平台:S5PV210
杂项设备的好处:
有了字符型设备后,为什么要用杂项设备?
1.节省主设备号,所有杂项设备的主设备号都是10
2.杂项设备开发起来比字符型设备简单
开发步骤:
1.定义1个杂项设备
2.定义杂项设备的文件操作集
3.申请物理内存区
4.获取相应的虚拟地址
5.注册混杂设备
6.示例代码
1.定义杂项设备
static struct miscdevice led_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "misc_led",
.fops = &fops,
};
如上代码:其中.minor代表动态生成次设备号,不用改。
.name表示这个混杂设备文件结点的名字,用户程序用open在 /dev 目录下打开文件的就是 .name 的值。
.fops代表杂项设备的文件操作集
2.定义文件操作集
static struct file_operations fops =
{
.owner = THIS_MODULE,
.open = misc_open,
.write = misc_write,
};
文件操作集表示了杂项设备用了哪些系统调用,如上代码就是用了 open 和 write。
第一个参数不用变,.owner一定写THIS_MODULE,用于初始化。
3.申请物理内存区
static struct resource *res = NULL;
res = request_mem_region(0xE0200280,8,"LED");
上面表示,控制LED的物理地址为0XE0200280开始,8个字节,并把这段内存起名为LED
4.获取相应虚拟地址。
操作系统操作的都是虚拟地址。
static unsigned int va = NULL;
va = ioremap(0xE02000A0,8);
va就是物理地址映射的虚拟地址的起始地址。
5.注册杂项设备
int ret;
ret = misc_register(&led_misc);
if(ret < 0)
{
printk("misc register is error\n");
return -1;
}
misc_register()的参数就是第一步定义的杂项设备变量。
6.示例代码(LED驱动)
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
static struct resource* res = NULL;
static unsigned int *GPJ2CON_VA = NULL;
static unsigned int *GPJ2DAT_VA = NULL;
static char wbuf[1];
static ssize_t misc_write(struct file *f, const char __user*buf,
size_t len, loff_t *t)
{
copy_from_user(wbuf,buf,len);
if(wbuf[0] == '0')
{
*GPJ2DAT_VA &=~0xf;
}
if(wbuf[0] == '1')
{
*GPJ2DAT_VA &=~0xf;
*GPJ2DAT_VA |=0xf;
}
return 0;
}
/*2.定义文件操作集*/
static struct file_operations fops={
.owner = THIS_MODULE,
.write = misc_write,
};
/*1.定义misc杂项设备变量*/
static struct miscdevice misc_led={
.minor = MISC_DYNAMIC_MINOR,
.name = "led_misc",
.fops = &fops,
};
static int __init misc_init(void)
{
int ret;
/*3.申请物理内存*/
res = request_mem_region(0xe0200280,8,"LED");
if(res == NULL)
{
printk("failed to request_mem_region\n");
goto failed_request_mem;
}
/*4.获取虚拟地址*/
GPJ2CON_VA = ioremap(0xe0200280,8);
if(GPJ2CON_VA == NULL)
{
printk("failed to ioremap\n");
goto failed_ioremap;
}
GPJ2DAT_VA = GPJ2CON_VA + 1;
/*5注册杂项设备*/
ret = misc_register(&misc_led);
if(ret<0)
{
printk("failed to register misc\n");
goto failed_register;
}
printk("init completed\n");
return 0;
failed_register:
iounmap(GPJ2CON_VA);
failed_ioremap:
release_mem_region(0xe0200280,8);
failed_request_mem:
return -1;
}
static void __exit misc_exit(void)
{
iounmap(GPJ2CON_VA);
release_mem_region(0xe0200280,8);
misc_deregister(&misc_led);
}
module_init(misc_init);
module_exit(misc_exit);
MODULE_LICENSE("GPL");
用户程序测试代码:
#include <stdio.h>
#include <fcntl.h>
int main()
{
char buf[1];
buf[0]='0';
int fd = open("/dev/led_misc",O_WRONLY);
if(fd<0)
{
perror("failed to open");
return -1;
}
while(1)
{
write(fd,buf,1);
sleep(1);
buf[0] = '1';
write(fd,buf,1);
sleep(1);
buf[0] = '0';
}
return 0;
}