Linux 字符设备驱动实例

合集下载

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中提供了⼀些宏,这些宏可根据便于理解的字符串⽣成命令码,或者是从命令码得到⼀些⽤户可以理解的字符串以标明这个命令对应的设备类型、设备序列号、数据传送⽅向和数据传输尺⼨。

基于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的方向,分别作为输出或输入。

LinuxI2C驱动--用户态驱动简单示例

LinuxI2C驱动--用户态驱动简单示例

LinuxI2C驱动--⽤户态驱动简单⽰例1. Linux内核⽀持I2C通⽤设备驱动(⽤户态驱动:由应⽤层实现对硬件的控制可以称之为⽤户态驱动),实现⽂件位于drivers/i2c/i2c-dev.c,设备⽂件为/dev/i2c-02. I2C通⽤设备驱动以字符设备注册进内核的static const struct file_operations i2cdev_fops = {.owner = THIS_MODULE,.llseek = no_llseek,.read = i2cdev_read,.write = i2cdev_write,.unlocked_ioctl = i2cdev_ioctl,.open = i2cdev_open,.release = i2cdev_release,};res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);3. 对设备⽂件进⾏读写时,可以调⽤read、write或者ioctl等⽅法,他们都是通过调⽤函数i2c_transfer来实现对I2C设备的操作的int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num){int ret;/* REVISIT the fault reporting model here is weak:** - When we get an error after receiving N bytes from a slave,* there is no way to report "N".** - When we get a NAK after transmitting N bytes to a slave,* there is no way to report "N" ... or to let the master* continue executing the rest of this combined message, if* that's the appropriate response.** - When for example "num" is two and we successfully complete* the first message but get an error part way through the* second, it's unclear whether that should be reported as* one (discarding status on the second message) or errno* (discarding status on the first one).*/if (adap->algo->master_xfer) {#ifdef DEBUGfor (ret = 0; ret < num; ret++) {dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, ""len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)'R' : 'W', msgs[ret].addr, msgs[ret].len,(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");}#endifif (in_atomic() || irqs_disabled()) {ret = mutex_trylock(&adap->bus_lock);if (!ret)/* I2C activity is ongoing. */return -EAGAIN;} else {mutex_lock_nested(&adap->bus_lock, adap->level);}ret = adap->algo->master_xfer(adap,msgs,num);mutex_unlock(&adap->bus_lock);return ret;} else {dev_dbg(&adap->dev, "I2C level transfers not supported\n");return -EOPNOTSUPP;}}4. i2c_transfer通过代码可以看出,i2c_transfer 通过调⽤相应的 adapter 的 master_xfer ⽅法实现的,⽽ master_xfer 主要是根据 struct i2c_msg 类型的msgs来进⾏处理的。

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

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

实验二:字符设备驱动实验一、实验目的通过本实验的学习,了解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字符驱动框架(用户态的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中定义,⽤来存储驱动内核模块提供的对设备进⾏各种操作的函数的指针。

基于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

字符设备驱动(1)驱动代码完整源码:charButtons.c

字符设备驱动(1)驱动代码完整源码:charButtons.c

字符设备驱动(1)驱动代码完整源码:charButtons.c 内核版本:Linux3.0.8开发板:基于三星S5PV210处理器的Tiny210开发板驱动名称:charButtons.c驱动描述:按键触发中断,中断处理程序执⾏相应的简单LED点亮操作⽅案1注册字符设备使⽤新的接⼝实现(需要好⼏个函数来实现。

貌似更复杂)⽅案2注册字符设备使⽤⽼的接⼝实现(貌似⽼接⼝更简单)/*****************************************************************************简述:简单字符型驱动程序,⼿动静态分配设备号,⼿动创建设备节点******************************************************************************/#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/cdev.h>#include <linux/fs.h>#include <linux/wait.h>#include <linux/poll.h>#include <linux/sched.h>#include <linux/irq.h>#include <asm/irq.h>#include <linux/interrupt.h>#include <mach/map.h>#include <mach/gpio.h>#include <mach/regs-gpio.h>#include <plat/gpio-cfg.h>#include <linux/slab.h>#define DEVICE_NAME "buttons"struct button_desc {int gpio;int number;char *name;};struct led_desc {int gpio;int number;char *name;};static struct button_desc buttons[] = {{ S5PV210_GPH2(0), 0, "KEY0" },{ S5PV210_GPH2(1), 1, "KEY1" },{ S5PV210_GPH2(2), 2, "KEY2" },{ S5PV210_GPH2(3), 3, "KEY3" },{ S5PV210_GPH3(0), 4, "KEY4" },{ S5PV210_GPH3(1), 5, "KEY5" },{ S5PV210_GPH3(2), 6, "KEY6" },{ S5PV210_GPH3(3), 7, "KEY7" },};static struct led_desc leds[] = {{S5PV210_GPJ2(0),1,"LED1"},{S5PV210_GPJ2(1),2,"LED2"},{S5PV210_GPJ2(2),3,"LED3"},{S5PV210_GPJ2(3),4,"LED4"},};#define OK (0)#define ERROR (-1)struct gpio_chip *chip;struct cdev *gDev;struct file_operations *gFile;dev_t devNum;unsigned int subDevNum = 1;//要申请的次设备号个数int reg_major = 234;int reg_minor = 0;static irqreturn_t button_interrupt(int irq, void *dev_id){struct button_desc *bdata = (struct button_desc *)dev_id;int down;unsigned tmp;tmp = gpio_get_value(bdata->gpio);/* active low */down = !tmp;printk("KEY %d: %08x\n", bdata->number, down);if(bdata->number < 4){gpio_set_value(leds[bdata->number].gpio,0);printk("LED %d: On \n",leds[bdata->number].number);}else{gpio_set_value(leds[(bdata->number) - 4].gpio,1);printk("LED %d: OFF \n",leds[(bdata->number)-4].number); }return IRQ_HANDLED;}int butsOpen(struct inode *p, struct file *f){int irq;int i;int err = 0;printk(KERN_EMERG"butsOpen\r\n");for (i = 0; i < ARRAY_SIZE(buttons); i++) {if (!buttons[i].gpio)continue;irq = gpio_to_irq(buttons[i].gpio);// irq = IRQ_EINT(16)+i =160+i "S5PV210_GPH2(i)"//irq = IRQ_EINT(24)+i =168+i "S5PV210_GPH3(i)"err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_BOTH,buttons[i].name, (void *)&buttons[i]);if (err)break;}for(i = 0; i<ARRAY_SIZE(leds);i++){if(!leds[i].gpio)continue;gpio_direction_output(leds[i].gpio,1);}if (err) {i--;for (; i >= 0; i--) {if (!buttons[i].gpio)continue;irq = gpio_to_irq(buttons[i].gpio);disable_irq(irq);free_irq(irq, (void *)&buttons[i]);}return -EBUSY;}return0;}int charDrvInit(void){devNum = MKDEV(reg_major, reg_minor);printk(KERN_EMERG"devNum is %d\r\n", devNum);if(OK == register_chrdev_region(devNum, subDevNum, DEVICE_NAME)) {printk(KERN_EMERG"register_chrdev_region ok\r\n");}else{printk(KERN_EMERG"register_chrdev_region error\r\n");return ERROR;}/*if(OK == alloc_chrdev_region(&devNum, subDevNum, subDevNum,"test")) {printk(KERN_EMERG"register_chrdev_region ok\r\n");}else{printk(KERN_EMERG"register_chrdev_region error\r\n");return ERROR;}*/gDev = kzalloc(sizeof(struct cdev), GFP_KERNEL);gFile = kzalloc(sizeof(struct file_operations), GFP_KERNEL);gFile->open = butsOpen;//注册设备函数到file_operations结构体gFile//gDev->owner = THIS_MODULE;gFile->owner = THIS_MODULE;cdev_init(gDev, gFile);//在cdev结构体中添加指针指向file_operations结构体gFilecdev_add(gDev, devNum, 3);//建⽴设备号与cdev结构体联系printk(KERN_EMERG"button driver initial done...\r\n");return0;}void __exit charDrvExit(void){int i,irq;cdev_del(gDev);unregister_chrdev_region(devNum, subDevNum);for (i = 0; i < ARRAY_SIZE(buttons); i++) {if (!buttons[i].gpio)continue;irq = gpio_to_irq(buttons[i].gpio);disable_irq(irq);free_irq(irq, (void *)&buttons[i]);}return;}module_init(charDrvInit);//执⾏insmod时会执⾏此⾏代码并调⽤charDrvInit,驱动开始module_exit(charDrvExit);//执⾏rmmod时,结束MODULE_LICENSE("GPL");代码实现⽅案1/*****************************************************************************简述:简单字符型驱动程序,⼿动静态分配设备号,⼿动创建设备节点******************************************************************************/#include <linux/module.h>#include <linux/fs.h>#include <mach/gpio.h>#include <linux/irq.h>#include <linux/kdev_t.h>#include <linux/interrupt.h>#include <linux/init.h>#define DEVICE_NAME "leds"struct button_desc {int gpio;int number;char *name;};struct led_desc {int gpio;int number;char *name;};static struct button_desc buttons[] = {{ S5PV210_GPH2(0), 0, "KEY0" },{ S5PV210_GPH2(1), 1, "KEY1" },{ S5PV210_GPH2(2), 2, "KEY2" },{ S5PV210_GPH2(3), 3, "KEY3" },{ S5PV210_GPH3(0), 4, "KEY4" },{ S5PV210_GPH3(1), 5, "KEY5" },{ S5PV210_GPH3(2), 6, "KEY6" },{ S5PV210_GPH3(3), 7, "KEY7" },};static struct led_desc leds[] = {{S5PV210_GPJ2(0),1,"LED1"},{S5PV210_GPJ2(1),2,"LED2"},{S5PV210_GPJ2(2),3,"LED3"},{S5PV210_GPJ2(3),4,"LED4"},};#define OK (0)#define ERROR (-1)dev_t devNum;unsigned int subDevNum = 1;//要申请的次设备号个数int reg_major = 234;int reg_minor = 0;static irqreturn_t button_interrupt(int irq, void *dev_id){struct button_desc *bdata = (struct button_desc *)dev_id;int down;unsigned tmp;tmp = gpio_get_value(bdata->gpio);/* active low */down = !tmp;printk("KEY %d: %08x\n", bdata->number, down);if(bdata->number < 4){gpio_set_value(leds[bdata->number].gpio,0);printk("LED %d: On \n",leds[bdata->number].number);}else{gpio_set_value(leds[(bdata->number) - 4].gpio,1);printk("LED %d: OFF \n",leds[(bdata->number)-4].number); }return IRQ_HANDLED;}int butsOpen(struct inode *p, struct file *f){int irq;int i;int err = 0;printk(KERN_EMERG"butsOpen\r\n");for (i = 0; i < ARRAY_SIZE(buttons); i++) {if (!buttons[i].gpio)continue;irq = gpio_to_irq(buttons[i].gpio);// irq = IRQ_EINT(16)+i =160+i "S5PV210_GPH2(i)"//irq = IRQ_EINT(24)+i =168+i "S5PV210_GPH3(i)"err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_FALLING, buttons[i].name, (void *)&buttons[i]);if (err)break;}for(i = 0; i<ARRAY_SIZE(leds);i++){if(!leds[i].gpio)continue;gpio_direction_output(leds[i].gpio,1);}if (err) {i--;for (; i >= 0; i--) {if (!buttons[i].gpio)continue;irq = gpio_to_irq(buttons[i].gpio);disable_irq(irq);free_irq(irq, (void *)&buttons[i]);}return -EBUSY;}return0;}static const struct file_operations gFile ={.owner = THIS_MODULE,.open = butsOpen,};int charDrvInit(void){devNum = MKDEV(reg_major, reg_minor);printk(KERN_EMERG"devNum is %d\r\n", devNum);if(OK == register_chrdev(reg_major, DEVICE_NAME, &gFile)){printk(KERN_EMERG "register_chrdev_region ok\r\n");}else{printk(KERN_EMERG"register_chrdev_region error\r\n");return ERROR;}printk(KERN_EMERG "button driver initial done...\r\n");return0;}void __exit charDrvExit(void){int i,irq;unregister_chrdev(reg_major, DEVICE_NAME);for (i = 0; i < ARRAY_SIZE(buttons); i++) {if (!buttons[i].gpio)continue;irq = gpio_to_irq(buttons[i].gpio);disable_irq(irq);free_irq(irq, (void *)&buttons[i]);}return;}module_init(charDrvInit);//执⾏insmod时会执⾏此⾏代码并调⽤charDrvInit,驱动开始module_exit(charDrvExit);//执⾏rmmod时,结束MODULE_LICENSE("GPL");MODULE_AUTHOR("LiuB");代码实现⽅案2函数修饰符__init,本质上是⼀个宏定义,在内核源代码中定义:#define __init __section(.init.text) __cold notrace作⽤就是,将被它修饰的函数放⼊.init.text段中去。

设备驱动——字符设备驱动

设备驱动——字符设备驱动

一、字符设备驱动重要数据结构:struct file_operations在<linux/fs.h>定义如下:struct file_operations {struct module *owner; // 拥有该结构的模块的指针,一般为THIS_MODULES loff_t (*llseek) (struct file *,loff_t ,int); ssize_t (*read) (struct file *filp, char *buff, size_t count, loff_t *offp); ssize_t (*write) (struct file *filp, const char *buff, size_t count, loff_t *offp);int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *); int (*fasync) (int, struct file *, int); int (*check_media_change) (kdev_t dev); int (*revalidate) (kdev_t dev); int (*lock) (struct file *, int, struct file_lock *); }; srtuct file数据结构定义如下:struct file { mode_t f_mode;/*标识文件是否可读或可写,FMODE_READ或FMODE_WRITE*/ dev_t f_rdev; /* 用于/dev/tty */ off_t f_pos; /* 当前文件位移 */ unsigned short f_flags; /* 文件标志,如O_RDONLY、O_NONBLOCK和O_SYNC */ unsigned short f_count; /* 打开的文件数目 */ unsigned short f_reada; struct inode *f_inode; /*指向inode的结构指针 */ struct file_operations *f_op;/* 文件索引指针 */ };1、字符设备驱动编写流程:(1)定义加载驱动模块接口 module_init(call_init);(2)定义file_operations结构变量并实现结构函数(3)编写初始化call_init()函数,在该函数中注册设备(4)定义卸载驱动模块入口module_exit(call_exit);(5)编写call_exit()函数,在该函数中注销设备;实例:dev.c#include <linux/module.h>#include <linux/fs.h>#include <linux/kernel.h>#define DEV_NAME "calldev"#define DEV_MAJOR 240loff_t call_llseek (struct file *filp,loff_t off,int whence){printk("call llseek ->off :%08x,whence :%08x \n",off,whence);return 0x23;}ssize_t call_read (struct file *filp,char *buff,size_t count,loff_t *offp){printk("call read --->buf :%08x,count :%08x \n",buff,count);return 0x33;}ssize_t call_write (struct file *filp,const char *buf,size_t count,loff_t *f_pos){printk("call write --->buf :%08x , count :%08x \n",buf,count);return 0x43;}int call_ioctl (struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg){printk("call ioctl --->cmd :%08x ,arg :%08x \n",cmd,arg);return 0x53;}int call_open(struct inode *inode ,struct file *filp){int num = MINOR(inode->i_rdev);printk("call open-->minor:%d \n",num);return 0;}int call_release(struct inode *inode ,struct file *filp){printk("callrelease\n");return 0;}struct file_operations dev_fops ={.owner = THIS_MODULE,.llseek = call_llseek,.read = call_read,.write = call_write,.ioctl = call_ioctl,.open = call_open,.release = call_release,};int call_init(void){int dev;printk("call_dev init\n");dev = register_chrdev(DEV_MAJOR,DEV_NAME,&dev_fops);if(dev < 0){printk("register_chrdev failed\n");return dev;}return 0;}void call_exit(void){printk("call_dev exit\n");unregister_chrdev(DEV_MAJOR,DEV_NAME);}module_init(call_init);module_exit(call_exit);MODULE_LICENSE("DUAL BSD?GPL");2、Makefile编写:obj-m := dev.oKDIR := /lib/modules/¥(shell uname -r)/buildPWD := ¥(shell pwd)default :¥(MAKE) -C ¥(KDIR) SUBDIRS=¥(PWD) modulesclean :rm -rf *.korm -rf *.mod.*rm -rf .*.cmdrm -rf *.o编译驱动程序:make clean;make加载驱动:insmod dev.ko卸载驱动:rmmod dev.ko查看设备的主设备号:cat /proc/devices创建设备文件结点:mknod /dev/calldev c 240 03、编写测试驱动程序,并编译运行实例:#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <fcntl.h>#include <unistd.h>#define DEVICE_FILENAME "/dev/calldev"int main(){int dev;char buff[128];int ret;printf("1-->device file open\n");dev = open(DEVICE_FILENAME,O_RDWR | O_NDELAY);if (dev > 0){printf("2-->seek function call\n");ret = lseek(dev,0x20,SEEK_SET);printf("ret = %08x \n",ret);printf("3-->read function call\n");ret = read(dev,0x30,0x31);printf("ret = %08x \n",ret);printf("4-->write function call\n");ret = write(dev,0x40,0x41);printf("ret = %08x \n",ret);printf("5-->ioctl function call\n");ret = ioctl(dev,0x50,0x51);printf("ret = %08x \n",ret);printf("6-->close function call\n");ret = close(dev);printf("ret = %08x \n",ret);}return 0;}。

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

main()
{
int fd, num;
fd=open("/dev/globalvar", O_RDWR, S_IRUSR|S_IWUSR); 式打开设备文件
//可读写方
if(fd!=-1)
{
变量
read(fd, &num, sizeof(int));
printf("The globalvar is %d\n", num);
{
my_dev->global_var=0; 0
//设备变量初始化为
cdev_init(&my_dev->cdev, &globalvar_fops); ev 结构
//初始化设备中的 cd
my_dev->cdev.owner=THIS_MODULE; 所有者字段
//初始化 cdev 中的
err=cdev_add(&my_dev->cdev, devno, 1); ev 结构的信息
//读取设备
printf("Please input the num written to globalvar\n");
scanf("%d", &num);
write(fd, &num, sizeof(int)); 量
//写设备变
read(fd, &num, sizeof(int)); 刚才写的值
printf("The globalvar is %d\n", num);
{ owner: THIS_MODULE,
//指向拥有该模块结构的指针
open: globalvar_open,
release: globalvar_release,
read: globalvar_read,
write: globalvar_write,
};
struct globalvar_dev {
//关闭设备文件系统调用对应的操作 int globalvar_release(struct inode *inode, struct file *filp) {
return 0; }
//读设备文件系统调用对应的操作 ssize_t globalvar_read(struct file *filp, char *buf, size_t len, loff _t *off) {
//再次读取
文件
close(fd);
}
else
{
printf("Device open failure\n");
//关闭设备
} }
//获取指向已分配数据的指针 struct globalvar_dev *dev=filp->private_data; //将设备变量值复制到用户空间 if(copy_to_user(buf, &dev->global_var, sizeof(int))) {
return -EFAULT; } return sizeof(int); //返回读取数据的大小 }
//向内核添加这个 cd
if(err<0)
错误消息
printk("add device failure\n");
//如果添加失败打印
}
return ret;
}
//打开设备文件系统调用对应的操作
int globalvar_open(struct inode *inode, struct file *filp) {
//#globalvar.c
#include <linux/module.h> //模块所需的大量符号和函数定义
#include <linux/init.h>
//指定初始化和清楚函数
#include <linux/fs.h>
//文件系统相关的函数和头文件
#include <linux/cdev.h>
struct globalvar_dev *dev; //根据 inode 结构的 cdev 字段,获得整个设备结构的指针 dev=container_of(inode->i_cdev, struct globalvar_dev, cdev); //将 file 结构中的 private_data 字段指向已分配的设备结构 filp->private_data=dev; return 0; }
//用来表示我们定义设备的结构
int global_var; struct cdev cdev; };
//这个变量代表要操作的设备 //内核中表示字符设备的结构
struct globalvar_dev *my_dev;
//设备结构的指针
static void __exit globalvar_exit(void)
ssize_t globalvar_write(struct file *, const char *, size_t, loff_ t *);
int dev_major = 50;
//指定主设备号
int dev_minor = 0;
//指定次设备号
struct file_operations globalvar_fops= //将文件操作与分配的设备号相连
接下来运行如下代码,将驱动加入内核。 insmod globalvar.ko 此时可以用 dmesg 或 lsmod 命令检查一下模块加载是否成功。如果没有问题,就可以使用 mk nod 构造一个设备文件:
mknod /dev/globalvar c
最后,按照下面内容编写一个简单的测试文件并用 gcc 编译。 //#test.c #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <fcntl.h>
//写设备文件系统调用对应的操作 ssize_t globalvar_write(struct file *filp, const char *buf, size_t le n, loff_t *off)
{ //获取指向已分配数据的指针 struct globalvar_dev *dev=filp->private_data; //从用户空间复制数据到内核中的设备变量 if(copy_from_user(&dev->global_var, buf, sizeof(int))) { return -EFAULT; } return sizeof(int); //返回写数据的大小
my_dev=kmalloc(sizeof(struct globalvar_dev), GFP_KERNEL);
if(!my_dev)
{
ret=-ENOMEM;
//如果分配失败返回错误信息
printk("create device failed\n");
}
else
//如果分配成功就可以完成设
备的初始化
//动态分配设备号,次设备号已经指定
ret=alloc_chrdev_region(&devno, dev_minor, 1, "globalvar");
//保存动态分配的主设备号
dev_major=MAJOR(devno);
//根据期望值分配设备号
//ret=register_chrdev_region(devno, 1, "globalvar");
//退出模块时的操作
{
dev_t devno=MKDEV(dev_major, dev_minor); 号的结构
//dev_t 是用来表示设备编
cdev_del(&my_dev->cdev); 备
//从系统中移除一个字符设
kfree(my_dev);
//释放自定义的设备结构
unregister_chrdev_region(devno, 1);
}
module_init(globalvar_init); module_exit(globalvar_exit);
//模块被装载时调用 globalvar_init //模块被卸载时调用 globalvar_exit
//按如下内容编写一个 Makefile 文件,然后输入 make 就可以开始自动编译了。编译之后得 到了一个名为 globalvar.ko 的模块文件,这就是我们需要的设备驱动文件。
//#Makefile ifneq ($(KERNELRELEASE), )
obj-m := globalvar.o else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) all: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: $(MAKE) -C $(KERNELDIR) M=$(PWD) clean endif
Linux 字符设备驱动实例 1
看了《Linux 设备驱动程序》的前几章,我结合这篇教程中给出的一个 2.4 版内核的字符驱动, 自己编写了一个 2.6 版内核的驱动程序,并且加上了详细的注释。这个程序很简单,但是对初 学者把握 2.6 版内核的字符驱动的脉络应该有一定的帮助,也可以算作我对《Linux 设备驱动 程序》前几章学习的一个小结。
//cdev 结构的头文件
#include <asm/uaccess.h> //在内核和用户空间中移动数据的函数
MODULE_LICENSE("GPL");
相关文档
最新文档