spi驱动之can总线mcp2515驱动测试

问1:linux内核.config Makefile Kbuild的关系?
答1:在word里可以找到答案

问2:因为mcp2515是spi转can芯片,所以首先移植spi驱动,分析spi驱动过程
答2:

----------------------------spi驱动整体框架---------------------------------------------		

	spi驱动分三个层次:spi核心层,spi控制器驱动层,spi设备驱动层
	spi核心层		:	与平台无关,向上提供统一接口,位置SPI核心层的代码位于driver/spi/spi.c
	spi控制器驱动层 :	平台移植相关层,每条spi总线提供相应的读写方法,物理上连接若干个从设备,
						 一个控制器驱动可以用数据结构struct spi_master来描述
	spi设备驱动层   :  用户接口层,通过struct spi_driver和struct spi_device描述。	

//-------------------------------------------------------
spi设备驱动层
*********************************************************
1.		spi_driver和spi_device结构

struct spi_driver {
	const struct spi_device_id *id_table;
	int			(*probe)(struct spi_device *spi);
	int			(*remove)(struct spi_device *spi);
	void			(*shutdown)(struct spi_device *spi);
	int			(*suspend)(struct spi_device *spi, pm_message_t mesg);
	int			(*resume)(struct spi_device *spi);
	struct device_driver	driver;
};

struct spi_device {
	struct device		dev;
	struct spi_master	*master;
	u32			max_speed_hz;
	u8			chip_select;
	u8			mode;
	u8			bits_per_word;
	int			irq;
	void			*controller_state;
	void			*controller_data;
	char			modalias[SPI_NAME_SIZE];

}

 		.modalias   = "m25p10",

        .mode   =SPI_MODE_0,   //CPOL=0, CPHA=0 此处选择具体数据传输模式

        .max_speed_hz    = 10000000, //最大的spi时钟频率

        /* Connected to SPI-0 as 1st Slave */

        .bus_num    = 0,   //设备连接在spi控制器0上

        .chip_select    = 0, //片选线号,在S5PC100的控制器驱动中没有使用它作为片选的依据,而是选择了下文controller_data里的方法。

        .controller_data = &smdk_spi0_csi[0],  

通常来说spi_device对应着SPI总线上某个特定的slave。并且spi_device封装了一个spi_master结构体。
spi_device结构体包含了私有的特定的slave设备特性,包括它最大的频率,片选那个,输入输出模式等等

总结:	Driver是为device服务的,spi_driver注册时会扫描SPI bus上的设备,进行驱动和设备的绑定,
		probe函数用于驱动和设备匹配时被调用。从上面的结构体注释中我们可以知道,SPI的通信是通过消息队列机制,
		而不是像I2C那样通过与从设备进行对话的方式。

***********************************************
2.		spi_device在board中如何注册,通过spi_board_info结构体

spi_device以下一系列的操作是在platform板文件中完成!

spi_device的板信息用spi_board_info结构体来描述:

struct spi_board_info {

charmodalias[SPI_NAME_SIZE];

const void*platform_data;

void*controller_data;

intirq;

u32max_speed_hz;

u16bus_num;

u16chip_select;

u8mode;

};

这个结构体记录了SPI外设使用的主机控制器序号、片选信号、数据比特率、SPI传输方式等

构建的操作是以下的两个步骤:

1.
static struct spi_board_info s3c_spi_devs[] __initdata = {

{

.modalias = "m25p10a",

.mode = SPI_MODE_0,

.max_speed_hz = 1000000,

.bus_num = 0,

.chip_select = 0,

.controller_data = &smdk_spi0_csi[SMDK_MMCSPI_CS],

},

};

2.

而这个info在init函数调用的时候会初始化:

spi_register_board_info(s3c_spi_devs,ARRAY_SIZE(s3c_spi_devs));

