现在的位置: 首页 > 综合 > 正文

【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】PowerPC + Linux2.6.25平台下的SPI驱动架构分析

2012年11月24日 ⁄ 综合 ⁄ 共 40598字 ⁄ 字号 评论关闭

PowerPC + Linux2.6.25平台下的SPI驱动架构分析

 

Sailor_forever  sailing_9806#163.com

(本原创文章发表于Sailor_forever 的个人blog,未经本人许可,不得用于商业用途。任何个人、媒体、其他网站不得私自抄袭;网络媒体转载请注明出处,增加原文链接,否则属于侵权行为。如 有任何问题,请留言或者发邮件给sailing_9806#163.com)
http://blog.csdn.net/sailor_8318/archive/2010/10/31/5977733.aspx

 

 

【摘要】本文以PowerPC+Linux 2.6.25 平台为例,详细分析了SPI总线的驱动架构。首先介绍了SPI的总体架构,从用户的角度将其分为三个层面,不同的开发者只需要关注相应的层面即可。然后分析了主要数据结构及其之间的相互关系,接着分析了不同层的具体实现,最后以一款SPI接口的时钟芯片为例讲述了如何在用户空间访问SPI驱动。对于ARM + Linux平台,只有平台依赖层即总线控制器驱动有差异。

【关键字】PowerPC, SPI, Master, Slave, spidev

 

 

目录

1    SPI概述    3
2    SPI总体架构    3
2.1    硬件抽象层    3
2.2    平台依赖层    3
2.3    用户接口层    3
3    主要的数据结构    4
3.1    Spi_master    4
3.2    SPI_driver    5
3.3    Spi_device    6
4    平台依赖层-总线控制器驱动    7
4.1    platform device    8
4.2    platform driver    11
4.3    SPI Master    14
5    硬件抽象层-SPI core    14
5.1    总线初始化    14
5.2    Master注册    15
5.3    驱动注册    19
5.4    数据传输    19
6    用户接口层-SPI设备驱动    21
6.1    统一的设备模型    21
6.1.1    关键数据结构    21
6.1.2    初始化    22
6.1.3    Open及release    24
6.1.4    数据收发    25
6.2    特定的设备驱动    36
6.2.1    关键数据结构    37
6.2.2    初始化    38
6.2.3    数据收发    42
7    驱动访问示例    42
7.1.1    写操作    43
7.1.2    读操作    43
8    参考鸣谢    44

      
    

 
1    SPI概述
SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时或者硬件复用两根数据线),也是所有基于SPI的设备共有的,它们是MISO、MOSI、SCK、CS。
(1)SDO – 主设备数据输出,从设备数据输入
(2)MISO– 主设备数据输入,从设备数据输出
(3)SCK – 时钟信号,由主设备产生
(4)CS – 从设备使能信号,由主设备控制
其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效,这就允许在同一总线上连接多个SPI设备成为可能。接下来就负责通讯的3根线了,通讯是通过数据交换完成的。SPI是串行通讯协议,也就是说数据是一位一位从MSB或者LSB开始传输的,这就是SCK时钟线存在的原因,由SCK提供时钟脉冲,MISO、MOSI则基于此脉冲完成数据传输。 SPI支持4-32bits的串行数据传输,支持MSB和LSB,每次数据传输时当从设备的大小端发生变化时需要重新设置SPI Master的大小端。

2    SPI总体架构
在2.6的Linux内核中,SPI的驱动架构分为如下三个层次:硬件抽象层、平台依赖层和用户接口层。
2.1    硬件抽象层
SPI-bitbang.c和SPI.c为其主体框架代码,提供了核心数据结构的定义、SPI控制器驱动和设备驱动的注册、注销管理等API。其为硬件平台无关层,向下屏蔽了物理总线控制器的差异,定义了统一的访问策略和接口;其向上提供了统一的接口,以便SPI设备驱动通过总线控制器进行数据收发。
2.2    平台依赖层
SPI总线Master就是一条SPI总线的控制器(所谓控制是相对于本CPU来说的),在物理上连接若干SPI从设备。在Linux驱动中,每种处理器平台有自己的控制器驱动,属于平台移植相关层。PowerPC平台来说,其是spi_mpc83xx.c。其按照核心层定义的接口实现了spi_master。
2.3    用户接口层
设备驱动层为用户接口层,其为用户提供了通过SPI总线访问具体设备的接口。

 

3    主要的数据结构
3.1    Spi_master
spi_master是对某一条SPI总线的抽象,是特定总线的相关属性的集合。
/**
 * struct spi_master - interface to SPI master controller
 * @dev: device interface to this driver
 * @bus_num: board-specific (and often SOC-specific) identifier for a
 *    given SPI controller.
 * @num_chipselect: chipselects are used to distinguish individual
 *    SPI slaves, and are numbered from zero to num_chipselects.
 *    each slave has a chipselect signal, but it's common that not
 *    every chipselect is connected to a slave.
 * @setup: updates the device mode and clocking records used by a
 *    device's SPI controller; protocol code may call this.  This
 *    must fail if an unrecognized or unsupported mode is requested.
 *    It's always safe to call this unless transfers are pending on
 *    the device whose settings are being modified.
 * @transfer: adds a message to the controller's transfer queue.
 * @cleanup: frees controller-specific state
 *
 * Each SPI master controller can communicate with one or more @spi_device
 * children.  These make a small bus, sharing MOSI, MISO and SCK signals
 * but not chip select signals.  Each device may be configured to use a
 * different clock rate, since those shared signals are ignored unless
 * the chip is selected.
 *
 * The driver for an SPI controller manages access to those devices through
 * a queue of spi_message transactions, copying data between CPU memory and
 * an SPI slave device.  For each such message it queues, it calls the
 * message's completion function when the transaction completes.
 */
struct spi_master {
    struct device    dev;

    /* other than negative (== assign one dynamically), bus_num is fully
     * board-specific.  usually that simplifies to being SOC-specific.
     * example:  one SOC has three SPI controllers, numbered 0..2,
     * and one board's schematics might show it using SPI-2.  software
     * would normally use bus_num=2 for that controller.
     */
    s16            bus_num;

