Linux 内核 spi 的 API

1. SPI

1.1 SPI 相关结构体

1.1.1 struct spi_device spi 设备结构体

#include <linux/spi.h>

struct spi_device {
	struct device dev;	/* 继承的 device 结构体 */
	/* 当前 spi 设备挂载的 spi 控制器 */
	struct spi_controller *controller;
	/* 指定 spi 设备挂载的 spi 总线 */
	struct spi_controller *master; /* compatibility layer */
	u32 max_speed_hz;	/* spi 的最大传输速率 */
	u8 chip_select;		/* spi 总线用于区分不同 spi 设备的标号 */
	u8 bits_per_word;	/* spi 通讯时一个字节多少个位 */
	u16 mode;			/* spi 工作模式 */

	/* 
		下列的宏定义是用来设置 mode 成员的
		mode 的每个位都代表不同的模式功能
		如 mode 的第 0 位为 1 时,SPI_CPHA 使能,为 0 时 SPI_CPHA 失能
	 */
	/* 工作模式如以上代码中的宏定义。包括时钟极性、位宽等等,这些宏定义可以使用或运算 | 进行组合 */
	#define SPI_CPHA 0x01 		/* clock phase */
	#define SPI_CPOL 0x02 		/* clock polarity */
	#define SPI_MODE_0 (0|0) 	/* (original MicroWire)␣
*/	
	/* SPI_CPOL 和 SPI_CPHA的值同时也指定了全双工模式/半双工等工作模式 */
	#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;	/* 记录 spi 的名字,用于和 spi_driver 进行匹配 */
};

1.1.2 spi_controller spi 总线控制器结构体

#include  <linux/spi/spi.h>

struct spi_controller {
    struct device dev;
    ...
    struct list_head list;	/* 链表节点,IC 可能有多个 spi 控制器 */
    s16	bus_num;	/* spi 控制器的编号 */
    u16	num_chipselect; /* spi 片选信号的个数 */
    ...
    struct spi_message	*cur_msg; /* spi_message 结构体,发送消息时都会把信息封装在这个结构体中 */
    ...
    int (*setup)(struct spi_device *spi);
    /* 用于将数据加入 spi 控制器的消息队列中 */
    int (*transfer)(struct spi_device *spi,		
	                struct spi_message *mesg);	
	/* 当 spi_message 被释放时,执行清理工作 */
    void (*cleanup)(struct spi_device *spi);
    /* 内核线程工人,spi 可以使用异步传输的方式发送数据 */
    struct kthread_worker kworker;
    struct task_struct  *kworker_task;
    struct kthread_work pump_messages;	/* 具体传输工作 */
    struct list_head queue;	/* 所有等待传输消息队列挂载的头部链表节点 */
    struct spi_message *cur_msg;

    ...
    int (*transfer_one)(struct spi_controller *ctlr, struct spi_device *spi,struct spi_transfer *transfer);
    int (*prepare_transfer_hardware)(struct spi_controller *ctlr);
    /* 产生 spi 时序的底层接口 */
    int (*transfer_one_message)(struct spi_controller *ctlr,struct spi_message *mesg);
    void (*set_cs)(struct spi_device *spi, bool enable);
    ...
    int *cs_gpios;	/* 记录 spi 上具体的片选信号 */
}

1.1.3 struct spi_transfer 数据传输结构体

#include <linux/spi/spi.h>

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;	/* 传输长度,根据 spi 的特性发送和接收的长度相等 */

    dma_addr_t      tx_dma;	/* 如果使用了 DMA,用于指定 DMA 传输的发送地址 */
    dma_addr_t      rx_dma; /* 如果使用了 DMA,用于指定 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;	/* spi 的发送频率 */

    struct list_head transfer_list;
};

1.1.4 struct spi_message spi 消息结构体

· 总体来说 spi 设备驱动中的数据是以消息队列的形式发送的
· 发送和接收数据都是封装在 spi_message 消息结构体中的

#inlude <linux/spi/spi.h>

struct spi_message {
    struct list_head        transfers;	/* spi 数据传输结构体链表头部节点 */

