分类: Linux驱动程序2012-07-11 20:44 3006人阅读 评论(2) 收藏 举报

水平有限,描述不当之处还请之处,转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7737833

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

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

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

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

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

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

  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. /* chipselect only toggles at start or end of operation */
  9. if (count > bufsiz)
  10. return -EMSGSIZE;
  11. spidev = filp->private_data;
  12. mutex_lock(&spidev->buf_lock);
  13. //将用户要发送的数据拷贝到spidev->buffer
  14. missing = copy_from_user(spidev->buffer, buf, count);
  15. if (missing == 0) {//全部拷贝成功,则调用spidev_sysn_write()
  16. status = spidev_sync_write(spidev, count);
  17. } else
  18. status = -EFAULT;
  19. mutex_unlock(&spidev->buf_lock);
  20. return status;
  21. }
  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. spi_message_init(&m);
  10. spi_message_add_tail(&t, &m);//将transfer添加到message中
  11. return spidev_sync(spidev, &m);
  12. }

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

  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. dma_addr_t  tx_dma;
  11. dma_addr_t  rx_dma;
  12. unsigned    cs_change:1; //该位如果为1,则表示当该transfer传输完后,改变片选信号
  13. u8      bits_per_word;//字比特数
  14. u16     delay_usecs;  //传输后的延时
  15. u32     speed_hz;  //指定的时钟
  16. struct list_head transfer_list;//用于将该transfer链入message
  17. };
  1. struct spi_message {
  2. struct list_head    transfers;//用于链接spi_transfer
  3. struct spi_device   *spi;      //指向目的从设备
  4. unsigned        is_dma_mapped:1;
  5. /* REVISIT:  we might want a flag affecting the behavior of the
  6. * last transfer ... allowing things like "read 16 bit length L"
  7. * immediately followed by "read L bytes".  Basically imposing
  8. * a specific message scheduling algorithm.
  9. *
  10. * Some controller drivers (message-at-a-time queue processing)
  11. * could provide that as their default scheduling algorithm.  But
  12. * others (with multi-message pipelines) could need a flag to
  13. * tell them about such special cases.
  14. */
  15. /* completion is reported through a callback */
  16. void            (*complete)(void *context);//用于异步传输完成时调用的回调函数
  17. void            *context;                  //回调函数的参数
  18. unsigned        actual_length;            //实际传输的长度
  19. int         status;
  20. /* for optional use by whatever driver currently owns the
  21. * spi_message ...  between calls to spi_async and then later
  22. * complete(), that's the spi_master controller driver.
  23. */
  24. struct list_head    queue; //用于将该message链入bitbang等待队列
  25. void            *state;
  26. };

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

  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. message->complete = spidev_complete;//设置回调函数
  7. message->context = &done;
  8. spin_lock_irq(&spidev->spi_lock);
  9. if (spidev->spi == NULL)
  10. status = -ESHUTDOWN;
  11. else
  12. status = spi_async(spidev->spi, message);//调用spi核心层的函数spi_async()
  13. spin_unlock_irq(&spidev->spi_lock);
  14. if (status == 0) {
  15. wait_for_completion(&done);
  16. status = message->status;
  17. if (status == 0)
  18. status = message->actual_length;
  19. }
  20. return status;
  21. }</span>
  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()

  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. m->actual_length = 0;
  7. m->status = -EINPROGRESS;
  8. bitbang = spi_master_get_devdata(spi->master);
  9. spin_lock_irqsave(&bitbang->lock, flags);
  10. if (!spi->max_speed_hz)
  11. status = -ENETDOWN;
  12. else {
  13. list_add_tail(&m->queue, &bitbang->queue);//将message添加到bitbang的等待队列
  14. queue_work(bitbang->workqueue, &bitbang->work);//调度运行work
  15. }
  16. spin_unlock_irqrestore(&bitbang->lock, flags);
  17. return status;
  18. }

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

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

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

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

  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. if (spsta & S3C2410_SPSTA_DCOL) {
  8. dev_dbg(hw->dev, "data-collision\n");
  9. complete(&hw->done);
  10. goto irq_done;
  11. }
  12. /*设备忙检测*/
  13. if (!(spsta & S3C2410_SPSTA_READY)) {
  14. dev_dbg(hw->dev, "spi not ready for tx?\n");
  15. complete(&hw->done);
  16. goto irq_done;
  17. }
  18. hw->count++;
  19. if (hw->rx)//读取数据到缓冲区
  20. hw->rx[count] = readb(hw->regs + S3C2410_SPRDAT);
  21. count++;
  22. if (count < hw->len)//向从机写入数据
  23. writeb(hw_txbyte(hw, count), hw->regs + S3C2410_SPTDAT);
  24. else//count == len,一个字段发送完成,唤醒完成量
  25. complete(&hw->done);
  26. irq_done:
  27. return IRQ_HANDLED;
  28. }

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

