第一部分,将对SPI子系统整体进行描述,同时给出SPI的相关数据结构,最后描述SPI总线的注册。
第二部分,该文将对SPI的主控制器(master)驱动进行描述。
第三部分,即本篇文章,该文将对SPI设备驱动,也称protocol 驱动,进行讲解。
第四部分,通过SPI设备驱动留给用户层的API,我们将从上到下描述数据是如何通过SPI的protocol 驱动,由bitbang中转,最后由master驱动将数据传输出去。
本文属于第三部分。
5. SPI设备驱动
在主控制器驱动中,spi_device已经注册了,在设备驱动中,首先要做的就是注册spi_driver,并提供用户层相应的API。
5.1 SPI设备驱动的注册
SPI设备驱动的定义、注册、以及卸载,所在位置/kernel3.0/driver/spi/spidev.c
static const struct file_operations spidev_fops = { .owner = THIS_MODULE, .open = spidev_open, .write = spidev_write, .read = spidev_read, .release = spidev_release, .llseek = no_llseek, .unlocked_ioctl = spidev_ioctl, .compat_ioctl = spidev_compat_ioctl, }; static struct spi_driver spidev_spi_driver = { .driver = { .name = "spidev", //spi驱动和设备匹配的标识 .owner = THIS_MODULE, }, .probe = spidev_probe, .remove = __devexit_p(spidev_remove), }; static int __init spidev_init(void) { int status; BUILD_BUG_ON(N_SPI_MINORS > 256); //检查次设备号 status = register_chrdev(SPIDEV_MAJOR,"spi",&spidev_fops); if(status < 0) //注册字符设备(可以在硬件的/proc/devices目录下查看) return status; spidev_class = class_create(THIS_MODULE,"spidev"); if(IS_ERR(spidev_class)) //创建spidev类(可以在/sys/bus/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) //spi设备驱动的注册,深入追踪可以发现其调用了spidev_probe函数 { class_destroy(spidev_class); unregister_chrdev(SPIDEV_MAJOR,spidev_spi_driver.driver.name); } return status; }; module_init(spidev_init); static void __exit spidev_exit(void) { spi_unregister_driver(&spidev_spi_driver); class_destroy(spidev_class); unregister_chrdev(SPIDEV_MAJOR,spidev_spi_driver.driver.name); }; module_exit(spidev_exit);
在这里我们看到了SPI设备驱动是如何提供API给用户层的,那就是通过再熟悉不过的字符设备。通过字符设备,给用户层提供了5个API:open,release,write,read和ioctl。本文在后面将介绍open和close,剩余3个将在本系列的第四篇文章中介绍。
1、spi_register_driver函数, 该函数位于/kernel3.0/drivers/spi/spidev.c。
int spi_register_driver(struct spi_driver *sdrv) { sdrv->driver.bus = &spi_bus_type; if(sdrv->probe) //driver下的函数赋值 sdrv->driver.probe = spi_drv_probe; if(sdrv->remove) sdrv->driver.remove = spi_drv_remove; if(sdrv->shutdown) sdrv->driver.shutdown = spi_drv_shutdown; return driver_register(&sdrv->driver); } EXPORT_SYMBOL_GPL(spi_register_driver);
int driver_register(struct device_driver *drv) { int ret; struct device_driver *other; BUG_ON(!drv->bus->p); if((drv->bus->probe && drv->probe) || (drv->bus->remove && drv->remove) || (drv->bus->shutdown && drv->shutdown)) printk(KERN_WARNING "Driver '%s' needs updating -please use" "bus_type methods\n",drv->name); other = driver_find(drv->name,drv->bus); if(other) { put_driver(other); printk(KERN_ERR "Error: Driver '%s' is already registered, " "aborting...\n", drv->name); return -EBUSY; } ret = bus_add_driver(drv); if(ret) return ret; ret = driver_add_groups(drv,drv->groups); if(ret) bus_remove_driver(drv); return ret; }; EXPORT_SYMBOL_GPL(driver_register);
3、bus_add_driver函数,该函数位于/kernel3.0/drivers/base/bus.c。
int bus_add_driver(struct device_driver *drv) { struct bus_type *bus; struct driver_private *priv; int error = 0; bus = bus_get(drv->bus); //获取驱动对应的bus总线 if(!bus) return -EINVAL; pr_debug("bus: '%s':add driver %s\n",bus->name,drv->name); priv = kalloc(sizeof(*priv),GFP_KERNEL); if(!priv) { error = -ENOMEM; goto out_put_bus; } klist_init(&priv->klist_devices,NULL,NULL); priv->driver = drv; drv->p = priv; priv->kobj.kset = bus->p->drivers_kset; error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, "%s", drv->name); if (error) goto out_unregister; if (drv->bus->p->drivers_autoprobe) { error = driver_attach(drv); if (error) goto out_unregister; } klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); module_add_driver(drv->owner, drv); error = driver_create_file(drv, &driver_attr_uevent); if (error) { printk(KERN_ERR "%s: uevent attr (%s) failed\n", __func__, drv->name); } error = driver_add_attrs(bus, drv); if (error) { /* How the hell do we get out of this pickle? Give up */ printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n", __func__, drv->name); } if (!drv->suppress_bind_attrs) { error = add_bind_files(drv); if (error) { /* Ditto */ printk(KERN_ERR "%s: add_bind_files(%s) failed\n", __func__, drv->name); } } kobject_uevent(&priv->kobj, KOBJ_ADD); return 0; out_unregister: kobject_put(&priv->kobj); kfree(drv->p); drv->p = NULL; out_put_bus: bus_put(bus); return error; }
int driver_attach(struct device_driver *dev) { return bus_for_each_dev(dev->bus,NULL,drv,__driver_attach); } EXPORT_SYMBOL_GPL(driver_attach); /*往下追踪bus_for_each_dev函数,该函数位于/kernel3.0/drivers/base/bus.c*/ int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *)) { struct klist_iter i; struct device *dev; int error = 0; if (!bus) return -EINVAL; klist_iter_init_node(&bus->p->klist_devices, &i, (start ? &start->p->knode_bus : NULL)); while ((dev = next_device(&i)) && !error) error = fn(dev, data); //通过上一个函数,可以发现fn就是传进来的__driver_attach函数, klist_iter_exit(&i); //fn(dev,data) 也就是__driver_attach(dev,data) return error; } EXPORT_SYMBOL_GPL(bus_for_each_dev); /*往下追踪__driver_attach函数,该函数位于/kernel3.0/drivers/base/dd.c*/ static int __driver_attach(struct device *dev, void *data) { struct device_driver *drv = data; /* * Lock device and try to bind to it. We drop the error * here and always return 0, because we need to keep trying * to bind to devices and some drivers will return an error * simply if it didn't support the device. * * driver_probe_device() will spit a warning if there * is an error. */ //在这里进行了驱动和设备的匹配 if (!driver_match_device(drv, dev)) //匹配(重点) return 0; if (dev->parent) /* Needed for USB */ device_lock(dev->parent); device_lock(dev); if (!dev->driver) driver_probe_device(drv, dev); //probe(重点) device_unlock(dev); if (dev->parent) device_unlock(dev->parent); return 0; } /*往下追踪driver_match_device函数,看看该函数是如何匹配的,*/ /*该函数位于/kernel3.0/drivers/base/base.h */ static inline int driver_match_device(struct device_driver *drv, struct device *dev) { //dri->bus->match = spi_match_device(该函数位于/kernel3.0/drivers/spi/spi.c里面) //到此为止spi驱动和设备进行了匹配 return drv->bus->match ? drv->bus->match(dev, drv) : 1; } /*往下追踪driver_probe_device函数,看该函数是如何调用spi设备驱动的probe函数,*/ /*该函数位于/kernel3.0/drivers/base/dd.c*/ int driver_probe_device(struct device_driver *drv, struct device *dev) { int ret = 0; if (!device_is_registered(dev)) return -ENODEV; pr_debug("bus: '%s': %s: matched device %s with driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); pm_runtime_get_noresume(dev); pm_runtime_barrier(dev); ret = really_probe(dev, drv); //(重点) pm_runtime_put_sync(dev); return ret; } /*往下追踪really_probe函数,在该函数中调用了SPI设备里面的probe函数,*/ /*该函数位于/kernel3.0/drivers/base/dd.c*/ static int really_probe(struct device *dev, struct device_driver *drv) { int ret = 0; atomic_inc(&probe_count); pr_debug("bus: '%s': %s: probing driver %s with device %s\n", drv->bus->name, __func__, drv->name, dev_name(dev)); WARN_ON(!list_empty(&dev->devres_head)); dev->driver = drv; if (driver_sysfs_add(dev)) { printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n", __func__, dev_name(dev)); goto probe_failed; } if (dev->bus->probe) { //在这里进行了probe函数的调用 ret = dev->bus->probe(dev); if (ret) goto probe_failed; } else if (drv->probe) { ret = drv->probe(dev); if (ret) goto probe_failed; } driver_bound(dev); ret = 1; pr_debug("bus: '%s': %s: bound device %s to driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); goto done; probe_failed: devres_release_all(dev); driver_sysfs_remove(dev); dev->driver = NULL; if (ret != -ENODEV && ret != -ENXIO) { /* driver matched but the probe failed */ printk(KERN_WARNING "%s: probe of %s failed with error %d\n", drv->name, dev_name(dev), ret); } /* * Ignore errors returned by ->probe so that the next driver can try * its luck. */ ret = 0; done: atomic_dec(&probe_count); wake_up(&probe_waitqueue); return ret; }vi者相等
5.2 SPI驱动函数分析
1、spidev_probe函数,该函数位于/kernel3.0/drivers/spi/spidev.c。
static int __devinit spidev_probe(struct spi_device *spi) { struct spidev_data *spidev; int status; unsigned long minor; /*申请一个spidev_data结构体空间*/ spidev = kzalloc(sizeof(*spidev),GFP_KERNEL); if(!spidev) return -ENOMEM; /*初始化spidev结构体元素*/ spidev->spi = spi; //保存spi_device spin_lock_init(&spidev->spi_lock); //初始化自旋锁 mutex_init(&spidev->buf_lock); //初始化互斥体 INIT_LIST_HEAD(&spidev->device_entry); //初始化双向循环链表头 mutex_lock(&device_list_lock); //上锁 /*在一个unsigned long类型的变量的位图中找到其第一个为0位的所在的位置即为minor(次设备号)的值*/ minor = find_first_zero_bit(minors,N_SPI_MINORS); if(minor < N_SPI_MINORS) { struct device *dev; spidev->devt = MKDEV(SPIDEV_MAJOR,minor); //根据主次设备号获取设备号 /*在/dev下面创建spidev设备节点*/ dev = device_create(spidev_class,&spi->dev,spidev->devt, spidev,"spidev%d.%d",spi->master->bus_num,spi->chip_select); status = IS_ERR(dev) ? PTR_ERR(dev) : 0; } else { dev_dbg(&spi->dev,"no minor number available!\n"); status = -ENODEV; } if(status == 0) { set_bit(minor,minors); //保存已使用的次设备号 list_add(&spidev->device_entry,&device_list); //在链表头list后面添加spidev条目 } mutex_unlock(&device_list_lock); //解锁 if(status == 0) spi_set_drvdata(spi,spidev); else kfree(spidev); return status; }
static int __devexit spidev_remove(struct spi_device *spi) { struct spidev_data *spidev = spi_get_drvdata(spi); spin_lock_irq(&spidev->spi_lock); spidev->spi = NULL; spi_set_drvdata(spi,NULL); spin_unlock_irq(&spidev->spi_lock); mutex_lock(&device_list_lock); //上锁 list_del(&spidev->device_entry); //将链表里的device_entry删除 device_destroy(spidev_class,spidev->devt); //销毁/dev下的设备节点 clear_bit(MINOR(spidev->devt),minors); //将位图中申请的次设备号的位置置0 if(spidev->users == 0) kfree(spidev); mutex_unlock(&device_list_lock); //解锁 return 0; }
static int spidev_open(struct *inode,struct file *filp) { struct spidev_data *spidev; int status = -ENXIO; mutex_lock(&device_list_lock); //上锁(多个程序调用open方法,但他们共享一个buffer, // 因此对buufer需要进行互斥保护) //从list头开始遍历entry,即遍历所有的spidev list_for_each_entry(spidev,&device_list,device_entry) { if(spidev->devt == inode->i_rdev) //判断设备号是否相等 { status = 0; //找到匹配的spi设备 break; } } if(status == 0) { if(!spidev->buffer) { //分配buffer缓冲区,默认4KB spidev->buffer = kmalloc(bufsiz,GFP_KERNEL); if(!spidev->buffer) { dev_dbg(&spidev->spi->dev,"open/ENOMEX\n"); status = -ENOMEME; } } if(status == 0) { spidev->users++; //成功open后,用户增加 filp->private_data = spidev; //将spidev保存到文件的私有指针里面 nonseekable_open(inode,filp); //禁用lseek } } else { pr_debug("spidev:nothing for minor %d\n",iminor(inode)); } mutex_unlock(&device_list_lock); //解锁 return status; }
在这里,以device_list为链表头,遍历所有的spidev_data结构,通过设备节点的设备号和spidev_data中保存的设备号进行匹配,来找到属于该设备节点的spi设备。随后,分配了spi设备驱动层所使用的缓冲区,最后增加打开计数。
4、spidev_release函数,该函数位于/kernel3.0/drivers/spi/spidev.c。
static int spidev_release(struct inode *inode,struct file *filp) { struct spidev_data *spidev; int status = 0; mutex_lock(&device_list_lock); //上锁 spidev = filp->private_data; //将文件保存的数据给spidev filp->private_data = NULL; //清空文件的私有数据 spidev->users--; //用户减一 if(!spidev->users) //如果用户为0 { int dofree; kfree(spidev->buffer); //释放缓冲区 spidev->buffer = NULL; spin_lock_irq(&spidev->spi_lock); dofree = (spidev->spi == NULL); spin_unlock_irq(&spidev->spi_lock); if(dofree) kfree(spidev); //释放spidev,在probe中申请的 } mutex_unlock(&device_list_lock); //解锁 return status; }