linux SPI驱动框架(二) -- 设备驱动

来源:互联网 发布:亚马逊订单管理 软件 编辑:程序博客网 时间:2024/05/22 06:23

转载请标明出处floater的csdn blog,http://blog.csdn.net/flaoter

之前文章linux SPI驱动框架(一)— 控制器驱动http://blog.csdn.net/flaoter/article/details/50001133对进行了介绍,本节内容对SPI从设备的设备驱动进行讲解。

设备驱动关注的结构体主要有两个,struct spi_device描述spi从设备,struct spi_driver是从设备的设备驱动。

struct spi_device {    struct device       dev;    struct spi_master   *master;    u32         max_speed_hz;    u8          chip_select;    u8          bits_per_word;    u16         mode;#define SPI_CPHA    0x01            /* clock phase */#define SPI_CPOL    0x02            /* clock polarity */#define SPI_MODE_0  (0|0)           /* (original MicroWire) */#define SPI_MODE_1  (0|SPI_CPHA)#define SPI_MODE_2  (SPI_CPOL|0)#define SPI_MODE_3  (SPI_CPOL|SPI_CPHA)#define SPI_CS_HIGH 0x04            /* chipselect active high? */#define SPI_LSB_FIRST   0x08            /* per-word bits-on-wire */#define SPI_3WIRE   0x10            /* SI/SO signals shared */#define SPI_LOOP    0x20            /* loopback mode */#define SPI_NO_CS   0x40            /* 1 dev/bus, no chipselect */#define SPI_READY   0x80            /* slave pulls low to pause */#define SPI_TX_DUAL 0x100           /* transmit with 2 wires */#define SPI_TX_QUAD 0x200           /* transmit with 4 wires */#define SPI_RX_DUAL 0x400           /* receive with 2 wires */#define SPI_RX_QUAD 0x800           /* receive with 4 wires */    int         irq;    void            *controller_state;    void            *controller_data;    char            modalias[SPI_NAME_SIZE];    int         cs_gpio;    /* chip select gpio */    /*     * likely need more hooks for more protocol options affecting how     * the controller talks to each chip, like:     *  - memory packing (12 bit samples into low bits, others zeroed)     *  - priority     *  - drop chipselect after each word     *  - chipselect delays     *  - ...     */};struct spi_driver {    const struct spi_device_id *id_table;    int         (*probe)(struct spi_device *spi);    int         (*remove)(struct spi_device *spi);    void            (*shutdown)(struct spi_device *spi);    int         (*suspend)(struct spi_device *spi, pm_message_t mesg);    int         (*resume)(struct spi_device *spi);    struct device_driver    driver;};

初始化函数如下,代码解释参见代码注释。此代码中包含了设备注册的代码,但是在dts普遍应用后,此种设备注册的方式已经被设备树的方式代替了,此处的设备注册的应用场景多出现在以模块方式编译的驱动设计中。因此请不要被设备注册产生混淆。

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);//字符设备注册,主设备号是SPIDEV_MAJOR,设备名是"spi",文件操作ops是spidev_fops,注册后/proc/devices下有相关设备,但不会向udev发送uevent,如需要/dev下设备节,需要手动配置    status = register_chrdev(SPIDEV_MAJOR, "spi", &spidev_fops);      if (status < 0)        return status;//创建spidev class, /sys/class/spidev    spidev_class = class_create(THIS_MODULE, "spidev");    if (IS_ERR(spidev_class)) {        status = PTR_ERR(spidev_class);        goto error_class;    }//设备驱动spidev_spi_driver注册    status = spi_register_driver(&spidev_spi_driver);    if (status < 0)        goto error_register;//设备注册,一般是作为module driver的时候用这种注册方式,busnum, chipselect可作为MODULE_PARAM传入;//正常情况下,设备的注册会在控制器驱动spi_master注册过程中,对dts进行解析后进行spi slave设备spi_device的注册    if (busnum != -1 && chipselect != -1) {        struct spi_board_info chip = {                    .modalias   = "spidev",        //与device_driver进行match使用                    .mode       = spimode, //spi工作模式                    .bus_num    = busnum,          //spi master busnum                    .chip_select    = chipselect,                      .max_speed_hz   = maxspeed,        };        struct spi_master *master;        master = spi_busnum_to_master(busnum);  //根据busnum找到注册的spi_master        if (!master) {            status = -ENODEV;            goto error_busnum;        }//spi slave设备spi_device注册的实现,并建立master和slave之间的关系        /* We create a virtual device that will sit on the bus */        spi = spi_new_device(master, &chip);          if (!spi) {            status = -EBUSY;            goto error_mem;        }        dev_dbg(&spi->dev, "busnum=%d cs=%d bufsiz=%d maxspeed=%d",            busnum, chipselect, bufsiz, maxspeed);    }    return 0;error_mem:error_busnum:    spi_unregister_driver(&spidev_spi_driver);error_register:    class_destroy(spidev_class);error_class:    unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);    return status;}module_init(spidev_init);

下面是设备驱动的定义,当compatible与设备注册的属性一致时,spidev_probe会被调用。

static const struct of_device_id spidev_dt_ids[] = {    { .compatible = "rohm,dh2228fv", },    { .compatible = "qcom,spi-msm-codec-slave", },    {},};MODULE_DEVICE_TABLE(of, spidev_dt_ids);static struct spi_driver spidev_spi_driver = {    .driver = {        .name =     "spidev",        .owner =    THIS_MODULE,        .of_match_table = of_match_ptr(spidev_dt_ids),    },    .probe =    spidev_probe,    .remove =   spidev_remove,    /* NOTE:  suspend/resume methods are not necessary here.     * We don't do anything except pass the requests to/from     * the underlying controller.  The refrigerator handles     * most issues; the controller driver handles the rest.     */};

