linux内核led驱动开发,从Linux内核LED驱动来理解字符设备驱动开发流程
目录
博客说明
开发环境
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驱动来理解字符设备驱动开发流程相关推荐
- linux open函数_Linux驱动开发 / 字符设备驱动内幕 (1)
哈喽,我是老吴,继续记录我的学习心得. 一.保持专注的几个技巧 将最重要的事放在早上做. 待在无干扰环境下,比如图书馆. 意识到刚坐下开始投入工作前,有点负面小情绪是特别正常的现象. 让"开 ...
- Linux之字符设备驱动框架
目录 一.驱动介绍 1.内核模块 2.日志级别 3.模块符号的导出 4.内核模块参数 二.字符设备驱动(一) 1.模块加载 2.注册字符设备驱动 3.内存映射 三.字符设备驱动(二) 1.模块加载 2 ...
- Linux 字符设备驱动结构(一)—— cdev 结构体、设备号相关知识解析
一.字符设备基础知识 1.设备驱动分类 linux系统将设备分为3类:字符设备.块设备.网络设备.使用驱动程序: 字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数 ...
- Linux字符设备驱动剖析
以下内容转载于博客http://blog.csdn.net/yueqian_scut/article/details/45938557.有删改和格式调整,如有侵权,请告知删除 . 一.应用层的程序 很 ...
- linux 字符设备驱动实战
文章目录 字符设备 1.加载卸载模块 2.字符设备相关结构体 2.1.file_operations 结构体 2.2.cdev 结构体 2.3.cdev 系列函数 常使用模板如下 3.注册设备号 常使 ...
- Linux驱动之字符设备驱动
系列文章目录 第一章 Linux入门之驱动框架 第二章 Linux驱动之字符设备驱动 文章目录 系列文章目录 前言 一.认识字符设备驱动 1.基本概念 2.基本概念 二.字符设备旧框架 1.注册和注销 ...
- 02_字符设备驱动开发
目录 1 字符设备驱动简介 2 字符设备驱动开发步骤 2.1 驱动模块的加载和卸载 2.2 字符设备注册与注销 2.3 实现设备的具体操作函数 2.4 添加 LICENSE 和作者信息 3 Linux ...
- STM32MP157驱动开发——字符设备驱动
一.简介 字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节 流进行读写操作的设备,读写数据是分先后顺序的.比如我们最常见的点灯.按键. IIC. SPI, LCD ...
- 字符设备驱动开发的流程
目录 1.字符设备驱动简介 2.字符设备驱动开发步骤 2.1驱动模块的加载和卸载 2.2字符设备注册与注销 2.3实现设备的具体操作函数 3.linux设备号 3.1设备号的组成 3.2设备号的分配 ...
最新文章
- nginx+keepalive主从双机热备+自动切换解决方案
- 左转待转区----当同向直行信号灯绿灯亮时,左转弯的车辆进入左转待转区等候放行信号(即使此时左转弯灯是红色的) 注意:当直行红灯时候禁止进入...
- reg怎样存取注册表信息
- 使用Httpclient来替代客户端的jsonp跨域解决方案
- mmap映射大于4g的文件_浅谈mmap_刘伟
- 自定义ui_如何允许用户自定义UI
- ListView中convertView和ViewHolder的工作原理
- C语言和C++语言在语法上面的部分区别
- [转载]C#获取DLL的目录
- 浅谈软件测试人员不要这样写测试用例(给测试新手和老鸟的提示)
- php 常用时间处理函数,PHP date函数常用时间处理方法_PHP
- JS 实现两表格里的数据来回 转移
- ACR122U-A9|ACR1251|ACM1252系列NFC读写器读卡器PCSC Tool测试工具使用步骤说明
- 关闭app服务器系统,ios12系统服务哪些关掉
- 浮动时间怎么计算_软考学习第21天-----软考案例分析的计算题
- 理解和应用向量积与数量积
- php爬拉钩数据,拉勾网数据两种爬取
- 读了100多本书只向你推荐这6本(豆瓣评分8.0以上)
- iPadmini能运行c语言吗,一个移动固态硬盘,可以给iPad Pro2020和iPad Mini用是什么体验?...
- EasyPoi导出Excel实现标记颜色
热门文章
- MFC之图像绘制---高速绘图控件(High-speed Charting Control)应用(二)
- js 无弹框打印_打印预览,关闭窗口等js代码
- 计算机网络---TCP序列号和确认号
- 做了个多语种网站 不能被GOOGLE,yahoo收录,baidu可以收录 终于找到问题
- 一方包、二方包、三方包是什么?
- 预测大盘最准确的指标_一辈子死记一个指标,完全弄透彻,即可预测一个月的股市升跌!...
- APP使用monkey进行稳定性测试过程
- 使用Python实现键盘记录器和邮箱自动通知
- 蜡烛图plotly_Python数据可视化:如何用mplfinance创建蜡烛图
- 程序员不得不学的操作系统知识(一)