spi 协议硬件分析以及在linux上的实现分析

Spi几种模式:
模式0: CPOL=0 CPHA=0
模式1: CPOL=0 CPHA=1
模式2: CPOL=1 CPHA=0
模式3: CPOL=1 CPHA=1

现在看看3模式 1.CLK空闲的时候为高电平 [CPOL = 1]
2.在第二个边沿采样 [CPHA = 1]

发送一字节 8bit数据 0xF
如下为示波器波形:
这里写图片描述

不好意思开个玩笑:

这里写图片描述

再来看看如果是传送 两个字节 16bit的数据呢,时序图是怎么样的, 此时我将CS也加上了[由于只有两个探头,故用两张图来描述]
图片一[ 黄色CH1 为 CS信号 蓝绿色CH2为 CLK信号]:
这里写图片描述

图片二[ 黄色CH1 为 CLK信号 蓝绿色CH2为 MOSI信号]:
这里写图片描述

可以看到此时16bit 的数据 较之于 8bit 的数据

相同点是:他们同一个CS周期内完成的, 紧跟在连续的8个时钟周期之后 开始下一个连续的8个时钟周期.

关于linux c 内核里有例程,相信很多博客都有提及 [ 里面有相关的API使用说明以及设备树, 设备驱动的配置说明 ]
路径: 内核根目录下的Documentation/spi目录中:

~/base/code/s905x-karaoke/s912_0907/common/Documentation/spi $ ls
00-INDEX   ep93xx_spi  pxa2xx  spidev_fdx.c   spi-lm70llp    spi-summary
butterfly  Makefile    spidev  spidev_test.c  spi-sc18is602

以下是spidev_test.c的源码:

 * SPI testing utility (using spidev driver)
 *
 * Copyright (c) 2007  MontaVista Software, Inc.
 * Copyright (c) 2007  Anton Vorontsov <avorontsov@ru.mvista.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License.
 *
 * Cross-compile with cross-gcc -I/path/to/cross-kernel/include
 */

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

static void pabort(const char *s)
{
    perror(s);
    abort();
}

static const char *device = "/dev/spidev1.1";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 500000;
static uint16_t delay;

static void transfer(int fd)
{
    int ret;
    uint8_t tx[] = {
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
        0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
        0xF0, 0x0D,
    };
    uint8_t rx[ARRAY_SIZE(tx)] = {0, };
    struct spi_ioc_transfer tr = {
        .tx_buf = (unsigned long)tx,
        .rx_buf = (unsigned long)rx,
        .len = ARRAY_SIZE(tx),
        .delay_usecs = delay,
        .speed_hz = speed,
        .bits_per_word = bits,
    };

    ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
    if (ret < 1)
        pabort("can't send spi message");

    for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
        if (!(ret % 6))
            puts("");
        printf("%.2X ", rx[ret]);
    }
    puts("");
}

static void print_usage(const char *prog)
{
    printf("Usage: %s [-DsbdlHOLC3]\n", prog);
    puts("  -D --device   device to use (default /dev/spidev1.1)\n"
         "  -s --speed    max speed (Hz)\n"
         "  -d --delay    delay (usec)\n"
         "  -b --bpw      bits per word \n"
         "  -l --loop     loopback\n"
         "  -H --cpha     clock phase\n"
         "  -O --cpol     clock polarity\n"
         "  -L --lsb      least significant bit first\n"
         "  -C --cs-high  chip select active high\n"
         "  -3 --3wire    SI/SO signals shared\n");
    exit(1);
}

static void parse_opts(int argc, char *argv[])
{
    while (1) {
        static const struct option lopts[] = {
            { "device",  1, 0, 'D' },
            { "speed",   1, 0, 's' },
            { "delay",   1, 0, 'd' },
            { "bpw",     1, 0, 'b' },
            { "loop",    0, 0, 'l' },
            { "cpha",    0, 0, 'H' },
            { "cpol",    0, 0, 'O' },
            { "lsb",     0, 0, 'L' },
            { "cs-high", 0, 0, 'C' },
            { "3wire",   0, 0, '3' },
            { "no-cs",   0, 0, 'N' },
            { "ready",   0, 0, 'R' },
            { NULL, 0, 0, 0 },
        };
        int c;

        c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);

        if (c == -1)
            break;

        switch (c) {
        case 'D':
            device = optarg;
            break;
        case 's':
            speed = atoi(optarg);
            break;
        case 'd':
            delay = atoi(optarg);
            break;
        case 'b':
            bits = atoi(optarg);
            break;
        case 'l':
            mode |= SPI_LOOP;
            break;
        case 'H':
            mode |= SPI_CPHA;
            break;
        case 'O':
            mode |= SPI_CPOL;
            break;
        case 'L':
            mode |= SPI_LSB_FIRST;
            break;
        case 'C':
            mode |= SPI_CS_HIGH;
            break;
        case '3':
            mode |= SPI_3WIRE;
            break;
        case 'N':
            mode |= SPI_NO_CS;
            break;
        case 'R':
            mode |= SPI_READY;
            break;
        default:
            print_usage(argv[0]);
            break;
        }
    }
}

int main(int argc, char *argv[])
{
    int ret = 0;
    int fd;

    parse_opts(argc, argv);

    fd = open(device, O_RDWR);
    if (fd < 0)
        pabort("can't open device");

    /*
     * spi mode
     */
    ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
    if (ret == -1)
        pabort("can't set spi mode");

    ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
    if (ret == -1)
        pabort("can't get spi mode");
    /*
     * bits per word
     */
    ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
    if (ret == -1)
        pabort("can't set bits per word");

    ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
    if (ret == -1)
        pabort("can't get bits per word");

    /*
     * max speed hz
     */
    ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    if (ret == -1)
        pabort("can't set max speed hz");

    ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
    if (ret == -1)
        pabort("can't get max speed hz");

    printf("spi mode: %d\n", mode);
    printf("bits per word: %d\n", bits);
    printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);

    transfer(fd);

    close(fd);

    return ret;
}                        

1.void parse_opts(int argc, char *argv[])
parse_opts用于处理 运行linux c 时带参解析 [ speed mode CPOL CPHA等等都可以通过这个设置]

2.一系列ioctl检测和设置数值
读写检测 SPI_IOC_WR_MODE SPI_IOC_RD_MODE
设置读写每字节的bit数 我设置的都是8bits/w SPI_IOC_WR_BITS_PER_WORD SPI_IOC_RD_BITS_PER_WORD
设置读写最大速率 SPI_IOC_WR_MAX_SPEED_HZ SPI_IOC_RD_MAX_SPEED_HZ

