Linux spi驱动分析(三)----spiddev分析

转载 2015年11月17日 21:26:44

一、spidev简单介绍

        如果在内核中配置spidev,会在“/dev”目录下产生设备节点,通过此节点可以操作挂载在该SPI总线上的设备,接下来将从驱动层和应用层

来分析程序。

二、spidev驱动层

2.1、驱动注册

        分析一个设备驱动,一般都是从module_init和module_exit处开始,本文也不例外,程序如下:

点击(此处)折叠或打开

  1. #define SPIDEV_MAJOR            153    /* assigned */
  2. #define N_SPI_MINORS            32    /* ... up to 256 */

  3. static DECLARE_BITMAP(minors, N_SPI_MINORS);
  4. static struct spi_driver spidev_spi_driver = {
  5.     .driver = {
  6.         .name =        "spidev",
  7.         .owner =    THIS_MODULE,
  8.     },
  9.     .probe =    spidev_probe,
  10.     .remove =    __devexit_p(spidev_remove),

  11.     /* NOTE: suspend/resume methods are not necessary here.
  12.      * We don't do anything except pass the requests to/from
  13.      * the underlying controller. The refrigerator handles
  14.      * most issues; the controller driver handles the rest.
  15.      */
  16. };

  17. /*-------------------------------------------------------------------------*/

  18. static int __init spidev_init(void)
  19. {
  20.     int status;

  21.     /* Claim our 256 reserved device numbers. Then register a class
  22.      * that will key udev/mdev to add/remove /dev nodes. Last, register
  23.      * the driver which manages those device numbers.
  24.      */
  25.     BUILD_BUG_ON(N_SPI_MINORS > 256);
  26.     status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);
  27.     if (status < 0)
  28.         return status;

  29.     spidev_class = class_create(THIS_MODULE, "spidev");
  30.     if (IS_ERR(spidev_class)) {
  31.         unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
  32.         return PTR_ERR(spidev_class);
  33.     }

  34.     status = spi_register_driver(&spidev_spi_driver);
  35.     if (status < 0) {
  36.         class_destroy(spidev_class);
  37.         unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
  38.     }
  39.     return status;
  40. }
  41. module_init(spidev_init);

  42. static void __exit spidev_exit(void)
  43. {
  44.     spi_unregister_driver(&spidev_spi_driver);
  45.     class_destroy(spidev_class);
  46.     unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
  47. }
  48. module_exit(spidev_exit);

  49. MODULE_AUTHOR("Andrea Paterniani, ");
  50. MODULE_DESCRIPTION("User mode SPI device interface");
  51. MODULE_LICENSE("GPL");
  52. MODULE_ALIAS("spi:spidev");

        说明:

        1) 在驱动注册函数中,首先注册函数操作集spidev_fops,该内容将在2.3中具体讲述。

        2) 生成spidev设备类,此类的表现是在“/sys/class”目录下生成一个名为spidev目录。

        3) 注册spi驱动。

        4) 退出函数是注册函数的相反过程,就是释放注册函数中注册的资源。

2.2、探测和移除函数

        探测函数程序如下:

点击(此处)折叠或打开

  1. static int __devinit 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;
  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.         spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
  23.         dev = device_create(spidev_class, &spi->dev, spidev->devt,
  24.                  spidev, "spidev%d.%d",
  25.                  spi->master->bus_num, spi->chip_select);
  26.         status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
  27.     } else {
  28.         dev_dbg(&spi->dev, "no minor number available!\n");
  29.         status = -ENODEV;
  30.     }
  31.     if (status == 0) {
  32.         set_bit(minor, minors);
  33.         list_add(&spidev->device_entry, &device_list);
  34.     }
  35.     mutex_unlock(&device_list_lock);

  36.     if (status == 0)
  37.         spi_set_drvdata(spi, spidev);
  38.     else
  39.         kfree(spidev);

  40.     return status;
  41. }

        说明:

        1) 首先声明spidev_data结构体内存。

        2) 初始化其成员变量。

        3) 在位图中寻找第一个未被用到的位。

        4) 如果位图寻找正确,创建“/dev”目录下的设备节点。

        4) 将spidev结构体中的链表插入局部全局链表device_list中,以便在函数操作集中的open函数中使用            

        移除函数如下:

