为啥 ioctl 返回“错误地址”

Posted

技术标签:

【中文标题】为啥 ioctl 返回“错误地址”【英文标题】:why does ioctl return "bad address"为什么 ioctl 返回“错误地址” 【发布时间】:2013-04-02 16:50:50 【问题描述】:

我使用下面的代码从嵌入式板的 SPI 端口输出数据(olimex imx233-micro -- 这不是板特定的问题)。当我运行代码ioctl 时,返回“bad address”。我正在修改http://twilight.ponies.cz/spi-test.c 上的代码,效果很好。谁能告诉我我做错了什么?

root@ubuntu:/home# gcc test.c -o test
test.c:20: warning: conflicting types for ‘msg_send’
test.c:16: note: previous implicit declaration of ‘msg_send’ was here
root@ubuntu:/home# ./test
errno:Bad address - cannot send SPI message
root@ubuntu:/home# uname -a
Linux ubuntu 3.7.1 #2 Sun Mar 17 03:49:39 CET 2013 armv5tejl GNU/Linux

代码:

//test.c
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <errno.h>

static uint16_t delay;

int main(int argc,char *argv[])
     msg_send(254); //the message that I want to send decimal "254"
     return 0;


void msg_send(int msg)
    int fd;
    int ret = 0;
    fd = open("/dev/spidev32766.1", O_RDWR); //ls /dev outputs spidev32766.1
    if(fd < 0)
        fprintf(stderr, "errno:%s - FD could be not opened\n ", strerror(errno));  
        exit(1);
        

    struct spi_ioc_transfer tr = 
        .len = 1,
        .delay_usecs = delay,
        .speed_hz = 500000, //500 kHz
        .bits_per_word = 8,
        .tx_buf = msg,
        .rx_buf = 0, //half duplex
    ;

    ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
    if (ret <1 )
        fprintf(stderr, "errno:%s - cannot send SPI message\n ", strerror(errno));
    
    close(fd);

谢谢!

【问题讨论】:

您知道将msg_send() 函数移到main() 上方会消除该警告吗? 谢谢你,迈克,我很粗心。警告消失但bad address 错误仍然存​​在 .tx_buf=msg -- 你认为这里应该发生什么? n.m.,我以为它会将我要发送到发送器缓冲区的消息(254)传递给发送器缓冲区,然后它将通过ioctl 发送,是我做错了吗? 【参考方案1】:

错误消息“Bad address”来自错误代码EFAULT,当您将地址传递给内核时,该地址在您的进程的虚拟地址空间中不是有效的虚拟地址。 tr 结构的地址显然是有效的,所以问题一定出在其中一个成员上。

根据definition of struct spi_ioc_transfer.tx_buf.rx_buf 成员必须是指向用户空间缓冲区的指针,或者为空。您将 .tx_buf 设置为整数 254,这不是有效的用户空间指针,所以这就是错误地址的来源。

我不熟悉这个 IOCTL,所以我最好的猜测是您需要将数据放入二进制中。一种方法是:

struct spi_ioc_transfer tr = 
    .len = sizeof(msg),  // Length of rx and tx buffers
     ...
    .tx_buf = (u64)&msg, // Pointer to tx buffer
    ...
;

如果您需要将其作为 ASCII 码发送,则应使用 snprintf(3) 等函数将整数转换为 ASCII 字符串,然后将 TX 缓冲区指向该字符串并相应地设置长度。

【讨论】:

以上是关于为啥 ioctl 返回“错误地址”的主要内容,如果未能解决你的问题,请参考以下文章

为啥 ioctl I_SENDFD 不返回任何权限(EPERM)? [复制]

为啥来自 /dev/null 的 ioctl FIONREAD 在 Mac OS X 上返回 0 而在 Linux 上返回随机数?

Linux ioctl 返回值由谁解释?

WDF 内部 IOCTL 不返回输出

Ioctl返回-1的一个Bug查找

调用 ioctl() 导致 errno 指示“错误地址”