当前位置:才华咖 > 互联网计算机 > 计算机 > 操作系统 > Linux系统字符设备驱动框架笔记
手机版

Linux系统字符设备驱动框架笔记

来源:才华咖 阅读:1.22W 次

不积跬步,何以至千里。掌握知识都是从很小的点开始的。下面是小编整理的Linux系统字符设备驱动框架笔记,欢迎阅读!

Linux系统字符设备驱动框架笔记

字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标、键盘、显示器、串口等等,当我们执行 ls -l /dev 的时候,就能看到大量的设备文件, c 就是字符设备, b 就是块设备,网络设备没有对应的设备文件。编写一个外部模块的字符设备驱动,除了要实现编写一个模块所需要的代码之外,还需要编写作为一个字符设备的代码。

  驱动模型

Linux一切皆文件,那么作为一个设备文件,它的操作方法接口封装在 struct file_operations ,当我们写一个驱动的时候,一定要实现相应的接口,这样才能使这个驱动可用,Linux的内核中大量使用"注册+回调"机制进行驱动程序的编写,所谓注册回调,简单的理解,就是当我们open一个设备文件的时候,其实是通过VFS找到相应的inode,并执行此前创建这个设备文件时注册在inode中的'open函数,其他函数也是如此,所以,为了让我们写的驱动能够正常的被应用程序操作,首先要做的就是实现相应的方法,然后再创建相应的设备文件。

#include //for struct cdev

#include //for struct file

#include //for copy_to_user

#include //for error number

/* 准备操作方法集 */

/*

struct file_operations {

struct module *owner; //THIS_MODULE

//读设备

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

//写设备

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

//映射内核空间到用户空间

int (*mmap) (struct file *, struct vm_area_struct *);

//读写设备参数、读设备状态、控制设备

long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

//打开设备

int (*open) (struct inode *, struct file *);

//关闭设备

int (*release) (struct inode *, struct file *);

//刷新设备

int (*flush) (struct file *, fl_owner_t id);

//文件定位

loff_t (*llseek) (struct file *, loff_t, int);

//异步通知

int (*fasync) (int, struct file *, int);

//POLL机制

unsigned int (*poll) (struct file *, struct poll_table_struct *);

。。。

};

*/

ssize_t myread(struct file *filep, char __user * user_buf, size_t size, loff_t* offset)

{

return 0;

}

struct file fops = {

r = THIS_MODULE,

= myread,

...

};

/* 字符设备对象类型 */

struct cdev {

//public

struct module *owner; //模块所有者(THIS_MODULE),用于模块计数

const struct file_operations *ops; //操作方法集(分工:打开、关闭、读/写、...)

dev_t dev; //设备号(第一个)

unsigned int count; //设备数量

//private

...

};

static int __init chrdev_init(void)

{

...

/* 构造cdev设备对象 */

struct cdev *cdev_alloc(void);

/* 初始化cdev设备对象 */

void cdev_init(struct cdev*, const struct file_opeartions*);

/* 为字符设备静态申请设备号 */

int register_chedev_region(dev_t from, unsigned count, const char* name);

/* 为字符设备动态申请主设备号 */

int alloc_chedev_region(dev_t* dev, unsigned baseminor, unsigned count, const char* name);

MKDEV(ma,mi) //将主设备号和次设备号组合成设备号

MAJOR(dev) //从dev_t数据中得到主设备号

MINOR(dev) //从dev_t数据中得到次设备号

/* 注册字符设备对象cdev到内核 */

int cdev_add(struct cdev* , dev_t, unsigned);

...

}

static void __exit chrdev_exit(void)

{

...

/* 从内核注销cdev设备对象 */

void cdev_del(struct cdev* );

/* 从内核注销cdev设备对象 */

void cdev_put(stuct cdev *);

/* 回收设备号 */

void unregister_chrdev_region(dev_t from, unsigned count);

...

}

实现read,write

Linux下各个进程都有自己独立的进程空间,即使是将内核的数据映射到用户进程,该数据的PID也会自动转变为该用户进程的PID,由于这种机制的存在,我们不能直接将数据从内核空间和用户空间进行拷贝,而需要专门的拷贝数据函数/宏:

long copy_from_user(void *to, const void __user * from, unsigned long n)

long copy_to_user(void __user *to, const void *from, unsigned long n)

这两个函数可以将内核空间的数据拷贝到回调该函数的用户进程的用户进程空间,有了这两个函数,内核中的read,write就可以实现内核空间和用户空间的数据拷贝。

ssize_t myread(struct file *filep, char __user * user_buf, size_t size, loff_t* offset)

{

long ret = 0;

size = size > MAX_KBUF?MAX_KBUF:size;

if(copy_to_user(user_buf, kbuf,size)

return -EAGAIN;

}

return 0;

}

本文链接:https://www.caihuaka.com/jsjzs/caozuo/z1g1vy.html

Copyright © 2024. 才华咖 All right reserved. 浙ICP备20120231号-3

文字美图素材,版权属于原作者。部分文章内容由网友提供推送时因种种原因未能与原作者联系上,若涉及版权问题,敬请原作者联系我们,立即处理。