全部博文(321)
分类: 嵌入式
2013-03-04 14:28:05
/proc/devices/----设备名 /dev/-----节点名称
一、驱动程序:
驱动程序是应用层和硬件设备之间的一个软件层
它向应用层提供了一组标准化的调用接口, 同时完全隐藏设备的工作细节
二、操作系统的作用:
1、一个复杂的软件系统需要处理多个并发的任务。
2、提供内存管理机制。每个进程都独立地访问4GB的内存空间,其中0-3GB属于用 户空间,3GB-4GB属内核空间。
3、设备驱动都按照操作系统给出的独立于设备的接口而设计,应用程序将可使用统一 的系统调用接口来访问各个设备。
三、驱动的存在:
a) 编译进内核: 启动内核时就会驱动此硬件设备。
b) 模块方式: 编译生成一个.ko文件, 当应用程序需要时再动态加载进内核空间运行。
c) 用脚本来写。
字符设备是一种可以按字节以串行顺序依次访问的设备===字节流
四、Linux用户程序通过设备文件(又名:设备节点 /dev/)来使用驱动程序操作设备
网络设备: 任何网络事务都通过一个接口来进行, 一个接口通常是一个硬件设备(eth0), 但是它也可以是一个纯粹的软件设备, 比如回环接口(lo)。一个网络接口负责发送和接收数据报文。网络设备没有对应设备文件
五、内核编程注意事项:
a) 不能调用用户空间函数
b) 不能使用浮点数
c) 不能使用死循环
六、字符设备文件<====设备号====>字符设备驱动
七、设备当文件处理,所以有目录:/dev/===节点。一个设备可以有多个节点或者一个节点
八、节点名/dev/称最好和设备名/proc/devices一样。
加载 insmod (insmod hello.ko)
卸载 rmmod (rmmod hello)
查看所有已加载模块 lsmod
查看某个模块信息 modinfo hello.ko
查看输出:
dmesg
cat /var/log/messages
在ARM平台上无需查看,自动自动打印输出到屏幕
Struct file ===代表一个打开的文件,可以有多个,因为可以多次被打开
Struct inode===代表一个设备文件
Struct cdev===代表内核中的一个设备
Struct file_operations====代表设备的操作函数:应用程序和VFS(虚拟文件系统)之间的接口是系统调用。而VFS和磁盘文件系统及普通设备之间的接口是file_operations。
由于字符设备的上层没有磁盘文件系统,所以字符设备的file_operations函数由驱动提供。File_operations正是字符设备驱动的核心。
Cdev 重要成员file_operations 定义了字符设备驱动提供给虚拟文件系统的接口函数。
One:
先定义一个代表这个设备类型的结构体
struct globalmem_dev
{
struct cdev cdev; /*cdev结构体*/
unsigned char mem[GLOBALMEM_SIZE]; /*全局内存*/
};
并定义全局的struct globalmem_dev *globalmem_devp; /*设备结构体指针*/
Two:
module_init(globalmem_init);
module_exit(globalmem_exit);
实现:
int globalmem_init(void)
{
一、申请设备号:
1、静态申请
result = register_chrdev_region(devno, 1, "globalmem");
Devno:主次设备号;
1:申请几个;
"globalmem":设备名;/proc/devices
2、动态分配
result = alloc_chrdev_region(&devno, 0, 1, "globalmem");
Devno:主次设备号
0:次设备号从0开始
1: 1个次设备
"globalmem":设备名;/proc/devices
globalmem_major = MAJOR(devno);//分解出主设备号
二、动态申请设备结构体的内存
globalmem_devp = kmalloc(sizeof(struct globalmem_dev), GFP_KERNEL);
memset(globalmem_devp, 0, sizeof(struct globalmem_dev));
用来存放设备文件的信息;返回设备结构体的指针。
或者在fifo中通过它来访问。转换。
三、初始化并注册cdev
1、分配cdev
cdev_init(&dev->cdev, &globalmem_fops);
2、初始化cdev:列出三个重要成员
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &globalmem_fops;
//dev->cdev.dev = devno;
1和2步是为了让内核能够识别cdev
3、添加设备到内核中
err = cdev_add(&dev->cdev, devno, 1);//devno:主次设备号
四、创建节点:即设备文件
1、手工创建:
mknod /dev/test c 235 0
Mknod 设备名 字符设备c 主设备号 次设备号
2、自动创建
a) 先创建struct class *my_class;
class-> my_class = class_create(THIS_MODULE, "my_class");
b) 创建节点: device_create( my_class, NULL, MKDEV(globalmem_major, 0), NULL, "globalmem");
附:2.6.12->devfs_mk_cdev(devno, S_IFCHR|S_IRUGO|S_IWUSR, "globalmem");
}
void globalmem_exit(void)
{
一、注销cdev
cdev_del(&globalmem_devp->cdev);
二、删除节点:
device_destroy(my_class, MKDEV(globalmem_major, 0));
三、删除class:
class_destroy(my_class);
四、释放设备结构体内存:
kfree(globalmem_devp);
五、注销设备号:
unregister_chrdev_region(MKDEV(globalmem_major, 0), 1);
}
Three:
1、定义变量(文件操作结构体)
static const struct file_operations globalmem_fops =
{
.owner = THIS_MODULE,
.llseek = globalmem_llseek,
.read = globalmem_read,
.write = globalmem_write,
.ioctl = globalmem_ioctl,
.open = globalmem_open,
.release = globalmem_release;
};
2、对应实现上面的函数:
a) static loff_t globalmem_llseek(struct file *filp, loff_t offset, int orig)
b) static ssize_t globalmem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)//ppos是指向文件的位置
c) static ssize_t globalmem_write(struct file *filp, const char __user *buf,size_t size, loff_t *ppos)
d) static int globalmem_ioctl(struct inode *inodep, struct file *filp, unsigned int cmd, unsigned long arg)
e) int globalmem_release(struct inode *inode, struct file *filp)
f) int globalmem_open(struct inode *inode, struct file *filp)
注意:dev->cdev.owner = THIS_MODULE,owner几乎全部都是THIS_MODULE
1、模块加载函数(必需)
安装模块时被系统自动调用的函数,通过module_init宏来指定
2、模块卸载函数(必需)
卸载模块时被系统自动调用的函数,通过module_exit宏来指定
3、许可证申明(必须)
宏MODULE_LICENSE被用来告知内核, 该模块带有一个许可证,没有这样的说明,加载模块时内核会抱怨。
4、模块参数(可选)
通过宏MODULE_PARM指定模块参数,模块参数用于在加载模块时传递参数给它。
5、模块作者(可选)
MODULE_AUTHOR(“lai");
加载 insmod (insmod hello.ko)
卸载 rmmod (rmmod hello)
查看所有已加载模块 lsmod
查看某个模块信息 modinfo hello.ko
查看输出:
dmesg
cat /var/log/messages
模块信息:在/sys/module
在ARM平台上无需查看,自动自动打印输出到屏幕
在linux内核中,所有标示为 _ _init的函数在连接的时候都放在.ini.text这个区段内。
用于调试定义打印信息:
#ifdef DEBUG
#define DPRINTK printk
#else
#define DPRINTK( x... ) //注意:宏带可变参数是linux c 的语法。
#endif