Linux 字符驱动程序(一)

it2025-09-01  13

Linux 字符驱动程序(一)

于linux有三个主要的内核设备: 1 字符设备:          •字符设备的读写以字节为单位,存取时没有缓存。      •对字符设备发出读写请求时。实际的硬件I/O紧接着就发生了。

一般来说。字符设备不支持随机訪问。

     •典型的字符设备包含鼠标、键盘及串行口等。 2 块设备:      •块设备读写以块为单位,典型的块大小为512或1024字节。

     •利用一块系统内存作为缓冲区,当用户进程对设备发出读写请求时,驱动程序先察看缓冲区中的内容。若缓冲区中的数据能满足用户的要求就返回对应的数据,否则就调用对应的请求函数来进行实际的I/O操作,以提高效率。      •块设备主要包含硬盘、软盘、CD-ROM等。 3 网络设备:      •Linux的网络系统主要基于BSD Unix的Socket机制。

在系统和驱动程序之间定义有专门的数据结构进行数据的传递。系统里支持对发送数据和接收数据的缓存,提高流量控制机制。提供对多协议的支持。

4 每一个设备相应一个文件。放在/dev文件夹下 5 每一个设备文件都相应有两个设备号。存放在inode节点中    •主设备号标示设备的种类,也标识了该设备所使用的驱动程序;    •次设备号标识了使用同一设备驱动程序的不同硬件设备。 6 能够通过/proc/devices 来查看对应的设备号。通过mknod  /dev/xxx c major minor 来产生设备节点。从而将设备挂接到/dev文件夹下。

或者在编写驱动程序时动态的获取主设备号以及动态产生设备节点。

7 以下详细分析一个led的驱动程序。(该程序沿用了2.6曾经版本号的驱动程序的书写方法,后面会介绍新的书写方法,可是本质是一样的)。 声明为static是为了避免数据对内和造成污染,仅对该模块有效。 #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/irq.h> #include <asm/io.h> #include <asm/arch/regs-gpio.h> #include <asm/hardware.h> //相关的头文件,我们仿照其它模块载入就可以。 #define DEVICE_NAME     "leds"  /* 载入模式后。运行”cat /proc/devices”命令看到的设备名称 */ //用于自己主动产生设备节点 static struct class *leds_class; static struct class_device * leds_class_devs[4]; // LED的控制地址 volatile unsigned long *gpfcon = NULL; volatile unsigned long *gpfdat = NULL; //应用程序运行open时调用该函数。 static int first_drv_open(struct inode *inode, struct file *file) {       int minor = MINOR(inode->i_rdev); //MINOR(inode->i_cdev); switch(minor){            case 0: /* /dev/leds */          {             *gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));*gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));             break;         }         case 1: /* /dev/led1 */         {                          *gpfcon &= ~((0x3<<(4*2));*gpfcon |= ((0x1<<(4*2));             break;         }         case 2: /* /dev/led2 */         {             *gpfcon &= ~ (0x3<<(5*2));*gpfcon |= (0x1<<(5*2));             break;         }         case 3: /* /dev/led3 */         {             *gpfcon &= ~(0x3<<(6*2));*gpfcon |= (0x1<<(6*2));                          break;         }         }      return 0; } // 应用程序运行write时调用该函数; static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) {     int minor = MINOR(file->f_dentry->d_inode->i_rdev); // 获取次设备号     int val;     copy_from_user(&val, buf, count);     switch (minor)     {         case 0: /* /dev/leds */         {                          if (val == 1)     { // 点灯 *gpfdat &= ~((1<<4) | (1<<5) | (1<<6));     }     else     { // 灭灯 *gpfdat |= (1<<4) | (1<<5) | (1<<6);      }             break;         }         case 1: /* /dev/led1 */         {             if (val == 1)     { // 点灯 *gpfdat &= ~(1<<4);      }      else      { // 灭灯 *gpfdat |= (1<<4);      }             break;         }         case 2: /* /dev/led2 */         {             if (val == 1)      { // 点灯 *gpfdat &= ~(1<<5);      }      else      { // 灭灯 *gpfdat |= (1<<5);      }             break;         }         case 3: /* /dev/led3 */         {             if (val == 1)      { // 点灯 *gpfdat &= ~(1<<6);      }      else      { // 灭灯 *gpfdat |= (1<<6);       }             break;         }              } return 0; } //驱动程序与内核的接口; static struct file_operations first_drv_fops = {     .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自己主动创建的__this_module变量 */     .open   =   first_drv_open,          .write =   first_drv_write,    }; int major; //记录动态获取的设备号 /*  * 运行insmod命令时就会调用这个函数   */ static int first_drv_init(void) {       int minor = 0; //次设备号       major = register_chrdev(LED_MAJOR, DEVICE_NAME, & first_drv_fops); //注冊设备       if (major < 0) {       printk(DEVICE_NAME " can't register major number\n");       return major;      } leds_class = class_create(THIS_MODULE, "leds"); // 产生节点类,以leds_class声明的均为同一种设备 if (IS_ERR(leds_class)) return PTR_ERR(leds_class);   leds_class_devs[0] = class_device_create(leds_class, NULL, MKDEV(major, 0), NULL, "leds"); //产生不同的从设备,并以不同的名字挂接在/dev文件夹下。 for (minor = 1; minor < 4; minor++) { leds_class_devs[minor] = class_device_create(leds_class, NULL, MKDEV(major, minor), NULL, "led%d", minor); if (unlikely(IS_ERR(leds_class_devs[minor]))) return PTR_ERR(leds_class_devs[minor]); }          gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);//控制寄存器地址 gpfdat = gpfcon + 1;  //  0x56000054  //数据寄存器地址。         printk(DEVICE_NAME " initialized\n"); return 0; } /*  * 运行rmmod命令时就会调用这个函数   */ static void first_drv_exit(void) { int minor;     /* 卸载驱动程序 */        unregister_chrdev(major, DEVICE_NAME); for (minor = 0; minor < 4; minor++) { class_device_unregister(leds_class_devs[minor]); } class_destroy(leds_class); iounmap(gpfcon); } module_init(first_drv_init); module_exit(first_drv_exit); /* 描写叙述驱动程序的一些信息。不是必须的 */ MODULE_AUTHOR("http://www.100ask.net"); MODULE_VERSION("0.1.0"); MODULE_DESCRIPTION("LED Driver"); MODULE_LICENSE("GPL"); 我们写应用程序时。打开对应的设备/dev/leds , /dev/led1, /dev/led2, /dev/led3 向当中写1,0 就能够控制所有led或者某个led的亮灭.

版权声明:本文博主原创文章,博客,未经同意不得转载。

转载于:https://www.cnblogs.com/bhlsheji/p/4887607.html

最新回复(0)