linux驅動例1--計時器

1.製作一個定時器驅動

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/device.h>


#define DEV_NAME		"new_timer"
#define DEV_MAJOR		0
#define DEV_MONITOR		0
#define CLASS_NAME		"cla_ntimer"


struct timer_dev {
	struct cdev   cdev;
	struct class *pclas;
	struct device *pdev;
	int	dev_num;
	struct timer_list timer;
	int	count;
	atomic_t	atomic;
};

static struct timer_dev mytimer;

static void timer_irq_hand( unsigned long arg)
{
	mytimer.count ++;
	printk("time: %d\n", mytimer.count);
	mod_timer( &(mytimer.timer), jiffies + HZ);
	return;
}


static int timer_open(struct inode *inode, struct file *fp)
{
	DEFINE_TIMER( temp, timer_irq_hand, jiffies + HZ, 0);
	printk("open...KB\n");
	mytimer.timer = temp;
	mytimer.count = 0;
	add_timer( &(mytimer.timer));
	return 0;
	
}

static int timer_release(struct inode *inode, struct file *fp)
{
	printk("open...KB\n");
	del_timer( &(mytimer.timer));
	return 0;
}

static struct file_operations fops = {
	.owner = THIS_MODULE,
	.open = timer_open,
	.release =timer_release,
};

static int __init timer_init(void)
{
	int	ret = 0;
	printk("keyBoard_init..........\n");
	ret = alloc_chrdev_region( &(mytimer.dev_num), DEV_MONITOR, 1, DEV_NAME);
	if( ret<0 )
	{
		printk("error:%s, %d\n", __FUNCTION__,__LINE__);
		goto ERR_ALLOC_CHRDEV;
	}
	cdev_init( &(mytimer.cdev), &fops);
	mytimer.cdev.owner = THIS_MODULE;
	ret = cdev_add( &(mytimer.cdev), mytimer.dev_num, 1);
	if( ret<0)
	{
		printk("error: %s, %d\n", __FUNCTION__, __LINE__);
		goto ERR_CDEV_ADD;
	}
	mytimer.pclas = class_create( THIS_MODULE, CLASS_NAME);
	if( NULL==mytimer.pclas )
	{
		printk("error: %s, %d\n", __FUNCTION__, __LINE__);
		goto ERR_CLAS_CREAT;
	}
	mytimer.pdev = device_create( mytimer.pclas, NULL, mytimer.dev_num, NULL, DEV_NAME);
	if( NULL==mytimer.pdev )
	{
		goto ERR;
	}
	atomic_set( &(mytimer.atomic) , 1);
	
	return 0;
	ERR:
		class_destroy( mytimer.pclas);
	ERR_CLAS_CREAT:
		cdev_del( &(mytimer.cdev));
	ERR_CDEV_ADD:
		unregister_chrdev_region( mytimer.dev_num, 1);
	ERR_ALLOC_CHRDEV:
		return -1;
}

static void __exit timer_exit(void)
{
	printk("keyBoard_exit..........\n");
	device_destroy( mytimer.pclas, mytimer.dev_num);
	class_destroy( mytimer.pclas);
	cdev_del( &(mytimer.cdev));
	unregister_chrdev_region( mytimer.dev_num, 1);
	return;

}

MODULE_LICENSE("GPL");

module_init(timer_init);
module_exit(timer_exit);

        該驅動還沒有能力和應用層傳輸數據,以及進行讀取控制。防止在中斷函數修改數值時,進程讀取數據而造成的錯誤。

        嘗試用原子操作來保證驅動使用的併發數量限制。用信號量來保證臨界資源的訪問安全。並且測試了在中斷中進行進程調度的後果,it's awful, totally awful.

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/device.h>


#define DEV_NAME		"new_timer"
#define DEV_MAJOR		0
#define DEV_MONITOR		0
#define CLASS_NAME		"cla_ntimer"


struct timer_dev {
	struct cdev   cdev;
	struct class *pclas;
	struct device *pdev;
	int	dev_num;
	struct timer_list timer;
	atomic_t	open_cnt;		//原子操作來保證併發數量限制
	struct semaphore sem;		//信號量操作
	int	counter;				//用來給計時器計數用
	wait_queue_head_t		wqh;	//用來存儲阻塞進程用
};

static struct timer_dev mytimer;

