字符设备模板

linux驱动

makefile写法:

ifneq
($*KERNELRELEASE),)
obj-m
:=hello.o
else
KERNELDIR?=/lib/modules/$(shell
uname -r) /build
PWD
:=$(shell pwd)
default:
$(MAKE)
-C $(KERNELDIR) M=$(PWD) modules
endif

初始化和关闭:

static
int __init initialization_funciton(void)
{
*
}
module_init(initialization_function);
static
void __exit cleanup_function(void)
{
*
}
module_exit(cleanup_function);

__init意为在初始化时调用,调用完后会被清除出内存

__exit意为只在清除时调用,调用后即清除

参数传递:

static
char *whom =”world”;

static
int howmany=1;

module_param(howmany,int,S_IRUGO);

module_param(whom,charp,S_IRUGO);

内核支持的模块参数类型如下:

bool、invbool、charp、int、long、uint、ulong、ushort

声明数组参数用以下形式:
module_param_array(name,type,num,perm);

perm为访问许可值,S_IRUGO为任何人可读,S_IWUSR为root可写。

主设备号和次设备号

通常而言,主设备号指示设备对应的驱动程序,次设备号用于正确确定设备文件所指的设备。内核中用dev_t类型保存设备号

获得主次设备号:

MAJOR(dev_t,dev);

MINOR(dev_t,dev);

生成dev_t类型:

MKDEV(int
major,int minor);

分配和释放设备编号:

静态分配:

<linux/fs.h>

int
register_chrdev_region(dev_t first, unsigned int count, char *name);

first是要分配的设备编号的起始值,count是要请求连续设备编号的个数,name是与范围相关联的名称。成功返回0,错误返回负的错误码。

动态分配:

int
alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int
count, char *name);

释放:

void
unregister_chrdev_region(dev_t first, unsigned int count);

重要的数据结构:

设备号与驱动程序操作的关联:

file_operations,指向该结构的指针一般名叫fops,包含指向对文件各个操作的实现函数的指针。

有个owner字段需要被初始化为THIS_MODULE

file结构:

file结构代表一个打开的文件,系统中每一个打开的文件在内核空间都有一个对应file结构

需要用到的file字段:


mode_t


f_mode


文本模式,标识文件是可读可写,但不需要在驱动中检测


loff_t


f_ops


当前读写位置


unsigned
int


f_flags


文件标志,定义在<linux/fcntl.h>


Struct
file_operations


*
f_op


与文件相关的操作


void


*
private_data


私有数据,必须在file结构被销毁前在relrease方法中释放内存


struct
dentry


*
f_dentry


文件对应的目录项结构

inode结构:

dev_t
i_rdev; 对表示设备文件的inode结构,该字段包含了真正的设备编号

struct
cdev *i_cdev 表示字符设备的内核的内部结构。当inode指向一个字符设备文件时,该字段包含了指向struct
cdev结构的指针。

有两个宏可用来从一个inode中获得主设备号和次设备号:

unsigned
int iminor(struct inode *inode);

unsigned
int imajor(struct inode *inode);

字符设备的注册:

内核使用struct
cdev结构表示字符设备,定义在<linux/cdev.h>

分配和初始化cdev的两种方式:

获得独立的结构:

struct
cdev *my_code=cdev_alloc();

my_cdev->ops=&my_fops;

或者嵌入到自己的设备:(不知道为什么,,,)

void
cdev_init(struct cdev*cdev, struct file_operations *fops);

还有一个struct
cdev的owner字段需要被初始化为THIS_MODULE

告知内核该结构的信息:

int
cdev_add(struct cdev *dev, dev_t num, unsigned int count);

num是设备对应的第一个设备号,count应该是和该设备关联的设备编号的数量。

在驱动程序还没有完全准备好处理设备上的操作时,不能调用cdev_add。

从系统中移除一个字符设备:

void
cdev_del(struct cdev *dev);

open和release

open应该完成以下操作:
检查设备特定的错误

如果设备是首次打开,则对其进行初始化

如有必要,更新f_op指针

分配并填写置于filp-private_data里的数据结构。

open方法原型:

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

inode参数中i_cdev字段包含注册设备时的cdev的指针

struct
cdev结构体在装在模块时被装如内存,全局只有一个。

个人理解:cdev结构体代表着字符设备,自定义的字符设备需要将cdev结构嵌入到自定义的结构体中,当需要访问这个结构体时,需要通过inode结构获得cdev指针,然后通过container_of宏获得自定义结构体的指针。然后将这个指针通过写入file结构的private_data传递给其他文件操作函数。

release应该完成以下操作:

释放由open分配的保存在private_data中的所有内容

在最后一次关闭操作时关闭设备。

并不是每个close方法都调用release方法,只在file中维护的计数器为0时,才调用release。

实例scull的内存结构:

用到定义在<linux/slab.h>的两个函数

void
*kmalloc(size_t size, int flags);

void
kfree(void *ptr); //将kmalloc返回的指针传给kfree,但传NULL是合法的

定义在<asm/access.h>的两个函数

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

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

这两个函数还会检测用户空间指针的有效性,遇到无效地址不拷贝或仅拷贝部分内容,返回值为还需拷贝的数量。如果确定不需要检测,可使用__copy_to_user和__copy_from_user。

read和write:

ssize_t
read(struct file *filp, char __user *buf, size_t count, loff_t
*offp);

ssize_t
write(struct file *filp, const char __user *buff, size_t count,
loff_t *offp);

ssize_t类型为“signed
size type(有符号的尺寸类型)”
loff_t为“long
offset type(长偏移量类型)”,指明用户在文件中进行存取操作的位置。