3.demo的最后通过一个简单的transfer传输了一个数组

构建一个spi_ioc_transfer:

    struct spi_ioc_transfer tr = {     
        .tx_buf = (unsigned long)tx,   
        .rx_buf = (unsigned long)rx,   
        .len = ARRAY_SIZE(tx),
        .delay_usecs = delay, 
        .speed_hz = speed,    
        .bits_per_word = bits,
    };

使用ioctl 通过 SPI_IOC_MESSAGE(1) 发送这个MESSAGE!

当然这只是用户空间的一个demo而已,
其中的ioctl相关操作当然是通过

我们打开的设备

fd = open(device, O_RDWR);

通过文件句柄进行的相关操作,包括读写, ioctl这些.

关于用户空间是如何调用到内核空间的方法, 详细请看LDD3

在设备驱动注册进内核的时候,相关的文件操作接口方法表 file_opration 等的地址其实是包含在特定驱动的数据结构中的.

下面分析下设备和驱动之间的关系,中间可以穿插spidev驱动的设计模式来说明:

第一步应该是设备的创建
通过热插拔,动态插入设备模块注册,设备树等将模块信息注册进内核了.

第二步是驱动程序的注册
[ 驱动程序可编入内核,也可动态插入模块]

[[
1.spidev中在driver模块插入的时候,采用老的静态方法指定设备号, 注册[register_chrdev]了字符驱动并且
2.通过class_create在sysfs中创建了一个spi目录
3.然后spi_register_driver注册了spi-dev的驱动
]]

第三步是设备与驱动的匹配,

首先是驱动中要将感兴趣的设备特征陈列出来,这就需要构建一个of_match_table
其中compatible是关键识别码

static const struct of_device_id spidev_dt_ids[] = {
    { .compatible = "amlogic, spidev" },
    {},
};
MODULE_DEVICE_TABLE(of, spidev_dt_ids);

在特定驱动数据结构的driver成员中填充好of_match_table [*必须项]

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函数,
一般会先初始化driver相关的数据结构
然后在driver的数据结构中可以将包含device的相关信息导入进driver数据结构中
,此时亦可在文件系统中大展身手,通常情况下,匹配的过程中都会在sysfs中通过class_attribute创建DBG的节点
举个例子:

 #define SPI_READ_ATTR S_IRUGO
 #define SPI_WRITE_ATTR S_IWUGO
  //S_IRUGO = (S_IRUSR | S_IRGRP | S_IRUGO)

  static struct class_attribute hello_class_attrs[] = {
          __ATTR(test_store,  SPI_WRITE_ATTR, NULL,    store_test),
          __ATTR(test_show,  SPI_READ_ATTR, show_test, NULL),                                                                          
          __ATTR_NULL         
  };   

然后在probe函数中:
struct hellodev_data  *hellodev_data;
hellodev_data = kzalloc(sizeof(*hellodev_data), GFP_KERNEL);
...
hellodev_data->cls.name = kzalloc(10, GFP_KERNEL);
sprintf((char *)hellodev_data->cls.name, "hello%d", (int)0);

hellodev_data->cls.class_attrs = hello_class_attrs;
ret = class_register(&hellodev_data->cls);

此时在设计与设备信息相关的结构体的时候,也注意要将class放入

struct hellodev_data {        
      dev_t           devt;   
      spinlock_t      spi_lock;
      struct spi_device   *spi;
      struct list_head    device_entry;                                                                                                

      /* buffer is NULL unless this device is open (users > 0) */
      struct mutex        buf_lock;  
      unsigned        users;  
      u8          *buffer; 
      struct class cls;    
  };   

[[
1.spidev在probe函数开始的地方也未能幸免,同样落入俗套
先初始化了一个类型为spidev_data 的数据结构,其中保存probe传过来的spi_device信息
2.为设备找到一个空闲的次设备号, 并且使用device_create创建设备节点
(1)minor = find_first_zero_bit(minors, N_SPI_MINORS);

(2)spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
dev = device_create(spidev_class, &spi->dev, spidev->devt,
spidev, “spidev%d.%d”,
spi->master->bus_num, spi->chip_select);


/dev/spi0.0 /dev/spi0.1 …. /dev/spi1.0 /dev/spi1.1 …..

“/dev/spi%d.%d”, spi->master->bus_num, spi->chip_select
3.set_bit(minor, minors); 将当前已经用过的minor次设备号 告知minors
3.将描述该设备数据结构的spidev_data加入到链表中
4.将spidev_data 设置为设备的驱动数据.
]]

第四部是分析open函数:

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->devt == inode->i_rdev) {
            status = 0;       
            break;
        }
    }  
    if (status == 0) {
        if (!spidev->buffer) {
            spidev->buffer = kmalloc(bufsiz, GFP_KERNEL); 
            if (!spidev->buffer) {         
                dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
                status = -ENOMEM;              
            }                 
        }
        if (status == 0) {
            spidev->users++;
            filp->private_data = spidev;   
            nonseekable_open(inode, filp); 
        }
    } else
        pr_debug("spidev: nothing for minor %d\n", iminor(inode));

    mutex_unlock(&device_list_lock);
    return status;
}

open函数中参数列表是 inode 和 filp.
关于这两个参数:
inode 是节点, kdev_t i_rdev字段中保存了device的信息, 节点中保存了当用户空间操作设备文件, 所对应的底层处理, 底层处理包括以什么样的驱动处理 字符 块设备?
filp 是文件描述符 用户空间通过fp和inode相互联系 通过inode又可以找到对应的cdev 或者其它类型设备描述结构 通过其指定的驱动就可以对文件系统中的文件进行底层相应.

1.首先遍历device_list链表 通过成员spidev->devt  找到那个次设备号是inode->i_rdev 的 spidev_data类型数据的spidev节点.
2.分配spidev->buffer 定义的是4096个字节.
3.将spidev保存到filp->private_data中.
4.调用nonseekable_open(inode, filp); 通知内核该设备驱动不支持lseek操作.

当然在方法表中也已经声明成了no_llseek

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

使用数据区时,可以使用 lseek 来往上往下地定位数据。但像串口或键盘一类设备,使用的是数据流,所以定位这些设备没有意义;在这种情况下,不能简单地不声明 llseek 操作,因为默认方法是允许定位的。


