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

合集下载

linux驱动开发(一)

linux驱动开发(一)

linux驱动开发(⼀)1:驱动开发环境要进⾏linux驱动开发我们⾸先要有linux内核的源码树,并且这个linux内核的源码树要和开发板中的内核源码树要⼀直;⽐如说我们开发板中⽤的是linux kernel内核版本为2.6.35.7,在我们ubuntu虚拟机上必须要有同样版本的源码树,我们再编译好驱动的的时候,使⽤modinfo XXX命令会打印出⼀个版本号,这个版本号是与使⽤的源码树版本有关,如果开发板中源码树中版本与modinfo的版本信息不⼀致使⽆法安装驱动的;我们开发板必须设置好nfs挂载;这些在根⽂件系统⼀章有详细的介绍;2:开发驱动常⽤的⼏个命令lsmod :list moduel 把我们机器上所有的驱动打印出来,insmod:安装驱动rmmod:删除驱动modinfo:打印驱动信息3:写linux驱动⽂件和裸机程序有很⼤的不同,虽然都是操作硬件设备,但是由于写裸机程序的时候是我们直接写代码操作硬件设备,这只有⼀个层次;⽽我们写驱动程序⾸先要让linux内核通过⼀定的接⼝对接,并且要在linux内核注册,应⽤程序还要通过内核跟应⽤程序的接⼝相关api来对接;4:驱动的编译模式是固定的,以后编译驱动的就是就按照这个模式来套即可,下⾯我们来分下⼀下驱动的编译规则:#ubuntu的内核源码树,如果要编译在ubuntu中安装的模块就打开这2个#KERN_VER = $(shell uname -r)#KERN_DIR = /lib/modules/$(KERN_VER)/build# 开发板的linux内核的源码树⽬录KERN_DIR = /root/driver/kernelobj-m += module_test.oall:make -C $(KERN_DIR) M=`pwd` modulescp:cp *.ko /root/porting_x210/rootfs/rootfs/driver_test.PHONY: cleanclean:make -C $(KERN_DIR) M=`pwd` modules cleanmake -C $(KERN_DIR) M=`PWD` modules这句话代码的作⽤就是到 KERN_DIR这个⽂件夹中 make modules把当前⽬录赋值给M,M作为参数传到主⽬录的Makefile中,实际上是主⽬录的makefile中有⽬标modules,下⾯有⼀定的规则来编译驱动;#KERN_VER = $(shell uname -r)#KERN_DIR = /lib/modules/$(KERN_VER)/build我们在ubuntu中编译内核的时候⽤这两句代码,因为在ubuntu中为我们保留了⼀份linux内核的源码树,我们编译的时候直接调⽤那个源码树的主Makefile以及⼀些头⽂件、内核函数等;了解规则以后,我们设置好KERN_DIR、obj-m这两个变量以后直接make就可以了;经过编译会得到下⾯⼀些⽂件:下⾯我们可以使⽤lsmod命令来看⼀下我们ubuntu机器现有的⼀些驱动可以看到有很多的驱动,下⾯我们使⽤insmod XXX命令来安装驱动,在使⽤lsmod命令看⼀下实验现象可以看到我们刚才安装的驱动放在了第⼀个位置;使⽤modinfo来打印⼀下驱动信息modinfo xxx.ko这⾥注意vermagic 这个的1.8.0-41是你⽤的linux内核源码树的版本号,只有这个编译的版本号与运⾏的linux内核版本⼀致的时候,驱动程序才会被安装注意license:GPL linux内核开元项⽬的许可证⼀般都是GPL这⾥尽量设置为GPL,否则有些情况下会出现错误;下⾯使⽤rmmod xxx删除驱动;-------------------------------------------------------------------------------------5:下⾯我们分析⼀下驱动。

Linux设备驱动之Ioctl控制

Linux设备驱动之Ioctl控制

Linux设备驱动之Ioctl控制 ⼤部分驱动除了需要具备读写设备的能⼒之外,还需要具备对硬件控制的能⼒。

 ⼀、在⽤户空间,使⽤ioctl系统调⽤来控制设备,原型如下:int ioctl(int fd,unsigned long cmd,...);/*fd:⽂件描述符cmd:控制命令...:可选参数:插⼊*argp,具体内容依赖于cmd*/ ⽤户程序所作的只是通过命令码告诉驱动程序它想做什么,⾄于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。

⼆、驱动ioctl⽅法:int (*ioctl) (struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg);/*inode与filp两个指针对应于应⽤程序传递的⽂件描述符fd,这和传递open⽅法的参数⼀样。

cmd 由⽤户空间直接不经修改的传递给驱动程序arg 可选。

*/ 在驱动程序中实现的ioctl函数体内,实际上是有⼀个switch {case}结构,每⼀个case对应⼀个命令码,做出⼀些相应的操作。

怎么实现这些操作,这是每⼀个程序员⾃⼰的事情,因为设备都是特定的。

关键在于怎么样组织命令码,因为在ioctl中命令码是唯⼀联系⽤户程序命令和驱动程序⽀持的途径。

在Linux核⼼中是这样定义⼀个命令码的:____________________________________| 设备类型 | 序列号 | ⽅向 | 数据尺⼨ ||----------|--------|------|-------- || 8 bit | 8 bit | 2 bit |8~14 bit||----------|--------|------|-------- | 这样⼀来,⼀个命令就变成了⼀个整数形式的命令码。

