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

来源:互联网 发布:武汉seo建站 编辑:程序博客网 时间:2024/05/22 00:53

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

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

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

[html] view plaincopyprint?
  1. mknod /dev/led/ c 111 0   


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

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

 

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


 

[html] view plaincopyprint?
  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. }  

 

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

 

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

[html] view plaincopyprint?

  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_ 的值是当前的行号  

 

 

 

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

原创粉丝点击