LINUX字符设备驱动编写基本流程

合集下载

linux驱动开发知识点总结

linux驱动开发知识点总结

linux驱动开发知识点总结Linux驱动开发是指在Linux操作系统下开发和编写设备驱动程序的过程。

Linux作为一种开源操作系统,具有广泛的应用领域,因此对于驱动开发的需求也非常重要。

本文将从驱动程序的概念、驱动开发的基本步骤、常用的驱动类型以及驱动开发的注意事项等方面进行总结。

一、驱动程序的概念驱动程序是指控制计算机硬件和软件之间通信和交互的程序。

在Linux系统中,驱动程序负责与硬件设备进行交互,实现对硬件的控制和管理。

二、驱动开发的基本步骤1. 确定驱动的类型:驱动程序可以分为字符设备驱动、块设备驱动和网络设备驱动等。

根据具体的硬件设备类型和需求,选择合适的驱动类型。

2. 编写设备注册函数:设备注册函数用于向系统注册设备,使系统能够识别和管理该设备。

3. 实现设备的打开、关闭和读写操作:根据设备的具体功能和使用方式,编写设备的打开、关闭和读写操作函数。

4. 实现设备的中断处理:如果设备需要进行中断处理,可以编写中断处理函数来处理设备的中断请求。

5. 编写设备的控制函数:根据设备的需求,编写相应的控制函数来实现对设备的控制和配置。

6. 编译和安装驱动程序:将编写好的驱动程序进行编译,并将生成的驱动模块安装到系统中。

三、常用的驱动类型1. 字符设备驱动:用于控制字符设备,如串口、打印机等。

字符设备驱动以字符流的方式进行数据传输。

2. 块设备驱动:用于控制块设备,如硬盘、U盘等。

块设备驱动以块为单位进行数据传输。

3. 网络设备驱动:用于控制网络设备,如网卡。

网络设备驱动实现了数据包的收发和网络协议的处理。

4. 触摸屏驱动:用于控制触摸屏设备,实现触摸操作的识别和处理。

5. 显示驱动:用于控制显示设备,实现图像的显示和刷新。

四、驱动开发的注意事项1. 熟悉硬件设备的规格和寄存器的使用方法,了解硬件设备的工作原理。

2. 确保驱动程序的稳定性和可靠性,避免出现系统崩溃或死机等问题。

3. 对于需要频繁访问的设备,要考虑性能问题,尽量减少对硬件的访问次数。

实验二:字符设备驱动实验

实验二:字符设备驱动实验

实验二:字符设备驱动实验一、实验目的通过本实验的学习,了解Linux操作系统中的字符设备驱动程序结构,并能编写简单的字符设备的驱动程序以及对所编写的设备驱动程序进行测试,最终了解Linux操作系统如何管理字符设备。

二、准备知识字符设备驱动程序主要包括初始化字符设备、字符设备的I/O调用和中断服务程序。

在字符设备驱动程序的file_operations结构中,需要定义字符设备的基本入口点。

open()函数;release()函数read()函数write()函数ioctl()函数select()函数。

另外,注册字符设备驱动程序的函数为register_chrdev()。

