Hello driver

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

 



Hi all,

I have written some very basic kernel modules. If it interests someone, i attach it.
One is a hello device drviver and the other shows some mm tricks.
It's very well annoted I think :)
/*
 * 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
 * _ Procfs operations
 * _ Input/Output control
 * _ Semaphores Locking
 * _ Wait queues
 *
 * All that in a simp;e module. This module isn't very useful and I don't
 * 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 !:)   
 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/devfs_fs_kernel.h>
#include <asm/semaphore.h>
#include <linux/poll.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>

#define DRIVER_AUTHOR "tyler@xxxxxxxx"
#define DRIVER_DESC "Hello World Driver"
#define DEVICE_NAME "hello"


int loading(void) ;
void unloading(void) ;
static int hello_open(struct inode *, struct file *) ;
static int hello_release(struct inode *, struct file *) ;
static ssize_t hello_read(struct inode *, char *, size_t, loff_t) ;
static ssize_t hello_write(struct inode *, const char *, size_t, loff_t) ;
static int hello_ioctl(struct inode *, struct file *, unsigned int , unsigned long);
static unsigned int hello_poll(struct file *, poll_table *) ;
static loff_t hello_llseek(struct file *, loff_t) ;
static int proc_hello_read(char *,char **,off_t,int,int *,void *) ;
static int proc_hello_write(struct file *,const char *,unsigned long,void *) ;

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

static int Major=0 ;
static int count=0 ;
static char *msg ; 
static char *msgPtr ;
static struct semaphore sem ;
static wait_queue_head_t attente ;
static wait_queue_head_t inq ;
static struct proc_dir_entry *hello_dir ;
static struct proc_dir_entry *hello ;


/*
 * Loading function
 */
int loading(void)
{
	Major = register_chrdev(Major,DEVICE_NAME,&fops) ;

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

	printk("<1>Major number %d has been assigned to device %s.\n",Major,DEVICE_NAME);
	sema_init(&sem,1) ;
	init_waitqueue_head(&attente) ;
	init_waitqueue_head(&inq) ;

	hello_dir = proc_mkdir("hello",NULL) ;
	if(hello_dir==NULL){
		unregister_chrdev(Major,DEVICE_NAME) ;
		return -ENOMEM ;
	}

	hello = create_proc_entry("hello",0644,hello_dir) ;
	if(hello==NULL){
		remove_proc_entry("hello",NULL) ;
		unregister_chrdev(Major,DEVICE_NAME) ;
		return -ENOMEM ;
	}

	hello->read_proc = proc_hello_read ;
	hello->write_proc = proc_hello_write ;
	
	return 0 ; 
}

/*
 * Unloading function
 */
void unloading(void)
{
	int ret=0 ;
	ret = unregister_chrdev(Major,DEVICE_NAME) ;

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

	remove_proc_entry("hello",NULL) ;
	remove_proc_entry("hello",hello_dir) ;
}	

/*
 * Read acces to the file /proc/hello
 */
int proc_hello_read(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	int len ;

	down_interruptible(&sem) ;
	len = sprintf(page,"%s",msg) ;
	up(&sem) ;

	return len ;
}

/*
 * Write acces to the file /proc/hello
 */
static int proc_hello_write(struct file *file, const char *buffer, unsigned long count, void *data)
{
	char *bufPtr = buffer ;
	int len = 0 ;
	char *msgPtr = msg ;

	down_interruptible(&sem) ;
	if(msg){
		kfree(msg) ;
		msg=(char *)kmalloc((count+1)*sizeof(char),GFP_KERNEL) ;
	}	 

	while(count && *bufPtr){
		get_user(*(msgPtr++),bufPtr++) ;
		count-- ;
		len++ ;
	}	
	
	up(&sem) ;

	return len ;
}

/*
 * Hello open function
 */
static int hello_open(struct inode *inode, struct file *file)
{
	down_interruptible(&sem) ;
	if(count==0){
		msg=(char *)kmalloc(9*sizeof(char),GFP_KERNEL) ;
		sprintf(msg,"Nothing\n") ;
	}	
	count++ ;
	up(&sem) ;
	msgPtr=msg ;
	return 0 ;
}	

/*
 * Hello close function
 */
static int hello_release(struct inode *inode, struct file *file)
{
	return 0 ;
}	

