当前位置:文档之家› Linux内核驱动模块编写概览-ioctl,class_create,device_create

Linux内核驱动模块编写概览-ioctl,class_create,device_create

如果你对内核驱动模块一无所知,请先学习内核驱动模块的基础知识。

如果你已经入门了内核驱动模块,但是仍感觉有些模糊,不能从整体来了解一个内核驱动模块的结构,请赏读一下这篇拙文。

如果你已经从事内核模块编程N年,并且道行高深,也请不吝赐教一下文中的疏漏错误。

本文中我将实现一个简单的Linux字符设备,旨在大致勾勒出linux内核模块的编写方法的轮廓。

其中重点介绍ioctl的用途。

我把这个简单的Linux字符设备模块命名为hello_mod.设备类型名为hello_cl ass设备名为hello该设备是一个虚拟设备,模块加载时会在/sys/class/中创建名为hello_class的逻辑设备,在/dev/中创建hello的物理设备文件。

模块名为hello_mod,可接受输入字符串数据(长度小于128),处理该输入字符串之后可向外输出字符串。

并且可以接受ioctl()函数控制内部处理字符串的方式。

例如:a.通过write函数写入“Tom”,通过ioctl函数设置langtype=chinese,通过read函数读出的数据将会是“你好!Tom/n”b.通过write函数写入“Tom”,通过ioctl函数设置langtype=english,通过read函数读出的数据将会是“hello!Tom/n”c.通过write函数写入“Tom”,通过ioctl函数设置langtype=pinyin,通过read函数读出的数据将会是“ni hao!Tom/n”一般的内核模块中不会负责设备类别和节点的创建,我们在编译完之后会得到.o或者.k o文件,然后insmod之后需要mk nod来创建相应文件,这个简单的例子中我们让驱动模块加载时负责自动创建设备类别和设备文件。

这个功能有两个步骤,1)创建设备类别文件class_cr eate();2)创建设备文件dev ice_create();关于这两个函数的使用方法请参阅其他资料。

linux设备驱动的编写相对wi ndows编程来说更容易理解一点因为不需要处理IR P,应用层函数和内核函数的关联方式浅显易懂。

比如当应曾函数对我的设备调用了open()函数,而最终这个应用层函数会调用我的设备中的自定义open()函数,这个函数要怎么写呢,我在我的设备中定义的函数名是hello_mod_open,注意函数名是可以随意定义,但是函数签名是要符合内核要求的,具体的定义是怎么样请看<linux/fs.h>static int hello_mod_open(struct inode *, struct file *);这样就定义了内核中的open函数,这只是定义还需要与我们自己的模块关联起来,这就要用到一个结构struct file_operations这个结构里面的成员是对应于设备操作的各种函数的指针。

我在设备中用到了这些函数所以就如下定义,注意下面的写法不是标准ANSI C的语法,而是GNU扩展语法。

struct file_operations hello_mod_fops ={.owner = THIS_MODULE,.open = hello_mod_open,.read = hello_mod_read,.write = hello_mod_write,.ioctl = hello_mod_ioc tl,.releas e = hello_mod_rel ease,};这个结构体变量定义好之后我们在模块初始化函数中就可以通过register_chrdev()或者填充cdev结构来关联所有的操作到我们的模块函数了。

和设备交互的数据我们总称为“数据”,但是大致可划分为两种“功能数据”:我们要输入设备处理的和设备处理完之后输出的数据。

“控制数据”:我们用来控制设备特性功能的命令和参数。

open,read,write,release等函数是对一个驱动模块的使用,就是我们对“设备的功能”的使用。

但是一个设备有可能有很多功能,那么我们要怎么控制设备让设备完成指定的功能呢?据个例子来说:假如我们有一个翻译机(姑且说机吧,也可能是器)实体设备,主要功能是输入中文,然后可以输出各种语言对应的翻译结果,那这个机的功能就是翻译,我们真正用来处理的数据是我们输入的中文,我们要得到的“设备功能”就是翻译后的输出内容,而各种语言则是我们的选择控制了,我们可设定这个设备翻译成何种语言。

这就要求我们要向设备发送命令,设定目标语言。

请注意我们要发送的是两个“控制数据”,命令和参数。

命令:一个设备可能有很多种行为,我们的命令就是代表我们要让设备执行何种行为。

“复位”,“设定目标语言”,“获得当前目标语言”等参数:对于某一个命令,可能需要参数可能不需要参数。

比如:“复位”命令就不需要参数。

“设定目标语言”则需要传入目标语言的类型。

“获取目标语言”则需要传入一个能够接收目标语言类型的参数。

自己画了一个设备“数据流”图,希望能加深理解。

对于我们自己的设备我们就要自己定义设备命令了,如果你要想搞清命令的格式,请参考其他资料关于ioctl的参数的介绍。

这里不打算介绍这个,只介绍ioctl实际功能。

定义自己的IO控制命令需要用到宏。

