TQ2440 LINUX 2.6.30.4 LED驱动感言,从最初的打印字符,到自动分配设备号,到自动创建设备节点,到此设备号分控led

1.第一次写驱动感言

开始写驱动,从来没写过,第一次写完最基础的打印字符驱动之后明白了驱动的框架,和系统是怎样加载,卸载,调用驱动的,及上层应用函数怎么调用的驱动函数。不同的linux内核的拥有的系统驱动函数不一样,这就要求在写基于不同的linux内核写驱动的时候,要先了解内核的函数名称和使用方法。这样写出的驱动才能在所在的linux内核中成功被linux内核所调用。

 

2.自动分配设备号

 

如果要所写的驱动自动分配设备号,则需要在register_chrdev()函数中的第一个参数设置为0.这样在驱动入口函数的加载时系统会自动分配设备号。为什么最好需要自动创建设备号呢,这是因为,在写驱动的时候必须为自己写的驱动注册设备号,这样系统才能调用,但是也可以固定分配设备号,但是这样麻烦,因为在分配设备号的时候需要用cat /proc/devices查看当前系统中的驱动设备号的分配情况。如下:

 
 
[root@EmbedSky /]# insmod led_drv.ko 
[root@EmbedSky /]# cat /proc/devices 
Character devices:
  1 mem
  4 /dev/vc/0
  4 tty
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 10 misc
 13 input
 14 sound
 29 fb
 81 video4linux
 89 i2c
 90 mtd
116 alsa
128 ptm
136 pts
180 usb
188 ttyUSB
189 usb_device
204 tq2440_serial
252 led_drv
253 usb_endpoint
254 rtc

Block devices:
259 blkext
  7 loop
  8 sd
 31 mtdblock
 65 sd

如果在分配设备号的时候出现了系统中已经出现了的设备号,则会出现冲突,导致驱动不能成功调用。所以为了避免每次分配驱动设备号的时候都要重复去查看 /proc/devices文件,最好自动创建设备号。方法就是在register_chrdev()函数中的第一个参数设置为0,这样系统就自动创建设备号。

3.自动创建设备节点(手动创建)

可以手动创建设备节点,为什么需要创建设备节点呢,这是因为linux系统在上层应用函数中调用设备驱动函数的时候需要利用设备文件名打开所对应的驱动函数,但是我们的驱动函数是以设备号为驱动识别标志的,linux系统里也是利用设备号设别的不同的设备驱动,但是为了上层应用函数在调用驱动函数的时候是以设备文件为标示符的,所以为了能够兼顾linux系统内核和上层应用函数之间的差别,就需要把驱动函数的设备号和上层需要调用的驱动设备文件之间建立一种联系,即创建设备节点,如果手动创建设备节点则可以用命令:

  1. mknod /dev/led/ c 111 0   
mknod /dev/led/ c 111 0 


字符c代表char型即字符型设备,111为主设备号,0为测设备号。

为了避免每次创建设备节点的时候都需要查看设备号,这样麻烦,所以就自动创建设备号。利用linux系统中提供的函数和数据类型,自动创建设备号。下面一个例子:

 

  1. /************ MDEV根据/sys目录下的文件信息自动创建设备文件节点,也就是设备文件的设备号,  
  2.  通过这些设备文件打开所对应的设备号**********/  
  3. static struct class * led_class;   //定义类指针为入口函数注册测次设备号文件做准备  
  4. static struct device    * led_class_devs[5];    //定义类设备指针为在类下建立设备文件做准备  
/************ MDEV根据/sys目录下的文件信息自动创建设备文件节点,也就是设备文件的设备号,
 通过这些设备文件打开所对应的设备号**********/
static struct class * led_class;   //定义类指针为入口函数注册测次设备号文件做准备
static struct device	* led_class_devs[5];    //定义类设备指针为在类下建立设备文件做准备


 

  1. static int led_drv_init(void)  
  2. {     
  3.     int minor;  
  4. /*****字符设备注册函数,"led_drv"在/proc/devices文件中表明了主设备号,名字就是led_drv,  
  5. 第一个参数为0。表明主设备号为自动分配,第三个参数&led_drv_fops就是和这个主设备号对应的,  
  6. 在应用程序中调用函数时linux系统就会首先找设备号,以设备号为识别依据,进而调用到&led_drv_fops  
  7. 中的函数*****/    
  8.     major=register_chrdev(0,"led_drv",&led_drv_fops);   
  9.   
  10.     /******定义类,类设备的文件都是在/sys/class目录下生成的,MDEV通过/sys/class目录下的信息在/dev/目录下  
  11.     自动生成设备文件及其对应的设备号**********/  
  12.   
  13.       
  14.     led_class = class_create(THIS_MODULE, "leddrv"); //定义类,在/sys/class/文件夹下生成 leddrv文件夹  
  15.       
  16.     /*******定义类设备,在/sys/class/leddrv/目录下生成响应的设备文件,如 leds .  
  17.     MDEV根据次设备就是在/dev/目录下生成相应的设备文件如 "leds"*********/  
  18.     led_class_devs[0]= device_create(led_class, NULL, MKDEV(major, 0), 0, "leds");  
  19.     /*****连续生成4个主设备号相同但是次设备号不相同设备文件*******/  
  20.     for(minor = 1;minor <= 4;minor ++)  
  21.         {  
  22.             led_class_devs[minor]=device_create(led_class, NULL, MKDEV(major,minor), 0, "led%d",minor);  
  23.         }  
  24.   
  25.   
  26.    /******映射地址空间中的寄存器的地址******/  
  27.     gpbcon=(volatile unsigned long *)ioremap(0x56000010,16);  
  28.     gpbdat=gpbcon + 1; // 1代表一个int型的字节大小,即 4 个字节  
  29.     return 0;  
  30. }  
