1, 设备号的内部表示形式
类型:dev_t 32=12(主设备号) + 20(次设备号)
相关宏:<linux/kdev_t.h>
MAJOR(dev_t dev)
MINOR(dev_t dev)
MKDEV(int major, int minor);
2. 分配和释放设备号
相关函数:
//静态分配设备号
int register_chrdev_region(dev_t first, unsigned int count,
char *name);
//动态分配设备号
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,
unsigned int count, char *name);
void unregister_chrdev_region(dev_t first, unsigned int count);
3.获取设备号的通常写法
if (scull_major) {
dev = MKDEV(scull_major, scull_minor);
result = register_chrdev_region(dev, scull_nr_devs, "scull");
} else {
result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs,
"scull");
scull_major = MAJOR(dev);
}
if (result < 0) {
printk(KERN_WARNING "scull: can't get major %d\n", scull_major);
return result;
}
4.一些重要的数据结构
struct file_operations
{
//用于防止一个正在使用的模块被卸载,通常值为THIS_MODULE
struct module *owner;
//seek
loff_t (*llseek) (struct file *, loff_t, int);
//read
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*aio_read)(struct kiocb *, char __user *, size_t, loff_t);
//write
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_write)(struct kiocb *, const char __user *, size_t, loff_t *);
//readdir
int (*readdir) (struct file *, void *, filldir_t);
//poll
unsigned int (*poll) (struct file *, struct poll_table_struct *);
//ioctl
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
//mmap
int (*mmap) (struct file *, struct vm_area_struct *);
//open
int (*open) (struct inode *, struct file *);
//flush
int (*flush) (struct file *);
//release
int (*release) (struct inode *, struct file *);
//fsync
int (*fsync) (struct file *, struct dentry *, int);
int (*aio_fsync)(struct kiocb *, int);
int (*fasync) (int, struct file *, int);
//lock
int (*lock) (struct file *, int, struct file_lock *);
//readv writev
ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);
//sendfile
ssize_t (*sendfile)(struct file *, loff_t *, size_t, read_actor_t, void *);
//sendpage
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *,
int);
//get_unmapped_area
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned
long, unsigned long, unsigned long);
//check_flags
int (*check_flags)(int)
//dir_notify
int (*dir_notify)(struct file *, unsigned long);
...
};
//代表打开的文件
struct file
{
//读写权限
mode_t f_mode;
//文件读写位置
loff_t f_pos;
//文件标志(O_RDONLY, O_NONBLOCK, O_SYNC)
unsigned int f_flags;
//文件操作
struct file_operations *f_op;
//设备文件的私有数据
void *private_data;
//与文件相关的目录,
struct dentry *f_dentry;
...
};
struct inode
{
//对于设备文件来说,此域表示真实的设备号
dev_t i_rdev;
//当引结点指向一个字符设备时,代表内核内部结构的字符设备
struct cdev *i_cdev;
};
//从I结点中获取次设备号
unsigned int iminor(struct inode *inode);
// 从I结点中获取主设备号
unsigned int imajor(struct inode *inode);
5. 注册字符设备
头文件:<linux/cdev.h>
cdev 结构体的定义:
struct cdev
{
struct kobject kobj; /*内嵌的kobject对象*/
struct module *owner; /*所属模块*/
struct file_operations *ops; /*相关的文件操作*/
struct list_head list;
dev_t dev; /*设备模块*/
unsigned int count;
};
分配和初始化字符设备相关结构
方法1(将cdev作为单独的一个结构):
struct cdev *my_cdev = cdev_alloc( ); //分配设备空间
my_cdev->ops = &my_fops;
//设备初始化
void cdev_init(struct cdev *cdev, struct file_operations *fops);
//添加设备
int cdev_add(struct cdev *dev, dev_t num, unsigned int count);
//删除设备
void cdev_del(struct cdev *dev);
方法2(将cdev作为自定义设备结构的一个成员):
struct mydev
{
…
struct cdev;
}
设备分配初始化以及删除操作类似
PS: 2.6内核以前的字符设备注册方法
int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
int unregister_chrdev(unsigned int major, const char *name);
6. 字符设备驱动程序模板
字符设备驱动模块加载和卸载函数模板
//设备结构体
struct xxx_dev_t
{
struct cdev cdev;
…
};
//设备驱动模块加载函数
static int __init xxx_init(void)
{
…
//初始化cdev
cdev_init(&xxx_dev.cdev, &xxx_fops);
//获取字符设备号
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);
…
}
字符设备常用I/O操作函数模板
//读设备
ssize_t xxx_read(struct file *filep, char __user *buf, size_t count, loff_t *f_pos)
{
…
copy_to_user(buf, …, …);
…
}
//写设备
ssize_t xxx_write(struct file *filep, const char __user *buf, size_t count, loff_t *f_pos)
{
…
copy_from_user(…, buf, …);
…
}
//ioctl函数
int xxx_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg)
{
…
switch(cmd)
{
case XXX_CMD1:
…
break;
case XXX_CMD2:
…
break;
default:
//不能支持的命令
return –ENOTTY;
}
return 0;
}
用户空间与内核空间的数据传输
//内核空间到用户空间数据的复制
unsigned long copy_to_user(void __user *to, const void *from,
unsigned long count);
//用户空间到内核空间的复制
unsigned long copy_from_user(void *to, const void __user *from,
unsinged long count);
上述函数均返回不能被复制的字节数,因此,如果完全复制成功,返回值为0.
如果要复制的内在是简单类型,如char, int,long等,则可以使用简单的put_user()和get_user()函数。
如:
int val;
…
get_user(val, (int*)arg);
…
put_user(val, (int*)arg);