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的。我们下次继续分析。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 wps逗号隔的空不一样大怎么办 电脑盘里的文件删不了怎么办 在淘宝买的qq账号被找回了怎么办 微信公众号的密码忘了怎么办 公众号安全助手密码忘了怎么办 微博账号存在发布违规信息怎么办 余额宝转出到银行卡被冻结怎么办 银行账户被冻结被转出钱怎么办 从余额宝转出的资金被冻结怎么办 微信账号卖了但是实名认证了怎么办 uc下载文档里的文档全没了怎么办 二手乐视没有账号和密码怎么办 华为账号密码忘了手机卡丢了怎么办 联想平板微信更新后不可兼容怎么办 小米手机刷完机账号密码忘了怎么办 红米手机的小米账号密码忘了怎么办 小米手机账号密码手机号忘了怎么办 小米手机忘了账号和密码怎么办 自己的小米账号密码忘了怎么办 小米手机丢了不记得小米账号怎么办 小米手环账号密码忘了怎么办 阴阳师一个区的账号找不到了怎么办 阴阳师手机账号代练登录了后怎么办 我的微信账号被盗更改密码了怎么办 vivo手机密保密码忘了怎么办 华为手机保密柜忘记密保问题怎么办 支付宝账号突然说没有了怎么办 快手号密码可能被盗登不上该怎么办 海岛奇兵小米版换手机了怎么办 申诉找回微信密码验证吗错误怎么办 微信密码忘了申诉不成功怎么办 微信密码忘了申诉不了怎么办 微信密码忘了申诉不回来怎么办 微信号密码忘了申诉失败怎么办 手机微信密码忘了无需申诉怎么办 安全守护2手机绑定密码错误怎么办 新办的手机号注册过魅族账号怎么办 百度网盘手机找回被别人关了怎么办 把朋友微信号弄没了怎么办 小米顶配版手机无线网速度慢怎么办 刺激战场用过模拟器后用手机怎么办