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                                                                        
 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值