点击(此处)折叠或打开

  1. static int __devexit spidev_remove(struct spi_device *spi)
  2. {
  3.     struct spidev_data    *spidev = spi_get_drvdata(spi);

  4.     /* make sure ops on existing fds can abort cleanly */
  5.     spin_lock_irq(&spidev->spi_lock);
  6.     spidev->spi = NULL;
  7.     spi_set_drvdata(spi, NULL);
  8.     spin_unlock_irq(&spidev->spi_lock);

  9.     /* prevent new opens */
  10.     mutex_lock(&device_list_lock);
  11.     list_del(&spidev->device_entry);
  12.     device_destroy(spidev_class, spidev->devt);
  13.     clear_bit(MINOR(spidev->devt), minors);
  14.     if (spidev->users == 0)
  15.         kfree(spidev);
  16.     mutex_unlock(&device_list_lock);

  17.     return 0;
  18. }

        说明:

        1) 移除函数就是探测函数的相反过程,主要就是释放探测函数中申请的资源。

2.2、函数操作集spidev_fop

        spidev_fop具体如下:

点击(此处)折叠或打开

  1. static const struct file_operations spidev_fops = {
  2.     .owner =    THIS_MODULE,
  3.     /* REVISIT switch to aio primitives, so that userspace
  4.      * gets more complete API coverage. It'll simplify things
  5.      * too, except for the locking.
  6.      */
  7.     .write =    spidev_write,
  8.     .read =        spidev_read,
  9.     .unlocked_ioctl = spidev_ioctl,
  10.     .compat_ioctl = spidev_compat_ioctl,
  11.     .open =        spidev_open,
  12.     .release =    spidev_release,
  13.     .llseek =    no_llseek,
  14. };

        首先看下打开函数spidev_open,程序如下:

点击(此处)折叠或打开

  1. static int spidev_open(struct inode *inode, struct file *filp)
  2. {
  3.     struct spidev_data    *spidev;
  4.     int            status = -ENXIO;

  5.     mutex_lock(&device_list_lock);

  6.     list_for_each_entry(spidev, &device_list, device_entry) {
  7.         if (spidev->devt == inode->i_rdev) {
  8.             status = 0;
  9.             break;
  10.         }
  11.     }
  12.     if (status == 0) {
  13.         if (!spidev->buffer) {
  14.             spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);
  15.             if (!spidev->buffer) {
  16.                 dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
  17.                 status = -ENOMEM;
  18.             }
  19.         }
  20.         if (status == 0) {
  21.             spidev->users++;
  22.             filp->private_data = spidev;
  23.             nonseekable_open(inode, filp);
  24.         }
  25.     } else
  26.         pr_debug("spidev: nothing for minor %d\n", iminor(inode));

  27.     mutex_unlock(&device_list_lock);
  28.     return status;
  29. }

        说明:

        1) 程序首先上本地全局互斥锁。

        2) 遍历链表device_list,通过设备号找到在探测函数中创建的设备结构体spidev。

        3) 如果寻找成功,将其保存在file的私有成员中,供其他操作集中的函数使用。

        realease函数如下:

点击(此处)折叠或打开

  1. static int spidev_release(struct inode *inode, struct file *filp)
  2. {
  3.     struct spidev_data    *spidev;
  4.     int            status = 0;

  5.     mutex_lock(&device_list_lock);
  6.     spidev = filp->private_data;
  7.     filp->private_data = NULL;

  8.     /* last close? */
  9.     spidev->users--;
  10.     if (!spidev->users) {
  11.         int        dofree;

  12.         kfree(spidev->buffer);
  13.         spidev->buffer = NULL;

  14.         /* ... after we unbound from the underlying device? */
  15.         spin_lock_irq(&spidev->spi_lock);
  16.         dofree = (spidev->spi == NULL);
  17.         spin_unlock_irq(&spidev->spi_lock);

  18.         if (dofree)
  19.             kfree(spidev);
  20.     }
  21.     mutex_unlock(&device_list_lock);

  22.     return status;
  23. }

        说明:

        1) 首先减少使用次数。

        2) 释放传输buf空间。

        3) 释放结构体内存。

        spi读数据函数如下:

