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

来源:互联网 发布:姜文 知乎 编辑:程序博客网 时间:2024/05/07 23:06
问1:linux内核.config Makefile Kbuild的关系?答1:在word里可以找到答案问2:因为mcp2515是spi转can芯片,所以首先移植spi驱动,分析spi驱动过程答2:----------------------------spi驱动整体框架---------------------------------------------spi驱动分三个层次:spi核心层,spi控制器驱动层,spi设备驱动层spi核心层:与平台无关,向上提供统一接口,位置SPI核心层的代码位于driver/spi/spi.cspi控制器驱动层 :平台移植相关层,每条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_driverdriver;};struct spi_device {struct devicedev;struct spi_master*master;u32max_speed_hz;u8chip_select;u8mode;u8bits_per_word;intirq;void*controller_state;void*controller_data;charmodalias[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链表中的devicelist_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.cspi设备文件的自动产生代码分析       这部分代码相当于注册了一个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_driverstatic 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;u32transfer_count;struct dc_spi_transfertx;struct dc_spi_transferrx;...}  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;unsignedlen;dma_addr_ttx_dma;dma_addr_trx_dma;unsignedcs_change:1;u8bits_per_word;u16delay_usecs;u32speed_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 writeint total_read;// total number of bytes to readint total_write_or_read;// total number of bytes to read or writeint bytes;// number of bytes read-writesint qwords;// number of qword read-writesint readout;// number of byte reads at the end of transferint priming;// number of bytes to primeint 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_initINIT_WORK


0 0