spi_register_board_info(s3c_spi_devs,ARRAY_SIZE(s3c_spi_devs));//注册spi_board_info。
这个代码会把spi_board_info注册到链表board_list上。spi_device封装了一个spi_master结构体,
事实上spi_master的注册会在spi_register_board_info之后,spi_master注册的过程中会调用scan_boardinfo扫描board_list,
找到挂接在它上面的spi设备,然后创建并注册spi_device。

至此spi_device就构建并注册完成了!!!!!!!!!!!!!

*******************************************************************
3. spi_driver的构建与注册

driver有几个重要的结构体:spi_driver、spi_transfer、spi_message

driver有几个重要的函数    :spi_message_init、spi_message_add_tail、spi_sync

   //spi_driver的构建

static struct spi_driver   m25p80_driver = { 

.driver = {

        .name   ="m25p80",

        .bus    =&spi_bus_type,

        .owner  = THIS_MODULE,

    },

    .probe  = m25p_probe,

    .remove =__devexit_p(m25p_remove),

};

//spidriver的注册

spi_register_driver(&m25p80_driver);

在有匹配的spi_device时,会调用m25p_probe

probe里完成了spi_transfer、spi_message的构建;

spi_message_init、spi_message_add_tail、spi_sync、spi_write_then_read函数的调用

----------------------------spi驱动分析流程---------------------------------------------
在spi核心层 driver/spi/spi.c	 				

static int __init spi_init(void)
				注册spi总线
				bus_register(&spi_bus_type)	

		  		在sys/class下产生spi_master这个节点,用于自动产生设备节点,下面挂接的是控制器的节点
				class_register(&spi_master_class);	 	

其中注册bus结构体
struct bus_type spi_bus_type = {
	.name		= "spi",
	.dev_attrs	= spi_dev_attrs,
	.match		= spi_match_device,		//匹配函数
	.uevent		= spi_uevent,
	.suspend	= spi_suspend,
	.resume		= spi_resume,
};							

类结构体
static struct class spi_master_class = {
	.name		= "spi_master",
	.owner		= THIS_MODULE,
	.dev_release	= spi_master_release,
};	 				

顺便分析下匹配函数
static int spi_match_device(struct device *dev, struct device_driver *drv)
				const struct spi_device	*spi = to_spi_device(dev);	

				利用id表格匹配
				spi_match_id(sdrv->id_table, spi);

				利用名字匹配
				strcmp(spi->modalias, drv->name)	 				

再来看看spi注册函数
int spi_register_driver(struct spi_driver *sdrv)
		注册标准的driver,匹配bus设备链表上的device,如果找到执行相应的函数
		driver_register(&sdrv->driver);

最后看看device相关的板级信息函数
int __init spi_register_board_info(struct spi_board_info const *info, unsigned n)
{
	struct boardinfo	*bi;

	bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);
	if (!bi)
		return -ENOMEM;
	bi->n_board_info = n;
	memcpy(bi->board_info, info, n * sizeof *info);

	mutex_lock(&board_lock);
	list_add_tail(&bi->list, &board_list);
	mutex_unlock(&board_lock);
	return 0;
}
函数很简单,利用定义的spi_board_info信息,填充了boardinfo结构,并挂到board_list链表

spi的控制层中重要函数,master的注册函数
int spi_register_master(struct spi_master *master)

			扫描并实例化spi设备
			scan_boardinfo(master);
				寻找注册好的board_list链表中的device
				list_for_each_entry(bi, &board_list, list)

				因为可能存在多个spi总线,因此spi信息结构也会有
 				多个,找到bus号匹配的就对了
				for (n = bi->n_board_info; n > 0; n--, chip++) {
					if (chip->bus_num != master->bus_num)

				//找到了就要实例化它上面的设备了
				spi_new_device(master, chip)	

driver/spi/spidev.c
spi设备文件的自动产生代码分析

      这部分代码相当于注册了一个spi实际driver,既是核心平台无关代码,又是个具体实例,
      我们来看下一下具体做了什么,也好学习一spi_driver的各部分具体都需要做些什么,
      代码位于driver/spi/spidev.c下

static int __init spidev_init(void)

			    注册主设备号为153的spi字符设备,spidev_fops结构下面会介绍
			    status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);

				在sys/class下产生spidev这个节点,udev会利用其在dev下产生spidev
				spidev_class = class_create(THIS_MODULE, "spidev"); 

				//注册spi驱动
				status = spi_register_driver(&spidev_spi_driver);

