1
votes

Hi I have written char driver, everything was working fine but when I implement ioctl calls it not working. I am writing code to read and write the device through ioctl calls. here goes the code.

driver code ioctl.c

#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/fs.h> /*this is the file structure, file open read close */
#include<linux/cdev.h> /* this is for character device, makes cdev avilable*/
#include<linux/semaphore.h> /* this is for the semaphore*/
#include<linux/uaccess.h> /*this is for copy_user vice vers*/
#include <linux/errno.h>


#include "cioctl.h" /*my ioctl header file */

int chardev_init(void);
void chardev_exit(void);
static int device_open(struct inode *, struct file *);
static int device_close(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
static loff_t device_lseek(struct file *file, loff_t offset, int orig);
/*had some tough time, in making ioctl calls work. 
int ioctl_funcs(struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg)
has changed to 
long ioctl(struct file *filp,unsigned int cmd, unsigned long arg)
after 2.6.36*/
long device_ioctl(struct file *file, unsigned int ioctl_num , unsigned long  ioctl_parm);

/*new code*/
#define BUFFER_SIZE 1024
static char device_buffer[BUFFER_SIZE];
struct semaphore sem;
struct cdev *mcdev; /*this is the name of my char driver that i will be registering*/
int major_number; /* will store the major number extracted by dev_t*/
int ret; /*used to return values*/
dev_t dev_num; /*will hold the major number that the kernel gives*/

#define DEVICENAME "megharajchard"

/*inode reffers to the actual file on disk*/
static int device_open(struct inode *inode, struct file *filp) {
    if(down_interruptible(&sem) != 0) {
        printk(KERN_ALERT "megharajchard : the device has been opened by some other device, unable to open lock\n");
        return -1;
    }
    //buff_rptr = buff_wptr = device_buffer;
    printk(KERN_INFO "megharajchard : device opened succesfully\n");
    return 0;
}

static ssize_t device_read(struct file *fp, char *buff, size_t length, loff_t *ppos) {
    int maxbytes; /*maximum bytes that can be read from ppos to BUFFER_SIZE*/
    int bytes_to_read; /* gives the number of bytes to read*/
    int bytes_read;/*number of bytes actually read*/
    maxbytes = BUFFER_SIZE - *ppos;
    if(maxbytes > length) 
        bytes_to_read = length;
    else
        bytes_to_read = maxbytes;
    if(bytes_to_read == 0)
        printk(KERN_INFO "megharajchard : Reached the end of the device\n");
    bytes_read = bytes_to_read - copy_to_user(buff, device_buffer + *ppos, bytes_to_read);
    printk(KERN_INFO "megharajchard : device has been read %d\n",bytes_read);
    *ppos += bytes_read;
    printk(KERN_INFO "megharajchard : device has been read\n");
    return bytes_read;
}

static ssize_t device_write(struct file *fp, const char *buff, size_t length, loff_t *ppos) {
    int maxbytes; /*maximum bytes that can be read from ppos to BUFFER_SIZE*/
    int bytes_to_write; /* gives the number of bytes to write*/
    int bytes_writen;/*number of bytes actually writen*/
    maxbytes = BUFFER_SIZE - *ppos;
    if(maxbytes > length) 
        bytes_to_write = length;
    else
        bytes_to_write = maxbytes;
    bytes_writen = bytes_to_write - copy_from_user(device_buffer + *ppos, buff, bytes_to_write);
    printk(KERN_INFO "megharajchard : device has been written %d\n",bytes_writen);
    *ppos += bytes_writen;
    printk(KERN_INFO "megharajchard : device has been written\n");
    return bytes_writen;
}

static loff_t device_lseek(struct file *file, loff_t offset, int orig) {
    loff_t new_pos = 0;
    printk(KERN_INFO "megharajchard : lseek function in work\n");
    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 = BUFFER_SIZE - offset;
            break;
    }
    if(new_pos > BUFFER_SIZE)
        new_pos = BUFFER_SIZE;
    if(new_pos < 0)
        new_pos = 0;
    file->f_pos = new_pos;
    return new_pos;
}

long device_ioctl(struct file* filp ,unsigned int ioctl_num , unsigned long  ioctl_parm) {
    /*dont eve decode wrong command*/
    if(_IOC_TYPE(ioctl_num) != IOCTL_MAGIC_NUMBER) {
        printk(KERN_INFO "megharajchard : ioctl majic number failed\n");
        return -ENOTTY;
    }
    if(_IOC_NR(ioctl_num) != IOCTL_MAGIC_MAX) return -ENOTTY;

    printk(KERN_INFO "megharajchard : ioctl method has been called\n");
    switch(ioctl_num) {
        case IOCTL_READ: 
                device_read(filp,(unsigned char *) ioctl_parm, BUFFER_SIZE, 0);
                    break;
        case IOCTL_WRITE:
                device_write(filp,(unsigned char *) ioctl_parm, BUFFER_SIZE, 0);
                    return -EFAULT;
                return 0;
        case IOCTL_HELLO : 
                printk(KERN_INFO "megharajchard : hello reading through ioctl calls\n");
                break;

    }
    return 0;
}


static int device_close(struct inode *inode, struct file *filp) {
    up(&sem);
    printk(KERN_INFO "megharajchard : device has been closed\n");
    return ret;
}

