nuc980 通过SPI总线与外部设备通讯:
INT32S SPIMasterTransceive(int fd,INT8U *tx,INT8U *rx,INT32U Len)
{
int ret;
static struct spi_ioc_transfer tr;
tr.tx_buf = (unsigned long)tx;
tr.rx_buf = (unsigned long)rx;
tr.len = Len;
tr.delay_usecs = 0;
tr.speed_hz = 0;
tr.bits_per_word = 0;
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
return ret;
}
应用程序如上, 把程序编译下载到评估板上运行,应用程序能打开对应的spidev设备 ,但是外部设备没有反应,
出动示波器测试SPI 的MOSI、MISO、SCK、CS 啥波形都没有
查看内核源码,SPIDEV设备对应的源码为spidev.c
static const struct file_operations spidev_fops = {
.owner = THIS_MODULE,
/* REVISIT switch to aio primitives, so that userspace
* gets more complete API coverage. It'll simplify things
* too, except for the locking.
*/
.write = spidev_write,
.read = spidev_read,
.unlocked_ioctl = spidev_ioctl,
.compat_ioctl = spidev_compat_ioctl,
.open = spidev_open,
.release = spidev_release,
.llseek = no_llseek,
};
应该程序调用ioctl()函数,对应调用内核 spidev.c中的 spidev_compat_ioctl()函数
static long
spidev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
if (_IOC_TYPE(cmd) == SPI_IOC_MAGIC
&& _IOC_NR(cmd) == _IOC_NR(SPI_IOC_MESSAGE(0))
&& _IOC_DIR(cmd) == _IOC_WRITE)
return spidev_compat_ioc_message(filp, cmd, arg);
return spidev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
}
通过函数跟踪得出 spidev_compat_ioc_message( ) 函数直接就返回 ,并没有运行下面的spidev_ioctl(),SPI 对应的引脚当然就没有信号
static long
spidev_compat_ioc_message(struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct spi_ioc_transfer __user *u_ioc;
int retval = 0;
struct spidev_data *spidev;
struct spi_device *spi;
unsigned n_ioc, n;
struct spi_ioc_transfer *ioc;
u_ioc = (struct spi_ioc_transfer __user *) compat_ptr(arg);
if (!access_ok(VERIFY_READ, u_ioc, _IOC_SIZE(cmd)))
return -EFAULT;
/* guard against device removal before, or while,
* we issue this ioctl.
*/
spidev = filp->private_data;
spin_lock_irq(&spidev->spi_lock);
spi = spi_dev_get(spidev->spi);
spin_unlock_irq(&spidev->spi_lock);
if (spi == NULL)
return -ESHUTDOWN;
/* SPI_IOC_MESSAGE needs the buffer locked "normally" */
mutex_lock(&spidev->buf_lock);
/* Check message and copy into scratch area */
ioc = spidev_get_ioc_message(cmd, u_ioc, &n_ioc); <-------- 这个函数有奸情
if (IS_ERR(ioc)) {
retval = PTR_ERR(ioc);
goto done;
}
if (!ioc)
goto done; /* n_ioc is also 0 */
/* Convert buffer pointers */
for (n = 0; n < n_ioc; n++) {
ioc[n].rx_buf = (uintptr_t) compat_ptr(ioc[n].rx_buf);
ioc[n].tx_buf = (uintptr_t) compat_ptr(ioc[n].tx_buf);
}
/* translate to spi_message, execute */
retval = spidev_message(spidev, ioc, n_ioc);
kfree(ioc);
done:
mutex_unlock(&spidev->buf_lock);
spi_dev_put(spi);
return retval;
}
接下来跟踪spidev_compat_ioc_message()函数,得出 spidev_get_ioc_message( )函数报告异常,直接返回
static struct spi_ioc_transfer *
spidev_get_ioc_message(unsigned int cmd, struct spi_ioc_transfer __user *u_ioc,
unsigned *n_ioc)
{
struct spi_ioc_transfer *ioc;
u32 tmp;
/* Check type, command number and direction */
if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC
|| _IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))
|| _IOC_DIR(cmd) != _IOC_WRITE)
return ERR_PTR(-ENOTTY);
tmp = _IOC_SIZE(cmd);
if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) <------ 这行代码不通过
return ERR_PTR(-EINVAL);
*n_ioc = tmp / sizeof(struct spi_ioc_transfer);
if (*n_ioc == 0)
return NULL;
/* copy into scratch area */
ioc = kmalloc(tmp, GFP_KERNEL);
if (!ioc)
return ERR_PTR(-ENOMEM);
if (__copy_from_user(ioc, u_ioc, tmp)) {
kfree(ioc);
return ERR_PTR(-EFAULT);
}
return ioc;
}
进入spidev_get_ioc_message( )函数, 分析得出:
tmp = _IOC_SIZE(cmd);
if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) <------ 这行代码不通过
return ERR_PTR(-EINVAL);
通过打印得出 tmp 的长度为 : 40
sizeof(struct spi_ioc_transfer) 的长度为: 32
40%32 = 8 肯定不为0
tmp = _IOC_SIZE(cmd)这个是应用程序编译传入的 spi_ioc_transfer 结构体长度
另外一个为内核 spidev.h 定义spi_ioc_transfer的结构体长度
以下这个结构体为编译工具链 arm_linux_4.8\arm_linux_4.8\arm-nuvoton-linux-uclibceabi\include\linux\spi\spidev.h 所定义
struct spi_ioc_transfer {
__u64 tx_buf;
__u64 rx_buf;
__u32 len;
__u32 speed_hz;
__u16 delay_usecs;
__u8 bits_per_word;
__u8 cs_change;
__u8 tx_nbits;
__u8 rx_nbits;
#define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */
#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
__u32 pad; <--------------- 这个定义为 __u32
/* If the contents of 'struct spi_ioc_transfer' ever change
* incompatibly, then the ioctl number (currently 0) must change;
* ioctls with constant size fields get a bit more in the way of
* error checking than ones (like this) where that field varies.
*
* NOTE: struct layout is the same in 64bit and 32bit userspace.
*/
};
这个结构体为内核源码 linux-4.4\linux-4.4.y\include\uapi\linux\spi\spidev.h 所定义
struct spi_ioc_transfer {
__u64 tx_buf;
__u64 rx_buf;
__u32 len;
__u32 speed_hz;
__u16 delay_usecs;
__u8 bits_per_word;
__u8 cs_change;
__u8 tx_nbits;
__u8 rx_nbits;
__u16 pad; <------------- 这个定义为 __u16
/* If the contents of 'struct spi_ioc_transfer' ever change
* incompatibly, then the ioctl number (currently 0) must change;
* ioctls with constant size fields get a bit more in the way of
* error checking than ones (like this) where that field varies.
*
* NOTE: struct layout is the same in 64bit and 32bit userspace.
*/
};
struct spi_ioc_transfer {
__u64 tx_buf;
__u64 rx_buf;
__u32 len;
__u32 speed_hz;
__u16 delay_usecs;
__u8 bits_per_word;
__u8 cs_change;
__u8 tx_nbits;
__u8 rx_nbits;
__u16 pad; <------------- 把这个定义修改为 __u16
#define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */
#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
/* If the contents of 'struct spi_ioc_transfer' ever change
* incompatibly, then the ioctl number (currently 0) must change;
* ioctls with constant size fields get a bit more in the way of
* error checking than ones (like this) where that field varies.
*
* NOTE: struct layout is the same in 64bit and 32bit userspace.
*/
};
把编译器所在的spidev.h 头文件修改为与内核定义的一样,重新编译应该成,问题排除 !
QQ交流群:761781147