注册的spi_driver 结构体spidev_spi_driver
static struct spi_driver spidev_spi = {
	.driver = {
		.name =		"spidev",
		.owner =	THIS_MODULE,
	},
	.probe =	spidev_probe,					//匹配成功
	.remove =	__devexit_p(spidev_remove),

};

匹配函数
static int spidev_probe(struct spi_device *spi)
			下面两句用于在sys/class/spidev下产生类似于,spidev%d.%d形式的节点,这样,udev等工具就可以
			自动在dev下创建相应设备号的设备节点
			dev = device_create(spidev_class, &spi->dev, spidev->devt,
			spidev, "spidev%d.%d",
		    spi->master->bus_num, spi->chip_select);

至此,spi驱动分析完毕。	

问3:利用linux2.6.32内核自带的spi测试程序,测试的时候
     经过测试,mosi发送数据正确,但是mosi没有直连miso脚,也能收到数据
     用示波器测量miso脚,也没有接收到数据波形,默认高电平,怎么回事?
答3:     

问4:逆向跟踪分析miso脚寄存器相关调用函数?
答4:spi_read_reg(drv_data, RA_UART_EMI_REC)  打印寄存器的值是正确的,现在是怎么把寄存器的数据放到驱动中间层?  

struct spi_driver_data
{
	/* Driver model hookup */
	struct platform_device *pdev;

	/* SPI framework hookup */
	struct spi_master *master;

	/* Driver message queue */
	struct workqueue_struct	*workqueue;
	spinlock_t lock;
	struct list_head queue;
	int busy;
	int run;

	/* Message Transfer pump */
	struct work_struct pump_messages;
	struct tasklet_struct pump_transfers;
	struct spi_message* cur_msg;
	struct spi_transfer* next_transfer;

	u32		transfer_count;

	struct dc_spi_transfer		tx;
	struct dc_spi_transfer		rx;

	...

}  

struct dc_spi_transfer
{
	unsigned int idx;
	unsigned int pos;
	struct spi_transfer* cur;
	struct spi_transfer* transfers[MAX_SPI_TRANSFER_PER_MESSAGE];
}

struct spi_transfer {
	/* it's ok if tx_buf == rx_buf (right?)
	 * for MicroWire, one buffer must be null
	 * buffers must work with dma_*map_single() calls, unless
	 *   spi_message.is_dma_mapped reports a pre-existing mapping
	 */
	const void	*tx_buf;
	void		*rx_buf;
	unsigned	len;

	dma_addr_t	tx_dma;
	dma_addr_t	rx_dma;

	unsigned	cs_change:1;
	u8		bits_per_word;
	u16		delay_usecs;
	u32		speed_hz;

	struct list_head transfer_list;
}

问5:分析spidev的ioctl接口函数?
答5:
在spidev.c中

static long spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
		 	spidev_message(spidev, ioc, n_ioc)
		 		spidev_sync(spidev, &msg)
		 			spi_async(spidev->spi, message)
		 				master->transfer(spi, message)

//--------------
函数spi->master->transfer()在函数spi_bitbang_start()中被初始化为函数spi_bitbang_transfer()如下 

if (!bitbang->master->transfer)
     bitbang->master->transfer = spi_bitbang_transfer; 

函数spi_bitbang_transfer()又在函数s3c24xx_spi_probe()中被调用。 

函数spi_bitbang_transfer()在文件spi_bitbang.c中实现。 