    struct spi_device       *spi;	/* 对应的 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;
};

1.1.5 struct spi_driver spi 驱动结构体

#include <linux/spi/spi.h>

struct spi_driver {
    const char             *name;         // 驱动程序名称
    const char             *modalias;     // 驱动程序的别名,可选
    unsigned int            bus_type;     // 总线类型,如 SPI_BUS_TYPE_SPI
    int                    max_speed_hz;  // 最大传输速率,单位为 Hz
    int                    max_queue_len; // 最大队列长度
    struct device_driver   *drv;          // 继承的驱动程序实例
    struct bus_type        *bus;          // SPI 总线实例
    struct device_node     *of_node;      // 设备树节点
    const struct fwnode_handle *fwnode;   // Firmware 节点
    const char             *modalias_of_node;  // 设备树节点的驱动程序别名
    void (*probe)(struct spi_device *);  	// 设备探测回调函数
    void (*remove)(struct spi_device *); 	// 设备删除回调函数
    void (*shutdown)(struct spi_device *); 	// 设备关闭回调函数
    void (*suspend)(struct spi_device *);  	// 设备挂起回调函数
    void (*resume)(struct spi_device *);   	// 设备恢复回调函数
    void (*init)(struct spi_device *);    	// 设备初始化回调函数
    struct list_head        entry;        	// 设备列表
    unsigned int            num_devices;  	// 设备数量
};

1.1.n struct spi_ioc_transfer 应用层 spi 通讯结构体

#include <linux/spi/spidev.h>

struct spi_ioc_transfer {
   __u64	tx_buf;     //发送数据缓存
   __u64	rx_buf;     //接收数据缓存

   __u32	len;        //数据长度(以字节为单位的)
   __u32	speed_hz;   //通讯速率

   __u16	delay_usecs;    //两个spi_ioc_transfer之间的延时,微秒
   __u8		bits_per_word;  //数据长度
   __u8		cs_change;      //取消选中片选
   __u8		tx_nbits;       //单次数据宽度(多数据线模式)
   __u8		rx_nbits;       //单次数据宽度(多数据线模式)
   __u16	pad;

};

1.2 spi_register_driver 注册 SPI 驱动

#include <linux/spi.h>

int spi_register_driver(sturct spi_driver *sdrv); 
· int :执行成功返回 0,执行失败返回负数
· sturct spi_driver *sdrv :申请注册的 SPI 驱动结构体对象

1.3 spi_unregister_driver 注销 SPI 驱动

#include <linux/spi.h>

static inline void spi_unregister_driver(sturct spi_driver *sdrv);

· sturct spi_driver *sdrv :申请注册的 SPI 驱动结构体对象

1.4 spi_setup 设置 spi

· spi_setup 函数用于设置 spi 的传输速率,带宽和片选信号等

#include <linux/spi.h>

int spi_setup(struct spi_device *spi)
· int : 执行成功返回 0,执行失败返回负值
· struct spi_device *spi : spi spi_device spi设备结构体

1.5 spi_message_init 消息结构体初始化

#include <linux/spi.h>

static inline void spi_message_init(struct spi_message *m)
{
    memset(m, 0, sizeof *m); /* 清空内存操作 */
    spi_message_init_no_memset(m);	/* 初始化结构体的接口 */
}

· struct spi_message *m : 要初始化的消息结构体指针对象

1.6 spi_message_add_tail 添加传输结构体到消息结构体对象中

· 将 spi_transfer 结构体添加到 spi_message 队列的末尾

#include <linux/spi.h>

static inline void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
{
    list_add_tail(&t->transfer_list, &m->transfers);
}
· struct spi_transfer *t : 要添加的数据传输结构体的指针对象
· struct spi_message *m : 操作的消息结构体指针指针对象

1.7 spi_sync spi 同步传输

· 阻塞当前进程,直至将 message 中的数据传输完成才结束阻塞

#include <linux/spi.h>

int spi_sync(struct spi_device *spi, struct spi_message *message)
{
    int ret;

    mutex_lock(&spi->controller->bus_lock_mutex);
    ret = __spi_sync(spi, message);
    mutex_unlock(&spi->controller->bus_lock_mutex);

    return ret;
}
static int __spi_sync(struct spi_device *spi, struct spi_message *message)
{
    int status;
    struct spi_controller *ctlr = spi->controller;
    unsigned long flags;

    status = __spi_validate(spi, message);
    if (status != 0)
        return status;

    message->complete = spi_complete;
    message->context = &done;
    message->spi = spi;
    ...
    if (ctlr->transfer == spi_queued_transfer) {
        spin_lock_irqsave(&ctlr->bus_lock_spinlock, flags);

        trace_spi_message_submit(message);

        status = __spi_queued_transfer(spi, message, false);

        spin_unlock_irqrestore(&ctlr->bus_lock_spinlock, flags);
    } else {
        status = spi_async_locked(spi, message);
    }


    if (status == 0) {
        ...
        wait_for_completion(&done);
        status = message->status;
    }
    message->context = NULL;
    return status;
}
· struct spi_message *m : 操作的消息结构体指针指针对象
· struct spi_device *spi : spi spi_device spi设备结构体

1.8 spi_async spi 异步传输

· 异步传输不会阻塞当前进程,但内核态又要怎么在 spi 异步通讯完成后去通知用户空间呢?
	·   struct spi_message 在 spi 的消息结构体中你可以看到下面这俩个成员
		· void (*complete)(void *context);
	    · void *context;
	 · 当 spi 异步传输完成后,内核会执行 complete 这个回调函数,而 context 是 complete 回调函数的参数
	 · 所以只需要自定义 complete 类型的回调函数供内核调用,在回调函数中编写通知用户态的内容即可
#include <linux/spi.h>

int spi_async(struct spi_device *spi, struct spi_message *message)
{
    ...
    ret = __spi_async(spi, message);
    ...
}
static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
    struct spi_controller *ctlr = spi->controller;
    ...
    return ctlr->transfer(spi, message);
}
· struct spi_message *m : 操作的消息结构体指针指针对象
· struct spi_device *spi : spi spi_device spi设备结构体
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值