SPI (三) -- 如何编写SPI设备驱动程序

编写SPI设备驱动程序

把大象放进冰箱,总共分三步:

编写设备树

  • 查看原理图,确定你的SPI设备挂在哪个SPI总线下面,去构造一个dts设备树,比如添加一个子节点。
  • 以下面tlc5615为例,你需要清楚SCK/MOSI/MISO/CS的引脚。也需要查看数据手册,这款芯片SPI时钟信号支持的最大时钟频率。
    SPI (三) -- 如何编写SPI设备驱动程序_第1张图片

下面是设备树的信息。

SPI (三) -- 如何编写SPI设备驱动程序_第2张图片

注册 spi_driver

  • 子节点会被转换成一个 spi_device
  • 对于相应的spi_device,需要编写一个spi_driver,并注册这个spi_driver。
  • spi_driver中的of_match_table表示支持的设备,内容是为了去和dts中的compatible变量去匹配,同时其中的probe函数可以去注册字符设备程序,调用register_chrdev。
  • 字符设备程序中,核心是file_operation结构体,结构体中会有open/write/read/ioctl功能函数定义。
static const struct of_device_id dac_of_match[] = {
	{.compatible = "fairchild,74hc595"},
	{}
};

static struct spi_driver dac_driver = {
	.driver = {
		.name	= "dac",
		.of_match_table = dac_of_match,
	},
	.probe		= dac_probe,
	.remove		= dac_remove,
	//.id_table	= dac_spi_ids,
};

发起SPI传输

对于SPI传输,功能无非就是写和读,在Linux内核的spi头文件中,对读写的接口函数进行了定义和说明

  • 同步写
/**
 * spi_write - SPI synchronous write
 * @spi: device to which data will be written 
 * @buf: data buffer
 * @len: data buffer size
 * Context: can sleep
 *
 * This function writes the buffer @buf.
 * Callable only from contexts that can sleep.
 *
 * Return: zero on success, else a negative error code.
 */
static inline int
spi_write(struct spi_device *spi, const void *buf, size_t len)//spi:写哪个设备
{
	struct spi_transfer	t = {
			.tx_buf		= buf,//需要写的数据内容
			.len		= len,//写数据的长度
		};

	return spi_sync_transfer(spi, &t, 1);//成功返回0,失败返回err code
}

函数最终返回时,调用了 spi_sync_transfer 函数。

SPI (三) -- 如何编写SPI设备驱动程序_第3张图片
SPI (三) -- 如何编写SPI设备驱动程序_第4张图片
所以大家关注的最终核心的函数是spi_sync,这是一个同步、阻塞的SPI传输函数,返回要么成功,要么失败。此外还有spi_async函数,对应的是异步的SPI传输函数,简单地说就是这个函数即刻返回,它返回后SPI传输不一定已经完成。

  • spi_sync
    SPI (三) -- 如何编写SPI设备驱动程序_第5张图片
    无法获得锁时,mutex会选择挂起当前线程,进入阻塞状态。所以,mutex是无法在中断上下文使用。
  • spi_async
    SPI (三) -- 如何编写SPI设备驱动程序_第6张图片
    和spi_sync相比,spi_async这个函数可以在_irq和其他不能休眠的上下文中使用,也可以在可以休眠的任务上下文中使用。此外
    • 完成SPI传输后,回调函数被调用,它是在"无法休眠的上下文"中被调用的,所以回调函数里不能有休眠操作。
    • 在回调函数被调用前message->statuss是未定义的值,没有意义。
    • 当回调函数被调用时,就可以根据message->status判断结果: 0-成功,负数表示失败码
    • 当回调函数执行完后,驱动程序要认为message等结构体已经被释放,不能再使用它们。
    • 在传输过程中一旦发生错误,整个message传输都会中止,对spi设备的片选被取消。
核心的数据结构

上面将函数读写的上下层调用关系简单说明,其中,有个重要的数据结构,就是 struct spi_message *message。spi_message中存放的transfer链表,是由多个spi_transfer结构体组成,链表通过上图中的spi_message_add_tail函数,将传输的buff放入链表尾部。
SPI (三) -- 如何编写SPI设备驱动程序_第7张图片

spi_message_add_tail函数:SPI (三) -- 如何编写SPI设备驱动程序_第8张图片
所以spi_transfer是我们传输数据的最基本单位。

SPI (三) -- 如何编写SPI设备驱动程序_第9张图片
那么,以同步写函数为例,主要的流程包括4步:

  • 构造spi_transfer结构体和内容
  • init spi_message
  • 将spi_transfer加入到spi_message尾部
  • 调用spidev_sync发送

SPI (三) -- 如何编写SPI设备驱动程序_第10张图片

还有其他的函数实现,在这里简单罗列,读者可以根据内核源码自行查找,还包括:

  • 同步读
/**
 * spi_read - SPI synchronous read
 * @spi: device from which data will be read
 * @buf: data buffer
 * @len: data buffer size
 * Context: can sleep
 *
 * This function reads the buffer @buf.
 * Callable only from contexts that can sleep.
 *
 * Return: zero on success, else a negative error code.
 */
static inline int
spi_read(struct spi_device *spi, void *buf, size_t len)//spi:读哪个设备
{
	struct spi_transfer	t = {
			.rx_buf		= buf,//读到的数据
			.len		= len,//数据长度
		};

	return spi_sync_transfer(spi, &t, 1);//成功返回0,失败返回err code
}
  • 先写再读
    • 这个函数是一个半双工操作,先发送txbuf中的数据,在读数据,将读到的数据放到rxbuf中
    • 这个函数用来传输少量数据(建议不要操作32字节), 它的效率不高
    • 返回值: 0-成功, 负数-失败码
/* 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,//txbuf:发送buffer,n_tx:发送多少字节
		void *rxbuf, unsigned n_rx);//rxbuf:接收buffer,n_tx:接收多少字节
  • 写8位读8位
/**
 * 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
 *
 * Callable only from contexts that can sleep.
 *
 * Return: the (unsigned) eight bit number returned by the
 * device, or else a negative error code.
 */
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; //返回值: 成功的话返回一个8位数据(unsigned), 负数表示失败码
}
  • 写8位读16位
/**
 * 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
 *
 * The number is returned in wire-order, which is at least sometimes
 * big-endian.
 *
 * Callable only from contexts that can sleep.
 *
 * Return: the (unsigned) sixteen bit number returned by the
 * device, or else a negative error code.
 */
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;//返回值: 成功的话返回一个16位数据(unsigned, 被转换为本地字节序), 负数表示失败码
}

你可能感兴趣的:(SPI,Linux驱动开发及设备树,驱动开发,linux)