字符设备驱动程序——开发步骤(1)

驱动程序?

  • 使硬件工作的软件
驱动分类:
1、字符设备驱动
	按字节来访问的设备,通常实现open、close、read、write系统调用。
2、网络接口驱动
	一个网络接口负责发送和接受数据报文。
3、块设备驱动

使用驱动程序

字符驱动程序
	应用程序---->字符设备文件---->字符设备驱动程序---->字符设备
块设备驱动程序
	应用程序---->文件系统---->块设备文件---->块设备驱动程序---->块设备
网络接口驱动程序
	应用程序---->套接字---->协议栈---->网络设备驱动---->网络接口设备
主、次设备号
cd /dev
ls -l

在这里插入图片描述
图中以逗号间隔开的两个数字依次是主、次设备号。

设备号
- 字符设备文件通过主设备号与字符设备驱动建立起连接。
- 主设备号用来标识与设备文件相连的驱动程序。
- 次设备号被驱动程序用来辨别操作的是哪个设备。

主设备号用来反映设备类型
次设备号用来区分同类型的设备

==========================================================================
Q:内核中如何描述设备号?
A:dev_t
其实质是unsigned int 32位整数,其中高十二位为主设备号,低二十位为次设备号。
==========================================================================
Q:如何从dev_t中分解出主设备号?
A:MAJOR(dev_t dev)
==========================================================================
Q:如何从dev_t中分解出次设备号?
A:MINOR(dev_t dev)
==========================================================================

linux内核可以采用静态申请和动态分配两种方法。

1、静态申请
int register_chrdev_region(dev_t from,unsigned count,const char *name)
/*
功能:
	申请使用从from开始的count个设备号(主设备号不变,次设备号依次增加)。
参数:
	from:希望申请使用的设备号
	count:希望申请使用的设备号的数目
	name:设备名(体现在/proc/devices)
*/
2、动态分配
int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name)
/*
功能:
	请求内核动态分配count个设备号,且次设备号从baseminor开始。
参数:
	dev:分配到的设备号
	baseminor:起始次设备号
	count:需要分配的设备号的数目
	name:设备名(体现在/proc/devices)
*/

注销设备号

void unregister_chrdev_region(dev_t from,unsigned count)
/*
功能:
	释放从from开始的count个设备号。
*/

创建设备文件

手动创建

/*
mknod用法:
	mknod filename type major minor

参数:
	filename:设备文件名
	type:设备文件类型 eg:char(c),block(b)
	major:主设备号
	minor:次设备号
*/
mknod serial0 c 100 0

重要数据结构

1、Struct File
/*
代表一个打开的文件。系统中每打开一个文件,在内核空间都有一个关联的struct file。它由内核在打开文件时创建,在文件关闭后释放。
重要成员:
	loff_t f_pos				//文件读写的位置
	struct file_operations *f_op
*/
2、Struct Inode
/*
用来记录文件物理上的信息。它和代表打开文件的File结构不同,一个文件可以对应多个File结构,但只有一个Inode结构。
重要成员:
	dev_t i_rdev:设备号
*/
3、Struct file_operations
/*
一个函数指针的集合,定义能在设备上进行的操作。结构中的成员指向驱动中的函数,这些函数实现一个特别的操作,对于不支持的操作保留为NULL。
*/

//例:mem_fops
struct file_operations mem_fops={
.owner = THIS_MODULE,
.llseek = mem_seek,
.read = mem_read,
.write = mem_write,
.ioctl = mem_ioctl,
.open = mem_open,
.release = mem_release,
};

设备注册

1、分配cdev
/*
Struct cdev的分配可使用cdev_alloc函数来完成。
*/
struct cdev *cdev_alloc(void)
2、初始化cdev
/*
Struct cdev的初始化使用cdev_init函数来完成。

参数:
	cdev:待初始化的cdev结构
	fops:设备对应的操作函数集
*/
void cdev_init(struct cdev *cdev,const struct file_operations *fops)
3、添加cdev
/*
Struct cdev的注册使用cdev_add函数来完成。

参数:
	p:待添加到内核的字符设备结构
	dev:设备号
	count:添加的设备个数
*/
int cdev_add(struct cdev *p,dev_t dev,unsigned count)

设备操作

int (*open)(struct incode *,struct file *)
/*
	在设备文件上的第一个操作,并不要求驱动程序一定要实现这个方法。
	**如果该项为NULL,设备的打开操作永远成功。**
*/

void (*release)(struct inode *,struct file *)
/*
	当设备文件被关闭时调用这个操作。
	**与open相仿,release也可以没有。**
*/

void (*read)(struct file *,char __user *,size_t,loff_t *)
/*
	从设备中读取数据
*/

void (*write)(struct file *,const char __user *,size_t,loff_t *)
/*
	向设备发送数据
*/

void (*poll)(struct file *,struct poll_table_struct *)
/*
	对应select系统调用
*/

void (*ioctl)(struct inode *,struct file *,unsigned int,unsigned long)
/*
	控制设备
*/

int (*mmap)(struct file *,struct vm_area_struct *)
/*
	将设备映射到进程虚拟地址空间中。
*/

off_t (*llseek)(struct file *,char __user *,size_t,loff_t *)
/*
	修改文件的当前读写位置,并将新位置作为返回值。
*/

OPEN方法

- 初始化设备
- 标明次设备号

RELEASE方法

- 关闭设备

读和写

- 从设备中读取数据到用户空间
- 将数据传递给驱动程序
ssize_t xxx_read(struct file *filp,char __user *buff,size_t count,loff_t *offp)
ssize_t xxx_write(struct file *filp,char __user *buff,size_t count,loff_t *offp)

/*
	对于这两个方法,filp是文件指针,count是请求传输的数据量。
	buff参数指向数据缓存,offp指出文件当前访问位置。
	
	Read和Write 方法的buff参数是用户空间指针,不能被内核代码直接引用。
	因为,用户空间指针在内核空间时可能根本是无效的-------没有那个地址的映射。
*/
//内核专门提供了函数用于访问用户空间的指针

int copy_from_usr(void *to,const void __usr *from,int n)

int copy_to_usr(void __usr *to,const void *from,int n)

设备注销

int cdev_del(struct cdev *p)

/*
参数:
	p:要注销的字符设备结构
*/
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值