栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

内核驱动之字符设备驱动

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

内核驱动之字符设备驱动

一、Linux 内核设备驱动类型
二、驱动程序和应用程序的差异
三、驱动程序说明
四、字符设备驱动编写流程
五、简单字符设备驱动程序

一、Linux设备驱动类型
字符设备驱动
GPIO、鼠标、键盘、显卡、声卡、
jpeg、视频、摄像头…
misc(binder、Ashmem、logger)

块设备驱动
大容量的存储设备
nand flash、SD卡、移动硬盘、U盘…

网络设备
网卡

二、驱动编程与应用编程的差异
驱动程序是没有main函数
应用程序有入口(main函数),“无”出口
驱动程序有入口和出口(module_init()/module_exit())
设计驱动程序需要Linux内核源码包
驱动程序使用Linux内核提供的函数
驱动程序有固定的框架
驱动程序要求高效、稳定、简洁
驱动程序要有很好的可移植性

三、驱动程序说明
使用linux/module.h头文件定义的宏函
数,对驱动程序信息进行说明。内核模块
描述信息:
MODULE_AUTHOR(“xxx”);
MODULE_DESCRIPTION(“xxx”);
MODULE_LICENSE(“GPL”);
MODULE_VERSION(“Vxxx”);

几个关键字
__init:
标记内核启动时使用的初始化代码,内核启动完成后不再需要。以此标记的代码位于.init.text 内存区域,当内核启动完毕后,这个段中的内存会被释放掉。
__exit:
与__init类似,对于非模块无效。
__initdata:
使用该关键字修改的变量放在初始化的数据段(.init.data)。该数据在使用完成后,其所占用的内存会被释放掉。
__user:
(void __user *)arg,告诉内核这是一个用户空间的地址,不能直接进行访问,要通过copy_from_user/copy_to_user等来操作

四、字符设备驱动
Linux内核中绝大部分驱动都是字符设备驱动。

字符设备驱动模型

字符设备的描述
在字符设备驱动中,使用cdev结构体来描述字符设备
struct cdev {
struct kobject kobj;//管理结构体
struct module *owner;//所归属模块,一般赋值为THIS_MODULE
const struct file_operations ops;//文件操作集
struct list_head list;//内核链表
dev_t dev;//设备号
unsigned int count;//此设备的数量----有几个次设备号
};/ common/include/cdev.h */

在/sys下显示的每一个对象,都对应一个kobject

字符设备驱动设计流程

1 定义字符设备
struct cdev led_drv;
2 定义文件操作集,并对文件操作集进行初始化
3 分配或注册设备号
动态分配:alloc_chrdev_region();
静态注册: register_chrdev_region();
4 字符设备的初始化
void cdev_init()
5 向内核加入字符设备
cdev_add()

文件操作集
struct file_operations:每个cdev都有file_operations,是驱动程序给应用程序提供的 接口。

struct file_operations {
struct module *owner;
…
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 *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file );
…
};/ common/include/linux/fs.h */

定义并初始化文件操作集
static int led_open(struct inode *inode, struct file *file)
{
printk(“openning the driver of ledn”);
return 0;
}
…
static const struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_writte,
.release = led_release,
}

设备号
dev_t:(32bits)无符号整型数,在Linux下,每个设备都有唯一的ID,叫设备号。设计一个字符设备驱动时,必须申请设备号。
typedef u_long dev_t;

主设备号和次设备号:
主设备号:设备号的高12bits —>硬件的类型
次设备号:设备号的低20bits —>一个硬件类型下的具体实例
1 由主设备号ma和次设备号mi得到设备号的函数
#define MINORBITS 20
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
2) 由设备号得到主次设备号
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))

设备号动态分配与静态注册
动态申请:int alloc_chrdev_region(dev_t *dev, unsigned baseminor,
unsigned count,const char *name);
dev:linux系统分配的设备号
baseminor:次设备号的起始值
count:该cdev次设备的数量
name:设备名称
静态注册:int register_chrdev_region(dev_t from, unsigned count,
const char *name);
dev:向linux系统静态注册的设备号
count:连续编号范围,该cdev有多少次设备
name:编号相关联的设备名称(/proc/devices)
注销:void unregister_chrdev_region(dev_t from, unsigned count);
from:要注销的设备编号范围的初始值
count:连续编号范围
例: register_chrdev_region(led_drv_num, 1, “test_led”);
unregister_chrdev_region(led_drv_num, 1);

字符设备操作

void cdev_init(struct cdev *, struct file_operations *);//初始化
参数说明:
struct cdev *cdev:字符设备
const struct file_operations *fops:文件操作集

int cdev_add(struct cdev *, dev_t, unsigned);//向内核加入字符设备
参数说明:
struct cdev *cdev:字符设备
dev_t dev:设备号
unsigned count:次设备的数量

void cdev_del(struct cdev *);//从内核删除该字符设备

例:cdev_init(&led_drv, &led_fops);
cdev_add(&led_drv,led_drv_num, 1 );
cdev_del(&led_drv);

驱动模块使用
modinfo
查看驱动模块信息
insmod
安装驱动模块
rmmod
卸载驱动模块
lsmod
显示内核安装的驱动模块

五、简单字符设备驱动示例
int main(void)
{
…
char buf[]=“hello drivern”;
fd = open(“/dev/led_drv”, O_WRONLY);
ret = write(fd, buf, sizeof(buf));
return 0;
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/939182.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号