blackberrypi模块spi驱动分析(1)
来源:互联网 发布:北京粉尘数据 编辑:程序博客网 时间:2024/06/03 23:43
下面我们将分析spidev是如何处理ioctl发送的spi消息的。blackberry的kernel可以从https://github.com/raspberrypi/linux获得。
git initgit fetch git://github.com/raspberrypi/linux.git rpi-3.6.y:refs/remotes/origin/rpi-3.6.ygit checkout rpi-3.6.y如果你自己编译内核,可一参考这个篇文章http://elinux.org/RPi_Kernel_Compilation。
raspbian的spi驱动由两个模块组成,一个是spidev, 另一个是bcm2708-spi。spidev依赖bcm2708-spi提供的功能注册一个字符设备,提供类似oopen, write, ioctl的操作
static const struct file_operations spidev_fops = { .owner = THIS_MODULE, /* REVISIT switch to aio primitives, so that userspace * gets more complete API coverage. It'll simplify things * too, except for the locking. */ .write = spidev_write, .read = spidev_read, .unlocked_ioctl = spidev_ioctl, .compat_ioctl = spidev_compat_ioctl, .open = spidev_open, .release = spidev_release, .llseek = no_llseek,};
spi驱动主要涉及一下几个文件
<linux-source>/linux/include/spi/spi.h<linux-source>/drivers/spi/spidev.c
<linux-source>/drivers/spi/spi.c
<linux-source>/drivers/spi/bcm2708-spi.c
<linux-source>/arch/arm/mach-bcm2708/bcm2708.c
主要涉及的结构有
spidev_data
spi_master
spi_device
下面我们通过跟踪一个ioctl message怎么从spidev传递到bcm2708-spi来看spi驱动是如何工作的。我们先看怎么样向blackberrypi的spi设备发送消息。
当spidev初次装载时,内核会我们调用spidev_init
static int __init spidev_init(void){int status;/* Claim our 256 reserved device numbers. Then register a class * that will key udev/mdev to add/remove /dev nodes. Last, register * the driver which manages those device numbers. */BUILD_BUG_ON(N_SPI_MINORS > 256);status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);if (status < 0)return status;spidev_class = class_create(THIS_MODULE, "spidev");if (IS_ERR(spidev_class)) {unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);return PTR_ERR(spidev_class);}status = spi_register_driver(&spidev_spi_driver);if (status < 0) {class_destroy(spidev_class);unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);}return status;}
spidev在初始化时先注册一个字符设备,再创建一个叫spidev的class,然后调用spi_register_driver在spi_bus_type上注册一个驱动程序。当我们打开这个字符设备时,就会调用spidev_open,spidev_open会为当前device寻找一个spidev_data结构体,这个结构体是在驱动的spidev_probe被调用时创建的。
static int spidev_open(struct inode *inode, struct file *filp){struct spidev_data*spidev;intstatus = -ENXIO;mutex_lock(&device_list_lock);list_for_each_entry(spidev, &device_list, device_entry) {if (spidev->devt == inode->i_rdev) {status = 0;break;}}if (status == 0) {if (!spidev->buffer) {spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);if (!spidev->buffer) {dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");status = -ENOMEM;}}if (status == 0) {spidev->users++;filp->private_data = spidev;nonseekable_open(inode, filp);}} elsepr_debug("spidev: nothing for minor %d\n", iminor(inode));mutex_unlock(&device_list_lock);return status;}
这个spidev_data其实是用来存放ioctl消息的一个缓存,当用户调用ioctl时同时传递的消息就会被拷贝到这个内存空间里。spidev_data中的buffer就是这个缓存空间。
struct spidev_data {dev_tdevt;spinlock_tspi_lock;struct spi_device*spi;struct list_headdevice_entry;/* buffer is NULL unless this device is open (users > 0) */struct mutexbuf_lock;unsignedusers;u8*buffer;};
用户使用ioctl向spi发送或读取消息,最终就是调用spidev_ioctl。我们这里省去了设备设置相关的ioctl命令,只关注消息的发送和接受,也就是switch的default部分。spi_ioctl分配了一个spi_message对象,将该消息对象和用户给出的spi_ioc_transfer传给spidev_message函数。
static longspidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){interr = 0;intretval = 0;struct spidev_data*spidev;struct spi_device*spi;u32tmp;unsignedn_ioc;struct spi_ioc_transfer*ioc;/* Check type and command number */if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)return -ENOTTY;/* Check access direction once here; don't repeat below. * IOC_DIR is from the user perspective, while access_ok is * from the kernel perspective; so they look reversed. */if (_IOC_DIR(cmd) & _IOC_READ)err = !access_ok(VERIFY_WRITE,(void __user *)arg, _IOC_SIZE(cmd));if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)err = !access_ok(VERIFY_READ,(void __user *)arg, _IOC_SIZE(cmd));if (err)return -EFAULT;/* guard against device removal before, or while, * we issue this ioctl. */spidev = filp->private_data;spin_lock_irq(&spidev->spi_lock);spi = spi_dev_get(spidev->spi);spin_unlock_irq(&spidev->spi_lock);if (spi == NULL)return -ESHUTDOWN;/* use the buffer lock here for triple duty: * - prevent I/O (from us) so calling spi_setup() is safe; * - prevent concurrent SPI_IOC_WR_* from morphing * data fields while SPI_IOC_RD_* reads them; * - SPI_IOC_MESSAGE needs the buffer locked "normally". */mutex_lock(&spidev->buf_lock);switch (cmd) { <这里略去了和设置spi相关的cmd>default:/* segmented and/or full-duplex I/O request */if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))|| _IOC_DIR(cmd) != _IOC_WRITE) {retval = -ENOTTY;break;}tmp = _IOC_SIZE(cmd);if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {retval = -EINVAL;break;}n_ioc = tmp / sizeof(struct spi_ioc_transfer);if (n_ioc == 0)break;/* copy into scratch area */ioc = kmalloc(tmp, GFP_KERNEL);if (!ioc) {retval = -ENOMEM;break;}if (__copy_from_user(ioc, (void __user *)arg, tmp)) {kfree(ioc);retval = -EFAULT;break;}/* translate to spi_message, execute */retval = spidev_message(spidev, ioc, n_ioc);kfree(ioc);break;}mutex_unlock(&spidev->buf_lock);spi_dev_put(spi);return retval;}下面再看spidev_message继续做什么处理。spidev_message将用户空间的消息内容spi_ioc_transfer::tx_buf拷贝到spidev_data::buffer指向的内核空间中,并将spi_ioc_transfer转换成spi_transfer,并将spi_transfer一个一个地加入到spi_message的一个list成员transfers里。然后再调用spidev_sync()。等spidev_sync返回后,又将spidev_data::buf的内容拷贝回spi_ioc_transfer::rx_buf。如果我们没有猜错spidev_sync肯定是通知一个workqueue去处理spi_message中的内容。
static int spidev_message(struct spidev_data *spidev,struct spi_ioc_transfer *u_xfers, unsigned n_xfers){struct spi_messagemsg;struct spi_transfer*k_xfers;struct spi_transfer*k_tmp;struct spi_ioc_transfer *u_tmp;unsignedn, total;u8*buf;intstatus = -EFAULT;spi_message_init(&msg);k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);if (k_xfers == NULL)return -ENOMEM;/* Construct spi_message, copying any tx data to bounce buffer. * We walk the array of user-provided transfers, using each one * to initialize a kernel version of the same transfer. */buf = spidev->buffer;total = 0;for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;n;n--, k_tmp++, u_tmp++) {k_tmp->len = u_tmp->len;total += k_tmp->len;if (total > bufsiz) {status = -EMSGSIZE;goto done;}if (u_tmp->rx_buf) {k_tmp->rx_buf = buf;if (!access_ok(VERIFY_WRITE, (u8 __user *)(uintptr_t) u_tmp->rx_buf,u_tmp->len))goto done;}if (u_tmp->tx_buf) {k_tmp->tx_buf = buf;if (copy_from_user(buf, (const u8 __user *)(uintptr_t) u_tmp->tx_buf,u_tmp->len))goto done;}buf += k_tmp->len;k_tmp->cs_change = !!u_tmp->cs_change;k_tmp->bits_per_word = u_tmp->bits_per_word;k_tmp->delay_usecs = u_tmp->delay_usecs;k_tmp->speed_hz = u_tmp->speed_hz;#ifdef VERBOSEdev_dbg(&spidev->spi->dev," xfer len %zd %s%s%s%dbits %u usec %uHz\n",u_tmp->len,u_tmp->rx_buf ? "rx " : "",u_tmp->tx_buf ? "tx " : "",u_tmp->cs_change ? "cs " : "",u_tmp->bits_per_word ? : spidev->spi->bits_per_word,u_tmp->delay_usecs,u_tmp->speed_hz ? : spidev->spi->max_speed_hz);#endifspi_message_add_tail(k_tmp, &msg);}status = spidev_sync(spidev, &msg);if (status < 0)goto done;/* copy any rx data out of bounce buffer */buf = spidev->buffer;for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {if (u_tmp->rx_buf) {if (__copy_to_user((u8 __user *)(uintptr_t) u_tmp->rx_buf, buf,u_tmp->len)) {status = -EFAULT;goto done;}}buf += u_tmp->len;}status = total;done:kfree(k_xfers);return status;}以上就是使用ioctl发送spi消息时的大体流程。我们还有一个迷没有解开,就是spidev_sync是怎么处理spi_message的。我们下次继续分析。
- blackberrypi模块spi驱动分析(1)
- SPI子系统分析之四:驱动模块
- SPI子系统分析之三:驱动模块
- Linux下SPI驱动分析(1)
- FL2440开发板spi驱动分析(1)
- inux spi子系统驱动分析(续 )
- spi flash驱动代码分析(一)
- spi flash驱动代码分析(二)
- SPI总线(二):驱动分析篇
- linux spi驱动分析
- SPI驱动分析
- linux spi驱动分析
- linux spi驱动分析
- SPI驱动初步分析
- spi驱动分析
- linux spi驱动分析
- linux spi驱动分析
- SPI总线驱动分析
- webservice soapHeader
- EOF是什么?
- 黑马程序员-笔记--网络编程
- 黑马程序员-笔记-反射与内省
- getProperty 参数大全
- blackberrypi模块spi驱动分析(1)
- Code Contracts - Precondition Failed
- 黑马程序员-笔记-泛型
- ThreadLocal
- Android手机做电脑摄像头
- loadrunner--脚本的参数化
- computer专业术语总结
- 蓝桥杯-买不到的数目
- 编辑器使用心得