//-------------------------------------------------------
调试打印记录
parse_spi_message: TX transfer for 3 bytes, buffer VA cc170000(ccf5aa55)

parse_spi_message:
1 xfers,
wr 3,
rd 0
(pri=3,
qw=0,
bytes=0,
rout=3,
rxs=3)

SPI_DEBUG_PRINT_INFO("%s: %d xfers, wr %d, rd %d (pri=%d, qw=%d, bytes=%d, rout=%d, rxs=%d)\n",
		__func__,
		drv_data->transfer_count,
		drv_data->total_write,
		drv_data->total_read,
		drv_data->priming,
		drv_data->qwords,
		drv_data->bytes,
		drv_data->readout,
		drv_data->rx_start_idx)		 				

	int total_write;					// total number of bytes to write
	int total_read;						// total number of bytes to read
	int total_write_or_read;			// total number of bytes to read or write

	int bytes;							// number of bytes read-writes
	int qwords;							// number of qword read-writes
	int readout;						// number of byte reads at the end of transfer
	int priming;						// number of bytes to prime
	int rx_start_idx;					// position where to start saving rx into input buffer

问6:spi的probe函数执行过程,如何调用底层驱动函数?
答6:
int __init dc_usart_spi_probe(struct platform_device *pdev)
				init_queues(drv_data)
					tasklet_init(&drv_data->pump_transfers,	pump_transfers,	(unsigned long)drv_data)
					INIT_WORK(&drv_data->pump_messages, pump_messages)

						RELEASE_STATIC void pump_messages(struct work_struct *data)
												drv_data->cur_msg->state = parse_spi_message(drv_data)
												tasklet_schedule(&drv_data->pump_transfers)		

													RELEASE_STATIC void pump_transfers(unsigned long data)
																		dc_usart_nondma_transfer(drv_data)
																			rxc = spi_receive_byte(drv_data)

通过queue_work(drv_data->workqueue, &drv_data->pump_messages)调用工作任务函数pump_messages,
认为调用parse_spi_message函数中
else if (transfer->rx_buf)一直为假,所以drv_data->total_read=0,所以spi_receive_byte接收的寄存器数据没有填充spi_transfer
所以,应用接口层没有收到底层发来的数据

当spi有接收数据的时候,通过什么机制调用parse_spi_message

了解两个函数
tasklet_init
INIT_WORK																		

时间: 2024-07-30 17:43:34

spi驱动之can总线mcp2515驱动测试的相关文章

16.总线设备驱动模型学习

总线设备驱动模型学习 一.总线概述 随着技术的不断进步,系统的拓扑结构也越来越复杂,对热插拔,跨平台移植性的要求也越来越高,2.4内核已经难以满足这些需求.为适应这种形势的需要,从Linux 2.6内核开始提供了全新的设备模型. 总线:创建一条总线,跟按键一样,首先是描述总线结构,接着是注册总线,注销总线.总线设备,例如usb总线,上面会有很多类型的usb的驱动,例如鼠标.键盘.....等,当我们把之一的usb插上的时候,usb总线会把每个驱动遍历一遍,找到相应的驱动程序执行. 1.1总线描述

8.总线设备驱动模型

总线设备驱动模型 总线:创建一条总线,跟我们前面的按键一样,首先是描述总线结构,接着是注册总线,注销总线.总线设备,例如usb总线,上面会有很多类型的usb的驱动,例如鼠标.键盘.....等,当我们把之一的usb插上的时候,usb总线会把每个驱动遍历一遍,找到相应的驱动程序执行. 接下来用bus.c创建一条总线. #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #in

20150225 IMX257 总线设备驱动模型编程之总线篇