出错时,read和write方法都返回一个负值,大于等于0的返回值告诉的调用程序成功传递输了多少个字节。如果在正确传输部分数据之后发生了错误,则返回值必须是成功传输的字节数,但这个错误只能在下一次函数调用时才会得到报告。这种实现惯例要求驱动程序必须记住错误的发生,这样才能在将来把错误状态返回给应用程序。

内核函数通过返回负值来表示错误,而且该返回值表明了错误的类型,但运行在用户空间的程序看到的始终是作为返回值的-1。为了找出出错的原因,用户空间的程序必须访问errno变量。用户空间这种行为源于POSIX标准,但该标准未对内核内部的操作做任何要求。

read方法:
返回值是等于read系统调用传递的count参数,则说明所请求的字节数成功完成了。

返回值小于count,则说明只传输了部分数据。需要程序继续读取,如果用fread函数,则会一直读

返回值为0,表示已经到文件尾

负值表示出现错误。

还一种情况是,现在没有数据,以后可能会有

write方法:

返回值等于count则完成传输

是正的但小于count,则完成部分

为0表示什么也没写入。但这不是错误,可能是没有理由返回一个错误码,例如阻塞

负值表示错误。错误码定义在<linux/errno.h>

readv和writev

向量操作,这些向量型函数具有一个结构体数组,每个结构包含一个指向缓冲区的指针和一个长度值。如果驱动程序没有提供用于处理向量操作的方法,readv和writev会通过read和write方法的多次调用实现。

原型:ssize_t
(*readv) (struct file *filp, const struct iovec *iov, unsigned long
count, loff_t *ppos);

ssize_t
(*writev)(struct file *filp, const struct iovec *iov, unsigned long
count, loff_t  *ppos);

struct
iovec

{

void
__user *iov_base;

__kernel_size_t
iov_len;

}

iovec应由用户程序创建。

时间: 2024-12-06 23:42:05

字符设备模板的相关文章

字符设备模板1

源代码: /****************************************************************************** *Name: memdev.c *Desc: 字符设备驱动程序的框架结构,该字符设备并不是一个真实的物理设备, * 而是使用内存来模拟一个字符设备 *Parameter: *Return: *Author: derek *Date: 2013-6-4 ***************************************

Linux驱动开发之字符设备模板

/***************************** ** 驱动程序模板* 版本:V1* 使用方法(末行模式下):* :%s/xxx/"你的驱动名称"/g********************************/ #include <linux/mm.h>#include <linux/miscdevice.h>#include <linux/slab.h>#include <linux/vmalloc.h>#includ

字符设备模板2

源代码: #include <linux/kernel.h> #include <linux/init.h> #include <linux/types.h> #include <linux/spinlock.h> #include <linux/blkdev.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/errno.h>

字符设备集中编程模型

1.重要数据结构 - struct file_operaions - struct inode - struct file 2.杂设备驱动模板:struct miscdevice - 注册:int misc_register(struct miscdevice *misc) - 注销:int misc_deregister(struct miscdevice *misc) 3.早期标准字符设备驱动模板: - 注册:int register_chrdev(unsigned int major, c

linux字符设备驱动

一.字符设备.字符设备驱动与用户空间访问该设备的程序三者之间的关系. 如图,在Linux内核中使用cdev结构体来描述字符设备,通过其成员dev_t来定义设备号(分为主.次设备号)以确定字符设备的唯一性.通过其成员file_operations来定义字符设备驱动提供给VFS的接口函数,如常见的open().read().write()等. 在Linux字符设备驱动中,模块加载函数通过register_chrdev_region( ) 或alloc_chrdev_region( )来静态或者动态获

20150216简单的Linux字符设备驱动程序

20150216简单的Linux字符设备驱动程序 2015-02-16 李海沿 关于字符设备驱动程序详细的知识点,本文就不再介绍了,很多同志,看了知识点,还是一头雾水,写不出来,所以,本文从实战出发,带领各位同胞们来实现一个字符设备驱动程序,改程序可作为字符设备的通用模板. 好了废话不多说,先上驱动程序,在驱动程序中加入详细注释: 1 /****************************** 2 linux 字符设备驱动程序 3 *****************************/

深入浅出~Linux设备驱动之字符设备驱动

一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流的设备,常见的字符设备有鼠标.键盘.串口.控制台和LED设备等. 块设备:是指可以从设备的任意位置读取一定长度数据的设备.块设备包括硬盘.磁盘.U盘和SD卡等. 每一个字符设备或块设备都在/dev目录下对应一个设备文件.linux用户程序通过设备文件(或称设备节点)来使用驱动程序操作字符设备和块设备

字符设备驱动之Led驱动学习记录

一.概述 Linux内核就是由各种驱动组成的,内核源码中大约有85%的各种渠道程序的代码.一般来说,编写Linux设备驱动大致流程如下: 1.查看原理图,数据手册,了解设备的操作方法. 2.在内核中找到相近的驱动程序,以它为模板开发. 3.实现驱动的初始化:比如像内核注册这个驱动程序 4.设计要实现的操作:open,close,read,write等 5.实现中断服务(不是必须的) 6.编译该驱动程序到内核中,或insmod命令加载 7.测试驱动程序. 二.驱动程序的加载与卸载 module_i

【转】深入浅出:Linux设备驱动之字符设备驱动

深入浅出:Linux设备驱动之字符设备驱动 一.linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据.字符设备是面向流的设备,常见的字符设备有鼠标.键盘.串口.控制台和LED设备等. 块设备:是指可以从设备的任意位置读取一定长度数据的设备.块设备包括硬盘.磁盘.U盘和SD卡等. 每一个字符设备或块设备都在/dev目录下对应一个设备文件.linux用户程序通过设备文件(或称