static int led_drv_init(void)
{   
	int minor;
/*****字符设备注册函数,"led_drv"在/proc/devices文件中表明了主设备号,名字就是led_drv,
第一个参数为0。表明主设备号为自动分配,第三个参数&led_drv_fops就是和这个主设备号对应的,
在应用程序中调用函数时linux系统就会首先找设备号,以设备号为识别依据,进而调用到&led_drv_fops
中的函数*****/	
	major=register_chrdev(0,"led_drv",&led_drv_fops); 

	/******定义类,类设备的文件都是在/sys/class目录下生成的,MDEV通过/sys/class目录下的信息在/dev/目录下
	自动生成设备文件及其对应的设备号**********/

	
	led_class = class_create(THIS_MODULE, "leddrv"); //定义类,在/sys/class/文件夹下生成 leddrv文件夹
	
	/*******定义类设备,在/sys/class/leddrv/目录下生成响应的设备文件,如 leds .
	MDEV根据次设备就是在/dev/目录下生成相应的设备文件如 "leds"*********/
	led_class_devs[0]= device_create(led_class, NULL, MKDEV(major, 0), 0, "leds");
	/*****连续生成4个主设备号相同但是次设备号不相同设备文件*******/
	for(minor = 1;minor <= 4;minor ++)
		{
			led_class_devs[minor]=device_create(led_class, NULL, MKDEV(major,minor), 0, "led%d",minor);
		}


   /******映射地址空间中的寄存器的地址******/
	gpbcon=(volatile unsigned long *)ioremap(0x56000010,16);
	gpbdat=gpbcon + 1; // 1代表一个int型的字节大小,即 4 个字节
	return 0;
}

 

注明:led_drv文件在  /proc/devices中的建立,对应相应的主设备号,leddrv在/sys/class/目录下建立然后在leddrv目录下建立leds文件,这样MDEV根据/sys/目录下的信息在 /dev目录下建立 leds文件。此文件对应相应的主设备号和次设备号。

 

4.驱动函数中的write()函数的参数含义,及其应用程序中的调用原理

  1. static ssize_t led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)  
  2. {  
  3.     //printk("first_drv_write\n");  
  4.     int minor = MINOR(file->f_dentry->d_inode->i_rdev);//利用write()函数的参数找到次设备号了  
  5. /**************************此字符一定要设置成 char 型的,否则是不会传递成功的,  
  6. 如果设置成 int 则led 灯是点不亮的,0,1传递之后就不是0,1了**********************/  
  7.     char val;    
  8.     copy_from_user(&val,buf,count);  //从用户空间向内核空间传递参数,以用户空间为标准判断 from to   
  9.     printk("%d,%d\n",__LINE__,val);  //打印调试val的值  _LINE_ 的值是当前的行号  

 

static ssize_t led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
	//printk("first_drv_write\n");
	int minor = MINOR(file->f_dentry->d_inode->i_rdev);//利用write()函数的参数找到次设备号了
/**************************此字符一定要设置成 char 型的,否则是不会传递成功的,
如果设置成 int 则led 灯是点不亮的,0,1传递之后就不是0,1了**********************/
	char val;  
	copy_from_user(&val,buf,count);  //从用户空间向内核空间传递参数,以用户空间为标准判断 from to 
	printk("%d,%d\n",__LINE__,val);  //打印调试val的值  _LINE_ 的值是当前的行号

 

 

注意:val的值一定要定义为char,因为要把char类型的 buf的值传递给val,所以要把val定义为char,如果不这样就会产生错误导致led的灯点不亮,这个错误导致当时的led灯没有被点亮,后来用了打印语句调试才知道原来传递后的val不是1或0 ,就是因为val没有被定义为char,而定义成了int。以后要记得数值与数值之间的传递要复合数值的类型。否则会导致错误发生。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值