/*
 * Hello read function : 
 * returns the current string stored in the device
 * if there is no data in the device, the funcion blocks until there is some data
 * written
 */
static ssize_t hello_read(struct inode *inode, char *buf, size_t len, loff_t offset)
{
	int bytes=0 ;

	int minor = MINOR(inode->i_rdev) ;

	printk(KERN_ERR "*************************************************%d**",minor) ;
	
	down_interruptible(&sem) ;
	while(strncmp(msg,"Nothing\n",8)==0){
		up(&sem) ;
		printk("<1> Sleep on\n") ;
		interruptible_sleep_on(&attente) ;
		down_interruptible(&sem) ;
	}	

	while(len && *msgPtr){
		put_user(*(msgPtr++),buf++) ;
		len-- ;
		bytes++ ;
	}	
	up(&sem) ;
	return bytes ;
}	

/*
 * Hello write function
 * Change the string stored in the hello device and wakes up
 * the eventual sleeping read's
 */
static ssize_t hello_write(struct inode *inode, const char *buf, size_t len, loff_t offset)
{
	int bytes=0 ;
	const char *bufPtr = buf ;
	char *msgPtr = msg ;

	down_interruptible(&sem) ;
	if(msg){
		kfree(msg) ;
		msg=(char *)kmalloc((len+1)*sizeof(char),GFP_KERNEL) ;
	}	 

	while(len && *bufPtr){
		get_user(*(msgPtr++),bufPtr++) ;
		len-- ;
		bytes++ ;
	}	
	*msgPtr='\0' ;
	up(&sem) ;
	wake_up_interruptible(&attente) ;
	return bytes ;
}

/*
 * Implementation of an IOCTL to reset the device
 */
static int hello_ioctl(struct inode *inode, struct file *filp, unsigned int ioctl_num, unsigned long ioctl_param)
{
	switch(ioctl_num){
		case 0 :/* Reseting the device */
			down_interruptible(&sem) ;
			if(msg){
				kfree(msg) ;
				msg=(char *)kmalloc(9*sizeof(char),GFP_KERNEL) ;
				sprintf(msg,"Nothing\n") ;
			}	
			up(&sem) ;
			break ;
		default:
			printk(KERN_ERR "Erreur : ioctl non implemente.\n");	
			return -ENODEV ;
			break ;
	}		
	return 0 ;	
}

/*
 * Polling on the hello device
 */
static unsigned int hello_poll(struct file *filp, poll_table *wait) 
{
	unsigned int mask = 0 ;

	poll_wait(filp,&inq,wait) ;
	
	if(strncmp(msg,"Nothing\n",8)!=0)
		mask |= POLLIN | POLLRDNORM ;

	mask |= POLLOUT | POLLWRNORM ;

	return mask ;
}	

/*
 * Seek the device
 */
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 ;
}	

module_init(loading) ;
module_exit(unloading) ;

MODULE_LICENSE("GPL") ;
MODULE_AUTHOR(DRIVER_AUTHOR) ;
MODULE_DESCRIPTION(DRIVER_DESC) ;
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>

unsigned long new_page ;
unsigned long temp ;

struct page *new_desc_page ;

int init_module()
{
	new_page = __get_free_page(GFP_KERNEL) ;

	new_desc_page = (struct page *)virt_to_page(new_page) ;

	/*
	 * That's how the virtual memory system works.
	 * To retrieve the virtual adress corresponding to the page,
	 * we get the index in the page frame (new_desc_page-mem_map).
	 * We then multiply it by the size of the page. We get the physical adress.
	 * And in kernel mode, the differences between virtual and physical adresses
	 * is just an offset of PAGE_OFFSET. So we add PAGE_OFFSET
	 */
	temp = (new_desc_page-mem_map)*PAGE_SIZE+PAGE_OFFSET ;

	printk("0x%x\n",temp) ;
	printk("0x%x\n",new_page) ;

	/* 
	 * In kernel mode, the virtual adresses has just an offset of PAGE_OFFSET
	 * (the page tables are properly configured to do this)
	 * On i386, the PAGE_OFFSET=0xC0000000
	 */
	temp = __pa(new_page) ;

	printk("0x%x\n",new_page-PAGE_OFFSET) ;
	printk("0x%x\n",temp) ;

	return 0 ;
}

void cleanup_module()
{
	free_page(new_page) ;
}	

[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