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.mzph.cn/news/746660.shtml

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

相关文章

LeetCode每日一题 将有序数组转换为二叉搜索树(分治)

题目描述 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵平衡二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,null,9] 也将被视…

49、C++/友元、常成员函数和常对象、运算符重载学习20240314

一、封装类 用其成员函数实现&#xff08;对该类的&#xff09;数学运算符的重载&#xff08;加法&#xff09;&#xff0c;并封装一个全局函数实现&#xff08;对该类的&#xff09;数学运算符的重载&#xff08;减法&#xff09;。 代码&#xff1a; #include <iostream…

力扣刷题 Days18-第二题-完全二叉树的节点个数(js)

1,题目 给你一棵 完全二叉树 的根节点 root &#xff0c;求出该树的节点个数。 完全二叉树 的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一层的节点都集中在该层最左边的若干位…

有没有能用蓝牙的游泳耳机?四大年度最佳游泳耳机由衷推荐

随着科技的不断发展&#xff0c;游泳爱好者们对于游泳耳机的追求也越来越高。在游泳过程中&#xff0c;音乐和播客是许多泳者们的最佳伴侣&#xff0c;它能帮助他们保持节奏、提高兴趣。然而&#xff0c;传统的有线耳机在水下容易产生拉扯&#xff0c;不仅影响游泳体验&#xf…

【Linux操作系统】:Linux进程概念(2)

一、Z(zombie)-僵尸进程 1.僵尸进程概念 故事 张三每天都有跑步的习惯&#xff0c;这一天他和往常一样跑步&#xff0c;跑了两三圈&#xff0c;突然跑在它前面的一个人倒在地上不动了&#xff0c;作为热心市民张三赶紧报警并且拨打120。很快120就来了&#xff0c;但是没过几分…

使用 QLoRA 在 Google Colab 中微调 Mistral 7b(完整指南)

使用 QLoRA 在 Google Colab 中微调 Mistral 7b&#xff08;完整指南&#xff09; 在本文中&#xff0c;我们将在一个名为 Enlighten 的游戏的整个代码库上微调 Mistral 7b&#xff0c;所有这些都在 Google Colab&#xff08;或 Kaggle&#xff09;中免费提供合成数据。在我们的…

深度学习 精选笔记(11)深度学习计算相关:GPU、参数、读写、块

学习参考&#xff1a; 动手学深度学习2.0Deep-Learning-with-TensorFlow-bookpytorchlightning ①如有冒犯、请联系侵删。 ②已写完的笔记文章会不定时一直修订修改(删、改、增)&#xff0c;以达到集多方教程的精华于一文的目的。 ③非常推荐上面&#xff08;学习参考&#x…

中科数安 | 企业办公透明加密系统,终端文件数据 \ 资料防泄密管理软件

#公司办公文件数据 \ 资料防泄密软件系统# "中科数安"是一家专注于数据安全领域的公司&#xff0c;其提供的企业办公加密系统是一种针对企事业单位内部数据安全需求而设计的解决方案。该系统通过先进的加密技术&#xff0c;对企业在日常办公过程中产生的各类敏感信息…

突飞猛进,智能饮品机器人如何助力实体经济?

近日&#xff0c;财务部公布了2024年第一季度及全年财报。数据显示&#xff0c;连锁品牌增长速度惊人&#xff0c;这其中不得不提到智能饮品机器人的使用&#xff0c;为不同的品牌门店拼速度、抢点位立下了不小的功劳&#xff0c;那么智能饮品机器人到底如何助力各门店&#xf…

Outlook API发送邮件的方法?如何设置接口?

如何使用Outlook API发送电子邮件&#xff1f;怎么调用API接口&#xff1f; 为了满足更高级别的需求&#xff0c;我们可能需要通过编程的方式来操作Outlook&#xff0c;这时候&#xff0c;Outlook API就显得尤为重要了。那么&#xff0c;如何使用Outlook API发送邮件呢&#x…

