Linux SPI框架 (3)

来源:互联网 发布:手机淘宝怎么提交改价 编辑:程序博客网 时间:2024/03/29 20:26

     本节以spidev设备驱动为例,来阐述SPI数据传输的过程。spidev是内核中一个通用的设备驱动,我们注册的从设备都可以使用该驱动,只需在注册时将从设备的modalias字段设置为"spidev",这样才能和spidev驱动匹配成功。我们要传输的数据有时需要分为一段一段的(比如先发送,后读取,就需要两个字段),每个字段都被封装成一个transfer,N个transfer可以被添加到message中,作为一个消息包进行传输。当用户发出传输数据的请求时,message并不会立刻传输到从设备,而是由之前定义的transfer()函数将message放入一个等待队列中,这些message会以FIFO的方式有workqueue调度进行传输,这样能够避免SPI从设备同一时间对主SPI控制器的竞争。和之前一样,还是习惯先画一张图来描述数据传输的主要过程。

 

         在使用spidev设备驱动时,需要先初始化spidev. spidev是以字符设备的形式注册进内核的。

[cpp] view plaincopy
  1. static int __init spidev_init(void)  
  2. {  
  3.     int status;  
  4.   
  5.     /* Claim our 256 reserved device numbers.  Then register a class 
  6.      * that will key udev/mdev to add/remove /dev nodes.  Last, register 
  7.      * the driver which manages those device numbers. 
  8.      */  
  9.     BUILD_BUG_ON(N_SPI_MINORS > 256);  
  10.     /*将spidev作为字符设备注册*/  
  11.     status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);  
  12.     if (status < 0)  
  13.         return status;  
  14.   
  15.     /*创建spidev类*/  
  16.     spidev_class = class_create(THIS_MODULE, "spidev");  
  17.     if (IS_ERR(spidev_class)) {  
  18.         unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);  
  19.         return PTR_ERR(spidev_class);  
  20.     }  
  21.   
  22.     /*注册spidev的driver,可与modalias字段为"spidev"的spi_device匹配*/  
  23.     status = spi_register_driver(&spidev_spi);  
  24.     if (status < 0) {  
  25.         class_destroy(spidev_class);  
  26.         unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);  
  27.     }  
  28.     return status;  
  29. }  


与相应的从设备匹配成功后,则调用spidev中的probe函数

[cpp] view plaincopy
  1. static int spidev_probe(struct spi_device *spi)  
  2. {  
  3.     struct spidev_data  *spidev;  
  4.     int         status;  
  5.     unsigned long       minor;  
  6.   
  7.     /* Allocate driver data */  
  8.     spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);  
  9.     if (!spidev)  
  10.         return -ENOMEM;  
  11.   
  12.     /* Initialize the driver data */  
  13.     spidev->spi = spi;//设定spi  
  14.     spin_lock_init(&spidev->spi_lock);  
  15.     mutex_init(&spidev->buf_lock);  
  16.   
  17.     INIT_LIST_HEAD(&spidev->device_entry);  
  18.   
  19.     /* If we can allocate a minor number, hook up this device. 
  20.      * Reusing minors is fine so long as udev or mdev is working. 
  21.      */  
  22.     mutex_lock(&device_list_lock);  
  23.     minor = find_first_zero_bit(minors, N_SPI_MINORS);//寻找没被占用的次设备号  
  24.     if (minor < N_SPI_MINORS) {  
  25.         struct device *dev;  
  26.         /*计算设备号*/  
  27.         spidev->devt = MKDEV(SPIDEV_MAJOR, minor);  
  28.         /*在spidev_class下创建设备*/  
  29.         dev = device_create(spidev_class, &spi->dev, spidev->devt,  
  30.                     spidev, "spidev%d.%d",  
  31.                     spi->master->bus_num, spi->chip_select);  
  32.         status = IS_ERR(dev) ? PTR_ERR(dev) : 0;  
  33.     } else {  
  34.         dev_dbg(&spi->dev, "no minor number available!\n");  
  35.         status = -ENODEV;  
  36.     }  
  37.     if (status == 0) {  
  38.         set_bit(minor, minors);//将minors的相应位置位,表示该位对应的次设备号已被占用  
  39.         list_add(&spidev->device_entry, &device_list);//将创建的spidev添加到device_list  
  40.     }  
  41.     mutex_unlock(&device_list_lock);  
  42.   
  43.     if (status == 0)  
  44.         spi_set_drvdata(spi, spidev);  
  45.     else  
  46.         kfree(spidev);  
  47.   
  48.     return status;  
  49. }  


