基于Amlogic 安卓9.0, 驱动简说(一):字符设备驱动,手动创建设备

news2024/12/29 10:09:35

文章目录

  • 一、前言
  • 二、系列文章
  • 三、解析:完整源码
    • 1. helloworld_amlogic_char_driver.c
    • 2. Makefile
  • 四、编译执行
    • 4.1 编译
    • 4.2 执行
      • (1)部署
      • (2)加载ko文件
      • (3)查看结果
      • (4)是否有设备文件了呢?
    • 4.3 手动创建设备
  • 五、应用层调用
    • 5.1 源码
    • 5.2 测试结果
  • 六、源码下载
  • 七、篇尾

一、前言

  • 本文提取书籍中,较为经典的例子。
  • 让驱动的学习,变得更加简单。
  • 目前市面上,很难找到一本讲得特别好的嵌入式驱动开发教程,尤其是适配Android 平台的。LDD3是一本翻译较好,写得较好的书,但其内容基于Linux kernel 2.6版本,接口和架构都较老了。国产书籍普遍较为片面,翻译的书籍则表述较差(机器翻译),于是乎,看完之后,想总结一下!

本文基于Amlogic T972 , Android 9.0, 内核版本 4.9.113

二、系列文章

第1篇:基于Amlogic 安卓9.0, 驱动简说(一):字符设备驱动,手动创建设备
第2篇:基于Amlogic 安卓9.0, 驱动简说(二):字符设备驱动,自动创建设备
第3篇:基于Amlogic 安卓9.0, 驱动简说(三):使用misc框架,让驱动更简单
第4篇:基于Amlogic 安卓9.0, 驱动简说(四):Platform平台驱动,驱动与设备的分离


三、解析:完整源码

提要:
(1)主设备号用于对设备进行分类
(2)此设备号代表具体的设备,第N个

1. helloworld_amlogic_char_driver.c

  • 缺点1:手动分配主设备号
  • 缺点2:需要手动创建设备文件
  • 文件,参考位置:android9.0\common\drivers\amlogic\input\helloworld_amlogic_char_driver.c
/* 模块初始化、卸载的接口头文件  */
#include <linux/module.h>

/* 字符设备头文件 */
#include <linux/cdev.h>
#include <linux/fs.h>

/* 定义主设备号 */
#define MY_MAJOR_NUM	202

/* 定义设备的私有结构体,因为简单,直接使用struct cdev,
如果要支持同个相同的设备,则一般需要为每个设备分配一个结构体,
代表对应的设备 */
static struct cdev aml_cdev;

/* 应用层系统调用:open()、fopen 打开设备节点文件时,将回调此函数  */
static int aml_cdev_open(struct inode *inode, struct file *file)
{
	pr_info("aml_cdev_open() is called.\n");
	return 0;
}

/* 应用层系统调用:close()、fclose 关闭设备节点文件时,将回调此函数  */
static int aml_cdev_close(struct inode *inode, struct file *file)
{
	pr_info("aml_cdev_close() is called.\n");
	return 0;
}

/*  应用层系统调用:ioctrl() 操作设备节点文件时,将回调此函数  */
static long aml_cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	pr_info("aml_cdev_ioctl() is called. cmd = %d, arg = %ld\n", cmd, arg);
	return 0;
}

/* 注册回调函数:只需要实现需要的接口即可 */
static const struct file_operations aml_cdev_fops = {
	.owner = THIS_MODULE,
	.open = aml_cdev_open,
	.release = aml_cdev_close,
	.unlocked_ioctl = aml_cdev_ioctl,
};

/* 模块初始化函数  */
static int __init aml_cdev_init(void)
{
	int ret;
	/* 手动分配一个主设备号、次设备号,需要避开系统已使用的号码*/
	dev_t dev = MKDEV(MY_MAJOR_NUM, 0);
	pr_info("aml_cdev_init \n");

	/* 注册并获取此设备号 */
	ret = register_chrdev_region(dev, 1, "aml_char_device");
	if (ret < 0){
		pr_info("Unable to allocate mayor number %d\n", MY_MAJOR_NUM);
		return ret;
	}

	/* 初始化aml_cdev,并将其添加内核中*/
	cdev_init(&aml_cdev, &aml_cdev_fops);
	ret= cdev_add(&aml_cdev, dev, 1);
	if (ret < 0){
		unregister_chrdev_region(dev, 1);
		pr_info("Unable to add cdev\n");
		return ret;
	}

	return 0;
}

