Один ioctl не выполняется в модуле ядра - PullRequest
0 голосов
/ 23 апреля 2020

Я написал модуль ядра, демонстрирующий, как работает 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("%s\n", __func__);
    file->f_pos = 0;
    buffer_index = 0;
    return 0;
}

static int device_release(struct inode *inode, struct file *file)
{
    pr_info("%s\n", __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:%lld\n", __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:%d\n", bytes_to_read);

    if (bytes_to_read == 0) {
        pr_err("%s: No available space in the buffer for reading\n",
                __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:%d\n", __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:%lld\n", __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 writing\n",
                __func__);
        return -ENOSPC;
    }

    bytes_written = bytes_to_write - copy_from_user(kernel_buffer+*offset,  user_buffer, bytes_to_write);
    pr_info("%s: Bytes written:%d\n", __func__, bytes_written);
    pr_info("%s: kernel_buffer:%s\n", __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:%u\t Arg:%lu\n", __func__, cmd, arg);

    switch(cmd)
    {
        //Get Length of buffer
        case 0x01:
            pr_info("Get Buffer Length\n");
            put_user(MAX_SIZE, (unsigned int *)arg);
            break;
        //clear buffer
        case 0x02:
            pr_info("Clear buffer\n");
            memset(kernel_buffer, 0, sizeof(kernel_buffer));
            break;
        //fill character
        case 0x03:
            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;
        default:
            pr_info("Unknown Command:%u\n", 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 registered\n");
        printk("Major number received:%d\n", 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 Failed\n");

    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:%u\n", 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:%d\n", length);
    buffer[1023] = '\0';
    printf("Buffer:%s\n", buffer);
    close(fd);
}

Команды ioctl 1, 3 работают, но не 2. Может пожалуйста, укажите в чем ошибка в коде

1 Ответ

0 голосов
/ 23 апреля 2020

Вы должны ознакомиться с требованиями для 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>.
...