点击(此处)折叠或打开

  1. static inline ssize_t
  2. spidev_sync_read(struct spidev_data *spidev, size_t len)
  3. {
  4.     struct spi_transfer    t = {
  5.             .rx_buf        = spidev->buffer,
  6.             .len        = len,
  7.         };
  8.     struct spi_message    m;

  9.     spi_message_init(&m);
  10.     spi_message_add_tail(&t, &m);
  11.     return spidev_sync(spidev, &m);
  12. }
  13. static ssize_t
  14. spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
  15. {
  16.     struct spidev_data    *spidev;
  17.     ssize_t            status = 0;

  18.     /* chipselect only toggles at start or end of operation */
  19.     if (count > bufsiz)
  20.         return -EMSGSIZE;

  21.     spidev = filp->private_data;

  22.     mutex_lock(&spidev->buf_lock);
  23.     status = spidev_sync_read(spidev, count);
  24.     if (status > 0) {
  25.         unsigned long    missing;

  26.         missing = copy_to_user(buf, spidev->buffer, status);
  27.         if (missing == status)
  28.             status = -EFAULT;
  29.         else
  30.             status = status - missing;
  31.     }
  32.     mutex_unlock(&spidev->buf_lock);

  33.     return status;
  34. }

        说明:

        1) 进入读函数中,首先上互斥锁。

        2) 然后调用spidev_sync_read函数读数据。

        3) 如果读取成功,将读取到的数据拷贝到应用层。

        4) 解互斥锁。

        5) 在spidev_sync_read函数中,首先定义一个传输结构体spi_transfer,由于是读操作,所以只初始化其接收buf。

        6) 初始化spi_message,调用函数spidev_sync传输数据,其具体程序如下:

点击(此处)折叠或打开

  1. 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);
  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. }

        说明:

        1) 首先定义并初始化一个completion。

        2) 上自旋锁,调用spi_async函数传输数据,spi_async就是在Linux核心中提供的传输函数。

        3) 如果调用传输函数成功,则等待。

        3) wait_for_completion(&done);等待传输完成,如果传输正确,函数返回值为传输的字节数。

        函数操作集写函数spidev_write程序如下:

点击(此处)折叠或打开

  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;

  9.     spi_message_init(&m);
  10.     spi_message_add_tail(&t, &m);
  11.     return spidev_sync(spidev, &m);
  12. }
  13. static ssize_t
  14. spidev_write(struct file *filp, const char __user *buf,
  15.         size_t count, loff_t *f_pos)
  16. {
  17.     struct spidev_data    *spidev;
  18.     ssize_t            status = 0;
  19.     unsigned long        missing;

  20.     /* chipselect only toggles at start or end of operation */
  21.     if (count > bufsiz)
  22.         return -EMSGSIZE;

  23.     spidev = filp->private_data;

  24.     mutex_lock(&spidev->buf_lock);
  25.     missing = copy_from_user(spidev->buffer, buf, count);
  26.     if (missing == 0) {
  27.         status = spidev_sync_write(spidev, count);
  28.     } else
  29.         status = -EFAULT;
  30.     mutex_unlock(&spidev->buf_lock);

  31.     return status;
  32. }

        说明:

        1) 写函数首先上互斥锁,然后从应用层拷贝需要写的数据。

        2) 如果拷贝成功,调用函数spidev_sync_write完成写数据。

        3) 解互斥锁。

        4) spidev_sync_write函数中,首先定义spi_transfer传输结构体,由于是写,此结构体只有tx_buf。

        5) 初始化spi_message,然后调用spidev_sync传输数据,此函数在上面的读操作中已经讲述。

        ioctl函数如下:

