驱动学习笔记4 共用驱动程序,通过次设备号区分硬件个体

了解两个数据结构体:struct inode和struct file  


a)

struct inode {
    dev_t    i_rdev;//保存设备号信息
    struct cdev  *i_cdev; //指向字符设备对象,例如:led_cdev
    ...
};
功能:描述一个文件的物理上的信息,例如:ls -l  /dev/myled //看到的各种属性都是用inode来描述
生命周期:一旦创建文件成功(mknod,vim,touch,dd,cp,mv等),linux内核就会帮你自动创建一个inode对象
    用此对象来描述创建的新文件各种属性信息,一旦删除文件(rm),内核也会帮你自动删除对应的inode对象
结论:
1.一个文件仅有一个inode对象
2.字符设备驱动操作接口
    struct file_operations {
        int (*open)(struct inode *inode, ....);
        int (*release)(struct inode *inode, ....);
    };
    这些接口的第一个形参inode指针就是指向内核自动帮你创建的inode对象
    将来驱动open,release接口可以通过inode指针来获取对应的文件信息
    重点关注设备号:i_rdev这个成员
    能够提取主次设备号:MAJOR(inode->i_rdev), MINOR(inode->i_rdev);

b)

struct file { 
    const struct file_operations    *f_op;//指向字符设备驱动硬件操作接口对象,例如:led_fops
    ...
};
功能:描述一个文件被成功打开open以后的各种属性(权限啦,读写位置信息等)
生命周期:文件一旦被open成功打开,linux内核(sys_open)自动帮你创建一个file对象
    来描述文件打开以后的属性,文件一旦被关闭close,内核自动销毁对应的file对象
结论:
1.一个文件可以有多个file对象
2.字符设备驱动操作接口:
    struct file_operations {
        int (*open)(struct inode *inode, struct file *file);
        int (*release)(struct inode *inode, struct file *file);
        int (*read)(struct file *file,...);
        int (*write)(struct file *file, ...);
        int (*unlocked_ioctl)(struct file *file, ...);
    };
    这些接口的file指针指向内核创建的file对象
    问:如何通过file指针来获取对应文件的inode对象指针呢?
    答:参见fbmem.c(LCD显示屏驱动)
    struct inode *inode = file->f_path.dentry->d_inode;
    再通过inode就可以获取设备号了!

案例:编写LED字符设备驱动,利用ioctl实现开关某个灯
    目前要求:将四个LED灯作为四个硬件个体,又由于他们的硬件特性是一致的
    所以他们共用一个驱动程序,驱动将来通过次设备号来区分LED硬件个体
分析:
硬件个数:4个
设备文件:4个:/dev/myled0,/dev/myled1,/dev/myled2,/dev/myled3
驱动程序:1个
cdev对象:1个
主设备号:1个
次设备号:4个:0/1/2/3
操作接口对象led_fops:1个,共用的,所以led_ioctl接口函数也是共用
inode对象:4个,inode0,inode1,inode2,inode3
file对象:4个,file0,file1,file2,file3
fd:4个,fd0,fd1,fd2,fd3
关系:
/dev/myled0->fd0->file0->inode0->i_rdev->主A,次0
/dev/myled1->fd1->file1->inode1->i_rdev->主A,次1
/dev/myled2->fd2->file2->inode2->i_rdev->主A,次2
/dev/myled3->fd3->file3->inode3->i_rdev->主A,次3
应用程序:
int fd0 = open("/dev/myled0");
ioctl(fd0, ...);

  1 //应用测试程序
  2 #include<stdio.h>
  3 #include<sys/types.h>
  4 #include<sys/stat.h>
  5 #include<fcntl.h>
  6 
  7 #define LED_ON    0x100001    //开灯命令
  8 #define LED_OFF   0x100002    //关灯命令
  9 
 10 int main(int argc,char *argv[]){
 11     int fd0, fd1, fd2, fd3;
 12 
 13 
 14     fd0 = open("/dev/myled0", O_RDWR);
 15     if(fd0 < 0){
 16         printf("设备打开失败.\n");
 17         return -1;
 18     }
 19     fd1 = open("/dev/myled1", O_RDWR);
 20     if(fd1 < 0){
 21         printf("设备打开失败.\n");
 22         return -1;
 23     }
 24     fd2 = open("/dev/myled2", O_RDWR);
 25     if(fd2 < 0){
 26         printf("设备打开失败.\n");
 27         return -1;
 28     }
 29     fd3 = open("/dev/myled3", O_RDWR);
 30     if(fd3 < 0){
 31         printf("设备打开失败.\n");
 32         return -1;
 33     }
 34 
 35 
 36     ioctl(fd0, LED_ON);
 37     sleep(1);
 38     ioctl(fd0, LED_OFF);
 39     sleep(1);
 40     ioctl(fd1, LED_ON);
 41     sleep(1);
 42     ioctl(fd1, LED_OFF);
 43     sleep(1);
 44     ioctl(fd2, LED_ON);
 45     sleep(1);
 46     ioctl(fd2, LED_OFF);
 47     sleep(1);
 48     ioctl(fd3, LED_ON);
 49     sleep(1);
 50     ioctl(fd3, LED_OFF);
 51     sleep(1);
 52 
 53     close(fd0);
 54     close(fd1);
 55     close(fd2);
 56     close(fd3);
 57     return 0;
 58 }