register_chrdev() 原型如下:int register_chrdev(unsigned int major, //主设备号const char *name, //设备名称struct file_operations *ops); //指向设备操作函数指针其中major是设备驱动程序向系统申请的主设备号。

如果major为0,则系统为该驱动程序动态分配一个空闲的主设备号。

name是设备名称,ops是指向设备操作函数的指针。

注销字符设备驱动程序的函数是unregister_chrdev(),原型如下:int unregister_chrdev(unsigned int major,const char *name);字符设备注册后,必须在文件系统中为其创建一个设备文件。

该设备文件可以在/dev目录中创建,每个设备文件代表一个具体的设备。

使用mknod命令来创建设备文件。

创建设备文件时需要使用设备的主设备号和从设备号作为参数。

阅读教材相关章节知识,了解字符设备的驱动程序结构。

三、实验内容根据教材提供的实例。

编写一个简单的字符设备驱动程序。

要求该字符设备包括open()、write()、read()、ioctl()和release()五个基本操作,并编写一个测试程序来测试所编写的字符设备驱动程序。

Linux中设备驱动程序的编写

Linux中设备驱动程序的编写
中 图 分 类 号 :P 1 .9 T 3 6 8
Ln x作 为 源代 码 开 放 、 定 、 iu 稳 网络 功 能强 大 的免 费系统 , 经越 来越 广泛 应 用 于各个 领域 。 已 Ln x系统是从 Unx系统 演 变而来 , iu i 故其 驱 动程 序 的设计 思 想 和 Unx系 统 驱 动 程 序 相 似 。 当前 , i 还 有 相 当一部 分特殊 设备在 L n x系统 中找不到驱 动 iu
1 Ln x中的 设备 i u
‘ 1 1 设 备 文 件 .
化 。这部 分驱动 程序仅 在初 始化 的时候 调用一 次 。
() 2 服务 于 IO请求 的子程序 /
调 用 这 部 分 是 由 于 系 统 调 用 的 结 果 。这 部 分 程
每个设 备用 “ 备 文件 ” 代 表 , 有 对硬 件 设 设 来 所
2 Ln x中 的设 备 驱 动 程 序 的 编 写 i u
2 1 Ln x下 设 备 驱 动 程 序 的 基 本 结 构 . i u
对 用 户程 序 而 言 , 备 驱 动程 序 隐藏 了设 备 的 设 具体细 节 , 对各 种 不 同设 备提 供 了一 致 的接 口。一 般设 备驱动程 序可 以分 为三个 主要 组成部 分 : () 1 自动 配置 和初始 化子 程序 自动配 置和 初 始 化 子 程 序 常 在 相应 的 d ie— r r v ii ) nt 中实 现 , ( 负责检 测所要 驱动 的硬件 设备 是 否存 在和是 否 能正常 工 作 。如 果该 设 备 正 常 , 则对 这 个 设 备及其 相关 的设备驱 动程 序需要 的状 态进行 初始
文 件和字 符设备 文件 。通过 块 ( 符) 备文件 存取 字 设 的设备称 为块 ( 字符 ) 设备 。 1 3 设备 号 的表示 . 设备 由一个 主设 备号和 一个 次设 备号标识 。主

嵌入式linux驱动开发流程

嵌入式linux驱动开发流程
当应用程序使用open、release等函数打开某个设备时,设备驱动程序的file_operations结构中的相应成员就会被调用。
三、设备的中断和轮询处理
对于不支持中断的设备,读写时需要轮询设备状态,以及是否需要继续进行数据传输。例如,打印机。如果设备支持中断,则可按照中断方式进行。
struct file_operations Key7279_fops =
{
.open = Key7279_Open,
.ioctl = Key7279_Ioctl,
.release = Key7279_Close,
.read = Key7279_Read,
};
1、 设备的打开和释放
模块在使用中断前要先请求一个中断通道(或者 IRQ中断请求),并在使用后释放它。通过request_irq()函数来注册中断,free_irq()函数来释放。
四、驱动程序的测试
对驱动程序的调试可以通过打印的方式来进行,就是通过在驱动程序中添加printk()打印函数,来跟踪驱动程序的执行过程,以此来判断问题。
◇ 设备的打开和释放。
ห้องสมุดไป่ตู้◇ 设备的读写操作。
◇ 设备的控制操作。
◇ 设备的中断和轮询处理。
Linux主要将设备分为三类:字符设备、块设备和网络设备。字符设备是指发送和接收数据以字符的形式进行,没有缓冲区的设备;块设备是指发送和接收数据以整个数据缓冲区的形式进行的设备;网络设备是指网络设备访问的BSD socket 接口。下面以字符设备为例,写出其驱动编写框架:
二、 构造file_operations结构中要用到的各个成员函数
Linux操作系统将所有的设备都看成文件,以操作文件的方式访问设备。应用程序不能直接操作硬件,使用统一的接口函数调用硬件驱动程序,这组接口被成为系统调用。每个系统调用中都有一个与之对应的函数(open、release、read、write、ioctl等),在字符驱动程序中,这些函数集合在一个file_operations类型的数据结构中。以一个键盘驱动程序为例:

linux字符驱动框架(用户态的read,write,poll是怎么操作驱动的)

linux字符驱动框架(用户态的read,write,poll是怎么操作驱动的)

linux字符驱动框架(⽤户态的read,write,poll是怎么操作驱动的)前⾔这篇⽂章是通过对⼀个简单字符设备驱动的操作来解释,⽤户态的读写操作是怎么映射到具体设备的。

因为针对不同版本的linux内核,驱动的接⼝函数⼀直有变化,这贴出我测试的系统信息:root@ubuntu:~/share/dev/cdev-2# cat /etc/os-release |grep -i verVERSION="16.04.5 LTS (Xenial Xerus)"VERSION_ID="16.04"VERSION_CODENAME=xenialroot@ubuntu:~/share/dev/cdev-2#root@ubuntu:~/share/dev/cdev-2# uname -r4.15.0-33-generic字符驱动这⾥给出了⼀个不怎么标准的驱动,定义了⼀个结构体 struct dev,其中buffer成员模拟驱动的寄存器。

由wr,rd作为读写指针,len作为缓存buffer的长度。

具体步骤如下:1. 定义 init 函数,exit函数,这是在 insmod,rmmod时候调⽤的。

2. 定义驱动打开函数open,这是在⽤户态打开设备时候调⽤的。

3. 定义release函数,这是在⽤户态关闭设备时候⽤到的。

4. 定义read,write,poll函数,并挂接到 file_operations结构体中,所有⽤户态的read,write,poll都会最终调到这些函数。

chardev.c/*参考:深⼊浅出linux设备驱动开发*/#include <linux/module.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/uaccess.h>#include <linux/wait.h>#include <linux/semaphore.h>#include <linux/sched.h>#include <linux/cdev.h>#include <linux/types.h>#include <linux/kdev_t.h>#include <linux/device.h>#include <linux/poll.h>#define MAXNUM 100#define MAJOR_NUM 400 //主设备号 ,没有被使⽤struct dev{struct cdev devm; //字符设备struct semaphore sem;int flag;poll_table* table;wait_queue_head_t outq;//等待队列,实现阻塞操作char buffer[MAXNUM+1]; //字符缓冲区char *rd,*wr,*end; //读,写,尾指针}globalvar;static struct class *my_class;int major=MAJOR_NUM;static ssize_t globalvar_read(struct file *,char *,size_t ,loff_t *);static ssize_t globalvar_write(struct file *,const char *,size_t ,loff_t *);static int globalvar_open(struct inode *inode,struct file *filp);static int globalvar_release(struct inode *inode,struct file *filp);static unsigned int globalvar_poll(struct file* filp, poll_table* wait);/*结构体file_operations在头⽂件 linux/fs.h中定义,⽤来存储驱动内核模块提供的对设备进⾏各种操作的函数的指针。

如何编写Linux设备驱动程序

如何编写Linux设备驱动程序

Ke wo d y rs: L n x S s e i u y t m;D v c r v r e i e D i e
0 引言
w i e c oe等。如何把系统调用和驱动 程序关联起来 , rt 、l s 这 需要 了解一个 非常关键的数 据结 构:i e o ea i n 。 f l— p r t o s 这个

样对硬件设备进行操作。设备驱动程序是 内核的一 部分 ,
①对设备初始化和释放 ;
它完成以下功 能: ②把数据传送到硬 件和从硬件读取数据 ; ④读 取应用程序传送给设备 文件的数据和 回送 应用程 序请求 的数据 ; ④检查和处理设备 出现 的错误。
2 实 例 剖 析
f l ’i ec a b f i tc u t ( ie f l ,hr u ,n o n )
关 键 词 :Ln x 作 系统 ; 备 驱 动 程 序 iu 操 设
中图分类号 :T 3 4 P1
文献标识码 : A
文章编号 : 6 1 49 一 20 )— 09 0 1 7 — 7 2 (088 09 — 2
Ab t c : T e o c p o i u d v c d i e s a i t o u e , a d h n h i t r a m c a i m a d h s r t h c n e t f L n x e i e r v r w s n r d c d a n t e t e n e n l e h n s s n t e
1 iu e i r e 的概 念 l xd vc di r n e v
结构的每一个成员的名字都对应着一个系统调用。 用户进程 利用系统调用在对设备 文件进行诸如 ra/rt edwie操作时, 系统调 用通过设备文件 的主设 备号找 到相应 的设备 驱动程

基于linux2.6内核的字符设备驱动程序设计

基于linux2.6内核的字符设备驱动程序设计

, sr t t uc
o ne = TH I M ODULE; w r S
… … … … …
//获 取 字 符 设 备 号
2ce d v结构体
存 ln x2. 核 中 , 用 c e 结 构 体 描 iu 6内 使 dv 述 一 个 字 符 设 备 , d v结 构 体 定 义 如 下 : ce

_
的 结构 体 , 中 包 含设 备所 涉 及 到 的c e 其 d v、 私有数据及信号量等信息 。 下所 示: 如 / 设 备 结 构 体 /
sr t t uc XXX—d v t —e
— —
{, i e t l f t ) sz , of ;
_ —

s r t de c v; t uc c v de
s ie sz
— —
ቤተ መጻሕፍቲ ባይዱ
/ /从 设 备 中 同 步 读 取 数 据 t* ie(tutf e} h r u r (wrt) rc i .c a s s l e

}, i e t, l f t { ; sz of )
//该 设 备 其 他 的 私 有 数 据 和 信 号 量 的 信 息 的 定 义
… …
/ /向 设 备 发 送 数 据 u sg e n * o1 sr c i ,src n in d it(p l(tutfl ) e十 tu t
p l t bl sr c ) o l a e tu t :

}X —e X X d v; 模 块 加 载 和 卸 载 函数 的 形 式 如 下 : /+ 备 驱 动 模 块 加 载 函 数 / 设