但是命令码⾮常的不直观,所以Linux Kernel中提供了⼀些宏,这些宏可根据便于理解的字符串⽣成命令码,或者是从命令码得到⼀些⽤户可以理解的字符串以标明这个命令对应的设备类型、设备序列号、数据传送⽅向和数据传输尺⼨。

Linux设备驱动程序原理及框架-内核模块入门篇

Linux设备驱动程序原理及框架-内核模块入门篇

Linux设备驱动程序原理及框架-内核模块入门篇内核模块介绍应用层加载模块操作过程内核如何支持可安装模块内核提供的接口及作用模块实例内核模块内核模块介绍Linux采用的是整体式的内核结构,这种结构采用的是整体式的内核结构,采用的是整体式的内核结构的内核一般不能动态的增加新的功能。

为此,的内核一般不能动态的增加新的功能。

为此,Linux提供了一种全新的机制,叫(可安装) 提供了一种全新的机制,可安装) 提供了一种全新的机制模块” )。

利用这个机制“模块”(module)。

利用这个机制,可以)。

利用这个机制,根据需要,根据需要,在不必对内核重新编译链接的条件将可安装模块动态的插入运行中的内核,下,将可安装模块动态的插入运行中的内核,成为内核的一个有机组成部分;成为内核的一个有机组成部分;或者从内核移走已经安装的模块。

正是这种机制,走已经安装的模块。

正是这种机制,使得内核的内存映像保持最小,的内存映像保持最小,但却具有很大的灵活性和可扩充性。

和可扩充性。

内核模块内核模块介绍可安装模块是可以在系统运行时动态地安装和卸载的内核软件。

严格来说,卸载的内核软件。

严格来说,这种软件的作用并不限于设备驱动,并不限于设备驱动,例如有些文件系统就是以可安装模块的形式实现的。

但是,另一方面,可安装模块的形式实现的。

但是,另一方面,它主要用来实现设备驱动程序或者与设备驱动密切相关的部分(如文件系统等)。

密切相关的部分(如文件系统等)。

课程内容内核模块介绍应用层加载模块操作过程内核如何支持可安装模块内核提供的接口及作用模块实例内核模块应用层加载模块操作过程内核引导的过程中,会识别出所有已经安装的硬件设备,内核引导的过程中,会识别出所有已经安装的硬件设备,并且创建好该系统中的硬件设备的列表树:文件系统。

且创建好该系统中的硬件设备的列表树:/sys 文件系统。

(udev 服务就是通过读取该文件系统内容来创建必要的设备文件的。

)。

一、如何编写LinuxPCI驱动程序

一、如何编写LinuxPCI驱动程序

⼀、如何编写LinuxPCI驱动程序PCI的世界是⼴阔的,充满了(⼤部分令⼈不快的)惊喜。

由于每个CPU体系结构实现不同的芯⽚集,并且PCI设备有不同的需求(“特性”),因此Linux内核中的PCI⽀持并不像⼈们希望的那么简单。

这篇简短的⽂章介绍⽤于PCI设备驱动程序的Linux APIs。

1.1 PCI驱动程序结构PCI驱动程序通过pci_register_driver()在系统中"发现"PCI设备。

事实上,恰恰相反。

当PCI通⽤代码发现⼀个新设备时,具有匹配“描述”的驱动程序将被通知。

详情如下。

pci_register_driver()将设备的⼤部分探测留给PCI层,并⽀持在线插⼊/删除设备[因此在单个驱动程序中⽀持热插拔PCI、CardBus和Express-Card]。

pci_register_driver()调⽤需要传⼊⼀个函数指针表,从⽽指⽰驱动程序的更⾼⼀级结构体。

⼀旦驱动程序知道了⼀个PCI设备并获得了所有权,驱动程序通常需要执⾏以下初始化:启⽤设备请求MMIO / IOP资源设置DMA掩码⼤⼩(⽤于⼀致性DMA和流式DMA)分配和初始化共享控制数据(pci_allocate_coherent())访问设备配置空间(如果需要)注册IRQ处理程序(request_irq())初始化non-PCI(即LAN/SCSI/等芯⽚部分)启⽤DMA /处理引擎当使⽤设备完成时,可能需要卸载模块,驱动程序需要采取以下步骤:禁⽌设备产⽣irq释放IRQ (free_irq())停⽌所有DMA活动释放DMA缓冲区(包括流式DMA和⼀致性DMA)从其他⼦系统注销(例如scsi或netdev)释放MMIO / IOP资源禁⽤该设备下⾯⼏节将介绍这些主题中的⼤部分。

其余部分请查看LDD3或<linux/pci.h>。

如果PCI⼦系统没有配置(没有设置CONFIG_PCI),下⾯描述的⼤多数PCI函数都被定义为内联函数,要么完全空,要么只是返回⼀个适当的错误代码,以避免在驱动程序中出现⼤量ifdefs。

(笔记)Linux下的ioctl()函数详解

(笔记)Linux下的ioctl()函数详解

(笔记)Linux下的ioctl()函数详解我这⾥说的ioctl函数是指驱动程序⾥的,因为我不知道还有没有别的场合⽤到了它,所以就规定了我们讨论的范围。

写这篇⽂章是因为我前⼀阵⼦被ioctl给搞混了,这⼏天才弄明⽩它,于是在这⾥清理⼀下头脑。