Linux SPI框架(下)的更多相关文章

  1. linux驱动基础系列--linux spi驱动框架分析

    前言 主要是想对Linux 下spi驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如平台驱动.设备模型等也不进行详细说明原理.如果有任何错误地方,请指出,谢谢! spi ...

  2. linux驱动基础系列--linux spi驱动框架分析(续)

    前言 这篇文章是对linux驱动基础系列--linux spi驱动框架分析的补充,主要是添加了最新的linux内核里设备树相关内容. spi设备树相关信息 如之前的文章里所述,控制器的device和s ...

  3. MySQL在Django框架下的基本操作(MySQL在Linux下配置)

    [原]本文根据实际操作主要介绍了Django框架下MySQL的一些常用操作,核心内容如下: ------------------------------------------------------ ...

  4. Linux Spi驱动移植小结

    2012-01-07 22:21:29 效果图: 理论学习后,主要是linux中spi子系统设备框架的了解后,主控制器与设备分离的思想,那么我要开始动手了. 1,  make menuconfig添加 ...

  5. Linux SPI总线和设备驱动架构之四:SPI数据传输的队列化

    我们知道,SPI数据传输可以有两种方式:同步方式和异步方式.所谓同步方式是指数据传输的发起者必须等待本次传输的结束,期间不能做其它事情,用代码来解释就是,调用传输的函数后,直到数据传输完成,函数才会返 ...

  6. Linux SPI总线和设备驱动架构之三:SPI控制器驱动

    通过第一篇文章,我们已经知道,整个SPI驱动架构可以分为协议驱动.通用接口层和控制器驱动三大部分.其中,控制器驱动负责最底层的数据收发工作,为了完成数据的收发工作,控制器驱动需要完成以下这些功能:1. ...

  7. Linux 驱动框架---i2c驱动框架

    i2c驱动在Linux通过一个周的学习后发现i2c总线的驱动框架还是和Linux整体的驱动框架是相同的,思想并不特殊比较复杂的内容如i2c核心的内容都是内核驱动框架实现完成的,今天我们暂时只分析驱动开 ...

  8. Linux驱动框架之framebuffer驱动框架

    1.什么是framebuffer? (1)framebuffer帧缓冲(一屏幕数据)(简称fb)是linux内核中虚拟出的一个设备,framebuffer向应用层提供一个统一标准接口的显示设备.帧缓冲 ...

  9. linux spi驱动开发学习-----spidev.c和spi test app

    一.spidev.c文件 看一个设备驱动的方法: module_init标识的入口初始化函数spidev_init,(module_exit标识的出口函数) 设备与设备驱动匹配时候调用的probe方法 ...

随机推荐

  1. 【转】 当程序崩溃的时候怎么办 part-1

    转自:http://www.tairan.com/archives/1006 有这样一种情形:当我们正在快乐的致力于我们的app时,并且什么看都是无比顺利,但是突然,坑爹啊,它崩溃了.(悲伤地音乐响起 ...

  2. Linux 下操作gpio(两种方法,驱动和mmap)

    目前我所知道的在linux下操作GPIO有两种方法: 1.  编写驱动,这当然要熟悉linux下驱动的编写方法和技巧,在驱动里可以使用ioremap函数获得GPIO物理基地址指针,然后使用这个指针根据 ...

  3. codeforce 604B More Cowbell

    题意:n个球,分成k堆.问堆的最大值的最小. #include <bits/stdc++.h> typedef long long ll; using namespace std; int ...

  4. C++中的虚函数解析[The explanation for virtual function of CPlusPlus]

    1.什么是虚函数?                                                                                            ...

  5. 【tyvj1952】easy

    AK大神又AK了!!! orzorzorz 题意: 给出一个字符串由'x'.'o'.'?' '?'有一半的几率为'x' 一半几率为'o' 得分为所有连续的'o'的个数的平方和 如ooxooo 得分为2 ...

  6. HDU5791--Two (DP)

    题意:两个数列a,b,求相同的子序列有多少对,内容相同位置不同也算不同. 题解:dp[i][j]表示a数列前i个数个 b数列前j个数 有多少对 递推方程: dp[i][j] = dp[i-1][j-1 ...

  7. TCP/IP协议详解内容总结

    TCP/IP协议 TCP/IP不是一个协议,而是一个协议族的统称.里面包括IP协议.IMCP协议.TCP协议. TCP/IP分层:   这里有几个需要注意的知识点: 互联网地址:也就是IP地址,一般为 ...

  8. 删除TreeView节点以及其子节点

    //1.删除TreeView节点以及其子节点procedure TForm2.Button1Click(Sender: TObject);var TreeNode:TTreeNode;begin  i ...

  9. elecworks 电缆型号管理器

    部件的命名:系列  导线数x尺寸                                            (不含保护类型的电线) 或:系列  导线数G尺寸      G(指保护线颜色gr ...

  10. [置顶] 【cocos2d-x入门之五】导演类CCDirector

    原创作品,转载请标明:http://blog.csdn.net/jackystudio/article/details/12646337 既然cocos2d-x都帮我们封装好了,使得开发与平台无关,那 ...