1.第一个是count,这个变量代表了有多少个相同的设备,例如开发板上可能对应有多个并口,这个count值就代表了并口的数目。
2.第二个是dev,这个变量代表了这个设备的设备号嵌入式linux设备驱动开发详解redflag linux,通过dev_t来定义,dev_t实际上是unsignedint无符号的32位整形内核规定,每位设备都要对应一个设备号,设备号又分为主设备号和次设备号。不同设备对应的主设备号是不相同的,多个同种设备是通过次设备号来区分开的。也就是说我们定义的这样一个cdev,是可以同时支持多个同种设备的,我们通过设置不同次设备号来将它们区分开。在linux终端下输入ls-l/dev来查看当前接入系统的设备。设备在linux文件系统中都是以设备文件来表示的。一个设备文件即代表了一个设备。
方形框里的数值代表主设备号,椭圆内的数值代表次设备号。我们可以看见这是几个同类型的设备,所以它们的主设备号都是1,而次设备号是不同的。字符设备文件和字符设备驱动程序是通过主设备号来构建起联系的。一个设备号由高12位的主设备号和低20位的次设备号组成。内核圆通过几个宏拓展的实现来完成设备号的分解和合成,代码写的特别有意思,我们可以来看一下。
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
通过MKDEV将主设备号和次设备号合成为设备号,虽然就是将主设备号左移20位后位或低20位的次设备号。
dev_tdev=MKDEV(主设备号,次设备号)
MAJOR拿来提取设备号中的主设备号,宏定义中我们可以看见就是将主设备号右移了20位linux开源软件,剔除了次设备号,同时将主设备号右对齐。
MINOR拿来提取设备号中的次设备号,宏定义中我们可以看见次设备号是通过主设备号位与一个次设备号网段来获得,这个次设备号写的很有意思,将1左移20位后再乘以1嵌入式linux设备驱动开发详解,因而得到一个低20位都为1的网段。因而次设备号得到提取。
这么怎样为一个设备分配一个设备号呢?Linux内核提供了两种分配设备号的方式。第一种是静态分配,第二种是动态分配。
2.初始化设备描述结构
cdev设备描述结构也可以有两种分配方法
-静态分配:structcdevmdev;
-动态分配:structcdev*pdev=cdev_alloc();
通过cdev_init()函数来初始化设备描述结构
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
/*cdev:待初始化的设备描述结构 fops:设备对应的操作函数集*/
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;
}
3.注册设备描述结构
字符设备描述结构的注册通过cdev_add()函数来完成
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
/*p待添加到内核的设备描述结构 dev设备号 count同类型设备的数量*/
{
p->dev = dev;
p->count = count;
return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}
4.硬件初始化Part2:实现设备操作
内核定义了一个file_operations结构体来实现对设备的各类操作,下边注重剖析几个常用的函数。我们可以看见,file_operations里定义了十分多的函数表针,我们在编撰操作函数的时侯,要对应这种函数表针规定的参数和返回值。这也是设备驱动模型规范化的彰显。
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
};
在Linux系统中,每一个打开的文件,在内核中还会关联一个structfile结构体,这个结构体有几个重要成员须要关注。
loff_t f_pos;/*文件读写指针*/
const struct file_operations *f_op;/*该文件对应的操作*/
在Linux系统中,每一个存在于文件系统里的文件就会关联一个structinode结构体,该结构主要拿来记录文件一些数学上的信息。注意和里面的structfile区分开,structfile结构体只有当文件打开时就会被关联,一个文件没有被打开时是不会关联structfile的。重要的成员为
dev_t i_rdev;/*设备号*/
open设备方式主要拿来为之后的操作完成初始化打算工作的。在大部份的驱动程序中,open完成如下操作:
release设备方式和open恰好相反。这个设备方式有时亦称为close。
-关掉设备
read设备方式一般完成两件事情:
Part3:驱动注销
无论使用何种方法分配设备号,都要在设备挂起驱动退出时注销设备号。我们使用内核提供的unregister_chrdev_region()函数来释放那些设备号。
void unregister_chrdev_region(dev_t from, unsigned count)
{ /*from 要注销的设备号 count 设备数*/
dev_t to = from + count;
dev_t n, next;
for (n = from; n < to; n = next) {
next = MKDEV(MAJOR(n)+1, 0);
if (next > to)
next = to;
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
}
}
void cdev_del(struct cdev *p)
{/*p要注销的设备描述结构*/
cdev_unmap(p->dev, p->count);
kobject_put(&p->kobj);
}
文章评论