⼀、什么是ioctlioctl是设备驱动程序中对设备的I/O通道进⾏管理的函数。

所谓对I/O通道进⾏管理,就是对设备的⼀些特性进⾏控制,例如串⼝的传输波特率、马达的转速等等。

它的调⽤个数如下:int ioctl(int fd, ind cmd, …);其中fd是⽤户程序打开设备时使⽤open函数返回的⽂件标⽰符,cmd是⽤户程序对设备的控制命令,⾄于后⾯的省略号,那是⼀些补充参数,⼀般最多⼀个,这个参数的有⽆和cmd的意义相关。

ioctl函数是⽂件结构中的⼀个属性分量,就是说如果你的驱动程序提供了对ioctl的⽀持,⽤户就可以在⽤户程序中使⽤ioctl函数来控制设备的I/O通道。

下表列出了⽹络相关ioctl 请求的request 参数以及arg 地址必须指向的数据类型:类别Request说明数据类型套接⼝SIOCATMARKSIOCSPGRPSIOCGPGRP是否位于带外标记设置套接⼝的进程ID 或进程组ID获取套接⼝的进程ID 或进程组IDintintint⽂件FIONBINFIOASYNCFIONREADFIOSETOWNFIOGETOWN设置/ 清除⾮阻塞I/O 标志设置/ 清除信号驱动异步I/O 标志获取接收缓存区中的字节数设置⽂件的进程ID 或进程组ID获取⽂件的进程ID 或进程组IDintintintintint接⼝SIOCGIFCONFSIOCSIFADDRSIOCGIFADDRSIOCSIFFLAGSSIOCGIFFLAGSSIOCSIFDSTADDRSIOCGIFDSTADDRSIOCGIFBRDADDRSIOCSIFBRDADDRSIOCGIFNETMASKSIOCSIFNETMASKSIOCGIFMETRICSIOCSIFMETRICSIOCGIFMTUSIOCxxx获取所有接⼝的清单设置接⼝地址获取接⼝地址设置接⼝标志获取接⼝标志设置点到点地址获取点到点地址获取⼴播地址设置⼴播地址获取⼦⽹掩码设置⼦⽹掩码获取接⼝的测度设置接⼝的测度获取接⼝MTU(还有很多取决于系统的实现)struct ifconfstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqstruct ifreqARP SIOCSARPSIOCGARPSIOCDARP创建/ 修改ARP 表项获取ARP 表项删除ARP 表项struct arpreqstruct arpreqstruct arpreq路由SIOCADDRTSIOCDELRT增加路径删除路径struct rtentrystruct rtentry流I_xxx⼆、 ioctl的必要性如果不⽤ioctl的话,也可以实现对设备I/O通道的控制,但那是蛮拧了。

基于rk3568的linux驱动开发——gpio知识点

基于rk3568的linux驱动开发——gpio知识点

基于rk3568的linux驱动开发——gpio知识点基于rk3568的Linux驱动开发——GPIO知识点一、引言GPIO(General Purpose Input/Output)通用输入/输出,是现代计算机系统中的一种常用接口,它可以根据需要配置为输入或输出。

通过GPIO 接口,我们可以与各种外设进行通信,如LED灯、按键、传感器等。

在基于Linux系统的嵌入式设备上开发驱动程序时,熟悉GPIO的使用是非常重要的一环。

本文将以RK3568芯片为例,详细介绍GPIO的相关知识点和在Linux驱动开发中的应用。

二、GPIO概述GPIO是系统中的一个基本的硬件资源,它可以通过软件的方式对其进行配置和控制。

在嵌入式设备中,通常将一部分GPIO引脚连接到外部可编程电路,以实现与外部设备的交互。

在Linux中,GPIO是以字符设备的形式存在,对应的设备驱动为"gpiolib"。

三、GPIO的驱动开发流程1. 导入头文件在驱动程序中,首先需要导入与GPIO相关的头文件。

对于基于RK3568芯片的开发,需要导入头文件"gpiolib.h"。

2. 分配GPIO资源在驱动程序中,需要使用到GPIO资源,如GPIO所在的GPIO Bank和GPIO Index等。

在RK3568芯片中,GPIO资源的分配是通过设备树(Device Tree)来进行的。

在设备树文件中,可以定义GPIO Bank和GPIO Index等信息,以及对应的GPIO方向(输入或输出)、电平(高电平或低电平)等属性。

在驱动程序中,可以通过设备树接口(Device Tree API)来获取这些GPIO资源。

3. GPIO的配置与控制在驱动程序中,首先要进行GPIO的初始化与配置。

可以通过函数"gpiod_get()"来打开指定的GPIO,并判断其是否有效。

如果成功打开GPIO,则可以使用函数"gpiod_direction_output()"或"gpiod_direction_input()"来设置GPIO的方向,分别作为输出或输入。

Linux内核模块

Linux内核模块

⼯作模式⼯作性质层次权限影响竞态运⾏⽅式应⽤程序USR 模式策略性⽤户层低局部局部主动内核模块SVC 模式功能性内核层⾼全局全局被挡Linux 内核模块1、什么是内核模块?内核模块是Linux 提供的⼀种机制,允许在内核运⾏时动态加载进内核中,具有两个特点: 1)内核模块本⾝不编译⼊内核映像,有效控制缩减内核镜像⼤⼩ 2)内核模块⼀旦被加载,他就和内核中的其他部分完全⼀样2、为什么需要内核模块?如果在内核编译时把所有的功能都编译进去,就会导致内核很⼤,⽽且要往内核中添加或删除功能时必须重新编译内核⽐如在Ubuntu 在通⽤PC 平台上,预先⽆法知道需要什么设备,就不知道预先编译什么驱动。

