### SPI掌握器驱动构造体
`struct spi_master`抽象了掌握器硬件,在SoC中的指的便是内部SPI掌握器,当向SPI核心层注册一个SPI掌握器时就须要供应这样的一个构造体变量。它的定义在 `include/linux/spi/spi.h` 文件,如下:
```c

/
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
supported. 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
@can_dma: determine whether this master supports DMA
@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
@idling: the device is entering idle state
@cur_msg: the currently in-flight message
@cur_msg_prepared: spi_prepare_message was called for the currently
in-flight message
@cur_msg_mapped: message has been mapped for DMA
@xfer_completion: used by core transfer_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
@max_dma_len: Maximum length of a DMA transfer for the device.
@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.
@handle_err: the subsystem calls the driver to handle an error that occurs
in the generic implementation of transfer_one_message().
@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).
@statistics: statistics for the spi_master
@dma_tx: DMA transmit channel
@dma_rx: DMA receive channel
@dummy_rx: dummy receive buffer for full-duplex devices
@dummy_tx: dummy transmit buffer for full-duplex devices
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 /
#define SPI_MASTER_MUST_RX BIT(3) / requires rx /
#define SPI_MASTER_MUST_TX BIT(4) / requires tx /
/ 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);
/
Used to enable core support for DMA handling, if can_dma()
exists and returns true then the transfer will be mapped
prior to transfer_one() being called. The driver should
not modify or store xfer and dma_tx and dma_rx must be set
while the device is prepared.
/
bool (can_dma)(struct spi_master master,
struct spi_device spi,
struct spi_transfer xfer);
/
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 idling;
bool busy;
bool running;
bool rt;
bool auto_runtime_pm;
bool cur_msg_prepared;
bool cur_msg_mapped;
struct completion xfer_completion;
size_t max_dma_len;
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);
void (handle_err)(struct spi_master master,
struct spi_message message);
/ gpio chip select /
int cs_gpios;
/ statistics /
struct spi_statistics statistics;
/ DMA channels for use with core dmaengine helpers /
struct dma_chan dma_tx;
struct dma_chan dma_rx;
/ dummy data for full duplex devices /
void dummy_rx;
void dummy_tx;
};
```
参数含义如下:
- `dev`:spi_master是一个device,以是包含了一个device的实例,设备模型利用;把`spi_master`看做是device的子类;
- `list`:用于构建双向链表,链接到`spi_controller list`链表中;
- `bus_num`:SPI掌握器的编号,比如某SoC有3个SPI掌握,那么这个构造描述的是第几个;
- `num_chipselect`:片选数量,决定该掌握器下面挂接多少个SPI设备,从设备的片选号不能大于这个数量;
- `mode_bits`:SPI 掌握器支持模式标志位,比如:
- `SPI_CPHA`:支持时钟相位选择;
- `SPI_CPOL`:支持时钟记性选择;
- `SPI_CS_HIGH`:片选旗子暗记为高电平;
- `bits_per_word_mask`:位掩码,指示驱动程序支持的`bits_per_word`值。设置了第n位表示支持`bits_per_word n+1`。如果设置了该位,SPI核将谢绝任何利用不受支持的`bits_per_word`进行的传输。如果未设置该位,则该值将被忽略,由各个驱动程序实行任何验证。
- `min_speed_hz/max_speed_hz`:最大最小速率;
- `slave`:是否是 slave;
- `bus_lock_spinlock`:用于SPI总线锁定的自旋锁。
- `bus_lock_mutex`:用于SPI总线锁定的互斥锁。
- `bus_lock_flag`:指示SPI总线是否被独占利用的标志。
- `setup`:SPI掌握器初始化函数指针,用来设置SPI掌握器和事情办法、clock等;
- `cleanup`:在`spidev_release`函数中被调用;
- `transfer`:添加到行列步队的方法。这个函数不可就寝,它的职责是安排传送并且调用注册的回调函 complete()。这个不同的掌握器要详细实现,传输数据末了都要调用这个函数;
- `queue`:行列步队,用于连接挂载该掌握器下的`spi_message`构造;掌握器上可以同时被加入多个`spi_message`进行排队;
- `kworker`:事情线程,卖力实行事情的线程;
- `kworker_task`:调用`kthread_run`为kworker创建并启动一个内核线程来处理事情,创建返回的`task_struct`构造体;
- `pump_messages`:指的便是详细的事情(work),通过`kthread_queue_work`可以将事情挂到事情线程;
- `cur_msg`:当前正在处理的`spi_message`;
- `busy`: message pump is busy:指定是当前SPI是否正在进行数据传输;
- `idling`:内核事情线程是空闲的,即kworker没有事情须要实行;
- `auto_runtime_pm` 用于指示 SPI 主掌握器驱动程序在准备硬件时是否须要自动管理运行时电源管理(Runtime PM)。
- `cur_msg_prepared` 用于表示当前(`cur_msg`)是否已经准备好进行传输。
- `cur_msg_mapped` 表示当前的(`cur_msg`)是否已经被映射为 DMA 缓冲区。
- `max_dma_len` 变量指定了设备在单个 DMA 传输中所支持的最大数据长度。它限定了每次 DMA 传输的数据量,以确保在硬件和系统限定范围内进行有效的数据传输。
- `prepare_transfer_hardware`:在利用 DMA 进行数据传输时,SPI 主掌握器须要在实际传输之前准备干系的硬件设置和配置。这包括配置 DMA 掌握器、分配 DMA 缓冲区、设置传输方向和传输参数等。回调函数的目的是为传输做好准备事情,确保硬件和干系资源处于精确的状态以进行传输。该回调函数在每次传输开始之前被调用,以便准备传输所需的硬件资源。
- `unprepare_transfer_hardware` 回调函数的目的是在传输完成后进行清理事情,开释传输过程中所利用的硬件资源,以便它们可以被其他传输所利用。
- `prepare_message` :是一个函数指针,指向一个用于准备单个传输的回调函数。在利用 SPI 主掌握器进行传输之前,须要进行一些准备事情,例如设置传输参数、映射 DMA 缓冲区等。`prepare_message` 回调函数的目的便是在实际传输之前实行这些准备操作。
- `unprepare_message` 是一个函数指针,指向一个用于取消准备传输的回调函数。在利用 SPI 主掌握器进行传输之后,可以利用 `unprepare_message` 回调函数实行一些清理操作,以撤消在传输准备阶段所做的配置和初始化。
- `set_cs`:函数指针,可以用来实现SPI掌握器片选旗子暗记,比如设置片选、取消片选,这个函数一样平常由SoC厂家的SPI掌握器驱动程序供应;如果指定了`cs_giops`、`cs_gpio`,该参数将无效;
- `cs_gpiod`:片选GPIO描述符指针数组,如果一个SPI掌握器外接了多个SPI从设备,这里存放的便是每个从设备的片选引脚GPIO描述符;
- `running`: message pump is running:指的是行列步队是否启用,在`spi_start_queue`函数会将这个参数设置为true;不出意外的话,注册完SPI中断掌握器后,这个参数时钟都是true;
- `transfer、transfer_one、transfer_one_message`:用于SPI数据传输;其差异在于:
- `transfer`:添加一个message到SPI掌握器传输行列步队;如果未指定,由SPI核心默认初始化为`spi_queued_transfer`;
- `transfer_one_message`:传输一个`spi_message`,传输完成将会调用`spi_finalize_current_message`函数;由SPI核心默认初始化为`spi_transfer_one_message`;
- `transfer_one`:传输一个`spi_transfer`,0:传输完成,1:传输进行中,传输完成须要调用`spi_finalize_current_transfer`函数;这个函数一样平常由SoC厂家的SPI掌握器驱动程序供应;
- `cs_gpio`:片选GPIO编号数组,如果一个SPI掌握器外接了多个SPI从设备,这里存放的便是每个从设备的片选引脚GPIO编号;
- `struct spi_statistics statistics` 是一个用于记录 SPI 主掌握器统计信息的构造体。
- `u32 transfer_count`:记录实行的传输次数。
- `u32 rx_bytes`:吸收的总字节数。
- `u32 tx_bytes`:发送的总字节数。
- `u32 rx_errors`:吸收期间发生的缺点次数。
- `u32 tx_errors`:发送期间发生的缺点次数。
- `u32 rx_overruns`:吸收期间发生的溢出错误次数。
- `u32 tx_underruns`:发送期间发生的欠流缺点次数。
- `dma_tx`:指向 DMA 传输通道的指针。DMA 传输通道用于处理 SPI 主掌握器的发送数据部分。通过利用 DMA,可以实现在数据传输期间无需 CPU 干预,从而提高传输效率。
- `dma_rx`:指向 DMA(直接内存访问)吸收通道的指针。
- `dummy_rx`:用于全双工设备的虚拟吸收缓冲区。对付全双工的SPI设备,须要同时发送和吸收数据。由于硬件限定,必须在发送数据时吸收数据。但是,并非所有运用都须要实际吸收数据,因此可以利用虚拟吸收缓冲区 `dummy_rx`。在这种情形下,吸收到的数据将被丢弃,而不会被处理或利用。
- `dummy_tx`:用于全双工设备的虚拟发送缓冲区。
这里须要重点关注下transfer函数,根据上文注释中对transfer函数的描述,可以提炼出函数具备以下特性:
- 支持双向批量传输
- `transfer()`方法不能休眠;它的紧张浸染是将添加到行列步队中
- 目前还没有从行列步队中删除操作,或者任何其他要求管理
- 对付给定的`spi_device`,行列步队是纯挚的FIFO办法
- 选择一个芯片然后传输数据,master的紧张事情是处理它的行列步队
- 如果有多个spi_device子节点,i/o行列步队仲裁算法未指定详细办法,可以是轮询,fifo,优先、保留、优先等
- 片选旗子暗记在全体期间保持生动,除非被`spi_transfer.cs_change != 0`所修正
- 传输利用时钟和SPI模式参数,这些参数是由setup()之前为这个设备建立
### SPI设备信息构造体
`struct spi_device`抽象了连接到SPI总线上的SPI从设备,`struct spi_device`的成员变量很多来自于`spi_board_info`。它的定义在 `include/linux/spi/spi.h` 文件,如下:
```c
struct spi_device {
struct device dev;
struct spi_master master;
u32 max_speed_hz;
u8 chip_select;
u8 bits_per_word;
u16 mode;
#define SPI_CPHA 0x01 / clock phase /
#define SPI_CPOL 0x02 / clock polarity /
#define SPI_MODE_0 (0|0) / (original MicroWire) /
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 / chipselect active high? /
#define SPI_LSB_FIRST 0x08 / per-word bits-on-wire /
#define SPI_3WIRE 0x10 / SI/SO signals shared /
#define SPI_LOOP 0x20 / loopback mode /
#define SPI_NO_CS 0x40 / 1 dev/bus, no chipselect /
#define SPI_READY 0x80 / slave pulls low to pause /
#define SPI_TX_DUAL 0x100 / transmit with 2 wires /
#define SPI_TX_QUAD 0x200 / transmit with 4 wires /
#define SPI_RX_DUAL 0x400 / receive with 2 wires /
#define SPI_RX_QUAD 0x800 / receive with 4 wires /
int irq;
void controller_state;
void controller_data;
char modalias[SPI_NAME_SIZE];
int cs_gpio; / chip select gpio /
/ the statistics /
struct spi_statistics statistics;
/
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
- ...
/
};
```
- `dev`: 一个指向 `struct device` 的指针,表示该 SPI 设备所属的设备。
- `master`: 一个指向 `struct spi_master` 的指针,表示该 SPI 设备所连接的 SPI 主掌握器。
- `max_speed_hz`: 一个无符号 32 位整数,表示该 SPI 设备的最大传输速率。
- `chip_select`: 一个无符号 8 位整数,表示该 SPI 设备的芯片选择线编号。
- `bits_per_word`: 一个无符号 8 位整数,表示每个数据传输字中的位数。
- `mode`: 一个无符号 16 位整数,表示 SPI 设备的事情模式。它是通过按位与操作来组合以下定义的模式选项的值:
- `SPI_CPHA`: 表示时钟相位(Clock Phase)。
- `SPI_CPOL`: 表示时钟极性(Clock Polarity)。
- `SPI_MODE_0`: SPI 模式 0。
- `SPI_MODE_1`: SPI 模式 1。
- `SPI_MODE_2`: SPI 模式 2。
- `SPI_MODE_3`: SPI 模式 3。
- `SPI_CS_HIGH`: 芯片选择旗子暗记是否为高电平有效。
- `SPI_LSB_FIRST`: 数据传输的位顺序,是否为最低有效位(LSB)先传输。
- `SPI_3WIRE`: 是否共享单线的串行输入和输出旗子暗记。
- `SPI_LOOP`: 是否开启回环模式。
- `SPI_NO_CS`: 是否禁用芯片选择旗子暗记。
- `SPI_READY`: 从设备拉低该旗子暗记以停息传输。
- `SPI_TX_DUAL`: 利用两根线进行发送。
- `SPI_TX_QUAD`: 利用四根线进行发送。
- `SPI_RX_DUAL`: 利用两根线进行吸收。
- `SPI_RX_QUAD`: 利用四根线进行吸收。
- `irq`: 一个整数,表示与该 SPI 设备干系联的中断要求(IRQ)线。
- `controller_state`: 一个指向掌握器状态的指针,用于保存掌握器的特定状态信息。
- `controller_data`: 一个指向掌握器数据的指针,用于保存掌握器的特定配置数据。
- `modalias`: 一个长度为 `SPI_NAME_SIZE` 的字符数组,表示 SPI 设备的模块别名。
- `cs_gpio`: 一个整数,表示与该 SPI 设备干系联的芯片选择旗子暗记所连接的 GPIO 引脚编号。
- `statistics`: 一个 `struct spi_statistics` 类型的变量,用于记录 SPI 设备的统计信息。
须要把稳的是:
- spi总线中,同一个韶光,spi掌握器只能跟一个从设备进行沟通。(这点类似I2C总线)
- spi掌握器是通过cs 片选引脚的高低来掌握和那个设备进行沟通。片选号对应片选引脚。
- `struct spi_device ->mode` 非常主要,紧张是相位(CPHA)和极性(CPOL)的搭配办法。
- `struct spi_device ->bits_per_word`,指定每次读写的字长,单位bit,如果该值为0,则默认利用8bit为字长。
### SPI设备驱动构造体
`struct spi_driver`描述一个SPI设备驱动,与对应的 `spi_device` 构造进行匹配后调用 probe;定义在 `include/linux/spi/spi.h`文件,如下:
```c
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);
struct device_driver driver;
};
```
`spi_driver`为主机端协议驱动数据构造,个中支持的函数或构造体功能定义:
- `id_table`:每每一个驱动可能能同时支持多个硬件,这些硬件的名字都放在该构造体数组中;
- `probe`:当驱动和设备信息匹配成功之后,就会调用probe函数,驱动所有的资源的注册和初始化全部放在probe函数中;
- `remove`:从spi设备解绑定该驱动程序
- `shutdown`:在系统状态转换期间利用的标准关机回调函数,例如powerdown/halt和kexec
- `driver`: SPI设备驱动程序该当初始化该构造的name和owner字段。
### SPI传输信息构造体
`struct spi_transfer`代表一个读写缓冲对,包括吸收缓冲区和发送缓冲区,`spi_transfer`的发送是通过构建`spi_message`实现,通过将`spi_transfer`中链表节点`transfer_list`链接到`spi_message`中的transfers,再以`spi_message`形式向底层发送数据。
实际上`spi_transfer`才是传输的最小单位,之以是又引进了`spi_message`进行打包,我以为缘故原由是:有时候希望往SPI从设备的多个不连续的地址(或寄存器)一次性写入,如果没有`spi_message`进行把这样的多个`spi_transfer`打包,由于常日真正的数据传送事情是在另一个内核线程(事情行列步队)中完成的,不打包的后果便是会造成更多的进程切换,效率降落,延迟增加,尤其对付多个不连续地址的小规模数据传送而言就更为明显。
每个`spi_transfer`都可以对传输的一些参数进行设备,使得`spi_controller`按照它哀求的参数进行数据发送。
`struct spi_transfer`定义在 `include/linux/spi/spi.h`文件,如下:
```c
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;
struct sg_table tx_sg;
struct sg_table rx_sg;
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;
};
```
- `tx_buf`: 要写入的数据缓冲区的指针,如果为 NULL,则在添补 `rx_buf` 时将输出零值。
- `rx_buf`: 要读取的数据缓冲区的指针,如果为 NULL,则吸收到的数据将被丢弃。
- `tx_dma`: `tx_buf` 的 DMA 地址
- `rx_dma`: `rx_buf` 的 DMA 地址
- `tx_nbits`: 用于写入的位数。如果为 0,则利用默认值 `SPI_NBITS_SINGLE`。
- `rx_nbits`: 用于读取的位数。如果为 0,则利用默认值 `SPI_NBITS_SINGLE`。
- `len`: 读写缓冲区的大小(以字节为单位)。
- `speed_hz`: 选择除设备默认速率以外的传输速率。如果为 0,则利用 `spi_device` 的默认速率。
- `bits_per_word`: 选择除设备默认 `bits_per_word` 以外的传输位数。如果为 0,则利用 `spi_device` 的默认 `bits_per_word`。
- `cs_change`: 传输完成后是否影响芯片选择旗子暗记。
- `delay_usecs`: 传输完成后延迟的微秒数,然后(可选地)变动芯片选择状态,然后开始下一个传输或完成当前的 `spi_message`。
- `transfer_list`: 用于在 `spi_message` 中顺序实行多个传输的链表。
- `tx_sg`: 用于传输的散列表(Scatterlist)。
- `rx_sg`: 用于吸收的散列表(Scatterlist)。
`struct spi_tranfer` 表示一个读写缓存对。`spi_transfer`的传送是通过构建一个`spi_message`来实现的。
`spi_transfer`的特点如下:
- 一个或多个`spi_transfer`组成一个`spi_message`.
- 一个`spi_message` 便是一个完全的spi传输,也便是一个CS 由高到低,再由低到高的过程。
- `spi_transfer` 代表一个读写缓冲对,包含吸收缓冲区及发送缓冲区,实在,`spi_transfer`的发送是通过构建`spi_message`实现。
- 通过将`spi_transfer`中的链表`transfer_list`链接到`spi_message`中的transfers,再以`spi_message`形势向底层发送数据。
- 每个`spi_transfer`都可以对传输的一些参数进行设置,使得spi主掌握器按照它哀求的参数进行数据发送。
- 每一个`spi_transfer` 都有自己的通讯速率,字宽 的哀求
### SPI 构造体
`struct spi_message`描述一个SPI传输的数据,`spi_message`用于实行数据传输的原子序列,由多个`spi_transfer`段组成,一旦掌握器吸收了一个`spi_message`,个中的`spi_transfer`该当按顺序被发送,并且不能被其它`spi_message`打断,以是我们认为`spi_message`便是一次SPI数据交流的原子操作。
`spi_message`定义在 `include/linux/spi/spi.h`文件,如下:
```c
/
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
@frame_length: the total number of bytes in the message
@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 always 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;
};
```
- `transfers`:一个链表,用于存储在该事务中的多个传输段。每个传输段都由构造体 `struct spi_transfer` 表示。可以通过遍历链表来访问每个传输段,以实行详细的数据传输操作。
- `spi`:指向该事务所关联的SPI设备的指针。通过该指针,可以确定将要实行通信的特定SPI设备。
- `is_dma_mapped`:这是一个标志位,如果设置为1,则表示对付每个传输缓冲区,调用者已经供应了DMA和CPU虚拟地址。这对付利用DMA进行高效数据传输非常有用。
- `complete`:该字段是一个函数指针,用于指定当事务完成时要调用的回调函数。在事务完成时,将调用指定的回调函数来处理事务完成的事宜。
- `context`:一个指针,作为参数通报给 `complete` 回调函数。可以用来通报额外的高下文信息给回调函数,以便进行干系处理。
- `frame_length`:表示该事务中的总字节数。它包括所有传输段的字节数之和。
- `actual_length`:表示在所有成功传输的传输段中实际传输的总字节数。该字段用于记录实际传输的数据量。
- `status`:表示事务的状态。如果为0,则表示事务成功完成;否则,表示发生了缺点,其值为负的缺点码(errno)。
- `queue`:该字段是为当前拥有该的驱动程序保留的,可供驱动程序在处理时利用。
- `state`:该字段也是为当前拥有该的驱动程序保留的,用于保存驱动程序内部的状态信息。
### SPI从设备构造体
`struct spi_board_info`描述的是详细的SPI从设备,定义在`include/linux/spi/spi.h`文件中,该构造记录着SPI从设备利用的SPI掌握器编号、片选旗子暗记、数据比特率、SPI传输模式等。如下:
```c
/
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
/
};
```
- `modalias`:该字段用于初始化 `spi_device.modalias` 字段,它是一个字符串,常日包含驱动程序的名称。这个字段在设备树中与设备关联起来,帮助内核识别并加载对应的驱动程序。
- `platform_data`:这个字段用于初始化 `spi_device.platform_data` 字段,它是一个指向特定于驱动程序的数据的指针。驱动程序可以利用这个字段通报一些特定于设备的配置参数或其他数据。
- `controller_data`:该字段用于初始化 `spi_device.controller_data` 字段,它是一个指向掌握器干系数据的指针。一些掌握器可能须要一些硬件设置的提示,例如关于 DMA 的设置,这个字段可以用来通报这些信息。
- `irq`:这个字段初始化了 `spi_device.irq` 字段,用于指定与该设备干系的中断号。详细的中断号取决于板子的布线办法和硬件设计。
- `max_speed_hz`:该字段初始化了 `spi_device.max_speed_hz` 字段,用于指定 SPI 设备的最大时钟速率。这个值常日基于芯片的数据手册以及板级特定的旗子暗记质量问题进行确定。
- `bus_num`:这个字段用于标识所属的 `spi_master`,即 SPI 掌握器的编号。它与 `spi_master` 构造体中的 `bus_num` 字段相对应,表示该设备所连接的 SPI 掌握器。
- `chip_select`:该字段初始化了 `spi_device.chip_select` 字段,用于指定与该设备干系的片选旗子暗记。详细的片选旗子暗记取决于板子的布线办法和硬件设计。
- `mode`:这个字段初始化了 `spi_device.mode` 字段,用于指定 SPI 设备的通信模式。通信模式是根据芯片的数据手册、板子的布线办法以及片选路径中是否存在反相器等成分确定的。
`spi_board_info` 中的大部分成员都是通过解析设备树得到,而`spi_board_info` 将来会被转换成`spi_device`类型,`spi_device`和`spi_board_info`内部很多成员都是相似的,`spi_device`中的很多成员变量值都继续于`spi_board_info`。
- `modalias`将初始化 `spi_device.modalias`,末了会与从设备驱动中`spi_device_id`的`name`做匹配;
- `flags` 将初始化 `spi_device.flags`;
- `platform_data` 将初始化 `spi_device.dev.platform_data`;
- `controller_data`将初始化`spi_device.controller_data`;
- `irq` 将初始化 `spi_device.irq` ;
- `max_speed_hz`将初始化 `spi_device.max_speed_hz`;
- `chip_select`将初始化 `spi_device.chip_select`;
- `mode`将初始化 `spi_device.mode`;
须要把稳的是,`struct spi_board_info` 构造体中的字段供应的是设备的静态信息,不能包含通过驱动程序的 `probe()` 函数动态确定的信息。
### spi_bitbang
`spi_bitbang`构造体的紧张用场是实现SPI总线的位操作(bit-banging)接口,用于掌握SPI总线的传输和通信,以知足特定的运用需求。
首先,对付多数情形来说,我们所用的SPI,都是有对应的SPI的掌握器的,其卖力和外部SPI设备进行通信,卖力两者通信时候的旗子暗记之间的同步,担保旗子暗记的timing都符合SPI协议,担保可以正常进行SPI通信。
但是有些时候,没有此对应的硬件上的SPI掌握器,而还想要和SPI设备通信,那么就只能用GPIO端口去仿照对应的SPI接口的对应的pin:片选CS,数据输入Data In,数据输出Data Out,时钟Clock,去仿照SPI协议,和对应spi设备进行通信。
以是,此时你对每个端口的操作,作为编程者,你自己要去卖力旗子暗记的同步,担保timing符合协议规定,才能正常进行SPI通信。
这样的SPI的bit-bang,优点是不须要SPI的掌握器了,但是缺陷很明显,除了要用户自己卖力同步,timing等事情之外,相对来说,纵然本身SPI设备支持以很高的频率运行,可以实现很好的性能,但是以`bit-bang`的办法去利用的话,实际性能每每很差。
末了,可以用一句话来阐明,什么是SPI的bitbang/bit-bang:
Use software to control serial communication at general-purpose I/O pins
通过GPIO引脚,用软件来仿照串行通信(SPI/I2C)
```c
struct spi_bitbang {
struct mutex lock;
u8 busy;
u8 use_dma;
u8 flags; / extra spi->mode support /
struct spi_master master;
/ setup_transfer() changes clock and/or wordsize to match settings
for this transfer; zeroes restore defaults from spi_device.
/
int (setup_transfer)(struct spi_device spi,
struct spi_transfer t);
void (chipselect)(struct spi_device spi, int is_on);
#define BITBANG_CS_ACTIVE 1 / normally nCS, active low /
#define BITBANG_CS_INACTIVE 0
/ txrx_bufs() may handle dma mapping for transfers that don't
already have one (transfer.{tx,rx}_dma is zero), or use PIO
/
int (txrx_bufs)(struct spi_device spi, struct spi_transfer t);
/ txrx_word[SPI_MODE_]() just looks like a shift register /
u32 (txrx_word[4])(struct spi_device spi,
unsigned nsecs,
u32 word, u8 bits);
};
```
- `struct mutex lock`: 这是一个互斥锁(mutex),用于对SPI总线进行同步访问。在并发访问SPI总线时,通过得到该锁,可以确保每次只有一个线程或进程能够访问SPI总线,避免数据竞争和冲突。
- `u8 busy`: 这个成员表示SPI总线的劳碌状态。当其值为1时,表示SPI总线正在实行传输操作;当其值为0时,表示SPI总线处于空闲状态。
- `u8 use_dma`: 这个成员表示是否利用DMA(Direct Memory Access)进行数据传输。如果其值为1,表示利用DMA办法传输数据;如果其值为0,表示利用PIO(Programmed Input/Output)办法进行数据传输。
- `u8 flags`: 这个成员是一些额外的标志,用于支持SPI模式的特定功能。
- `struct spi_master master`: 这是一个指向SPI主掌握器(spi_master)的指针。通过该指针,可以与特定的SPI主掌握器进行通信和交互,包括设置时钟频率、发送和吸收数据等。
- `int (setup_transfer)(struct spi_device spi, struct spi_transfer t)`: 这是一个函数指针,指向一个函数,用于设置SPI传输的参数。在每次传输之前,可以调用该函数来改变时钟频率、字大小等传输设置。该函数吸收两个参数,`spi`表示SPI设备的指针,`t`表示SPI传输的参数构造体(spi_transfer)。
- `void (chipselect)(struct spi_device spi, int is_on)`: 这是一个函数指针,指向一个函数,用于掌握SPI设备的片选旗子暗记。通过调用该函数,可以使能或禁用SPI设备的片选旗子暗记。函数的第一个参数是SPI设备的指针,第二个参数`is_on`表示片选旗子暗记的状态。宏定义`BITBANG_CS_ACTIVE`表示片选旗子暗记处于激活状态(常日为低电平),`BITBANG_CS_INACTIVE`表示片选旗子暗记处于非激活状态。
- `int (txrx_bufs)(struct spi_device spi, struct spi_transfer t)`: 这是一个函数指针,指向一个函数,用于在SPI设备和主机之间传输数据。该函数可以处理没有进行DMA映射的传输(`transfer.{tx,rx}_dma`为零),或者利用PIO办法进行数据传输。函数的第一个参数是SPI设备的指针,第二个参数是SPI传输的参数构造体。
- `u32 (txrx_word[4])(struct spi_device spi, unsigned nsecs, u32 word, u8 bits)`: 这是一个函数指针数组,用于在SPI设备和主机之间以位移寄存