混杂设备实现led点灯
一、为了简单方便操作,linux提供了混杂设备驱动编程这一方法,混杂设备驱动程序是指那些简单的字符驱动程序,它们拥有一些相同的特性,内核将这些相同的特性抽象至一个API(drivers/char/misc.c)中。由此可见混杂设备在某种程度上来说也是属于字符驱动的。这能够大大的简化我们字符驱动的编程工作量。
首先来看下我们的字符驱动程序会做什么事情:
1、 通过alloc_chrdev_region来注册主次设备号
2、 使用device_creat来创建设备结点,当然,如果实验的时候你是可以手动创建设备结点
3、 使用cdev_init和cdev_add函数来将自身注册为字符驱动
但是我们使用的混杂设备的功能又是什么呢,具有什么优势呢?
上面三个步骤如果使用混杂设备来做的话,那么将会变得相当简单
1、 声明结构体
static struct miscdevice led_dev = { .minor = MINOR_NUM, .name = "s3c6410_led", //这个名字含义:挂载驱动后将会自动在/dev目录下面生成s3c6410_led设备文件,这归功于混杂设备。 .fops = &led_fops, };
直接使用混杂设备注册函数
misc_deregister(&led_dev);
至于混杂设备注册函数里面做了什么,我想大家都能够猜到,必然也是做了上面的事情,只不过我们这里偷了个懒,使用linux内核给我们提供的api罢了。
还是将注册代码贴上来
int misc_register(struct miscdevice * misc) { struct miscdevice *c; dev_t dev; int err = 0; INIT_LIST_HEAD(&misc->list); mutex_lock(&misc_mtx); list_for_each_entry(c, &misc_list, list) { if (c->minor == misc->minor) { mutex_unlock(&misc_mtx); return -EBUSY; } } if (misc->minor == MISC_DYNAMIC_MINOR) { int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS); if (i >= DYNAMIC_MINORS) { mutex_unlock(&misc_mtx); return -EBUSY; } misc->minor = DYNAMIC_MINORS - i - 1; set_bit(i, misc_minors); } dev = MKDEV(MISC_MAJOR, misc->minor); misc->this_device = device_create(misc_class, misc->parent, dev, misc, "%s", misc->name); if (IS_ERR(misc->this_device)) { int i = DYNAMIC_MINORS - misc->minor - 1; if (i < DYNAMIC_MINORS && i >= 0) clear_bit(i, misc_minors); err = PTR_ERR(misc->this_device); goto out; } /* * Add it to the front, so that later devices can "override" * earlier defaults */ list_add(&misc->list, &misc_list); out: mutex_unlock(&misc_mtx); return err; }
#include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/fs.h> #include <asm/io.h> #include <linux/ioport.h> #include <linux/miscdevice.h> #define GPIOM_BASE 0x7F008820 #define MINOR_NUM 1 unsigned long VA; unsigned long gpmdat,gpmpud,gpmcon; /* open函数完成的功能:初始化gpio为输出方向 */ int s3c6410_led_open(struct inode *inodep, struct file *filp) { unsigned tmp; tmp = ioread32(gpmcon); printk("tmp = %x ... \n",tmp); tmp &= (~0xFFFF); tmp |= (0x1111); iowrite32(tmp,gpmcon); printk("s3c6410_led_open ... \n"); return 0; } long s3c6410_led_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) { unsigned tmp; switch(cmd){ case 0: tmp = ioread32(gpmdat); tmp &= 0x10; iowrite32(tmp,gpmdat); break; case 1: tmp = ioread32(gpmdat); tmp |= 0x1f; iowrite32(tmp,gpmdat); break; default: break; } printk("s3c6410_led_ioctl ... \n"); return 0; } int s3c6410_led_release(struct inode *inodep,struct file *filp) { printk("s3c6410_led_release ... \n"); return 0; } static struct file_operations led_fops = { .owner = THIS_MODULE, .unlocked_ioctl = s3c6410_led_ioctl, .open = s3c6410_led_open, .release = s3c6410_led_release, }; static struct miscdevice led_dev = { .minor = MINOR_NUM, .name = "s3c6410_led", //这个名字含义:挂载驱动后将会自动在/dev目录下面生成s3c6410_led设备文件,这归功于混杂设备。 .fops = &led_fops, }; static int __init led_init(void) { int ret = 0; VA = (unsigned long)ioremap(GPIOM_BASE,0x0c); gpmcon = VA + 0x00; gpmdat = VA + 0x04; gpmpud = VA + 0x08; ret = misc_register(&led_dev); printk("led_init ...\n"); return ret; } static void __exit led_exit(void) { misc_deregister(&led_dev); printk("led_exit ...\n"); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL");
其实代码和led1的版本没什么本质的区别,底层操作还是一样的,只是运用了一些小手段使之能够和应用程序打交道ioctl
这里我没有使用魔数的方法来定义CMD。
misc_register和misc_deregister是成对出现的
2、再看测试程序
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> int main(int argc,char **argv) { int ret = 0; int i = 0; int fd = open("/dev/s3c6410_led",2,777); if(fd < 0) { printf("open led dev failed \n"); } printf("open led dev success ! \n"); for(i = 0; i < 10; i ++) { ioctl(fd,0); sleep(1); ioctl(fd,1); sleep(1); } return ret; }