3、内核模块和应⽤程序的区别4、内核模块的基本构成|——两个函数(⼀般需要)| |——模块初始化(加载)函数:当内核模块加载进内核的时候,做⼀些准备⼯作| |——模块卸载函数:回收、清理资源||——授权(许可证声明)(必须):Linux 内核受GPL (General Public License )授权约束|——模块参数(可选):模块被加载时可以被传递给它的值,本⾝对应模块内的全局变量|——模块导出符号(可选)|——模块信息说明(可选)5、模块加载(初始化)函数⼀般以 __init 标识声明函数命名规则 xxx_init xxx 设备名 init 功能名(初始化)函数形式:static ini __init xxx_init(void ){/* 初始化代码* 返回值: 成功:0 失败:负数,绝对值是错误码* 应⽤层得到的返回值是-1,错误码保存到errno (每个进程有⼀个); 标准化errno.h 已经明确定义linux/errno.h */}注册⽅式: module_init(x); x 为模块初始化函数的⾸地址 6、模块卸载函数⼀般以 __exit 标识声明函数命名规则 xxx_exit xxx 设备名 exit 功能名(卸载)static ini __exit xxx_exit(void ){/* 释放代码 */}注册⽅式: module_exit(x); x为模块卸载函数的⾸地址7、模块许可证声明MODULE_LICENSE(_license) //_license就是授权名称的字符串//"GPL" [GNU Public License v2 or later]//"GPL v2" [GNU Public License v2]//"GPL and additional rights" [GNU Public License v2 rights and more]//"Dual BSD/GPL" [GNU Public License v2 or BSD license choice]//"Dual MIT/GPL" [GNU Public License v2 or MIT license choice]//"Dual MPL/GPL" [GNU Public License v2 or Mozilla license choice]8、模块声明与描述在Linux内核模块中,我们可以⽤MODULE_AUTHOR、MODULE_DESCRIPTION、MODULE_VERSION、MODULE_DEVICE_TABLE、MODULE_ALIAS分别来声明模块的作者、描述、版本、设备表和别名,例如:MODULE_AUTHOR(author);MODULE_DESCRIPTION(description);MODULE_VERSION(version_string);MODULE_DEVICE_TABLE(table_info);MODULE_ALIAS(alternate_name);对于USB、PCI等设备驱动,通常会创建⼀个MODULE_DEVICE_TABLE,表明该驱动模块⽀持的设备,如:/* 对应此驱动的设备列表 */static struct usb_device_id skel_table [ ] = {{USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) }, { } /* 表结束 */}};MODULE_DEVICE_TABLE (usb, skel_table);9、模块参数:在加载模块时,可以给模块传参头⽂件 linux/moduleparam.hA、传递普通变量module_param(name, type, perm);声明内核模块参数/*name - 接收参数的变量名type - 变量类型 Standard types are: byte, short, ushort, int, uint, long, ulong charp: a character pointer bool: a bool, values 0/1, y/n, Y/N. invbool: the above, only sense-reversed (N = true)perm - 权限 头⽂件 linux/stat.h #define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO) #define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO) #define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH) #define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH) #define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)*/范例:int i = 0;module_param(i, int, 0644);运⾏:# insmod xxx.ko i=10B、传递数组参数module_param_array(name, type, nump, perm)/*声明内核模块数组参数name - 数组名type - 数组成员类型nump – ⼀个指向保存数组长度的整型变量的指针perm - 权限*/范例:int arr[] = {1,2,3,4,5,6};int len=0;module_param(arr, int, &len, 0644);运⾏:# insmod xxx.ko arr=1,2,3,4,5C、传递字符串参数module_param_string(name, string, len, perm)/*声明内核模块字符串参数name - 字符串缓存的外部名(传⼊变量名)string - 字符串缓存的内部名nump - 数组的数量perm - 权限*/范例:char insidestr[] = "hello world";module_param(extstr, insidestr, szieof(insidestr), 0644);运⾏:# insmod xxx.ko extstr="hello"10、编译内核模块如果⼀个内核模块要加载到某个内核中运⾏,则这个模块必须使⽤编译该内核镜像的源码进⾏编译,否则运⾏时会出错A、头⽂件(语法问题)B、编译结果(最主要影响)编译时符号表(只在编译时使⽤)运⾏时内核符号表# cat /proc/kallsyms 运⾏时内核符号表C、编译系统⽰例Makefile:# 内核模块的Makefile(模块源码在内核源码外,且内核先编译)# 1、找内核的Makefile# 2、内核的Makefile找内核模块的Makeifle内核模块的Makeifle定义要编译对象ifneq ($(KERNELRELEASE),)#要编译对象表⽰把demo.c编译成demo.ko obj-m = demo.oelse#内核源码⽬录KERNELDIR := /lib/modules/$(shell uname -r)/buildPWD := $(shell pwd)modules: $(MAKE) -C $(KERNELDIR) M=$(PWD) modulesendifclean: rm -rf .tmp_versions Module.symvers modules.order .tmp_versions .*.cmd *.o *.ko *.mod.cKERNELRELEASE 是在内核源码的顶层Makefile中定义的⼀个变量,在第⼀次读取执⾏此Makefile时,KERNELRELEASE没有被定义,所以make将读取执⾏else之后的内容。

