问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