一個簡單的字符設備模塊

一個簡單的字符設備模塊,包括設備文件的讀寫等基本操作,可以作爲模板來使用。

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);

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章