从用户空间C调用ioctl时出错

从用户空间C调用ioctl时出错,c,linux-kernel,ioctl,C,Linux Kernel,Ioctl,我正在尝试实现一个程序来访问嵌入式系统上的内存。我需要访问一些控制寄存器,所以我认为ioctl是最好的方法。我已将ioctl添加到fops: struct file_operations aes_fops = { read: aes_read, write: aes_write, unlocked_ioctl: aes_ioctl, open: aes_open, release: aes_release }; 并设置了以下功能: int aes_ioctl(struct

我正在尝试实现一个程序来访问嵌入式系统上的内存。我需要访问一些控制寄存器,所以我认为ioctl是最好的方法。我已将ioctl添加到fops:

struct file_operations aes_fops = {
  read: aes_read,
  write: aes_write,
  unlocked_ioctl: aes_ioctl,
  open: aes_open,
  release: aes_release
};
并设置了以下功能:

int aes_ioctl(struct inode *inode,  
     struct file *file, 
     unsigned int ioctl_num,    
     unsigned long ioctl_param){

     printk(KERN_INFO "in ioctl\n");
....
}
但是我没有进入这个函数的内部。这是我的用户空间代码。请帮助我理解,如果我这样做是完全错误的

int main(int argc, char* argv[]){
    int fd = fopen("/dev/aes", "r+");
    ioctl(fd, 0, 1);
    fclose(fd);
}

有些代码显然是针对较旧的内核的,因为我编译的是一个嵌入式系统,其中修改了较旧版本的Linux。

您的
aes\u fops
结构设置正确吗?我从来没有见过这样做。我所有的代码都是:

.unlocked_ioctl = aes_ioctl,
而不是:

unlocked_ioctl: aes_ioctl,
据我所知(以及在定义过程中),结构中的冒号(正如您在设置中所做的)字段用于位字段,而不是初始化单个字段

换句话说,尝试:

struct file_operations aes_fops = {
    .read            = aes_read,
    .write           = aes_write,
    .unlocked_ioctl  = aes_ioctl,
    .open            = aes_open,
    .release         = aes_release
};


注:gcc似乎曾经允许该结构字段初始化的变体,但自gcc 2.5(见gcc文档中的直接内容)以来,该变体已经过时。您确实应该使用适当的方法(即ISO标准所认可的方法)来完成此操作。

代码的问题在于您使用的请求编号-
0
。内核保留了一些请求号供内部使用。内核将请求号视为一个结构,将其划分为字段并为其调用正确的子系统

请参阅文档/ioctl/ioctl-number.txt(来自Linux 3.4.6):

根据您正在执行的操作,您必须遵循添加新ioctls()的内核指南:

如果要向内核添加新的ioctl,则应使用
在中定义的宏:
_IO是一个不带参数的ioctl
_IOW带有写入参数的ioctl(从用户处复制)
_IOR具有读取参数的ioctl(复制到用户)
_IOWR具有写入和读取参数的ioctl。
请参阅内核自己的
文档/ioctl/ioctl decoding.txt
文档,了解有关这些数字的结构的更多详细信息


实际上,如果您选择代码1,即从0x100开始,一直到0x1ff,您就可以了。

如果不知道返回的错误,很难说。。。我的第一个想法是您对文件描述符的权限。我以前也见过类似的问题。首先,如果查看ioctl的返回,您可以获得有关失败的更多信息:

#include <errno.h>
int main(int argc, char* argv[])
{
    long ret;     
    int fd = fopen("/dev/aes", "r+");
    ret = ioctl(fd, 0, 1);
    if (ret < 0)
        printf("ioctl failed. Return code: %d, meaning: %s\n", ret, strerror(errno));
    fclose(fd); 
} 
如果您看到以下内容:

crw------- 1 root root 10, 57 Aug 21 10:24 /dev/aes
然后只需发布一个:

sudo chmod 777 /dev/aes
然后再试一次。我打赌这对你会有用的。(注意,我使用root权限运行,因为root是我的mod版本的所有者)

如果权限已经确定,那么我还有一些建议:

1) 对我来说,fopen/fclose的用法很奇怪。你真的只需要做:

int fd = open("/dev/aes");
close(fd);
我的系统甚至不允许你的代码按原样编译