在 open 方法中调用 nonseekable_open() 时,它会通知内核设备不支持 llseek,nonseekable_open() 函数的实现定义在 fs/open.c 中

关于nonseekable_open 具体可以参考这篇博文(http://blog.csdn.net/gongmin856/article/details/8273545)

第五部分是分析下ioctl
众所周知在linux 2.6 file_operation中旧的 ioctl就已经被干掉了 而取而代之的是 unlocked_ioctl

1.ioctl的常规检查
包括检查幻数 或者啊 根据cmd是读写操作而检查文件系统中该设备的可读写性.
(1)检查幻数

/* Check type and command number */
    if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)
        return -ENOTTY;  spidev = filp->private_data;   

(2)读写检查

 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;

(3)取出设备相关的数据
很常规的操作, 拿到文件描述符存储的私有数据 filp->private_data
spidev = filp->private_data;
然后通过设备封装的方法拿出device相关的数据
spi = spi_dev_get(spidev->spi);

    /* guard against device removal before, or while,
     * we issue this ioctl.
     */
    spidev = filp->private_data;
    spin_lock_irq(&spidev->spi_lock);
    spi = spi_dev_get(spidev->spi);
    spin_unlock_irq(&spidev->spi_lock);

    if (spi == NULL)
        return -ESHUTDOWN;

需要注意下这两个数据结构 spidev_data 和 spi_device
下面先把这两个数据结构的原型丢出来
接下来我还会把这个重要的头文件spi.h源码贴出来.

 struct spidev_data  *spidev;
 struct spi_device   *spi;

spi_device

  struct spi_device {
    struct device       dev;
    struct spi_master   *master;
    u32         max_speed_hz;
    u8          chip_select;
    u8          bits_per_word;
    u16         mode;
    int         irq;
    void            *controller_state;
    void            *controller_data;
    char            modalias[SPI_NAME_SIZE];
    int         cs_gpio;    /* chip select gpio */

spidev_data

struct spidev_data {                                                                                                                 
>>    dev_t           devt;
>>    spinlock_t      spi_lock;
      struct spi_device   *spi;
>>    struct list_head    device_entry;

      /* buffer is NULL unless this device is open (users > 0) */
>>    struct mutex        buf_lock;
      unsigned        users;
>>    u8          *buffer;
  };

spi_device 是probe 时内核传递过来的描述设备信息的数据结构,可以从数据结构中清晰看出他的意图
每一个设备模型都有的再熟悉不过的数据结构 struct device !
第二个是struct spi_master 看起来很陌生 因为它完完全全跟我们的设备驱动扯不上关系, 甚至是抽象的, 甚至像我们设备驱动从未谋面的亲生父母?
回头一想,如果接触过i2c系统的老表们, spi_master 是不是和i2c_adapter这些有异曲同工之妙呢?
如果是的话, 他们为何都这样设计呢?

答案就是 不管是i2c 还是spi 我们驱动编写人员都只是在编写他们的设备驱动而已
而该类协议i2c 抑或 spi 都是已经固定不变的协议了

所谓协议就是 玩家甲 与玩家乙 甲是中国人 乙是老美 
两个玩家联机玩游戏 他们虽然不是一个国家的人 但是他们都知道玩石头剪刀布
只能一次出一个 只能出石头 剪刀或者布

这就是协议
有了协议就可以避免很多沟通之间的误会与误解. 甲乙事先不需要沟通或者只需要简单沟通[如确认对方四肢健全 没有少手指 或者不是智障?这样真的好吗 问号脸]

所以spi_master 和i2c_adapter就做了这部分工作.
可以说他们是内核中的协议层

具体表现在设备驱动中的就是 当我们去实现某些接口时,比如file_operation中的读写操作
我们只需要给出要传输的数据, 然后设置好传输的模式,速率,每字节bit数等,将数据封装成 协议层给出的标准数据结构
然后以spi_transfer 与spi_message的成员与链表形式 将自己的封装数据spi_ioc_message发出去就行了.
至于协议层如何提取message,然后将message中的数据解析出来,再根据与设备信息相关的寄存器
将内存映射,进行setb操作等等, 都是协议层去做的事情.

我们只是做了驱动层的应用部分,其实不然,使用的大多是内核提供的数据结构, 调用的大部分是内核提供的接口.

所以驱动人员比起LINUX内核主线维护人员不知要轻松多少倍!!!!

(4)大case
在硕大无比的case中,包裹了很多内涵信息,现在举两个例子
<1> case SPI_IOC_RD_MAX_SPEED_HZ:

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

这里使用了__put_user这个接口, 自然而然就是直接从内核空间put 到用户空间的接口, 一系列调用之后将spi中保存的最大速度传递给用户空间地址的arg变量.
特别要注意的是 这个__user 修饰, 这应该是个内核的关键字, 表明arg是表示的用户空间地址.
<2> case SPI_IOC_WR_MAX_SPEED_HZ:

case SPI_IOC_WR_MAX_SPEED_HZ:
    retval = __get_user(tmp, (__u32 __user *)arg);
          if (retval == 0) {
              u32 save = spi->max_speed_hz;

              spi->max_speed_hz = tmp;
              retval = spi_setup(spi);
              if (retval < 0)
                  spi->max_speed_hz = save;
              else
                  dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
          }
          break;

这个使用了__get_user和__put_user作用相反, 原理都一样.

同时注意:
在重新设置了spi通讯相关的参数之后, spi_setup了一次.这也是通用的master-slava设备模型所需要的.

(3)接下来是默认操作了

    default:
          /* segmented and/or full-duplex I/O request */
          if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))
                  || _IOC_DIR(cmd) != _IOC_WRITE) {
              retval = -ENOTTY;
              break;
          }

          tmp = _IOC_SIZE(cmd);
          if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {
              retval = -EINVAL;
              break;
          }
          n_ioc = tmp / sizeof(struct spi_ioc_transfer);
          if (n_ioc == 0)
              break;

          /* copy into scratch area */
          ioc = kmalloc(tmp, GFP_KERNEL);
          if (!ioc) {
              retval = -ENOMEM;
              break;
          }
          if (__copy_from_user(ioc, (void __user *)arg, tmp)) {
              kfree(ioc);
              retval = -EFAULT;
              break;
          }

          /* translate to spi_message, execute */
          retval = spidev_message(spidev, ioc, n_ioc);
          kfree(ioc);
          break;
      }                  