点击(此处)折叠或打开

  1. static long
  2. spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
  3. {
  4.     int            err = 0;
  5.     int            retval = 0;
  6.     struct spidev_data    *spidev;
  7.     struct spi_device    *spi;
  8.     u32            tmp;
  9.     unsigned        n_ioc;
  10.     struct spi_ioc_transfer    *ioc;

  11.     /* Check type and command number */
  12.     if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)
  13.         return -ENOTTY;

  14.     /* Check access direction once here; don't repeat below.
  15.      * IOC_DIR is from the user perspective, while access_ok is
  16.      * from the kernel perspective; so they look reversed.
  17.      */
  18.     if (_IOC_DIR(cmd) & _IOC_READ)
  19.         err = !access_ok(VERIFY_WRITE,
  20.                 (void __user *)arg, _IOC_SIZE(cmd));
  21.     if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)
  22.         err = !access_ok(VERIFY_READ,
  23.                 (void __user *)arg, _IOC_SIZE(cmd));
  24.     if (err)
  25.         return -EFAULT;

  26.     /* guard against device removal before, or while,
  27.      * we issue this ioctl.
  28.      */
  29.     spidev = filp->private_data;
  30.     spin_lock_irq(&spidev->spi_lock);
  31.     spi = spi_dev_get(spidev->spi);
  32.     spin_unlock_irq(&spidev->spi_lock);

  33.     if (spi == NULL)
  34.         return -ESHUTDOWN;

  35.     /* use the buffer lock here for triple duty:
  36.      * - prevent I/O (from us) so calling spi_setup() is safe;
  37.      * - prevent concurrent SPI_IOC_WR_* from morphing
  38.      * data fields while SPI_IOC_RD_* reads them;
  39.      * - SPI_IOC_MESSAGE needs the buffer locked "normally".
  40.      */
  41.     mutex_lock(&spidev->buf_lock);

  42.     switch (cmd) {
  43.     /* read requests */
  44.     case SPI_IOC_RD_MODE:
  45.         retval = __put_user(spi->mode & SPI_MODE_MASK,
  46.                     (__u8 __user *)arg);
  47.         break;
  48.     case SPI_IOC_RD_LSB_FIRST:
  49.         retval = __put_user((spi->mode & SPI_LSB_FIRST) ? 1 : 0,
  50.                     (__u8 __user *)arg);
  51.         break;
  52.     case SPI_IOC_RD_BITS_PER_WORD:
  53.         retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);
  54.         break;
  55.     case SPI_IOC_RD_MAX_SPEED_HZ:
  56.         retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);
  57.         break;

  58.     /* write requests */
  59.     case SPI_IOC_WR_MODE:
  60.         retval = __get_user(tmp, (u8 __user *)arg);
  61.         if (retval == 0) {
  62.             u8    save = spi->mode;

  63.             if (tmp & ~SPI_MODE_MASK) {
  64.                 retval = -EINVAL;
  65.                 break;
  66.             }

  67.             tmp |= spi->mode & ~SPI_MODE_MASK;
  68.             spi->mode = (u8)tmp;
  69.             retval = spi_setup(spi);
  70.             if (retval < 0)
  71.                 spi->mode = save;
  72.             else
  73.                 dev_dbg(&spi->dev, "spi mode %02x\n", tmp);
  74.         }
  75.         break;
  76.     case SPI_IOC_WR_LSB_FIRST:
  77.         retval = __get_user(tmp, (__u8 __user *)arg);
  78.         if (retval == 0) {
  79.             u8    save = spi->mode;

  80.             if (tmp)
  81.                 spi->mode |= SPI_LSB_FIRST;
  82.             else
  83.                 spi->mode &= ~SPI_LSB_FIRST;
  84.             retval = spi_setup(spi);
  85.             if (retval < 0)
  86.                 spi->mode = save;
  87.             else
  88.                 dev_dbg(&spi->dev, "%csb first\n",
  89.                         tmp ? 'l' : 'm');
  90.         }
  91.         break;
  92.     case SPI_IOC_WR_BITS_PER_WORD:
  93.         retval = __get_user(tmp, (__u8 __user *)arg);
  94.         if (retval == 0) {
  95.             u8    save = spi->bits_per_word;

  96.             spi->bits_per_word = tmp;
  97.             retval = spi_setup(spi);
  98.             if (retval < 0)
  99.                 spi->bits_per_word = save;
  100.             else
  101.                 dev_dbg(&spi->dev, "%d bits per word\n", tmp);
  102.         }
  103.         break;
  104.     case SPI_IOC_WR_MAX_SPEED_HZ:
  105.         retval = __get_user(tmp, (__u32 __user *)arg);
  106.         if (retval == 0) {
  107.             u32    save = spi->max_speed_hz;

  108.             spi->max_speed_hz = tmp;
  109.             retval = spi_setup(spi);
  110.             if (retval < 0)
  111.                 spi->max_speed_hz = save;
  112.             else
  113.                 dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
  114.         }
  115.         break;

  116.     default:
  117.         /* segmented and/or full-duplex I/O request */
  118.         if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))
  119.                 || _IOC_DIR(cmd) != _IOC_WRITE) {
  120.             retval = -ENOTTY;
  121.             break;
  122.         }

  123.         tmp = _IOC_SIZE(cmd);
  124.         if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {
  125.             retval = -EINVAL;
  126.             break;
  127.         }
  128.         n_ioc = tmp / sizeof(struct spi_ioc_transfer);
  129.         if (n_ioc == 0)
  130.             break;

  131.         /* copy into scratch area */
  132.         ioc = kmalloc(tmp, GFP_KERNEL);
  133.         if (!ioc) {
  134.             retval = -ENOMEM;
  135.             break;
  136.         }
  137.         if (__copy_from_user(ioc, (void __user *)arg, tmp)) {
  138.             kfree(ioc);
  139.             retval = -EFAULT;
  140.             break;
  141.         }

  142.         /* translate to spi_message, execute */
  143.         retval = spidev_message(spidev, ioc, n_ioc);
  144.         kfree(ioc);
  145.         break;
  146.     }

  147.     mutex_unlock(&spidev->buf_lock);
  148.     spi_dev_put(spi);
  149.     return retval;
  150. }

        说明:

        1) 函数首先判断命令是否有效,如果无效,直接退出。

        2) 上互斥锁。

        3) switch分支中,前面的case都是支持对SPI设备的设置,包括模式、传输位、位方向和最大速率等。

        4) 在设置分支中,最后调用spi_setup实现设置,此函数最终是调用总线驱动中的gsc3280_spi_setup(struct spi_device *spi)

