1.cdev 结构体
Linux2.6内核使用 cdev 结构体描述字符设备
struct cdev // cdev 的定义在 <linux/cdev.h>
{
struct kobject kobj; //内嵌的kobject
struct module *owner; //所属模块
const struct file_operations *ops;
//文件操作结构,在写驱动时,其结构体内的大部分函数要被实现
struct list_head list;
dev_t dev; //设备号,int 类型,高12位为主设备号,低20位为次设备号
unsigned int count;
};
其中 dev_t 定义了设备号为32位,前12位为主设备号,后20位为次设备号.可以使用如下宏调用来获得主、次设备号:
MAJOR(dev_t dev)
MINOR(dev_t dev)
MKDEV(int major,int minor) //通过主次设备号来生成dev_t
以上宏调用在内核源码中如此定义:
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)
//(1<<20 -1) 此操作后,MINORMASK宏的低20位为1,高12位为0
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
下面一组函数用来对cdev结构体进行操作:
void cdev_init(struct cdev *, const struct file_operations *);//初始化,建立cdev和file_operation 之间的连接
struct cdev *cdev_alloc(void); //动态申请一个cdev内存
void cdev_put(struct cdev *p); //释放
int cdev_add(struct cdev *, dev_t, unsigned); //注册设备,通常发生在驱动模块的加载函数中
void cdev_del(struct cdev *);//注销设备,通常发生在驱动模块的卸载函数中
cdev_init()的源代码清单如下
// linux/fs/char_dev.c
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;
}
cdev_alloc()用于动态申请一个cdev内存,代码清单如下
struct cdev *cdev_alloc(void)
{
struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);
if (p) {
INIT_LIST_HEAD(&p->list);
kobject_init(&p->kobj, &ktype_cdev_dynamic);
}
return p;
}
cdev_add 和 cdev_del 分别用于添加和删除一个 cdev,完成字符设备的注册和注销。
在字符设备模块加载函数中调用 cdev_add,模块写在函数中调用 cdev_del
2. 分配和释放设备号
在注册时应该先调用:
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)函数代替,
他们之间的区别在于:register_chrdev_region()用于已知设备号时,另一个用于动态申请,其优点在于不会造成设备号重复的冲突。
在注销之后,应调用:void unregister_chrdev_region(dev_t from,unsigned count)函数释放原先申请的设备号。
他们之间的顺序关系如下:
register_chrdev_region()-->cdev_add() //加载模块中
cdev_del()-->unregister_chrdev_region() //卸载模块中
3. file_operations 结构体
主要成员有open, close , llseek, read, write, readdir, ioctl, mmap,poll等,结构比较庞大,他们最终会被调用
4 Linux字符设备驱动的组成
4.1 字符设备驱动模块加载与卸载
模板如下
//xxdevice struct
struct xxx_dev
{
struct cdev cdev;
…
}xxx_dev;
static int __init xxx_init(void)
{
…
cdev_init( &xxx_dev.cdev, &xx_fops);//init the cdev ,and add file operations
register_chrdev_region or alloc_chrdev_region //get the device number
ret = cdev_add( )// register the device
…
}
static void __exit xxx_exit(void)
{
…
Unrgister the device number use unregister_chrdev_region()
…
cdev_del()
…
}
4.2 完成file_operations中的成员函数
按照file_operations中定义的统一结构编写实现函数
注意 copy_from_user 和 copy_to_user 的使用
struct file_operations xxx_fops =//init the fops
{
.owner = THIS_MODULE,
.read = xxx_read,
.open = xxx_open,
…
};
具体实例 :
Makefile:
运行 insmod globalmem.ko ,加载模块
lsmod 和 /proc/devices 中可以看见 globalmem
通过: mknod /dev/globalmem c $device_number 0 创建设备节点
在 /sysy/module 中会有相应的目录和文件