2) 您的IOCTL参数列表很旧,我不知道编译的内核版本,但最近的内核使用以下格式:

long aes_ioctl(struct file *file, unsigned int ioctl_num, unsigned long ioctl_param){   
注意inode的删除和返回类型的更改。当我在我的系统上运行你的代码时,我做了这些更改

祝你好运

注意:当我们“没有进入ioctl”时,为什么要检查退货?让我举个例子:

//Kernel Code:
//assume include files, other fops, exit, miscdev struct, etc. are present

long hello_ioctl(struct file *file, unsigned long ioctl_num, unsigned long ioctl_param) {
    long ret = 0;
    printk("in ioctl");
    return ret;
}

static const struct file_operations hello_fops = {
    owner: THIS_MODULE,
    read: hello_read,
    unlocked_ioctl: hello_ioctl,
};

static int __init hello_init(void) {
    int ret;
    printk("hello!\n");
    ret = misc_register(&hello_dev); //assume it worked...
    return ret;
}
用户空间代码:

//assume includes

void main() {
  int fd;
  long ret;
  fd = open("/dev/hello");
  if(fd) {
    c = ioctl(fd, 0, 1);
    if (c < 0)
      printf("error: %d, errno: %d, meaning: %s\n", c, errno, strerror(errno));
    close(fd);
  }
  return;
}
看来我们没有“进入”ioctl。程序的输出是什么

error: -1, errno: 9, meaning: Bad file descriptor
许多有用的输出。显然ioctl电话做了些什么,只是不是我们想要的。现在更改权限并重新运行,我们可以看到新的dmesg:

[ 2388.051660] Hello!
[ 2625.025339] in ioctl

C99标准是向后兼容的,如果愿意,他可以使用旧的初始化方法(除非驱动程序提交给内核维护人员)。不过,这与手头的问题无关。c99标准和早期的ISO标准都没有提到这种方法。我想你指的是gcc的“标准版”,即扩展版。你可能在实际原因上是对的,在这种情况下,你的答案无疑会把我的答案从水里吹出来:-)我还是把我的留在这里,因为我认为使用适当的语言功能是个好主意。请看。@Don,我知道C99已经指定了初始化器,但
字段:value
变体是一个gcc扩展(现已过时),不是标准的一部分。实际上,它在您在另一个答案中提供的链接文档中推断了同样多的内容。这种形式已经过时了,因为标准现在提供了一种实现这种形式的方法。这就是我要说的重点,并不是说你对指定初始值设定者本身的描述有缺陷。事实上,@DanAloni,因为你的答案无疑比我对问题的实际原因的回答更正确,所以我向你投赞成票:-)不要回应,我想确保最好的答案排在第一位。我不相信我会进入ioctl函数,所以不会有任何返回值。我也有适当的权限。你能进一步解释一下“打开”功能吗。这适合C语言吗?你能给我指一些文档吗?我更新了OP,解释说这是针对嵌入式系统的旧版本Linux。你试过检查吗?如果您的代码编译并运行,那么您确实“进入”了ioctl调用,您可能还没有完全进入您的aes_ioctl处理程序。如果是这种情况,那么返回不是您设置的,而是系统让您知道您没有访问函数的内容/方式的方式。例如,您可能会得到一个EBADF(坏文件处理程序)或EFAULT(不可访问内存中的引用)。很好地说明了为什么没有调用ioclt处理程序。对于open()和close(),这些只是C系统调用,我认为用户空间ioctl调用的函数不对。我在ioctl函数中有一个printk,它没有出现在控制台中,所以我假设这两个函数还没有连接。“打开”功能有3个参数,对吗?与“fopen”相比,它有什么优势?请查看我的编辑,了解为什么您应该
//assume includes

void main() {
  int fd;
  long ret;
  fd = open("/dev/hello");
  if(fd) {
    c = ioctl(fd, 0, 1);
    if (c < 0)
      printf("error: %d, errno: %d, meaning: %s\n", c, errno, strerror(errno));
    close(fd);
  }
  return;
}
[ 2388.051660] Hello!
error: -1, errno: 9, meaning: Bad file descriptor
[ 2388.051660] Hello!
[ 2625.025339] in ioctl