    /* chipselects will be integral to many controllers; some others
     * might use board-specific GPIOs.
     */
    u16            num_chipselect;

    /* setup mode and clock, etc (spi driver may call many times) */
    int            (*setup)(struct spi_device *spi);

    /* bidirectional bulk transfers
     *
     * + The transfer() method may not sleep; its main role is
     *   just to add the message to the queue.
     * + For now there's no remove-from-queue operation, or
     *   any other request management
     * + To a given spi_device, message queueing is pure fifo
     *
     * + The master's main job is to process its message queue,
     *   selecting a chip then transferring data
     * + If there are multiple spi_device children, the i/o queue
     *   arbitration algorithm is unspecified (round robin, fifo,
     *   priority, reservations, preemption, etc)
     *
     * + Chipselect stays active during the entire message
     *   (unless modified by spi_transfer.cs_change != 0).
     * + The message transfers use clock and SPI mode parameters
     *   previously established by setup() for this device
     */
    int            (*transfer)(struct spi_device *spi,
                        struct spi_message *mesg);

    /* called on release() to free memory provided by spi_master */
    void            (*cleanup)(struct spi_device *spi);
};

3.2    SPI_driver

http://lxr.linux.no/#linux+v2.6.25/include/linux/SPI.h#L105

/**
 * struct spi_driver - Host side "protocol" driver
 * @probe: Binds this driver to the spi device.  Drivers can verify
 *    that the device is actually present, and may need to configure
 *    characteristics (such as bits_per_word) which weren't needed for
 *    the initial configuration done during system setup.
 * @remove: Unbinds this driver from the spi device
 * @shutdown: Standard shutdown callback used during system state
 *    transitions such as powerdown/halt and kexec
 * @suspend: Standard suspend callback used during system state transitions
 * @resume: Standard resume callback used during system state transitions
 * @driver: SPI device drivers should initialize the name and owner
 *    field of this structure.
 *
 * This represents the kind of device driver that uses SPI messages to
 * interact with the hardware at the other end of a SPI link.  It's called
 * a "protocol" driver because it works through messages rather than talking
 * directly to SPI hardware (which is what the underlying SPI controller
 * driver does to pass those messages).  These protocols are defined in the
 * specification for the device(s) supported by the driver.
 *
 * As a rule, those device protocols represent the lowest level interface
 * supported by a driver, and it will support upper level interfaces too.
 * Examples of such upper levels include frameworks like MTD, networking,
 * MMC, RTC, filesystem character device nodes, and hardware monitoring.
 */
struct spi_driver {
    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;
};
Driver是为device服务的,SPI_driver注册时会扫描SPI bus上的设备,进行驱动和设备的绑定。
3.3    Spi_device

http://lxr.linux.no/#linux+v2.6.25/include/linux/SPI.h#L168

/**
 * struct spi_device - Master side proxy for an SPI slave device
 * @dev: Driver model representation of the device.
 * @master: SPI controller used with the device.
 * @max_speed_hz: Maximum clock rate to be used with this chip
 *    (on this board); may be changed by the device's driver.
 *    The spi_transfer.speed_hz can override this for each transfer.
 * @chip_select: Chipselect, distinguishing chips handled by @master.
 * @mode: The spi mode defines how data is clocked out and in.
 *    This may be changed by the device's driver.
 *    The "active low" default for chipselect mode can be overridden
 *    (by specifying SPI_CS_HIGH) as can the "MSB first" default for
 *    each word in a transfer (by specifying SPI_LSB_FIRST).
 * @bits_per_word: Data transfers involve one or more words; word sizes
 *    like eight or 12 bits are common.  In-memory wordsizes are
 *    powers of two bytes (e.g. 20 bit samples use 32 bits).
 *    This may be changed by the device's driver, or left at the
 *    default (0) indicating protocol words are eight bit bytes.
 *    The spi_transfer.bits_per_word can override this for each transfer.
 * @irq: Negative, or the number passed to request_irq() to receive
 *    interrupts from this device.
 * @controller_state: Controller's runtime state
 * @controller_data: Board-specific definitions for controller, such as
 *    FIFO initialization parameters; from board_info.controller_data
*
 * A @spi_device is used to interchange data between an SPI slave
 * (usually a discrete chip) and CPU memory.
 *
 * In @dev, the platform_data is used to hold information about this
 * device that's meaningful to the device's protocol driver, but not
 * to its controller.  One example might be an identifier for a chip
 * variant with slightly different functionality; another might be
 * information about how this particular board wires the chip's pins.
 */
struct spi_device {
    struct device        dev;
    struct spi_master    *master;
    u32            max_speed_hz;
    u8            chip_select;
    u8            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 */
    u8            bits_per_word;
    int            irq;
    void            *controller_state;
    void            *controller_data;
。。。
};

spi_device对应着SPI总线上某个特定的slave。每个slave都有特定的大小端、速率及传输位宽,各个slave相互之间无干扰。

4    平台依赖层-总线控制器驱动
总线控制器驱动,本质上就是实现了具体的总线传输算法并向核心层注册了控制器。主要分为三个层面,platform device,platform driver及与SPI core的接口层。

Linux内核的所有SPI控制器驱动程序都在driver/SPI/下面,MPC8xxx驱动是spi_mpc83xx.c。
4.1    platform device
2.6内核中硬件资源的注册都采用了platform device的机制。对于PowerPC来说,其硬件资源是通过DTS来描述的。
spi@7000 {
    cell-index = <0>;
    compatible = "fsl,spi";
    reg = <0x7000 0x1000>;
    interrupts = <16 0x8>;
    interrupt-parent = <&ipic>;
    mode = "cpu";
};
中断号、中断触发电平、寄存器的基地址及范围等信息会在设备树中描述了,此后只需利用platform_get_resource等标准接口自动获取即可,实现了驱动和资源的分离。Cell-index标识了总线编号,也就是SPI master的编号。

随后在系统启动阶段会解析DTB文件,将相关资源注册到Platform bus上。

http://lxr.linux.no/#linux+v2.6.25/arch/powerpc/sysdev/fsl_soc.c#L454

