Find the answer to your Linux question:
Results 1 to 1 of 1
I've been trying to port a module I wrote for FreeBSD over to Linux. Right now I'm running Ubuntu. The output of uname -a follows: Linux crashbuntu 2.6.32-21-server #32-Ubuntu SMP ...
Enjoy an ad free experience by logging in. Not a member yet? Register.
  1. #1
    Just Joined!
    Join Date
    Aug 2010
    Posts
    1

    ioctl failing on character device (custom module)


    I've been trying to port a module I wrote for FreeBSD over to Linux. Right now I'm running Ubuntu. The output of uname -a follows:
    Linux crashbuntu 2.6.32-21-server #32-Ubuntu SMP Fri Apr 16 09:17:34 UTC 2010 x86_64 GNU/Linux

    The module creates a character device at /dev/string

    The device can be read, producing a string.
    One can write to the device, replacing the stored string.
    This much works.

    In addition to the above, one should be able to send an ioctl command to /dev/string that toggles whether or not its stored string should be displayed in upper-case when it is read. This part works in the FreeBSD version of my module. However, in my linux version (attached), whenever I call the ioctl system call in my userland program, it returns EINVAL. I've traced through the system call code in the kernel, and for the life of me I can't figure out why this happens. I've reassigned the ioctl number for the command, as well as the ioctl type for the driver, to no effect. In fact, the ioctl function in the file_operations structure for my character device IS NEVER ENTERED AT ALL.

    Could anyone explain why I am experiencing this behavior? I've pored through kernel code and documentation for hours, but I can't figure it out. Any help would be greatly appreciated.

    ATTACHED please find the tarball of the code for my kernel module, as well as the userland test program.

    EDIT: I am aware that the code for the string_ioctl function is not written correctly. However please do not harp on this; at the beginning of the function a message should be written using printk, but this never happens. I just need to know why the function is not being entered.
    Also, for convenience, I've entered the code below:

    string_module.c
    Code:
    #include <linux/module.h>
    #include <linux/moduleparam.h>
    #include <linux/init.h>
    
    #include <linux/kernel.h> /* printk() */
    #include <linux/fs.h>     /* everything... */
    #include <linux/errno.h>  /* error codes */
    #include <linux/types.h>  /* size_t */
    #include <linux/device.h>
    #include <linux/ctype.h>
    
    #include <asm/uaccess.h>
    
    #include "string_module.h"
    
    MODULE_LICENSE("Dual BSD/GPL");
    
    static char *initial_string = "Hello World!\n";
    static int major_num = MAJOR_NUM; 
    
    
    static struct string_device {
       unsigned long size;
       struct mutex mutex;
       u8 *data;
       int upper_mode;
       struct device *this_device;
    } Device;
    
    static struct class *string_class;
    
    static ssize_t string_read(struct file *fp, char *buffer,
          size_t nbytes, loff_t *offset) {
       int bytes_read;
       char current_byte;
    
       printk("string_read called\n");
       
       if (*offset >= Device.size) {
          return 0;
       }
    
       bytes_read = 0;
       while (bytes_read < nbytes && bytes_read < Device.size)  {
    
          current_byte = Device.data[bytes_read];
          if (Device.upper_mode) {
             current_byte = toupper(current_byte);
          }
          
          put_user(current_byte, buffer++);
          bytes_read++;
          (*offset)++;
       }
    
       return bytes_read;
    }
    
    static ssize_t string_write(struct file *fp, const char *buffer,
          size_t nbytes, loff_t *offset) {
       int bytes_written;
       
       printk("string_write called\n");
       
       bytes_written = 0;
       if (nbytes == 0) {
          return 0;
       }
       Device.data = krealloc(Device.data, nbytes, GFP_KERNEL);
       if (Device.data == NULL) {
          Device.size = 0;
          return -ENOMEM;
       }
       Device.size = nbytes;
       while (bytes_written < nbytes) {
          Device.data[bytes_written] = buffer[bytes_written];
          bytes_written++;
          (*offset)++;
       }
       return bytes_written;
    }
    
    static int string_open(struct inode *ip, struct file *fp) {
       printk("string_open called\n");
       if (!mutex_trylock(&Device.mutex)) {
          printk("string_module: string_open: module in use\n");
          return -EBUSY;
       }
       try_module_get(THIS_MODULE);
       return 0;
    }
    
    static int string_ioctl(struct inode *ip, struct file *fp, unsigned int ioctl_num,
          unsigned long ioctl_param) {
       printk("string_ioctl called\n");
    
       switch(ioctl_num) {
          case 0:
             Device.upper_mode = !(Device.upper_mode);
             return 0;
          default:
             return -ENOTTY;
       }
    }
    
    static int string_release(struct inode *ip, struct file *fp) {
       printk("string_release called\n");
       mutex_unlock(&Device.mutex);
       module_put(THIS_MODULE);
       return 0;
    }
    
    static struct file_operations string_ops = {
          .owner   = THIS_MODULE,
          .read    = string_read,
          .write   = string_write,
          .open    = string_open,
          .ioctl   = string_ioctl,
          .release = string_release
    };
    
    static int __init string_init(void) {
       dev_t device;
       int error;
       int ret_val;
    
       /*
        * Set up class
        */
       /* TODO: set up error checking */
       string_class = class_create(THIS_MODULE, "stringclass");
    
       /*
        * Set up our internal device.
        */
       Device.upper_mode = 0;
       Device.size = strlen(initial_string) + 1;
       mutex_init(&Device.mutex);
       Device.data = kmalloc(Device.size, GFP_KERNEL);
       if (Device.data == NULL) {
          error = -ENOMEM;
          goto allocate_data_out;
       }
       strcpy(Device.data, initial_string);
       /*
        * Get registered.
        */
       ret_val = register_chrdev(major_num, "string", &string_ops);
       if (ret_val < 0) {
          printk(KERN_WARNING "string: unable to get major number\n");
          error = major_num;
          goto get_registered_out;
       }
       printk("string_module: major_num = %d\n", major_num);
       /*
        * Create device node
        * TODO: add error checking
        */
       device = MKDEV(major_num, 0);
       Device.this_device = device_create(string_class, NULL, device, "%s",
          "string");
       
       return 0;
    
    get_registered_out:
       kfree(Device.data);
    
    allocate_data_out:
       mutex_destroy(&Device.mutex);
       return error;
    
    }
    
    static void __exit string_exit(void)
    {
       device_destroy(string_class, MKDEV(major_num, 0));
       unregister_chrdev(major_num, "string");
       class_destroy(string_class);
       kfree(Device.data);
       mutex_destroy(&Device.mutex);
    }
    
    module_init(string_init);
    module_exit(string_exit);
    string_module.h
    Code:
    #ifndef _STRING_IOCTL_H_
    #define _STIRNG_IOCTL_H_
    
    #define MAJOR_NUM 100
    
    #define TOGGLE_UPPER_CMD _IO(MAJOR_NUM, 0)
    
    
    #endif
    toggle_upper.c
    Code:
    #include <sys/ioctl.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <limits.h>
    #include <stdlib.h>
    
    #include "module/string_module.h"
    
    int main() {
       int error;
       int fd;
      
       if ((fd = open("/dev/string", O_NONBLOCK | O_RDONLY | O_WRONLY) == -1)) {
          perror("toggle_upper: open");
          return 1;
       }
       if ((error = ioctl(fd, TOGGLE_UPPER_CMD)) == -1) {
          perror("toggle_upper: ioctl");
          return 1;
       }
       if (close(fd) == -1) {
          perror("toggle_upper: close");
          return 1;
       }
       return 0;
    }
    Attached Files Attached Files
    Last edited by datanasty; 08-16-2010 at 09:24 AM. Reason: comment

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •