目录

博客说明

开发环境

1. Linux字符设备驱动的组成

1.1 字符设备驱动模块加载与卸载函数

1.2 字符设备驱动的file_operations 结构体中的成员函数

2. 字符设备驱动——设备号注册卸载

2.1 设备号注册

2.2 设备号注销

3. 字符设备驱动——文件操作

参考资料

示例代码

@(从Linux内核LED驱动来理解字符设备驱动开发流程)

博客说明

撰写日期

2018.12.08

完稿日期

2019.10.06

最近维护

暂无

本文作者

multimicro

联系方式

[email protected]

GitHub

https://github.com/wifialan

本文地址

https://blog.csdn.net/multimicro/article/details/84898135

开发环境

环境说明

详细信息

备注信息

操作系统

Ubunut 18.04.3 LTS

开发板

S3C2440(JZ2440-V3)

kernel版本

Linux-3.4.2

官网地址

busybox版本

busybox-1.22.1

官网地址

编译器

arm-Linux-gcc-4.4.3

下载地址

编译器路径

/opt/FriendlyARM/toolschain/4.4.3/bin

绝对路径

1. Linux字符设备驱动的组成

引自宋宝华《Linux设备驱动开发详解--基于最新的Linux 4.0内核》P138内容:

在Linux中,字符设备驱动由如下几个部分组成。

1. 字符设备驱动模块加载与卸载函数

2. 字符设备驱动的file_operations 结构体中的成员函数

这里先介绍一下字符设备的开发流程:字符设备驱动是通过设备号 与上位机程序连接。而上位机程序对驱动的控制则是通过文件操作,即read、write、ioctl等完成。

ps.(对于linux系统而言,一切皆文件,驱动加载成功后,会在/proc/devices里面添加驱动节点号信息)

因此一个字符设备驱动应包含1. 设备号的注册、卸载和2. 文件操作两个功能,注册的设备号用于提供接口,而文件操作用于对驱动的操作。

字符设备驱动的结构如下图所示:

对于cdev_init函数中,建立file_operations之间的连接的疑问,看一下cdev_init的实现

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

{

memset(cdev, 0, sizeof *cdev);

INIT_LIST_HEAD(&cdev->list);

kobject_init(&cdev->kobj, &ktype_cdev_default);

cdev->ops = fops;

}

可以看出,最后的一个语句cdev->ops = fops;完成了在cdev中的file_operations的绑定

下面从程序语言角度感性的认识一下设备号的注册、卸载函数原型,和文件操作函数原型。

1.1 字符设备驱动模块加载与卸载函数

//加载函数

static int __init xxx_init(void)

{

... ...

}

//卸载函数

static int __exit xxx_exit(void)

{

... ...

}

1.2 字符设备驱动的file_operations 结构体中的成员函数

static const struct file_operations xxx_fileops = {

.owner = THIS_MODULE,

.write = xxx_write,

.read = xxx_read,

.open = xxx_open,

.unlocked_ioctl = xxx_ioctl,

... ...

};

static int xxx_open( struct inode *inodes, struct file *filp )

{

... ...

}

static long xxx_ioctl( struct file *file, unsigned int cmd, unsigned long arg )

{

... ...

}

static ssize_t xxx_write( struct file *filp, const char __user *buffer, size_t size, loff_t *f_pos )

{

... ...

}

static ssize_t xxx_read( struct file *filp, const char __user *buffer, size_t size, loff_t *f_pos )

{

... ...

}

2. 字符设备驱动——设备号注册卸载

以我写的字符设备驱动源代码为例,路径为Linux-3.4.2\drivers\char\s3c2440_leds.c,文章后附有完整的代码

设备号的注册由static int __init s3c2440_leds_init(void)完成

设备号的卸载由static int __init s3c2440_leds_exit(void)完成

首先分析设备号的注册,然后分析卸载

2.1 设备号注册

设备号分为主设备号和次设备号,若源代码中定义了主设备号(次设备号一般为0),那么可以直接完成设备号的注册,其流程为

注册成功后,可通过cat /proc/devices命令查看设备号

2.2 设备号注销

相比设备号的注册,注销流程就十分简单:

3. 字符设备驱动——文件操作

当上位机程序通过调用open函数打开(链接上)相应的驱动程序后,open函数会返回一个==文件描述符==暂且记为fd,然后对该驱动的read、write、ioctl等操作都可以通过使用fd完成。简单的字符设备驱动程序大多采用ioctl函数控制驱动程序,而这个ioctl函数本身也不难,其实现为:

static long s3c2440_leds_ioctl( struct file *file, unsigned int cmd, unsigned long arg )

函数中

第一个参数:表示要操作的文件描述符

第二个参数:表示传递的命令字

第三个参数:表示传递的变量字

第二个参数和第三个参数的含义没有硬性规定,传递的参数符合对应的关键字限定类型即可

下面的给出示例参考

static long s3c2440_leds_ioctl( struct file *file, unsigned int cmd, unsigned long arg )

{

printk(DRV_NAME "\tRecv cmd: %u\n", cmd);

printk(DRV_NAME "\tRecv arg: %lu\n", arg);

//IO operations function.

if(arg > 4) {

return -EINVAL;

}

switch (cmd) {

case IOCTL_LED_ON: //#define IOCTL_LED_ON 1

s3c2410_gpio_setpin(S3C2410_GPF(arg+3), 0);//Set pin

printk("Open LED %lu ",arg);

return 0;

case IOCTL_LED_OFF: //#define IOCTL_LED_OFF 0

s3c2410_gpio_setpin(S3C2410_GPF(arg+3), 1);

printk("Close LED %lu ",arg);

return 0;

default:

return -EINVAL;

}

}

参考资料

1. 宋宝华《Linux设备驱动开发详解–基于最新的Linux 4.0内核》 第6章 字符设备驱动

示例代码

/*

* Driver for S3C2440 base board.

*

* Copyright (C) 2019 Alan NWPU

*

* This program is free software; you can redistribute it and/or modify

* it under the terms of the GNU General Public License as published by

* the Free Software Foundation; either version 2 of the License, or

* (at your option) any later version.

*

* This program is distributed in the hope that it will be useful,

* but WITHOUT ANY WARRANTY; without even the implied warranty of

* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

* GNU General Public License for more details.

*

* You should have received a copy of the GNU General Public License

* along with this program; if not, write to the Free Software

* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

* MULTIBEANS, NPU Youyi West Ave, Beilin District, Xi'an, China.

*/

#include /* Every Linux kernel module must include this head */

#include /* Every Linux kernel module must include this head */

#include /* printk() */

#include /* struct fops */

#include /* error codes */

#include /* cdev_alloc() */

#include /* request_mem_region() */

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define DRV_NAME "s3c2440_leds"

#define DRV_AUTHOR "Alan Tian "

#define DRV_DESC "S3C2440 LED Pin Driver"

#define S3C2440_LED_SIZE 0x1000

#define S3C2440_LED_MAJOR 230

#define S3C2440_LED_MINOR 0

static int major = S3C2440_LED_MAJOR;

static int minor = S3C2440_LED_MINOR;

/* 应用程序执行ioctl(fd, cmd, arg)时的第2个参数 */

#define IOCTL_LED_ON 0

#define IOCTL_LED_OFF 1

static int s3c2440_leds_open( struct inode *inodes, struct file *filp );

static long s3c2440_leds_ioctl( struct file *file, unsigned int cmd, unsigned long arg );

static ssize_t s3c2440_leds_write( struct file *filp, const char __user *buffer, size_t size, loff_t *f_pos );

static ssize_t s3c2440_leds_read( struct file *filp, const char __user *buffer, size_t size, loff_t *f_pos );

struct s3c2440_leds_dev_t

{

struct cdev cdev;

unsigned char mem[S3C2440_LED_SIZE];

} *s3c2440_leds_dev;

//Step 2: Add file operations

static const struct file_operations s3c2440_leds_fileops = {

.owner = THIS_MODULE,

.write = s3c2440_leds_write,

.read = s3c2440_leds_read,

.open = s3c2440_leds_open,

.unlocked_ioctl = s3c2440_leds_ioctl,

};

static int s3c2440_leds_open( struct inode *inodes, struct file *filp )

{

//int ret;

filp->private_data = s3c2440_leds_dev;

printk(DRV_NAME"\tS3C2440 open function...\n");

return 0;

}

static long s3c2440_leds_ioctl( struct file *file, unsigned int cmd, unsigned long arg )

