Android驱动笔记(1)——LED驱动

一、Android源自Linux

 Android驱动实际就是linux驱动和封装,内核通过驱动与底层硬件“交互”并为framework层提供统一接口。linux中诸如进程管理、内存管理、中断管理、虚拟文件系统(vfs)、网络管理等内容的都是差别不大的。但在驱动构成上存在下面的差异。

驱动构成的差异:
Linux:内核 + 文件系统 + rootfs
Android:内核 + 文件系统 + rootfs + android文件系统挂载到system

 怎么理解这个“android文件系统挂载到system”?需要了解Android系统源代码目录结构:

abi *:应用程序二进制接口
bionic *:基础的库的源代码
bootloader/legacy *:启动引导相关代码
build *:存放编译系统的.mk文件
development *:程序开发需要的模板和工具
device *:设备相关代码
framworks *:核心框架——java和C++语言,是Android应用程序的框架
hardware *:主要是硬件适配层HAL代码
out *:编译完成全部img文件
packages *:Android的各种系统级应用程序
system *:Android根文件系统相关源码

 而基本上一般的Linux文件系统没有system这个目录。

二、一个LED驱动

 指示灯在Android设备上主要作用就是显示手机状态,高通平台通常情况下默认的RGB接口连接指示灯,个别用户会选择外接的LED芯片控制灯效。Android的LED驱动和Linux下的LED驱动没有太大区别。Android的LED驱动基本位于Kernel目录下。详细的led驱动可以参考drivers/leds/目录下的代码。

  1. 通常在dts中搜索指示灯关键字red可以找到平台默认相关配置。
  2. 对应的_deconfig文件中打开相应的配置宏 CONFIG_LEDS_QTI_TRI_LED –高通平台默认参考驱动模式不同选择的宏不同,根据平台以及硬件连接项目需要配置,无特殊情况选择默认配置即可。
  3. 确认对应的makefile文件Drivers/leds/makefile中有CONFIG_LEDS_QTI_TRI_LED宏配置
  4. 检查驱动代码 根据配置的宏查到对应的源文件,查看驱动代码Driver/leds/leds-qti-tri-led.c
  5. 检查dts配置 :arch/arm64/boot/dts/qcom/***.dtsi。dts相关配置参数说明可以查看: Documentation/devicetree/bingdings/leds/leds-qti-tri-led.txt.

2.0、LED的硬件原理

 LED的原理图非常简单,一般采用平台默认的GPIO引脚,通过高低电平控制LED亮灭,同时可以调节占空比来调节LED电流。

2.1、如何编写一个驱动?

 按照内核为某个设备提供的框架去编写,驱动编写分为四步:为结构体分配空间、设置结构体、硬件的相关操作、注册。为了给应用程序提供统一的接口,驱动采用分层的思想,其中核心层是内核为设备提供的框架,除此以外还有设备驱动层。

参考代码:/drivers/leds/led-class.c

 实际操作依旧按照:为结构体分配空间、设置结构体、硬件的相关操作、注册来执行。

2.2、驱动源码

# Makefile
KERNELDIR  :=/home/linux/fspad_733/lichee/linux-3.4
test:
	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -C $(KERNELDIR) M=$(shell  pwd) modules
clean:
	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- -C $(KERNELDIR) M=$(shell  pwd) clean
obj-m += fspad_leds.o
/*own led*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/leds.h>
#include <asm/io.h>
#define FSPAD733_GPFCON 0x01C208B4
#define FSPAD733_GPFDAT 0x01C208C4

unsigned int *gpfcon;
unsigned int *gpfdat;

struct led_classdev  *led_dev; //led_dev结构体声明

//step3:实现结构体中定义的操作(设置LED初始值)
void fspad_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness){
	if(brightness == LED_OFF) {
		writel(readl(gpfdat)|(0x1 << 2),gpfdat); //led off
	}else{
		writel(readl(gpfdat)&~(0x1 << 2),gpfdat); //led on
	}
}

//step2:驱动初始化函数
int fspad_leds_init(void){
	int  ret;
	//1、为结构体分配空间,kzalloc分配空间,初始化结构体成员为0,必须要手动释放空间
	if(NULL == (led_dev  = kzalloc(sizeof(struct led_classdev),GFP_KERNEL)))
		return  -ENOMEM;

	//2、设置结构体
	led_dev->name  = "led0";
	led_dev->flags = LED_CORE_SUSPENDRESUME;
	led_dev->brightness_set = fspad_brightness_set;

	//3、硬件相关的操作
	gpfcon = ioremap(FSPAD733_GPFCON,0x4); //将物理地址映射到一个虚拟地址上
	gpfdat = ioremap(FSPAD733_GPFDAT,0x4);
	writel((readl(gpfcon)&~(0xf << 8))|(0x1 << 8),gpfcon);
    //led init:gpfcon的8~11位先清零,再在第8位写1
	writel(readl(gpfdat)&~(0x1 << 2),gpfdat);
    //led on

	//4、注册
	ret = led_classdev_register(NULL, led_dev);
	if(ret < 0)
		return  ret;
	return  0;
}

//step4:驱动注销
void  fspad_leds_exit(void)
{
	led_classdev_unregister(led_dev);
	iounmap(gpfcon);
	iounmap(gpfdat);
	kfree(led_dev);
}

//step1:初始化模块三要素
module_init(fspad_leds_init);
module_exit(fspad_leds_exit);
MODULE_LICENSE("GPL");

2.3、驱动编译和执行

 在编译uboot和内核的过程中,采用的是外部传参的方式进行编译,Makefile的写法见上文。另外需要执行:

#sudo vi /etc/bash.bashrc
export PATH=$PATH:/home/linux/lichee/out/sun8iw5p1/android/common/buildroot/external-toolchain/bin

 指定输出目录并使其立即生效:

# source /etc/bash.bashrc

 insmod own_leds.ko将led驱动加载进内核,会在/sys/class/leds/led0/下生成led节点,进而可对led进行控制。

2.4、LED上层简介

1.framwork 层: LightsService.java 提供java调用jni层方法。一般作为service启动。
2、jni 层: com_android_server_LightsService.cpp
3 hardware层: Lights.c 通过节点控制led状态。

2.5、调试方法

1.通过节点来调整led状态,配合万用表测量对应pin脚输出状态。
2 常用节点,设备注册成功后 会在/sys/class/leds/目录下生成对应的red blue green对应的目录。

# cd sys/class/leds/red/
# ls
blink brightness device fusion led_time max_brightness power subsystem trigger uevent
# echo 255 > brightness --打开点亮led
# echo 0 > brightness --关闭led
原文地址:https://www.cnblogs.com/hansenn/p/12714181.html