计 算机技 术 ・
基 于 l u 26 i x . 内核 的字 符 设 备驱 动程 序设 计 n

linux-GPIO驱动实验

linux-GPIO驱动实验

GPIO驱动实验一、实验目的1.理解Linux GPIO驱动程序的结构、原理。

2.掌握Linux GPIO驱动程序的编程。

3.掌握Linux GPIO动态加载驱动程序模块的方法。

二、实验内容1. 编写GPIO字符设备驱动程序。

2. 编写Makefile文件。

3. 编写测试程序。

4. 调试GPIO驱动程序和测试程序。

三、实验设备1.硬件:PC机,基于ARM9系统教学实验系统实验箱1台;网线;串口线,电压表。

2.软件:PC机操作系统;Putty;服务器Linux操作系统;arm-v5t_le-gcc交叉编译环境。

3.环境:ubuntu12.04.4;文件系统版本为filesys_clwxl;烧写的内核版本为uImage_slh_gpio,编译成的驱动模块为davinci_dm365_gpios.ko,驱动源码见GPIO文件夹。

四.预备知识4.1 概述在嵌入式系统中,常常有数量众多,但是结构却比较简单的外部设备/电路,对这些设备/电路有的需要CPU为之提供控制手段,有的则需要被CPU用作输入信号。