记得我们是这样发送的:ioctl(fd, SPI_IOC_MESSAGE(1), &argv)
那么这里就是创建了一个spi_ioc_transfer

 tmp = _IOC_SIZE(cmd);
          if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) {
              retval = -EINVAL;
              break;
          }
          n_ioc = tmp / sizeof(struct spi_ioc_transfer);
          if (n_ioc == 0)
              break;

然后在内核分配空间,将参数列表拷贝到ioc中,

 /* copy into scratch area */
          ioc = kmalloc(tmp, GFP_KERNEL);
          if (!ioc) {
              retval = -ENOMEM;
              break;
          }
          if (__copy_from_user(ioc, (void __user *)arg, tmp)) {
              kfree(ioc);
              retval = -EFAULT;
              break;
          }

(5)最后launch发送message

     /* translate to spi_message, execute */
          retval = spidev_message(spidev, ioc, n_ioc); 

接下来就是分析spidev_message了,代码比前面的要稍微长一点点而已.

  static int spidev_message(struct spidev_data *spidev,
          struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
  {
      struct spi_message  msg;
      struct spi_transfer *k_xfers;
      struct spi_transfer *k_tmp;
      struct spi_ioc_transfer *u_tmp;
      unsigned        n, total;
      u8          *buf;
      int         status = -EFAULT;

      spi_message_init(&msg);
      k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);
      if (k_xfers == NULL)
          return -ENOMEM;

      /* Construct spi_message, copying any tx data to bounce buffer.
       * We walk the array of user-provided transfers, using each one
       * to initialize a kernel version of the same transfer.
       */ 
      buf = spidev->buffer;
      total = 0;
      for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
              n;
              n--, k_tmp++, u_tmp++) {
          k_tmp->len = u_tmp->len;

          total += k_tmp->len;
          if (total > bufsiz) {
              status = -EMSGSIZE;
              goto done;
          }

          if (u_tmp->rx_buf) {
              k_tmp->rx_buf = buf;
              if (!access_ok(VERIFY_WRITE, (u8 __user *)
                          (uintptr_t) u_tmp->rx_buf,
                          u_tmp->len))
                  goto done;
          }
          if (u_tmp->tx_buf) {
              k_tmp->tx_buf = buf;
              if (copy_from_user(buf, (const u8 __user *)
                          (uintptr_t) u_tmp->tx_buf,
                      u_tmp->len))
                  goto done;
          }
          buf += k_tmp->len;

          k_tmp->cs_change = !!u_tmp->cs_change;
          k_tmp->bits_per_word = u_tmp->bits_per_word;
          k_tmp->delay_usecs = u_tmp->delay_usecs;
          k_tmp->speed_hz = u_tmp->speed_hz;
  #ifdef VERBOSE
          dev_dbg(&spidev->spi->dev,
              "  xfer len %zd %s%s%s%dbits %u usec %uHz\n",
              u_tmp->len,
              u_tmp->rx_buf ? "rx " : "",
              u_tmp->tx_buf ? "tx " : "",
              u_tmp->cs_change ? "cs " : "",
              u_tmp->bits_per_word ? : spidev->spi->bits_per_word,
              u_tmp->delay_usecs, 
              u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
  #endif  
          spi_message_add_tail(k_tmp, &msg);
      }

      status = spidev_sync(spidev, &msg);
      if (status < 0)
          goto done;

      /* copy any rx data out of bounce buffer */
      buf = spidev->buffer;
      for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
          if (u_tmp->rx_buf) {
              if (__copy_to_user((u8 __user *)
                      (uintptr_t) u_tmp->rx_buf, buf,
                      u_tmp->len)) {
                  status = -EFAULT;
                  goto done;
              }
          }
          buf += u_tmp->len;
      }
      status = total;

  done:
      kfree(k_xfers);
      return status;
  }

1.这部分关键的就是三个数据结构

 struct spi_message
 struct spi_transfer
 struct spi_ioc_transfer

原型在下面:

spi_message原型:

  struct spi_message {
      struct list_head    transfers;

      struct spi_device   *spi;

      unsigned        is_dma_mapped:1;
      /* completion is reported through a callback */
      void            (*complete)(void *context);
      void            *context;
      unsigned        frame_length;
      unsigned        actual_length;
      int         status;
      struct list_head    queue;
      void            *state;

spi_transfer原型

  struct spi_transfer {       
      /* it's ok if tx_buf == rx_buf (right?)
       * for MicroWire, one buffer must be null
       * buffers must work with dma_*map_single() calls, unless
       *   spi_message.is_dma_mapped reports a pre-existing mapping                                                                    
       */
      const void  *tx_buf;    
      void        *rx_buf;
      unsigned    len;        

      dma_addr_t  tx_dma;     
      dma_addr_t  rx_dma;     

      unsigned    cs_change:1;
      unsigned    tx_nbits:3; 
      unsigned    rx_nbits:3; 
  #define SPI_NBITS_SINGLE    0x01 /* 1bit transfer */
  #define SPI_NBITS_DUAL      0x02 /* 2bits transfer */
  #define SPI_NBITS_QUAD      0x04 /* 4bits transfer */
      u8      bits_per_word;  
      u16     delay_usecs;    
      u32     speed_hz;       

      struct list_head transfer_list;                                                                                                  
  };   

spi_ioc_transfer

struct spi_ioc_transfer {
 __u64 tx_buf;
 __u64 rx_buf;
 __u32 len;
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
 __u32 speed_hz;
 __u16 delay_usecs;
 __u8 bits_per_word;
 __u8 cs_change;
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
 __u32 pad;
};

2.开始首先以spi_message->transfers链表头初始化链表.
spi_message_init实际就是封装了初始链表的内核接口.

static inline void spi_message_init(struct spi_message *m)
  {
      memset(m, 0, sizeof *m);
      INIT_LIST_HEAD(&m->transfers);                                                                                                   
  }

然后初始化需要多个spi_transfer
说白了就是填充spi_transfer这个数据结构 包括从 spi_ioc_transfer结构中tx_buf所指向的用户空间地址拷贝数据到 spi_transfer 所指向的tx_buf内核地址空间.

然后将device相关的参数都拷贝一份到spi_transfer中.

接着也是链表操作,把这个spi_transfer插入到链表尾部

spi_message_add_tail(k_tmp, &msg);

然后还有一步:

  /* copy any rx data out of bounce buffer */                                                                                      
      buf = spidev->buffer;
      for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
          if (u_tmp->rx_buf) {
              if (__copy_to_user((u8 __user *)
                      (uintptr_t) u_tmp->rx_buf, buf,
                      u_tmp->len)) {
                  status = -EFAULT;
                  goto done;
              }
          }
          buf += u_tmp->len;
      }
      status = total;

