Linux字符设备与I2C驱动结合使用

引言

在Linux操作系统中,设备驱动程序充当硬件和软件之间的桥梁。字符设备驱动是一种特殊类型的驱动,它允许用户以字节流的形式访问硬件设备。这些设备包括键盘、鼠标、串口等。在本博客中,我们将探讨Linux字符设备驱动的基础知识,构建过程,以及如何管理这些设备。

第1章:Linux设备驱动概述

在Linux中,设备通常分为字符设备、块设备和网络设备。字符设备允许按字节进行数据传输,而块设备则基于数据块操作。网络设备则用于处理网络通信。

第2章:字符设备基础

字符设备是可以按字节流进行读写的设备。它们通常不支持随机访问,数据传输必须按顺序进行。在这一章节中,我们将详细讨论字符设备的定义和特点,以及它们在Linux系统中的表示方法。

第3章:字符设备驱动程序结构

在这一章节中,我们将探讨字符设备驱动程序的基本结构。这包括设备文件的创建和注册,以及cdev结构体的重要性。cdev结构体是Linux内核用来表示字符设备的核心数据结构,它包含了设备的主要信息和操作函数接口。

在Linux内核中,cdev结构体是用来表示字符设备的关键数据结构。它包含了字符设备的主要信息和操作函数接口。以下是cdev结构体的一些主要成员及其作用:

struct cdev {struct kobject kobj;               // 内嵌的内核对象struct module *owner;              // 指向拥有该字符设备的内核模块的指针const struct file_operations *ops; // 指向文件操作函数的指针,这些函数定义了设备的行为struct list_head list;             // 用于将所有已注册的字符设备链接成一个链表dev_t dev;                         // 设备号,由主设备号和次设备号构成unsigned int count;                // 隶属于同一主设备号的次设备号的个数
};
  • kobj:用于内核对象模型,提供设备模型与sysfs的接口。
  • owner:通常设置为THIS_MODULE,确保在模块被卸载时,设备驱动不会被使用。
  • ops:指向file_operations结构,定义了字符设备的操作方法,如openreadwrite等。
  • list:用于将设备添加到内核的设备链表中。
  • dev:设备号,用于唯一标识设备。
  • count:表示与该设备关联的次设备号的数量。

如何将一个I2C驱动 描述为字符设备驱动 运用:

...
#define MYMA 301      //主设备号
#define COUNT 1
typedef struct {struct cdev cdev;struct i2c_client *cli;struct class *cls;
} i2cDev_data_t;int myprobe(struct i2c_client *cli, const struct i2c_device_id *id)
{struct device_node *np;struct device *dev;int ret;i2cDev_data_t *data;static int mi = 0;dev_t devid;devid = MKDEV(MYMA, mi);ret = register_chrdev_region(devid, COUNT, cli->name);if (ret < 0)goto err0;data = kzalloc(sizeof(*data), GFP_KERNEL);if (NULL == data) {ret = -ENOMEM;goto err1;}cdev_init(&data->cdev, &fops);data->cdev.owner = THIS_MODULE;ret = cdev_add(&data->cdev, devid, COUNT);if (ret < 0)goto err2;data->cls = class_create(THIS_MODULE, cli->name);device_create(data->cls, NULL, devid, NULL, "%s.%d", cli->name, mi++);data->cli = cli;i2c_set_clientdata(cli, data);dev = &cli->dev;if (!dev)return -ENODEV;np = dev->of_node;return 0;
err2:kfree(data);
err1:unregister_chrdev_region(devid, COUNT);
err0:return ret;
}
...

各API作用如下:

 1.MKDEV: #define MKDEV(major, minor) (((major) << MINORBITS) | (minor))
MKDEV宏用于将主设备号(major number)和次设备号(minor number)组合成一个dev_t类型的设备编号。这个设备编号通常用于设备文件的创建和设备驱动程序的注册。

2.register_chrdev_region:

在Linux内核编程中,register_chrdev_region函数用于静态注册一组字符设备编号。如果您已经知道要使用的主设备号和次设备号,可以使用此函数进行注册。以下是该函数的原型和简要说明:

int register_chrdev_region(dev_t first, unsigned int count, char *name);
  • first:要注册的第一个设备编号,包括主设备号和起始次设备号。
  • count:要注册的设备数量,即次设备号的个数。
  • name:与设备编号关联的设备名称,这个名称会出现在/proc/devices目录下。

当调用register_chrdev_region函数时,如果指定的设备编号范围已经被占用,函数将返回一个负值错误代码。如果注册成功,函数将返回0。在注册设备编号之前,您应该检查/proc/devices以确保所需的设备号没有被占用

3.cdev_init :用于初始化一个已经分配的cdev结构体。这个函数将file_operations结构体与cdev结构体关联起来,为设备提供必要的文件操作方法。

4.cdev_add:用于将一个cdev结构体添加到内核中,使得相应的字符设备立即可用。

5.class_create:用于创建一个新的设备类,这个类将出现在/sys/class目录下,成功调用class_create后,您可以在/sys/class/<name>下找到新创建的设备类。

6.device_create:用于在已创建的设备类下创建一个设备,并在/dev目录下自动创建相应的设备文件节点。

第4章:文件操作接口

字符设备驱动程序通常需要实现一系列文件操作接口,如openreleaseclose)、readwrite等。这些接口允许用户空间的程序通过设备文件与设备进行交互。我们将详细讨论这些接口的实现和它们在设备驱动中的作用。

ssize_t chr_write(struct file *fl, const char __user *buf, size_t len,loff_t *off)
{struct i2c_msg msg;struct cdev *cdev = fl->f_path.dentry->d_inode->i_cdev;i2cDev_data_t *data = container_of(cdev, i2cDev_data_t, cdev);char *kbuf = NULL;int ret =0;char addr;struct i2c_client *cli = data->cli;kbuf = kzalloc(len+1, GFP_KERNEL);ret = copy_from_user(kbuf, buf, len);addr = atoi(kbuf);msgs.addr = cli->addr;msgs.flags = 0;msgs.len = 1;msgs.buf = &addr;ret = i2c_transfer(cli->adapter, &msgs, 1);if (ret < 0) {chr_DEBUG("%s error %d\n", __func__, ret);}kfree(kbuf);return len;
}static long chr_ioctl(struct file *fl, unsigned int cmd, unsigned long arg)
{printk("chr_ioctl");return 0;
}int chr_open(struct inode *ind, struct file *fl)
{printk("chr_open");return 0;
}ssize_t chr_read(struct file *fl, char __user *buf, size_t len, loff_t *off)
{chr_DEBUG("chr_read audioValue=%d\n", audioValue);return audioValue;
}struct file_operations fops = {.owner = THIS_MODULE,.read = chr_read,.open = chr_open,.write = chr_write,.unlocked_ioctl = chr_ioctl,
};

1.container_of:

container_of宏是一个非常有用的工具,它允许您通过结构体的一个成员的地址来获取整个结构体的地址。这在驱动开发和内核编程中非常常见,尤其是当您只有对结构体中某个成员的引用时。以下是container_of宏的一般用法:

#define container_of(ptr, type, member) ({          \const typeof( ((type *)0)->member ) *__mptr = (ptr); \(type *)( (char *)__mptr - offsetof(type, member) );})
  • tr:指向结构体中成员的指针。
  • type:结构体的类型。
  • member:结构体中的成员名称

  2.struct file_operations :

struct file_operations {struct module *owner;  // 指向模块所有者的指针loff_t (*llseek) (struct file *, loff_t, int);  // 改变文件读写位置的方法ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);  // 从设备读取数据的方法ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);  // 向设备写入数据的方法int (*open) (struct inode *, struct file *);  // 打开设备的方法int (*release) (struct inode *, struct file *);  // 释放设备的方法// ... 其他操作方法
};

