第十五章_Android内核驱动——杂项
- 格式:pdf
- 大小:540.94 KB
- 文档页数:8
往android的内核添加驱动及ueventd.rc修改向android的内核添加驱动,其实就是向linux内核添加驱动。
主要在两个文件里添加点信息,一个是Kconfig文件,一个是Makefile 文件。
比如你添加的驱动你代码放到drivers目录下面的XXX目录,那么修在该目录下的Kconfig文件和Makefile文件。
具体的修改方法:比如我现在修改drivers/staging/android/下的Kconfig文件。
config ANDROID_LOGGERtristate "Android log driver"default nconfig ANDROID_RAM_CONSOLEbool "Android RAM buffer console"default nconfig ANDROID_RAM_CONSOLE_ENABLE_VERBOSEbool "Enable verbose console messages on Android RAM console"default ydepends on ANDROID_RAM_CONSOLE如果我想添加一个驱动,驱动名字为 somedrv,那么在这个文件里加入:config ANDROID_DEMUXERbool "Android Somedrv Driver"default n这样就可以了。
这样在编译内核的时候,make menuconfig命令之后,会有Android Somedrv Driver的选择,选择它就可以将驱动编进内核。
然后在Makefile里面添加一句obj-$(CONFIG_ANDROID_SOMEDRV) += somedrv.o。
这样就修改好了。
这样编译内核的时候就把驱动编译进去了,前提编译前选择驱动。
现在我编译的内核都是用在模拟器上的,也就是说在qemu上运行内核。
Android驱动学习-app调⽤内核驱动过程(驱动框架回顾)考研已经过去了,android驱动的学习也断了半年多了,现在重新捡起来学习,回顾⼀下Android驱动的⼤体框架。
Android系统的核⼼是java,其有⼀个David虚拟机。
Android-app操作硬件也相当于是java操作硬件。
在Linux系统上操作硬件是通过open read write等来实现,也就是操作C库。
如果java能直接调⽤C库中的函数,也就解决了app操作硬件的问题。
下⾯的⽂章是java调⽤C/C++库的⽅法。
链接:1.⽅法1——jni调⽤底层驱动在android框架中写⼊c/c++直接调⽤底层linux驱动,并向上提供jni接⼝给应⽤程序:优点:简单易⾏;缺点:主要在于驱动,由于在linux中需要遵循GPL协议,需要开源,⽽许多⼚商的⼀些代码不希望开源。
⽽且像屏幕等设备可能需要多个app同时操作,这样的话将会导致各种各样的问题。
2.⽅法2——增加硬件抽象层将驱动程序⼀分为⼆,⼀部分开源在内核中,⼀部分不开源在android框架中:⼆、举例led android驱动:从这⾥我们将看到整个应⽤框架层到底层驱动的⾛向。
⾸先,⽆论是哪种⽅法,我们都需要实现⼀个linux驱动以供上层访问led资源。
同样也是通过jni来加载C库,从⽽通过调⽤open等来实现对硬件的操作。
Android为了实现多个APP能操作同⼀个硬件,硬件不由app来直接操作,⽽是有SystemServer来操作。
app需要把请求通过serviceManager发给SystemServer,由SystemServer最终完成对硬件的操作。
这⾥我们反过来思考⼀个app操作硬件的过程:1、app需要申请服务和获得服务getservice。
⽽这个服务是有接⼝完成的。
所以第⼀步我们需要建⽴⼀个aidl⽂件,来⽣成接⼝类。
frameworks/base/core/java/android/os/ILedService.aidl同时修改 frameworks/base/Android.mk,加⼊新建的aidl⽂件。
嵌入式linux(贺丹丹等编著)课后习题答案第八章一、填空题。
1、ARM-Linux内核的配置系统由三个部分组成,它们分别是Makefile、配置文件和配置工具。
2、配置工具一般包括配置命令解释器和配置用户界面,前者主要作用是对配置脚本中使用的配置命令进行解释;而后者则是提供基于字符界面、基于Ncurses图形界面以及基于X Window图形界面的用户配置界面。
3、Makefile文件主要包含注释、编译目标定义和适配段。
4、Linux内核常用的配置命令有make oldconfig、make config、make menuconfig和make xconfig。
其中以字符界面配置的命令是make config。
5、内核编译结束后,会在“/arch/arm/boot/”目录下面和根目录下面生成一个名为zImage的内核镜像文件。
二、选择题C AD D B三、叙述题1、Linux内核各个部分与内核源码的各个目录都是对应起来的,比如有关驱动的内容,内核中就都组织到“drive”这个目录中去,有关网络的代码都集中组织到“net”中。
当然,这里有的目录是包含多个部分的内容。
具体各个目录的内容组成如下:arch:arch目录包括了所有和体系结构相关的核心代码。
include:include 目录包括编译核心所需要的大部分头文件,例如与平台无关的头文件在include/linux 子目录下;init:init 目录包含核心的初始化代码(不是系统的引导代码),有main.c 和Version.c 两个文件;mm:mm 目录包含了所有的内存管理代码。
与具体硬件体系结构相关的内存管理代码位于arch/*/mm 目录下;drivers:drivers 目录中是系统中所有的设备驱动程序。
它又进一步划分成几类设备驱动,每一种有对应的子目录,如声卡的驱动对应于drivers/sound;ipc:ipc 目录包含了核心进程间的通信代码;modules:modules 目录存放了已建好的、可动态加载的模块;fs:fs 目录存放Linux 支持的文件系统代码。
android驱动开发和移植详解本文出处:《Android 底层开发技术实战详解——内核、移植和驱动》我们开发的Android 驱动程序是基于Linux 内核的,本文首先介绍移植Android 系统的基本知识和基本原理,这也是为驱动开发打下坚实基础,知其然也知其所以然;最后我们将通过深入浅出的案例学习驱动开发过程。
1.1 驱动开发需要做的工作Android 作为当前最流行的手机操作系统之一,受到了广大开发人员和商家的青睐。
Android正在逐渐形成一个蓬勃发展的产业,带来了无限商机。
既然Android 这么火爆,我们程序员可以学习它的哪一方面的内容呢?本书的驱动开发又属于哪一领域呢?接下来将为读者奉上这两个问题的答案。
Android 是一个开放的系统,这个系统的体积非常庞大,开发人员无须掌握整个Android 体系中的开发知识,只需熟悉其中某一个部分即可收获自己的未来。
从具体功能上划分,Android 开发主要分为如下三个领域。
1. 移植开发移动电话系统移植开发的目的是构建硬件系统,并且移植Android 的软件系统,最终形成手机产品。
2. Android 应用程序开发应用程序开发的目的是开发出各种Android 应用程序,然后将这些应用程序投入Android 市场,进行交易。
Android 的应用程序开发是Android 开发的另一个方面。
从开发的角度来看,这种形式的开发可以基于某个硬件系统,在没有硬件系统的情况下也可以基于Linux 或者Windows 下的Android模拟器来开发。
这种类型的开发工作在Android 系统的上层。
事实上,在Android 软件系统中,第3 个层次(Java 框架)和第4 个层次(Java 应用)之间的接口也就是Android 的系统接口(系统API)。
这个层次是标准的接口,所有的Android 应用程序都是基于这个层次的接口开发出来的。
Android 系统的第4 个层次就是一组内置的Android应用程序。
Android repo 魔法Android 为企业提供一个新的市场,无论大企业,小企业都是处于同一个起跑线上。
研究Android 尤其是Android 系统核心或者是驱动的开发,首先需要做的就是本地克隆建立一套Android 版本库管理机制。
Android 使用Git 作为代码管理工具,开发了Gerrit 进行代码审核以便更好的对代码进行集中式管理,还开发了Repo 命令行工具,对Git 部分命令封装,将百多个Git 库有效的进行组织。
要想克隆和管理这百多个Git 库,还真不是一件简单的事情。
在研究Repo 的过程中,发现很多文档在Google Group 上,非“翻墙”不可看。
非法的事情咱不干,直接阅读repo 的代码吧。
创建本地Android 版本库镜像的思路如果了解了Repo 的实现,参考《Using Repo and Git》 , 建立一个本地的android 版本库镜像还是不难的:∙下载repo bootstrap 脚本∙提供–mirror 参数调用repo init ,建立git 版本库克隆o使用–morror 则下一步和源同步的时候,本地按照源的版本库组织方式进行组织,否则会按照manifest.xml 指定的方式重新组织并检出到本地∙开始和源同步∙修改manifest ,修改git 库地址,指向本地的git 服务器o修改platform/manifest.git 库中现有的xml 文件,或者创建一个新的xml 文件o将git 的地址改为本地地址,提交并push∙本地repo 镜像建立完毕之后,就可以在执行repo init 时,使用本地更改后的manifest 库,之后执行repo sync 就是基于本地版本库进行同步了。
∙也可以改造repo,使得不必为repo 工具初始化,也在本地网络完成操作…Repo init 干了些什么?实际上,得到客户使用repo 的信息后,首先下载repo 执行脚本开始研究。
Android驱动开发示例及系统编译1基础部分在Ubuntu上下载、编译和安装Android最新源代码看完了前面说的几本书之后,对Linux Kernel和Android有一定的认识了,是不是心里蠢蠢欲动,想小试牛刀自己编译一把Android源代码了呢?一直习惯使用Windows系统,而Android源代码是不支持在Windows上编译上,于是决定使用虚拟机安装Ubuntu,然后下载、编译和安装Android源代码。
一. 环境准备1. 磁盘空间预留20G左右,内存3G,因为一边要跑主机,一边要跑虚拟机,内存要求还是比较高的,这样才会比较流畅。
2. 安装VMWare 7.1.4。
我的操作系统是Win7,VMWare的版本要新一点的,旧版本的VMWare在网络支持上比较差,由于要在虚拟机上下载Android源代码,没有网络是万万不行的。
3. 安装好VMWare后,接下来就安装Ubuntu系统了。
我选择目前最新的版本ubuntu-11.04-alternate-i386,从网上查到的资料说,要编译Android源代码,Ubuntu的最低版本是8.04。
下载好后,安装时采用一直默认安装即可。
4. 安装Git工具。
Android源代码采用Git工具来管理,与SVN相比,这是一种分布式的源代码管理工具,而SVN是集中式的源代码管理工具。
要安装Git工具,在Ubuntu上执行以下命令即可:USER-NAME@MACHINE-NAME:~$ sudo apt-get install git-core gnupg5. 安装Java SDK。
在Ubuntu上执行以下命令:USER-NAME@MACHINE-NAME:~$ sudo add-apt-repository ppa:ferramroberto/javaUSER-NAME@MACHINE-NAME:~$ sudo apt-get updateUSER-NAME@MACHINE-NAME:~$ sudo apt-get install sun-java6-jre sun-java6-pluginUSER-NAME@MACHINE-NAME:~$ sudo apt-get install sun-java6-jdk6. 依赖的其它包。
前言意外在网上发现了这扁文章,看后感觉很有必要分享,所以整理并上传,希望大家喜欢。
Android 硬件抽象层(HAL)概要介绍和学习计划Android 的硬件抽象层,简单来说,就是对Linux 内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。
也就是说,把对硬件的支持分成了两层,一层放在用户空间(User Space),一层放在内核空间(Kernel Space),其中,硬件抽象层运行在用户空间,而Linux 内核驱动程序运行在内核空间。
为什么要这样安排呢?把硬件抽象层和内核驱动整合在一起放在内核空间不可行吗?从技术实现的角度来看,是可以的,然而从商业的角度来看,把对硬件的支持逻辑都放在内核空间,可能会损害厂家的利益。
我们知道,Linux 内核源代码版权遵循GNU License,而Android 源代码版权遵循Apache License,前者在发布产品时,必须公布源代码,而后者无须发布源代码。
如果把对硬件支持的所有代码都放在Linux 驱动层,那就意味着发布时要公开驱动程序的源代码,而公开源代码就意味着把硬件的相关参数和实现都公开了,在手机市场竞争激烈的今天,这对厂家来说,损害是非常大的。
因此,Android 才会想到把对硬件的支持分成硬件抽象层和内核驱动层,内核驱动层只提供简单的访问硬件逻辑,例如读写硬件寄存器的通道,至于从硬件中读到了什么值或者写了什么值到硬件中的逻辑,都放在硬件抽象层中去了,这样就可以把商业秘密隐藏起来了。
也正是由于这个分层的原因,Android 被踢出了Linux 内核主线代码树中。
大家想想,Android 放在内核空间的驱动程序对硬件的支持是不完整的,把Linux 内核移植到别的机器上去时,由于缺乏硬件抽象层的支持,硬件就完全不能用了,这也是为什么说Android 是开放系统而不是开源系统的原因。
撇开这些争论,学习Android 硬件抽象层,对理解整个Android 整个系统,都是极其有用的,因为它从下到上涉及到了Android 系统的硬件驱动层、硬件抽象层、运行时库和应用程序框架层等等,下面这个图阐述了硬件抽象层在Android 系统中的位置,以及它和其它层的关系:在学习Android 硬件抽象层的过程中,我们将会学习如何在内核空间编写硬件驱动程序、如何在硬件抽象层中添加接口支持访问硬件、如何在系统启动时提供硬件访问服务以及如何编写JNI 使得可以通过Java 接口来访问硬件,而作为中间的一个小插曲,我们还将学习一下如何在Android 系统中添加一个C可执行程序来访问硬件驱动程序。
第十五章Android内核驱动——杂项15.1 日志系统基本原理Android的logger是一种轻量级的日志系统。
在内核中实现为一种misc设备驱动,它与用户态的logcat工具配合实现了方便的调试工具,开发应用程序的时候可以利用logger查看日志,进行跟踪调试。
logger的实现logger的源代码在drivers/staging/android/logger.c中,它用三个结构体logger_log,logger_reader和logger_entry来维护logger设备的信息。
其中logger_log代表一个log设备,logger_reader代表一个读日志的reader,logger_entry代表writer写入的一条日志。
logger在模块初始化时注册三个misc设备:log_main,log_events和log_radio。
其中log_main 记录主要的日志信息,log_events记录与事件有关的信息,log_radio记录与通信有关的信息,实现了以下的file operation:●logger_open标准的open接口,如果以读模式打开,则分配一个logger_reader,初始化其成员变量,并把这个logger_reader保存在file->private_data中。
如果是write模式打开,则直接把对应logger 设备的logger_log保存在file->private_data。
此后在读或者写的时候就可以通过file-> private_data找到对应的logger_reader或logger_log。
●logger_read首先当前进程(读log的进程)加到logger_log->wq等待队列上,判断当前日志buffer是否为空,如果空则调度别的进程运行,自己挂起(如果指定了非阻塞模式,则直接返回-EAGAIN),重复上述过程直buffer中有日志可读,此时,读出一条日志,拷贝到用户空间,返回。
●logger_aio_write写操作支持同步、异步以及scatter方式的写操作。
写操作几乎总是成功的,当buffer满的时候,新写入的日志会覆盖最初的日志。
总之,buffer是环形的,如果没有及时被读出,数据会丢失。
●logger_ioctl支持以下命令:⏹LOGGER_GET_LOG_BUF_SIZE:得到logger device 环形缓冲区的大小⏹LOGGER_GET_LOG_LEN:得到当前日志buffer 中未被读出的日志长度LOGGER_GET_NEXT_ENTRY_LEN:得到下一条日志长度(即紧接着上次读出的日志后面一条)⏹LOGGER_FLUSH_LOG:清空日志●logger_poll查询当前进程是否可以对logger device操作。
POLLOUT总是成立的,即进程总是可以写入日志。
但只有以FMODE_READ模式打开logger设备的进程,并且当前日志非空,才可以读到日志。
用户接口logger日志系统是标准的misc设备,提供标准的file operation,应用程序可以通过标准的C 库文件函数操作日志系统。
在Android应用开发中,比较有用的是logcat命令,通过该命令可以查看系统的日志输出,在调试的时候,应用程序插入日志,在logcat中就可以看到,这样就实现了方便的插桩跟踪调试。
logcat 的选项包括:-s 设置过滤器,例如指定'*:s'-f <filename> 输出到文件,默认情况是标准输出。
-r [<kbytes>] Rotate log every kbytes. (16 if unspecified). Requires –f-n <count> Sets max number of rotated logs to <count>,default 4-v <format> 设置log 的打印格式,<format> 是下面的一种:brief process tag thread raw time threadtime long-c 清除所有log并退出-d 得到所有log 并退出(不阻塞)-g 得到环形缓冲区的大小并退出-b <buffer> 请求不同的环形缓冲区('main'(默认),'radio','events')-B 输出log 到二进制中过滤器的格式是一个这样的串:其中<tag>表示log的component,tag(或者使用* 表示所有),可以应用中定义,这样在用logcat查看日志的时候就可以指定这个tag,只查看这个tag的应用产生的日志。
priority如下所示:V VerboseD DebugI InfoW WarnE ErrorF FatalS Silent事实上logcat的功能是由Android的类android.util.Log决定的,在程序中log的使用方法如以上log的级别依次升高,DEBUG信息应当只存在于开发中,INFO,WARN,ERROR这三种log将出现在发布版本中。
15.2 Switch基本原理Switch是Android引进的新的驱动,目的是用于检测一些开关量,比如检测耳机插入、检测USB 设备插入等。
Switch在sysfs文件系统中创建相应entry,用户可以通过sysfs与之交互;此外还可以通过uevent机制与之交互,从而检测switch状态。
Switch的实现Switch class在Android中实现为一个module,可动态加载;而具体的switch gpio则是基于platform device框架。
代码在drivers\switch\switch_class.c和drivers\switch\switch_gpio.c中。
其中switch_class.c实现了一个switch class,而switch_gpio.c则是这个class中的一个device,即针对gpio的一个switch设备。
switch_class.c文件创建了一个switch_class,实现了内核的switch机制,提供支持函数供其他switch device驱动调用。
init函数调用create_switch_class->class_create创建switch_class设备类。
相对应exit则是销毁这个设备类。
该文件导出两个函数供其他switch设备驱动调用,分别是注册switch设备switch_dev_register 和注销switch_dev_unregister。
然后是两个sysfs操作函数(state_show和name_show),分别用于输出switch device的name 和state。
当用户读取sysfs中对应的switch entry(/sys/class/switch/<dev_name>/name和/sys/class/switch/<dev_name>/state)时候,系统会自动调用这两个函数向用户返回switch设备的名称和状态。
可见,这两个函数就是直接调用对应的switch_dev中的print_state和print_name函数;如果没有定义这两个函数,则调用sprintf把信息打印到buf缓冲区里。
最后是switch_set_state 函数,该函数是内核内部使用,并不为用户调用,它完成的功能主要是两件事:●调用name_show和state_show输出switch设备名称和状态至sysfs文件系统●发送uevent通知用户switch device的信息(名称和状态)switch_gpio.c文件基于switch class实现了一个gpio的switch设备驱动,其实现的原理如下:基于platform device/driver框架,在probe函数中完成初始化,包括获取gpio的使用权限,设置gpio方向为输入,注册switch_dev设备,为gpio分配中断,指定中断服务程序,初始化一个gpio_switch_work工作,最后读取gpio初始状态。
当GPIO 引脚状态发生变化时,则会触发中断,在中断服务程序中调用schedule_work,这个被schedule的work即前面初始化的gpio_switch_work,最后这个work被执行,在gpio_switch_work函数中读取当前gpio电平,调用switch_set_state更新sysfs并通过uevent 通知上层应用。
这个设备驱动只实现了print_state函数:switch_gpio_print_state,没有实现print_name函数。
当gpio_switch_work执行的时候,里面调用switch_set_state->switch_gpio_print_state输出GPIO状态到sysfs。
用户接口sysfs文件系统和uevent机制。
sysfs文件为sys/class/switch/<dev_name>/name,sys/class/switch/<dev_name>/state,uevent环境变量为SWITCH_NAME=<name>,SWITCH_STATE=<state>。
15.3 Timed GPIO基本原理Timed GPIO基于platform driver实现了一个增强的GPIO驱动。
与普通GPIO驱动不同的地方就是Timed GPIO将普通GPIO与内核定时器绑定在一起,实现了一种时钟控制的GPIO;当定时器过期后,GPIO的状态会设置为指定的状态。
Android的timed GPIO实际上实现的功能是:通过sysfs操作GPIO,比如可以让GPIO 输出高/低电平;但同时可以指定一个定时器过期时间。
到达过期时间后,执行一个callback 函数,可以重新设置GPIO的输出电平。
Timed GPIO的实现Timed GPIO代码在drivers/staging/android下,对应的文件为:timed_gpio.c,timed_gpio.h,timed_output.c,timed_output.h。
Timed GPIO驱动实现为platform driver,在其probe函数中,sysfs创建了相应的设备文件和class文件,应用程序可以通过设备文件实现与内核驱动的交互。