Thread: Simple Device Driver help

  1. #1
    Registered User
    Join Date
    Feb 2013
    Posts
    6

    Simple Device Driver help

    Hello all, I've visited this site several times but this is my first time posting. I've been assigned to write a basic character device driver, which will use kprint() to output a message after a certain amount of time has elapsed. Not too overwhelming, but as part of the assignment, I am supposed to write a user-space application which can be called from the command line. Depending on the options passed with the call, the user program will either print a list of all of the current 'timers' that have been created (indexed by the message they are going to print, followed by the amount of seconds until expiration). The other option is to pass a string followed by an integer, which will create a new timer to print the string after the inputted amount of seconds.
    I have been reading/leafing through Linux Device Drivers 3, and hunting around the resources on the internet, but most of what I've come across is either too basic or way over my head. Here is what I have so far, but this does very little and is basically a sloppy attempt at merging a timer module and a simple buffer module which can store chars.

    Code:
    /* Necessary includes for device drivers */
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kernel.h> /* printk() */
    #include <linux/slab.h> /* kmalloc() */
    #include <linux/fs.h> /* everything... */
    #include <linux/errno.h> /* error codes */
    #include <linux/types.h> /* size_t */
    #include <linux/proc_fs.h>
    #include <linux/fcntl.h> /* O_ACCMODE */
    #include <asm/system.h> /* cli(), *_flags */
    #include <asm/uaccess.h> /* copy_from/to_user */
    
    
    MODULE_LICENSE("GPL");
    
    
    /* Declaration of memory.c functions */
    static int mytimer_open(struct inode *inode, struct file *filp);
    static int mytimer_release(struct inode *inode, struct file *filp);
    static ssize_t mytimer_read(struct file *filp,
                    char *buf, size_t count, loff_t *f_pos);
    static ssize_t mytimer_write(struct file *filp,
                    const char *buf, size_t count, loff_t *f_pos);
    static void mytimer_exit(void);
    static int mytimer_init(void);
    
    
    /* Structure that declares the usual file */
    /* access functions */
    struct file_operations mytimer_fops = {
            read: mytimer_read,
            write: mytimer_write,
            open: mytimer_open,
            release: mytimer_release
    };
    
    
    /* Declaration of the init and exit functions */
    module_init(mytimer_init);
    module_exit(mytimer_exit);
    
    
    static unsigned capacity = 128;
    static unsigned bite = 128;
    
    
    /* Global variables of the driver */
    /* Major number */
    static int mytimer_major = 61;
    
    
    /* Buffer to store data */
    static char *mytimer_buffer;
    /* length of the current message */
    static int mytimer_len;
    
    
    static int mytimer_init(void)
    {
            int result;
    
    
            /* Registering device */
            result = register_chrdev(mytimer_major, "mytimer", &mytimer_fops);
            if (result < 0)
            {
                    printk(KERN_ALERT
                            "mytimer: cannot obtain major number %d\n", mytimer_major);
                    return result;
            }
    
    
            /* Allocating mytimer for the buffer */
            mytimer_buffer = kmalloc(capacity, GFP_KERNEL);
            if (!mytimer_buffer)
            {
                    printk(KERN_ALERT "Insufficient kernel memory\n");
                    result = -ENOMEM;
                    goto fail;
            }
            memset(mytimer_buffer, 0, capacity);
            mytimer_len = 0;
    
    
            printk(KERN_ALERT "Inserting mytimer module\n");
            return 0;
    
    
    fail:
            mytimer_exit();
            return result;
    }
    
    
    static void mytimer_exit(void)
    {
            /* Freeing the major number */
            unregister_chrdev(mytimer_major, "mytimer");
    
    
            /* Freeing buffer memory */
            if (nibbmytimerler_buffer)
            {
                    kfree(mytimer_buffer);
            }
    
    
            printk(KERN_ALERT "Removing mytimer module\n");
    
    
    }
    
    
    static int mytimer_open(struct inode *inode, struct file *filp)
    {
            printk(KERN_INFO "open called: process id %d, command %s\n",
                    current->pid, current->comm);
            /* Success */
            return 0;
    }
    
    
    static int mytimer_release(struct inode *inode, struct file *filp)
    {
            printk(KERN_INFO "release called: process id %d, command %s\n",
                    current->pid, current->comm);
            /* Success */
            return 0;
    }
    
    
    static ssize_t mytimer_read(struct file *filp, char *buf,
                                                            size_t count, loff_t *f_pos)
    {
            int temp;
            char tbuf[256], *tbptr = tbuf;
    
    
            /* end of buffer reached */
            if (*f_pos >= mytimer_len)
            {
                    return 0;
            }
    
    
            /* do not go over then end */
            if (count > mytimer_len - *f_pos)
                    count = mytimer_len - *f_pos;
    
    
            /* do not send back more than a bite */
            if (count > bite) count = bite;
    
    
            /* Transfering data to user space */
            if (copy_to_user(buf, mytimer_buffer + *f_pos, count))
            {
                    return -EFAULT;
            }
    
    
            tbptr += sprintf(tbptr,                                                 
                    "read called: process id %d, command %s, count %d, chars ",
                    current->pid, current->comm, count);
    
    
            for (temp = *f_pos; temp < count + *f_pos; temp++)                      
                    tbptr += sprintf(tbptr, "%c", mytimer_buffer[temp]);
    
    
            printk(KERN_INFO "%s\n", tbuf);
    
    
            /* Changing reading position as best suits */
            *f_pos += count;
            return count;
    }
    
    
    static ssize_t mytimer_write(struct file *filp, const char *buf,
                                                            size_t count, loff_t *f_pos)
    {
            int temp;
            char tbuf[256], *tbptr = tbuf;
    
    
            /* end of buffer reached */
            if (*f_pos >= capacity)
            {
                    printk(KERN_INFO
                            "write called: process id %d, command %s, count %d, buffer full\n",
                            current->pid, current->comm, count);
                    return -ENOSPC;
            }
    
    
            /* do not eat more than a bite */
            if (count > bite) count = bite;
    
    
            /* do not go over the end */
            if (count > capacity - *f_pos)
                    count = capacity - *f_pos;
    
    
            if (copy_from_user(mytimer_buffer + *f_pos, buf, count))
            {
                    return -EFAULT;
            }
    
    
            tbptr += sprintf(tbptr,                                                 
                    "write called: process id %d, command %s, count %d, chars ",
                    current->pid, current->comm, count);
    
    
            for (temp = *f_pos; temp < count + *f_pos; temp++)                      
                    tbptr += sprintf(tbptr, "%c", mytimer_buffer[temp]);
    
    
            printk(KERN_INFO "%s\n", tbuf);
    
    
            *f_pos += count;
            mytimer_len = *f_pos;
    
    
            return count;
    }
    Any advice/ help would be really appreciated. I don't have any code started for the user application, but I figured that would be far simpler as it would only require echoing to/ catting from /dev/mytimer and printing out some strings to the user. I have more experience in C++ than in C, and I'm currently feeling like I dove into the deep end of linux programming without properly wetting my feet.

  2. #2
    Registered User
    Join Date
    Jun 2005
    Posts
    6,815
    Suggest mods move this to the Linux programming forum. The substance of this question has nothing to do with C or C++.
    Right 98% of the time, and don't care about the other 3%.

    If I seem grumpy or unhelpful in reply to you, or tell you you need to demonstrate more effort before you can expect help, it is likely you deserve it. Suck it up, Buttercup, and read this, this, and this before posting again.

  3. #3
    Registered User
    Join Date
    Feb 2013
    Posts
    6
    Sorry, wasn't sure where to put it especially because the user-space portion is entirely C, whereas the code I posted is more related to linux

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    Your user space program would have these elements.
    Code:
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    fd = open("/dev/mytimer",O_RDWR);
    
    char msg[] = "hello";
    ssize_t n = write( fd, msg, sizeof(msg) );
    
    char ans[100];
    ssize_t n = read( fd, ans, sizeof(ans) );
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Registered User
    Join Date
    Feb 2013
    Posts
    6
    Thank you for the help Salem. For my purposes, when writing to the device, I would need to pass a time value (lets say an int) as well as the message to print (the array of chars). I know how I could pass these both to the user space program through the command line, but I'm wondering how I could write() two variables (especially of different type) to the device? Forgive me if there is a straightforward way of achieving this; the task seems daunting to me without writing some hacky and presumptuous code for the kernel mod.
    Thanks again!

  6. #6
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    You can write anything you want, so long as both the userspace and driver both agree on what the protocol is supposed to be.

    Eg
    Code:
    char buff[100];
    sprintf(buff,"Time=%d,message=%s", 1234, "hello" );
    write( fd, buff, strlen(buff)+1 );
    The driver parses out Time= and message=

    Or for more compactness, you can represent Time and message as opcodes, like say
    sprintf(buff,"1=%d,2=%s", 1234, "hello" );
    More compact, but harder to write the user manual for it perhaps.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Virtual Device Driver
    By xx3nvyxx in forum Windows Programming
    Replies: 8
    Last Post: 08-22-2006, 11:01 PM
  2. device driver programming....how does it work
    By the bassinvader in forum C Programming
    Replies: 4
    Last Post: 07-09-2006, 03:31 AM
  3. Replies: 4
    Last Post: 06-30-2004, 03:11 PM
  4. writing device driver
    By ronin in forum A Brief History of Cprogramming.com
    Replies: 7
    Last Post: 07-08-2002, 10:48 AM
  5. Printing to Printer via Device Driver
    By JamMan in forum C++ Programming
    Replies: 1
    Last Post: 11-19-2001, 05:20 PM

Tags for this Thread