一、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/目錄下的代碼。
- 通常在dts中搜索指示燈關鍵字
red
可以找到平台默認相關配置。- 對應的_deconfig文件中打開相應的配置宏 CONFIG_LEDS_QTI_TRI_LED –高通平台默認參考驅動模式不同選擇的宏不同,根據平台以及硬件連接項目需要配置,無特殊情況選擇默認配置即可。
- 確認對應的makefile文件Drivers/leds/makefile中有CONFIG_LEDS_QTI_TRI_LED宏配置
- 檢查驅動代碼 根據配置的宏查到對應的源文件,查看驅動代碼Driver/leds/leds-qti-tri-led.c。
- 檢查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