这里是拷贝spi_transfer->rx_buf的数据到用户空间的spi_ioc_message->rx_buf中.

我们也可以管中窥豹,在spidev_test的 transfer函数中我们就看到put spi_ioc_transfer->rx_buf的数据.

不过我目前打印出的rx_buf都是0.
tx_buf是我要发送的数据.

剩下的接口没什么分析的价值了,如果有时间还会写写关于spi_master 控制器层也称协议层的代码,看看底层实现如何!

向上吧,少年!!!!
!!




下面的内容是spi.h的简单列出, 方便查询API和数据结构.

在<linux/spi.h>中,可以看到
Linux C中的定义:
      #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_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 */

下面我们来详细看看spi.h中的数据结构以及 常用的操作函数.

#ifndef __LINUX_SPI_H
#define __LINUX_SPI_H

#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/completion.h>

/*
 * INTERFACES between SPI master-side drivers and SPI infrastructure.
 * (There's no SPI slave support for Linux yet...)
 */
extern struct bus_type spi_bus_type;

spi_device结构的原型



   /**
   * 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
   * @modalias: Name of the driver to use with this device, or an alias
   *  for that name.  This appears in the sysfs "modalias" attribute
   *  for driver coldplugging, and in uevents used for hotplugging
   * @cs_gpio: gpio number of the chipselect line (optional, -ENOENT when
   *  when not using a GPIO line)
   *
   * 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          bits_per_word;
    u16         mode;
    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
     *  - ...
     */
#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 */

};

spi_device 相关的操作

static inline struct spi_device *to_spi_device(struct device *dev)
{
    return dev ? container_of(dev, struct spi_device, dev) : NULL;
}

/* most drivers won't need to care about device refcounting */
static inline struct spi_device *spi_dev_get(struct spi_device *spi)
{
    return (spi && get_device(&spi->dev)) ? spi : NULL;
}

static inline void spi_dev_put(struct spi_device *spi)
{
    if (spi)
        put_device(&spi->dev);
}

/* ctldata is for the bus_master driver's runtime state */
static inline void *spi_get_ctldata(struct spi_device *spi)
{
    return spi->controller_state;
}

static inline void spi_set_ctldata(struct spi_device *spi, void *state)
{
    spi->controller_state = state;
}

/* device driver data */
static inline void spi_set_drvdata(struct spi_device *spi, void *data)
{
    dev_set_drvdata(&spi->dev, data);
}

static inline void *spi_get_drvdata(struct spi_device *spi)
{
    return dev_get_drvdata(&spi->dev);
}

struct spi_message;
struct spi_transfer;

spi_driver结构和相关的:

/**
 * struct spi_driver - Host side "protocol" driver
 * @id_table: List of SPI devices supported by this 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 {
    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; }; static inline struct spi_driver *to_spi_driver(struct device_driver *drv) { return drv ? container_of(drv, struct spi_driver, driver) : NULL; } extern int spi_register_driver(struct spi_driver *sdrv); /** * spi_unregister_driver - reverse effect of spi_register_driver * @sdrv: the driver to unregister * Context: can sleep */ static inline void spi_unregister_driver(struct spi_driver *sdrv) { if (sdrv) driver_unregister(&sdrv->driver); } /** * module_spi_driver() - Helper macro for registering a SPI driver * @__spi_driver: spi_driver struct * * Helper macro for SPI drivers which do not do anything special in module * init/exit. This eliminates a lot of boilerplate. Each module may only * use this macro once, and calling it replaces module_init() and module_exit() */ #define module_spi_driver(__spi_driver) \ module_driver(__spi_driver, spi_register_driver, \ spi_unregister_driver)

spi_master结构体

/**
 * struct spi_master - interface to SPI master controller
 * @dev: device interface to this driver
 * @list: link with the global spi_master list
 * @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.
 * @dma_alignment: SPI controller constraint on DMA buffers alignment.
 * @mode_bits: flags understood by this controller driver
 * @bits_per_word_mask: A mask indicating which values of bits_per_word are
 *  supported by the driver. Bit n indicates that a bits_per_word n+1 is
 *  suported. If set, the SPI core will reject any transfer with an
 *  unsupported bits_per_word. If not set, this value is simply ignored,
 *  and it's up to the individual driver to perform any validation.
 * @min_speed_hz: Lowest supported transfer speed
 * @max_speed_hz: Highest supported transfer speed
 * @flags: other constraints relevant to this driver
 * @bus_lock_spinlock: spinlock for SPI bus locking
 * @bus_lock_mutex: mutex for SPI bus locking
 * @bus_lock_flag: indicates that the SPI bus is locked for exclusive use
 * @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
 * @queued: whether this master is providing an internal message queue
 * @kworker: thread struct for message pump
 * @kworker_task: pointer to task for message pump kworker thread
 * @pump_messages: work struct for scheduling work to the message pump
 * @queue_lock: spinlock to syncronise access to message queue
 * @queue: message queue
 * @cur_msg: the currently in-flight message
 * @cur_msg_prepared: spi_prepare_message was called for the currently
 *                    in-flight message
 * @xfer_completion: used by core tranfer_one_message()
 * @busy: message pump is busy
 * @running: message pump is running
 * @rt: whether this queue is set to run as a realtime task
 * @auto_runtime_pm: the core should ensure a runtime PM reference is held
 *                   while the hardware is prepared, using the parent
 *                   device for the spidev
 * @prepare_transfer_hardware: a message will soon arrive from the queue
 *  so the subsystem requests the driver to prepare the transfer hardware
 *  by issuing this call
 * @transfer_one_message: the subsystem calls the driver to transfer a single
 *  message while queuing transfers that arrive in the meantime. When the
 *  driver is finished with this message, it must call
 *  spi_finalize_current_message() so the subsystem can issue the next
 *  message
 * @unprepare_transfer_hardware: there are currently no more messages on the
 *  queue so the subsystem notifies the driver that it may relax the
 *  hardware by issuing this call
 * @set_cs: set the logic level of the chip select line.  May be called
 *          from interrupt context.
 * @prepare_message: set up the controller to transfer a single message,
 *                   for example doing DMA mapping.  Called from threaded
 *                   context.
 * @transfer_one: transfer a single spi_transfer.
 *                  - return 0 if the transfer is finished,
 *                  - return 1 if the transfer is still in progress. When
 *                    the driver is finished with this transfer it must
 *                    call spi_finalize_current_transfer() so the subsystem
 *                    can issue the next transfer. Note: transfer_one and
 *                    transfer_one_message are mutually exclusive; when both
 *                    are set, the generic subsystem does not call your
 *                    transfer_one callback.
 * @unprepare_message: undo any work done by prepare_message().
 * @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
 *  number. Any individual value may be -ENOENT for CS lines that
 *  are not GPIOs (driven by the SPI controller itself).
 *
 * 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;

    struct list_head list;
        /* 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;

    /* some SPI controllers pose alignment requirements on DMAable
     * buffers; let protocol drivers know about these requirements.
     */
    u16         dma_alignment;

    /* spi_device.mode flags understood by this controller driver */
    u16         mode_bits;

    /* bitmask of supported bits_per_word for transfers */
    u32         bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))

    /* limits on transfer speed */
    u32         min_speed_hz;
    u32         max_speed_hz;

    /* other constraints relevant to this driver */
    u16         flags;
