混杂设备
在设备号比较紧张的情况下,我们可以采用混杂设备,可用将一些之间没有联系的设备使用同一个主设备号,而使用不同的次设备号。主设备号通常为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");