linux设备驱动程序的设计与实现

linux设备驱动程序的设计与实现

linux设备驱动程序的设计与实现
Linux设备驱动程序的设计与实现是一个涉及底层系统编程和硬件交互的复杂过程。

下面是一个简单的步骤指南,以帮助你开始设计和实现Linux设备驱动程序:
1. 了解硬件:首先,你需要熟悉你要驱动的硬件设备的规格和特性。

这包括硬件的内存空间、I/O端口、中断请求等。

2. 选择驱动程序模型:Linux支持多种设备驱动程序模型,包括字符设备、块设备、网络设备等。

根据你的硬件设备和需求,选择合适的驱动程序模型。

3. 编写Makefile:Makefile是一个文本文件,用于描述如何编译和链接你的设备驱动程序。

它告诉Linux内核构建系统如何找到并编译你的代码。

4. 编写设备驱动程序:在Linux内核源代码树中创建一个新的驱动程序模块,并编写相应的C代码。

这包括设备注册、初始化和卸载函数,以及支持读写和配置硬件的函数。

5. 测试和调试:编译你的设备驱动程序,并将其加载到运行中的Linux内核中。

使用各种测试工具和方法来验证驱动程序的正确性和稳定性。

6. 文档和发布:编写清晰的文档,描述你的设备驱动程序的用途、用法和已知问题。

发布你的代码以供其他人使
用和改进。

linux 驱动的 ioctl 详细说明

linux 驱动的 ioctl 详细说明

linux 驱动的ioctl 详细说明摘要:1.概述ioctl 的作用和用法2.ioctl 的错误码及含义3.ioctl 的参数4.ioctl 的返回值及意义5.ioctl 在Linux 声卡驱动中的应用正文:一、概述ioctl 的作用和用法ioctl(input/output control)是Linux 系统中一种用于设备控制的系统调用,通过ioctl,用户进程可以对设备进行配置、查询和控制等操作。

ioctl 的用法通常为:```int ioctl(int fd, int request,...);```其中,fd 表示设备的文件描述符,request 表示设备驱动程序所支持的控制请求,后面的省略号表示可能的附加参数。

二、ioctl 的错误码及含义ioctl 系统调用可能返回以下错误码:- -1:表示发生了错误,此时errno 系统变量将包含具体的错误码。

- 0:表示操作成功完成。

- 其他大于0 的值:表示设备的某些特殊状态,具体含义需根据设备类型和驱动程序来确定。

三、ioctl 的参数ioctl 的参数主要包括以下几类:1.设备文件描述符fd:表示要控制的设备的文件描述符。

2.控制请求request:表示要执行的设备控制操作,如配置、查询、控制等。

3.附加参数:根据设备类型和控制请求的不同,可能需要提供不同的附加参数。

这些参数通常是设备驱动程序所支持的数据结构或整数变量。

四、ioctl 的返回值及意义ioctl 的返回值表示设备驱动程序处理控制请求的结果。

如果返回值为-1,则表示发生了错误;如果返回值为0,则表示操作成功完成;如果返回值为其他大于0 的值,则表示设备的某些特殊状态。

具体的错误码和含义可以通过errno 系统变量获取。

五、ioctl 在Linux 声卡驱动中的应用在Linux 声卡驱动中,ioctl 被广泛应用于配置声卡设备、查询声卡状态、控制声音播放等。

例如,通过ioctl 可以实现以下功能:- 获取声卡设备的信息,如设备型号、支持的采样率等。

linux 开发新驱动步骤

linux 开发新驱动步骤

linux 开发新驱动步骤Linux作为一款开源的操作系统,其内核源码也是开放的,因此,许多开发人员在Linux上进行驱动开发。

本文将介绍在Linux上进行新驱动开发的步骤。

第一步:确定驱动类型和接口在进行驱动开发前,需要确定驱动类型和接口。

驱动类型包括字符设备驱动、块设备驱动、网络设备驱动等。

接口包括设备文件、系统调用、ioctl等。

根据驱动类型和接口的不同,驱动开发的流程也有所不同。

第二步:了解Linux内核结构和API驱动开发需要熟悉Linux内核的结构和API。

Linux内核由许多模块组成,每个模块都有自己的功能。

API是应用程序接口,提供了许多函数和数据结构,开发人员可以使用这些函数和数据结构完成驱动开发。

第三步:编写驱动代码在了解了Linux内核结构和API后,就可以编写驱动代码了。

驱动代码需要按照Linux内核的编码规范编写,确保代码风格统一、可读性好、可维护性强等。

在编写代码时,需要使用API提供的函数和数据结构完成相应的功能。

第四步:编译驱动代码和内核模块驱动代码编写完成后,需要编译成内核模块。

编译内核模块需要使用内核源码中的Makefile文件。

编译完成后,会生成一个.ko文件,这个文件就是内核模块。

第五步:加载和卸载内核模块内核模块编译完成后,需要加载到Linux系统中。

可以使用insmod命令加载内核模块,使用rmmod命令卸载内核模块。

在加载和卸载内核模块时,需要注意依赖关系,确保依赖的模块已经加载或卸载。

第六步:调试和测试驱动开发完成后,需要进行调试和测试。

