mapleft的笔记 https://bbs.21ic.com/?41444 [收藏] [复制] [RSS]

日志

nuc980 spidev 访问设备失败问题解决

已有 821 次阅读2020-3-11 21:32 |个人分类:工蚁手记|系统分类:兴趣爱好

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


路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)