Spring Security自定义认证授权过滤器

自定义认证授权过滤器 自定义认证授权过滤器1、SpringSecurity内置认证流程2、自定义Security认证过滤器2.1 自定义认证过滤器2.2 定义获取用户详情服务bean2.3 定义SecurityConfig类2.4 自定义认证流程测试 3、 基于JWT实现无状态认证3.1 认证成功响应JWT实现3.2 SpringSecuri…

OceanBase中binlog service 功能的试用

OBLogProxy简介 OBLogProxy即OceanBase的增量日志代理服务&#xff0c;它可与OceanBase建立连接并读取增量日志&#xff0c;从而为下游服务提供了变更数据捕获&#xff08;CDC&#xff09;的功能。 关于OBLogProxy的详尽介绍与具体的安装指引&#xff0c;您可以参考这篇官方OB…

基于R语言的水文、水环境模型优化技术及快速率定方法与多模型教程

原文链接&#xff1a;基于R语言的水文、水环境模型优化技术及快速率定方法与多模型教程https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247597847&idx7&snd71869f1290d0ef9dd7fd3f74dd7ca33&chksmfa823ef0cdf5b7e655af5e773a3d3a1b200632a5981f99fe72f0…

普林斯顿算法讲义(一)

原文&#xff1a;普林斯顿大学算法课程 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 1. 基础知识 原文&#xff1a;algs4.cs.princeton.edu/10fundamentals 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 概述。 本书的目标是研究各种重要和有用的算法——…

多线程编程

多线程写作类 倒计时协调器CountDownLatch 某个线程需要等待其他线程执行到特定操作结束即可。例如&#xff1a;在多Web服务中&#xff0c;在启动指定服务时需要启动若干启动过程中比较耗时的服务&#xff0c;为了尽可能减少服务启动过程的总耗时&#xff0c;该服务会使用专门…

深入探讨MES管理系统与MOM系统之间的关系

在制造业的信息化浪潮中&#xff0c;各种系统与技术层出不穷&#xff0c;其中MES制造执行系统和MOM制造运营管理无疑是备受瞩目的两大主角。尽管它们都是制造业信息化不可或缺的部分&#xff0c;但许多人对它们之间的区别与联系仍感到困惑。本文将对MES管理系统和MOM系统进行深…

#数据结构 线性表的顺序存储

目录 每日文案 一、线性表的定义 二、线性表的操作 顺序表的存储结构 顺序表的初始化操作 判断顺序表是否为空表 将顺序表置为空表 计算顺序表中的元素个数 取出顺序表中的对应位置元素 取出对应数值的位序 在对应位置插入元素 将对应位置的元素删除 将顺序表中的数据…

1.Python数据分析—数据分析与挖掘详讲

1.Python数据分析—数据分析与挖掘详讲 一个人简介二数据分析与挖掘概述三什么是数据分析和挖掘四数据分析与挖掘在不同领域的应用4.1医疗领域&#xff1a;4.1.1 建立疾病数据库&#xff1a;4.1.2 临床决策支持&#xff1a;4.1.3 疾病预警和监控&#xff1a; 4.2 电子商务领域&…

第12章 指针

以下内容是学习尚硅谷 12.1 指针基本介绍 1&#xff09;指针是C语言的精华&#xff0c;也是C语言的难点 2&#xff09;指针&#xff0c;也就是内存的地址&#xff1b;所谓指针变量&#xff0c;也就是保存了内存地址的变量。关于指针的基本使用&#xff0c;在讲变量的时候做了…

d2-crud-plus 使用小技巧(四)—— 搜索限制只能输入数字

需求 搜索时有些字段需要限制&#xff0c;比如只能输入数字&#xff0c;不能存在其他字符包括空格。 效果 事情焦点后先触发校验&#xff0c;在触发查询。 代码 crud.js export const crudOptions (vm) > {return {columns: [{title: 号码,key: number,search: { //…