static void timer_irq_hand( unsigned long arg)
{
	down( &(mytimer.sem));	//主要與read()互斥
	mytimer.counter++;
	up( &(mytimer.sem));

	printk("time: %d\n", mytimer.counter);
	mod_timer( &(mytimer.timer), jiffies + HZ);

#if 0		//測試中斷中進行進程調度的後果
	 struct semaphore sem;
	sema_init( &sem, 0);
	down( &sem);		//這裏會進入阻塞
		//實驗發現,由於進程調度,操作系統確實跑飛了
#endif
}


static int timer_open(struct inode *inode, struct file *fp)
{
#if 1
	if( atomic_sub_return( 1, &(mytimer.open_cnt))< 0 )	//假如計數已經小於0,表明已經達到最大併發數
	{
		atomic_add(1, &(mytimer.open_cnt));
		return -EBUSY;			//不能得到資源就立即返回
	}
#else
	while( atomic_sub_return( 1, &(mytimer.open_cnt))< 0)
	{
		atomic_add( 1, &(mytimer.open_cnt));
		wait_queue_t	wq;
		init_wait( &(wq));
		add_wait_queue( &(mytimer.wqh), &wq);
		
		set_current_state( TASK_INTERRUPTIBLE);
		schedule();
		if( signal_pending( current))
		{
			return 
		}
		remove_wait_queue( &(mytimer.wqh), &wq);
		set_current_state( TASK_RUNNING);
		wake_up( &(mytimer.wqh));
	}
#endif

	DEFINE_TIMER( temp, timer_irq_hand, jiffies + HZ, 0);

	printk("open...KB\n");
	mytimer.timer = temp;
	add_timer( &(mytimer.timer));
	sema_init( &(mytimer.sem), 1);

	return 0;
	
}

static int timer_release(struct inode *inode, struct file *fp)
{
	printk("open...KB\n");
	atomic_add( 1, &(mytimer.open_cnt));		//釋放資源
	del_timer( &(mytimer.timer));
	return 0;
}

static ssize_t timer_read(struct file *fp, char __user *buf, size_t sz, loff_t *off)
{
	printk("read....\n");
	int temp;
#if 1		//阻塞版
	down( &(mytimer.sem));	//主要與中斷中的自加互斥
#else		//非阻塞版
	if( down_trylock( &(mytimer.sem)))
	{
		return -EBUSY;
	}
#endif
	temp = mytimer.counter;	//安全的獲取數據
	up( &(mytimer.sem));
	copy_to_user( buf, &temp, sz);
	return sz;
}

static struct file_operations fops = {
	.owner = THIS_MODULE,
	.open = timer_open,
	.release =timer_release,
	.read =timer_read,
};

static int __init timer_init(void)
{
	int	ret = 0;
	printk("keyBoard_init..........%s\n", __TIME__);
	ret = alloc_chrdev_region( &(mytimer.dev_num), DEV_MONITOR, 1, DEV_NAME);
	if( ret<0 )
	{
		printk("error:%s, %d\n", __FUNCTION__,__LINE__);
		goto ERR_ALLOC_CHRDEV;
	}
	cdev_init( &(mytimer.cdev), &fops);
	mytimer.cdev.owner = THIS_MODULE;
	ret = cdev_add( &(mytimer.cdev), mytimer.dev_num, 1);
	if( ret<0)
	{
		printk("error: %s, %d\n", __FUNCTION__, __LINE__);
		goto ERR_CDEV_ADD;
	}
	mytimer.pclas = class_create( THIS_MODULE, CLASS_NAME);
	if( NULL==mytimer.pclas )
	{
		printk("error: %s, %d\n", __FUNCTION__, __LINE__);
		goto ERR_CLAS_CREAT;
	}
	mytimer.pdev = device_create( mytimer.pclas, NULL, mytimer.dev_num, NULL, DEV_NAME);
	if( NULL==mytimer.pdev )
	{
		goto ERR;
	}
	atomic_set( &(mytimer.open_cnt) , 1);		//只允許打開一次
	init_waitqueue_head( &(mytimer.wqh));

	return 0;
	ERR:
		class_destroy( mytimer.pclas);
	ERR_CLAS_CREAT:
		cdev_del( &(mytimer.cdev));
	ERR_CDEV_ADD:
		unregister_chrdev_region( mytimer.dev_num, 1);
	ERR_ALLOC_CHRDEV:
		return -1;
}

static void __exit timer_exit(void)
{
	printk("keyBoard_exit..........\n");
	device_destroy( mytimer.pclas, mytimer.dev_num);
	class_destroy( mytimer.pclas);
	cdev_del( &(mytimer.cdev));
	unregister_chrdev_region( mytimer.dev_num, 1);
	return;

}

MODULE_LICENSE("GPL");

module_init(timer_init);
module_exit(timer_exit);


      

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