#define SPI_MASTER_HALF_DUPLEX  BIT(0)      /* can't do full duplex */
#define SPI_MASTER_NO_RX    BIT(1)      /* can't do buffer read */
#define SPI_MASTER_NO_TX    BIT(2)      /* can't do buffer write */

    /* lock and mutex for SPI bus locking */
    spinlock_t      bus_lock_spinlock;
    struct mutex        bus_lock_mutex;

    /* flag indicating that the SPI bus is locked for exclusive use */
    bool            bus_lock_flag;

    /* Setup mode and clock, etc (spi driver may call many times).
     *
     * IMPORTANT:  this may be called when transfers to another
     * device are active.  DO NOT UPDATE SHARED REGISTERS in ways
     * which could break those transfers.
     */
    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);

    /*
     * These hooks are for drivers that want to use the generic
     * master transfer queueing mechanism. If these are used, the
     * transfer() function above must NOT be specified by the driver.
     * Over time we expect SPI drivers to be phased over to this API.
     */
    bool                queued;
    struct kthread_worker       kworker;
    struct task_struct      *kworker_task;
    struct kthread_work     pump_messages;
    spinlock_t          queue_lock;
    struct list_head        queue;
    struct spi_message      *cur_msg;
    bool                busy;
    bool                running;
    bool                rt;
    bool                auto_runtime_pm;
    bool                            cur_msg_prepared;
    struct completion               xfer_completion;

    int (*prepare_transfer_hardware)(struct spi_master *master);
    int (*transfer_one_message)(struct spi_master *master, struct spi_message *mesg); int (*unprepare_transfer_hardware)(struct spi_master *master); int (*prepare_message)(struct spi_master *master, struct spi_message *message); int (*unprepare_message)(struct spi_master *master, struct spi_message *message); /* * These hooks are for drivers that use a generic implementation * of transfer_one_message() provied by the core. */ void (*set_cs)(struct spi_device *spi, bool enable); int (*transfer_one)(struct spi_master *master, struct spi_device *spi, struct spi_transfer *transfer); /* gpio chip select */ int *cs_gpios; }; 

spi_master相关函数:

static inline void *spi_master_get_devdata(struct spi_master *master)
{
    return dev_get_drvdata(&master->dev);
}

static inline void spi_master_set_devdata(struct spi_master *master, void *data)
{
    dev_set_drvdata(&master->dev, data);
}

static inline struct spi_master *spi_master_get(struct spi_master *master)
{
    if (!master || !get_device(&master->dev))
        return NULL;
    return master;
}

static inline void spi_master_put(struct spi_master *master)
{
    if (master)
        put_device(&master->dev);
}

/* PM calls that need to be issued by the driver */
extern int spi_master_suspend(struct spi_master *master);
extern int spi_master_resume(struct spi_master *master);

/* Calls the driver make to interact with the message queue */
extern struct spi_message *spi_get_next_queued_message(struct spi_master *master);
extern void spi_finalize_current_message(struct spi_master *master);
extern void spi_finalize_current_transfer(struct spi_master *master);

/* the spi driver core manages memory for the spi_master classdev */
extern struct spi_master *
spi_alloc_master(struct device *host, unsigned size);

extern int spi_register_master(struct spi_master *master);
extern int devm_spi_register_master(struct device *dev,
                    struct spi_master *master);
extern void spi_unregister_master(struct spi_master *master);

extern struct spi_master *spi_busnum_to_master(u16 busnum);

spi_transfer和spi_message是一对

/*
 * 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.)
 *
 * NOTE:  Allocation of spi_transfer and spi_message memory is entirely
 * up to the protocol driver, which guarantees the integrity of both (as
 * well as the data buffers) for as long as the message is queued.
 */

/**
 * 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
 * @tx_nbits: number of bits used for writting. If 0 the default
 *      (SPI_NBITS_SINGLE) is used.
 * @rx_nbits: number of bits used for reading. If 0 the default
 *      (SPI_NBITS_SINGLE) is used.
 * @len: size of rx and tx buffers (in bytes)
 * @speed_hz: Select a speed other than the device default for this
 *      transfer. If 0 the default (from @spi_device) is used.
 * @bits_per_word: select a bits_per_word other than 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.
 * In some cases, they may also want to provide DMA addresses for
 * the data being transferred; that may reduce overhead, when the
 * underlying driver uses dma.
 *
 * 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.
 *
 * When SPI can transfer in 1x,2x or 4x. It can get this tranfer information
 * from device through @tx_nbits and @rx_nbits. In Bi-direction, these
 * two should both be set. User can set transfer mode with SPI_NBITS_SINGLE(1x)
 * SPI_NBITS_DUAL(2x) and SPI_NBITS_QUAD(4x) to support these three transfer.
 *
 * 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 {
    /* it's ok if tx_buf == rx_buf (right?)
     * for MicroWire, one buffer must be null
     * buffers must work with dma_*map_single() calls, unless
     *   spi_message.is_dma_mapped reports a pre-existing mapping
     */
    const void  *tx_buf;
    void        *rx_buf;
    unsigned    len;

    dma_addr_t  tx_dma;
    dma_addr_t  rx_dma;

    unsigned    cs_change:1;
    unsigned    tx_nbits:3;
    unsigned    rx_nbits:3;