实现设置。

        5) default分支中是进行数据传输的分支,首先判断是否是有效的数据,如果不是,退出switch分支。

        6) 申请内存,复制从应用层传过来的数据。

        7) 调用spidev_message函数实现数据传输。

        8) 解互斥锁,退出。

        spidev_message函数如下:

点击(此处)折叠或打开

  1. static int spidev_message(struct spidev_data *spidev,
  2.         struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
  3. {
  4.     struct spi_message    msg;
  5.     struct spi_transfer    *k_xfers;
  6.     struct spi_transfer    *k_tmp;
  7.     struct spi_ioc_transfer *u_tmp;
  8.     unsigned        n, total;
  9.     u8            *buf;
  10.     int            status = -EFAULT;

  11.     spi_message_init(&msg);
  12.     k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);
  13.     if (k_xfers == NULL)
  14.         return -ENOMEM;

  15.     /* Construct spi_message, copying any tx data to bounce buffer.
  16.      * We walk the array of user-provided transfers, using each one
  17.      * to initialize a kernel version of the same transfer.
  18.      */
  19.     buf = spidev->buffer;
  20.     total = 0;
  21.     for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
  22.             n;
  23.             n--, k_tmp++, u_tmp++) {
  24.         k_tmp->len = u_tmp->len;

  25.         total += k_tmp->len;
  26.         if (total > bufsiz) {
  27.             status = -EMSGSIZE;
  28.             goto done;
  29.         }

  30.         if (u_tmp->rx_buf) {
  31.             k_tmp->rx_buf = buf;
  32.             if (!access_ok(VERIFY_WRITE, (u8 __user *)
  33.                         (uintptr_t) u_tmp->rx_buf,
  34.                         u_tmp->len))
  35.                 goto done;
  36.         }
  37.         if (u_tmp->tx_buf) {
  38.             k_tmp->tx_buf = buf;
  39.             if (copy_from_user(buf, (const u8 __user *)
  40.                         (uintptr_t) u_tmp->tx_buf,
  41.                     u_tmp->len))
  42.                 goto done;
  43.         }
  44.         buf += k_tmp->len;

  45.         k_tmp->cs_change = !!u_tmp->cs_change;
  46.         k_tmp->bits_per_word = u_tmp->bits_per_word;
  47.         k_tmp->delay_usecs = u_tmp->delay_usecs;
  48.         k_tmp->speed_hz = u_tmp->speed_hz;
  49. #ifdef VERBOSE
  50.         dev_dbg(&spidev->spi->dev,
  51.             " xfer len %zd %s%s%s%dbits %u usec %uHz\n",
  52.             u_tmp->len,
  53.             u_tmp->rx_buf ? "rx " : "",
  54.             u_tmp->tx_buf ? "tx " : "",
  55.             u_tmp->cs_change ? "cs " : "",
  56.             u_tmp->bits_per_word ? : spidev->spi->bits_per_word,
  57.             u_tmp->delay_usecs,
  58.             u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
  59. #endif
  60.         spi_message_add_tail(k_tmp, &msg);
  61.     }

  62.     status = spidev_sync(spidev, &msg);
  63.     if (status < 0)
  64.         goto done;

  65.     /* copy any rx data out of bounce buffer */
  66.     buf = spidev->buffer;
  67.     for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
  68.         if (u_tmp->rx_buf) {
  69.             if (__copy_to_user((u8 __user *)
  70.                     (uintptr_t) u_tmp->rx_buf, buf,
  71.                     u_tmp->len)) {
  72.                 status = -EFAULT;
  73.                 goto done;
  74.             }
  75.         }
  76.         buf += u_tmp->len;
  77.     }
  78.     status = total;

  79. done:
  80.     kfree(k_xfers);
  81.     return status;
  82. }

        说明:

        1) 申请传输的结构体内存。

        2) 通过for循环,对spi_ioc_transfer类型的数据进行转换。

        3) 转换中,首先对本次传输的数据大小进行累计,如果总传输量超出buf的大小,直接退出。

        4) 如果本次传输是接收,则设置接收数组,并对buf进行检查,查看是否可读。

        5) 如果本次传输是写,则从应用层拷贝数据。

        6) 对传输中参数进行赋值,包括速度、模式、速率等等。

        7) 调用spidev_sync进行数据传输。

        8) 将接收数据拷贝到应用层。

