3

我正在尝试实现一个程序来访问嵌入式系统上的内存。我需要访问一些控制寄存器,所以我认为 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 的嵌入式系统进行编译。

4

3 回答 3

7

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

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

Code  Seq#(hex) Include File            Comments
========================================================
0x00    00-1F   linux/fs.h              conflict!
0x00    00-1F   scsi/scsi_ioctl.h       conflict!
0x00    00-1F   linux/fb.h              conflict!
0x00    00-1F   linux/wavefront.h       conflict!
0x02    all     linux/fd.h
0x03    all     linux/hdreg.h
...

根据您所处的环境,您必须遵循内核指南来添加新的 ioctls():

If you are adding new ioctl's to the kernel, you should use the _IO
macros defined in <linux/ioctl.h>:

    _IO    an ioctl with no parameters
    _IOW   an ioctl with write parameters (copy_from_user)
    _IOR   an ioctl with read parameters  (copy_to_user)
    _IOWR  an ioctl with both write and read parameters.

Documentation/ioctl/ioctl-decoding.txt有关这些数字的结构的更多详细信息,请参阅内核自己的文档。

在实践中,如果您选择 Code 1,这意味着从0x100直到开始0x1ff,您就可以了。

于 2012-08-21T04:10:27.323 回答
3

你的aes_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 标准支持的方法)来执行此操作。

于 2012-08-21T03:19:29.797 回答
2

在不知道返回的错误的情况下,很难说...我的第一个问题是您对文件描述符的权限。我以前见过类似的问题。首先,如果您查看 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); 
} 

检查返回值,这应该有助于您搜索。为什么要检查?请参阅帖子的底部... 接下来要检查是否是权限问题,请运行以下命令:

ls -l /dev/aes

如果你看到类似的东西:

crw------- 1 root root 10, 57 Aug 21 10:24 /dev/aes

然后只需发出一个:

sudo chmod 777 /dev/aes

再试一次。我敢打赌它会为你工作。(请注意,我使用 root 权限运行它,因为 root 是我的 mod 版本的所有者)

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

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;
}

那么输出是什么?让我们假设 /dev/hello 上有错误的文件权限(这意味着我们的用户空间程序无法访问 /dev/hello)。

dmesg | 尾部显示:

[ 2388.051660] Hello!

所以看起来我们没有“进入”ioctl。程序的输出是什么?

error: -1, errno: 9, meaning: Bad file descriptor

很多有用的输出。显然 ioctl 调用做了一些事情,只是不是我们想要的。现在更改权限并重新运行,我们可以看到新的 dmesg:

[ 2388.051660] Hello!
[ 2625.025339] in ioctl
于 2012-08-21T14:45:19.607 回答