#define SPI_NBITS_SINGLE    0x01 /* 1bit transfer */
#define SPI_NBITS_DUAL      0x02 /* 2bits transfer */
#define SPI_NBITS_QUAD      0x04 /* 4bits transfer */
    u8      bits_per_word;
    u16     delay_usecs;
    u32     speed_hz;

    struct list_head transfer_list;
};


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
 * 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 alway executed in FIFO order.
 *
 * 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_message {
    struct list_head    transfers;

    struct spi_device   *spi;

    unsigned        is_dma_mapped:1;

    /* REVISIT:  we might want a flag affecting the behavior of the
     * last transfer ... allowing things like "read 16 bit length L"
     * immediately followed by "read L bytes".  Basically imposing
     * a specific message scheduling algorithm.
     *
     * Some controller drivers (message-at-a-time queue processing)
     * could provide that as their default scheduling algorithm.  But
     * others (with multi-message pipelines) could need a flag to
     * tell them about such special cases.
     */

    /* completion is reported through a callback */
    void            (*complete)(void *context);
    void            *context;
    unsigned        frame_length;
    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;
    void            *state;
};

static inline void spi_message_init(struct spi_message *m)
{
    memset(m, 0, sizeof *m);
    INIT_LIST_HEAD(&m->transfers);
}

static inline void
spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
{
    list_add_tail(&t->transfer_list, &m->transfers);
}

static inline void
spi_transfer_del(struct spi_transfer *t)
{
    list_del(&t->transfer_list);
}

/**
 * spi_message_init_with_transfers - Initialize spi_message and append transfers
 * @m: spi_message to be initialized
 * @xfers: An array of spi transfers
 * @num_xfers: Number of items in the xfer array
 *
 * This function initializes the given spi_message and adds each spi_transfer in
 * the given array to the message.
 */
static inline void
spi_message_init_with_transfers(struct spi_message *m,
struct spi_transfer *xfers, unsigned int num_xfers)
{
    unsigned int i;

    spi_message_init(m);
    for (i = 0; i < num_xfers; ++i)
        spi_message_add_tail(&xfers[i], m);
}
/* It's fine to embed message and transaction structures in other data
 * structures so long as you don't free them while they're in use.
 */

static inline struct spi_message *spi_message_alloc(unsigned ntrans, gfp_t flags)
{
    struct spi_message *m;

    m = kzalloc(sizeof(struct spi_message)
            + ntrans * sizeof(struct spi_transfer),
            flags);
    if (m) {
        unsigned i;
        struct spi_transfer *t = (struct spi_transfer *)(m + 1);

        INIT_LIST_HEAD(&m->transfers);
        for (i = 0; i < ntrans; i++, t++)
            spi_message_add_tail(t, m);
    }
    return m;
}

static inline void spi_message_free(struct spi_message *m)
{
    kfree(m);
}

extern int spi_setup(struct spi_device *spi);
extern int spi_async(struct spi_device *spi, struct spi_message *message);
extern int spi_async_locked(struct spi_device *spi,
                struct spi_message *message);

/*---------------------------------------------------------------------------*/

/* All these synchronous SPI transfer routines are utilities layered                                                                                                                                               
 * over the core async transfer primitive.  Here, "synchronous" means
 * they will sleep uninterruptibly until the async transfer completes.
 */
extern int spi_sync(struct spi_device *spi, struct spi_message *message);
extern int spi_sync_locked(struct spi_device *spi, struct spi_message *message);
extern int spi_bus_lock(struct spi_master *master);
extern int spi_bus_unlock(struct spi_master *master);

spi_read 和 spi_write


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

    spi_message_init(&m);
    spi_message_add_tail(&t, &m);
    return spi_sync(spi, &m);
}

/**
 * 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, void *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_sync_transfer - synchronous SPI data transfer
 * @spi: device with which data will be exchanged
 * @xfers: An array of spi_transfers
 * @num_xfers: Number of items in the xfer array
 * Context: can sleep
 *
 * Does a synchronous SPI data transfer of the given spi_transfer array.
 *
 * For more specific semantics see spi_sync().
 *
 * It returns zero on success, else a negative error code.
 */
static inline int
spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers,
    unsigned int num_xfers)
{
    struct spi_message msg;

    spi_message_init_with_transfers(&msg, xfers, num_xfers);

    return spi_sync(spi, &msg);
}

/* this copies txbuf and rxbuf data; for small transfers only! */
extern int spi_write_then_read(struct spi_device *spi,
        const void *txbuf, unsigned n_tx,
        void *rxbuf, unsigned n_rx);

/**
 * spi_w8r8 - SPI synchronous 8 bit write followed by 8 bit read
 * @spi: device with which data will be exchanged
 * @cmd: command to be written before data is read back
 * Context: can sleep
 *
 * This returns the (unsigned) eight bit number returned by the
 * device, or else a negative error code.  Callable only from
 * contexts that can sleep.
 */
static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd)
{
    ssize_t         status;
    u8          result;

    status = spi_write_then_read(spi, &cmd, 1, &result, 1);

    /* return negative errno or unsigned value */
    return (status < 0) ? status : result;
}


/**
 * spi_w8r16 - SPI synchronous 8 bit write followed by 16 bit read
 * @spi: device with which data will be exchanged
 * @cmd: command to be written before data is read back
 * Context: can sleep
 *
 * This returns the (unsigned) sixteen bit number returned by the
 * device, or else a negative error code.  Callable only from
 * contexts that can sleep.
 *
 * The number is returned in wire-order, which is at least sometimes
 * big-endian.
 */
static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd)
{
    ssize_t         status;
    u16         result;

    status = spi_write_then_read(spi, &cmd, 1, &result, 2);

    /* return negative errno or unsigned value */
    return (status < 0) ? status : result;
}

/**
 * spi_w8r16be - SPI synchronous 8 bit write followed by 16 bit big-endian read
 * @spi: device with which data will be exchanged
 * @cmd: command to be written before data is read back
 * Context: can sleep
 *
 * This returns the (unsigned) sixteen bit number returned by the device in cpu
 * endianness, or else a negative error code. Callable only from contexts that
 * can sleep.
 *
 * This function is similar to spi_w8r16, with the exception that it will
 * convert the read 16 bit data word from big-endian to native endianness.
 *
 */
static inline ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd)

{
    ssize_t status;
    __be16 result;

    status = spi_write_then_read(spi, &cmd, 1, &result, 2);
    if (status < 0)
        return status;

    return be16_to_cpu(result);
}

