zoukankan      html  css  js  c++  java
  • 指定子设备号创建字符设备


    title: 指定子设备号创建字符设备
    tags: linux
    date: 2018/12/28 19:57:24
    toc: true

    指定子设备号字符设备

    流程

    内核中设备号分为主设备号和次设备号,以前注册字符设备驱动的时候,直接占用了主设备号包含了255个子设备号,也就是内核最多支持255个设备驱动(如果主设备号占据8位)

    在Linux2.6中内核可以指定主设备号和对应的子设备号给一个fileoperation,如下图所示

    mark

    流程如下:

    1. 确认需要得到几个子设备号使用

    2. 如果指定主设备号使用register_chrdev_region,当返回值小于0,表示注册失败

      dev_t devid;
      devid = MKDEV(major, 0);
      /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */
      register_chrdev_region(devid, HELLO_CNT, "hello");
      
    3. 如果需要系统分配主设备号,alloc_chrdev_region(&devid, 0, HELLO_CNT, "hello");当返回值小于0,表示注册失败

      /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */
      alloc_chrdev_region(&devid, 0, HELLO_CNT, "hello");
      major = MAJOR(devid);
      
    4. 这里使用 cdev管理驱动,并将file_operations结构体放入cdev-> ops

      cdev_init(&hello_cdev, &hello_fops);
      cdev_add(&hello_cdev, devid, HELLO_CNT);
      
      struct cdev {
             struct kobject    kobj;             // 内嵌的kobject对象 
             struct module   *owner;             //所属模块
             const struct file_operations  *ops; //操作方法结构体
             struct list_head  list; //与 cdev 对应的字符设备文件inode->i_devices 的链表头
             dev_t dev;               //起始设备编号,可以通过MAJOR(),MINOR()来提取主次设备号
             unsigned int count;               //连续注册的次设备号个数
      };
      
    5. 创建设备文件是与以前一样的,使用class_create创建类和class_device_create创建设备文件

    6. 卸载

      /*将系统中的cdev结构体删除掉*/
      void cdev_del(struct cdev *p); 
      /*注销字符设备*/
      void unregister_chrdev_region(dev_t from, unsigned count);
      

    实例程序

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <linux/irq.h>
    #include <asm/uaccess.h>
    #include <asm/irq.h>
    #include <asm/io.h>
    #include <asm/arch/regs-gpio.h>
    #include <asm/hardware.h>
    #include <linux/poll.h>
    #include <linux/cdev.h>
    
    /* 1. 确定主设备号 */
    static int major;
    
    static int hello_open(struct inode *inode, struct file *file)
    {
    	printk("hello_open
    ");
    	return 0;
    }
    
    static int hello2_open(struct inode *inode, struct file *file)
    {
    	printk("hello2_open
    ");
    	return 0;
    }
    
    /* 2. 构造file_operations */
    static struct file_operations hello_fops = {
    	.owner = THIS_MODULE,
    	.open  = hello_open,
    };
    
    static struct file_operations hello2_fops = {
    	.owner = THIS_MODULE,
    	.open  = hello2_open,
    };
    
    
    #define HELLO_CNT   2
    
    static struct cdev hello_cdev;
    static struct cdev hello2_cdev;
    static struct class *cls;
    
    static int hello_init(void)
    {
    	dev_t devid;
    	
    	/* 3. 告诉内核 */
    #if 0
    	major = register_chrdev(0, "hello", &hello_fops); /* (major,  0), (major, 1), ..., (major, 255)都对应hello_fops */
    #else
    	if (major) {
    		devid = MKDEV(major, 0);
    		register_chrdev_region(devid, HELLO_CNT, "hello");  /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */
    	} else {
    		alloc_chrdev_region(&devid, 0, HELLO_CNT, "hello"); /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */
    		major = MAJOR(devid);                     
    	}
    	
    	cdev_init(&hello_cdev, &hello_fops);
    	cdev_add(&hello_cdev, devid, HELLO_CNT);
    
    	devid = MKDEV(major, 2);
    	register_chrdev_region(devid, 1, "hello2");
    	cdev_init(&hello2_cdev, &hello2_fops);
    	cdev_add(&hello2_cdev, devid, 1);
    	
    #endif
    
    	cls = class_create(THIS_MODULE, "hello");
    	class_device_create(cls, NULL, MKDEV(major, 0), NULL, "hello0"); /* /dev/hello0 */
    	class_device_create(cls, NULL, MKDEV(major, 1), NULL, "hello1"); /* /dev/hello1 */
    	class_device_create(cls, NULL, MKDEV(major, 2), NULL, "hello2"); /* /dev/hello2 */
    	class_device_create(cls, NULL, MKDEV(major, 3), NULL, "hello3"); /* /dev/hello3 */
    	
    	
    	return 0;
    }
    
    static void hello_exit(void)
    {
    	class_device_destroy(cls, MKDEV(major, 0));
    	class_device_destroy(cls, MKDEV(major, 1));
    	class_device_destroy(cls, MKDEV(major, 2));
    	class_device_destroy(cls, MKDEV(major, 3));
    	class_destroy(cls);
    
    	cdev_del(&hello_cdev);
    	unregister_chrdev_region(MKDEV(major, 0), HELLO_CNT);
    
    	cdev_del(&hello2_cdev);
    	unregister_chrdev_region(MKDEV(major, 2), 1);
    }
    module_init(hello_init);
    module_exit(hello_exit);
    MODULE_LICENSE("GPL");
    

    测试

    查看到只是用了同一个主设备号

    cat  /proc/devices
    
    # cat /proc/devices
    Character devices:
    252 hello				#同一个主设备号
    252 hello2				#同一个主设备号
    

    查看设备文件

    # ls /dev/hello* -l
    crw-rw----    1 0        0        252,   0 Jan  1 04:18 /dev/hello0
    crw-rw----    1 0        0        252,   1 Jan  1 04:18 /dev/hello1
    crw-rw----    1 0        0        252,   2 Jan  1 04:18 /dev/hello2
    crw-rw----    1 0        0        252,   3 Jan  1 04:18 /dev/hello3
    
    驱动fileopration 次设备号
    hello_fops 0,1
    hello2_fops 2

    也就是对应设备文件的驱动如下

    设备文件
    /dev/hello0 hello_fops
    /dev/hello1 hello_fops
    /dev/hello2 hello2_fops
    /dev/hello3 没有

    使用测试文件去打开文件如下结果

    # ./test  /dev/hello0
    hello_open					#驱动1打开的
    can open /dev/hello0
    # ./test  /dev/hello1
    hello_open					#驱动1打开的
    can open /dev/hello1
    # ./test  /dev/hello2
    hello2_open					#驱动2打开的
    can open /dev/hello2
    # ./test  /dev/hello3
    can't open /dev/hello3		#无法打开
    
    

    测试程序如下

    //arm-linux-gcc -o test test.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    /*
     * test /dev/hello0
     */
    
    void print_usage(char *file)
    {
            printf("%s <dev>
    ", file);
    }
    
    int main(int argc, char **argv)
    {
            int fd;
            if (argc != 2)
            {
                    print_usage(argv[0]);
                    return 0;
            }
            fd = open(argv[1], O_RDWR);
            if (fd < 0)
                    printf("can't open %s
    ", argv[1]);
            else
                    printf("can open %s
    ", argv[1]);
    
            return 0;
    }
    
    
  • 相关阅读:
    T450的Fn lock
    移民,不应该是走投无路后的选择
    门槛低的行业看天赋,门槛高的行业看毅力
    个人是时代的一朵浪花
    转载:XPath基本语法
    爪哇国新游记之三十四----Dom4j的XPath操作
    常去的论坛今天两个传统行业的坛友要下岗了
    异常中要了解的Throwable类中的几个方法
    感觉JVM的默认异常处理不够好,既然不好那我们就自己来处理异常呗!那么如何自己处理异常呢?
    JVM对异常的默认处理方案
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10192518.html
Copyright © 2011-2022 走看看