{

printk(DRV_NAME "\tRecv cmd: %u\n", cmd);

printk(DRV_NAME "\tRecv arg: %lu\n", arg);

//IO operations function.

if(arg > 4) {

return -EINVAL;

}

switch (cmd) {

case IOCTL_LED_ON:

s3c2410_gpio_setpin(S3C2410_GPF(arg+3), 0);//Set pin

printk("Open LED %lu ",arg);

return 0;

case IOCTL_LED_OFF:

s3c2410_gpio_setpin(S3C2410_GPF(arg+3), 1);

printk("Close LED %lu ",arg);

return 0;

default:

return -EINVAL;

}

}

static ssize_t s3c2440_leds_write( struct file *filp, const char __user *buffer, size_t size, loff_t *f_pos )

{

unsigned long p = *f_pos;

unsigned int count = size;

int ret = 0;

struct s3c2440_leds_dev_t *dev = filp->private_data;

if(p >= S3C2440_LED_SIZE)

return 0;

if(count > S3C2440_LED_SIZE - p)

count = S3C2440_LED_SIZE - p;

memset(dev->mem, 0, S3C2440_LED_SIZE);

if(copy_from_user(dev->mem + p, buffer, count)) {

ret = -EFAULT;

} else {

*f_pos += count;

ret = count;

printk(KERN_INFO "writter %u bytes(s) from %lu\n", count, p);

}

return ret;

}

static ssize_t s3c2440_leds_read( struct file *filp, const char __user *buffer, size_t size, loff_t *f_pos )

{

unsigned long p = *f_pos;

unsigned int count = size;

int ret = 0;

struct s3c2440_leds_dev_t *dev = filp->private_data;

if(p >= S3C2440_LED_SIZE)

return 0;

if(count > S3C2440_LED_SIZE - p)

count = S3C2440_LED_SIZE - p;

if(copy_to_user(buffer, dev->mem + p, count)) {

ret = -EFAULT;

} else {

*f_pos += count;

ret = count;

printk(KERN_INFO "read %u bytes(s) from %lu\n", count, p);

}

return ret;

}

static int __init s3c2440_leds_init(void)

{

int ret,err;

dev_t devid;

if(major) {

devid = MKDEV(major, 0);

ret = register_chrdev_region(devid, 1, DRV_NAME);

printk("Origin Creat node %d\n",major);

} else {

ret = alloc_chrdev_region(&devid, 0, 1, DRV_NAME);

major = MAJOR(devid);

printk("Arrage1 Creat node %d\n",major);

}

if(ret < 0) {

printk(DRV_NAME "\ts3c2440 new device failed\n");

//goto fail_malloc;

return ret;

}

s3c2440_leds_dev = kzalloc(sizeof(struct s3c2440_leds_dev_t), GFP_KERNEL);

if(!s3c2440_leds_dev) {

ret = -ENOMEM;

goto fail_malloc;

}

printk("success init leds\n");

cdev_init(&s3c2440_leds_dev->cdev, &s3c2440_leds_fileops);

err = cdev_add(&s3c2440_leds_dev->cdev, devid, 1);

if(err)

printk(KERN_NOTICE "Error %d adding s2c2440_leds %d",err, 1);

return 0;

fail_malloc:

unregister_chrdev_region(devid, 1);

return ret;

}

static void __exit s3c2440_leds_exit(void)

{

printk("Starting delet node %d\n",major);

cdev_del(&s3c2440_leds_dev->cdev);

kfree(s3c2440_leds_dev);

unregister_chrdev_region(MKDEV(major, minor), 1);

printk("Delete node %d\n",major);

}

module_init(s3c2440_leds_init);

module_exit(s3c2440_leds_exit);

MODULE_AUTHOR(DRV_AUTHOR);

MODULE_DESCRIPTION(DRV_DESC);

MODULE_LICENSE("GPL");

