我写了一个内核模块来演示ioctl是如何工作的。
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
int base_minor = 0;
char *device_name = "msg";
int count = 1;
dev_t devicenumber;
static struct class *class = NULL;
static struct device *device = NULL;
static struct cdev mycdev;
#define MAX_SIZE 1024
char kernel_buffer[MAX_SIZE];
int buffer_index;
MODULE_LICENSE("GPL");
static int device_open(struct inode *inode, struct file *file)
{
pr_info("%sn", __func__);
file->f_pos = 0;
buffer_index = 0;
return 0;
}
static int device_release(struct inode *inode, struct file *file)
{
pr_info("%sn", __func__);
return 0;
}
static ssize_t device_read(struct file *file, char __user *user_buffer,
size_t read_count, loff_t *offset)
{
int bytes_read;
int available_space;
int bytes_to_read;
pr_info("%s read offset:%lldn", __func__, *offset);
available_space = MAX_SIZE - *(offset);
if (read_count < available_space)
bytes_to_read = read_count;
else
bytes_to_read = available_space;
pr_info("bytes_to_read:%dn", bytes_to_read);
if (bytes_to_read == 0) {
pr_err("%s: No available space in the buffer for readingn",
__func__);
return -ENOSPC;
}
if (buffer_index > *offset)
bytes_to_read = buffer_index - *offset;
else
return 0;
bytes_read = bytes_to_read - copy_to_user(user_buffer, kernel_buffer+*offset, bytes_to_read);
pr_info("%s: Copy to user returned:%dn", __func__, bytes_to_read);
//update file offset
*offset += bytes_read;
return bytes_read;
}
static ssize_t device_write(struct file *file, const char __user *user_buffer,
size_t write_count, loff_t *offset)
{
int bytes_written;
int available_space;
int bytes_to_write;
pr_info("%s write offset:%lldn", __func__, *offset);
available_space = MAX_SIZE - *(offset);
if (write_count < available_space)
bytes_to_write = write_count;
else
bytes_to_write = available_space;
if (bytes_to_write == 0) {
pr_err("%s: No available space in the buffer for writingn",
__func__);
return -ENOSPC;
}
bytes_written = bytes_to_write - copy_from_user(kernel_buffer+*offset, user_buffer, bytes_to_write);
pr_info("%s: Bytes written:%dn", __func__, bytes_written);
pr_info("%s: kernel_buffer:%sn", __func__, kernel_buffer);
//update file offset
*offset += bytes_written;
buffer_index += bytes_written;
return bytes_written;
}
static loff_t device_lseek(struct file *file, loff_t offset, int orig)
{
loff_t new_pos = 0;
switch(orig) {
case 0 : /*seek set*/
new_pos = offset;
break;
case 1 : /*seek cur*/
new_pos = file->f_pos + offset;
break;
case 2 : /*seek end*/
new_pos = MAX_SIZE - offset;
break;
}
if(new_pos > MAX_SIZE)
new_pos = MAX_SIZE;
if(new_pos < 0)
new_pos = 0;
file->f_pos = new_pos;
return new_pos;
}
long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
unsigned char ch;
pr_info("%s: Cmd:%ut Arg:%lun", __func__, cmd, arg);
switch(cmd)
{
//Get Length of buffer
case 0x01:
pr_info("Get Buffer Lengthn");
put_user(MAX_SIZE, (unsigned int *)arg);
break;
//clear buffer
case 0x02:
pr_info("Clear buffern");
memset(kernel_buffer, 0, sizeof(kernel_buffer));
break;
//fill character
case 0x03:
get_user(ch, (unsigned char *)arg);
pr_info("Fill Character:%cn", ch);
memset(kernel_buffer, ch, sizeof(kernel_buffer));
buffer_index = sizeof(kernel_buffer);
break;
default:
pr_info("Unknown Command:%un", cmd);
return -EINVAL;
}
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
};
static int test_hello_init(void)
{
class = class_create(THIS_MODULE, "myclass");
if (!alloc_chrdev_region(&devicenumber, base_minor, count, device_name)) {
printk("Device number registeredn");
printk("Major number received:%dn", MAJOR(devicenumber));
device = device_create(class, NULL, devicenumber, NULL, device_name);
cdev_init(&mycdev, &device_fops);
mycdev.owner = THIS_MODULE;
cdev_add(&mycdev, devicenumber, count);
}
else
printk("Device number registration Failedn");
return 0;
}
static void test_hello_exit(void)
{
device_destroy(class, devicenumber);
class_destroy(class);
cdev_del(&mycdev);
unregister_chrdev_region(devicenumber, count);
}
module_init(test_hello_init);
module_exit(test_hello_exit);
然后我写了一个用户空间代码
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h>
int main(int argc, char *argv[])
{
char buffer[1024];
int fd;
unsigned int length;
unsigned char ch = 'A';
int i = 0;
fd = open("/dev/msg", O_RDWR);
if (fd < 0) {
perror("fd failed");
exit(2);
}
//Get Length - 0x01
ioctl(fd, 0x01, &length);
printf("Length:%un", length);
ioctl(fd, 0x02);
//Set Character - 0x03
ioctl(fd, 0x03, &ch);
perror("ioctl");
lseek(fd, 0, SEEK_SET);
perror("lseek");
length = read(fd, buffer, 1024);
perror("Read");
printf("length:%dn", length);
buffer[1023] = ' ';
printf("Buffer:%sn", buffer);
close(fd);
}
ioctl命令1、3可以工作,但不能工作2。你能提供代码中的错误吗
您应该在手册页上查看ioctl
的要求:
DESCRIPTION
The ioctl() system call manipulates the underlying device parameters of
special files. In particular, many operating characteristics of char‐
acter special files (e.g., terminals) may be controlled with ioctl()
requests. The argument fd must be an open file descriptor.
The second argument is a device-dependent request code. The third
argument is an untyped pointer to memory. It's traditionally char
*argp (from the days before void * was valid C), and will be so named
for this discussion.
An ioctl() request has encoded in it whether the argument is an in
parameter or out parameter, and the size of the argument argp in bytes.
Macros and defines used in specifying an ioctl() request are located in
the file <sys/ioctl.h>.