int __init fsl_spi_init(struct spi_board_info *board_infos,
            unsigned int num_board_infos,
            void (*activate_cs)(u8 cs, u8 polarity),
            void (*deactivate_cs)(u8 cs, u8 polarity))
{
    u32 sysclk = -1;
    int ret;
。。。。。
    if (sysclk == -1) {
        struct device_node *np;
        const u32 *freq;
        int size;

        np = of_find_node_by_type(NULL, "soc"); //获得SOC中注册的总线频率
        if (!np)
            return -ENODEV;

        freq = of_get_property(np, "clock-frequency", &size);
        if (!freq || size != sizeof(*freq) || *freq == 0) {
            freq = of_get_property(np, "bus-frequency", &size);
            if (!freq || size != sizeof(*freq) || *freq == 0) {
                of_node_put(np);
                return -ENODEV;
            }
        }

        sysclk = *freq;
        of_node_put(np);
    }

    ret = of_fsl_spi_probe(NULL, "fsl,spi", sysclk, board_infos,
                   num_board_infos, activate_cs, deactivate_cs);
    if (!ret)
        of_fsl_spi_probe("spi", "fsl_spi", sysclk, board_infos,
                 num_board_infos, activate_cs, deactivate_cs);

    return spi_register_board_info(board_infos, num_board_infos);  //将SPI board info注册进系统SPI设备列表中
}

static int __init of_fsl_spi_probe(char *type, char *compatible, u32 sysclk,
                   struct spi_board_info *board_infos,
                   unsigned int num_board_infos,
                   void (*activate_cs)(u8 cs, u8 polarity),
                   void (*deactivate_cs)(u8 cs, u8 polarity))
{
    struct device_node *np;
    unsigned int i = 0;

    for_each_compatible_node(np, type, compatible) { //根据compatible属性"fsl,spi"查找相关device node节点
        int ret;
        unsigned int j;
        const void *prop;
        struct resource res[2];
        struct platform_device *pdev;
        struct fsl_spi_platform_data pdata = { //板级相关的SPI片选实现函数
            .activate_cs = activate_cs,
            .deactivate_cs = deactivate_cs,
        };

        memset(res, 0, sizeof(res));

        pdata.sysclk = sysclk;

        prop = of_get_property(np, "reg", NULL);
        if (!prop)
            goto err;
        pdata.bus_num = *(u32 *)prop; //reg是寄存器的范围,如何与总线编号挂钩的呢?

        prop = of_get_property(np, "cell-index", NULL);
        if (prop)
            i = *(u32 *)prop;

        prop = of_get_property(np, "mode", NULL);
        if (prop && !strcmp(prop, "cpu-qe"))
            pdata.qe_mode = 1;

        for (j = 0; j < num_board_infos; j++) { //根据板级移植相关的board info指定的bus num进行匹配
            if (board_infos[j].bus_num == pdata.bus_num)
                pdata.max_chipselect++;
        }

        if (!pdata.max_chipselect)
            continue;

        ret = of_address_to_resource(np, 0, &res[0]);
        if (ret)
            goto err;

        ret = of_irq_to_resource(np, 0, &res[1]);
        if (ret == NO_IRQ)
            goto err;

        pdev = platform_device_alloc("mpc83xx_spi", i); //以mpc83xx_spi为name申请platform device,后续的platform driver将以mpc83xx_spi为匹配因子
        if (!pdev)
            goto err;

        ret = platform_device_add_data(pdev, &pdata, sizeof(pdata)); //将pdata等特定的属性添加到platform device中,以供相应的platform driver检测。
        if (ret)
            goto unreg;

        ret = platform_device_add_resources(pdev, res,
                            ARRAY_SIZE(res));
        if (ret)
            goto unreg;

        ret = platform_device_add(pdev); //将SPI相关的platform device添加到platform bus上
        if (ret)
            goto unreg;

        goto next;
unreg:
        platform_device_del(pdev);
err:
        pr_err("%s: registration failed/n", np->full_name);
next:
        i++;
    }

    return i;
}

4.2    platform driver
static struct platform_driver mpc83xx_spi_driver = {
    .remove = __exit_p(mpc83xx_spi_remove),
    .driver = {
        .name = "mpc83xx_spi",
        .owner = THIS_MODULE,
    },
};

static int __init mpc83xx_spi_init(void)
{
    return platform_driver_probe(&mpc83xx_spi_driver, mpc83xx_spi_probe);
}

static void __exit mpc83xx_spi_exit(void)
{
    platform_driver_unregister(&mpc83xx_spi_driver);
}

mpc83xx_spi_driver注册时会扫描platform bus上的所有设备,匹配因子是mpc83xx_spi,匹配成功后调用mpc83xx_spi_probe将设备和驱动绑定起来,具体过程参加《详解Linux2.6内核中基于platform机制的驱动模型》,随后向系统注册一个adapter。

http://blog.csdn.net/sailor_8318/archive/2010/01/29/5267698.aspx