然后就可以利用spidev模块提供的接口来实现主从设备之间的数据传输了。我们以spidev_write()函数为例来分析数据传输的过程,实际上spidev_read()和其是差不多的,只是前面的一些步骤不一样,可以参照上图。

[cpp] view plaincopy
  1. static ssize_t  
  2. spidev_write(struct file *filp, const char __user *buf,  
  3.         size_t count, loff_t *f_pos)  
  4. {  
  5.     struct spidev_data  *spidev;  
  6.     ssize_t         status = 0;  
  7.     unsigned long       missing;  
  8.   
  9.     /* chipselect only toggles at start or end of operation */  
  10.     if (count > bufsiz)  
  11.         return -EMSGSIZE;  
  12.   
  13.     spidev = filp->private_data;  
  14.   
  15.     mutex_lock(&spidev->buf_lock);  
  16.     //将用户要发送的数据拷贝到spidev->buffer  
  17.     missing = copy_from_user(spidev->buffer, buf, count);  
  18.     if (missing == 0) {//全部拷贝成功,则调用spidev_sysn_write()  
  19.         status = spidev_sync_write(spidev, count);  
  20.     } else  
  21.         status = -EFAULT;  
  22.     mutex_unlock(&spidev->buf_lock);  
  23.   
  24.     return status;  
  25. }  


 

[cpp] view plaincopy
  1. static inline ssize_t  
  2. spidev_sync_write(struct spidev_data *spidev, size_t len)  
  3. {  
  4.     struct spi_transfer t = {//设置传输字段  
  5.             .tx_buf     = spidev->buffer,  
  6.             .len        = len,  
  7.         };  
  8.     struct spi_message   m;//创建message  
  9.   
  10.     spi_message_init(&m);  
  11.     spi_message_add_tail(&t, &m);//将transfer添加到message中  
  12.     return spidev_sync(spidev, &m);  
  13. }  


我们来看看struct spi_transfer和struct spi_message是如何定义的

[cpp] view plaincopy
  1. struct spi_transfer {  
  2.     /* it's ok if tx_buf == rx_buf (right?) 
  3.      * for MicroWire, one buffer must be null 
  4.      * buffers must work with dma_*map_single() calls, unless 
  5.      *   spi_message.is_dma_mapped reports a pre-existing mapping 
  6.      */  
  7.     const void  *tx_buf;//发送缓冲区  
  8.     void        *rx_buf;//接收缓冲区  
  9.     unsigned    len;    //传输数据的长度  
  10.   
  11.     dma_addr_t  tx_dma;  
  12.     dma_addr_t  rx_dma;  
  13.   
  14.     unsigned    cs_change:1; //该位如果为1,则表示当该transfer传输完后,改变片选信号  
  15.     u8      bits_per_word;//字比特数  
  16.     u16     delay_usecs;  //传输后的延时   
  17.     u32     speed_hz;  //指定的时钟  
  18.   
  19.     struct list_head transfer_list;//用于将该transfer链入message  
  20. };  


 

[cpp] view plaincopy
  1. struct spi_message {  
  2.     struct list_head    transfers;//用于链接spi_transfer  
  3.   
  4.     struct spi_device   *spi;      //指向目的从设备  
  5.   
  6.     unsigned        is_dma_mapped:1;  
  7.   
  8.     /* REVISIT:  we might want a flag affecting the behavior of the 
  9.      * last transfer ... allowing things like "read 16 bit length L" 
  10.      * immediately followed by "read L bytes".  Basically imposing 
  11.      * a specific message scheduling algorithm. 
  12.      * 
  13.      * Some controller drivers (message-at-a-time queue processing) 
  14.      * could provide that as their default scheduling algorithm.  But 
  15.      * others (with multi-message pipelines) could need a flag to 
  16.      * tell them about such special cases. 
  17.      */  
  18.   
  19.     /* completion is reported through a callback */  
  20.     void            (*complete)(void *context);//用于异步传输完成时调用的回调函数  
  21.     void            *context;                  //回调函数的参数  
  22.     unsigned        actual_length;            //实际传输的长度  
  23.     int         status;  
  24.   
  25.     /* for optional use by whatever driver currently owns the 
  26.      * spi_message ...  between calls to spi_async and then later 
  27.      * complete(), that's the spi_master controller driver. 
  28.      */  
  29.     struct list_head    queue; //用于将该message链入bitbang等待队列  
  30.     void            *state;  
  31. };  