可以使用printk函数输出调试信息,在/var/log/messages文件中查看。

测试时需要模拟各种可能的情况,确保驱动程序的稳定性和可靠性。

Linux驱动开发需要掌握Linux内核结构和API,熟悉驱动类型和接口,按照编码规范编写驱动代码,并进行编译、加载、调试和测试。

只有掌握了这些技能,才能进行高效、稳定和可靠的驱动开发。

linux内核驱动中_IO, _IOR, _IOW, _IOWR 宏的用法与解析

linux内核驱动中_IO, _IOR, _IOW, _IOWR 宏的用法与解析

在驱动程序里,ioctl() 函数上传送的变量cmd 是应用程序用于区别设备驱动程序请求处理内容的值。

cmd除了可区别数字外,还包含有助于处理的几种相应信息。

cmd的大小为32位,共分 4 个域:bit31~bit30 2位为“区别读写” 区,作用是区分是读取命令还是写入命令。

bit29~bit15 14位为"数据大小" 区,表示ioctl() 中的arg 变量传送的内存大小。

bit20~bit08 8位为“魔数"(也称为"幻数")区,这个值用以与其它设备驱动程序的ioctl 命令进行区别。

bit07~bit00 8位为"区别序号" 区,是区分命令的命令顺序序号。

像命令码中的“区分读写区” 里的值可能是_IOC_NONE (0值)表示无数据传输,_IOC_READ (读),_IOC_WRITE (写) ,_IOC_READ|_IOC_WRITE (双向)。

内核定义了_IO() , _IOR() , IOW() 和_IOWR() 这4 个宏来辅助生成上面的cmd 。

下面分析_IO() 的实现,其它的类似:在asm-generic/ioctl.h 里可以看到_IO() 的定义:#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)再看_IOC() 的定义:#define _IOC(dir,type,nr,size) \(((dir) << _IOC_DIRSHIFT) | \((type) << _IOC_TYPESHIFT) | \((nr) << _IOC_NRSHIFT) | \((size) << _IOC_SIZESHIFT))可见,_IO() 的最后结果由_IOC() 中的4 个参数移位组合而成。

再看_IOC_DIRSHIT 的定义:#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS) _IOC_SIZESHIFT 的定义:#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS) _IOC_TYPESHIF 的定义:#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) _IOC_NRSHIFT 的定义:#define _IOC_NRSHIFT 0_IOC_NRBITS 的定义:#define _IOC_NRBITS 8_IOC_TYPEBITS 的定义:#define _IOC_TYPEBITS 8由上面的定义,往上推得到:引用_IOC_TYPESHIFT = 8_IOC_SIZESHIFT = 16_IOC_DIRSHIFT = 30所以,(dir) << _IOC_DIRSHIFT) 表是dir 往左移30 位,即移到bit31~bit30 两位上,得到方向(读写)的属性;(size) << _IOC_SIZESHIFT) 位左移16 位得到“数据大小”区;(type) << _IOC_TYPESHIFT) 左移8位得到"魔数区" ;(nr) << _IOC_NRSHIFT) 左移0 位( bit7~bit0) 。

ioctl函数源码

ioctl函数源码

ioctl函数源码
ioctl函数是Linux系统中用于进行设备操作的系统调用函数。

它的作用是向操作系统内核发送指令,控制设备的运作方式、获取设备状态及更改设备参数等。

ioctl函数的函数原型如下:
int ioctl(int fd, unsigned long request, ...);
其中,fd表示要进行操作的设备文件的文件描述符,request为设备操作指令,...为可选参数,用于传递指令所需的参数。

ioctl函数的操作指令由request参数指定,它是一个无符号长整型数值,并且由ioctl.h头文件中定义。

每个设备的操作指令都不同,所以需要查看设备的说明文档或驱动程序源码才能确定具体的操作指令。

ioctl函数的实现过程比较复杂,涉及到内核态和用户态之间的交互。

在用户态,ioctl函数将指令和参数打包成一个ioctl_cmd结构体,并通过系统调用将其传送给内核态。

内核态通过驱动程序解析指令和参数,并对设备进行相应的操作,最后将结果返回给用户态。

在内核态中,ioctl函数的实现函数为do_ioctl函数,它定义在
fs/ioctl.c文件中。

总的来说,ioctl函数是Linux系统中一个非常重要的系统调用函数,对于进行设备操作、控制和管理等方面都有着至关重要的作用。

- 1 -。

Linux内核的ioctl函数学习

Linux内核的ioctl函数学习

Linux内核的ioctl函数学习本函数影响由fd参数引用的一个打开的文件。

#include#includeint ioctl( int fd, int request, .../* void *arg */ );返回0:成功-1:出错第三个参数总是一个指针,但指针的类型依赖于request参数。