驱动:
led_ioctl(struct file *file, ...) {
    //file->file0
    struct inode *inode = file->f_path.dentry->d_inode; //inode0
    int minor = MINOR(inode->i_rdev); //获取次设备号0
    //通过次设备号区分硬件个体
    gpio_set_value(led_info[minor].gpio, 0);

应用程序:
int fd1 = open("/dev/myled1");
ioctl(fd1, ...);
驱动:
led_ioctl(struct file *file, ...) {
    //file->file1
    struct inode *inode = file->f_path.dentry->d_inode; //inode1
    int minor = MINOR(inode->i_rdev); //获取次设备号1
    //通过次设备号区分硬件个体
    gpio_set_value(led_info[minor].gpio);

  1 //驱动程序
  2 #include<linux/init.h>
  3 #include<linux/module.h>
  4 #include<linux/gpio.h>
  5 #include<mach/platform.h>
  6 #include<linux/fs.h>//struct file_operation声明
  7 #include<linux/cdev.h>//struct cdev声明
  8 #include<linux/uaccess.h>//copy_from_user声明
  9 #include<linux/device.h>//struct class/class_create等声明 
 10 
 11 //声明描述LED硬件信息的结构体
 12 struct led_resource{
 13     char* name;
 14     int gpio;
 15 };
 16 //结构体类型数组
 17 static struct led_resource led_info[]={
 18     {
 19         .name = "LED1",
 20         .gpio = PAD_GPIO_C + 12
 21     },
 22     {
 23         .name = "LED2",
 24         .gpio = PAD_GPIO_C + 7
 25     },
 26     {
 27         .name = "LED3",
 28         .gpio = PAD_GPIO_C + 11
 29     },
 30     {
 31         .name = "LED4",
 32         .gpio = PAD_GPIO_B + 26
 33     }
 34 };
 35 //定义设备号变量
 36 static dev_t dev;
 37 
 38 //定义硬件控制接口操作函数
 39 //调用关系:应用ioctl->c库ioctl->软中断->sys_ioctl->驱动led_ioctl
 40 //ioctl(fdx, LED_ON/LED_OFF)
 41 //fd0->file0->inode0->i_rdev->次设备号0
 42 //fd1->file1->inode1->i_rdev->次设备号1
 43 //fd0->file2->inode2->i_rdev->次设备号2
 44 //fd0->file3->inode3->i_rdev->次设备号3
 45 #define LED_ON   0x100001
 46 #define LED_OFF  0x100002
 47 static long led_ioctl(struct file *file, unsigned int cmd, unsigned long buf){
 48     //1.通过file获得对应的inode指针
 49     struct inode *inode = file->f_path.dentry->d_inode;
 50     //2.通过inode获取次设备号
 51     int minor = MINOR(inode->i_rdev);
 52     //结果:minor=0/1/2/3
 53     //3.解析命令cmd
 54     switch(cmd) {
 55         case LED_ON:
 56             gpio_set_value(led_info[minor].gpio, 0);
 57             printk("%s:打开第%d个灯\n", __func__, minor+1);
 58             break;
 59         case LED_OFF:
 60             gpio_set_value(led_info[minor].gpio, 1);
 61             printk("%s:关闭第%d个灯\n", __func__, minor+1);
 62             break;
 63         default:
 64             printk("命令不对.\n");
 65             return -1;
 66     }
 67     return 0;
 68 }
 69 
 70 //定义初始化硬件操作接口对象
 71 static struct file_operations led_fops = {
 72     .unlocked_ioctl = led_ioctl //控制接口
 73 };
 74 //定义字符设备驱动对象
 75 static struct cdev led_cdev;
 76 
 77 //定义设备类对象指针(幼苗)
 78 static struct class *cls;
 79 
 80 static int led_init(void){
 81     //1,申请GPIO资源
 82     //2,配置为输出,默认输出1,关灯
 83     int i;
 84     for(i = 0; i < ARRAY_SIZE(led_info); i++){
 85         gpio_request(led_info[i].gpio,led_info[i].name);
 86         gpio_direction_output(led_info[i].gpio,1);
 87     }
 88     //3,申请设备号
 89     alloc_chrdev_region(&dev, 0, 4, "tarena");//次设备号:0/1/2/3
 90     //4,初始化字符设备驱动对象,关联硬件操作接口对象
 91     cdev_init(&led_cdev, &led_fops);
 92     //5,向内核注册设备字符驱动对象,这样内核就有了一个真实的LED驱动并且提供了open,release接口
 93     //静静的在内容中等待应用程序来访问
 94     cdev_add(&led_cdev, dev, 4);
 95     //6.创建设备类对象(树枝)
 96     //结果是在:/sys/class/tarena1目录
 97     cls = class_create(THIS_MODULE, "tarena1");
 98     //7.创建设备文件:/dev/myled(苹果)
 99     device_create(cls, NULL, MKDEV(MAJOR(dev), 0), NULL, "myled0");
100     device_create(cls, NULL, MKDEV(MAJOR(dev), 1), NULL, "myled1");
101     device_create(cls, NULL, MKDEV(MAJOR(dev), 2), NULL, "myled2");
102     device_create(cls, NULL, MKDEV(MAJOR(dev), 3), NULL, "myled3");
103     return 0;
104 }
105 
106 static void led_exit(void){
107     int i;
108     //1.从内核卸载字符设备
109     cdev_del(&led_cdev);
110     //2.释放设备号
111     unregister_chrdev_region(dev, 4);
112     //3.关灯
113     //4.释放GPIO资源
114     for(i=0;i<ARRAY_SIZE(led_info);i++){
115         gpio_set_value(led_info[i].gpio, 1);
116         gpio_free(led_info[i].gpio);
117     }
118     //5.删除设备文件,摘苹果
119     device_destroy(cls, MKDEV(MAJOR(dev), 0));
120     device_destroy(cls, MKDEV(MAJOR(dev), 1));
121     device_destroy(cls, MKDEV(MAJOR(dev), 2));
122     device_destroy(cls, MKDEV(MAJOR(dev), 3));
123     //6.删除设备类对象,砍树枝
124     class_destroy(cls);
125 }
126 module_init(led_init);
127 module_exit(led_exit);
128 MODULE_LICENSE("GPL");

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux设备驱动程序是用于控制和管理硬件设备的软件模块。学习Linux设备驱动程序可以帮助开发人员理解和掌握Linux内核的工作原理,以及如何编写和调试设备驱动程序。 以下是一些学习Linux设备驱动程序笔记和建议: 1. 理解Linux设备模型:Linux设备模型是一种用于管理设备的框架,它提供了一种统一的方式来表示和操作设备学习Linux设备模型可以帮助你理解设备的注册、初始化和销毁过程。 2. 学习字符设备驱动程序:字符设备是一种以字节为单位进行读写的设备,如串口、终端等。学习字符设备驱动程序可以帮助你了解字符设备的打开、关闭、读写等操作,并学习如何实现设备文件的注册和操作。 3. 学习设备驱动程序:块设备是一种以块为单位进行读写的设备,如硬盘、闪存等。学习设备驱动程序可以帮助你了解块设备的分区、缓存、IO调度等操作,并学习如何实现块设备的注册和操作。 4. 学习中断处理:中断是设备向处理器发送信的一种机制,用于通知处理器设备的状态变化。学习中断处理可以帮助你了解中断的注册、处理和释放过程,并学习如何编写中断处理程序。 5. 学习设备驱动程序的调试技巧:设备驱动程序的调试是一个重要的技能,可以帮助你快速定位和解决问题。学习设备驱动程序的调试技巧可以帮助你理解和使用调试工具,如 printk、kprobe等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值