这些方法对应于用户空间程序对设备文件执行的系统调用。例如,当用户程序调用`read()`系统调用时,内核会调用file_operations中的read方法来从设备读取数据

实际运用将一个I2C驱动添加字符设备接口

dtsi:
&i2c5 {status = "okay";chr_drive: chr_drive@44 {status = "okay";compatible = "chr_drive";reg = <0x44>;};
};

chr_driver.c 

#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#define MYMA 301
#define COUNT 1static unsigned char audioValue = 43;typedef struct {struct cdev cdev;struct i2c_client *cli;struct class *cls;
} i2cDev_data_t;static int atoi(const char *str)
{int result = 0;int sign = 0;if (str == NULL) {return -1;}while (*str == ' ' || *str == '\t' || *str == '\n')++str;if (*str == '-') {sign = 1;++str;} else if (*str == '+') {++str;}while (*str >= '0' && *str <= '9') {result = result * 10 + *str - '0';++str;}if (sign == 1)return -result;elsereturn result;
}
ssize_t chr_write(struct file *fl, const char __user *buf, size_t len,loff_t *off)
{struct i2c_msg msg;struct cdev *cdev = fl->f_path.dentry->d_inode->i_cdev;i2cDev_data_t *data = container_of(cdev, i2cDev_data_t, cdev);char *kbuf = NULL;int ret =0;char addr;struct i2c_client *cli = data->cli;kbuf = kzalloc(len+1, GFP_KERNEL);ret = copy_from_user(kbuf, buf, len);addr = atoi(kbuf);msg.addr = cli->addr;msg.flags = 0;msg.len = 1;msg.buf = &addr;printk("chr_write %d",addr);ret = i2c_transfer(cli->adapter, &msg, 1);if (ret < 0) {printk("%s error %d\n", __func__, ret);}kfree(kbuf);return len;
}static long chr_ioctl(struct file *fl, unsigned int cmd, unsigned long arg)
{printk("chr_ioctl");return 0;
}int chr_open(struct inode *ind, struct file *fl)
{printk("chr_open");return 0;
}ssize_t chr_read(struct file *fl, char __user *buf, size_t len, loff_t *off)
{printk("chr_read");return 0;
}struct file_operations fops = {.owner = THIS_MODULE,.read = chr_read,.open = chr_open,.write = chr_write,.unlocked_ioctl = chr_ioctl,
};int myprobe(struct i2c_client *cli, const struct i2c_device_id *id)
{struct device_node *np;struct device *dev;int ret;i2cDev_data_t *data;static int mi = 0;dev_t devid;devid = MKDEV(MYMA, mi);ret = register_chrdev_region(devid, COUNT, cli->name);if (ret < 0)goto err0;data = kzalloc(sizeof(*data), GFP_KERNEL);if (NULL == data) {ret = -ENOMEM;goto err1;}cdev_init(&data->cdev, &fops);data->cdev.owner = THIS_MODULE;ret = cdev_add(&data->cdev, devid, COUNT);if (ret < 0)goto err2;data->cls = class_create(THIS_MODULE, cli->name);device_create(data->cls, NULL, devid, NULL, "%s.%d", cli->name, mi++);data->cli = cli;i2c_set_clientdata(cli, data);dev = &cli->dev;if (!dev)return -ENODEV;np = dev->of_node;return 0;
err2:kfree(data);
err1:unregister_chrdev_region(devid, COUNT);
err0:return ret;
}int myremove(struct i2c_client *cli)
{i2cDev_data_t *data = i2c_get_clientdata(cli);device_destroy(data->cls, data->cdev.dev);class_destroy(data->cls);cdev_del(&data->cdev);unregister_chrdev_region(data->cdev.dev, COUNT);kfree(data);return 0;
}struct i2c_device_id ids[] = {{ "chr_drive", 0 },{},
};MODULE_DEVICE_TABLE(i2c, ids);static const struct of_device_id chr_of_match[] = {{ .compatible = "chr_drive" },{},
};struct i2c_driver mydrv = {.probe = myprobe,.remove = myremove,.driver = {.name = "chr_drive",.of_match_table = chr_of_match,.owner = THIS_MODULE,},.id_table = ids,
};module_i2c_driver(mydrv);
MODULE_LICENSE("GPL");

