1 Linux字符设备概述
Linux从各异的设备中提取共性,将其划分成三大类:字符设备、块设备和网络设备。
内核针对每一类设备都提供了对应的驱动模型框架,包括内核设施和文件系统接口。
常见的字符设备-键盘、鼠标、液晶显示、打印机等.
常见的块设备--flash、sd卡、硬盘.
2 常用的数据结构
2.1 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 *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
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);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
--};
//在设备驱动中主要是实现的是file_operations中的方法--open/read/write/mmap等
2.2 字符设备--struct cdev
--struct cdev {
struct kobject kobj; /*字符设备内核对象*/
struct module *owner; /*字符设备驱动程序所在的内核模块对象指针*/
const struct file_operations *ops; /*访问字符设备的操作方法*/
struct list_head list; /*字符设备链表*/
dev_t dev; /*字符设备号*/
unsigned int count; /*同一个主设备的次设备号的个数-->串口*/
--};
3 常见的操作
3.1 定义字符设备
--定义字符设备通常有两种方法:静态定义(struct cdev char_dev)
--动态定义:struct cdev char_dev = cdev_alloc( );-->常用;
3.2 初始化字符设备
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops; /*将fops赋值给cdev->ops, 从文件系统来访问*/
}
3.3 分配字符设备号
--Linux系统中设备号由主设备号和次设备号构成,主设备号主要来定位对应的设备驱动程序,
--次设备号主要来管理若干同类设备--例如串口;
--linux用dev_t类型变量来标识一个设备号-->32位无符号的整数;
--dev_t由高12位主设备号+低20位次设备号构成。
常用操作:
---1 MAJOR(dev)--取主设备号,MINOR(dev)-取次设备号
----------MKDEV(ma_dev, mi_dev)--将主设备号和次设备号构成设备号
---2 分配设备号
静态分配:
----/**
* register_chrdev_region() - register a range of device numbers
* @from: the first in the desired range of device numbers; must include
* the major number.
* @count: the number of consecutive device numbers required
* @name: the name of the device or driver.
*
* Return value is zero on success, a negative error code on failure.
*/
int register_chrdev_region(dev_t from, unsigned count, const char *name)
{
struct char_device_struct *cd;
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;
cd = __register_chrdev_region(MAJOR(n), MINOR(n),
next - n, name);
if (IS_ERR(cd))
goto fail;
}
return 0;
}
--主要是将当前设备驱动程序所使用的设备号记录到chrdevs数组中.
动态分配:
/**
* alloc_chrdev_region() - register a range of char device numbers
* @dev: output parameter for first assigned number
* @baseminor: first of the requested range of minor numbers
* @count: the number of minor numbers required
* @name: the name of the associated device or driver
*
* Allocates a range of char device numbers. The major number will be
* chosen dynamically, and returned (along with the first minor number)
* in @dev. Returns zero or a negative error code.
*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
const char *name)
{
struct char_device_struct *cd;
/* Register a single major with a specified minor range.
*
* If major == 0 this functions will dynamically allocate a major and return
* its number.
*/
cd = __register_chrdev_region(0, baseminor, count, name);
*dev = MKDEV(cd->major, cd->baseminor);
return 0;
}
3.4 注册字符设备
/**
* cdev_add() - add a char device to the system
* @p: the cdev structure for the device
* @dev: the first device number for which this device is responsible
* @count: the number of consecutive minor numbers corresponding to this
* device
*
* cdev_add() adds the device represented by @p to the system, making it
* live immediately. A negative error code is returned on failure.
*/
struct kobj_map {
struct probe {
struct probe *next;
dev_t dev;
unsigned long range;
struct module *owner;
kobj_probe_t *get;
int (*lock)(dev_t, void *);
void *data;
} *probes[255];
struct mutex *lock;
};
/*
static struct kobj_map *cdev_map;static struct kobj_map *cdev_map; -->全局变量cdev_map;
在Linux启动期间由chrdev_init函数负责初始化
*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
p->dev = dev;
p->count = count;
/*将设备加入到cdev_map(hash表)中*/
return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
}