一個簡單的字符設備模塊,包括設備文件的讀寫等基本操作,可以作爲模板來使用。
hello.c
/*--------------------------------------------------
Derived from Linux Device Drivers(3rd edition)
http://www.makelinux.net/ldd3/
Midas Zhou
---------------------------------------------------*/
#include <linux/module.h>
#include <linux/init.h> /* __init() __exit() */
#include <linux/sched.h>
#include <linux/fs.h> /* register_chrdev_region(), alloc_chrdev_region()...*/
#include <linux/cdev.h> /* cdev_init(), cdev_add(), cdev_del() */
#include <asm/errno.h>
#include <linux/slab.h> /* kmalloc() kfree() */
#include <linux/uaccess.h> /* copy_to_user(), copy_from_user() */
#define HELLO_DATA_LEN 1024 /* data length for hello_cdev.data */
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Free");
MODULE_DESCRIPTION("A LINUX Free Hello World Example");
MODULE_VERSION("0.1");
static char * whom ="This is the Free Linux World! ";
static int howmany = 1;
module_param(whom,charp, S_IRUGO); /* S_IRUGO = S_IRUSR | S_IRGRP | S_IROTH */
module_param(howmany,int,S_IRUGO);
MODULE_PARM_DESC(whom, "The name to display");
MODULE_PARM_DESC(howmany, "How many times to print");
static dev_t hello_devnum; /* 32bits device number: major(12) + minor(20) */
struct hello_cdev {
char *data; /* mem data for the device */
struct semaphore sem; /* mutual exclusion */
struct cdev cdev; /* Char device */
};
static struct hello_cdev my_hello_cdev;
struct class * hello_class;
struct device *hello_device;
/* ----------------------- File Operation Functions: open/close/read/write/lseek ------------------- */
static int hello_open(struct inode *inode, struct file *filp)
{
struct hello_cdev *pdev;
pdev=container_of(inode->i_cdev,struct hello_cdev, cdev); // to locate and get helloc_cdev here.
printk(KERN_INFO "%s: Hello, you just open the Free Linux World!\n",__func__);
// printk(KERN_INFO "%s: read data in helloc_cdev: %s\n",__func__,pdev->data);
filp->private_data = pdev; /* put pdev to private data for other operation reference */
return 0;
}
static int hello_close(struct inode *inode, struct file *filp)
{
printk(KERN_INFO "%s: Hello, you just close the Free Linux World!\n",__func__);
return 0;
}
ssize_t hello_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
struct hello_cdev *dev = filp->private_data;
ssize_t retval = 0;
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
if( *f_pos > HELLO_DATA_LEN-1 )
{
printk(KERN_ALERT "%s: f_pos exceeds the end of the file.\n",__func__);
goto out;
}
if( count > HELLO_DATA_LEN-*f_pos )
count = HELLO_DATA_LEN-*f_pos;
if( copy_to_user(buf, (dev->data)+*f_pos, count))
{
retval=-EFAULT;
goto out;
}
*f_pos += count;
retval = count;
out:
up(&dev->sem);
return retval;
}
ssize_t hello_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
struct hello_cdev *dev = filp->private_data;
ssize_t retval =0;
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
if ( count > HELLO_DATA_LEN - *f_pos )
count = HELLO_DATA_LEN - *f_pos;
if (copy_from_user(dev->data+*f_pos, buf, count))
{
retval = -EFAULT;
goto out;
}
*f_pos += count;
retval = count;
out:
up(&dev->sem);
return retval;
}
loff_t hello_llseek(struct file *filp, loff_t off, int whence)
{
// struct hello_cdev *dev = filp->private_data;
loff_t newpos;
switch(whence) {
case 0: /*SEEK_SET*/
newpos = off;
break;
case 1: /*SEEK_CUR*/
newpos = filp->f_pos + off;
break;
default:
return -EINVAL;
}
if (newpos < 0 || newpos > HELLO_DATA_LEN-1 )
return -EINVAL;
filp->f_pos=newpos;
return newpos;
}
static const struct file_operations hello_fops={
.owner = THIS_MODULE,
.open = hello_open,
.read = hello_read,
.write = hello_write,
.llseek = hello_llseek,
.release = hello_close,
};
/* ------------------------------ Module Functions ------------------------- */
static int __init hello_init(void)
{
int k;
int ret=0;
for(k=0;k<howmany;k++)
{
printk(KERN_ALERT "Hello, %s!\n",whom); /* No';' after KERN_ALERT !!!*/
}
printk(KERN_INFO "The process is \"%s\" (pid %i)\n",current->comm, current->pid);
/* --- allocate mem for hello_cdev.data --- */
my_hello_cdev.data = kmalloc(HELLO_DATA_LEN, GFP_KERNEL);
if(IS_ERR(my_hello_cdev.data)) {
printk(KERN_ALERT "kmalloc_fail, kmalloc for my_hello_cdev.data failed!\n");
ret = -1;
goto kmalloc_fail;
}
memset(my_hello_cdev.data,0,HELLO_DATA_LEN);
strcpy(my_hello_cdev.data,"-/-/-/-/-/-/-/-/-/-/-/");
/* --- init. semaphore for hello_cdev ---*/
sema_init(&my_hello_cdev.sem,1);
/* ------------------ Char Device Registration and Creation ---------------------- */
/* --- 1. register and get dev number / name --- !!!!! ASSOCIATE name WITH major devnum */
ret=alloc_chrdev_region(&hello_devnum, 0, 1, "dev_hello"); //warning: devnum must NOT be pointer type.
if(ret<0){
printk("%s: alloc_chrdev_region() error!\n",__func__);
ret = -2;
goto alloc_region_fail;
}
/* --- 2. char device init --- !!!!! ASSOCIATE file operations */
cdev_init(&my_hello_cdev.cdev, &hello_fops); // !!!! ASSOCIATE hello_cdev with inode, to use container_of() to exact helloc_cdev later.
/* --- 3. add char dev to kernel ---- */
ret=cdev_add(&my_hello_cdev.cdev,hello_devnum,1);
if(ret<0){
printk("%s: cdev_add() error!\n",__func__);
ret = -3;
goto cdev_fail;
}
/* --- 4. create class in sysfs */
hello_class = class_create(THIS_MODULE,"hello_class");
if( IS_ERR(hello_class)) {
printk(KERN_INFO "class_create() fail!\n");
ret = -4;
goto class_fail;
}
/* --- 5. create devices file in /dev */
hello_device = device_create(hello_class, NULL, hello_devnum, NULL, "hello_dev");
if ( IS_ERR(hello_device)) {
printk(KERN_INFO "device_create() fail!\n");
ret = -5;
goto device_fail;
}
/* --- 6. module init. success --- */
goto init_success;
device_fail: /* --- 1. destroy class --- */
class_destroy(hello_class);
class_fail: /* --- 2. del char dev --- */
cdev_del(&my_hello_cdev.cdev);
cdev_fail: /* --- 3. unregister char dev region --- */
unregister_chrdev_region(hello_devnum,1);
/* --- 4. kfree mem --- */
kfree(my_hello_cdev.data);
kmalloc_fail:
alloc_region_fail:
return ret; /* if ret !=0; the module will NOT be inserted to the kernel. */
init_success: /* --- init. success --- */
return 0;
}
static void __exit hello_exit(void) /* !!!! void */
{
/*--- 1. unregister device ---*/
printk(KERN_INFO "unregister device \n");
device_unregister(hello_device);
/*--- 2. del class ---*/
printk(KERN_INFO "destroy class \n");
class_destroy(hello_class);
/*--- 3. del char dev ---*/
printk(KERN_INFO "delete char dev.\n");
cdev_del(&my_hello_cdev.cdev);
/*--- 4. unregister char dev region ---*/
printk(KERN_INFO "unregister chardev region.\n");
unregister_chrdev_region(hello_devnum,1);
/*--- 5. kfree all mem ---*/
printk(KERN_INFO "kfree mem.\n");
kfree(my_hello_cdev.data);
printk(KERN_INFO "The process is \"%s\" (pid %i)\n",current->comm, current->pid);
printk(KERN_ALERT "Goodbye, Cruel World!\n");
}
module_init(hello_init);
module_exit(hello_exit);