嵌入式Linux驱动——SPI子系统解读(三)

来源:互联网 发布:yandex优化怎么做 编辑:程序博客网 时间:2024/05/17 23:42

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

该函数中,创建了一个字符设备以提供API给用户层,同时创建了一个spidev类,最后注册spi_driver到内核中。

在这里我们看到了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);        
         2、driver_register函数,该函数位于/kernel3.0/drivers/base/driver.c。

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;}
         4、driver_attech函数,该函数位于/kernel3.0/drivers/base/dd.c。
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者相等
在该系列函数中,SPI设备驱动注册后,在driver_match_device函数中进行了SPI设备和SPI设备驱动的匹配,最后在really_probe函数中调用了spidev_probe函数。

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;      }              
此外,将spidev添加到device_list中,这样做就方便查找该spidev。
        2、spidev_remove函数,该函数位于/kernel3.0/drivers/spi/spidev.c。

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;       }
        3、spidev_open函数,该函数位于/kernel3.0/drivers/spi/spidev.c。

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;}     

 至此,对于protocol驱动层的框架进行了简单的分析,在下一篇将对该驱动层很多未分析的函数进行一一讲解。
1 0
原创粉丝点击