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]
不好意思开个玩笑:
再来看看如果是传送 两个字节 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); }} elsepr_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;elsedev_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 VERBOSEdev_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_messagestruct spi_transferstruct 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 */