KernelNewbies
  • Comments
  • Immutable Page
  • Menu
    • Navigation
    • RecentChanges
    • FindPage
    • Local Site Map
    • Help
    • HelpContents
    • HelpOnMoinWikiSyntax
    • Display
    • Attachments
    • Info
    • Raw Text
    • Print View
    • Edit
    • Load
    • Save
  • Login

Kernel Hacking

  • Frontpage

  • Kernel Hacking

  • Kernel Documentation

  • Kernel Glossary

  • FAQ

  • Found a bug?

  • Kernel Changelog

  • Upstream Merge Guide

Projects

  • KernelJanitors

  • KernelMentors

  • KernelProjects

Community

  • Why a community?

  • Regional Kernelnewbies

  • Personal Pages

  • Upcoming Events

References

  • Mailing Lists

  • Related Sites

  • Programming Links

Wiki

  • Recent Changes

  • Site Editors

  • Side Bar

  • Tips for Editors

  • Hosted by WikiWall

Navigation

  • RecentChanges
  • FindPage
  • HelpContents

Upload page content

You can upload content for the page named below. If you change the page name, you can also upload content for another page. If the page name is empty, we derive the page name from the file name.

File to load page content from
Page name
Comment

Revision 4 as of 2005-09-28 16:56:54
KernelNewbies:
  • HelloDriverSource