static int __init mpc83xx_spi_probe(struct platform_device *dev)
{
    struct spi_master *master;
    struct mpc83xx_spi *mpc83xx_spi;
    struct fsl_spi_platform_data *pdata;
    struct resource *r;
    u32 regval;
    int ret = 0;

    /* Get resources(memory, IRQ) associated with the device */
    master = spi_alloc_master(&dev->dev, sizeof(struct mpc83xx_spi)); // 分配一个SPI Master

    if (master == NULL) {
        ret = -ENOMEM;
        goto err;
    }

    platform_set_drvdata(dev, master);
    pdata = dev->dev.platform_data;  //获得platform device注册的特定资源

    if (pdata == NULL) {
        ret = -ENODEV;
        goto free_master;
    }

    r = platform_get_resource(dev, IORESOURCE_MEM, 0);
    if (r == NULL) {
        ret = -ENODEV;
        goto free_master;
    }

    mpc83xx_spi = spi_master_get_devdata(master);
    mpc83xx_spi->bitbang.master = spi_master_get(master);
    mpc83xx_spi->bitbang.chipselect = mpc83xx_spi_chipselect;
    mpc83xx_spi->bitbang.setup_transfer = mpc83xx_spi_setup_transfer;
    mpc83xx_spi->bitbang.txrx_bufs = mpc83xx_spi_bufs;
    mpc83xx_spi->activate_cs = pdata->activate_cs;
    mpc83xx_spi->deactivate_cs = pdata->deactivate_cs; // 将特定的片选实现函数保存到mpc83xx_spi中
    mpc83xx_spi->qe_mode = pdata->qe_mode;
    mpc83xx_spi->get_rx = mpc83xx_spi_rx_buf_u8;
    mpc83xx_spi->get_tx = mpc83xx_spi_tx_buf_u8;
    mpc83xx_spi->spibrg = pdata->sysclk;

    mpc83xx_spi->rx_shift = 0;
    mpc83xx_spi->tx_shift = 0;
    if (mpc83xx_spi->qe_mode) {
        mpc83xx_spi->rx_shift = 16;
        mpc83xx_spi->tx_shift = 24;
    }

    mpc83xx_spi->bitbang.master->setup = mpc83xx_spi_setup;
    init_completion(&mpc83xx_spi->done);

    mpc83xx_spi->base = ioremap(r->start, r->end - r->start + 1);
    if (mpc83xx_spi->base == NULL) {
        ret = -ENOMEM;
        goto put_master;
    }

    mpc83xx_spi->irq = platform_get_irq(dev, 0); //从platform device中获得相应的寄存器和中断等信息

    if (mpc83xx_spi->irq < 0) {
        ret = -ENXIO;
        goto unmap_io;
    }

    /* Register for SPI Interrupt */
    ret = request_irq(mpc83xx_spi->irq, mpc83xx_spi_irq,
              0, "mpc83xx_spi", mpc83xx_spi);

    if (ret != 0)
        goto unmap_io;

    master->bus_num = pdata->bus_num;
    master->num_chipselect = pdata->max_chipselect;

    /* SPI controller initializations */
    mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, 0);
    mpc83xx_spi_write_reg(&mpc83xx_spi->base->mask, 0);
    mpc83xx_spi_write_reg(&mpc83xx_spi->base->command, 0);
    mpc83xx_spi_write_reg(&mpc83xx_spi->base->event, 0xffffffff);

    /* Enable SPI interface */
    regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
    if (pdata->qe_mode)
        regval |= SPMODE_OP;

    mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode, regval);

    ret = spi_bitbang_start(&mpc83xx_spi->bitbang);  //MPC83xx系列将在spi_bitbang_start中注册SPI master

    if (ret != 0)
        goto free_irq;

    printk(KERN_INFO
           "%s: MPC83xx SPI Controller driver at 0x%p (irq = %d)/n",
           dev->dev.bus_id, mpc83xx_spi->base, mpc83xx_spi->irq);

    return ret;

free_irq:
    free_irq(mpc83xx_spi->irq, mpc83xx_spi);
unmap_io:
    iounmap(mpc83xx_spi->base);
put_master:
    spi_master_put(master);
free_master:
    kfree(master);
err:
    return ret;
}

4.3    SPI Master
每一个SPI master都要实现setup、transfer及cleanup等。
static int mpc83xx_spi_setup(struct spi_device *spi)
static int mpc83xx_spi_bufs(struct spi_device *spi, struct spi_transfer *t)

mpc83xx_spi->bitbang.txrx_bufs = mpc83xx_spi_bufs;

MPC837x SPI Master的具体实现待续。
5    硬件抽象层-SPI core
5.1    总线初始化
static int __init spi_init(void)
{
    int    status;

    buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
    if (!buf) {
        status = -ENOMEM;
        goto err0;
    }

    status = bus_register(&spi_bus_type);
    if (status < 0)
        goto err1;

    status = class_register(&spi_master_class);
    if (status < 0)
        goto err2;
    return 0;

err2:
    bus_unregister(&spi_bus_type);
err1:
    kfree(buf);
    buf = NULL;
err0:
    return status;
}

/* board_info is normally registered in arch_initcall(),
 * but even essential drivers wait till later
 *
 * REVISIT only boardinfo really needs static linking. the rest (device and
 * driver registration) _could_ be dynamically linked (modular) ... costs
 * include needing to have boardinfo data structures be much more public.
 */
subsys_initcall(spi_init);

关于被subsys_initcall修饰的spi_init何时初始化,请参考下文《详解Linux2.6内核中基于platform机制的驱动模型》

http://blog.csdn.net/sailor_8318/archive/2010/01/29/5267698.aspx

此时platform bus及platform device已经注册完毕,因此SPI控制器的相关资源已经就绪。

5.2    Master注册
/**
 * spi_register_master - register SPI master controller
 * @master: initialized master, originally from spi_alloc_master()
 * Context: can sleep
 *
 * SPI master controllers connect to their drivers using some non-SPI bus,
 * such as the platform bus.  The final stage of probe() in that code
 * includes calling spi_register_master() to hook up to this SPI bus glue.
 *
 * SPI controllers use board specific (often SOC specific) bus numbers,
 * and board-specific addressing for SPI devices combines those numbers
 * with chip select numbers.  Since SPI does not directly support dynamic
 * device identification, boards need configuration tables telling which
 * chip is at which address.
 */
int spi_register_master(struct spi_master *master)
{
    static atomic_t        dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
    struct device        *dev = master->dev.parent;
    int            status = -ENODEV;
    int            dynamic = 0;

    if (!dev)
        return -ENODEV;

    /* even if it's just one always-selected device, there must
     * be at least one chipselect
     */
    if (master->num_chipselect == 0)
        return -EINVAL;

    /* convention:  dynamically assigned bus IDs count down from the max */
    if (master->bus_num < 0) {
        /* FIXME switch to an IDR based scheme, something like
         * I2C now uses, so we can't run out of "dynamic" IDs
         */
        master->bus_num = atomic_dec_return(&dyn_bus_id);
        dynamic = 1;
    }

    /* register the device, then userspace will see it.
     * registration fails if the bus ID is in use.
     */
    snprintf(master->dev.bus_id, sizeof master->dev.bus_id,
        "spi%u", master->bus_num);
    status = device_add(&master->dev);
    if (status < 0)
        goto done;
    dev_dbg(dev, "registered master %s%s/n", master->dev.bus_id,
            dynamic ? " (dynamic)" : "");

    /* populate children from any spi device tables */
    scan_boardinfo(master);
    status = 0;
done:
    return status;
}
EXPORT_SYMBOL_GPL(spi_register_master);

