spidev相关
spidev相关
背景
有时候会发现,一些外挂的spi的flash或者外设用了spidev
这个驱动,没有适配特定的驱动,到底spidev
是个啥呢?
spidev
:SPI userspace API
下面是内核官方文档解释:
SPI devices have a limited userspace API, supporting basic half-duplex
read() and write() access to SPI slave devices. Using ioctl() requests,
full duplex transfers and device I/O configuration are also available.
它主要包括核内的spidev
驱动,相当于spi设备的万能通用驱动, 类似于通用phy驱动,主要代码:
drivers/spi/spidev.c
include/uapi/linux/spi/spidev.h
使用
dts匹配驱动
设备匹配通用的spidev
驱动即可,其他属性参数根据设备硬件特性来写,都是通用的spi属性,和spi.h
头文件里面的对应,也可以通过下面的ioctl
的接口去重写或读取。
&spi0 {
status = "okay";
num-cs = <1>;
...
aaa: bbb@0 {
status = "okay";
reg = <0>;
compatible = "spidev"; /* 使用"spidev"匹配spidev驱动即可 */
...
spi-cpol;
spi-cpha;
spi-tx-bus-width = <1>;
spi-rx-bus-width = <1>;
spi-max-frequency = <5000000>;
};
};
正常情况下会有以下几种节点,对于SPI总线 B
上的片选为 C
的设备而言:
/dev/spidevB.C
…character special device, major number 153 with a dynamically chosen minor device number. This is the node that userspace programs will open, created by “udev” or “mdev”.
/sys/devices/.../spiB.C
…as usual, the SPI device node will be a child of its SPI master controller.
/sys/class/spidev/spidevB.C
…created when the “spidev” driver binds to that device. (Directory or symlink, based on whether or not you enabled the “deprecated sysfs files” Kconfig option.)
核外操作
核外操作主要针对/dev/spidevB.C
字符设备节点,通过open()
ioctl()
read()``close()
等接口操作
相关参数读写
/* Read / Write of SPI mode (SPI_MODE_0..SPI_MODE_3) (limited to 8 bits) */
#define SPI_IOC_RD_MODE _IOR(SPI_IOC_MAGIC, 1, __u8)
#define SPI_IOC_WR_MODE _IOW(SPI_IOC_MAGIC, 1, __u8)
/* Read / Write SPI bit justification */
#define SPI_IOC_RD_LSB_FIRST _IOR(SPI_IOC_MAGIC, 2, __u8)
#define SPI_IOC_WR_LSB_FIRST _IOW(SPI_IOC_MAGIC, 2, __u8)
/* Read / Write SPI device word length (1..N) */
#define SPI_IOC_RD_BITS_PER_WORD _IOR(SPI_IOC_MAGIC, 3, __u8)
#define SPI_IOC_WR_BITS_PER_WORD _IOW(SPI_IOC_MAGIC, 3, __u8)
/* Read / Write SPI device default max speed hz */
#define SPI_IOC_RD_MAX_SPEED_HZ _IOR(SPI_IOC_MAGIC, 4, __u32)
#define SPI_IOC_WR_MAX_SPEED_HZ _IOW(SPI_IOC_MAGIC, 4, __u32)
/* Read / Write of the SPI mode field */
#define SPI_IOC_RD_MODE32 _IOR(SPI_IOC_MAGIC, 5, __u32)
#define SPI_IOC_WR_MODE32 _IOW(SPI_IOC_MAGIC, 5, __u32)
注: 支持配置的相关SPI mode(SPI_IOC_RD_MODE32 / SPI_IOC_WR_MODE32)
#define SPI_CPHA _BITUL(0) /* clock phase */
#define SPI_CPOL _BITUL(1) /* 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_MODE_X_MASK (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH _BITUL(2) /* chipselect active high? */
#define SPI_LSB_FIRST _BITUL(3) /* per-word bits-on-wire */
#define SPI_3WIRE _BITUL(4) /* SI/SO signals shared */
#define SPI_LOOP _BITUL(5) /* loopback mode */
#define SPI_NO_CS _BITUL(6) /* 1 dev/bus, no chipselect */
#define SPI_READY _BITUL(7) /* slave pulls low to pause */
#define SPI_TX_DUAL _BITUL(8) /* transmit with 2 wires */
#define SPI_TX_QUAD _BITUL(9) /* transmit with 4 wires */
#define SPI_RX_DUAL _BITUL(10) /* receive with 2 wires */
#define SPI_RX_QUAD _BITUL(11) /* receive with 4 wires */
#define SPI_CS_WORD _BITUL(12) /* toggle cs after each word */
#define SPI_TX_OCTAL _BITUL(13) /* transmit with 8 wires */
#define SPI_RX_OCTAL _BITUL(14) /* receive with 8 wires */
#define SPI_3WIRE_HIZ _BITUL(15) /* high impedance turnaround */
数据读写操作
对于数据的读写支持半双工和全双工。
基本的read()
/write()
仅仅支持半双工,而此时片选是不可用的;
全双工需要使用SPI_IOC_MESSAGE(N)
的ioctl
请求,不需要去激活片选
/* not all platforms use <asm-generic/ioctl.h> or _IOC_TYPECHECK() ... */
#define SPI_MSGSIZE(N) \
((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \
? ((N)*(sizeof (struct spi_ioc_transfer))) : 0)
#define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)])
注:
- 每次I/O请求有个默认的传输字节数限制:1 page,但可以使用模块参数进行修改
- 目前暂不知道异步I/O操作,都是同步的
参考代码
以数据传输为例:
static void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
{
int ret;
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)tx,
.rx_buf = (unsigned long)rx,
.len = len,
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
if (mode & SPI_TX_OCTAL)
tr.tx_nbits = 8;
else if (mode & SPI_TX_QUAD)
tr.tx_nbits = 4;
else if (mode & SPI_TX_DUAL)
tr.tx_nbits = 2;
if (mode & SPI_RX_OCTAL)
tr.rx_nbits = 8;
else if (mode & SPI_RX_QUAD)
tr.rx_nbits = 4;
else if (mode & SPI_RX_DUAL)
tr.rx_nbits = 2;
if (!(mode & SPI_LOOP)) {
if (mode & (SPI_TX_OCTAL | SPI_TX_QUAD | SPI_TX_DUAL))
tr.rx_buf = 0;
else if (mode & (SPI_RX_OCTAL | SPI_RX_QUAD | SPI_RX_DUAL))
tr.tx_buf = 0;
}
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
pabort("can't send spi message");
}
参考
- 测试用例:
内核源码/tools/spi/spidev_test.c
和spidev_fdx.c
(全双工) - 内核文档:
内核源码/Documentation/spi/spidev.rst