继续跟踪源码,进入spidev_sync(),从这一步开始,read和write就完全一样了

[cpp] view plaincopy
  1. <span style="font-size:12px;">static ssize_t  
  2. spidev_sync(struct spidev_data *spidev, struct spi_message *message)  
  3. {  
  4.     DECLARE_COMPLETION_ONSTACK(done);  
  5.     int status;  
  6.   
  7.     message->complete = spidev_complete;//设置回调函数  
  8.     message->context = &done;              
  9.   
  10.     spin_lock_irq(&spidev->spi_lock);  
  11.     if (spidev->spi == NULL)  
  12.         status = -ESHUTDOWN;  
  13.     else  
  14.         status = spi_async(spidev->spi, message);//调用spi核心层的函数spi_async()  
  15.     spin_unlock_irq(&spidev->spi_lock);  
  16.   
  17.     if (status == 0) {  
  18.         wait_for_completion(&done);  
  19.         status = message->status;  
  20.         if (status == 0)  
  21.             status = message->actual_length;  
  22.     }  
  23.     return status;  
  24. }</span>  


 

[cpp] view plaincopy
  1. static inline int  
  2. spi_async(struct spi_device *spi, struct spi_message *message)  
  3. {  
  4.     message->spi = spi;  
  5.     /*调用master的transfer函数将message放入等待队列*/  
  6.     return spi->master->transfer(spi, message);  
  7. }  


 

s3c24xx平台下的transfer函数是在bitbang_start()函数中定义的,为bitbang_transfer()

[cpp] view plaincopy
  1. int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m)  
  2. {  
  3.     struct spi_bitbang  *bitbang;  
  4.     unsigned long       flags;  
  5.     int         status = 0;  
  6.   
  7.     m->actual_length = 0;  
  8.     m->status = -EINPROGRESS;  
  9.   
  10.     bitbang = spi_master_get_devdata(spi->master);  
  11.   
  12.     spin_lock_irqsave(&bitbang->lock, flags);  
  13.     if (!spi->max_speed_hz)  
  14.         status = -ENETDOWN;  
  15.     else {  
  16.         list_add_tail(&m->queue, &bitbang->queue);//将message添加到bitbang的等待队列  
  17.         queue_work(bitbang->workqueue, &bitbang->work);//调度运行work  
  18.     }  
  19.     spin_unlock_irqrestore(&bitbang->lock, flags);  
  20.   
  21.     return status;  
  22. }  

这里可以看到transfer函数不负责实际的数据传输,而是将message添加到等待队列中。同样在spi_bitbang_start()中,有这样一个定义INIT_WORK(&bitbang->work, bitbang_work);因此bitbang_work()函数会被调度运行,类似于底半部机制