spi_borad_info结构体

/*---------------------------------------------------------------------------*/                                                                                                                                    

/*
 * INTERFACE between board init code and SPI infrastructure.
 *
 * No SPI driver ever sees these SPI device table segments, but
 * it's how the SPI core (or adapters that get hotplugged) grows
 * the driver model tree.
 *
 * As a rule, SPI devices can't be probed.  Instead, board init code
 * provides a table listing the devices which are present, with enough
 * information to bind and set up the device's driver.  There's basic
 * support for nonstatic configurations too; enough to handle adding
 * parport adapters, or microcontrollers acting as USB-to-SPI bridges.
 */

/**
 * struct spi_board_info - board-specific template for a SPI device
 * @modalias: Initializes spi_device.modalias; identifies the driver.
 * @platform_data: Initializes spi_device.platform_data; the particular
 *  data stored there is driver-specific.
 * @controller_data: Initializes spi_device.controller_data; some
 *  controllers need hints about hardware setup, e.g. for DMA.
 * @irq: Initializes spi_device.irq; depends on how the board is wired.
 * @max_speed_hz: Initializes spi_device.max_speed_hz; based on limits
 *  from the chip datasheet and board-specific signal quality issues.
 * @bus_num: Identifies which spi_master parents the spi_device; unused
 *  by spi_new_device(), and otherwise depends on board wiring.
 * @chip_select: Initializes spi_device.chip_select; depends on how
 *  the board is wired.
 * @mode: Initializes spi_device.mode; based on the chip datasheet, board
 *  wiring (some devices support both 3WIRE and standard modes), and
 *  possibly presence of an inverter in the chipselect path.
 *
 * When adding new SPI devices to the device tree, these structures serve
 * as a partial device template.  They hold information which can't always
 * be determined by drivers.  Information that probe() can establish (such
 * as the default transfer wordsize) is not included here.
 *
 * These structures are used in two places.  Their primary role is to
 * be stored in tables of board-specific device descriptors, which are
 * declared early in board initialization and then used (much later) to
 * populate a controller's device tree after the that controller's driver
 * initializes.  A secondary (and atypical) role is as a parameter to
 * spi_new_device() call, which happens after those controller drivers
 * are active in some dynamic board configuration models.
 */
struct spi_board_info {
    /* the device name and module name are coupled, like platform_bus;
     * "modalias" is normally the driver name.
     *
     * platform_data goes to spi_device.dev.platform_data,
     * controller_data goes to spi_device.controller_data,
     * irq is copied too
     */
    char        modalias[SPI_NAME_SIZE];
    const void  *platform_data;
    void        *controller_data;
    int     irq;

    /* slower signaling on noisy or low voltage boards */
    u32     max_speed_hz;


    /* bus_num is board specific and matches the bus_num of some
     * spi_master that will probably be registered later.
     *
     * chip_select reflects how this chip is wired to that master;
     * it's less than num_chipselect.
     */
    u16     bus_num;
    u16     chip_select;

    /* mode becomes spi_device.mode, and is essential for chips
     * where the default of SPI_CS_HIGH = 0 is wrong.
     */
    u16     mode;

    /* ... may need additional spi_device chip config data here.
     * avoid stuff protocol drivers can set; but include stuff
     * needed to behave without being bound to a driver:
     *  - quirks like clock rate mattering when not selected
     */
};

板载信息的填充,spi相关数据结构的创建 

以及相关设备在系统中的注册 注销

#ifdef  CONFIG_SPI
extern int
spi_register_board_info(struct spi_board_info const *info, unsigned n);
#else
/* board init code may ignore whether SPI is configured or not */
static inline int
spi_register_board_info(struct spi_board_info const *info, unsigned n)
    { return 0; }
#endif


/* If you're hotplugging an adapter with devices (parport, usb, etc)
 * use spi_new_device() to describe each device.  You can also call
 * spi_unregister_device() to start making that device vanish, but
 * normally that would be handled by spi_unregister_master().
 *
 * You can also use spi_alloc_device() and spi_add_device() to use a two
 * stage registration sequence for each spi_device.  This gives the caller
 * some more control over the spi_device structure before it is registered,
 * but requires that caller to initialize fields that would otherwise
 * be defined using the board info.
 */
extern struct spi_device *
spi_alloc_device(struct spi_master *master);

extern int
spi_add_device(struct spi_device *spi);

extern struct spi_device *
spi_new_device(struct spi_master *, struct spi_board_info *);

static inline void
spi_unregister_device(struct spi_device *spi)
{
    if (spi)
        device_unregister(&spi->dev);
}

extern const struct spi_device_id *
spi_get_device_id(const struct spi_device *sdev);

至此, spi.h所有内容都结束了.

#endif /* __LINUX_SPI_H */  
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
下面是在Linux下进行SPI硬件接口编程的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <linux/spi/spidev.h> #include <unistd.h> int main() { int fd; char buf[10]; // 打开SPI设备 if ((fd = open("/dev/spidev0.0", O_RDWR)) < 0) { perror("Failed to open spi device.\n"); exit(1); } // 设置SPI设备参数 unsigned char mode = SPI_MODE_0; unsigned char bits = 8; unsigned int speed = 1000000; if (ioctl(fd, SPI_IOC_WR_MODE, &mode) < 0) { perror("Failed to set spi mode.\n"); exit(1); } if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0) { perror("Failed to set bits per word.\n"); exit(1); } if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0) { perror("Failed to set max speed.\n"); exit(1); } // 向SPI设备写入数据 buf[0] = 0x01; buf[1] = 0x02; if (write(fd, buf, 2) != 2) { perror("Failed to write to spi device.\n"); exit(1); } // 从SPI设备读取数据 if (read(fd, buf, 2) != 2) { perror("Failed to read from spi device.\n"); exit(1); } printf("Read data: 0x%x 0x%x\n", buf[0], buf[1]); // 关闭SPI设备 close(fd); return 0; } ``` 上述代码中,首先通过open函数打开SPI设备文件,然后通过ioctl函数设置SPI设备参数,包括工作模式、数据位数、时钟速度等。接着,通过write函数向SPI设备写入数据,再通过read函数从SPI设备读取数据。最后,通过close函数关闭SPI设备文件。 需要注意的是,SPI设备的具体参数需要根据硬件设备和通信协议进行相应的设置,上述代码中的参数仅作为示例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值