【嵌入式Linux驱动程序-基础篇】- 混杂设备

2019-07-13 02:03发布

混杂设备

在设备号比较紧张的情况下,我们可以采用混杂设备,可用将一些之间没有联系的设备使用同一个主设备号,而使用不同的次设备号。主设备号通常为10。混杂设备使用miscdevice结构体来表示,如下所示: struct miscdevice{ int minor; // 次设备号 const char *name; // 混杂设备名称 const struct file_operations *fops; // 设备的操作方法,与字符设备相同 struct list_head_list; // 混杂设备的链表 struct device *parent; // 指向父设备 struct device *this_device; // 指向当前设备结构体 };
【注】可能有部分次设备号已经宏定义过,在头文件miscdevcie.h中可以查看。当然如果我们不想手动分配一个次设备号,可用将次设备号设为255,内核将会自动帮我们分配次设备号。在miscdevice.h中,255用MISC_DYNAMIC_MINOR表示。 混杂设备注册成功后,可在/proc/misc文件中查看混杂设备的次设备号。   1 混杂设备的注册和注销 驱动程序中需要对混杂设备进行注册和注销,内核提供了如下的接口:

1. 注册函数misc_register()

int misc_register(struct miscdevice *misc); 该函数内部检查次设备号是否合法,如果次设备号被占用,则返回设备忙状态。如果miscdevice的成员minor为255,则尝试动态申请一个次设备号。当此设备号可用时,函数会将混杂设备注册到内核的设备模型中。

2. 注销函数misc_deregister()

int misc_deregister(struct miscdevice *misc); 参考代码如下: #include #include #include #include #include #include // include #include #include #include // 与IO端口有关 #include #include #include volatile unsigned long *GPBCON, *GPBDAT, *GPBUP; #define GPIO_REG_START_ADDR 0x56000010 unsigned long *led_iomem; #define LED_MAGIC 'k' #define IOCTL_LED_ON _IOW(LED_MAGIC, 1, int) #define IOCTL_LED_OFF _IOW(LED_MAGIC, 2, int) #define IOCTL_LED_RUN _IOW(LED_MAGIC, 3, int) #define IOCTL_LED_SHINE _IOW(LED_MAGIC, 4, int) #define IOCTL_LED_ALLON _IOW(LED_MAGIC, 5, int) #define IOCTL_LED_ALLOFF _IOW(LED_MAGIC, 6, int) static unsigned long led_table[] = { S3C2410_GPB(5), S3C2410_GPB(6), S3C2410_GPB(7), S3C2410_GPB(8), }; void leds_all_on(void) { int i; for(i=0; i < 4; i++) { s3c2410_gpio_setpin(led_table[i], 0); } } void leds_all_off(void) { int i; for(i = 0; i < 4; i++) { s3c2410_gpio_setpin(led_table[i], 1); } } static int s3c2440_leds_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { unsigned int data; if(__get_user(data, (unsigned int __user *) arg)) return -EFAULT; switch(cmd) { case IOCTL_LED_ON: /* s3c2410_gpio_setpin(led_table[data], 0); */ outl(inl((unsigned long)(led_iomem+4)) & ~(1 <<5 | 1 << 6 | 1 <<7 | 1<< 8), (unsigned long)(led_iomem)+4); // *GPBDAT &= ~(1 <<5 | 1 << 6 | 1 <<7 | 1<< 8); return 0; case IOCTL_LED_OFF: /* s3c2410_gpio_setpin(led_table[data], 1); */ outl(inl((unsigned long)(led_iomem)+4) | 1 <<5 | 1 << 6 | 1 <<7 | 1<< 8, (unsigned long)(led_iomem)+4); // *GPBDAT |= (1 <<5 | 1 << 6 | 1 <<7 | 1<< 8); return 1; case IOCTL_LED_RUN:{ int i,j; for(i = 0; i < data; i++) { for(j = 0; j < 4; j++) { s3c2410_gpio_setpin(led_table[j], 0); mdelay(400); s3c2410_gpio_setpin(led_table[j], 1); mdelay(400); } } return 0; } case IOCTL_LED_SHINE: { int i, j; leds_all_off(); printk("IOCTL_LED_SHINE "); for(i = 0; i < data; i++) { for(j = 0; j < 4; j++) { s3c2410_gpio_setpin(led_table[j], 0); } mdelay(400); for(j = 0; j < 4; j++) { s3c2410_gpio_setpin(led_table[j], 1); } mdelay(400); } return 0; } case IOCTL_LED_ALLON: leds_all_on(); return 0; case IOCTL_LED_ALLOFF: leds_all_off(); return 0; default: return -EINVAL; } } static int s3c2440_leds_open(struct inode *inode, struct file *file) { /* int i; for(i = 0; i < 4; i ++) { s3c2410_gpio_cfgpin(led_table[i], S3C2410_GPIO_OUTPUT); } */ outl(inl((unsigned long)led_iomem) | 1 <<10 | 1 << 12 | 1 <<14 | 1<< 16, (unsigned long)led_iomem); // 配置LED GPIO outl(inl((unsigned long)(led_iomem)+4) | 1 <<5 | 1 << 6 | 1 <<7 | 1<< 8, (unsigned long)(led_iomem)+4); // LED_GPIO 默认高电平 outl(inl((unsigned long)(led_iomem)+8) | 1 <<5 | 1 << 6 | 1 <<7 | 1<< 8, (unsigned long)(led_iomem)+8); // LED_GPIO 上拉 // GPBCON = (unsigned long *)((unsigned long)led_iomem + 0x00);//指定需要操作的三个寄存器的地址 // GPBDAT = (unsigned long *)((unsigned long) led_iomem + 0x04); // GPBUP = (unsigned long *)((unsigned long) led_iomem + 0x08); // *GPBCON |= (1 << 10)|(1<<12)|(1<<14)|(1<<16); //output 输出模式 // *GPBDAT |= (1 <<5 | 1 << 6 | 1 <<7 | 1<< 8); // *GPBUP |= (1 <<5 | 1 << 6 | 1 <<7 | 1<< 8); //禁止上拉电阻 return 0; } static struct file_operations s3c2440_leds_fops = { .owner = THIS_MODULE, .open = s3c2440_leds_open, .ioctl = s3c2440_leds_ioctl, }; static struct miscdevice misc_led ={ .minor = MISC_DYNAMIC_MINOR, // 动态分配次设备号,部分已占用的次设备号在miscdencie.h有宏定义 .name = "led", .fops = &s3c2440_leds_fops, }; static int __init s3c2440_leds_init(void) { int result = 0; if(!request_region(GPIO_REG_START_ADDR, 3*4, "LED")) // 请求分配I/O端口 { result = -EBUSY; // 请求失败 printk(KERN_WARNING "Fail to request region at %x ", GPIO_REG_START_ADDR); goto err_map; } led_iomem = ioremap(GPIO_REG_START_ADDR, 3*4); // 地址映射 result = misc_register(&misc_led); // 注册混杂设备 if(result) // misc_register不为0,则表示注册失败 { printk(KERN_WARNING "register misc device failed!"); goto err_register_region; } return 0; err_map: err_register_region: return result; } static void __exit s3c2440_leds_exit(void) { misc_deregister(&misc_led); iounmap(led_iomem); release_region(GPIO_REG_START_ADDR, 3*4); printk("led device unistalled! "); } module_init(s3c2440_leds_init); module_exit(s3c2440_leds_exit); MODULE_AUTHOR("S"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("s3c2440 led driver");