static void scan_boardinfo(struct spi_master *master)
{
    struct boardinfo    *bi;

    mutex_lock(&board_lock);
    list_for_each_entry(bi, &board_list, list) {//board list已经在platform device注册时初始化
        struct spi_board_info    *chip = bi->board_info;
        unsigned        n;

        for (n = bi->n_board_info; n > 0; n--, chip++) {
            if (chip->bus_num != master->bus_num)  //Master的总线号和相应slave所在的总线号匹配
                continue;
            /* NOTE: this relies on spi_new_device to
             * issue diagnostics when given bogus inputs
             */
            (void) spi_new_device(master, chip);
        }
    }
    mutex_unlock(&board_lock);
}

/**
 * spi_new_device - instantiate one new SPI device
 * @master: Controller to which device is connected
 * @chip: Describes the SPI device
 * Context: can sleep
* Returns the new device, or NULL.
 */
struct spi_device *spi_new_device(struct spi_master *master,
                  struct spi_board_info *chip)
{
    struct spi_device    *proxy;
    struct device        *dev = master->dev.parent;
    int            status;

    /* NOTE:  caller did any chip->bus_num checks necessary.
     */

    /* Chipselects are numbered 0..max; validate. */
    if (chip->chip_select >= master->num_chipselect) {
        dev_err(dev, "cs%d > max %d/n",
            chip->chip_select,
            master->num_chipselect);
        return NULL;
    }

    if (!spi_master_get(master))
        return NULL;

    proxy = kzalloc(sizeof *proxy, GFP_KERNEL);  //分配一个SPI device
    if (!proxy) {
        dev_err(dev, "can't alloc dev for cs%d/n",
            chip->chip_select);
        goto fail;
    }

    proxy->master = master;  // SPI slave所依附的SPI Master
    proxy->chip_select = chip->chip_select;  // 保存board info中特定的属性,板级移植需要关注
    proxy->max_speed_hz = chip->max_speed_hz;
    proxy->mode = chip->mode;
    proxy->irq = chip->irq;
    proxy->modalias = chip->modalias;

    snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id,
            "%s.%u", master->dev.bus_id,
            chip->chip_select);
    proxy->dev.parent = dev;
    proxy->dev.bus = &spi_bus_type;   //此设备所依附的总线为SPI
    proxy->dev.platform_data = (void *) chip->platform_data;
    proxy->controller_data = chip->controller_data;
    proxy->controller_state = NULL;
    proxy->dev.release = spidev_release;

    /* drivers may modify this initial i/o setup */
    status = master->setup(proxy);
    if (status < 0) {
        dev_err(dev, "can't %s %s, status %d/n",
                "setup", proxy->dev.bus_id, status);
        goto fail;
    }

    /* driver core catches callers that misbehave by defining
     * devices that already exist.
     */
    status = device_register(&proxy->dev);  // 将该SPI device挂接到SPI总线上
    if (status < 0) {
        dev_err(dev, "can't %s %s, status %d/n",
                "add", proxy->dev.bus_id, status);
        goto fail;
    }
    dev_dbg(dev, "registered child %s/n", proxy->dev.bus_id);
    return proxy;

fail:
    spi_master_put(master);
    kfree(proxy);
    return NULL;
}
EXPORT_SYMBOL_GPL(spi_new_device);

5.3    驱动注册
/**
 * spi_register_driver - register a SPI driver
 * @sdrv: the driver to register
 * Context: can sleep
 */
int spi_register_driver(struct spi_driver *sdrv)
{
    sdrv->driver.bus = &spi_bus_type;
    if (sdrv->probe)
        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);

SPI driver采用内核通用的驱动模型,其流程和platform driver的注册过程类似,请请参考下文《详解Linux2.6内核中基于platform机制的驱动模型》

http://blog.csdn.net/sailor_8318/archive/2010/01/29/5267698.aspx

5.4    数据传输
/**
 * spi_sync - blocking/synchronous SPI data transfers
 * @spi: device with which data will be exchanged
 * @message: describes the data transfers
 * Context: can sleep
 *
 * This call may only be used from a context that may sleep.  The sleep
 * is non-interruptible, and has no timeout.  Low-overhead controller
 * drivers may DMA directly into and out of the message buffers.
 *
 * Note that the SPI device's chip select is active during the message,
 * and then is normally disabled between messages.  Drivers for some
 * frequently-used devices may want to minimize costs of selecting a chip,
 * by leaving it selected in anticipation that the next message will go
 * to the same chip.  (That may increase power usage.)
 *
 * Also, the caller is guaranteeing that the memory associated with the
 * message will not be freed before this call returns.
 *
 * It returns zero on success, else a negative error code.
 */
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
    DECLARE_COMPLETION_ONSTACK(done);
    int status;

    message->complete = spi_complete;
    message->context = &done;
    status = spi_async(spi, message);
    if (status == 0) {
        wait_for_completion(&done);
        status = message->status;
    }
    message->context = NULL;
    return status;
}
EXPORT_SYMBOL_GPL(spi_sync);
同步接口,待数据收发完毕后才返回,只能在非中断上下文中使用。

/**
 * spi_async - asynchronous SPI transfer
 * @spi: device with which data will be exchanged
 * @message: describes the data transfers, including completion callback
 * Context: any (irqs may be blocked, etc)
 *
 * This call may be used in_irq and other contexts which can't sleep,
 * as well as from task contexts which can sleep.
 *
 * The completion callback is invoked in a context which can't sleep.
 * Before that invocation, the value of message->status is undefined.
 * When the callback is issued, message->status holds either zero (to
 * indicate complete success) or a negative error code.  After that
 * callback returns, the driver which issued the transfer request may
 * deallocate the associated memory; it's no longer in use by any SPI
 * core or controller driver code.
 *
 * Note that although all messages to a spi_device are handled in
 * FIFO order, messages may go to different devices in other orders.
 * Some device might be higher priority, or have various "hard" access
 * time requirements, for example.
 *
 * On detection of any fault during the transfer, processing of
 * the entire message is aborted, and the device is deselected.
 * Until returning from the associated message completion callback,
 * no other spi_message queued to that device will be processed.
 * (This rule applies equally to all the synchronous transfer calls,
 * which are wrappers around this core asynchronous primitive.)
 */
