混杂设备实现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;
}
上面可以知道混杂设备都使用同一个主设备号10。
二、在接下来就将之前的led点灯驱动变身一下
1、先看驱动代码
#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;
}
简单的led闪烁灯就实现了。
本文介绍了Linux混杂设备驱动编程如何简化LED点灯的字符驱动程序开发过程。通过混杂设备注册函数,可以轻松地完成主次设备号注册、设备结点创建以及自身注册为字符驱动的步骤。实例代码展示了如何将原始LED点灯驱动转化为混杂设备驱动,并通过 ioctl 函数与应用程序进行交互,实现了简单的LED闪烁效果。
3425

被折叠的 条评论
为什么被折叠?



