秒字符设备

一个字符设备“second”的驱动,它在被打开的时候初始化一个定时器并将其添加到内核定时器链表,每秒输出一次当前的jifies(为此,定时器处理函数中每次都要修改新的expires)。代码如下:second_timer.c




#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/kdev_t.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/jiffies.h>

#define SECOND_MAJOR 88 /*预设second的主设备号*/

static int second_major = SECOND_MAJOR;

/*second 设备结构体*/
struct second_dev
{
	struct cdev cdev; 			//cdev结构体
	atomic_t counter; 			//原子变量,一共经历多少秒?
	struct timer_list s_timer;  //设备要使用的定时器
};

struct second_dev *second_devp;  //设备结构体指针

/*定时器处理函数*/
static void second_timer_handle(unsigned long arg)
{
	mod_timer(&second_devp->s_timer,jiffies + HZ);
	atomic_inc(&second_devp->counter);	//原子变量递增,相当于counter++
	
	printk(KERN_NOTICE "current jiffies is %ld\n",jiffies);//KERN_NOTICE 正常但又重要的条件,用于提醒
}

/*文件打开函数*/
int second_open(struct inode *inode,struct file *filp)
{
	/*初始化定时器*/
	init_timer(&second_devp->s_timer);	//初始化定时器对象中与内核实现相关的成员
	second_devp->s_timer.function = &second_timer_handle;
	second_devp->s_timer.expires = jiffies + HZ;
	
	add_timer(&second_devp->s_timer);	//添加(注册)定时器
	
	atomic_set(&second_devp->counter,0);//原子操作,设置counter的值,计数清零
	
	return 0;
}
/*文件释放函数*/
int second_release(struct inode *inode,struct file *filp)
{
	del_timer(&second_devp->s_timer);
	
	return 0;
}

/*globalfifo读函数*/
static ssize_t second_read(struct file *filp,char __user *buf,size_t count,loff_t *ppos)

{
	int counter;
	
	counter = atomic_read(&second_devp->counter);//原子读操作
	
	/*
	copy_to_user --   Copy a block of data into user space.
	put_user ( x, ptr);//Write a simple value into user space.
	x         Value to copy to user space.
	ptr		  Destination address, in user space.
	*/
	if(put_user(counter,(int *)buf))
		return - EFAULT;//内存空间访问错误
	else
		return sizeof(unsigned int);
}

/*文件操作结构体*/
static const struct file_operations second_fops = 
{
	.owner = THIS_MODULE,
	.open  = second_open,
	.release= second_release,
	.read = second_read,
};

/*初始化并注册cdev*/
static void second_setup_cdev(struct second_dev *dev,int index)
{
	/*
		函数:MKDEV(int major,int monor)
		头文件:<linux/kdev.h>
		参数:major为主设备号 ,monor为次设备号
		功能:将主设备号和次设备号转换成dev_t类型
		返回:dev_t类型的设备编号
		说明:dev_t dev; //设备号,int 类型,高12位为主设备号,低20位为次设备号
			  可以使用如下宏调用来获得主、次设备号:
			  MAJOR(dev_t dev)
			  MINOR(dev_t dev)		   
	*/
	int err,devno = MKDEV(second_major,index);

	cdev_init(&dev->cdev,&second_fops);
	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops = &second_fops;
	err = cdev_add(&dev->cdev,devno,1);//传入 cdev 结构的指针,起始设备编号,以及设备编号范围。
	if(err)
		printk(KERN_NOTICE "Error %d adding LED%d",err,index);
}

/*设备驱动模块加载函数*/
int second_init(void)
{
	int ret;
	dev_t devno = MKDEV(second_major,0);
	
	/*申请设备号*/
	if(second_major)
		ret = register_chrdev_region(devno,1,"second");
		/*
		register_chrdev_region(dev_t first,unsigned int count,char *name) 
		First :要分配的设备编号范围的初始值(次设备号常设为0); 
		Count:连续编号范围. 
		Name:编号相关联的设备名称. (/proc/devices);
		*/
		
	else /*动态申请设备号*/
	{
		/*
		alloc_chrdev_region() 是动态分配主次设备号。
		register_chrdev()。是老版本的设备号注册方式,他只分配主设备号。从设备号在mknod的时候指定。
		int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name); 
		firstminor : 通常为0; 
		*dev:存放返回的设备号; 
		*/
		ret = alloc_chrdev_region(&devno,0,1,"second");
		second_major = MAJOR(devno);
	}
	if(ret < 0)
		return ret;
	/*动态申请设备结构体内存*/
	second_devp = kmalloc(sizeof(struct second_dev),GFP_KERNEL);//GFP_KERNEL,他表示内存分配
	if(!second_devp) /*申请失败*/
	{
		ret = - ENOMEM;		//核心内存不足
		goto fail_malloc;
	}
	
	memset(second_devp,0,sizeof(struct second_dev));
	
	second_setup_cdev(second_devp,0);
	
	return 0;
	
fail_malloc: unregister_chrdev_region(devno,1);
}

/*模块卸载函数*/
void second_exit(void)
{
	cdev_del(&second_devp->cdev); //注销cdev
	kfree(second_devp);	//释放设备结构体内存
	unregister_chrdev_region(MKDEV(second_major,0),1);//释放设备号
}

MODULE_AUTHOR("Huang Dezhi");
MODULE_LICENSE("GPL");

module_param(second_major,int,S_IRUGO);

module_init(second_init);
module_exit(second_exit);

在second的open()函数中,将启动定时器,此后每秒会再次运行定时器处理函数,在second的release()函数中,定时器被删除。

second_dev结构体中的原子变量counter用于秒计数,每次在定时器处理函数中将被atomic_inc()调用原子增1,second的read()函数会将这个值返回给用户空间。

编译驱动,加载该内核模块并创建“second”设备文件节点后,使用用户空间测试程序打开“second”。

用户测试应用程序会不断地读取自打开“second”设备文件以来经历的秒数。



second设备用户空间测设程序:second_test.c




#include <stdio.h>
#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
	int fd;
	int counter = 0;
	int old_counter = 0;
	
	/*打开second设备文件*/
	fd = open("second",O_RDONLY);
	if(fd != -1)
	{
		while(1)
		{
			read(fd,&counter,sizeof(unsigned int));//读目前经历的秒数
			if(counter != old_counter)
			{
				printf("second after open sencond : %d\n",counter);
				old_counter = counter;
			}
		}
	}
	else
	{
		printf("Device open failure\n");
	}
	return 0;
}

运行second_test.c后,内核将不断地输出目前的jiffies值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值