static inline int spi_async(struct spi_device *spi, struct spi_message *message)
{
    message->spi = spi;
    return spi->master->transfer(spi, message);
}
异步接口,根据spi device找到其所在的spi master,用master所特定的transfer函数实现数据收发。

核心层提供的通用数据收发接口,屏蔽了底层差异。

6    用户接口层-SPI设备驱动
6.1    统一的设备模型
此驱动模型是针对SPI Slave的,只有注册board info时modalias是spidev的才能由此驱动访问。访问各个slave的基本框架是一致的,具体的差异将由从设备号来体现。
6.1.1    关键数据结构
static struct spi_driver spidev_spi = {
    .driver = {
        .name =        "spidev",
        .owner =    THIS_MODULE,
    },
    .probe =    spidev_probe,
    .remove =    __devexit_p(spidev_remove),
};
定义了一个标准的SPI_driver。

struct spidev_data {
    struct device        dev;
    struct spi_device    *spi;
    struct list_head    device_entry;

    struct mutex        buf_lock;
    unsigned        users;
    u8            *buffer;
};

static LIST_HEAD(device_list);
static DEFINE_MUTEX(device_list_lock);
定义了一个SPI dev列表,每个slave对应一个struct spidev_data, 以device_entry为节点连接在device_list上。

static struct file_operations spidev_fops = {
    .owner =    THIS_MODULE,
    .write =    spidev_write,
    .read =        spidev_read,
    .ioctl =    spidev_ioctl,
    .open =        spidev_open,
    .release =    spidev_release,
};
任意一个需要和用户空间通信的驱动必备的数据结构,其定义了具体的读写操作方法。

6.1.2    初始化

http://lxr.linux.no/#linux+v2.6.25/drivers/SPI/SPI-dev.c#L607

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;

    status = class_register(&spidev_class);
    if (status < 0) {
        unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
        return status;
    }

    status = spi_register_driver(&spidev_spi);
    if (status < 0) {
        class_unregister(&spidev_class);
        unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name);
    }
    return status;
}
module_init(spidev_init);

首先注册了一个字符型设备驱动,然后注册spi_driver,其将在SPI总线上查找对应的设备,根据关键字spidev进行匹配,匹配成功后将调用spi_driver的probe方法,即spidev_probe,将驱动和每一个salve绑定起来,并建立对应的dev设备节点,同时维护了一个spidev_data链表。

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;
    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) {
        spidev->dev.parent = &spi->dev;
        spidev->dev.class = &spidev_class;
        spidev->dev.devt = MKDEV(SPIDEV_MAJOR, minor);  //各个slave的从设备号是从0依次递增的
        snprintf(spidev->dev.bus_id, sizeof spidev->dev.bus_id,
                "spidev%d.%d",
                spi->master->bus_num, spi->chip_select);
        status = device_register(&spidev->dev);
    } else {
        dev_dbg(&spi->dev, "no minor number available!/n");
        status = -ENODEV;
    }
    if (status == 0) {
        set_bit(minor, minors);
        dev_set_drvdata(&spi->dev, spidev);
        list_add(&spidev->device_entry, &device_list);  // 将该SPI dev添加到SPI device列表中
    }
    mutex_unlock(&device_list_lock);

    if (status != 0)
        kfree(spidev);

    return status;
}
以SPI_MAJOR和动态从设备号为主从设备号注册设备节点,如果系统有udev或者是hotplug,那么就会在/dev下自动创建相关的设备节点了,其名称命名方式为spidevB.C,B为总线编号,C为片选序号。

6.1.3    Open及release
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) {
        if (spidev->dev.devt == inode->i_rdev) {  // 根据设备节点号进行匹配查找对应的spidev_data节点从而找到相应的从设备
            status = 0;
            break;
        }
    }
    if (status == 0) {
        if (!spidev->buffer) {
            spidev->buffer = kmalloc(bufsiz, GFP_KERNEL);  // 为每个slave分配了一段缓冲区
            if (!spidev->buffer) {
                dev_dbg(&spidev->spi->dev, "open/ENOMEM/n");
                status = -ENOMEM;
            }
        }
        if (status == 0) {
            spidev->users++;  // 访问该SPI slave的user加1
            filp->private_data = spidev;   //面向对象的典型应用,将特定数据保存在filp->private_data中,其他函数可以直接从filp->private_data中取出需要的东西,避免过多的使用全局变量来传递信息
            nonseekable_open(inode, filp);
        }
    } else
        pr_debug("spidev: nothing for minor %d/n", iminor(inode));

    mutex_unlock(&device_list_lock);
    return status;
}

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;
    filp->private_data = NULL;
    spidev->users--;
    if (!spidev->users) {  // 无用户再访问该slave时才释放其buffer
        kfree(spidev->buffer);
        spidev->buffer = NULL;
    }
    mutex_unlock(&device_list_lock);

    return status;
}

Open操作是用户空间程序和内核驱动交换的第一步,最终返回给用户空间的就是struct file结构体。对于SPI 驱动来说,用户空间所获得的就是spidev这个关键信息。
6.1.4    数据收发
对于SPI驱动来说,数据收发有两种途径,read/write或者ioctl方式。但无论哪种方式,最终都是构造一个spi_message。

/**
 * struct spi_message - one multi-segment SPI transaction
 * @transfers: list of transfer segments in this transaction
 * @spi: SPI device to which the transaction is queued
 * @is_dma_mapped: if true, the caller provided both dma and cpu virtual
 *    addresses for each transfer buffer
 * @complete: called to report transaction completions
 * @context: the argument to complete() when it's called
 * @actual_length: the total number of bytes that were transferred in all
 *    successful segments
 * @status: zero for success, else negative errno
 * @queue: for use by whichever driver currently owns the message
 * @state: for use by whichever driver currently owns the message
 *
 * A @spi_message is used to execute an atomic sequence of data transfers,
 * each represented by a struct spi_transfer.  The sequence is "atomic"
 * in the sense that no other spi_message may use that SPI bus until that
 * sequence completes.  On some systems, many such sequences can execute
 * as single programmed DMA transfer.  On all systems, these messages are
 * queued, and might complete after transactions to other devices.  Messages
 * sent to a given spi_device are always executed in FIFO order.
 *
*/
struct spi_message {
    struct list_head    transfers;  // 所包含的spi_transfer链表
    struct spi_device    *spi;  // 消息所发往的对象
    unsigned        is_dma_mapped:1;

