一、使用混杂设备驱动,设计字符设备设备驱动程序的流程
1. 定义一个混杂设备2. 定义混杂的file_operations和其接口函数
3. 将物理地址申请为内核的一个资源,request_mem_region()
4. 通过ioremap()函数得到物理地址对应的虚拟地址
5. 注册混杂设备 --->已经创建了class和device
6. 访问虚拟地址,控制硬件。
二、混杂设备结构体
混杂设备结构体在内核源码目录下include/linux/miscdevice.h下定义:struct miscdevice {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const char *nodename;
mode_t mode;
};
成员说明:
int minor; --->次设备号,可以指定,也可以动态分配:MISC_DYNAMIC_MINOR
const char *name; --->混杂设备的名字,也是设备文件的名字
const struct file_operations *fops; --->文件操作集
struct list_head list; --->内核链表
struct device *parent; --->父设备
struct device *this_device; --->设备
const char *nodename;
mode_t mode;
三、混杂设备的注册和注销函数
混杂设备的注册函数:函数原型:int misc_register(struct miscdevice *misc);
参数说明:
struct miscdevice *misc:混杂设备结构体
返回值:
成功返回0,失败返回一个负的错误码。
混杂设备的注销函数:
函数原型:int misc_deregister(struct miscdevice *misc);
参数说明:
struct miscdevice *misc:混杂设备结构体
返回值:
成功返回0,失败返回一个负的错误码。
四、查看混杂设备
混杂设备主设备号在/proc/devices下,如:cat /proc/devices
混杂设备在/proc/misc下,如:
cat /proc/misc
五、混杂设备驱动设计实例
驱动程序(led_dev.c):
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/miscdevice.h> //混杂设备相关定义
static struct resource * led_reg_res;
static void __iomem *gpj2_con_val;
static void __iomem *gpj2_dat_val;
static ssize_t led_dev_write(struct file *flip, const char __user *buf, size_t size, loff_t *ops)
{
int ret;
char kbuf[1];
if(size != 2)
return -EINVAL;
ret = copy_from_user(kbuf, buf, size);
if(ret != 0)
return -EFAULT;
if(kbuf[0]!=0 && kbuf[0]!=1){
printk("cmd error\n");
return -EINVAL;
}
if(kbuf[1]<0 || kbuf[1]>4){
printk("args error\n");
return -EINVAL;
}
if(kbuf[0]==0)
writel(readl(gpj2_dat_val) | (1<<(kbuf[1]-1)),gpj2_dat_val);//led off
else
writel(readl(gpj2_dat_val) & ~(1<<(kbuf[1]-1)),gpj2_dat_val); //led on
return size;
}
//2. 定义led_dev的文件操作集并初始化
static struct file_operations led_fops = {
.owner = THIS_MODULE,
//.open = led_dev_open,
.write = led_dev_write,
//.release = led_dev_release,
};
//1. 定义一个混杂设备的cdev
static struct miscdevice led_misc_cdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "led_drv",
.fops = &led_fops,
};
static int __init led_dev_init(void)
{
int ret = -1;
//3. 将物理地址申请为内核的一个资源
led_reg_res = request_mem_region(0xE0200280, 8, "GPJ2_LED");
if(led_reg_res == NULL){
printk("request mem region error\n");
ret = -EBUSY;
return ret;
}
//4. 将物理地址映射为虚拟地址
gpj2_con_val = ioremap(0xE0200280, 8);
if(gpj2_con_val == NULL){
printk("ioremap error\n");
ret = -EFAULT;
goto ioremap_error;
}
gpj2_dat_val = gpj2_con_val + 4;
//5. 注册混杂设备到内核
ret = misc_register(&led_misc_cdev);
if(ret < 0){
printk("misc register is error\n");
goto misc_reg_error;
}
writel((readl(gpj2_con_val) & (~0xFFFF) )| 0x1111,gpj2_con_val);
writel(readl(gpj2_dat_val) | 0xF,gpj2_dat_val);
printk("led_dev init\n");
return 0;
misc_reg_error:
iounmap(gpj2_con_val);
ioremap_error:
release_mem_region(0xE0200280,8);
return ret;
}
static void __exit led_dev_exit(void)
{
release_mem_region(0xE0200280,8);
iounmap(gpj2_con_val);
misc_deregister(&led_misc_cdev);
printk("led_dev exit\n");
}
module_init(led_dev_init);
module_exit(led_dev_exit);
MODULE_LICENSE("GPL");
编译文件(Makefile):
obj-m += led_dev.o
KERN_DIR=/home/gec/linux-2.6.35.7-gec-v3.0-gt110
modules:
$(MAKE) -C $(KERN_DIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERN_DIR) M=$(PWD) modules clean
测试文件(test.c):
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
int main(void)
{
int fd;
char buf[2];
fd = open("/dev/led_drv", O_WRONLY);
if(fd < 0){
perror("open led_drv");
return -1;
}
while(1){
buf[0]=1; buf[1]=2;//D2 亮
write(fd,buf,2);
sleep(1);
buf[0]=0; buf[1]=2;//D2 灭
write(fd,buf,2);
sleep(1);
}
close(fd);
return 0;
}