【发布时间】:2019-09-01 01:47:51
【问题描述】:
我正在学习设备驱动程序中的 ioctl 功能,
file_operations 中有一个函数指针 .compat_ioctl,它允许 32 位进程在 64 位机器中使用 ioctl。
以下是我的驱动程序代码:
#ifndef __IOCTL_CMD_H
#define __IOCTL_CMD_H
#define MSG_MAGIC_NUMBER 0x21
#define MSG_IOCTL_GET_LENGTH _IOR(MSG_MAGIC_NUMBER, 1, unsigned int)
#define MSG_IOCTL_CLEAR_BUFFER _IO(MSG_MAGIC_NUMBER, 2)
#define MSG_IOCTL_FILL_BUFFER _IOW(MSG_MAGIC_NUMBER, 3, unsigned char)
#define MSG_GET_ADDRESS _IOR(MSG_MAGIC_NUMBER, 4, unsigned long)
#define MSG_IOCTL_MAX_CMDS 4
#endif
long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
unsigned char ch;
int retval = 0;
long size = _IOC_SIZE(cmd);
pr_info("%s: Cmd:%u\t Arg:%lu Size:%lu add:%p\n", __func__, cmd, arg, size, &ch);
if (_IOC_TYPE(cmd) != MSG_MAGIC_NUMBER) return -ENOTTY;
if (_IOC_NR(cmd) > MSG_IOCTL_MAX_CMDS) return -ENOTTY;
//access_ok is kernel-oriented, so the concept of read and write is reversed
retval = access_ok((void __user *)arg, size);
pr_info("access_ok returned:%d\n", retval);
if (!retval)
return -EFAULT;
switch(cmd)
{
//Get Length of buffer
case MSG_IOCTL_GET_LENGTH:
pr_info("Get Buffer Length\n");
put_user(MAX_SIZE, (unsigned int *)arg);
break;
//clear buffer
case MSG_IOCTL_CLEAR_BUFFER:
pr_info("Clear buffer\n");
memset(kernel_buffer, 0, sizeof(kernel_buffer));
break;
//fill character
case MSG_IOCTL_FILL_BUFFER:
get_user(ch, (unsigned char *)arg);
pr_info("Fill Character:%c\n", ch);
memset(kernel_buffer, ch, sizeof(kernel_buffer));
buffer_index = sizeof(kernel_buffer);
break;
//address of kernel buffer
case MSG_GET_ADDRESS:
put_user(0x12345678, (unsigned long*)arg);
pr_info("MSG_GET_ADDRESS\n");
break;
default:
pr_info("Unknown Command:%u\n", cmd);
return -ENOTTY;
}
return 0;
}
long device_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
unsigned char ch;
int retval = 0;
long size = _IOC_SIZE(cmd);
pr_info("%s: Cmd:%u\t Arg:%lu Size:%lu add:%p\n", __func__, cmd, arg, size, &ch);
if (_IOC_TYPE(cmd) != MSG_MAGIC_NUMBER) return -ENOTTY;
if (_IOC_NR(cmd) > MSG_IOCTL_MAX_CMDS) return -ENOTTY;
//access_ok is kernel-oriented, so the concept of read and write is reversed
retval = access_ok((void __user *)arg, size);
pr_info("access_ok returned:%d\n", retval);
if (!retval)
return -EFAULT;
switch(cmd)
{
//Get Length of buffer
case MSG_IOCTL_GET_LENGTH:
pr_info("Get Buffer Length\n");
put_user(MAX_SIZE, (unsigned int *)arg);
break;
//clear buffer
case MSG_IOCTL_CLEAR_BUFFER:
pr_info("Clear buffer\n");
memset(kernel_buffer, 0, sizeof(kernel_buffer));
break;
//fill character
case MSG_IOCTL_FILL_BUFFER:
get_user(ch, (unsigned char *)arg);
pr_info("Fill Character:%c\n", ch);
memset(kernel_buffer, ch, sizeof(kernel_buffer));
buffer_index = sizeof(kernel_buffer);
break;
//address of kernel buffer
case MSG_GET_ADDRESS:
put_user(0x12345678, (unsigned long*)arg);
pr_info("MSG_GET_ADDRESS\n");
break;
default:
pr_info("Unknown Command:%u\n", cmd);
return -ENOTTY;
}
return 0;
}
struct file_operations device_fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release,
.llseek = device_lseek,
.unlocked_ioctl = device_ioctl,
.compat_ioctl = device_compat_ioctl
};
MSG_GET_ADDRESS ioctl 接受 unsigned long long,在 32 位进程中为 4 个字节,在 64 位进程中为 8 个字节。这就是我写 compat_ioctl 的原因。
当我从用户空间(32 位进程)调用以下代码时,它会因 compat_ioctl 定义中的未知 ioctl 而失败。
int main(int argc, char *argv[])
{
char buffer[1024];
int fd;
unsigned int length;
int i = 0;
unsigned long addr;
fd = open("/dev/msg", O_RDWR);
if (fd < 0) {
perror("fd failed");
exit(2);
}
printf("Size:%d\n", _IOC_SIZE(MSG_GET_ADDRESS));
printf("cmd:%u\n", MSG_GET_ADDRESS);
ioctl(fd, MSG_GET_ADDRESS, &addr);
perror("ioctl");
getchar();
printf("address:%x\n", addr);
close(fd);
}
我在这里犯了什么错误。
【问题讨论】:
-
unsigned long long始终为 64 位。 -
你应该打电话给
ret = ioctl(); if (ret < 0) perror();。 -
用户程序作为 ioctl 标识符传递的数字似乎与内核中的数字不匹配。我看到你在内核中有
pr_info;当用户程序工作时它的输出是什么? -
是的,32 位/64 位两种情况下的数字都不相同。在这种情况下如何处理。我们需要新建一个ioctl命令代码吗?
标签: linux linux-kernel linux-device-driver ioctl