这里直接写出我的定义而不是介绍宏的实现#define H ELLO_MAGIC 12#define H ELLO_IOCTL_RESETLANG _IO(HELLO_MAGIC,0) //设置复位,这个命令不带参数#define H ELLO_IOCTL_GETLANG _IOR(HELLO_MAGIC,1,int) //获取当前设备的语言类型参数,参数是int型#define H ELLO_IOCTL_SETLANG _IOW(HELLO_MAGIC,2,int) //设置设备的语言类型,参数是int型多的不说了,下面贴上完整代码,懒人没写注释。

不好意思。

hello_mod.c[cpp]view plaincopyprint?1./*2. * =====================================================================================3. *4. * Filename: hello.c5. *6. * Description: hello_mod7. *8. * Version: 1.09. * Created: 01/28/2011 05:07:55 PM10. * Revision: none11. * Compiler: gcc12. *13. * Author: Tishion (shion), tishion@14. * Company: LIM15. *16. * =====================================================================================17. */18.19.20.#include <linux/module.h>21.#include <linux/init.h>22.#include <linux/kernel.h>23.#include <linux/fs.h>24.#include <linux/uaccess.h>25.#include <linux/semaphore.h>26.#include <linux/cdev.h>27.#include <linux/device.h>28.#include <linux/ioctl.h>29.#include <linux/slab.h>30.#include <linux/errno.h>31.#include <linux/string.h>32.#include "hello_mod_ioctl.h"33.34.#define MAJOR_NUM 25035.#define MINOR_NUM 036.#define IN_BUF_LEN 25637.#define OUT_BUF_LEN 51238.39.MODULE_AUTHOR("Tishion");40.MODULE_DESCRIPTION("Hello_mod driver by tish ion");41.42.static struct class * hello_class;43.static struct cdev hello_cdev;44.static dev_t devnum = 0;45.static char * modname = "hello_mod";46.static char * devicename = "hello";47.static char * classname = "hello_class";48.49.static int open_count = 0;50.static struct semaphore sem;51.static spinlock_t spin = SPIN_LOCK_UNLOCKED;52.static char * inbuffer = NULL;53.static char * outbuffer = NULL;54.static lang_t langtype;55.56.static int hello_mod_open(struct inode *, st ruct file *);57.static int hello_mod_release(struct inode *, struct file *);58.static ssize_t hello_mod_read(struct file *,char *, size_t, loff_t *);59.static ssize_t hello_mod_write(struct file *, const char *, size_t, loff_t *);60.static int hello_mod_ioctl(struct inode *, s truct file *, unsigned int, u nsigned long);61.62.struct file_operations hello_mod_fops =63.{64. .owner = THIS_MODULE,65. .open = hello_mod_open,66. .read = hello_mod_read,67. .write = hello_mod_write,68. .ioctl = hello_mod_ioctl,69. .release = hello_mod_release,70.};71.72.static int hello_mod_open(struct inode *inod e, struct file *pfile)73.{74. printk("+hello_mod_open()!/n");75. spin_lock(&spin);76. if(open_count)77. {78. spin_unlock(&spin);79. return -EBUSY;80. }81. open_count++;82. spin_unlock(&spin);83. printk("-hello_mod_open()!/n");84. return 0;85.}86.static int hello_mod_release(struct inode *i node, struct file *pfile)87.{88. printk("+hello_mod_release()!/n");89. open_count--;90. printk("-hello_mod_release()!/n");91. return 0;92.}93.static ssize_t hello_mod_read(struct file *p file, char *user_buf, size_t len, loff_t *off)94.{95. printk("+hello_mod_read()!/n");96.97. if(down_interruptible(&sem))98. {99. return -ERESTARTSYS;100. }101. memset(outbuffer, 0, OUT_BUF_LEN);102. printk(" +switch()/n");103. switch(langtype)104. {105. case english:106. printk(" >in case: englis h/n");107. sprintf(outbuffer, "Hello! %s.", inbuffer);108. break;109. case chinese:110. printk(" >in case: chines e/n");111. sprintf(outbuffer, "你好! %s.", inbu ffer);112. break;113. case pinyin:114. printk(" >in case: pinyin/n");115. sprintf(outbuffer, "ni hao! %s.", inbuffer);116. break;117. default:118. printk(" >in case: defaul t/n");119. break;120. }121. printk(" -switch()/n");122. if(copy_to_user(user_buf, outbuffer, len))123. {124. up(&sem);125. return -EFAULT;126. }127. up(&sem);128. printk("-hello_mod_read()!/n");129. return 0;130.}131.static ssize_t hello_mod_write(struct file *pfile, const char *user_buf, size_t len, loff_t *off)132.{133. printk("+hello_mod_write()!/n");134. if(down_interruptible(&sem))135. {136. return -ERESTARTSYS;137. }138. if(len > IN_BUF_LEN)139. {140. printk("Out of input buffer/n");141. return -ERESTARTSYS;142. }143. if(copy_from_user(inbuffer, user_buf, le n))144. {145. up(&sem);146. return -EFAULT;147. }148. up(&sem);149. printk("-hello_mod_write()!/n");150. return 0;151.}152.static int hello_mod_ioctl(struct inode *ino de, struct file *pfile, unsig ned int cmd, unsigned long arg) 153.{154.int err = 0;155. printk("+hello_mod_ioctl()!/n");156. printk(" +switch()/n");157. switch(cmd)158. {159. case HELLO_IOCTL_RESETLANG:160. printk(" >in case: HELLO_IOCTL_RESETLANG/n");161. langtype = english;162. break;163. case HELLO_IOCTL_GETLANG:164. printk(" >in case: HELLO_IOCTL_GETLANG/n");165. err = copy_to_user((int *)arg, &langtype, sizeof(int));166. break;167. case HELLO_IOCTL_SETLANG:168. printk(" >in case: HELLO_IOCTL_SETLANG/n");169. err = copy_from_user(&langtype,(int *)arg, sizeof(int));170. break;171. default:172. printk(" >in case: defaul t/n");173. err = ENOTSUPP;174. break;175. }176. printk(" -switch()/n");177. printk("-hello_mod_ioctl()!/n");178. return err;179.}180.static int __init hello_mod_init(void)181.{182.int result;183. printk("+hello_mod_init()!/n");184. devnum = MKDEV(MAJOR_NUM, MINOR_NUM);185. result = register_chrdev_region(devnum, 1, modname);186.187. if(result < 0)188. {189. printk("hello_mod : can't get major number!/n");190. return result;191. }192.193. cdev_init(&hello_cdev, &hello_mod_fops);194. hello_cdev.owner = THIS_MODULE;195. hello_cdev.ops = &hello_mod_fops;196. result = cdev_add(&hello_cdev, devnum, 1);197. if(result)198. printk("Failed at cdev_add()");199. hello_class = class_create(THIS_MODULE, classname);200. if(IS_ERR(hello_class))201. {202. printk("Failed at class_create().Ple ase exec [mknod] before opera te the device/n"); 203. }204. else205. {206. device_create(hello_class, NULL, dev num,NULL, devicename);207. }208.209. open_count = 0;210. langtype = english;211. inbuffer = (char *)kmalloc(IN_BUF_LEN, G FP_KERNEL);212. outbuffer = (char *)kmalloc(OUT_BUF_LEN, GFP_KERNEL);213. init_MUTEX(&sem);214. printk("-hello_mod_init()!/n");215. return 0;216.}217.218.static void __exit hello_mod_exit(void)219.{220. printk("+hello_mod_exit!/n");221. kfree(inbuffer);222. kfree(outbuffer);223. cdev_del(&hello_cdev);224. device_destroy(hello_class, devnum);225. class_destroy(hello_class);226. unregister_chrdev_region(devnum, 1);227. printk("-hello_mod_exit!/n");228. return ;229.}230.231.module_init(hello_mod_init);232.module_exit(hello_mod_exit);233.MODULE_LICENSE("GPL");hello_mod_i otcl.h[cpp]view plaincopyprint?1./*2. * =====================================================================================3. *4. * Filename: hello_mod_ioctl.h5. *6. * Description: define the cmd supported by hello_mod7. *8. * Version: 1.09. * Created: 06/19/2011 10:24:20 PM10. * Revision: none11. * Compiler: gcc12. *13. * Author: Tishion (shion), tishion@14. * Company: LIM15. *16. * =====================================================================================17. */18.19.#ifndef __HELLO_MOD_IOCTL_H__20.#define __HELLO_MOD_IOCTL_H__21.22.#define HELLO_MAGIC 1223.#define HELLO_IOCTL_RESETLANG _IO(HELLO_MA GIC,0) //set langtype = english24.#define HELLO_IOCTL_GETLANG _IOR(HELLO_M AGIC,1,int) //get langtype25.#define HELLO_IOCTL_SETLANG _IOW(HELLO_M AGIC,2,int) //set langtype26.27.typedef enum _lang_t28.{29. english, chinese, pinyin30.}lang_t;31.32.#endifMakefile[cpp]view plaincopyprint?1.#**********************************************2.# Makefile linux 2.6 Module3.# This makefile is written for Ubuntu 10.104.# It may not perfomance without erros on the5.# other version or distributions.6.#**********************************************7.# BY:tishion8.# Mail:tishion@9.# 2011/06/1910.#**********************************************11.obj-m += hello_mod.o12.CURRENT_PATH := $(shell pwd)13.LINUX_KERNEL := $(shell uname -r)14.LINUX_KERNEL_PATH := /usr/src/linux-headers-$(LINUX_KERNEL)15.all:16. make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules17.clean:18. make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean19.install:20. insmod hello_mod.ko21.unistall:22. rmmod hello_mod附上一用户层测试文件,编译后需要root身份执行。

相关主题