我们可以把和网络相关的请求划分为6类:套接口操作文件操作接口操作ARP高速缓存操作路由表操作流系统下表列出了网络相关ioctl请求的request参数以及arg地址必须指向的数据类型:类别Request说明数据类型套接口SIOCATMARKSIOCSPGRPSIOCGPGRP是否位于带外标记设置套接口的进程ID或进程组ID获取套接口的进程ID或进程组IDintintint文件FIONBINFIOASYNCFIONREADFIOSETOWNFIOGETOWN设置/清除非阻塞I/O标志设置/清除信号驱动异步I/O标志获取接收缓存区中的字节数设置文件的进程ID或进程组ID获取文件的进程ID或进程组IDintintintintint接口SIOCGIFCONFSIOCSIFADD RSIOCGIFADDRSIOCSIFFL AGSSIOCGIFFLAGSSIOCSI FDSTADDRSIOCGIFDSTADD RSIOCGIFBRDADDRSIOCSI FBRDADDRSIOCGIFNETMAS KSIOCSIFNETMASKSIOCGI FMETRICSIOCSIFMETRICS获取所有接口的清单设置接口地址获取接口地址设置接口标志获取接口标志设置点到点地址获取点到点地址获取广播地址设置广播地址获取子网掩码设置子网掩码获取接口的测度设置接口的测度获取接口MTU(还有很多取决于系统的实struct ifconfstructifreqstructifreqstructifreqstructifreqstructifreqstructifreqstructifreqstruct IOCGIFMTUSIOCxxx现)ifreqstruct ifreqstructifreqstructifreqstructifreqstruct ifreqARP SIOCSARPSIOCGARPSIOCD ARP 创建/修改ARP表项获取ARP表项删除ARP表项struct arpreqstructarpreqstruct arpreq路由SIOCADDRTSIOCDELRT增加路径删除路径struct rtentrystructrtentry流I_xxx套接口操作:明确用于套接口操作的ioctl请求有三个,它们都要求ioctl的第三个参数是指向某个整数的一个指针。

linux中ioctl的用法

linux中ioctl的用法

linux中ioctl的用法关于Linux 中的ioctl 用法1. 介绍ioctl 是一个C 语言中的函数,用于在Linux 操作系统中的文件描述符上执行控制操作。

它允许用户空间程序与设备驱动程序进行交互,并向硬件设备发送特定的指令。

2. ioctl 的参数ioctl 函数的原型如下:int ioctl(int fd, unsigned long request, ...);其中,fd 是一个打开的文件描述符,request 是一个无符号长整型,表示要执行的操作,接下来的可选参数表示数据传输的缓冲区。

3. 常见的ioctl 异步操作(1)FIONREAD:用于非阻塞读取文件描述符上的数据。

该操作返回可读取的字节数。

(2)FIONBIO:用于设置文件描述符为非阻塞模式。

在非阻塞模式下,读取和写入操作不会被阻塞。

4._ioctl 的用法示例(1)获取设备信息ioctl 函数可用于获取设备的各种信息,比如文件大小、设备属性等等。

例如,通过以下代码可以获取文件的大小:int file_size;ioctl(fd, FIONREAD, &file_size);printf("File size: %d bytes\n", file_size);(2)设置设备属性ioctl 函数可以用于设置设备的属性,例如设置串口的波特率、数据位数等等。

例如,通过以下代码可以设置串口的波特率为9600:speed_t baud_rate = B9600;ioctl(fd, TCSETS, &baud_rate);其中,B9600 是一个预定义的标识符,表示波特率为9600。

(3)发送自定义指令通过ioctl 函数,用户空间程序可以发送自定义的指令给设备驱动程序,实现特定的功能。

例如,通过以下代码可以向设备发送一个自定义的指令:ioctl(fd, MY_CUSTOM_COMMAND, arg);其中,MY_CUSTOM_COMMAND 是一个用户定义的常数,用于表示自定义指令。

linux中编译驱动的方法

linux中编译驱动的方法

linux中编译驱动的方法
在Linux中编译驱动的方法通常涉及以下步骤:
1. 编写驱动代码:首先,您需要编写适用于Linux内核的驱动代码。

这通常是在内核源代码树之外编写的。

驱动代码通常以C语言编写,并遵循内核编程约定。

2. 获取内核源代码:为了编译驱动,您需要获得Linux内核的源代码。

您可以从Linux官方网站或镜像站点下载内核源代码。

3. 配置内核:在编译驱动之前,您需要配置内核以包含您的驱动。

这可以通过运行`make menuconfig`命令来完成。

在配置菜单中,您可以选择要编译的驱动以及相关的内核选项。

4. 编译驱动:一旦您配置了内核并选择了要编译的驱动,您可以使用`make`命令来编译驱动。

这将在内核源代码目录下生成可执行文件或模块文件。

5. 加载和测试驱动:一旦驱动被编译,您可以将其加载到Linux 内核中以进行测试。

您可以使用`insmod`命令将模块加载到内核,然后使用`dmesg`命令检查内核日志以查看驱动是否正确加载。

这些是基本的步骤,但具体的步骤可能会因您的环境和需求而有所不同。

在编译和加载驱动时,请确保您具有适当的权限和知识,因为这可能需要管理员权限,并且错误的操作可能会导致系统不稳定或损坏。

C语言嵌入式Linux开发驱动和系统调用

C语言嵌入式Linux开发驱动和系统调用

C语言嵌入式Linux开发驱动和系统调用在嵌入式系统领域中,C语言是最常用的编程语言之一。

它具有高效性、可移植性和灵活性,使得它成为开发嵌入式Linux驱动和系统调用的理想选择。

本文将详细介绍C语言在嵌入式Linux开发中的应用,包括驱动开发和系统调用的实现。

一、驱动开发1.1 驱动的定义和作用驱动是连接硬件和操作系统的关键组件,它允许操作系统与具体的硬件设备进行通信。

驱动的主要作用是提供对硬件设备的控制、管理和数据传输。

在嵌入式Linux系统中,驱动的开发需要使用C语言来编写。

