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

简介: 本文是关于在Amlogic安卓9.0平台上创建字符设备驱动的教程,详细介绍了驱动程序的编写、编译、部署和测试过程,并提供了完整的源码和应用层调用示例。

一、前言

  • 本文提取书籍中,较为经典的例子。
  • 让驱动的学习,变得更加简单。
  • 目前市面上,很难找到一本讲得特别好的嵌入式驱动开发教程,尤其是适配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.
:/ #

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

六、源码下载

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

git clone git@gitee.com:amizhou/amlogic_t972_android9_driver.git

七、篇尾

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

相关文章
|
17天前
|
Android开发
基于Amlogic 安卓9.0, 驱动简说(五):基于GPIO、LED子系统的LED驱动
这篇文章是关于如何在基于Amlogic T972的Android 9.0系统上,使用GPIO和LED子系统来实现LED驱动的教程,包括了DTS设备树配置、驱动源码编写以及如何在用户空间控制LED的亮度和开关。
21 0
基于Amlogic 安卓9.0, 驱动简说(五):基于GPIO、LED子系统的LED驱动
|
17天前
|
Android开发
基于Amlogic 安卓9.0, 驱动简说(四):Platform平台驱动,驱动与设备的分离
本文介绍了如何在基于Amlogic T972的Android 9.0系统上使用Platform平台驱动框架和设备树(DTS),实现设备与驱动的分离,并通过静态枚举在设备树中描述设备,自动触发驱动程序的加载和设备创建。
7 0
基于Amlogic 安卓9.0, 驱动简说(四):Platform平台驱动,驱动与设备的分离
|
17天前
|
Android开发
基于Amlogic 安卓9.0, 驱动简说(三):使用misc框架,让驱动更简单
如何使用Amlogic T972安卓9.0系统上的misc框架来简化驱动程序开发,通过misc框架自动分配设备号并创建设备文件,从而减少代码量并避免设备号冲突。
21 0
基于Amlogic 安卓9.0, 驱动简说(三):使用misc框架,让驱动更简单
|
17天前
|
Android开发 C语言
基于Amlogic 安卓9.0, 驱动简说(二):字符设备驱动,自动创建设备
这篇文章是关于如何在基于Amlogic T972的Android 9.0系统上,通过自动分配设备号和自动创建设备节点文件的方式,开发字符设备驱动程序的教程。
21 0
基于Amlogic 安卓9.0, 驱动简说(二):字符设备驱动,自动创建设备
|
17天前
|
传感器 Android开发 芯片
不写一行代码(三):实现安卓基于i2c bus的Slaver设备驱动
本文是系列文章的第三篇,展示了如何在Android系统中利用现有的i2c bus驱动,通过编写设备树节点和应用层的控制代码,实现对基于i2c bus的Slaver设备(如六轴陀螺仪模块QMI8658C)的控制,而无需编写设备驱动代码。
26 0
不写一行代码(三):实现安卓基于i2c bus的Slaver设备驱动
|
17天前
|
Android开发
不写一行代码(二):实现安卓基于PWM的LED设备驱动
本文介绍了在Android系统中不编写任何代码,通过设备树配置和内核支持的通用PWM LED驱动来实现基于PWM的LED设备驱动,并通过测试命令调整LED亮度级别。
25 0
不写一行代码(二):实现安卓基于PWM的LED设备驱动
|
17天前
|
Linux Android开发 C语言
不写一行代码(一):实现安卓基于GPIO的LED设备驱动
本文通过实践操作,展示了在Android系统中不编写任何代码,利用设备树(DTS)配置和内核支持的通用GPIO LED驱动来控制LED设备,并进一步通过C语言编写NDK测试APP来实现LED的闪烁效果。
31 0
不写一行代码(一):实现安卓基于GPIO的LED设备驱动
|
3天前
|
IDE 开发工具 Android开发
安卓与iOS开发对比:平台选择对项目成功的影响
【9月更文挑战第10天】在移动应用开发的世界中,选择正确的平台是至关重要的。本文将深入探讨安卓和iOS这两大主要移动操作系统的开发环境,通过比较它们的市场份额、开发工具、编程语言和用户群体等方面,为开发者提供一个清晰的指南。我们将分析这两个平台的优势和劣势,并讨论如何根据项目需求和目标受众来做出最佳选择。无论你是初学者还是有经验的开发者,这篇文章都将帮助你更好地理解每个平台的特性,并指导你做出明智的决策。
|
1天前
|
API Android开发 iOS开发
安卓与iOS开发中的线程管理对比
【9月更文挑战第12天】在移动应用的世界中,安卓和iOS平台各自拥有庞大的用户群体。开发者们在这两个平台上构建应用时,线程管理是他们必须面对的关键挑战之一。本文将深入探讨两大平台在线程管理方面的异同,通过直观的代码示例,揭示它们各自的设计理念和实现方式,帮助读者更好地理解如何在安卓与iOS开发中高效地处理多线程任务。