注册成功会在dev下生成chr_drive节点

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/278832.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Github 2024-03-16 Rust开源项目日报 Top10

根据Github Trendings的统计,今日(2024-03-16统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10TypeScript项目2Go项目1RustDesk: 用Rust编写的开源远程桌面软件 创建周期:1218 天开发语言:Rust, Dart协议类型:GNU Affero Gene…

二叉树遍历(牛客网)

描述 编一个程序&#xff0c;读入用户输入的一串先序遍历字符串&#xff0c;根据此字符串建立一个二叉树&#xff08;以指针方式存储&#xff09;。 例如如下的先序遍历字符串&#xff1a; ABC##DE#G##F### 其中“#”表示的是空格&#xff0c;空格字符代表空树。建立起此二叉树…

Qt实现简单的五子棋程序

Qt五子棋小程序 Qt五子棋演示及源码链接登陆界面单机模式联机模式联网模式参考 Qt五子棋 参考大佬中国象棋程序&#xff0c;使用Qt实现了一个简单的五子棋小程序&#xff0c;包含了单机、联机以及联网三种模式&#xff1b;单机模式下实现了简易的AI&#xff1b;联机模式为PtoP…

【LabVIEW FPGA入门】并行执行

利用图形化编程的并行特性以及 FPGA 上 LabVIEW 图的真正并行实现&#xff0c;您可以通过将应用程序代码划分为更小的进程来进一步优化执行速度。与整个应用程序在一个循环中运行相比&#xff0c;这使得每个进程能够实现更高的循环速率和更高的应用程序整体执行速率。 …

项目中遇到的sql问题记录

有一张表&#xff0c;表结构及数据如下&#xff1a; INSERT INTO test.test_approve(approve_no, tra_date, tablename, part_dt) VALUES (approve001, 2021-02-18 00:00:00, tableA, 2024-03-18); INSERT INTO test.test_approve(approve_no, tra_date, tablename, part_dt) …

基础:TCP三次握手做了什么,为什么要握手?

1. TCP 三次握手在做些什么 1. 第一次握手 &#xff1a; 1&#xff09;握手作用&#xff1a;客户端发出建立连接请求。 2&#xff09;数据处理&#xff1a;客户端发送连接请求报文段&#xff0c;将SYN位置为1&#xff0c;Sequence Number为x;然后&#xff0c;客户端进入SYN_S…

ideaSSM物流运输管理系统短路径算法开发mysql数据库web结构Dijstra编程计算机网页源码maven项目

一、源码特点 idea ssm 物流运输管理系统是一套完善的完整信息管理系统&#xff0c;结合SSM框架完成本系统SpringMVC spring mybatis &#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码和数…

每周编辑精选|微软开源 Orca-Math 高质量数学数据集、清华大学研究团队发布条件去噪扩散模型 SPDiff...

Orca-Math 是微软研究院发布的数学推理模型&#xff0c;该模型展示了较小的专业模型在特定领域的价值&#xff0c;它们可以匹配甚至超越更大模型的性能。微软近期开源了用于训练 Orca-Math 的 Orca-Math-200K 数学单词问题数据集&#xff0c;现已在 hyper.ai 官网提供下载&…

mysql虚拟列Generated Column

目录​​​​​​​ 1、Generated Column简介 生成的列定义具有以下语法&#xff1a; 2、实践 2.1 存储格式为json字段增加索引 2.2 手机号后四位 3、虚拟列索引介绍 3.1 虚拟列索引的限制 3.1.1 Virtal Generated Column 4、阿里云数据库环境是否支持 下期扩展&…

从入门到精通:深入解析IO流之FileWriter类的使用技巧!

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

构建部署_Docker常用命令

构建部署_Docker常见命令 启动命令镜像命令容器命令 启动命令 启动docker&#xff1a;systemctl start docker 停止docker&#xff1a;systemctl stop docker 重启docker&#xff1a;systemctl restart docker 查看docker状态&#xff1a;systemctl status docker 开机启动&…

linux常用命令之用户组管理命令

1.1groupadd新增组 gid 组id 1.2 usermod -g 更改用户所在的组 1.3 groupmod -n 更改组名 1.4groupdel 删掉一个用户组

基于Matlab的视频人面检测识别,Matalb实现

博主简介&#xff1a; 专注、专一于Matlab图像处理学习、交流&#xff0c;matlab图像代码代做/项目合作可以联系&#xff08;QQ:3249726188&#xff09; 个人主页&#xff1a;Matlab_ImagePro-CSDN博客 原则&#xff1a;代码均由本人编写完成&#xff0c;非中介&#xff0c;提供…

在Visual Studio中调试 .NET源代码

前言 在我们日常开发过程中常常会使用到很多其他封装好的第三方类库&#xff08;NuGet依赖项&#xff09;或者是.NET框架中自带的库。如果可以设置断点并在NuGet依赖项或框架本身上使用调试器的所有功能&#xff0c;那么我们的源码调试体验和生产效率会得到大大的提升。今天我…

Java后端面试:框架篇高频面试(Spring、SpringMVC、SpringBoot、MyBatis)

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;Java后端面试&#xff1a;MySQL面试篇&#xff08;底层事务、SQL调优&#xff09; &#x1f4da;订阅专栏&#xff1a;Java后端面…

mac os 配置两个github账号

1. 清空git全局配置的username和email git config --global --unset user.name git config --global --unset user.emailgit config --list 可以查看是否清空了 2. 定义两个标识符,这两个标识符以后会被用来代替“github.com”来使用。 假设两个账号的邮箱地址分别是a@gmai…

JAVA实战开源项目:农村物流配送系统(Vue+SpringBoot)

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统登录、注册界面2.2 系统功能2.2.1 快递信息管理&#xff1a;2.2.2 位置信息管理&#xff1a;2.2.3 配送人员分配&#xff1a;2.2.4 路线规划&#xff1a;2.2.5 个人中心&#xff1a;2.2.6 退换快递处理&#xff1a;…

【算法与数据结构】二叉树(前中后)序遍历

文章目录 &#x1f4dd;前言&#x1f320; 创建简单二叉树&#x1f309;二叉树的三种遍历&#x1f320;前序&#x1f309;中序遍历 &#x1f320;后序遍历 &#x1f320;二叉树节点个数&#x1f309;二叉树节点个数注意点 &#x1f6a9;总结 &#x1f4dd;前言 一棵二叉树是结…

云原生 PaaS 服务:构建现代应用的利器(分布式应用服务、配置中心、数据库服务、定时任务、实时监控、服务网关、技术组件)

在当今数字化时代&#xff0c;企业需要面对不断变化的市场需求和竞争压力&#xff0c;以及日益复杂的应用开发和部署挑战。在这样的背景下&#xff0c;云原生 PaaS&#xff08;Platform as a Service&#xff09;服务应运而生&#xff0c;为企业提供了一种现代化的应用开发和部…

计算机视觉之三维重建(1)---摄像机几何

文章目录 一、针孔模型和透镜1.1 针孔摄像机1.2 近轴折射模型1.3 透镜问题 二、摄像机几何2.1 像平面和像素平面2.2 齐次坐标下的投影变换2.3 摄像机倾斜2.4 规范化摄像机2.5 世界坐标系2.6 Faugeras定理2.7 投影变换性质&#xff1a; 三、其他投影摄像机模型3.1 弱透视投影摄像…