/*
 * tyler@agat.net
 *
 * Hello World Driver (intended to Linux Kernel 2.6(.12))
 *
 * Driver for the hello device. This driver is intended to show
 * and teach some basic kernel mechanics :
 * _ Linux Kernel Module programming
 * _ Driver programming
 * _ Debugfs operations
 * _ Semaphores Locking
 * _ Wait queues
 *
 * All that in a simple module. This module isn't very useful and I don't
 * know if someone will be interested.
 * However, to test it, you have to :
 * _ compile the module (I don't provide the Makefile, do it yourself :p)
 * _ load it
 * _ at the module loading, you will see a message : 
 *    "Major number X has been assigned to device hello"
 *    If you don't see it, have a look in the file /var/log/messages
 *    You now have to create the device node :
 *    mknod /dev/hello c X 0
 * _ you can now test the device driver and the hello device !:)   
 *
 * How to use the hello device ?
 * Once you've loaded the module, there is "Nothing" in the device. If you try to read
 * it, the process will block.
 * It will block until another process write some data in the device.
 * You can see the device's data by reading the device or by the debugfs file system.
 * You can write to the data by writing to the device or by the debugfs interface.
 * To reset the device, write "Nothing\n" in the device.
 *
 * I provide two userland programm to read and write to the hello device :
 * _ read_to_hello (which can only read 256 bytes)
 * _ write_to_hello
 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/errno.h>
#include <linux/debugfs.h>

#define DRIVER_AUTHOR "tyler@agat.net"
#define DRIVER_DESC "Hello World Driver"
#define DEVICE_NAME "hello"

#define NOTH_LEN 9

static int major;
static int count_user;
static char *hello_msg; 
static struct semaphore hello_sem;
static wait_queue_head_t attente;
static wait_queue_head_t inq;
static struct dentry *hello_dir;
static struct dentry *hello_dentry;

static int hello_open(struct inode *inode, struct file *file)
{
        down(&hello_sem);
        count_user++;
        up(&hello_sem);

        return 0;
}       

static int hello_release(struct inode *inode, struct file *file)
{
        down(&hello_sem);
        count_user--;
        up(&hello_sem);

        return 0;
}       

static ssize_t hello_read(struct file *filp, char __user *user_buf, size_t len, loff_t *offset)
{
        int bytes = 0;

        down(&hello_sem);
        while (strncmp(hello_msg, "Nothing\n", NOTH_LEN) == 0) {
                up(&hello_sem);
                printk(KERN_INFO "Process sleeps.\n");
                interruptible_sleep_on(&attente);
                printk(KERN_INFO "Process wakes up.\n");
                down(&hello_sem);
        }       
        bytes = copy_to_user(user_buf, hello_msg, len);
        if (bytes < 0) {
                up(&hello_sem);
                return -EFAULT;
        }
        up(&hello_sem);

        return bytes;
}       

static ssize_t hello_write(struct file *filp, const char __user *user_buf, size_t len, loff_t *offset)
{
        int bytes = 0;

        down(&hello_sem);
        if (hello_msg) {
                kfree(hello_msg);
                hello_msg = kmalloc(len+1, GFP_KERNEL);
                if (!hello_msg) {
                        up(&hello_sem);
                        return -ENOMEM;
                }
        }        
        
      bytes = copy_from_user(hello_msg, user_buf, len);
        if (bytes < 0) {
                up(&hello_sem);
                return -EFAULT;
        }
        hello_msg[len] = '\0';
        up(&hello_sem);
        wake_up_interruptible(&attente);

        return bytes;
}

static unsigned int hello_poll(struct file *filp, poll_table *wait) 
{
        unsigned int mask = 0;

        poll_wait(filp, &inq, wait);
        if (strncmp(hello_msg, "Nothing\n", NOTH_LEN) != 0)
                mask |= POLLIN | POLLRDNORM;
        mask |= POLLOUT | POLLWRNORM;

        return mask;
}       

static loff_t hello_llseek(struct file *filp, loff_t off, int origin)  
{
        switch (origin) {
                default:
                        off += filp->f_pos;
                        break;
        }

        if (off < 0) {
                return -EINVAL;
        }

        filp->f_pos = off;

        return filp->f_pos;
}       

static int debug_hello_open(struct inode *inode, struct file *file)
{
        return 0;
}

static int debug_hello_release(struct inode *inode, struct file *file)
{
        return 0;
}

static ssize_t debug_hello_read(struct file *file, char __user *user_buf, size_t count, loff_t *off)
{
        int len;

        down(&hello_sem);
        len = copy_to_user(user_buf,hello_msg,count);
        up(&hello_sem);

        if (len < 0) {
                return -EFAULT;
        }

        return len;
}

static struct file_operations fops = {
        read:   hello_read,
        write:  hello_write,
        open:   hello_open,
        release: hello_release,
        poll:   hello_poll,     
        llseek: hello_llseek,
};               

static struct file_operations debug_fops = {
        read:   debug_hello_read,
        open:   debug_hello_open,
        release: debug_hello_release,
};

int __init loading(void)
{      
        hello_msg = kmalloc(NOTH_LEN, GFP_KERNEL);
        if (!hello_msg) {
                return -ENOMEM;
        }
       
        sprintf(hello_msg, "Nothing\n");
       
        
        major = register_chrdev(major, DEVICE_NAME, &fops);

        if (major < 0) {
                printk(KERN_ERR "Cannot allocate a major number.\n");
                return major;
        }       

        printk(KERN_INFO "Major number %d has been assigned to device %s.\n", major, DEVICE_NAME);

        sema_init(&hello_sem, 1);
        init_waitqueue_head(&attente);
        init_waitqueue_head(&inq);

        hello_dir = debugfs_create_dir("hello_dir", NULL);
        hello_dentry = debugfs_create_file("hello", 0644, hello_dir, NULL, &debug_fops);

        return 0; 
}

void __exit unloading(void)
{
        int ret = 0;
        ret = unregister_chrdev(major, DEVICE_NAME);

        if (ret < 0) {
                printk(KERN_ERR "unregister_chrdev() error.\n");
        }       

        debugfs_remove(hello_dentry);
        debugfs_remove(hello_dir);

        kfree(hello_msg);
}       

module_init(loading);
module_exit(unloading);

MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
  • MoinMoin Powered
  • Python Powered
  • GPL licensed
  • Valid HTML 4.01