Linux字符设备驱动
2014-11-28 11:18
141 查看
Linux2.6内核中使用cdev结构体描述字符设备:
cdev结构体的dev_t定义了设备号,32位。高12位为主设备号,低20位为次设备号。
下列宏可从dev_t获得主、次设备号:
通过主、次设备号生成dev_t:
一组函数操作cdev结构体:
调用cdev_add()前,先调用register_chrdev_region()或alloc_chrdev_region():
cdev_del()后,unregister_chrdev_region()应被调用以释放设备号:
file_operations结构:
Linux字符设备驱动函数模板:
其他说明:
内核空间和用户空间的内存不能直接访问,需借助copy_from_user()和copy_to_user():
上述函数返回不能被复制的字节数。若完全复制成功,返回0.
若要复制的内存是简单类型,如char、int、long等,则可以使用简单的put_user()和get_user():
读和写函数中的__user是一个宏,表明其后的指针指向用户空间:
struct cdev |
{ |
struct kobject kobj; // 内嵌的kobject对象,描述设备引用计数 |
struct module *owner; // 所属模块,一般赋值为THIS_MODULE |
struct file_operations *ops; // 文件操作结构体 |
struct list_head list; |
dev_t dev; // 设备号 |
unsigned int count; |
}; |
下列宏可从dev_t获得主、次设备号:
MAJOR(dev_t dev) |
MINOR(dev_t dev) |
MKDEV(int major, int minor) |
一组函数操作cdev结构体:
void cdev_init(struct cdev *, struct file_operations *); // 初始化cdev成员,建立cdev和file_operations之间的连接 |
struct cdev *cdev_alloc(void); // 动态申请一个cdev内存 |
void cdev_put(struct cdev *p); // 减少模块的引用计数,释放结构体空间 |
int cdev_add(struct cdev *, dev_t, unsigned); // 向系统添加一个dev,完成字符设备的注册,常用于模块加载函数中 |
void cdev_del(struct cdev *); // 向系统删除一个dev,完成字符设备的注销,常用于模块卸载函数中 |
int register_chrdev_region(dev_t from, unsigned count, const char *name); // 用于已知起始设备号 |
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name); // 设备号未知,向系统动态申请未占用设备号放入dev |
void unregister_chrdev_region(dev_t from, unsigned count); |
1 | struct file_operations { |
2 | struct module *owner; |
3 | loff_t (*llseek) (struct file *, loff_t, int); |
4 | ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); |
5 | ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); |
6 | ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); |
7 | ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); |
8 | int (*readdir) (struct file *, void *, filldir_t); |
9 | unsigned int (*poll) (struct file *, struct poll_table_struct *); |
10 | int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); |
11 | long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); |
12 | long (*compat_ioctl) (struct file *, unsigned int, unsigned long); |
13 | int (*mmap) (struct file *, struct vm_area_struct *); |
14 | int (*open) (struct inode *, struct file *); |
15 | int (*flush) (struct file *, fl_owner_t id); |
16 | int (*release) (struct inode *, struct file *); |
17 | int (*fsync) (struct file *, int datasync); |
18 | int (*aio_fsync) (struct kiocb *, int datasync); |
19 | int (*fasync) (int, struct file *, int); |
20 | int (*lock) (struct file *, int, struct file_lock *); |
21 | ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); |
22 | unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); |
23 | int (*check_flags)(int); |
24 | int (*flock) (struct file *, int, struct file_lock *); |
25 | ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); |
26 | ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); |
27 | int (*setlease)(struct file *, long, struct file_lock **); |
28 | }; |
// 设备结构体 |
struct xxx_dev_t |
{ |
struct cdev cdev; |
... |
} |
// 设备驱动模块加载函数 |
static int __init xxx_init(void) |
{ |
... |
cdev_init(&xxx_cdev.cdev, &xxx_fops); // 初始化dev |
xxx_dev.cdev.owner = THIS_MODULE; |
// |
if(xxx_major) |
{ |
register_chrdev_region(xxx_dev_no, 1, DEV_NAME); |
} |
else |
{ |
alloc_chrdev_region(&xxx_dev_no, 0, 1, DEV_NAME); |
} |
ret = cdev_add(&xxx_dev.cdev, xxx_dev_no, 1); // 注册设备 |
... |
} |
// 设备驱动模块卸载函数 |
static void __exit xxx_exit(void) |
{ |
unregister_chrdev_region(xxx_dev_no, 1); // 释放占用的设备号 |
cdev_del(&xxx_dev.cdev); // 注销设备 |
} |
// 读设备 |
// filp是文件结构指针,buf是用户空间的内存地址,该地址空间不能直接读写,count是要读的字节数,f_pos是读的位置相对于文件开头的偏移 |
ssize_t xxx_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos) |
{ |
... |
copy_to_user(buf, ..., ...); |
... |
} |
// 写设备 |
ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos) |
{ |
... |
copy_from_user(..., buf, ...); |
... |
} |
// ioctl函数 |
int xxx_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) |
{ |
... |
switch(cmd) |
{ |
case xxx_CMD1: |
... |
break; |
case xxx_CMD2: |
... |
break; |
default: |
// 不支持的命令 |
return - ENOTTY; |
} |
return 0; |
} |
// 字符设备文件操作结构体,在模块加载函数的cdev_init(&xxx_dev.cdev, &xxx_fops)的语句中被建立与cdev的连接 |
struct file_operations xxx_fops = |
{ |
.owner = THIS_MODULE, |
.read = xxx_read, |
.write = xxx_write, |
.ioctl = xxx_ioctl, |
... |
}; |
内核空间和用户空间的内存不能直接访问,需借助copy_from_user()和copy_to_user():
unsigned long copy_from_user(void *to, const void __user *from, unsigned long count); |
unsigned long copy_to_user(void __user *to, const void *from, unsigned long count); |
若要复制的内存是简单类型,如char、int、long等,则可以使用简单的put_user()和get_user():
int val; // 内核空间整形变量 |
... |
get_user(val, (int *)arg); // 用户空间到内核空间,arg是用户空间的地址 |
... |
put_user(val, (int *)arg); // 内核空间到用户空间,arg是用户空间的地址 |
#ifdef __CHECKER__ |
#define __user __attribute__((noderef, address_space(1))) |
#else |
#define __user |
#endif |
相关文章推荐
- 编写Linux并行接口字符设备驱动
- Linux字符设备驱动学习
- Linux内核开发之简单字符设备驱动(下)
- Linux字符设备驱动(二)
- Linux字符设备驱动总结
- Linux字符设备驱动的register_chrdev()与unregister_chrdev()
- s3c2440基于linux的gpio led字符设备驱动实践 [转]
- LINUX—字符设备驱动之-globalmem
- Linux 字符设备驱动中的数据结构
- Linux字符设备驱动的register_chrdev()与unregister_chrdev()
- linux2.6字符设备驱动编程第一例:globalmem
- Linux嵌入式驱动初体验(七)--- LED驱动之字符设备篇
- linux分类驱动对字符设备框架压力的卸载
- 嵌入式Linux字符设备入门之--LED驱动详解
- 第12章 Linux字符设备驱动综合实例
- s3c2440基于linux的gpio led字符设备驱动实践
- Linux 驱动开发-字符设备驱动
- Linux 驱动开发-字符设备驱动一些函数用法
- Linux字符设备驱动的register_chrdev()与unregister_chrdev()
- LINUX—字符设备驱动之-globalmem