而且,许多这样的设备/电路只要求一位,即只要有开/关两种状态就够了,例如灯的亮与灭。

对这些设备/电路的控制,使用传统的串行口或并行口都不合适。

所以在微控制器芯片上一般都会提供一个通用可编程I/O接口,即GPIO (General Purpose Input Output)。

GPIO的驱动主要就是读取GPIO口的状态,或者设置GPIO口的状态。

就是这么简单,但是为了能够写好的这个驱动,在LINUX上作了一些软件上的分层。

为了让其它驱动可以方便的操作到GPIO,在LINUX里实现了对GPIO操作的统一接口,这个接口实则上就是GPIO驱动的框架。

在本实验中,将编写简单的GPIO驱动程序来控制LCD液晶屏屏幕的亮灭,然后动态加载模块,并编写测试程序,以验证驱动程序。

4.2 实现的功能1> 设置对应的GPIO口为输出。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

---简介Linux下的MISC简单字符设备驱动虽然使用简单,但却不灵活。

只能建立主设备号为10的设备文件。

字符设备比较容易理解,同时也能够满足大多数简单的硬件设备,字符设备通过文件系统中的名字来读取。

这些名字就是文件系统中的特殊文件或者称为设备文件、文件系统的简单结点,一般位于/dev/目录下使用ls进行查看会显示以C开头证明这是字符设备文件crw--w---- 1 root tty 4, 0 4月 14 11:05 tty0。

第一个数字是主设备号,第二个数字是次设备号。