probe函数定义如下,

static int spidev_probe(struct spi_device *spi){    struct spidev_data  *spidev;    int         status;    unsigned long       minor;    /* Allocate driver data */    spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);    if (!spidev)        return -ENOMEM;    /* Initialize the driver data */    spidev->spi = spi;    spin_lock_init(&spidev->spi_lock);    mutex_init(&spidev->buf_lock);    INIT_LIST_HEAD(&spidev->device_entry);    /* If we can allocate a minor number, hook up this device.     * Reusing minors is fine so long as udev or mdev is working.     */    mutex_lock(&device_list_lock);//找到最小的次设备号    minor = find_first_zero_bit(minors, N_SPI_MINORS);    if (minor < N_SPI_MINORS) {        struct device *dev;        spidev->devt = MKDEV(SPIDEV_MAJOR, minor);//设备创建,/sys/class/spidev下会有spidev%d.%d相应设备,同时udev也会在/dev下创建相应的节点        dev = device_create(spidev_class, &spi->dev, spidev->devt,                         spidev, "spidev%d.%d",                    spi->master->bus_num, spi->chip_select);        status = PTR_ERR_OR_ZERO(dev);    } 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);  //将spidev加入device_list链表    }    mutex_unlock(&device_list_lock);    if (status == 0)        spi_set_drvdata(spi, spidev);     //spi->dev->drvdata=spidev    else        kfree(spidev);    return status;}

文件操作函数定义如下,

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

下面以open和write函数为例,对设备操作函数进行说明,

static int spidev_open(struct inode *inode, struct file *filp){    struct spidev_data  *spidev;    int         status = -ENXIO;    mutex_lock(&device_list_lock);    list_for_each_entry(spidev, &device_list, device_entry) {  //根据节点inode->i_rdev在device_list链表中找到spidev        if (spidev->devt == inode->i_rdev) {            status = 0;            break;        }    }    if (status) {        pr_debug("spidev: nothing for minor %d\n", iminor(inode));        goto err_find_dev;    }    if (!spidev->tx_buffer) {        spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);  //申请tx buffer        if (!spidev->tx_buffer) {                dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");                status = -ENOMEM;            goto err_find_dev;            }        }    if (!spidev->rx_buffer) {        spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL); //申请rx buffer        if (!spidev->rx_buffer) {            dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");            status = -ENOMEM;            goto err_alloc_rx_buf;        }    }    spidev->users++;    //增加引用次数    filp->private_data = spidev;  //将spidev传给filp    nonseekable_open(inode, filp);    mutex_unlock(&device_list_lock);    return 0;err_alloc_rx_buf:    kfree(spidev->tx_buffer);    spidev->tx_buffer = NULL;err_find_dev:    mutex_unlock(&device_list_lock);    return status;}
static ssize_tspidev_write(struct file *filp, const char __user *buf,        size_t count, loff_t *f_pos){    struct spidev_data  *spidev;    ssize_t         status = 0;    unsigned long       missing;    /* chipselect only toggles at start or end of operation */    if (count > bufsiz)        return -EMSGSIZE;    spidev = filp->private_data;  //从传入参数filp获得spidev    mutex_lock(&spidev->buf_lock);    missing = copy_from_user(spidev->tx_buffer, buf, count);  //将用户态buf拷贝到spidev->tx_buf    if (missing == 0)        status = spidev_sync_write(spidev, count); //继续调用spidev_sync_write实现    else        status = -EFAULT;    mutex_unlock(&spidev->buf_lock);    return status;}

write函数的具体实现如下,

static inline ssize_tspidev_sync_write(struct spidev_data *spidev, size_t len){    struct spi_transfer t = {            .tx_buf     = spidev->tx_buffer,            .len        = len,        };    struct spi_message  m;    spi_message_init(&m);   //初始化spi_message    spi_message_add_tail(&t, &m);  //将spi_transfer加入spi_message的队列    return spidev_sync(spidev, &m); //继续调用spidev_sync}static ssize_tspidev_sync(struct spidev_data *spidev, struct spi_message *message){    DECLARE_COMPLETION_ONSTACK(done);    int status;    message->complete = spidev_complete;      message->context = &done;   //spi_message的完成量赋值    spin_lock_irq(&spidev->spi_lock);    if (spidev->spi == NULL)        status = -ESHUTDOWN;    else        status = spi_async(spidev->spi, message);  //调用spi_async异步传输    spin_unlock_irq(&spidev->spi_lock);    if (status == 0) {        wait_for_completion(&done);  //等待完成量        status = message->status;        if (status == 0)            status = message->actual_length;    }    return status;}int spi_async(struct spi_device *spi, struct spi_message *message){    struct spi_master *master = spi->master;    int ret;    unsigned long flags;    ret = __spi_validate(spi, message);    if (ret != 0)        return ret;    spin_lock_irqsave(&master->bus_lock_spinlock, flags);    if (master->bus_lock_flag)        ret = -EBUSY;    else        ret = __spi_async(spi, message);  //继续调用__spi_async    spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);    return ret;}static int __spi_async(struct spi_device *spi, struct spi_message *message){    struct spi_master *master = spi->master;   //获取master    message->spi = spi;  //spi_message发送到的设备    trace_spi_message_submit(message);    return master->transfer(spi, message);  //调用master->transfer进行传输}

transfer函数的实现在控制器驱动章节中有详细的描述,在此处不再进行重复。