[cpp] view plaincopy
  1. static void bitbang_work(struct work_struct *work)  
  2. {  
  3.     struct spi_bitbang  *bitbang =  
  4.         container_of(work, struct spi_bitbang, work);//获取bitbang  
  5.     unsigned long       flags;  
  6.   
  7.     spin_lock_irqsave(&bitbang->lock, flags);  
  8.     bitbang->busy = 1;  
  9.     while (!list_empty(&bitbang->queue)) {//等待队列不为空  
  10.         struct spi_message  *m;  
  11.         struct spi_device   *spi;  
  12.         unsigned        nsecs;  
  13.         struct spi_transfer *t = NULL;  
  14.         unsigned        tmp;  
  15.         unsigned        cs_change;  
  16.         int         status;  
  17.         int         (*setup_transfer)(struct spi_device *,  
  18.                         struct spi_transfer *);  
  19.         /*取出等待队列中的的第一个message*/  
  20.         m = container_of(bitbang->queue.next, struct spi_message,  
  21.                 queue);  
  22.         list_del_init(&m->queue);//将message从队列中删除  
  23.         spin_unlock_irqrestore(&bitbang->lock, flags);  
  24.   
  25.         /* FIXME this is made-up ... the correct value is known to 
  26.          * word-at-a-time bitbang code, and presumably chipselect() 
  27.          * should enforce these requirements too? 
  28.          */  
  29.         nsecs = 100;  
  30.   
  31.         spi = m->spi;  
  32.         tmp = 0;  
  33.         cs_change = 1;  
  34.         status = 0;  
  35.         setup_transfer = NULL;  
  36.   
  37.         /*遍历message中的所有传输字段,逐一进行传输*/  
  38.         list_for_each_entry (t, &m->transfers, transfer_list) {  
  39.   
  40.             /* override or restore speed and wordsize */  
  41.             if (t->speed_hz || t->bits_per_word) {  
  42.                 setup_transfer = bitbang->setup_transfer;  
  43.                 if (!setup_transfer) {  
  44.                     status = -ENOPROTOOPT;  
  45.                     break;  
  46.                 }  
  47.             }  
  48.             /*调用setup_transfer根据transfer中的信息进行时钟、字比特数的设定*/  
  49.             if (setup_transfer) {  
  50.                 status = setup_transfer(spi, t);  
  51.                 if (status < 0)  
  52.                     break;  
  53.             }  
  54.   
  55.             /* set up default clock polarity, and activate chip; 
  56.              * this implicitly updates clock and spi modes as 
  57.              * previously recorded for this device via setup(). 
  58.              * (and also deselects any other chip that might be 
  59.              * selected ...) 
  60.              */  
  61.             if (cs_change) {//使能外设的片选  
  62.                 bitbang->chipselect(spi, BITBANG_CS_ACTIVE);  
  63.                 ndelay(nsecs);  
  64.             }  
  65.             cs_change = t->cs_change;//这里确定进行了这个字段的传输后是否要改变片选状态  
  66.             if (!t->tx_buf && !t->rx_buf && t->len) {  
  67.                 status = -EINVAL;  
  68.                 break;  
  69.             }  
  70.   
  71.             /* transfer data.  the lower level code handles any 
  72.              * new dma mappings it needs. our caller always gave 
  73.              * us dma-safe buffers. 
  74.              */  
  75.             if (t->len) {  
  76.                 /* REVISIT dma API still needs a designated 
  77.                  * DMA_ADDR_INVALID; ~0 might be better. 
  78.                  */  
  79.                 if (!m->is_dma_mapped)  
  80.                     t->rx_dma = t->tx_dma = 0;  
  81.                 /*调用针对于平台的传输函数txrx_bufs*/  
  82.                 status = bitbang->txrx_bufs(spi, t);  
  83.             }  
  84.             if (status > 0)  
  85.                 m->actual_length += status;  
  86.             if (status != t->len) {  
  87.                 /* always report some kind of error */  
  88.                 if (status >= 0)  
  89.                     status = -EREMOTEIO;  
  90.                 break;  
  91.             }  
  92.             status = 0;  
  93.   
  94.             /* protocol tweaks before next transfer */  
  95.             /*如果要求在传输完一个字段后进行delay,则进行delay*/  
  96.             if (t->delay_usecs)  
  97.                 udelay(t->delay_usecs);  
  98.   
  99.             if (!cs_change)  
  100.                 continue;  
  101.               
  102.             /*最后一个字段传输完毕了,则跳出循环*/  
  103.             if (t->transfer_list.next == &m->transfers)  
  104.                 break;  
  105.   
  106.             /* sometimes a short mid-message deselect of the chip 
  107.              * may be needed to terminate a mode or command 
  108.              */  
  109.             ndelay(nsecs);  
  110.             bitbang->chipselect(spi, BITBANG_CS_INACTIVE);  
  111.             ndelay(nsecs);  
  112.         }  
  113.   
  114.         m->status = status;  
  115.         m->complete(m->context);  
  116.   
  117.         /* restore speed and wordsize */  
  118.         if (setup_transfer)  
  119.             setup_transfer(spi, NULL);  
  120.   
  121.         /* normally deactivate chipselect ... unless no error and 
  122.          * cs_change has hinted that the next message will probably 
  123.          * be for this chip too. 
  124.          */  
  125.         if (!(status == 0 && cs_change)) {  
  126.             ndelay(nsecs);  
  127.             bitbang->chipselect(spi, BITBANG_CS_INACTIVE);  
  128.             ndelay(nsecs);  
  129.         }  
  130.   
  131.         spin_lock_irqsave(&bitbang->lock, flags);  
  132.     }  
  133.     bitbang->busy = 0;  
  134.     spin_unlock_irqrestore(&bitbang->lock, flags);  
  135. }  