---分配和释放设备编号1)在建立字符设备驱动时首先要获取设备号,为此目的的必要的函数是register_chrdev_region,在linux/fs.h中声明:int register_chrdev_region(dev_t first, unsigned int count, char *name);first是你想要分配的起始设备编号,first的次编号通常是0,count是你请求的连续设备编号的总数。

count如果太大会溢出到下一个主设备号中。

name是设备的名字,他会出现在/proc/devices 和sysfs中。

操作成功返回0,如果失败会返回一个负的错误码。

2)如果明确知道设备号可用那么上一个方法可行,否则我们可以使用内核动态分配的设备号int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name);dev是个只输出的参数,firstminor请求的第一个要用的次编号,count和name的作用如上1)对于新驱动,最好的方法是进行动态分配3)释放设备号,void unregister_chrdev_region(dev_t first unsigned int count);---文件操作file_operations结构体,内部连接了多个设备具体操作函数。

该变量内部的函数指针指向驱动程序中的具体操作,没有对应动作的指针设置为NULL。

1)fops的第一个成员是struct module *owner 通常都是设置成THIS_MODULE。

linux/module.h中定义的宏。

用来在他的操作还在被使用时阻止模块被卸载。

2)loff_t (*llseek) (struct file *, loff_t, int);该方法用以改变文件中的当前读/写位置返回新位置。

3)ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);该函数用以从设备文件中读取数据,读取成功返回读取的字节数。

4)ssize_t (*write) (struct file *, const char __user *,size_t , loff_t *);该函数用以向设备写入数据,如果成功返回写入的字节数。

5)int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);ioctl系统调用提供发出设备特定命令的方法。

6)int (*open) (struct inode *, struct file *);设备文件进行的第一个操作,打开设备文件。

7)int (*release) (struct inode *, struct file *);释放文件结构函数指针。

一般初始化该结构体如下:struct file_operations fops = {.owner = THIS_MODULE, .llseek = xxx_llseek, .read = xxx_read, .write = xxx_write,.ioctl = xxx_ioctl, .open = xxx_open, .release = xxx_release };PS:以上的文件操作函数指针并不是全部,只是介绍了几个常用的操作。

---文件结构struct file定义在linux/fs.h中,是设备驱动中第二个最重要的数据结构,此处的file 和用户空间程序中的FILE指针没有关系。

前者位于内核空间,后者位于用户控件。

文件结构代表一个打开的文件。

(他不特定给设备驱动;系统中每个打开的文件有一个关联的struct file在内核空间)。

它由内核在open时创建,并可以传递给文件件操作函数,文件关闭之后,内核释放数据结构。

1)mode_t f_mode。

确定文件读写模式2)loff_t f_ops。

当前读写位置3)unsigned int f_flags 。

文件标志,O_RDONLY、O_NONBLOCK,4)struct file_operations *f_op。

关联文件相关操作5)void *private_data。

open系统调用设置该指针NULL,指向分配的数据。

6)struct dentry *f_dentry。

关联到文件的目录入口dentry结构。

---inode结构inode结构由内核在内部用来表示文件。

它和代表打开文件描述符的文件结构是不同的。

inode结构包含大量关于文件的信息。

作为通用规则,这个结构只有两个成员对驱动代码有作用。

dev_t i_rdev。

对于代表设备文件的节点,这个成员包含实际的设备编号。

struct cdev *i_cdev。

内核内部结构,代表字符设备。

---字符设备注册在内核调用你的设备操作前,你编写分配并注册一个或几个struct cdev.struct cdev *my_cdev = cdev_alloc(); my_cdev-;ops = &amp;my_fops;或者定义成static均可。

对定义的cdev变量进行初始化,可以使用专门的函数,或者使用如上的方法。

cdev_init( my_cdev, &amp;my_fops); 其实上边的两行代码就是做了这个函数的工作。

最后告诉内核该cdev。