/* 模块退出时执行卸载操作  */
static void __exit aml_cdev_exit(void)
{
	pr_info("aml_cdev_exit\n");
	cdev_del(&aml_cdev);//删除设备
	unregister_chrdev_region(MKDEV(MY_MAJOR_NUM, 0), 1); //注销设备号
}


/* 固定的模板部分,将导出模块的初始化和卸载接口符号 */
module_init(aml_cdev_init);
module_exit(aml_cdev_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("szhou <66176468@qq.com>");
MODULE_DESCRIPTION("This is a amlogic debug tool.");

2. Makefile

  • android9.0\common\drivers\amlogic\input\Makefile
#
# Makefile for the input core drivers.
#

# Each configuration option enables a list of files.

obj-$(CONFIG_AMLOGIC_AVIN_DETECT) += avin_detect/

obj-$(CONFIG_AMLOGIC_INPUT_KEYBOARD) += keyboard/

obj-$(CONFIG_AMLOGIC_REMOTE) += remote/

obj-$(CONFIG_AMLOGIC_TOUCHSCREEN) += touchscreen/

obj-$(CONFIG_AMLOGIC_SENSOR) += sensor/

# 添加下面语句,并将helloworld_amlogic_char_driver.c放在同一目录下
obj-m +=  helloworld_amlogic_char_driver.o


四、编译执行

4.1 编译

  • (1)执行安卓编译命令,全部或局部编译kernel
  • (2)模块生成位置:out/target/product/x301/obj/KERNEL_OBJ/drivers/amlogic/input/helloworld_amlogic_char_driver.ko
make: Leaving directory `/home/builder/android_x301/source/t962x3-t972-android9.0/out/target/product/x301/obj/KERNEL_OBJ'
[ 75% 3/4] Kernel installed
 Prebuilt:  (out/target/product/x301/kernel)
[100% 4/4] Target boot image: out/target/product/x301/boot.img

#### build completed successfully (05:59 (mm:ss)) ####

Build kernel ok!
root@d185403d1e6f:/home/builder/android_x301/source/t962x3-t972-android9.0# ./build.sh kernel

4.2 执行

(1)部署

  • 启动一个windows 命令行串口:ctrl + r , 输入 cmd
>adb usb
restarting in USB mode

>adb root
restarting adbd as root

>adb push helloworld_amlogic_char_driver.ko /data/
helloworld_amlogic_char_driver.ko: 1 file pushed, 0 skipped. 5.1 MB/s (101656 bytes in 0.019s)

(2)加载ko文件

>adb shell
x301:/ #
x301:/ # insmod /data/helloworld_amlogic_char_driver.ko

(3)查看结果

  • cat /proc/devicecs 可以看到已分配的设备节点,及其节点设备名称
  • 如下所示,已按楼上驱动,分配了主设备号:202
x301:/ # cat /proc/devices  | grep 202
202 aml_char_device
x301:/ #

(4)是否有设备文件了呢?

  • 创建的设备节点文件,会被放置在设备文件系统下,即/dev目录下
  • 通过命令查找,无论是文件名,主设备号,可以发现/dev目录下没有对应驱动的设备存在
  • 原因:我们只是注册了驱动,尚未创建设备
x301:/dev # ls -al  | grep 202
x301:/dev #
x301:/dev # ls -l aml_*
crw------- 1 root root 243,   0 2018-01-01 08:00 aml_demod
crw------- 1 root root 242,   0 2018-01-01 08:00 aml_demod_ui
x301:/dev #

4.3 手动创建设备

  • 在板子上,执行mknod命令创建设备节点
  • 路径:/dev
  • 文件名:aml_char ,这个可以随意
  • 主设备号:202, 必须和驱动声明的主设备号一致,否则驱动不知道你是谁
  • 次设备号:0, 一般从0开始编号
1|x301:/dev # mknod /dev/aml_char c 202 0
x301:/dev #

如此,我们就创建了和驱动能匹配的设备文件,应用层的程序通过系统调用,open(“/dev/aml_char”, O_RDWR),既可以以可读写方式打开设备。

五、应用层调用

5.1 源码

  • hello_aml.c
#include <stdio.h>

int main()
{


  int buf[4]={0};
  int fd=0;

  //(1) 测试 open
  fd = open("/dev/aml_char", O_RDWR);
  if(fd == -1){
	printf("Failed: open /dev/aml_char \n");
	return 0;
  }

  //(2) 测试 ioctl
  ioctl(fd, 0x12, buf); 

  //(3) 测试 clsoe	
  close(fd);
  return 0;

}

5.2 测试结果

  • 打印默认是输出到串口上,所以此处是在串口上执行命令
  • 使用adb shell的同学,可以执行后,输入dmesg查看打印
:/ # ./data/hello_aml 
[11843.579654@2]- aml_cdev_open() is called.
[11843.579734@2]- aml_cdev_ioctl() is called. cmd = 18, arg = -1148368336
[11843.579744@2]- aml_cdev_close() is called.
:/ # 

效果如下图:
在这里插入图片描述

六、源码下载

需要尝试的同学,可从下面地址获取

  • 地址:https://gitee.com/amizhou/amlogic_t972_android9_driver/tree/master/index_01_simpleSay_drivers/lesson_01
git clone git@gitee.com:amizhou/amlogic_t972_android9_driver.git

七、篇尾

保持持续学习, 欢迎私信交流。

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

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

相关文章

android 皮肤包换肤之Resources加载(一)

Android 换肤之资源(Resources)加载(一) 本系列计划3篇: Android 换肤之资源(Resources)加载(一) — 本篇setContentView() / LayoutInflater源码分析(二)换肤框架搭建(三) 看完本篇你可以学会什么? Resources在什么时候被解析并加载的 Application#ResourcesActivity#Reso…

【Python黑帽子】——搭建TCP端口扫描器

作者名&#xff1a;Demo不是emo 主页面链接&#xff1a;主页传送门 创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座…

计算1到n的和(不用循环且逐步限制条件)

目录 一、题目简单描述 二、递归实现 1、if…else… 2、三目运算符 &#xff1f;&#xff1a; 3、逻辑与操作符 && 三、公式实现 四、C调用构造函数累加法 注&#xff1a;满足题目要求的解法有递归实现的第三种、公式实现、C调用构造函数累加法三种方法、 一、题目简…

死锁的成因以及解决方案

&#x1f388;专栏链接:多线程相关知识详解 目录 一.什么是死锁以及死锁的成因 Ⅰ.一个线程一把锁 Ⅱ.两个线程两把锁 Ⅲ.多个线程多把锁 二.死锁的解决方案 一.什么是死锁以及死锁的成因 死锁是一个线程加上锁了之后,解不开了 在多线程编程中&#xff0c;我们为了防止多…

【微服务】3、NACOS 的使用

&#x1f516; Eureka 可以做注册中心【https://github.com/Netflix/eureka】 &#x1f516; 但它的功能比较少&#xff0c;仅仅注册中心 &#x1f516; nacos 也可做注册中心&#xff0c;且功能更加丰富【https://nacos.io/】 一、了解 Nacos ✏️ Nacos 是阿里巴巴的产品&am…

【Python】PyQt拖动控件对齐到网格

实现如下需求&#xff1a; 在PyQt界面上有一个控件&#xff0c;实现其可任意拖动&#xff0c;且鼠标释放时自动对齐到网格。 目录1.控件任意拖动并对齐到网格2.进阶&#xff1a;双击控件使其移动到其他网格1.控件任意拖动并对齐到网格 如下按钮(尺寸100100)&#xff0c;可任意…

【K3s】第11篇 解决“1 Preemption is not helpful for scheduling”问题

目录 1、遇到问题 2、问题解决 1、遇到问题 sudo kubectl get pods -A sudo kubectl describe pods coredns-b96499967-q5lzw -n kube-system Events: Type Reason Age From Message ---- ------ ---- ---- …

YXC | ADAS自动驾驶四大模块选用晶振有何要求

近几年无人驾驶汽车&#xff08;ADAS&#xff09;热度非常高&#xff0c;不少汽企巨头纷纷入局&#xff0c;那么无人驾驶汽车需具备什么硬件设备呢&#xff1f; 自动驾驶汽车依靠人工智能&#xff08;AI&#xff09;、视觉计算、监控系统模块、雷达测距系统模块、和GPS全球定位…

SpringBoot 这两个配置文件有什么区别?

本文讲解了关于 SpringBoot 自动装配的两个配置文件spring.factories 和 spring-autoconfigure-metadata.properties有什么区别&#xff1f;点击上方“后端开发技术”&#xff0c;选择“设为星标” &#xff0c;优质资源及时送达读过上一片文章你可能会发现&#xff0c;在自动装…

第二个脚本——自动登录学习通

目录 本篇主要内容&#xff1a; 详细步骤&#xff1a; 第一步&#xff1a;对登入页面进行分析 第二步&#xff1a;模拟点击&#xff0c;表单填写和多边框操作原理介绍 模拟点击 表单填写 操作多选框: 第三步&#xff0c;实现自动登录 完整代码&#xff1a; 本篇主要内…

日百万流量网站励志一生被K

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 曾经每天小百万IP的网站、Z-blog流量最大的网址之一&#xff0c;励志一生这两周被百度K了&#xff0c;流量瞬间没有了&#xff0c;联盟广告收入估计日落千丈。这个网站有多牛可能很多人不清楚&#…

Linux内存管理:NUMA技术详解(非一致内存访问架构)

一.背景 所谓物理内存&#xff0c;就是安装在机器上的&#xff0c;实打实的内存设备&#xff08;不包括硬件cache&#xff09;&#xff0c;被CPU通过总线访问。在多核系统中&#xff0c;如果物理内存对所有CPU来说没有区别&#xff0c;每个CPU访问内存的方式也一样&#xff0c…

基于springboot+jpa 实现多租户动态切换多数据源 - 使用Flyway实现多数据源数据库脚本管理和迭代更新

多租户动态多数据源系列 1、基于springbootjpa 实现多租户动态切换多数据源 - 数据隔离方案选择分库还是分表 2、基于springbootjpa 实现多租户动态切换多数据源 - 基于dynamic-datasource实现多租户动态切换数据源 3、基于springbootjpa 实现多租户动态切换多数据源 - 使用Fl…

Kafka Producer Acks机制

Kafka Producer Acks 设置ACK props.put("acks", "all");通过上述代码&#xff0c;配置kafka生产者发送消息后&#xff0c;是否等待Broker的回执信息。在集群环境下&#xff0c;该配置是kafka保证数据不丢的重要的参数之一&#xff0c;今天来学习一下&…

深入理解Elasticsearch分片

了解分片的基本原理&#xff0c;对Elasticsearch性能调优有帮助。 关系梳理 ES底层使用的是Lucene库&#xff0c;ES的分片&#xff08;shard &#xff09;是Lucene的索引&#xff0c;ES的索引是分片的集合&#xff0c;Lucene的索引是由多个段&#xff08;segment&#xff09;…

青岛OJ如何导入题库详细图示

打开你的后台管理 找到问题位置 增加题目是可以编辑题目&#xff0c;导入数据。 导入导出是用题目和数据直接导入的。 这个ID的话就是题目ID不能设置一样的 然后题目输入输出就都不说了 按照格式就可以了&#xff0c;这里说一下Tag是标签&#xff0c;每次都要设置&#xff…

shell-条件测试

1、编写一个 Shell脚本&#xff0c;程序执行时从键盘读入一个目录名&#xff0c;如果用户输入的目录不存在&#xff0c;则提示file does not exist&#xff1b;如果用户输入的不是目录则提示用户必须输入目录名&#xff1b;如果用户输入的是目录则显示这个目录下所有文件的信息…

小程序版 Three.js 框架下载及目录配置

1.库文件说明 由于微信官方提供的threejs适配库已经很久没有更新&#xff0c;而且开发者普遍反映使用起来很难用。 我这里分享的是独立的库文件&#xff0c;不需要npm安装&#xff0c;下载后将库文件放到项目中即可使用。 2.下载后的压缩包文件 3.解压后的文件夹结构 4.文件…

Vue2和Vue3的双向数据绑定原理

目录前言&#xff1a;vue2.x 是如何实现响应式系统的&#xff1a;defineProperty 的痛点&#xff1a;Object.defineProperty 代码的使用Proxy 方法的理解Proxy 代码的使用&#xff1a;总结&#xff1a;前言&#xff1a; 今天小编给大家讲解一下&#xff0c;Vue2和Vue3的双向数据…

CAPL学习之路-测试功能集函数(诊断测试)

TestCollectDiagEcuInformation 向诊断目标的诊断类下的所有诊断服务发送诊断请求,并将诊断响应写入测试报告中 testcase TCExample() {int status;status = TestCollectDiagEcuInformation( "Door", "Sessions");if( status == 0)TestStepPass( "EC…