linux内核led驱动开发,从Linux内核LED驱动来理解字符设备驱动开发流程相关推荐

  1. linux open函数_Linux驱动开发 / 字符设备驱动内幕 (1)

    哈喽,我是老吴,继续记录我的学习心得. 一.保持专注的几个技巧 将最重要的事放在早上做. 待在无干扰环境下,比如图书馆. 意识到刚坐下开始投入工作前,有点负面小情绪是特别正常的现象. 让"开 ...

  2. Linux之字符设备驱动框架

    目录 一.驱动介绍 1.内核模块 2.日志级别 3.模块符号的导出 4.内核模块参数 二.字符设备驱动(一) 1.模块加载 2.注册字符设备驱动 3.内存映射 三.字符设备驱动(二) 1.模块加载 2 ...

  3. Linux 字符设备驱动结构(一)—— cdev 结构体、设备号相关知识解析

    一.字符设备基础知识 1.设备驱动分类 linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数 ...

  4. Linux字符设备驱动剖析

    以下内容转载于博客http://blog.csdn.net/yueqian_scut/article/details/45938557.有删改和格式调整,如有侵权,请告知删除 . 一.应用层的程序 很 ...

  5. linux 字符设备驱动实战

    文章目录 字符设备 1.加载卸载模块 2.字符设备相关结构体 2.1.file_operations 结构体 2.2.cdev 结构体 2.3.cdev 系列函数 常使用模板如下 3.注册设备号 常使 ...

  6. Linux驱动之字符设备驱动

    系列文章目录 第一章 Linux入门之驱动框架 第二章 Linux驱动之字符设备驱动 文章目录 系列文章目录 前言 一.认识字符设备驱动 1.基本概念 2.基本概念 二.字符设备旧框架 1.注册和注销 ...

  7. 02_字符设备驱动开发

    目录 1 字符设备驱动简介 2 字符设备驱动开发步骤 2.1 驱动模块的加载和卸载 2.2 字符设备注册与注销 2.3 实现设备的具体操作函数 2.4 添加 LICENSE 和作者信息 3 Linux ...

  8. STM32MP157驱动开发——字符设备驱动

    一.简介 字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节 流进行读写操作的设备,读写数据是分先后顺序的.比如我们最常见的点灯.按键. IIC. SPI, LCD ...

  9. 字符设备驱动开发的流程

    目录 1.字符设备驱动简介 2.字符设备驱动开发步骤 2.1驱动模块的加载和卸载 2.2字符设备注册与注销 2.3实现设备的具体操作函数 3.linux设备号 3.1设备号的组成 3.2设备号的分配 ...

最新文章

  1. nginx+keepalive主从双机热备+自动切换解决方案
  2. 左转待转区----当同向直行信号灯绿灯亮时,左转弯的车辆进入左转待转区等候放行信号(即使此时左转弯灯是红色的) 注意:当直行红灯时候禁止进入...
  3. reg怎样存取注册表信息
  4. 使用Httpclient来替代客户端的jsonp跨域解决方案
  5. mmap映射大于4g的文件_浅谈mmap_刘伟
  6. 自定义ui_如何允许用户自定义UI
  7. ListView中convertView和ViewHolder的工作原理
  8. C语言和C++语言在语法上面的部分区别
  9. [转载]C#获取DLL的目录
  10. 浅谈软件测试人员不要这样写测试用例(给测试新手和老鸟的提示)
  11. php 常用时间处理函数,PHP date函数常用时间处理方法_PHP
  12. JS 实现两表格里的数据来回 转移
  13. ACR122U-A9|ACR1251|ACM1252系列NFC读写器读卡器PCSC Tool测试工具使用步骤说明
  14. 关闭app服务器系统,ios12系统服务哪些关掉
  15. 浮动时间怎么计算_软考学习第21天-----软考案例分析的计算题
  16. 理解和应用向量积与数量积
  17. php爬拉钩数据,拉勾网数据两种爬取
  18. 读了100多本书只向你推荐这6本(豆瓣评分8.0以上)
  19. iPadmini能运行c语言吗,一个移动固态硬盘,可以给iPad Pro2020和iPad Mini用是什么体验?...
  20. EasyPoi导出Excel实现标记颜色

热门文章

  1. MFC之图像绘制---高速绘图控件(High-speed Charting Control)应用(二)
  2. js 无弹框打印_打印预览,关闭窗口等js代码
  3. 计算机网络---TCP序列号和确认号
  4. 做了个多语种网站 不能被GOOGLE,yahoo收录,baidu可以收录 终于找到问题
  5. 一方包、二方包、三方包是什么?
  6. 预测大盘最准确的指标_一辈子死记一个指标,完全弄透彻,即可预测一个月的股市升跌!...
  7. APP使用monkey进行稳定性测试过程
  8. 使用Python实现键盘记录器和邮箱自动通知
  9. 蜡烛图plotly_Python数据可视化:如何用mplfinance创建蜡烛图
  10. 程序员不得不学的操作系统知识(一)