Re: Hello driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Ok thanks all for your replys and your interest. I've written a new version of the driver. Principle changes are :
_ CodingStyle corrections
_ Remove of the ioctl
_ Use of debugfs instead procfs
_ Some bugs correction I think


This one should be much more better

Thx,


/*
 * tyler@xxxxxxxx
 *
 * Hello World Driver (intended to Linux Kernel 2.6(.11))
 *
 * 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.
 */
#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@xxxxxxxx"
#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);
	if (count_user == 0) {
		hello_msg = kmalloc(NOTH_LEN, GFP_KERNEL);
		if (!hello_msg) {
			up(&hello_sem);
			return -ENOMEM;
		}
		sprintf(hello_msg, "Nothing\n");
	}	
	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 inode *inode, 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 inode *inode, 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;
	}
	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)  
{
	loff_t new_pos;

	new_pos = filp->f_pos + off;
	if (new_pos < 0)
		return -EINVAL;

	return new_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)
{
	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);

	if (hello_msg)
		kfree(hello_msg);
}	

module_init(loading);
module_exit(unloading);

MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);

[Index of Archives]     [Newbies FAQ]     [Linux Kernel Mentors]     [Linux Kernel Development]     [IETF Annouce]     [Git]     [Networking]     [Security]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux SCSI]     [Linux ACPI]
  Powered by Linux