    /* completion is reported through a callback */
    void            (*complete)(void *context);  //消息发送完毕后的回调函数
    void            *context;
    unsigned        actual_length;
    int            status;

    /* for optional use by whatever driver currently owns the
     * spi_message ...  between calls to spi_async and then later
     * complete(), that's the spi_master controller driver.
     */
    struct list_head    queue;  // spi_message会由SPI Maser驱动统一组织成链表,顺序处理,以此防止各个spi slave同时访问总线而导致冲突
    void            *state;
};

spi_message 包含了一系列spi_transfer,在所有的spi_transfer发送完毕前,SPI总线一直被占据,其间不会出现总线冲突,因此一个spi_message是一个原子系列,这种特性非常利于复杂的SPI通信协议,如先发送命令字然后才能读取数据。

/*
 * I/O INTERFACE between SPI controller and protocol drivers
 *
 * Protocol drivers use a queue of spi_messages, each transferring data
 * between the controller and memory buffers.
 *
 * The spi_messages themselves consist of a series of read+write transfer
 * segments.  Those segments always read the same number of bits as they
 * write; but one or the other is easily ignored by passing a null buffer
 * pointer.  (This is unlike most types of I/O API, because SPI hardware
 * is full duplex.)
 *
*/

/**
 * struct spi_transfer - a read/write buffer pair
 * @tx_buf: data to be written (dma-safe memory), or NULL
 * @rx_buf: data to be read (dma-safe memory), or NULL
 * @tx_dma: DMA address of tx_buf, if @spi_message.is_dma_mapped
 * @rx_dma: DMA address of rx_buf, if @spi_message.is_dma_mapped
 * @len: size of rx and tx buffers (in bytes)
 * @speed_hz: Select a speed other then the device default for this
 *      transfer. If 0 the default (from @spi_device) is used.
 * @bits_per_word: select a bits_per_word other then the device default
 *      for this transfer. If 0 the default (from @spi_device) is used.
 * @cs_change: affects chipselect after this transfer completes
 * @delay_usecs: microseconds to delay after this transfer before
 *    (optionally) changing the chipselect status, then starting
 *    the next transfer or completing this @spi_message.
 * @transfer_list: transfers are sequenced through @spi_message.transfers
 *
 * SPI transfers always write the same number of bytes as they read.
 * Protocol drivers should always provide @rx_buf and/or @tx_buf.
 *
 * If the transmit buffer is null, zeroes will be shifted out
 * while filling @rx_buf.  If the receive buffer is null, the data
 * shifted in will be discarded.  Only "len" bytes shift out (or in).
 * It's an error to try to shift out a partial word.  (For example, by
 * shifting out three bytes with word size of sixteen or twenty bits;
 * the former uses two bytes per word, the latter uses four bytes.)
 *
 * In-memory data values are always in native CPU byte order, translated
 * from the wire byte order (big-endian except with SPI_LSB_FIRST).  So
 * for example when bits_per_word is sixteen, buffers are 2N bytes long
 * (@len = 2N) and hold N sixteen bit words in CPU byte order.
 *
 * When the word size of the SPI transfer is not a power-of-two multiple
 * of eight bits, those in-memory words include extra bits.  In-memory
 * words are always seen by protocol drivers as right-justified, so the
 * undefined (rx) or unused (tx) bits are always the most significant bits.
 *
 * All SPI transfers start with the relevant chipselect active.  Normally
 * it stays selected until after the last transfer in a message.  Drivers
 * can affect the chipselect signal using cs_change.
 *
 * (i) If the transfer isn't the last one in the message, this flag is
 * used to make the chipselect briefly go inactive in the middle of the
 * message.  Toggling chipselect in this way may be needed to terminate
 * a chip command, letting a single spi_message perform all of group of
 * chip transactions together.
 *
 * (ii) When the transfer is the last one in the message, the chip may
 * stay selected until the next transfer.  On multi-device SPI busses
 * with nothing blocking messages going to other devices, this is just
 * a performance hint; starting a message to another device deselects
 * this one.  But in other cases, this can be used to ensure correctness.
 * Some devices need protocol transactions to be built from a series of
 * spi_message submissions, where the content of one message is determined
 * by the results of previous messages and where the whole transaction
 * ends when the chipselect goes intactive.
 *
 * The code that submits an spi_message (and its spi_transfers)
 * to the lower layers is responsible for managing its memory.
 * Zero-initialize every field you don't set up explicitly, to
 * insulate against future API updates.  After you submit a message
 * and its transfers, ignore them until its completion callback.
 */
struct spi_transfer {
    const void    *tx_buf;
    void        *rx_buf;
    unsigned    len;

    dma_addr_t    tx_dma;
    dma_addr_t    rx_dma;

    unsigned    cs_change:1;
    u8        bits_per_word;
    u16        delay_usecs;
    u32        speed_hz;

    struct list_head transfer_list;
};
一个spi_transfer是以某种特性如速率、延时及字长连续传输的最小单位。因为SPI是全双工总线,只要总线上有数据传输,则MOSI和MISO上同时有数据采样,对于SPI 控制器来说,其收发缓冲区中都有数据,但是对于用户来说,其可以灵活的选择tx_buf和rx_buf的设置。当发送数据时,tx_buf必须设置为待发送的数据,rx_buf可以为null或者指向无用的缓冲区,即不关心MISO的数据。而当接收数据时,rx_buf必须指向接收缓冲区,而tx_buf可以为Null,则MOSI上为全0,或者tx_buf指向不会引起从设备异常相应的数据。Len为待发送或者接收的数据长度。当一系列spi_transfer构成一个spi_message时,cs_change的设置非常讲究。当cs_change为0即不变时,则可以连续对同一个设备进行数据收发;当cs_change为1时通常用来停止command的传输而随后进行数据接收。因此cs_change可以利用SPI总线构造复杂的通信协议。

