Question

I have two questions as I'm trying device drivers as a beginner.

  1. I created one module , loaded it, it dynamically took major number 251 say. Number of minor devices is kept 1 only i.e minor number 0. For testing , I tried echo and cat on the device file (created using mknod) and it works as expected. Now if I unload the module but don't remove /dev entry and again load the module with same major number and try writing/reading to same device file which was used previously, kernel crashes. I know we shouldn't do this but just want to understand what happens in this scenario which causes this crash. I think something that VFS does.

  2. When I do cat on device file, the read keeps on happening indefinitely. why? To stop that needed to use offset manipulation. This looks to be because buffer length is coming as 32768 as default to read?

EDIT: further in this I added one ioctl function as below, then I'm getting error regarding the storage class of init and cleanup function, which work well if no ioctl is defined. Not getting the link between ioctl and the init/cleanup functions' storage class. Updated code is posted. Errors are below:

    /home/diwakar/Documents/my_modules/first_test_module/flowTest.c:95:12: error: invalid storage class for function ‘flow_init’
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c: In function ‘flow_init’:
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:98:2: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c: In function ‘flow_ioctl’:
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:112:13: error: invalid storage class for function ‘flow_terminate’
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:119:1: error: invalid storage class for function ‘__inittest’
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:119:1: warning: ‘alias’ attribute ignored [-Wattributes]
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:120:1: error: invalid storage class for function ‘__exittest’
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:120:1: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:120:1: warning: ‘alias’ attribute ignored [-Wattributes]
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:120:1: error: expected declaration or statement at end of input
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c: At top level:
/home/diwakar/Documents/my_modules/first_test_module/flowTest.c:73:13: warning: ‘flow_ioctl’ defined but not used [-Wunused-function]

Below is the code:

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>
#include <linux/kdev_t.h>
#include <linux/errno.h>
#include <linux/ioctl.h>

#define SUCCESS 0
#define BUF_LEN 80

#define FLOWTEST_MAGIC 'f'
#define FLOW_QUERY _IOR(FLOWTEST_MAGIC,1,int)

MODULE_LICENSE("GPL");
int minor_num=0,i;
int num_devices=1;
int fopen=0,counter=0,ioctl_test;

static struct cdev ms_flow_cd;
static char c;

///// Open , close and rest of the things

static int flow_open(struct inode *f_inode, struct file *f_file)
{
printk(KERN_ALERT "flowtest device: OPEN\n");
return SUCCESS;
}

static ssize_t flow_read(struct file *f_file, char __user *buf, size_t
  len, loff_t *off)
{
  printk(KERN_INFO "flowtest Driver: READ()\nlength len=%d, Offset = %d\n",len,*off);

/* Check to avoid the infinitely printing on screen. Return 1 on first read, and 0 on subsequent read */
if(*off==1)
return 0;

printk(KERN_INFO "Copying...\n");
copy_to_user(buf,&c,1);
printk(KERN_INFO "Copied : %s\n",buf);

*off = *off+1;
return 1;       // Return 1 on first read 


}

 static ssize_t flow_write(struct file *f_file, const char __user *buf,
  size_t len, loff_t *off)
{
  printk(KERN_INFO "flowtest Driver: WRITE()\n");
 if (copy_from_user(&c,buf+len-2,1) != 0)
  return -EFAULT;
 else
 {
 printk(KERN_INFO "Length len = %d\n\nLast character written  is  - %c\n",len,*(buf+len-2));
 return len;
}
}

static int flow_close(struct inode *i, struct file *f)
{
  printk(KERN_INFO "ms_tty Device: CLOSE()\n");
  return 0;
}

///* ioctl commands *///

static long flow_ioctl (struct file *filp,unsigned int cmd, unsigned long arg)
{
    switch(cmd) {
        case FLOW_QUERY:
            ioctl_test=51;
            return ioctl_test;
        default: 
            return -ENOTTY; 
} 
///////////////////File operations structure below/////////////////////////

struct file_operations flow_fops = {
         .owner =    THIS_MODULE,
         .llseek =   NULL,
         .read =     flow_read,
         .write =    flow_write,
         .unlocked_ioctl =   flow_ioctl,
         .open =     flow_open,
         .release =  flow_close
 };


static int flow_init(void)
    {
    printk(KERN_ALERT "Here with flowTest module ... loading...\n");
 int result=0;
 dev_t dev=0;
result = alloc_chrdev_region(&dev, minor_num, 
num_devices,"mod_flowtest");                              // allocate major number dynamically.

i=MAJOR(dev);
printk(KERN_ALERT "Major allocated = %d",i);

cdev_init(&ms_flow_cd,&flow_fops);
cdev_add(&ms_flow_cd,dev,1);

return 0;
    }

static void flow_terminate(void)
    {
    dev_t devno=MKDEV(i,0);         // wrap major/minor numbers in a dev_t structure , to pass for deassigning.
    printk(KERN_ALERT "Going out... exiting...\n");
    unregister_chrdev_region(devno,num_devices);        //remove entry from the /proc/devices
    }

module_init(flow_init);
module_exit(flow_terminate);
Was it helpful?

Solution

1- You're missing cdev_del() in your cleanup function. Which means the device stays registered, but the functions to handle it are unloaded, thus the crash. Also, cdev_add probably fails on the next load, but you don't know because you're not checking return values.

2- It looks ok... you modify offset, return the correct number of bytes, and then return 0 if offset is 1, which indicates EOF. But you should really check for *off >= 1.

EDIT- The length passed into your read handler function comes all the way from user-land read(). If the user opens the device file and calls read(fd, buf, 32768);, that just means the user wants to read up to 32768 bytes of data. That length gets passed all the way to your read handler. If you don't have 32768 bytes of data to supply, you supply what you have, and return the length. Now, the user code isn't sure if that's the end of the file or not, so it tries for another 32768 read. You really have no data now, so you return 0, which tells the user code that it has hit EOF, so it stops.

In summary, what you're seeing as some sort of default value at the read handler is just the block size that the utility cat uses to read anything. If you want to see a different number show up at your read function, try using dd instead, since it lets you specify the block size.

dd if=/dev/flowtest of=/dev/null bs=512 count=1

In addition, this should read one block and stop, since you're specifying count=1. If you omit count=1, it will look more like cat, and try to read until EOF.

OTHER TIPS

For 2, make sure you start your module as a char device when using mknod.

    mknod /dev/you_device c major_number minor_number
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top