国家合同模板网站,wordpress 主图截图,东莞网,西安有几个区SPI数据类型
SPI控制器驱动结构体
struct spi_master抽象了控制器硬件#xff0c;在SoC中的指的就是内部SPI控制器#xff0c;当向SPI核心层注册一个SPI控制器时就需要提供这样的一个结构体变量。它的定义在 include/linux/spi/spi.h 文件#xff0c;如下#xff1a;
/*…SPI数据类型
SPI控制器驱动结构体
struct spi_master抽象了控制器硬件在SoC中的指的就是内部SPI控制器当向SPI核心层注册一个SPI控制器时就需要提供这样的一个结构体变量。它的定义在 include/linux/spi/spi.h 文件如下
/*** 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 its 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 n1 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 its 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* devices SPI controller; protocol code may call this. This* must fail if an unrecognized or unsupported mode is requested.* Its always safe to call this unless transfers are pending on* the device whose settings are being modified.* transfer: adds a message to the controllers 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* messages 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 boards schematics might show it using SPI-2. software* would normally use bus_num2 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) /* cant do full duplex */
#define SPI_MASTER_NO_RX BIT(1) /* cant do buffer read */
#define SPI_MASTER_NO_TX BIT(2) /* cant 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 theres no remove-from-queue operation, or* any other request management* To a given spi_device, message queueing is pure fifo** The masters 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;
};参数含义如下
devspi_master是一个device所以包含了一个device的实例设备模型使用把spi_master看做是device的子类list用于构建双向链表链接到spi_controller list链表中bus_numSPI控制器的编号比如某SoC有3个SPI控制那么这个结构描述的是第几个num_chipselect片选数量决定该控制器下面挂接多少个SPI设备从设备的片选号不能大于这个数量mode_bitsSPI 控制器支持模式标志位比如 SPI_CPHA支持时钟相位选择SPI_CPOL支持时钟记性选择SPI_CS_HIGH片选信号为高电平 bits_per_word_mask位掩码指示驱动程序支持的bits_per_word值。设置了第n位表示支持bits_per_word n1。如果设置了该位SPI核将拒绝任何使用不受支持的bits_per_word进行的传输。如果未设置该位则该值将被忽略由各个驱动程序执行任何验证。 min_speed_hz/max_speed_hz最大最小速率 slave是否是 slavebus_lock_spinlock用于SPI总线锁定的自旋锁。bus_lock_mutex用于SPI总线锁定的互斥锁。bus_lock_flag指示SPI总线是否被独占使用的标志。setupSPI控制器初始化函数指针用来设置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_messagebusy: 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中断控制器后这个参数时钟都是truetransfer、transfer_one、transfer_one_message用于SPI数据传输其区别在于 transfer添加一个message到SPI控制器传输消息队列如果未指定由SPI核心默认初始化为spi_queued_transfertransfer_one_message传输一个spi_message传输完成将会调用spi_finalize_current_message函数由SPI核心默认初始化为spi_transfer_one_messagetransfer_one传输一个spi_transfer0传输完成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 文件如下
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文件如下
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和kexecdriver: 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文件如下
struct spi_transfer {/* its 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文件如下
/*** 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 its 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 dont 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(), thats 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传输模式等。如下
/*** 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 cant 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 controllers device tree after the that controllers 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;* its 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.flagsplatform_data 将初始化 spi_device.dev.platform_datacontroller_data将初始化spi_device.controller_datairq 将初始化 spi_device.irq max_speed_hz将初始化 spi_device.max_speed_hzchip_select将初始化 spi_device.chip_selectmode将初始化 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
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 dont* 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: 这个成员表示是否使用DMADirect Memory Access进行数据传输。如果其值为1表示使用DMA方式传输数据如果其值为0表示使用PIOProgrammed 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设备和主机之间以位移寄存