对于SPI总线协议来说,传输单位可以是4-32之间的任意bits,但对于SPI控制器来说,bits_per_word只能是8/16/32,即需要取整,待收发的数据在内存里都是以主机字节序右对齐存储,SPI控制器会自动根据传输单位及大小端进行转换。

对于read/write方式来说,如果不采用board info中的默认设置,则必须先通过ioctl方式设置该从设备特定的大小端、bits per word及速率等特性,后续数据传输时将一直采用此设置。

read/write与用户空间的接口是buf和count,分别为收发的相应数据指针和数据长度。

/* Read-only message with current device setup */
static ssize_t spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
    struct spidev_data    *spidev;
    struct spi_device    *spi;
    ssize_t            status = 0;

    /* chipselect only toggles at start or end of operation */  // 在count个数据发送期间,该slave的片选一直有效
    if (count > bufsiz)
        return -EMSGSIZE;

    spidev = filp->private_data;
    spi = spidev->spi;

    mutex_lock(&spidev->buf_lock); // 对同一个SPI slave的互斥访问
    status = spi_read(spi, spidev->buffer, count);
    if (status == 0) {
        unsigned long    missing;

        missing = copy_to_user(buf, spidev->buffer, count);
        if (count && missing == count)
            status = -EFAULT;
        else
            status = count - missing;
    }
    mutex_unlock(&spidev->buf_lock);

    return status;
}

/**
 * spi_read - SPI synchronous read
 * @spi: device from which data will be read
 * @buf: data buffer
 * @len: data buffer size
 * Context: can sleep
 *
 * This reads the buffer and returns zero or a negative error code.
 * Callable only from contexts that can sleep.
 */
static inline int spi_read(struct spi_device *spi, u8 *buf, size_t len)
{
    struct spi_transfer    t = {
            .rx_buf        = buf,
            .len        = len,
        };
    struct spi_message    m;

    spi_message_init(&m);
    spi_message_add_tail(&t, &m);
    return spi_sync(spi, &m);
}
自动构建一个spi_message,其只包含一个spi_transfer。

/* Write-only message with current device setup */
static ssize_t spidev_write(struct file *filp, const char __user *buf,
        size_t count, loff_t *f_pos)
{
    struct spidev_data    *spidev;
    struct spi_device    *spi;
    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;
    spi = spidev->spi;

    mutex_lock(&spidev->buf_lock);
    missing = copy_from_user(spidev->buffer, buf, count); // 将待发送的数据从用户空间拷贝至内核空间
    if (missing == 0) {
        status = spi_write(spi, spidev->buffer, count);
        if (status == 0)
            status = count;
    } else
        status = -EFAULT;
    mutex_unlock(&spidev->buf_lock);

    return status;
}

对于ioctl方式,默认情况下就是进行数据收发。

IO ctrl方式与用户空间的接口为spi_ioc_transfer,其数据结构如下:
/**
 * struct spi_ioc_transfer - describes a single SPI transfer
 * @tx_buf: Holds pointer to userspace buffer with transmit data, or null.
 *    If no data is provided, zeroes are shifted out.
 * @rx_buf: Holds pointer to userspace buffer for receive data, or null.
 * @len: Length of tx and rx buffers, in bytes.
 * @speed_hz: Temporary override of the device's bitrate.
 * @bits_per_word: Temporary override of the device's wordsize.
 * @delay_usecs: If nonzero, how long to delay after the last bit transfer
 *    before optionally deselecting the device before the next transfer.
 * @cs_change: True to deselect device before starting the next transfer.
 *
 * This structure is mapped directly to the kernel spi_transfer structure;
 * the fields have the same meanings, except of course that the pointers
 * are in a different address space (and may be of different sizes in some
 * cases, such as 32-bit i386 userspace over a 64-bit x86_64 kernel).
 * Zero-initialize the structure, including currently unused fields, to
 * accomodate potential future updates.
 *
 * SPI_IOC_MESSAGE gives userspace the equivalent of kernel spi_sync().
 * Pass it an array of related transfers, they'll execute together.
 * Each transfer may be half duplex (either direction) or full duplex.
 *
 *    struct spi_ioc_transfer mesg[4];
 *    ...
 *    status = ioctl(fd, SPI_IOC_MESSAGE(4), mesg);
 *
 * So for example one transfer might send a nine bit command (right aligned
 * in a 16-bit word), the next could read a block of 8-bit data before
 * terminating that command by temporarily deselecting the chip; the next
 * could send a different nine bit command (re-selecting the chip), and the
 * last transfer might write some register values.
 */
struct spi_ioc_transfer {
    __u64        tx_buf;
    __u64        rx_buf;

    __u32        len;
    __u32        speed_hz;

    __u16        delay_usecs;
    __u8        bits_per_word;
    __u8        cs_change;
    __u32        pad;

};
spi_ioc_transfer可以动态的改变各种传输特性,并且可以将多个spi_ioc_transfer组织成一个message连续传输,比read/write方式提供了更多灵活性。

static int spidev_ioctl(struct inode *inode, struct file *filp,
        unsigned int cmd, unsigned long arg)
{
    int            err = 0;
    int            retval = 0;
    struct spidev_data    *spidev;
    struct spi_device    *spi;
    u32            tmp;
    unsigned        n_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;

    spidev = filp->private_data;
    spi = spidev->spi;  // 找到对应的struct spi_device

    switch (cmd) {
    /* read requests */  // 读取该slave的相关属性
    case SPI_IOC_RD_MODE:

    case SPI_IOC_RD_LSB_FIRST:

    case SPI_IOC_RD_BITS_PER_WORD:

    case SPI_IOC_RD_MAX_SPEED_HZ:
        retval = __put_user(spi->max_speed_hz, (__u32 __user *)arg);
        break;

    /* write requests */
    case SPI_IOC_WR_MODE:
        retval = __get_user(tmp, (u8 __user *)arg);
        if (retval == 0) {
            u8    save = spi->mode;

            if (tmp & ~SPI_MODE_MASK) {
      

抱歉!评论已关闭.