只要bitbang->queue等待队列不为空,就表示相应的SPI主控制器上还有传输任务没有完成,因此bitbang_work()会被不断地调度执行。 bitbang_work()中的工作主要是两个循环,外循环遍历等待队列中的message,内循环遍历message中的transfer,在bitbang_work()中,传输总是以transfer为单位的。当选定了一个transfer后,便会调用transfer_txrx()函数,进行实际的数据传输,显然这个函数是针对于平台的SPI控制器而实现的,在s3c24xx平台中,该函数为s3c24xx_spi_txrx();

[cpp] view plaincopy
  1. static int s3c24xx_spi_txrx(struct spi_device *spi, struct spi_transfer *t)  
  2. {  
  3.     struct s3c24xx_spi *hw = to_hw(spi);  
  4.   
  5.     dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",  
  6.         t->tx_buf, t->rx_buf, t->len);  
  7.   
  8.     hw->tx = t->tx_buf;//获取发送缓冲区  
  9.     hw->rx = t->rx_buf;//获取读取缓存区  
  10.     hw->len = t->len;  //获取数据长度  
  11.     hw->count = 0;  
  12.   
  13.     init_completion(&hw->done);//初始化完成量  
  14.   
  15.     /* send the first byte */  
  16.     /*只发送第一个字节,其他的在中断中发送(读取)*/  
  17.     writeb(hw_txbyte(hw, 0), hw->regs + S3C2410_SPTDAT);  
  18.   
  19.     wait_for_completion(&hw->done);  
  20.   
  21.     return hw->count;  
  22. }  


 

[cpp] view plaincopy
  1. static inline unsigned int hw_txbyte(struct s3c24xx_spi *hw, int count)  
  2. {  
  3.     /*如果tx不为空,也就是说当前是从主机向从机发送数据,则直接将tx[count]发送过去, 
  4.       如果tx为空,也就是说当前是从从机向主机发送数据,则向从机写入0*/  
  5.     return hw->tx ? hw->tx[count] : 0;  
  6. }  


负责SPI数据传输的中断函数:

[cpp] view plaincopy
  1. static irqreturn_t s3c24xx_spi_irq(int irq, void *dev)  
  2. {  
  3.     struct s3c24xx_spi *hw = dev;  
  4.     unsigned int spsta = readb(hw->regs + S3C2410_SPSTA);  
  5.     unsigned int count = hw->count;  
  6.   
  7.     /*冲突检测*/  
  8.     if (spsta & S3C2410_SPSTA_DCOL) {  
  9.         dev_dbg(hw->dev, "data-collision\n");  
  10.         complete(&hw->done);  
  11.         goto irq_done;  
  12.     }  
  13.   
  14.     /*设备忙检测*/  
  15.     if (!(spsta & S3C2410_SPSTA_READY)) {  
  16.         dev_dbg(hw->dev, "spi not ready for tx?\n");  
  17.         complete(&hw->done);  
  18.         goto irq_done;  
  19.     }  
  20.   
  21.     hw->count++;  
  22.   
  23.     if (hw->rx)//读取数据到缓冲区  
  24.         hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);  
  25.   
  26.     count++;  
  27.   
  28.     if (count < hw->len)//向从机写入数据  
  29.         writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);  
  30.     else//count == len,一个字段发送完成,唤醒完成量  
  31.         complete(&hw->done);  
  32.   
  33.  irq_done:  
  34.     return IRQ_HANDLED;  
  35. }  

这里可以看到一点,即使tx为空,也就是说用户申请的是从从设备读取数据,也要不断地向从设备写入数据,只不过写入从设备的是无效数据(0),这样做得目的是为了维持SPI总线上的时钟。至此,SPI框架已分析完毕。


0 0
原创粉丝点击