1.2 驱动的开发流程驱动的开发可以分为以下几个步骤:1)了解硬件设备:首先要对驱动所涉及的硬件设备有一定的了解,包括设备的主要功能和寄存器的操作方式等。

2)驱动代码编写:使用C语言编写驱动代码,根据硬件设备的数据发送和接收过程设计函数和数据结构。

3)编译和链接:将驱动代码编译成可执行文件,并将其链接到操作系统的内核中。

4)加载和卸载:通过调用命令加载和卸载驱动,使其生效或失效。

5)测试和调试:进行驱动功能的测试和调试工作,确保驱动的正确性和稳定性。

1.3 驱动示例:LED驱动以一个简单的LED驱动为例,说明驱动的开发过程:1)定义LED设备的数据结构:创建一个结构体来表示LED设备的相关信息,例如设备的名称、设备的状态等。

2)实现LED控制函数:编写LED控制函数,通过操作硬件寄存器来控制LED的开关。

3)注册驱动:将驱动注册到操作系统的驱动框架中,使其与操作系统进行通信。

4)加载和卸载驱动:通过命令加载和卸载驱动,对LED进行控制。

二、系统调用2.1 系统调用的定义和作用系统调用是用户程序与操作系统之间的接口,它允许用户程序访问操作系统提供的服务和资源。

系统调用的主要作用是提供对底层硬件和操作系统功能的访问。

2.2 系统调用的分类系统调用可以分为以下几类:1)进程控制:如创建、终止和等待进程等。

2)文件操作:如打开、读取和关闭文件等。

ioctrl用法详解

ioctrl用法详解

ioctrl用法详解ioctl是Unix和Linux系统中常用的一个系统调用,它提供了一种设备驱动程序与应用程序进行通信的机制。

ioctl的全称是“input-output control”,意为输入/输出控制,它允许应用程序对设备驱动程序发出控制命令,从而实现对设备的控制。

ioctl函数的原型为:int ioctl(int fd, unsigned long request, ...);其中,fd是文件描述符,request是控制命令,后面的参数取决于request的类型和值。

ioctl的控制命令通常是由设备驱动程序定义的,不同的设备可能有不同的命令。

因此,ioctl的具体用法取决于设备驱动程序的实现。

一般来说,ioctl命令可以分为以下几类:设备特定命令:这些命令由设备驱动程序定义,用于控制设备的特定行为。

例如,网络设备驱动程序可能定义了用于配置网络接口、查看网络状态等命令。

文件系统命令:这些命令用于控制文件系统的行为。

例如,可以通过ioctl命令来扩展文件系统的功能,如创建新的文件系统、挂载文件系统等。

终端命令:这些命令用于控制终端的行为。

例如,可以通过ioctl命令来设置终端的属性、读取终端的输入等。

在使用ioctl时,应用程序需要知道设备驱动程序支持的命令及其参数格式。

这通常可以通过查阅设备驱动程序的文档或头文件来获取。

在调用ioctl时,应用程序需要将命令和参数打包成一个整数序列,并将其传递给设备驱动程序。

设备驱动程序会根据命令的类型和参数执行相应的操作,并返回操作结果。

总的来说,ioctl是一种强大的机制,它允许应用程序与设备驱动程序进行交互,从而实现对设备的控制。

但是,由于ioctl命令的多样性和复杂性,使用它时需要谨慎,并仔细阅读相关文档和头文件。

class_create()_device_create自动创建设备文件结点

class_create()_device_create自动创建设备文件结点

class_create(),device_create自动创建设备文件结点 .2011-11-24 17:23:13| 分类:Linux知识点| 标签:自动创建设备文件结点|字号大中小订阅从linux内核2.6的某个版本之后,devfs不复存在,udev成为devfs的替代。

相比devfs,udev有很多优势,在此就不罗嗦了,提醒一点,udev是应用层的东东,不要试图在内核的配置选项里找到它;加入对udev的支持很简单,以作者所写的一个字符设备驱动为例,在驱动初始化的代码里调用class_create为该设备创建一个class,再为每个设备调用class_device_create创建对应的设备。

(不太明白什么是devfs,udev,对linux中的文件系统没有什么概念呢。

)大致用法如下:struct class *myclass = class_create(THIS_MODULE, “my_device_driver”);class_device_create(myclass, NULL, MKDEV(major_num, 0), NULL, “my_device”);这样的module被加载时,udev daemon就会自动在/dev下创建my_device设备文件。

class_create()-------------------------------------------------linux-2.6.22/include/linux/device.hstruct class *class_create(struct module *owner, const char *name)class_create - create a struct class structure@owner: pointer to the module that is to "own" this struct class@name: pointer to a string for the name of this class.在/sys/class/下创建类目录class_device_create()-------------------------------------------------linux-2.6.22/include/linux/device.hstruct class_device *class_device_create(struct class *cls,struct class_device *parent,dev_t devt,struct device *device,const char *fmt, ...)class_device_create - creates a class device and registers it with sysfs@cls: pointer to the struct class that this device should be registered to.@parent: pointer to the parent struct class_device of this new device, if any.@devt: the dev_t for the char device to be added.@device: a pointer to a struct device that is assiociated with this class device.@fmt: string for the class device's name在驱动模块初始化函数中实现设备节点的自动创建我们在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev。

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

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

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

如果你已经从事内核模块编程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身份执行。

相关文档
最新文档