三、应用层

        在应用层提供了二中驱动的测试程序,在“/documenation/spi”目录下,文件名称为spidev_test.c中,具体程序如下:

点击(此处)折叠或打开

  1. int main(int argc, char *argv[])
  2. {
  3.     int ret = 0;
  4.     int fd;

  5.     parse_opts(argc, argv);

  6.     fd = open(device, O_RDWR);
  7.     if (fd < 0)
  8.         pabort("can't open device");

  9.     /*
  10.      * spi mode
  11.      */
  12.     ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
  13.     if (ret == -1)
  14.         pabort("can't set spi mode");

  15.     ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
  16.     if (ret == -1)
  17.         pabort("can't get spi mode");

  18.     /*
  19.      * bits per word
  20.      */
  21.     ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
  22.     if (ret == -1)
  23.         pabort("can't set bits per word");

  24.     ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
  25.     if (ret == -1)
  26.         pabort("can't get bits per word");

  27.     /*
  28.      * max speed hz
  29.      */
  30.     ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
  31.     if (ret == -1)
  32.         pabort("can't set max speed hz");

  33.     ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
  34.     if (ret == -1)
  35.         pabort("can't get max speed hz");

  36.     printf("spi mode: %d\n", mode);
  37.     printf("bits per word: %d\n", bits);
  38.     printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);

  39.     transfer(fd);

  40.     close(fd);

  41.     return ret;
  42. }

        说明:

        1) 首先打开设备。

        2) 然后设置工作模式、位大小和速率等。

        3) 传输数据

From:http://blog.chinaunix.net/uid-25445243-id-4059262.html

相关文章推荐

Linux spi驱动分析(三)----spiddev分析

转自:http://blog.chinaunix.net/uid-25445243-id-4059262.html 一、spidev简单介绍         如果在内核中配置spi...

linux spi驱动分析整理

1、SPI总线:        SPI(同步外设接口)是由摩托罗拉公司开发的全双工同步串行总线,其接口由 MISO(串行数据输入),MOSI(串行数据输出),SCK(串行移位时钟),SS/CS(...

linux spi子系统驱动分析

转自:http://blog.chinaunix.net/uid-20620288-id-3161198.html 2.6.18内核下已经添加了完整的spi子系统了,参考mtd的分析,将从下...

linux内核SPI总线驱动分析(一)

下面有两个大的模块: 一个是SPI总线驱动的分析            (研究了具体实现的过程) 另一个是SPI总线驱动的编写(不用研究具体的实现过程) SPI总线驱动分析 &...

linux spi驱动分析

关于spi的学习,我觉得最好的方法还是看Linux的源代码,主要是driver/spi/spi.c(h),spidev.c(h)。spi dev的示例可以看看at25.c,spi总线的示例可以看oma...

Linux下SPI驱动分析(4)

接下来继续看SPI主机注册、删除方法。 --------------------------------------------- 主机设备删除、添加方法 /* 又见神奇的分割线 */ 43...

linux spi驱动分析

本博客转载于:http://blog.chinaunix.net/uid-23036581-id-2230553.html 关于spi的学习,我觉得最好的方法还是看Linux的源代码,主要是d...

Linux下SPI总线驱动分析

Linux下SPI总线驱动有通用接口,一般的SPI设备驱动使用这个驱动接口实现设备驱动。分析驱动最好是先了解核心代码,然后从具体设备分析入手,然后从下至上,了解整个框架,再从上到下分析,理解透彻。 ...

Linux下SPI驱动分析

Linux下SPI总线驱动有通用接口,一般的SPI设备驱动使用这个驱动接口实现设备驱动。分析驱动最好是先了解核心代码,然后从具体设备分析入手,然后从下至上,了解整个框架,再从上到下分析,理解透彻。以下...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)