linux spi驱动开发学习(一)-----spi子系统架构
一.spi设备(各定义在include/linux/spi.h)
structspi_device {
structdevice dev;//设备文件
structspi_master *master;//spi主机
u32 max_speed_hz; //最大速率
u8 chip_select; //片选
u8 mode; //模式
u8 bits_per_word; //一个字有多少位
intirq;//中断号
void*controller_state;//控制器状态
void*controller_data;//控制器数据
charmodalias[SPI_NAME_SIZE];//名字
};
2.spi传输模式:
#define SPI_CPHA 0x01 //时钟相位
#define SPI_CPOL 0x02 //时钟继续
#define SPI_MODE_0 (0|0) //模式0
#define SPI_MODE_1 (0|SPI_CPHA) //模式1
#define SPI_MODE_2 (SPI_CPOL|0) //模式2
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA) //模式3
#define SPI_CS_HIGH 0x04 //片选高电平
#define SPI_LSB_FIRST 0x08 //LSB
#define SPI_3WIRE 0x10 //3线模式 SI和SO同一根线
#define SPI_LOOP 0x20 //回送模式
#define SPI_NO_CS 0x40 //单个设备占用一根SPI总线,所以没片选
#define SPI_READY 0x80 //从机拉低电平停止数据传输
3.spi设备的添加spi_new_device
structspi_device *spi_new_device(structspi_master *master,structspi_board_info *chip)
{
structspi_device *proxy;
intstatus;
proxy = spi_alloc_device(master); //3.1 spi设备初始化
if(!proxy)
returnNULL;
WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
proxy->chip_select = chip->chip_select; //片选
proxy->max_speed_hz = chip->max_speed_hz; //最大速率
proxy->mode = chip->mode; //模式
proxy->irq = chip->irq; //中断号
strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
proxy->dev.platform_data = (void*) chip->platform_data;
proxy->controller_data = chip->controller_data;
proxy->controller_state = NULL;
status = spi_add_device(proxy); //3.2 添加spi设备
if(status < 0) {
spi_dev_put(proxy); //增加spi设备引用计数
returnNULL;
}
returnproxy;
}
EXPORT_SYMBOL_GPL(spi_new_device);
3.1.分配spi设备
structspi_device *spi_alloc_device(structspi_master *master)
{
structspi_device *spi;
structdevice *dev = master->dev.parent;
if(!spi_master_get(master))//判断spi主机是否存在
returnNULL;
spi = kzalloc(sizeof*spi, GFP_KERNEL);//分配内存
if(!spi) {
dev_err(dev, "cannot alloc spi_device\n");
spi_master_put(master); //增加主机引用计数
returnNULL;
}
spi->master = master; //设置spi主机
spi->dev.parent = dev; //spi设备文件的父设备为spi主机设备文件的父设备
spi->dev.bus = &spi_bus_type; //总线类型
spi->dev.release = spidev_release; //释放方法
device_initialize(&spi->dev); //设备初始化
returnspi;
}
EXPORT_SYMBOL_GPL(spi_alloc_device);
3.2 添加spi设备
intspi_add_device(structspi_device *spi)
{
staticDEFINE_MUTEX(spi_add_lock);
structdevice *dev = spi->master->dev.parent;
structdevice *d;
intstatus;
if(spi->chip_select >= spi->master->num_chipselect) {
dev_err(dev, "cs%d >= max %d\n",spi->chip_select,spi->master->num_chipselect);
return-EINVAL;
}
dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),spi->chip_select);
mutex_lock(&spi_add_lock);
d = bus_find_device_by_name(&spi_bus_type, NULL, dev_name(&spi->dev)); //查找总线上的spi设备
if(d != NULL) {//判断是否已经在使用了
dev_err(dev, "chipselect %d already in use\n",spi->chip_select);
put_device(d);
status = -EBUSY;
gotodone;
}
status = spi_setup(spi); //调用spi主机 setup方法
if(status < 0) {
dev_err(dev, "can't setup %s, status %d\n",dev_name(&spi->dev), status);
gotodone;
}
status = device_add(&spi->dev); //添加设备
if(status < 0)
dev_err(dev, "can't add %s, status %d\n",dev_name(&spi->dev), status);
else
dev_dbg(dev, "registered child %s\n", dev_name(&spi->dev));
done:
mutex_unlock(&spi_add_lock);
returnstatus;
}
EXPORT_SYMBOL_GPL(spi_add_device);
3.2.1 spi setup方法
intspi_setup(structspi_device *spi)
{
unsigned bad_bits;
intstatus;
bad_bits = spi->mode & ~spi->master->mode_bits; //比较spi设备的模式和spi主机支持的模式
if(bad_bits) {//存在不支持的模式
dev_err(&spi->dev, "setup: unsupported mode bits %x\n",bad_bits);
return-EINVAL;
}
if(!spi->bits_per_word)//若没设置设备的每个字含多少位
spi->bits_per_word = 8; //则默认设置为8
status = spi->master->setup(spi); //调用spi主机的setup方法
dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s""%u bits/w, %u Hz max --> %d\n",
(int) (spi->mode & (SPI_CPOL | SPI_CPHA)),(spi->mode & SPI_CS_HIGH) ?"cs_high, ":"",
(spi->mode & SPI_LSB_FIRST) ? "lsb, ":"",(spi->mode & SPI_3WIRE) ?"3wire, ":"",
(spi->mode & SPI_LOOP) ? "loopback, ":"",spi->bits_per_word, spi->max_speed_hz,status);
returnstatus;
}
EXPORT_SYMBOL_GPL(spi_setup);
二.spi板级设备
1.板级设备结构体
structspi_board_info {
charmodalias[SPI_NAME_SIZE];//名字
constvoid*platform_data;//平台数据
void*controller_data;//控制器数据
intirq;//中断号
u32 max_speed_hz; //最大速率
u16 bus_num; //spi总线编号
u16 chip_select; //片选
u8 mode; //模式
};
2.板级设备注册(静态注册,一般在板级初始化函数中调用)
int__init spi_register_board_info(structspi_board_infoconst*info, unsigned n)
{
structboardinfo *bi;
inti;
bi = kzalloc(n * sizeof(*bi), GFP_KERNEL);//分配内存
if(!bi)
return-ENOMEM;
for(i = 0; i < n; i++, bi++, info++) {
structspi_master *master;
memcpy(&bi->board_info, info, sizeof(*info));//设置bi的板级信息
mutex_lock(&board_lock);
list_add_tail(&bi->list, &board_list); //添加bi->list到全局board_list链表
list_for_each_entry(master, &spi_master_list, list) //遍历spi主机链表
spi_match_master_to_boardinfo(master, &bi->board_info);
mutex_unlock(&board_lock);
}
return0;
}
2.1.spi板级设备与spi主机匹配
staticvoidspi_match_master_to_boardinfo(structspi_master *master,structspi_board_info *bi)
{
structspi_device *dev;
if(master->bus_num != bi->bus_num)
return;
dev = spi_new_device(master, bi);
if(!dev)
dev_err(master->dev.parent, "can't create new device for %s\n",bi->modalias);
}
三.spi设备驱动
1.spi设备驱动结构体
structspi_driver {
conststructspi_device_id *id_table;//spi设备id表
int(*probe)(structspi_device *spi);//probe方法(探测到设备)
int(*remove)(structspi_device *spi);//remove方法(设备移除)
void(*shutdown)(structspi_device *spi);//shutdown方法(关闭设备)
int(*suspend)(structspi_device *spi, pm_message_t mesg);//suspend方法(挂起设备)
int(*resume)(structspi_device *spi);//resume方法(唤醒设备)
structdevice_driver driver;//设备驱动文件
};
2.spi设备驱动注册
intspi_register_driver(structspi_driver *sdrv)
{
sdrv->driver.bus = &spi_bus_type; //总线类型
if(sdrv->probe)//若存在probe方法
sdrv->driver.probe = spi_drv_probe; //设置其设备驱动文件的probe方法为spi_drv_probe
if(sdrv->remove)//若存在remove方法
sdrv->driver.remove = spi_drv_remove; //设置其设备驱动文件的remove方法为spi_drv_remove
if(sdrv->shutdown)若存在shutdown方法
sdrv->driver.shutdown = spi_drv_shutdown; //设置其设备驱动文件的shutdown方法为spi_drv_shutdown
returndriver_register(&sdrv->driver);//注册设备驱动
}
EXPORT_SYMBOL_GPL(spi_register_driver);
这里的probe方法会在设备与驱动匹配的时候给调用
参看really_probe函数的部分代码
if(dev->bus->probe) {//若总线有probe方法(spi子系统的没有)
ret = dev->bus->probe(dev); //则调用总线的probe方法
if(ret)
gotoprobe_failed;
}
elseif(drv->probe) {//若存在设备驱动的probe方法
ret = drv->probe(dev); //则调用设备驱动的probe方法
if(ret)
gotoprobe_failed;
}
2.1 spi_drv_probe
staticintspi_drv_probe(structdevice *dev)
{
conststructspi_driver *sdrv = to_spi_driver(dev->driver);//根据设备文件的设备驱动找到spi设备驱动
returnsdrv->probe(to_spi_device(dev));//调用spi设备驱动的probe方法
}
3.spi设备驱动注销
staticinlinevoidspi_unregister_driver(structspi_driver *sdrv)
{
if(sdrv)
driver_unregister(&sdrv->driver); //注销设备驱动
}
四.spi主机
1.spi主机结构体
structspi_master {
structdevice dev;//spi主机设备文件
structlist_head list;
s16 bus_num; //spi总线号
u16 num_chipselect; //片选号
u16 dma_alignment; //dma算法
u16 mode_bits; //模式位
u16 flags; //传输类型标志
spinlock_t bus_lock_spinlock; //spi总线自旋锁
structmutex bus_lock_mutex;//spi总线互斥锁
boolbus_lock_flag;//上锁标志
int(*setup)(structspi_device *spi);//setup方法
int(*transfer)(structspi_device *spi,structspi_message *mesg);//传输方法
void(*cleanup)(structspi_device *spi);//cleanup方法
};
1.2.flags标志
#define SPI_MASTER_HALF_DUPLEX BIT(0) //半双工
#define SPI_MASTER_NO_RX BIT(1) //不读
#define SPI_MASTER_NO_TX BIT(2) //不写
2.spi主机初始化spi_alloc_master
structspi_master *spi_alloc_master(structdevice *dev, unsigned size)
{
structspi_master *master;
if(!dev)
returnNULL;
master = kzalloc(size + sizeof*master, GFP_KERNEL);//分配内存
if(!master)
returnNULL;
device_initialize(&master->dev); //初始化主机设备文件
master->dev.class= &spi_master_class;//指定设备类spi_master_class
master->dev.parent = get_device(dev); //设置spi主机设备的父设备
spi_master_set_devdata(master, &master[1]); //设置设备数据
returnmaster;
}
EXPORT_SYMBOL_GPL(spi_alloc_master);
3.注册spi主机
intspi_register_master(structspi_master *master)
{
staticatomic_t dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
structdevice *dev = master->dev.parent;//获得spi主机设备的父设备
structboardinfo *bi;
intstatus = -ENODEV;
intdynamic = 0;
if(!dev)
return-ENODEV;
if(master->num_chipselect == 0)//判断片选个数
return-EINVAL;
if(master->bus_num < 0) {//验证spi总线编号
master->bus_num = atomic_dec_return(&dyn_bus_id);
dynamic = 1;
}
spin_lock_init(&master->bus_lock_spinlock);
mutex_init(&master->bus_lock_mutex);
master->bus_lock_flag = 0;
dev_set_name(&master->dev, "spi%u", master->bus_num);//设置spi主机设备名
status = device_add(&master->dev); //添加spi主机设备
if(status < 0)
gotodone;
dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),dynamic ?" (dynamic)":"");
mutex_lock(&board_lock);
list_add_tail(&master->list, &spi_master_list); //spi主机list链表添加进全局spi_master_list链表
list_for_each_entry(bi, &board_list, list) //遍历全局board_list查找bi结构体
spi_match_master_to_boardinfo(master, &bi->board_info); //找到匹配的板级spi设备
mutex_unlock(&board_lock);
status = 0;
of_register_spi_devices(master);
done:
returnstatus;
}
EXPORT_SYMBOL_GPL(spi_register_master);
3.1 匹配spi主机和板级spi设备
staticvoidspi_match_master_to_boardinfo(structspi_master *master,structspi_board_info *bi)
{
structspi_device *dev;
if(master->bus_num != bi->bus_num)//判断是否所属的spi总线
return;
dev = spi_new_device(master, bi); //添加新的spi设备
if(!dev)
dev_err(master->dev.parent, "can't create new device for %s\n",bi->modalias);
}
在注册板级设备或主机设备的时候都会添加
spi板级设备添加进board_list链表,spi主机设备添加进spi_master_list链表
不管是先注册spi板级设备还是先注册spi主机设备
都会调用list_for_each_entry遍历对应的要匹配的设备的链表,查找是否有匹配的例子
若找到都会调用spi_match_master_to_boardinfo函数添加spi设备
4.注销spi主机
voidspi_unregister_master(structspi_master *master)
{
intdummy;
mutex_lock(&board_lock);
list_del(&master->list); //删除链表
mutex_unlock(&board_lock);
dummy = device_for_each_child(&master->dev, NULL, __unregister); //调用__unregister函数注销子设备
device_unregister(&master->dev); //注销设备
}
EXPORT_SYMBOL_GPL(spi_unregister_master);
4.1 注销挂载该总线上的spi子设备
staticint__unregister(structdevice *dev,void*null)
{
spi_unregister_device(to_spi_device(dev));
return0;
}
5.spi主机设备类
staticstructclassspi_master_class = {
.name = "spi_master",
.owner = THIS_MODULE,
.dev_release = spi_master_release,
};
五.spi总线
1.spi总线结构体
structbus_type spi_bus_type = {
.name = "spi",
.dev_attrs = spi_dev_attrs,
.match = spi_match_device, //匹配方法
.uevent = spi_uevent,
.suspend = spi_suspend,
.resume = spi_resume,
};
EXPORT_SYMBOL_GPL(spi_bus_type);
2.设备匹配方法spi_match_device
前面的匹配方法是spi板级设备与spi主机设备的匹配方法,匹配的结果是添加新spi设备spi_new_device
这里的匹配是spi设备和spi驱动的匹配,匹配的结果是会调用spi驱动的设备驱动文件probe方法,既spi_drv_probe
staticintspi_match_device(structdevice *dev,structdevice_driver *drv)
{
conststructspi_device *spi = to_spi_device(dev);
conststructspi_driver *sdrv = to_spi_driver(drv);
if(of_driver_match_device(dev, drv))//设备文件驱动表的匹配
return1;
if(sdrv->id_table)//spi设备驱动存在支持id表
return!!spi_match_id(sdrv->id_table, spi);//spi设备驱动表的匹配
returnstrcmp(spi->modalias, drv->name) == 0;//比较spi设备的名字和spi设备驱动的名字
}
2.1 of_driver_match_device
staticinlineintof_driver_match_device(conststructdevice *dev,conststructdevice_driver *drv)
{
returnof_match_device(drv->of_match_table, dev) != NULL;//调用of_match_device函数
}
2.1.1 of_match_device
conststructof_device_id *of_match_device(conststructof_device_id *matches,conststructdevice *dev)
{
if((!matches) || (!dev->of_node))//id表和设备节点都不存在
returnNULL;//则返回
returnof_match_node(matches, dev->of_node);//调用of_match_node函数
}
EXPORT_SYMBOL(of_match_device);
2.1.1.1 of_match_node //drv->of_match_table,dev->of_node
conststructof_device_id *of_match_node(conststructof_device_id *matches,conststructdevice_node *node)
{
while(matches->name[0] || matches->type[0] || matches->compatible[0]) {//名字,类型,兼容方法有一个存在
intmatch = 1;
if(matches->name[0])//判断名字
match &= node->name && !strcmp(matches->name, node->name);
if(matches->type[0])//判断类型
match &= node->type && !strcmp(matches->type, node->type);
if(matches->compatible[0])//兼容方法
match &= of_device_is_compatible(node,matches->compatible);
if(match)//匹配
returnmatches;//返回匹配的id
matches++; //matches指针++,指向下一个id
}
returnNULL;
}
EXPORT_SYMBOL(of_match_node);
2.2 spi_match_id
staticconststructspi_device_id *spi_match_id(conststructspi_device_id *id,conststructspi_device *sdev)
{
while(id->name[0]) {//id表的成员的名字域不为空
if(!strcmp(sdev->modalias, id->name))//则判断其名字是否与spi设备的名字一样
returnid;//一样则返回该id
id++; //id表指针++,指向下一个id
}
returnNULL;
}
六 spi消息和spi传输
1.spi消息结构体
structspi_message {
structlist_head transfers;//spi传输事务链表头
structspi_device *spi;//所属spi设备
unsigned is_dma_mapped:1;
void(*complete)(void*context);
void*context;
unsigned actual_length;
intstatus;//传输状态
structlist_head queue;
void*state;
};
2.初始化spi消息
staticinlinevoidspi_message_init(structspi_message *m)
{
memset(m, 0, sizeof*m);
INIT_LIST_HEAD(&m->transfers); //初始化spi消息的事务链表头
}
3.添加传输事务到spi传输链表
staticinlinevoidspi_message_add_tail(structspi_transfer *t,structspi_message *m)
{
list_add_tail(&t->transfer_list, &m->transfers);
}
4.spi传输结构体
structspi_transfer {
constvoid*tx_buf;//发送缓冲区指针
void*rx_buf;//接收缓冲区指针
unsigned len; //消息长度
dma_addr_t tx_dma; //DMA发送地址
dma_addr_t rx_dma; //DMA接收地址
unsigned cs_change:1;
u8 bits_per_word; //一个字多少位
u16 delay_usecs; //毫秒级延时
u32 speed_hz; //速率
structlist_head transfer_list;//传输链表头
};
七.spi子系统的初始化spi_init
staticint__init spi_init(void)
{
intstatus;
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL); //分配数据收发缓冲区
if(!buf) {
status = -ENOMEM;
gotoerr0;
}
status = bus_register(&spi_bus_type); //注册spi总线
if(status < 0)
gotoerr1;
status = class_register(&spi_master_class); //注册spi主机类 "/sys/class/spi_master"
if(status < 0)
gotoerr2;
return0;
err2:
bus_unregister(&spi_bus_type);
err1:
kfree(buf);
buf = NULL;
err0:
returnstatus;
}
postcore_initcall(spi_init); //入口声明 #define postcore_initcall(fn) __define_initcall("2",fn,2)
八.spi子系统的API
1.spi读 spi_read
staticinlineintspi_read(structspi_device *spi, u8 *buf,size_tlen)
{
structspi_transfer t = {
.rx_buf = buf,
.len = len,
};
structspi_message m;
spi_message_init(&m); //spi消息初始化(初始化传输事务链表头)
spi_message_add_tail(&t, &m); //添加spi传输到spi消息传输链表
returnspi_sync(spi, &m);//spi同步传输
}
2.spi写 spi_write
staticinlineintspi_write(structspi_device *spi,constu8 *buf,size_tlen)
{
structspi_transfer t = {
.tx_buf = buf,
.len = len,
};
structspi_message m;
spi_message_init(&m); //spi消息初始化(初始化传输事务链表头)
spi_message_add_tail(&t, &m); //添加spi传输到spi消息传输链表
returnspi_sync(spi, &m);//spi同步传输
}
spi的读写操作都是初始化一个spi_transfer传输结构体,并将其添加进spi消息传输事务链表中
然后通过spi_sync来同步读写操作,接着看下spi_sync的具体代码
2.1 spi_sync
intspi_sync(structspi_device *spi,structspi_message *message)
{
return__spi_sync(spi, message, 0);//调用__spi_sync函数
}
EXPORT_SYMBOL_GPL(spi_sync);
2.1.1 __spi_sync函数
staticint__spi_sync(structspi_device *spi,structspi_message *message,intbus_locked)
{
DECLARE_COMPLETION_ONSTACK(done); //声明一个工作队列
intstatus;
structspi_master *master = spi->master;//获取spi主机
message->complete = spi_complete; //设置spi消息的complete方法为spi_complete
message->context = &done; //设置spi消息的context
if(!bus_locked)
mutex_lock(&master->bus_lock_mutex); //上锁
status = spi_async_locked(spi, message);
if(!bus_locked)
mutex_unlock(&master->bus_lock_mutex); //解锁
if(status == 0) {
wait_for_completion(&done); //等待完成
status = message->status; //设置spi消息传输状态
}
message->context = NULL;
returnstatus;
}
2.1.1.1 spi_async_locked 函数
intspi_async_locked(structspi_device *spi,structspi_message *message)
{
structspi_master *master = spi->master;//获取spi主机
intret;
unsigned longflags;
spin_lock_irqsave(&master->bus_lock_spinlock, flags); //上自旋锁
ret = __spi_async(spi, message); //调用了spi异步同步方法
spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); //解自旋锁
returnret;
}
EXPORT_SYMBOL_GPL(spi_async_locked);
2.1.1.1.1 __spi_async函数(异步)
staticint__spi_async(structspi_device *spi,structspi_message *message)
{
structspi_master *master = spi->master;
//主机为半双工或spi设备为3线设备
if((master->flags & SPI_MASTER_HALF_DUPLEX)|| (spi->mode & SPI_3WIRE)) {
structspi_transfer *xfer;
unsigned flags = master->flags;
list_for_each_entry(xfer, &message->transfers, transfer_list) { //遍历spi消息的传输事务链表
if(xfer->rx_buf && xfer->tx_buf)//判断接收或发送缓冲区是否为空
return-EINVAL;
if((flags & SPI_MASTER_NO_TX) && xfer->tx_buf)//检验无spi数据发送的情况
return-EINVAL;
if((flags & SPI_MASTER_NO_RX) && xfer->rx_buf)//检验无spi数据接收的情况
return-EINVAL;
}
}
message->spi = spi; //设置spi消息所属的spi设备
message->status = -EINPROGRESS; //设置spi消息的传输状态
returnmaster->transfer(spi, message);//调用spi主机的transfer方法,收发spi信息给spi设备
}
3.spi先写后读 spi_write_then_read
intspi_write_then_read(structspi_device *spi,constu8 *txbuf, unsigned n_tx,u8 *rxbuf, unsigned n_rx)
{
staticDEFINE_MUTEX(lock);
intstatus;
structspi_message message;
structspi_transfer x[2];//声明两个spi传输结构体
u8 *local_buf;
if((n_tx + n_rx) > SPI_BUFSIZ)//验证发送和接收的数据总和是否溢出
return-EINVAL;
spi_message_init(&message); //spi消息初始化(初始化传输事务链表头)
memset(x, 0, sizeofx);//
if(n_tx) {
x[0].len = n_tx;
spi_message_add_tail(&x[0], &message); //添加spi传输到spi消息传输链表
}
if(n_rx) {
x[1].len = n_rx;
spi_message_add_tail(&x[1], &message); //添加spi传输到spi消息传输链表
}
if(!mutex_trylock(&lock)) {//尝试上锁 失败
local_buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL); //则分配local_buf内存
if(!local_buf)
return-ENOMEM;
} else
local_buf = buf; //指向默认分配好内存的缓冲区(spi_init中初始化的)
memcpy(local_buf, txbuf, n_tx); //发送缓冲区的内容复制到local_buf中
x[0].tx_buf = local_buf; //发送的spi传输结构体
x[1].rx_buf = local_buf + n_tx; //接收的spi传输结构体
status = spi_sync(spi, &message); //spi同步传输--发送数据
if(status == 0)
memcpy(rxbuf, x[1].rx_buf, n_rx); //接收返回的数据复制到rxbuf中
if(x[0].tx_buf == buf)
mutex_unlock(&lock); //解锁
else
kfree(local_buf); //释放内存
returnstatus;
}
EXPORT_SYMBOL_GPL(spi_write_then_read);
4.spi写8位后读8位数据 spi_w8r8
staticinlinessize_t spi_w8r8(structspi_device *spi, u8 cmd)
{
ssize_t status;
u8 result;
status = spi_write_then_read(spi, &cmd, 1, &result, 1); //写8位读8位
return(status < 0) ? status : result;
}
5.spi写8位数据后读16位数据 spi_w8r16
staticinlinessize_t spi_w8r16(structspi_device *spi, u8 cmd)
{
ssize_t status;
u16 result;
status = spi_write_then_read(spi, &cmd, 1, (u8 *) &result, 2); //写8位读16位
return(status < 0) ? status : result;
}
这里的API是内核空间使用的接口,设备驱动程序调用这些API直接操作spi的读写操作,来完成任务