cdev_add(struct cdev *dev, dev_t num, unsigned int count);/*上述总结,到此关于设备文件相关的结构数据以及如何注册销毁等操作相关的函数基本上都已经介绍完毕。

主要的还是要设计具体操作的函数来实现具体的逻辑操作*/以下代码整理、摘录自《Android深度探索HAL与驱动开发-李宁》LED驱动篇#include#include#include#include#include#include#include#deifne DEVICE_NAME s3c6410_leds#define DEVICE_COUNT 1#define S3C6410_LEDS_MAJOR 0#define S3C6410_LEDS_MINOR 234#define PARAM_SIZE 3static int major = S3C6410_LEDS_MAJOR;static int minor = S3C6410_LEDS_MINOR;static dev_t dev_number;static int leds_state = 1;static char *params[] = {string1,string2,string3};static iint param_size = PARAM_SIZE;static struct class *leds_class = NULL;static int s3c6410_leds_ioctl (struct file *file, unsigned int cmd, unsigned long arg){switch (cmd){unsigned tmp;case 0:case 1:if (arg ; 4)return -EINVAL;tmp = ioread32 (S3C64XX_GPMDAT);if (cmd == 1)tmp &amp;= (~(1 &lt;&lt; arg));elsetmp |= (1 &lt;&lt; arg);iowrite32 (tmp, S3C64XX_GPMDAT);return 0;default : return -EINVAL;}}static ssize_t s3c6410_leds_write (struct file *file, const char __user *buf, size_t count, loff_t *ppos){unsigned tmp = count;unsigned long i = 0;memset(mem, 0, 4);if (count ; 4)tmp = 4;if (copy_from_user (mem, buf, tmp) )return -EFAULT;else{for( i=0; i&lt;4; i++){tmp = ioread32(S3C64XX_GPMDAT);if (mem[i] == '1')tmp &amp;= (~(1 &lt;&lt; i));elsetmp |= (1 &lt;&lt; i);iowrite32(tmp, S3C64XX_GPMDAT);}return count;}}static struct file_operations dev_fops ={.owner = THIS_MODULE, .unlocked_ioctl = s3c6410_leds_ioctl, .write = s3c6410_leds_write};static struct cdev leds_cdev;static int leds_create_device(void){int ret = 0;int err = 0;cdev_init (&amp;leds_cdev, &amp;dev_fops);leds_cdev.owner = THIS_MODULE;if (major ; 0){dev_number = MKDEV(major,minor);err = register_chrdev_region(dev_number, DEVICE_COUNT, DEVICE_NAME);if (err &lt; 0){printk(KERN_WANRING register_chrdev_region errorn);return err}}else{err = alloc_chrdev_region(&amp;leds_cdev.dev, 10, DEVICE_COUNT, DEVICE_NAME); if(err &lt; 0){printk (KERN_WARNING alloc_chrdev_region errorn);return err;}major = MAJOR(leds_cdev.dev);major = MINOR(leds_cdev.dev);dev_number = leds_cdev.dev;}ret = cdev_add(&amp;leds_cdev,dev_number, DEVICE_COUNT);leds_class = class_create (THIS_MODULE, DEVICE_NAME);device_create (leds_class, NULL, dev_number, NULL, DEVICE_NAME); return ret;}static void leds_init_gpm(int leds_default){int tmp = 0;tmp = ioread32(S3C64XX_GPMCON);tmp &amp;= (~0xffff);tmp |= 0x1111;iowrite32(tmp,S3C64XX_GPMCON);tmp = ioread32(S3C64XX_GPMPUD);tmp &amp;= (~0XFF);tmp |= 0xaa;iowrite32(tmp,S3C64XX_GPMPUD);tmp = ioread32(S3C64XX_GPMDAT);tmp &amp;= (~0xf);tmp |= leds_default;iowrite32(tmp, S3C64XX_GPMDAT);}static leds_init( void){int ret;ret = leds_create_device();leds_init_gpm (~leds_state);printk(DEVICE_NAMEtinitializedn);return ret;}static void leds_destroy_device(void){device_destroy(leds_class, dev_number);if(leds_class)class_destroy(leds_class);unregister_chrdev_region(dev_number, DEVICE_NAME); }static void leds_exit(void){leds_destroy_device();printk(DEVICE_NAMEtexitn);}module_init(leds_init);module_exit(leds_exit);module_param(leds_state, int, S_IRUGO|S_IWUSR);module_param_array(params, charp, ?m_size, S_IRUGO|S_IWUSR); MODULE_LICENSE(GPL);MODULE_AUTHOR(lining);。

相关文档
最新文档