20150225 IMX257 总线设备驱动模型编程之总线篇 2015-02-25 19:40 李海沿 从现在开始,我们开始来实现 总线-设备-驱动模型中的总线.. 我们这个程序的目标是在 sysfs文件系统的/sys/bus/ 目录下面建立一个文件夹. 一.总线介绍 1. 总线数据结构bus_type struct bus_type 结构体的定义如下: struct bus_type { const char *name; --总线名 struct bus_attribute *bus_att

总线设备驱动模型【转】

本文转载自:http://blog.csdn.net/coding__madman/article/details/51428400 总线驱动设备模型: 1. 总线设备驱动模型概述 随着技术的不断进步,系统的拓扑结构也越来越复杂,对热插拔,跨平台移植性的要求也越来越高,2.4内核已经难以满足这些需求,为适应这宗形势的需求,从linux2.6内核开始提供了全新的设备模型 2. 总线 2.1 描述结构 2.2 注册 2.3 注销 void  bus_unregister(struct  bus_ty

openwrt 增加RTC(MCP7940 I2C总线)驱动详解

一.硬件平台 1.1 控制器:MT7620(A9内核) 1.2 RTC芯片:MCP7940(I2C总线) 二.软件平台 2.1.开发环境:Ubuntu12.04 2.2.软件版本:openwrt 官方15.05版本SDK开发包(CHAOS CALMER 15.05版本) 三.功能说明 本文章所选择的目标芯片为MT7620,profile 选择的为"Xiaomi MiWiFi Mini ". 3.1.在openwrt 系统上,移植mcp7940的rtc芯片驱动. 3.2.在openwrt

linux2.6.30.4 s3c2440 platform总线 led驱动

1  basic 在设备驱动程序中经常会见到和platform相关的字段,分布在驱动程序的多个角落,这也是2.6内核中比较重要的一种机制,把它的原理弄懂了,对以后分析驱动程序很有帮助,下面简单介绍一下:    在linux2.6设备模型中,关心总线,设备,驱动这三个实体,总线将设备和驱动绑定,在系统每注册一个设备的时候,会寻找与之匹配的驱动.相反,在系统每注册一个驱动的时候,寻找与之匹配的设备,匹配是由总线来完成的. 一个现实的Linux 设备和驱动通常都需要挂接在一种总线上,对于本身依附于PC

20150226 IMX257 总线设备驱动模型编程之平台总线设备platform

20150226 IMX257 总线设备驱动模型编程之平台总线设备platform 2015-02-26 李海沿 前面我们实现了总线设备驱动模型,下面我们来了解一下平台总线,平台设备驱动 分为平台设备和平台驱动两种,和前面所说的设备驱动差不多 platform总线是一种虚拟的总线,相应的设备则为platform_device,而驱动则为platform_driver.Linux 2.6的设备驱动模型中,把I2C.RTC.LCD等都归纳为platform_device. 一.平台设备介绍 1. p

20150226 IMX257 总线设备驱动模型编程之设备篇

20150226 IMX257 总线设备驱动模型编程之设备篇 2015-02-26 李海沿 前面我们呢实现了总线-设备-驱动模型中的总线,自然,我们的目标就是在我们建立的总线下面创建一个设备. http://www.cnblogs.com/lihaiyan/p/4301072.html 一.程序分析 1. 包含总线 既然我们的设备在总线上,自然我们既要包含总线了 如图所示,使用外部声明将我们的总线的结构体包含进来 2. 定义设备结构体 父目录为 my_bus 3. 定义属性文件结构体 属性文件结

20150226 IMX257 总线设备驱动模型编程之驱动篇

20150226 IMX257 总线设备驱动模型编程之驱动篇 2015-02-26 11:42 李海沿 前面我们已经实现了 总线和设备 的驱动程序,接下来我们的任务就是 实现 驱动 了 地址:http://www.cnblogs.com/lihaiyan/p/4301079.html http://www.cnblogs.com/lihaiyan/p/4301072.html 在实现驱动程序之前,我们来想两个问题: 一.问题分析 1.什么时候驱动程序会在总线上找它可以处理的设备? 在driver