第一部分,将对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;
}