异想天开

What's the true meaning of light, Could you tell me why

linux字符设备驱动

日期:2015-06-11 20:21:24
  
最后更新日期:2017-03-11 21:17:56
一个字符设备驱动的例子
[code lang="cpp"]
#include<linux/module.h>
#include<linux/init.h>
#include<linux/vmalloc.h>
#include<linux/fs.h>
#include<linux/cdev.h>
#include<asm/uaccess.h>

MODULE_LICENSE("GPL");

#define MEM_MALLOC_SIZE 4096
#define MEM_MAJOR 667
#define MEM_MINOR 0

char *mem_spvm;
struct cdev *mem_cdev;
struct class *mem_class;

static int __init cdev_add_del_init(void);
static int __init cdev_add_del_exit(void);

static int mem_open(struct inode *ind, struct file *filp);
static int mem_release(struct inode *ind, struct file *filp);
static int mem_read(struct file *filp, char __user *buf, size_t size, loff_t *fpos);
static int mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *fpos);

struct file_operations mem_fops = {
.open = mem_open,
.release = mem_release,
.read = mem_read,
.write = mem_write,
};

int __init cdev_add_del_init(void)
{
int res;
printk("<0>into the cdev_add_del_init\n");
dev_t devno = MKDEV(MEM_MAJOR,MEM_MINOR);
mem_spvm = (char *)vmalloc(MEM_MALLOC_SIZE);
if (mem_spvm==NULL){
printk("<0>vmalloc fail\n");
return 0;
} else {
printk("<0>vmalloc successfully! addr=0x%x\n",(unsigned int)mem_spvm);
}

mem_cdev = cdev_alloc();
if (mem_cdev==NULL){
printk("<0>cdev_alloc failed\n");
return 0;
}
cdev_init(mem_cdev,&mem_fops);
res=cdev_add(mem_cdev,devno,1);
if (res){
cdev_del(mem_cdev);
mem_cdev=NULL;
printk("<0>cdev_add Error\n");
} else {
printk("<0>cdev add OK\n");
}
printk("<0>out the cdev_add_del_init\n");
return 0;
}

int __exit cdev_add_del_exit(void){
printk("<0>init cdev_add_del_exit\n");
if (mem_cdev!=NULL){
cdev_del(mem_cdev);
}
printk("<0>cdev del OK\n");
if (mem_spvm!=NULL){
vfree(mem_spvm);
}
printk("<0>vfree OK!\n");
printk("<0>out cdev_add_del_exit\n");
return 0;
}

int mem_open(struct inode *ind, struct file *filp)
{
printk("<0>into mem_open\n");
try_module_get(THIS_MODULE);
printk("<0>out mem_open\n");
return 0;
}

int mem_release(struct inode *ind, struct file *filp)
{
printk("<0>into mem_release\n");
module_put(THIS_MODULE);
printk("<0>out mem_release\n");
return 0;
}

int mem_read(struct file *filp, char __user *buf, size_t size, loff_t *fpos)
{
int res=-1;
char *tmp;
printk("<0> copy data to the user space\n");
tmp = mem_spvm;
if (size>MEM_MALLOC_SIZE){
size = MEM_MALLOC_SIZE;
}
if (tmp!=NULL){
res = copy_to_user(buf,tmp,size);
}
if (res==0){
printk("<0>copy_to_user:%s\n",tmp);
return size;
} else {
printk("<0>copy_to_user fail:%d\n",res);
}
return 0;
}

int mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *fpos)
{
int res=-1;
char *tmp;
printk("<0>read data from the user space\n");
tmp=mem_spvm;
if (size>MEM_MALLOC_SIZE){
size=MEM_MALLOC_SIZE;
}
if (tmp!=NULL){
res=copy_from_user(tmp,buf,size);
}
if (res==0){
printk("<0>read data success and the data is:%s\n",tmp);
return 0;
} else {
printk("<0>read data fail\n");
}
return 0;
}
module_init(cdev_add_del_init);
module_exit(cdev_add_del_exit);
[/code]
所犯的错误:
1.cdev_add_del_exit前是用__exit修饰,而不是__init,代码没抄仔细
2.设备号注意不要与系统的有冲突

编译模板后执行:
[code lang="cpp"]
sudo mknod /dev/my_char_dev c 667 0
[/code]
用户态测试程序:
[code lang="cpp"]
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>

int main(){
int fd,cnt;
char buf[256];
printf("char device testing\n");
fd = open("/dev/my_char_dev",O_RDWR);
if (fd==0){
printf("the char dev file cannot be openned \n");
return 1;
}
printf("input the data for kernel: ");
scanf("%s",buf);
cnt = write(fd,buf,256);
if (cnt<0){
printf("write error: %d\n",cnt);
return 0;
}
cnt = read(fd,buf,256);
if (cnt>0){
printf("read data from kernel: %s \n",buf);
} else {
printf("read data error\n");
}
close(fd);
return 0;
}
[/code]
设备驱动相关其他函数说明:
1.向linux内核系统添加设备类及逻辑设备的操作其实是为了生成相关设备的设备文件,如函数class_create()和函数device_create()完成的作用与命令mknod /dev/my_char_dev c 667 0功能相同,都是创建设备文件/dev/my_char_dev。
2.函数register_chrdev()在实现过程中调用了函数cdev_aloc()和函数cdev_add(),完成实现字符设备的动态申请和注册,这样的封装,使register_chrdev()更加通用,一般编程者都是使用函数register_chrdev()注册字符设备的。
3.函数unregister_chrdev()实现过程中调用了函数cdev_del()实现字符设备的删除,即对cdev_del函数的封装,unregister_chrdev和register_chrdev是成对使用的。
4.函数__class_create()实现了对__class_register()的封装,首先函数__class_create()动态创建一个设备类,然后调用函数__class_register()将其注册进内核。而函数__class_register()的设备类不是动态创建的,是通过参数传递来的,增加了与编程者的交互性,同样也增加了不安全性。对于内核编程安全是十分重要的,所以函数__class_create()使用率更高,并且比函数__class_register()的作用更强。
5.函数class_destory()实现了对函数class_unregister()的封装,并且增加了安全性检查,所以其通用性比函数class_unregister()更强。函数class_destory()与函数__class_create()成对出现,函数class_unregister()与函数__class_register()也是成对出现。
6.函数device_create()实现了对函数device_register()的封装,函数device_register()实现了对函数device_initialize(),device_add()的封装。函数device_create()能够实现动态创建一个逻辑设备,然后对其初始化,并将其加入到linux内核系统中,其作用比device_register(),device_initialize(),device_add()三者都强,安全性好,通用性高。
7.函数device_destory()实现了对函数device_unregister()的封装,函数device_unregister()实现了对device_del()的封装,并且增加了安全性检查,在删除之前需要对其进行检查,这样函数device_destory()安全性和通用性更高。函数device_create()与函数device_destory()成对出现,函数device_register()与函数device_unregister配对出现,函数device_add()与函数device_del()也是形影不离。

参考:
1.c结构体c99指定成员赋值
2.例子来源于《linux内核API完全手册》
3.宋宝华 linux设备驱动开发详解