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

混杂设备

在设备号比较紧张的情况下,我们可以采用混杂设备,可用将一些之间没有联系的设备使用同一个主设备号,而使用不同的次设备号。主设备号通常为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 <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <mach/regs-gpio.h> // include <mahc/gpio-nrs.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <asm/uaccess.h>

// 与IO端口有关
#include <linux/io.h>				
#include <linux/ioport.h>

#include <linux/miscdevice.h>

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\n");
			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\n", 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!\n");
}



module_init(s3c2440_leds_init);
module_exit(s3c2440_leds_exit);


MODULE_AUTHOR("S");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("s3c2440 led driver");

猜你喜欢

转载自blog.csdn.net/santapasserby/article/details/81660264
今日推荐