struct file_operations fops = { /* these are the file operations provided by our driver */
    .owner = THIS_MODULE, /*prevents unloading when operations are in use*/
    .open = device_open,  /*to open the device*/
    .write = device_write, /*to write to the device*/
    .read = device_read, /*to read the device*/
    .release = device_close, /*to close the device*/
    .llseek = device_lseek,/* to see the device*/
    .unlocked_ioctl = device_ioctl,/* to implement ioctl calls*/
};


int chardev_init(void) 
{
    /* we will get the major number dynamically this is recommended please read ldd3*/
    ret = alloc_chrdev_region(&dev_num,0,1,DEVICENAME);
    if(ret < 0) {
        printk(KERN_ALERT " megharajchard : failed to allocate major number\n");
        return ret;
    }
    else
        printk(KERN_INFO " megharajchard : mjor number allocated succesful\n");
    major_number = MAJOR(dev_num);
    printk(KERN_INFO "megharajchard : major number of our device is %d\n",major_number);
    printk(KERN_INFO "megharajchard : to use mknod /dev/%s c %d 0\n",DEVICENAME,major_number);

    mcdev = cdev_alloc(); /*create, allocate and initialize our cdev structure*/
    mcdev->ops = &fops;   /*fops stand for our file operations*/
    mcdev->owner = THIS_MODULE;

    /*we have created and initialized our cdev structure now we need to add it to the kernel*/
    ret = cdev_add(mcdev,dev_num,1);
    if(ret < 0) {
        printk(KERN_ALERT "megharajchard : device adding to the kerknel failed\n");
        return ret;
    }
    else
        printk(KERN_INFO "megharajchard : device additin to the kernel succesful\n");
    sema_init(&sem,1);  /* initial value to one*/

    return 0;
}

void chardev_exit(void)
{
    cdev_del(mcdev); /*removing the structure that we added previously*/
    printk(KERN_INFO " megharajchard : removed the mcdev from kernel\n");

    unregister_chrdev_region(dev_num,1);
    printk(KERN_INFO "megharajchard : unregistered the device numbers\n");
    printk(KERN_ALERT " megharajchard : character driver is exiting\n");
}
//MODULE_LICENCE("GPL");    
MODULE_AUTHOR("A G MEGHARAJ(agmegharaj@gmail.com)");
MODULE_DESCRIPTION("A BASIC CHAR DRIVER");

module_init(chardev_init);
module_exit(chardev_exit);

make file

obj-m   := ioctl.o

KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)

all:
    $(MAKE) -C $(KERNELDIR) M=$(PWD)

clean:
    rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

load script make sure the major number is correct otherwise make corresponding changes.

#!/bin/sh

sudo insmod ioctl.ko
sudo mknod /dev/megharajchard c 250 0
sudo chmod 777 /dev/megharajchard

unload script

#!/bin/sh

sudo rmmod ioctl
sudo rm /dev/megharajchard

ioctl header file cioctl.h

#ifndef IOCTL_H
#define IOCTL_H
#include<linux/ioctl.h>

#define IOCTL_MAGIC_NUMBER 100
#define IOCTL_READ _IOR(IOCTL_MAGIC_NUMBER, 0, char *)
#define IOCTL_WRITE _IOW(IOCTL_MAGIC_NUMBER, 1, char *)
#define IOCTL_HELLO _IO(IOCTL_MAGIC_NUMBER, 2)

#define IOCTL_MAGIC_MAX 2

#endif

a simple c cppliaction to see the operation of ioctl calls

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>      /* open */
#include <unistd.h>     /* exit */
#include <sys/ioctl.h>      /* ioctl */
#include"cioctl.h"
#define DEVICE "/dev/megharajchard"

    int main()
    {
        int file_desc, ret_val;
        char *message = "My name is megharaj i am the owner of this device\n";
        char *answer;
        file_desc = open(DEVICE, O_RDWR);
        if(file_desc < 0)
            printf("failed to open the device \n");

        ret_val = ioctl(file_desc, IOCTL_WRITE, message);
        if(ret_val < 0)
            printf("failed to write the device \n");
        ret_val = ioctl(file_desc, IOCTL_READ,answer);
        if(ret_val < 0)
            printf("failed to write the device \n");
            printf("message read is %s  \n",answer);
        ret_val = ioctl(file_desc, IOCTL_HELLO,answer); /* only this command works*/
        close(file_desc);
        return 0;
    }

only IOCTL_HELLO works other two called to read and write the device buffer are not working

1
IOCTL_READ and IOCTL_WRITE i am calling device read and device write functions, but there is no output. Device opens and closes without reading or writingMegharaj
Why are you using ioctl() to read and write rather than read() and write()?Keith Thompson
I am learning ioctl calls. I know only character driver, so was experimenting like this.Megharaj
Simple approach: put printk logging in the module at the entry of the relevant functions so you can see that they are called and with what arguments, and put logging into the various decision & failure paths so that you can see how they do or do not do what you expect.Chris Stratton

1 Answers

4
votes

This line in device_ioctl():

if(_IOC_NR(ioctl_num) != IOCTL_MAGIC_MAX) return -ENOTTY;

is testing to see if your ioctl magic number is equal to IOCTL_MAGIC_MAX, which happens to be true only for IOCTL_HELLO. But you'd see that yourself if you just added a few more debugging statements to narrow down the exact failure location.

EDIT: One more error in the read call: you are passing an uninitialized pointer answer to the read ioctl. answer needs to point to a valid buffer for the kernel-side copy_to_user().