`

字符设备驱动程序学习笔记二

阅读更多

字符驱动程序
1 设备号
字符设备通过字符设备文件来存取
ls -l 如果输出的第一列是c标识,说明该文件是字符设备文件
设备文件项中的两个数分别为 主设备号/次设备号

设备文件与设备驱动通过主设备号建立联系
次设备号用来分辩操作的哪个设备
dev_t 用来描述设备号 在linux/types.h中声明,示例代码如下:
typedef __u32 __kernel_dev_t;


typedef __kernel_fd_setfd_set;
typedef __kernel_dev_tdev_t;
实质为unsigned int 32位整数,高难12位为主设备号,低12位为次设备号

#主设备号
MAJOR(dev_t dev)
#次设备号
MINOR(dev_t dev)
#将主设备号和次设备号转化为dev_t
MKDEV(int major,int minor)
以上几个宏在linux/kdev_t.h中声明
示例代码如下:
#define MAJOR(dev)((dev)>>8)
#define MINOR(dev)((dev) & 0xff)
#define MKDEV(ma,mi)((ma)<<8 | (mi))






linux内核有两种分配设备号的方法
1 静态申请
根据Documentation/devices.txt 确定设备号是否使用
使用register_chardev_region函数来注册设备号,在linux/fs.h中声明
extern int register_chrdev_region(dev_t from, unsigned count, const


char *name);
form: 需要申请的设备号
count:设备号数目
name:设备名



2 动态分配(推荐使用)
使用alloc_chardev_region函数来注册设备号,在linux/fs.h中声明
extern int alloc_chrdev_region(dev_t *dev, unsigned baseminor,


unsigned count, const char *name);
dev:分配的设备号
baseminor: 起始次设备号
count:设备号数目
name:设备名
缺点:无法在安装驱动前创建设备文件
安装驱动后,从/proc/devices中查询设备号


注:对于动态分配设备号的设备驱动程序,对insmod的调用可以替换为一个角
本,角本的主要内容:
1 调用insmod
2 在调用insmod后读取proc/devices取的新分配的主设备号
3 创建设备文件
可以使用awk工具取得proc/devices信息
示例代码如下:
#!/bin/sh
driver="test"

echo "角本执行 ...wait"
#读取/proc/devices信息
major=$(awk "{if(\$2==\"$driver\") {print \$1}}" /proc/devices)
echo $major
#创建设备文件
mknod /dev/$driver0 c $major 0

注销设备号
void unregister_chrdev_region(dev_t from,unsigned count)
form:起始设备号
count:设备号数目





2 创建设备文件



mknod命令手工创建
mknod filename type major minor
filename:设备文件名
type:设备文件类型
major: 主设备号
minor: 次设备号




自动创建




3 设备注册


4 重要数据结构
#打开一个文件
struct file
重要成员:


/*文件模式*/
mode_t f_mode

/*文件读写位置*/
loff_t f_pos

/*与文件相关的操作*/
struct file_operations *f_op

/*文件标志*/
unsigned int f_flags

/*跨系统调用时保存状态信息 */
void *private_data

/*文件对应的目录项结构*/
struct dentry *f_dentry




#用来记录文件物理上的信息
#一个文件可以对应多个file结构,但只有一个inode结构
struct inode
重要成员:
/*设备号*/
dev_t i_rdev

/*字符设备内核的内部结构*/
sturct cdev *i_cdev
/*从一个inode中取得主从设备号最好使用以下两个宏*/
unsigned int iminor(struct inode *inode);
unsigned int imajor(struct inode *inode)


#一个函数指针的集合,定义能在设备上进行的操作,在linux/fs.h中声明
struct file_operations
示例代码如下;
/*
* NOTE:
* read, write, poll, fsync, readv, writev, unlocked_ioctl and


compat_ioctl
* can be called without the big kernel lock held in all filesystems.
*/
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 *);
ssize_t (*aio_read) (struct kiocb *, const struct iovec *,


unsigned long, loff_t);
ssize_t (*aio_write) (struct kiocb *, const struct iovec *,


unsigned long, loff_t);
int (*readdir) (struct file *, void *, filldir_t);
unsigned int (*poll) (struct file *, struct poll_table_struct


*);
int (*ioctl) (struct inode *, struct file *, unsigned int,


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


long);
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 *, struct dentry *, int datasync);
int (*aio_fsync) (struct kiocb *, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t,


loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned


long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file


*, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct


pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **);
};






static const struct file_operations mem_fops = {
.llseek = memory_lseek,
.read= read_mem,
.write = write_mem,
.mmap= mmap_mem,
.open= open_mem,
.get_unmapped_area = get_unmapped_area_mem,
};




应用程序访问驱动程序的过程
read_write.c示例代码如下:
读文件
ssize_t vfs_read(struct file *file, char __user *buf, size_t count,


loff_t *pos){
...
/*f_op是file_operations,就是调用其中的read方法来完成*/
ret = file->f_op->read(file, buf, count, pos);
...
}



5 设备注册
字符设备是用struct cdev 来描述
字符设备的注册分为以下三步;
1 分配cdev
struct cdev *cdev_alloc(void)

2 初始化cdev
void cdev_init(struct cdev *cdev,const struct


file_operstions *fops)
cdev:要初始化的cdev结构
fops:设备对应的操作函数集


3 添加cdev
int cdev_add(struct cdev *p,dev_t dev,unsigned count)
p:要添加的字符设备结构
dev:设备号
count:添加设备的个数
设备注销
int cdev_del(struct cdev *p)
p:要注销的字符设备结构




6 设备操作
/*完成一些初始化工作
检查设备特定的错误码
首次打开则进行初始化
更新f_op指针
分配填写filp->private_data数据结构
*/
int (*open)(struct inode *,sturct file *)

/*设备关闭时调用这个操作*/
void (*release)(struct inode *,struct file *)






/*从设备中读取数据*/
ssize_t (*read) (struct file *,char__user *,size_t,loff_t *)


/*向设备发送数据*/
ssize_t (*write) (struct file *,const char__user *,size_t,loff_t *)




ssize_t xxx_read (struct file *filp,char__user *buff,size_t


count,loff_t *offp)
ssize_t xxx_write (struct file *filp,const char__user *buff,size_t


count,loff_t *offp)
filp:文件指针
count:请求传输的数据量
buff:参数指向的数据缓存
offp:文件当前的访问位置
注:buff参数是用户空间的指针,不能被内核代码直接引用
用于访问用户空间的指针的方法有:
int copy_from_user(void *to,const void__user *from,int n);
int copy_to_user(void __user *to,const void *from,int n);



/*对应select系统调用*/
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 *)

/*修改文件的当前读写位置*/
off_t (*llseek) (sturct file *, loff_t,int)

分享到:
评论

相关推荐

    linux字符设备驱动程序学习笔记

    详细介绍了linux字符设备驱动程序,对各个名词做了自己的理解,在学习中的笔记,有错误还请海涵

    Linux 驱动学习笔记pdf文档

    ·Linux设备驱动程序学习(1)-字符设备驱动程序 ·Linux设备驱动程序学习(0)-设备驱动介绍& Hello, world!模块 ·Linux设备驱动程序学习(2)-调试技术 ·Linux设备驱动程序学习(3)-并发和竞态 ·Linux设备...

    linux设备驱动学习笔记

    Linux 设备驱动程序学习(0) -设备驱动介绍& Hello, world!模块 模块结构介绍 字符设备驱动程序 调试技术 并发和竞态 高级字符驱动程序操作 阻塞型 I/O 和休眠

    Linux字符驱动设备--学习笔记

    第二步:编写Makefile文件,然后编译globalvar.c; 第三步:insmod加载模块,并生成设备节点mknod。(此步有两种方法); 第四步:编写测试文件test.c并编译成a.out后即可进行设备的读写测试。 在此,希望各位读者...

    Linux学习笔记 第一版

    普通文件:包括文本文件、数据文件、可执行的二进制程序等。  目录文件:简称为目录,Linux 中把目录看成是一种特殊的文件,利用它构成文件系统的分层树型结构。每个目录文件中至少包括两个文件, “..” 表示上一...

    嵌入式Linux入门笔记(十年程序员精品推荐、让你看得懂的笔记、结合开发板例程精心讲解)

    简单的字符设备驱动实验…… 第二阶段在开发板上学习研究Linux. 一.MIZI Linux SDK for S3C2410开发环境及工具使用. 1.构造软件开发环境. 2.编译嵌入式Linux生成image…… 3.将嵌入式Linux的image下载到目标板… … ...

    整理后java开发全套达内学习笔记(含练习)

    short 16bit, -2^15~2^15-1 (2^15=32768) int 32bit, -2^31~2^31-1 (2147483648,20亿,10位有效数字) long 64bit, -2^63~2^63-1 (900亿亿,20位有效数字) float 32bit, 9位有效数字,含小数(四舍五入)(小数点算...

    asp.net知识库

    VS2005 ASP.NET本地化学习笔记&感受 在自定义Server Control中捆绑JS文件 Step by Step 深度解析Asp.Net2.0中的Callback机制 使用 Web 标准生成 ASP.NET 2.0 Web 站点 ASP.NET 2.0基于SQLSERVER 2005的aspnetdb.mdf...

    若干源程序资料12.rar

    2012-06-11 21:32 318,464 实验42:L298电机驱动程序(ATme.rar 2012-06-11 21:07 13,091 嵌入式拼音输入法C代码.rar 2012-06-11 21:12 64,623 巴特沃斯、切比雪夫I和椭圆滤波器设计的源程序.zip 2012-06-11 21:29 ...

    图像处理工具 Hornil StylePix Pro 2.0.3.0 中文版.zip

    一种便携式版本的运行 Hornil StylePix 从可移动存储设备如USB闪存驱动器,闪存卡,或软盘(媒体)。要安装 Hornil StylePix 便携式,只要下载便携包,然后解压缩。要启动 Hornil StylePix 便携,只需双击您的便携 ...

Global site tag (gtag.js) - Google Analytics