工作笔记-code
1.
android系统启动完成会发送Intent.ACTION_BOOT_COMPLETED事件,我们在 base/services/java/com/android/server/WiredAccessoryObserver.java中可以看到类似代码
linux-3.0/drivers/switch/ switch_headset.c中会根据无耳机,三段耳机,四段耳机和四段耳机是否有hook键按下4个状态更新state的值为0 ,1, 2,3,并且切换机台MIC和耳机
private static final String uEventInfo[][] = { {"DEVPATH=/devices/virtual/switch/h2w",
"/sys/class/switch/h2w/state",
"/sys/class/switch/h2w/name"},
{"DEVPATH=/devices/virtual/switch/usb_audio",
"/sys/class/switch/usb_audio/state",
"/sys/class/switch/usb_audio/name"},
{"DEVPATH=/devices/virtual/switch/hdmi",
"/sys/class/switch/hdmi/state",
"/sys/class/switch/hdmi/name"} };
大体流程是用定时器每200ms检查一次是否有耳机插入,如果有4段耳机,延时30ms检查hook key是否按下,这样,如果旧的state和新的state不相等,就用uevent上报状态改变
事件
可参考电路图P11的说明:
检测耳机插入:
1、0V-0.2V 则判定为3节耳机;
2、1V-2.5V 则判定为4节耳机;
3、检测为4接耳机后如果ADC再次检测为0V则认为HOOK见按下。
2.
在drivers/media/pa中的是提供借口给上层做外音和内音切换的,也就是如FM,播放器等都会检测耳机是否插入,以进行外音和内音切换,看看其中的代码:
pa_dev_class = class_create(THIS_MODULE, "pa_cls");//这一句运行后,会创建虚拟文件系统/sys/class/pa_cls
device_create(pa_dev_class, NULL,dev_num, NULL, "pa_dev");//这一句运行后,会创建节点/dev/pa_dev
printk("[pa_drv] init end!!!\n");
他对应的framework层代码在device/softwinner/common/hardware/audio/audio_hw.c中
而file_operations结构体和节点对应起来则是通过如下的关系,如touch中:
ret= register_chrdev(I2C_MAJOR,"aw_i2c_ts",&aw_i2c_ts_fops );//I2C_MAJOR是自己定义的主设备号
接着调用:device_create(i2c_dev_class, &client->adapter->dev, MKDEV(I2C_MAJOR,client->adapter->nr), NULL, "aw_i2c_ts%d", client->adapter->nr);这样
就关联起来了
3.
两种定时器的使用:
3.1.
static struct hrtimer vibe_timer;
INIT_WORK(&vibrator_work, update_vibrator);
static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer)
{
schedule_work(&vibrator_work);
}
hrtimer_init(&vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
vibe_timer.function = vibrator_timer_func;
这样在你想要启动定时器的函数中调用如下语句:
hrtimer_start(&vibe_timer,
ktime_set(value / 1000, (value % 1000) * 1000000), //ktime_set的第一个参数单位时秒,第二个单位是纳秒
HRTIMER_MODE_REL);
schedule_work(&vibrator_work); //hrtimer_start后调用一次schedule_work,时间到后,再调用一次schedule_work,这样在两次schedule_work中可以做如控制按键灯
亮灭等动作,具体例子可参考drivers/misc/sun4i-vibrator.c
这个定时器还提供了一些查询功能,如可以查询定时器当前是否激活的,定时器当前的剩余时间等,如下面的函数:
static int vibrator_get_time(struct timed_output_dev *dev)
{
struct timespec time_tmp;
if (hrtimer_active(&vibe_timer)) {//判断是否激活
ktime_t r = hrtimer_get_remaining(&vibe_timer);//取得定时器剩余时间
time_tmp = ktime_to_timespec(r);//时间单位转换,他和上面的ktime_set相当于相反过程的转换
//return r.tv.sec * 1000 + r.tv.nsec/1000000;
return time_tmp.tv_sec* 1000 + time_tmp.tv_nsec/1000000;//返回的单位是毫秒
} else
return 0;
}
3.2.
struct timer_list timer;
static void earphone_hook_handle(unsigned long data)
{
mod_timer(&switch_data->timer, jiffies + msecs_to_jiffies(200));
}
init_timer(&timer);
timer.function = &earphone_hook_handle;
timer.data = (unsigned long)switch_data;
add_timer(&switch_data->timer);//这一句之后已经启动定时器了
4.
声卡的设备节点在/proc/asound/card0,创建过程为snd_card_create -> snd_ctl_create
创建sys节点snd_card_register -> device_create(sound_class, card->dev, MKDEV(0, 0), card, "card%i", card->number)sound_class = class_create(THIS_MODULE, "sound");
sound_class->devnode = sound_devnode;
其中:
static char *sound_devnode(struct device *dev, mode_t *mode)
{
if (MAJOR(dev->devt) == SOUND_MAJOR)
return NULL;
return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));
}
这样声卡的class出现在/class/sys/sound/中,sound_devnode就决定了设备节点出现在/dev/snd/中,用户空间操作的就是/dev/snd/中的设备节点。
接着调用snd_device_register_all函数注册前面挂接在card->device链表中的所有设备:
185 int snd_device_register_all(struct snd_card *card)
186 {
187 struct snd_device *dev;
188 int err;
189
190 if (snd_BUG_ON(!card))
191 return -ENXIO;
192 list_for_each_entry(dev, &card->devices, list) {
193 if (dev->state == SNDRV_DEV_BUILD && dev->ops->dev_register) {
194 if ((err = dev->ops->dev_register(dev)) < 0)
195 return err;
196 dev->state = SNDRV_DEV_REGISTERED;
197 }
198 }
199 return 0;
200 }
我们看看设备是如何挂在card->device链表中的:
snd_card_sun4i_codec_pcm -> snd_pcm_new -> snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)),其中ops结构体为:
720 static struct snd_device_ops ops = {
721 .dev_free = snd_pcm_dev_free,
722 .dev_register = snd_pcm_dev_register,
723 .dev_disconnect = snd_pcm_dev_disconnect,
724 };
snd_pcm_dev_register为:
976 static int snd_pcm_dev_register(struct snd_device *device)
977 {
978 int cidx, err;
979 struct snd_pcm_substream *substream;
980 struct snd_pcm_notify *notify;
981 char str[16];
982 struct snd_pcm *pcm;
983 struct device *dev;
984
985 if (snd_BUG_ON(!device || !device->device_data))
986 return -ENXIO;
987 pcm = device->device_data;
988 mutex_lock(®ister_mutex);
989 err = snd_pcm_add(pcm);
990 if (err) {
991 mutex_unlock(®ister_mutex);
992 return err;
993 }
994 for (cidx = 0; cidx < 2; cidx++) {
995 int devtype = -1;
996 if (pcm->streams[cidx].substream == NULL)
997 continue;
998 switch (cidx) {
999 case SNDRV_PCM_STREAM_PLAYBACK:
1000 sprintf(str, "pcmC%iD%ip", pcm->card->number, pcm->device);//这里就是设备节点的名字
1001 devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
1002 break;
1003 case SNDRV_PCM_STREAM_CAPTURE:
1004 sprintf(str, "pcmC%iD%ic", pcm->card->number, pcm->device);//这里就是设备节点的名字
1005 devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
1006 break;
1007 }
1008 /* device pointer to use, pcm->dev takes precedence if
1009 * it is assigned, otherwise fall back to card's device
1010 * if possible */
1011 dev = pcm->dev;
1012 if (!dev)
1013 dev = snd_card_get_device_link(pcm->card);
1014 /* register pcm */
1015 err = snd_register_device_for_dev(devtype, pcm->card,
1016 pcm->device,
1017 &snd_pcm_f_ops[cidx],
1018 pcm, str, dev);//在这个函数中,最终调用device_create(sound_class, device, MKDEV(major, minor),private_data,
//"%s", name);来创建设备
1019 if (err < 0) {
1020 list_del(&pcm->list);
1021 mutex_unlock(®ister_mutex);
1022 return err;
1023 }
1024 snd_add_device_sysfs_file(devtype, pcm->card, pcm->device,
1025 &pcm_attrs);
看看我的设备节点名称:
$ cd /dev/snd
$ ls -l
crw-rw----+ 1 root audio 116, 8 2011-02-23 21:38 controlC0
crw-rw----+ 1 root audio 116, 4 2011-02-23 21:38 midiC0D0
crw-rw----+ 1 root audio 116, 7 2011-02-23 21:39 pcmC0D0c
crw-rw----+ 1 root audio 116, 6 2011-02-23 21:56 pcmC0D0p
crw-rw----+ 1 root audio 116, 5 2011-02-23 21:38 pcmC0D1p
crw-rw----+ 1 root audio 116, 3 2011-02-23 21:38 seq
crw-rw----+ 1 root audio 116, 2 2011-02-23 21:38 timer
controlC0 --> 用于声卡的控制,例如通道选择,混音,麦克风的控制等
midiC0D0 --> 用于播放midi音频
pcmC0D0c --> 用于录音的pcm设备
pcmC0D0p --> 用于播放的pcm设备
seq --> 音序器
timer --> 定时器
其中,C0D0代表的是声卡0中的设备0,pcmC0D0c最后一个c代表capture,pcmC0D0p最后一个p代表playback,这些都是alsa-driver中的命名规则。从上面的列表可以看出,我
的声卡下挂了6个设备,根据声卡的实际能力,驱动实际上可以挂上更多种类的设备,在include/sound/core.h中,定义了以下设备类型,通常,我们更关心的是pcm和control
这两种设备。
对于sound/core/control.c文件
#ifdef CONFIG_COMPAT
#include "control_compat.c"
#else
#define snd_ctl_ioctl_compat NULL
#endif
下面的"controlC%i"声卡对应的控制节点fops的compat_ioctl,当没有定义CONFIG_COMPAT时,将被置为NULL
hal层的IOCTRL会对应KERNEL层的core/control.c文件
frameworks/base/services/java/com/android/server/WiredAccessoryObserver.java会监听耳机拔插事件
而打开hal层audio_hw.c的JNI层代码在frameworks/base/services/audioflinger/AudioFlinger.cpp中
audio_hw.c是按照hardware/libhardware/include/hardware/audio.h定义的接口实现就行了
5.
getprop 命令可以查看系统属性状态
6.
运行JAVA代码:
javac xxx.java(xxx为类的名字)java xxx
7.
编译时候提示,You have tried to change the API from what has been previously approved.
To make these errors go away, you have two choices:
1) You can add "@hide" javadoc comments to the methods, etc. listed in the
errors above.
2) You can update current.txt by executing the following command:
make update-api
To submit the revised current.txt to the main Android repository,
you will need approval.
******************************
如果你是想要让这个新增的API只能内部使用,则加上 /** {@hide} */,如我的代码:frameworks/base/core/java/android/os中新增了IHelloService.aidl这个服务,如下
为其中的内容:
1 package android.os;
2
3 /** {@hide} */
4 interface IHelloService {
5 void setVal(int val, String path);
6 int getVal(String path);
7 }
还有新增JNI层的一些用法:
66 {"init_native", "()Z", (void*)hello_init}, //无型参,Z表示返回值为boolean型
67 {"setVal_native", "(ILjava/lang/String;)V", (void*)hello_setVal},// I表示第一个型参为int,Ljava/lang/String表示第二个型参为String,
68 {"getVal_native", "(Ljava/lang/String;)I", (void*)hello_getVal},
由上面可以知道hello_setVal对应的HAL层原型为hello_setVal(int, char *), 在JNI中还要将String转换为char*才能使用,语法如下:
28 /*通过硬件抽象层定义的硬件访问接口读取硬件寄存器val的值*/
29 static jint hello_getVal(JNIEnv* env, jobject clazz, jstring path) {
30 const char *extraInfoStr = env->GetStringUTFChars(path, NULL);//将String转换为char*
31 int val = 0;
32 if(!hello_device) {
33 LOGI("Hello JNI: device is not open.");
34 return val;
35 }
36 hello_device->get_val(hello_device, &val, extraInfoStr);
37
38 LOGI("Hello JNI: get value %d from device.", val);
39 env->ReleaseStringUTFChars(path, extraInfoStr);//用完后要释放
40
41 return val;
42 }
8.
系统build.prop文件属性的读取文件在base/core/java/android/os/SystemProperties.java,属性接口在frameworks/base/core/java/android/os/Build.java中
9.
A10申请外部中断的步骤:
中断号的定义在drivers/input/touchscreen/ctp_platform_ops.h中: #define PIO_BASE_ADDRESS (0xf1c20800)//这是外部所有IO口中断的入口
#define PIOA_CFG1_REG (PIO_BASE_ADDRESS+0x4)
#define PIOA_DATA (PIO_BASE_ADDRESS+0x10)
#define DELAY_PERIOD (5)
#define SW_INT_IRQNO_PIO 28
#define PIO_INT_STAT_OFFSET (0x214)
#define CTP_IRQ_NO (IRQ_EINT21)
309 static int ctp_judge_int_occur(void)
310 {
311 //int reg_val[3];
312 int reg_val;
313 int ret = -1;
314
315 reg_val = readl(gpio_addr + PIO_INT_STAT_OFFSET);//偏移PIO_INT_STAT_OFFSET处的地址就是外部IO口中断的状态寄存器
316 if(reg_val&(1<<(CTP_IRQ_NO))){//CTP_IRQ_NO=IRQ_EINT21,就是TP的中断脚EINT21,配置脚本中有 ctp_int_port = port:PH21<6><default>
317 ret = 0;
318 }
319 return ret;
320 }
1681 static irqreturn_t ft5x_ts_interrupt(int irq, void *dev_id)
1682 {
1683 struct ft5x_ts_data *ft5x_ts = dev_id;
1684
1685 // print_int_info("==========------ft5x_ts TS Interrupt-----============\n");
1686 if(!ctp_ops.judge_int_occur()){ //要判断是外部哪一个IO口所引起的中断
1687 // print_int_info("==IRQ_EINT21=\n");
1688 ctp_ops.clear_penirq();
1689 if (!work_pending(&ft5x_ts->pen_event_work))
1690 {
1691 // print_int_info("Enter work\n");
1692 queue_work(ft5x_ts->ts_workqueue, &ft5x_ts->pen_event_work);
1693 }
1694 }else{
1695 // print_int_info("Other Interrupt\n");
1696 return IRQ_NONE;
1697 }
1698
1699 return IRQ_HANDLED;
1700 }
1895 err = request_irq(SW_INT_IRQNO_PIO, ft5x_ts_interrupt, IRQF_TRIGGER_FALLING | IRQF_SHARED, "ft5x_ts", ft5x_ts);
10.
格式化userdata分区名字的方法在init.sun4i.rc中修改即可,如代码:format_userdata /dev/block/nandi IPND5,IPND5即为电脑中看到的盘符名字
11.
全志平台的关机代码在drivers/power/axp_power/axp-mfd.c中:
311 /* PM hookup */
312 if(!pm_power_off)
313 pm_power_off = axp_power_off;axp_power_off定义如下:
static void axp_power_off(void)
{
uint8_t val;
#if defined (CONFIG_AW_AXP18)
axp_set_bits(&axp->dev, POWER18_ONOFF, 0x80);
#endif
#if defined (CONFIG_AW_AXP19)
axp_set_bits(&axp->dev, POWER19_OFF_CTL, 0x80);
#endif
#if defined (CONFIG_AW_AXP20)
if(pmu_pwroff_vol >= 2600 && pmu_pwroff_vol <= 3300){
if (pmu_pwroff_vol > 3200){
val = 0x7;
}
else if (pmu_pwroff_vol > 3100){
val = 0x6;
}
else if (pmu_pwroff_vol > 3000){
val = 0x5;
}
else if (pmu_pwroff_vol > 2900){
val = 0x4;
}
else if (pmu_pwroff_vol > 2800){
val = 0x3;
}
else if (pmu_pwroff_vol > 2700){
val = 0x2;
}
else if (pmu_pwroff_vol > 2600){
val = 0x1;
}
else
val = 0x0;
axp_update(&axp->dev, POWER20_VOFF_SET, val, 0x7);
}
val = 0xff;
if (!use_cou){
axp_read(&axp->dev, POWER20_COULOMB_CTL, &val);
val &= 0x3f;
axp_write(&axp->dev, POWER20_COULOMB_CTL, val);
val |= 0x80;
val &= 0xbf;
axp_write(&axp->dev, POWER20_COULOMB_CTL, val);
}
//led auto
axp_clr_bits(&axp->dev,0x32,0x38);
axp_clr_bits(&axp->dev,0xb9,0x80);
printk("[axp] send power-off command!\n");
mdelay(20);
if(power_start != 1){
axp_read(&axp->dev, POWER20_STATUS, &val);//读取是否处于充电状态,如果是,则reset,进入uboot充电
if(val & 0xF0){
axp_read(&axp->dev, POWER20_MODE_CHGSTATUS, &val);
if(val & 0x20){//判断电池在的话才进入充电
printk("[axp] set flag!\n");
axp_write(&axp->dev, POWER20_DATA_BUFFERC, 0x0f);
mdelay(20);
printk("[axp] reboot!\n");
arch_reset(0,NULL);
printk("[axp] warning!!! arch can't ,reboot, maybe some error happend!\n");
}
}
}
axp_write(&axp->dev, POWER20_DATA_BUFFERC, 0x00);
mdelay(20);
axp_set_bits(&axp->dev, POWER20_OFF_CTL, 0x80);//就是写1到寄存器32H,表示关闭除LDO1外的所有电源,请看AXP209手册P34
mdelay(20);
printk("[axp] warning!!! axp can't power-off, maybe some error happend!\n");
#endif
}
而arch_reset(0,NULL);的代码位于arch/arm/mach-sun4i/include/mach/system.h中,利用的是看门狗复位:
39 static inline void arch_reset(char mode, const char *cmd)
40 {
41 /* use watch-dog to reset system */
42 #define WATCH_DOG_CTRL_REG (SW_VA_TIMERC_IO_BASE + 0x0094)//SW_VA_TIMERC_IO_BASE是虚拟地址
43 *(volatile unsigned int *)WATCH_DOG_CTRL_REG = 0;
44 __delay(100000);
45 *(volatile unsigned int *)WATCH_DOG_CTRL_REG = 3;
46 while(1);
47 }
12.
codec控制设置:
Number of controls: 32
ctl type num name value 注释
0 INT 1 Master Playback Volume 59 //音量大小设置
1 BOOL 1 Playback PAMUTE SWITCH On //声音输出总开关,打开才有声音输出,看手册P258 PAMUTE
2 BOOL 1 Playback MIXPAS Off //输入的声音或者经过混音器输出的声音输出的开关,如3G的语音就是要打开,看手册
//P258 MIXPAS
3 BOOL 1 Playback DACPAS On //输入的声音不经混音器直接输出,如播放音乐时要打开,看手册P258 DACPAS
//但也可以选择声音从混音器输出,关掉3,使能2、5、6、7和15即可
4 INT 1 Mic Output Mix 0 //MIC1、2输入的左右声道是否打开到混音器中一起输出到外音
5 BOOL 1 Ldac Right Mixer Off //输入的声音经过混音器混音时(对应手册DACMISX开关),左声道混音,右没声音
6 BOOL 1 Rdac Right Mixer Off //输入的声音经过混音器混音时(对应手册DACMISX开关),右声道混音,左没声音
7 BOOL 1 Ldac Left Mixer Off //输入的声音经过混音器混音时(对应手册DACMISX开关),左声道、左声道的混音
8 BOOL 1 FmR Switch Off //FM到混音器是否打开,如收音时要打开,但这样上层无法控制音量
9 BOOL 1 FmL Switch Off //FM到混音器是否打开,如收音时要打开,但这样上层无法控制音量
10 BOOL 1 LineR Switch Off //线路输入到混音器是否打开,如3G通话要打开,而播放音乐不是线路输入,不用打开
11 BOOL 1 LineL Switch Off //线路输入到混音器是否打开,如3G通话要打开,而播放音乐不是线路输入,不用打开
12 INT 1 MIC output volume 3 //MIC到混音器输出的增益,
13 INT 1 Fm output Volume 3 //FM到混音器输出的增益,
14 BOOL 1 Line output Volume On //线路输入到混音器的增益,只有两个等级,-1.5db和0db
15 BOOL 1 MIX Enable Off //混音器使能
16 BOOL 1 DACALEN Enable On //数字到模拟的输出是否是能,如音乐要打开,但3G通话是直接旁路输出不用打开
17 BOOL 1 DACAREN Enable On //数字到模拟的输出是否是能,如音乐要打开,但3G通话是直接旁路输出不用打开
18 BOOL 1 PA Enable On //PA使能,只有PA打开,才有声音输出
19 BOOL 1 dither enable Off //手册没有说明
20 BOOL 1 Mic1outn Enable Off //MIC是否直接输出,如3G通话MIC直接输出到3G模块中
21 INT 1 LINEIN APM Volume 7 //线路模拟输入的增益,有7个增益等级,只是线路的增益,不会影响去他输入源的增益
22 BOOL 1 Line-in-r function define Off //输入信号差分是能,如3G通话时候要使能,否则2G卡会受到干扰,有噪音
23 INT 1 ADC Input source 0 //模拟到数字转换的输入源选择,可以有3G线路,FM输入,MIC输入等选择
24 INT 1 Capture Volume 3 //模拟到数字转换的增益,有8个等级,0-7(这是总的线路增益,影响到FM,MIC,
//线路等输入源)
25 INT 1 Mic2 gain Volume 2 //MIC2增益打开的情况下,输入增益的等级设置,没有用到这一路
26 INT 1 Mic1 gain Volume 2 //MIC2增益打开的情况下,输入增益的设置,有4个等级,3G通话时候有增益的话会有噪音
27 BOOL 1 VMic enable On //MIC电源,用MIC时候就要打开
28 BOOL 1 Mic2 amplifier enable Off //MIC2输入增益是否打开
29 BOOL 1 Mic1 amplifier enable On //MIC2输入增益是否打开
30 BOOL 1 ADCL enable Off //模拟到数字的左声道是否打开,如录音时候有耳麦或则没有耳麦都就要打开
31 BOOL 1 ADCR enable Off //模拟到数字的左声道是否打开,如录音时候有耳麦或则没有耳麦都就要打开
13.
camera的设置:
drivers/media/video/sun4i_csi目录下对应有csi0 、csi1两个文件夹,分别是两个摄像头对应的平台代码,他们对应的节点为/dev/video0前置ov5640, /dev/video1后置ov2655或者ov2643,sun4i_drv_csi.c中控制camera的主要是csi_power_en脚和csi_stby脚,其中两个camera的csi_power_en相同,在csi_open函数中会打开电源上电,
但打开后马上用 v4l2_subdev_call(dev->sd,core, s_power, CSI_SUBDEV_STBY_ON);来进入standby 模式,只有在摄像头切换中,启用它后调用
static int internal_s_input(struct csi_dev *dev, unsigned int i)函数,在该函数中使用v4l2_subdev_call(dev->sd,core, s_power, CSI_SUBDEV_STBY_OFF);
才真正工作,所以两个摄像头同时只有一个工作,另一个处于standby模式
14.
property_get/property_set会从以下文件读取属性:
1: /default.prop2: /system/build.prop
3: /system/default.prop
4: /data/local.prop
15.
android的电量警告和严重电量警告的设置在/frameworks/base/core/res/res/values/config.xml中,在frameworks/base/services/java/com/android/server/
BatteryService.java中读取这些值,而关机电量是在BatteryService.java文件中判断,在update函数中被调用,如下: 208 private final void shutdownIfNoPower()
{
209 // shut down gracefully if our battery is critically low and we are not powered.
210 // wait until the system has booted before attempting to display the shutdown dialog.
211 if (mBatteryLevel == 0 && !isPowered() && ActivityManagerNative.isSystemReady()) {//mBatteryLevel为0就关机
212 Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
213 intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
214 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
215 mContext.startActivity(intent);
216 }
217 }
16.
APK签名机制:
<uses-sdk android:minSdkVersion="7" android:sharedUserId="android.uid.system"/>java -jar signapk.jar platform.x509.pem platform.pk8 old.apk new.apk
17.
按键事件的上报,如果要上报某一个键值,要用set_bit来设置,否则不上报该键值,如:
for (i = 0; i < KEY_MAX_CNT; i++)
set_bit(sun4i_scankeycodes[i], sun4ikbd_dev->keybit);
18.
camera拍照的各种声音播放在frameworks/base/core/java/android/hardware/CameraSound.java中实现
19.
JNI_OnLoad函数是在android VM执行*so中的System.loadLibrary函数时候执行的,所以一般在该函数中做一些初始化设置和返回JNI版本,
20.
u-boot流程:
头文件在include/configs/sun4i.h中从arch/arm/cpu/armv7/start.S开始
跳到arch/arm/lib/board.c,
21.
android源代码中编译jar包的方法:
在需要导出jar包的目录下新建android,内容如下: LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_MODULE_TAGS := optional LOCAL_MODULE :=my_fmradio
include $(BUILD_JAVA_LIBRARY)
22.
android背光调节代码路线:
packages/apps/Settings/src/com/android/settings/BrightnessPreference.java -> setBrightness(myState.progress + mScreenBrightnessDim);
private void setBrightness(int brightness)
{
try {
IPowerManager power = IPowerManager.Stub.asInterface(
ServiceManager.getService("power"));
if (power != null) {
power.setBacklightBrightness(brightness);
}
} catch (RemoteException doe) {
}
}
调用IPowerManager类接口,实现在frameworks/base/services/java/com/android/server/PowerManagerService.java
brightness = Math.max(brightness, mScreenBrightnessDim);
mLcdLight.setBrightness(brightness);
mKeyboardLight.setBrightness(mKeyboardVisible ? brightness : 0);
mButtonLight.setBrightness(brightness);
可以看到,不但是设置了LCD的背光,如果有,键盘,按键的背光设置了,其中的private LightsService.Light mLcdLight;所以看到目录下的LightsService.java
public void setBrightness(int brightness, int brightnessMode) -> setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode) ->
setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);
看看JNI层的实现,进入frameworks/base/services/jni/com_android_server_LightsService.cpp
这里看看JNI是如何找到HAL层的 hw_module_t结构体的:
hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);//这里传进去的是二维指针,才能返回HAL层的指针哦,关于这个不多说
看看hw_get_module的定义,在hardware/libhardware/hardware.c中:
hw_get_module -> hw_get_module_by_class:
for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {
if (i < HAL_VARIANT_KEYS_COUNT) {
if (property_get(variant_keys[i], prop, NULL) == 0) {
continue;
}
snprintf(path, sizeof(path), "%s/%s.%s.so",
HAL_LIBRARY_PATH2, name, prop);
if (access(path, R_OK) == 0) break;
snprintf(path, sizeof(path), "%s/%s.%s.so",
HAL_LIBRARY_PATH1,name,prop);
if (access(path, R_OK) == 0) break;
} else {
snprintf(path, sizeof(path), "%s/%s.default.so",
HAL_LIBRARY_PATH1, name);
if (access(path, R_OK) == 0) break;
}
}
查找并加载hal层的so库,最后load(class_id, path, module);用dlopen动态加载so库,这里会比较打开的so库中id的名字和传进来的是否一样,如果都是"lights",才能加
载成功。
看看so库的实现,进入HAL层,在device/softwinner/crane-common/hardware/libhardware/lights/lights.c中:
这里打开的是dev->fd = open("/dev/disp", O_RDONLY);
他在kernel中对应的文件为:drivers/video/sun4i/disp/dev_disp.c
上面是手动调节背光的流程,如果是设备有自动感光设备(light-sensor),那么调节代码也是在PowerManagerService.java中开始,如下:
mSensorManager.registerListener(mLightListener, mLightSensor,
LIGHT_SENSOR_RATE);
LIGHT_SENSOR_RATE为监听时间间隔,而mLightListener定义为:
3133 SensorEventListener mLightListener = new SensorEventListener() {
3134 public void onSensorChanged(SensorEvent event) {
3135 synchronized (mLocks) {
3136 // ignore light sensor while screen is turning off
3137 if (isScreenTurningOffLocked()) {
3138 return;
3139 }
3140
3141 int value = (int)event.values[0];
3142 long milliseconds = SystemClock.elapsedRealtime();
3143 if (mDebugLightSensor) {
................................................
3159 mHandler.removeCallbacks(mAutoBrightnessTask);
3160 mLightSensorPendingDecrease = (value < mLightSensorValue);
3161 mLightSensorPendingIncrease = (value > mLightSensorValue);
3162 if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) {
3163 mLightSensorPendingValue = value;
3164 mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY);
..................................................
}
我们可以看到,如果光感有数据变化,会调用onSensorChanged方法,value = (int)event.values[0];读取光感数值,然后到3164行
mHandler.postDelayed(mAutoBrightnessTask, LIGHT_SENSOR_DELAY);去调节背光亮度,这里的mAutoBrightnessTask定义:
2469 private Runnable mAutoBrightnessTask = new Runnable() {
2470 public void run() {
2471 synchronized (mLocks) {
2472 if (mLightSensorPendingDecrease || mLightSensorPendingIncrease) {
2473 int value = (int)mLightSensorPendingValue;
2474 mLightSensorPendingDecrease = false;
2475 mLightSensorPendingIncrease = false;
2476 lightSensorChangedLocked(value);
2477 }
2478 }
2479 }
2480 };
最后是在2476中进行背光设置,在PowerManagerService.java中,我们也看到距离感(主要应用是放耳边听电话时候自动关闭背光,防止乱触摸现象)应也是在这个文件中读取
的:
SensorEventListener mProximityListener = new SensorEventListener() {
3090 public void onSensorChanged(SensorEvent event) {
................................
}
23.
编译,分析uboot:
./build.sh -p sun4i_crane或者 make sun4i
board_init_r 从汇编跳到C入口,入口在文件arch/arm/lib/board.c中
启动用到的三个设置环境命令:
include/configs/sun4i.h中:
bootcmd=run setargs boot_normal;
board/allwinner/a10-evb/a10-evb.c -> check_android_misc()中
setenv("bootcmd", "run setargs boot_recovery");
setenv("bootcmd", "run setargs boot_fastboot");
他们分别为:
boot_normal=nand read 50000000 boot; boota 50000000//nand对应cmd_nand命令,会查找boot分区,把它读到RAM地址0x50000000中
boot_recovery=nand read 50000000 recovery; boota 50000000//nand对应cmd_nand命令,会查找recovery分区,把它读到RAM地址0x50000000中
boot_fastboot=fastboot//启动cmd_fastboot命令,等待USB发送fastboot命令到来
24.
编译kernel
./build.sh -p sun4i_crane25.
binder通讯机制的理解:
25.1.
rocessState::ProcessState()
: mDriverFD(open_driver())
, mVMStart(MAP_FAILED)
, mManagesContexts(false)
, mBinderContextCheckFunc(NULL)
, mBinderContextUserData(NULL)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
这里打开的可以是service的fd,也是和servicemanager通讯的FD,和进程相关,一个进程映射一个FD就可以了,service要把自己添加到servicemanager中,就是打开该
fd,但也传进了另外一个参数mHandle(0),表示自己是要和servicemanager通讯,并且把自己的binder实体也传进去,以便挂在servicemanager端,然后service也是利用该FD
等待和client的通讯,整个系统一共有20多处打开FD的LOG。而service等待client数据并且处理数据的地方是下面这两句:
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
就是启动线程等待client的请求
25. 2.
每个进程都有自己的defaultServiceManager(),如frameworks/base/media/mediaserver/main_mediaserver.cpp这个编译出来的执行文件就是一个单独的进程:
int main(int argc, char** argv)
{
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm = defaultServiceManager();
LOGI("ServiceManager: %p", sm.get());
AudioFlinger::instantiate();
MediaPlayerService::instantiate();
CameraService::instantiate();
AudioPolicyService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
其他的进程会有自己独立的defaultServiceManager(),这一点从加入的log打印出来有20多个就可以证明:
34 sp<IServiceManager> defaultServiceManager()
35 {
36 if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
37
38 {
39 AutoMutex _l(gDefaultServiceManagerLock);
40 if (gDefaultServiceManager == NULL) {
41 LOGI("luis:go to here");//打印出来有20多个
42 gDefaultServiceManager = interface_cast<IServiceManager>(
43 ProcessState::self()->getContextObject(NULL));
44 }
45 }
25.3.
service传输的数据要拷贝到内核空间的MMAP共享的空间,servicemaneger和client则通过指针就可以保存可操作MMAP空间这些数据,所以MMAP只有一次数据拷贝,一就是
binder进程的通讯只有一次数据拷贝4. client要从servicemanager获得service接口,也要打开一个FD才能与servicemanager通讯,它也调用IPCThreadState::talkWithDriver这个与binder驱动交互,但打开
的FD是这个进程打开defaultServiceManager时候,函数创建ProcessState对象时,在ProcessState构造函数通过open文件操作函数打开设备文件/dev/binder时设置好的FD,
而且传进来的handle值为0,表示目标Binder对象是ServiceManager,但它自己并不像service一样有binder传给servicemanager,而相反,要从servicemanager中取得一个
binder实体的handle
26.
init.sun4i.rc中format_userdata /dev/block/nandi IPND5格式化UMS分区,他对应的命令在system/core/init/builtins.c中:
int do_format_userdata(int argc, char **argv)
{
const char *devicePath = argv[1];
char bootsector[512];
char lable[32];
int fd;
int num;
pid_t child;
int status;
fd = open(devicePath, O_RDONLY);
if( fd <= 0 ) {
ERROR("open device error :%s", strerror(errno));
return 1;
}
memset(bootsector, 0, 512);
read(fd, bootsector, 512);
close(fd);
if( (bootsector[510]==0x55) && (bootsector[511]==0xaa) )
{
ERROR("dont need format %s", devicePath);
return 1;
}
else // 格式化
{
ERROR("start format %s", devicePath);
child = fork();
if (child == 0) {
ERROR("fork to format %s", devicePath);
execl("/system/bin/logwrapper","/system/bin/logwrapper","/system/bin/newfs_msdos","-F","32","-O","android","-c","8", "-L",argv[2],argv
[1], NULL);
exit(-1);
}
ERROR("wait for format %s", devicePath);
while (waitpid(-1, &status, 0) != child) ;
ERROR("format %s ok", devicePath);
return 1;
}
}
27. sensor的代码结构:
frameworks/base/services/sensorservice/SensorService.cpp中
void SensorService::onFirstRef()
67 {
68 LOGD("nuSensorService starting...");
69
70 SensorDevice& dev(SensorDevice::getInstance());//取得sensor列表,就是调用hw_get_module加载so库,
switch (list[i].type) {
87 case SENSOR_TYPE_ORIENTATION:
88 orientationIndex = i;
89 break;
90 case SENSOR_TYPE_GYROSCOPE:
91 hasGyro = true;
92 break;
93 case SENSOR_TYPE_GRAVITY:
94 case SENSOR_TYPE_LINEAR_ACCELERATION://根据type来判断是那种类型的sensor,所以我们在device下的so库中要注意该类型的赋值
95 case SENSOR_TYPE_ROTATION_VECTOR:
96 virtualSensorsNeeds &= ~(1<<list[i].type);
97 break;
28. 上层读取input事件时,一般是通过约定的名字来确定到底和/dev/input下的哪一个设备关联,如imapx15的gsensor,hardware/bosch_sensors/sensors.cpp中,
打开/dev/input目录后,遍历所有设备,根据约定的字符名字匹配:
539 if(ioctl(d06_read_fd, EVIOCGNAME(sizeof(name)),name) > 0)
540 {
541 ALOGD("devname=%s\n",devname);
542 ALOGD("name=%s\n",name);
543 if(!strcmp(name,INPUT_NAME_ACC))//"DMT_Gsensor"))
544 {
545 ALOGD("%s:name=%s,fd=%d\n",__func__,name,d06_read_fd);
546 break;
547 }
而在底层的driver中有:
291 /* Set InputDevice Name */
292 input->name = INPUT_NAME_ACC;
这样就能匹配成功啦
29.
infotmic的LCD配置:
路径在drivers/InfotmMedia/lcd_api/source中,新增自己的c配置文件,填写手册中的配置在结构体lcdc_config_t中在drivers/InfotmMedia/lcd_api/source/lcd_cfg.h中增加新增的文件
而在drivers/InfotmMedia/external/project/ids_drv_i800/ids.c中,会对item文件进行解析,以确认到底用那个LCD配置
28.
javah生成JNI文件,其他一切都是浮云
在eclipse中编译好apk后,我的package为 com.example.javajni,class为HellojniActivity,看看生成的bin目录:├── bin
│ ├── AndroidManifest.xml
│ ├── classes
│ │ ├── com
│ │ │ └── example
│ │ │ └── javajni
│ │ │ ├── BuildConfig.class
│ │ │ ├── HellojniActivity.class
│ │ │ ├── R$attr.class
│ │ │ ├── R.class
│ │ │ ├── R$drawable.class
│ │ │ ├── R$id.class
│ │ │ ├── R$layout.class
│ │ │ ├── R$menu.class
│ │ │ ├── R$string.class
│ │ │ └── R$style.class
│ │ └── com_example_javajni_HellojniActivity.h
我们要在bin/classs目录下运行javah命令,如下javah -classpath ./ -jni com.example.javajni.HellojniActivity,这样才能根据后面的参数找到com/example/
javajni目录下的HellojniActivity.class文件
29.
kernel的makefile中链接.a库的写法:
1 obj-$(CONFIG_TOUCHSCREEN_AW5306) += aw5306_ts.o2 aw5306_ts-objs := AW5306_ts.o AW5306_userpara.o $@libAW5306.a
libAW5306.a就是该目录下的一个库
30.
allwiner的touch I2C注册过程:
I2C的注册分静态和动态,静态的i2c_register_board_info就不多说了,先创建好i2c device,后续的i2c_driver寻找匹配的device,从该device就可以找到对应的adapter;
而动态的注册是怎样的呢,driver和device都在同一个文件里面注册,而且不管注册先后,都可以找到对方,过程如下:
定义好driver:
static struct i2c_driver pcf8563_driver = {
.driver = {
.name = "rtc-pcf8563",
},
.probe = pcf8563_probe,
.remove = pcf8563_remove,
.id_table = pcf8563_id,
};
用i2c_add_driver注册该driver;
接着创建一个device:
struct i2c_board_info info;
struct i2c_adapter *adapter;
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = 0x51;
strlcpy(info.type, I2C_DEVICE_NAME, I2C_NAME_SIZE);
adapter = i2c_get_adapter(1);
if (!adapter) {
printk("%s : can't get i2c adapter %d\n",__func__,1);
goto err_driver;
}
client = i2c_new_device(adapter, &info);
i2c_put_adapter(adapter);
if (!client) {
printk("%s : can't add i2c device at 0x%x\n",__func__,
(unsigned int)info.addr);
goto err_driver;
}
printk("%s i2c add success! \n",__func__);
return 0;
err_driver:
return -ENODEV;
上述关键在于I2C_DEVICE_NAME名字要和i2c_driver中pcf8563_id定义的一致,还有 i2c_get_adapter(1)是获得编号为1的adapter,也就是pcf8563设备挂载那条i2c总线上,adapter是板级的,和芯片有关,启动时候肯定都注册了的,所以可以获得到。
这样就可以一个文件中完成I2C代码,可以编译成ko加载。
31.
让机器永不休眠并且没有锁屏界面:
frameworks/base/packages/SettingsProvider/res/values/defaults.xml中修改def_screen_off_timeout为-1
frameworks/base/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java中mExternallyEnabled设置为false
32.ND6的以太网服务分析frameworks/base/目录下:
首先在ethernet/java/android/net/ethernet中定义了aidl文件:
EthernetDevInfo.aidl //定义Parcelable传输对象流
EthernetDevInfo.java
EthernetManager.java
EthernetMonitor.java
EthernetNative.java
EthernetStateTracker.java
IEthernetManager.aidl //定义服务接口函数interface IEthernetManager
看到IEthernetManager.aidl就知道有服务要实现这些接口函数,它在frameworks/base/services/java/com/android/server/EthernetService.java中:
public class EthernetService<syncronized> extends IEthernetManager.Stub{........}
而这个服务在services/java/com/android/server/ConnectivityService.java中注册添加:
521 ServiceManager.addService(Context.ETHERNET_SERVICE, ethService);
这样以后我们就可以用getService的方法来获得该服务了。
回来看EthernetManager.java中的函数,它定义了对外提供访问的共有函数:
95 public EthernetDevInfo getSavedConfig() {
96 try {
97 return mService.getSavedConfig();
98 } catch (RemoteException e) {
99 Slog.i(TAG, "Can not get eth config");
100 }
101 return null;
102 }
可以看到都是同过调用mService的方法,而mService正是我们上面的ETHERNET_SERVICE,它在那里获得呢?看文件:
core/java/android/app/ContextImpl.java
487 if (Items.ItemExist("eth.model") == 1) {
488 registerService(ETHERNET_SERVICE, new ServiceFetcher() {
489 public Object createService(ContextImpl ctx) {
490 IBinder b = ServiceManager.getService(ETHERNET_SERVICE);
491 if (b == null)
492 {
493 Log.w(TAG, "Error getting service name:" + ETHERNET_SERVICE);
494 }
495 IEthernetManager service = IEthernetManager.Stub.asInterface(b);
496 return new EthernetManager(service, ctx.mMainThread.getHandler());
497 }});
498 }
果然是通过ServiceManager.getService(ETHERNET_SERVICE)来获得的,而且注册了EthernetManager服务,这样我们的应用中用getSystemService方法即可获得一个
EthernetManager.java中的对象了,当然还要把EthernetManager.java中的函数等声明为公开,应用程序才能调用得到,api/current.txt中:
12765 public class EthernetManager {
12766 ctor public EthernetManager(android.net.ethernet.IEthernetManager, android.os.Handler);
12767 method public java.lang.String[] getDeviceNameList();
12768 method public android.net.ethernet.EthernetDevInfo getSavedConfig();
12769 method public int getState();
12770 method public int getTotalInterface();
12771 method public boolean isConfigured();
12772 method public void updateDevInfo(android.net.ethernet.EthernetDevInfo);
12773 field public static final int ETHERNET_DEVICE_SCAN_RESULT_READY = 0; // 0x0
12774 field public static final java.lang.String ETHERNET_STATE_CHANGED_ACTION = "android.net.ethernet.ETHERNET_STATE_CHANGED";
12775 field public static final int ETHERNET_STATE_DISABLED = 1; // 0x1
12776 field public static final int ETHERNET_STATE_ENABLED = 2; // 0x2
12777 field public static final int ETHERNET_STATE_UNKNOWN = 0; // 0x0
12778 field public static final java.lang.String EXTRA_ETHERNET_STATE = "ETHERNET_state";
12779 field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
12780 field public static final java.lang.String EXTRA_PREVIOUS_ETHERNET_STATE = "previous_ETHERNET_state";
12781 field public static final java.lang.String NETWORK_STATE_CHANGED_ACTION = "android.net.ethernet.STATE_CHANGE";
12782 field public static final java.lang.String TAG = "EthernetManager";
12783 }
这样,应用中getSystemService(Context.ETHERNET_SERVICE)即可获得服务
33.
frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java中power key的处理分析:
2994 case KeyEvent.KEYCODE_POWER: {
...........
3023 interceptPowerKeyDown(!isScreenOn || hungUp //处理按键按下去的动作
3024 || mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);
interceptPowerKeyDown函数如下:
605 private void interceptPowerKeyDown(boolean handled) {
606 mPowerKeyHandled = handled;
607 if (!handled) {
608 mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout());//表示长按多久后弹出关机确认对话框
609 }
610 }
接着:
3028 if (interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) {//处理按键抬起的动作
3029 result = (result & ~ACTION_POKE_USER_ACTIVITY) | ACTION_GO_TO_SLEEP;
3030 }
interceptPowerKeyUp函数如下:
612 private boolean interceptPowerKeyUp(boolean canceled) {
613 if (!mPowerKeyHandled) {
614 mHandler.removeCallbacks(mPowerLongPress);//如果还没有弹出关机确认对话框,取消掉它
615 return !canceled;
616 }
617 return false;
618 }
34.
camera打开device/infotm/imapx800/etc/media_profiles.xml配置的位置:
apps/Camera/src/com/android/camera/VideoCamera.java中mMediaRecorder.setProfile(mProfile); //mMediaRecorder为打开录像的录音功能其中mProfile为 mProfile = CamcorderProfile.get(mCameraId, quality);它是通过调用JNI -> HAL层来解析media_profiles.xml文件的
35.
infotmic factroy reset流程:
1. packages/apps/Settings/src/com/android/settings/MasterClearConfirm.java------>getActivity().sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
2. frameworks/base/core/res/AndroidManifest.xml------>
1748 <receiverandroid:name="com.android.server.MasterClearReceiver"//这里的名字即为activity的路径
1749 android:permission="android.permission.MASTER_CLEAR"
1750 android:priority="100" >
3. frameworks/base/services/java/com/android/server/MasterClearReceiver.java------->
RecoverySystem.rebootWipeUserData(context);
4. frameworks/base/core/java/android/os/RecoverySystem.java------->
bootCommand(context, "--wipe_data");
378 public static void bootCommand(Context context, String arg) throws IOException {
379 RECOVERY_DIR.mkdirs(); // In case we need it
380 COMMAND_FILE.delete(); // In case it's not writable
381 LOG_FILE.delete();
382
383 FileWriter command = new FileWriter(COMMAND_FILE);
384 try {
385 command.write(arg);
386 command.write("\n");
387 } finally {
388 command.close();
389 }
390
391 // Having written the command file, go ahead and reboot
392 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
393 pm.reboot("recovery");
394
395 throw new IOException("Reboot failed (no permissions?)");
396 }
其中383行COMMAND_FILE定义:
71 private static File RECOVERY_DIR = new File("/cache/recovery");
72 private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");
385行将字符串--wipe_data写到/chache/recovery分区,接着看393行。
5. frameworks/base/services/java/com/android/server/PowerManagerService.java------->
ShutdownThread.reboot(mContext, finalReason, false);
6. frameworks/base/services/java/com/android/server/pm/ShutdownThread.java------->
reboot() -> shutdownInner() -> beginShutdownSequence() -> sInstance.start() -> run() -> rebootOrShutdown() ->
PowerManagerService.lowLevelShutdown() -> 到PowerManagerService.java中 -> nativeShutdown()
7. JNI frameworks/base/services/jni/com_android_server_PowerManagerService.cpp ------>
android_reboot(ANDROID_RB_POWEROFF, 0, 0);
8. system/core/libcutils/android_reboot.c //adb等命令也是通过该函数来reboot的
__reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART2, arg);//系统调用,进入kernel
9. kernel/kernel/sys.c -------->
case LINUX_REBOOT_CMD_RESTART2:
if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {
ret = -EFAULT;
break;
}
buffer[sizeof(buffer) - 1] = '\0';
//kernel_restart(buffer);
imap_reset(!strncmp(buffer, "recover", 7));
10. arch/arm/mach-imapx800/cpu.c ------->
void imap_reset(int type)
{
imapfb_shutdown();
writel(type, IO_ADDRESS(SYSMGR_RTC_BASE + 0x3c));
printk(KERN_EMERG "sysreboot: %s\n", (type == 2)?
"charger": ((type == 1)? "recovery":
"normal")); //这里肯定是recovery了
writel(0x1, IO_ADDRESS(SYSMGR_RTC_BASE + 0x2c));
writel(0x1, IO_ADDRESS(SYSMGR_RTC_BASE + 0x44));
imap_set_retry_param_default();
writel(0x3, IO_ADDRESS(SYSMGR_RTC_BASE));
while(1);
}
如果是全志平台4.0.3代码,从第6点rebootOrShutdown函数开始有点不一样,他是调用Power.shutdown(),所以跑到了Power.java中,在这个文件中调用reboot->rebootNative
这样到了JNI的方法android_os_Power.cpp,全志在这个文件里做了一些自己的改动,后面就和盈方微平台的一样了,这可能也是andorid4.1.2和4.0.3的改动吧
而且在kernel中也有点不同,A10的路线如下:
kernel/sys.c中:
case LINUX_REBOOT_CMD_RESTART2:
if (strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1) < 0) {
ret = -EFAULT;
break;
}
buffer[sizeof(buffer) - 1] = '\0';
kernel_restart(buffer);
break;
kernel_restart:
void kernel_restart(char *cmd)
{
kernel_restart_prepare(cmd);
if (!cmd)
printk(KERN_EMERG "Restarting system.\n");
else
printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);
kmsg_dump(KMSG_DUMP_RESTART);
machine_restart(cmd);
}
EXPORT_SYMBOL_GPL(kernel_restart);
kernel_restart_prepare函数将cmd命令(比如 adb reboot recovery,则cmd = "recovery")写入MISC分区,关闭外设,同步文件等操作:
void kernel_restart_prepare(char *cmd)
{
blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);
system_state = SYSTEM_RESTART;
usermodehelper_disable();
device_shutdown();
syscore_shutdown();
}
由于在arch/arm/mach-sun4i/reboot.c中注册了reboot监听事件:
static struct notifier_block sun4i_reboot_notifier = {
.notifier_call = sun4i_reboot,
};
static int sun4i_reboot_init(void)
{
return register_reboot_notifier(&sun4i_reboot_notifier);
}
所以blocking_notifier_call_chain调用后发出通知,sun4i_reboot函数就会被调用,他将recovery字符串写入MISC分区。
然后machine_restart调用的是arch/arm/kernel/process.c中:
void machine_restart(char *cmd)
{
machine_shutdown();
arm_pm_restart(reboot_mode, cmd);
}
void (*arm_pm_restart)(char str, const char *cmd) = arm_machine_restart;
EXPORT_SYMBOL_GPL(arm_pm_restart);
最后调用arch/arm/mach-sun4i/include/mach/system.h平台的arch_reset函数:
static inline void arch_reset(char mode, const char *cmd)
{
/* use watch-dog to reset system */
#define WATCH_DOG_CTRL_REG (SW_VA_TIMERC_IO_BASE + 0x0094)
*(volatile unsigned int *)WATCH_DOG_CTRL_REG = 0;
__delay(100000);
*(volatile unsigned int *)WATCH_DOG_CTRL_REG = 3;
while(1);
}
36.
infotmic 4.1.2代码Wifi流程:
首先从系统设置中,打开WIFI界面时候,调用了WifiSettings.java的onActivityCreated方法,做一些初始化设置后调用:mWifiEnabler = new WifiEnabler(activity, actionBarSwitch);
packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java:
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
华东WIFI的switch按钮后,调用onCheckedChanged函数:
mWifiManager.setWifiEnabled(isChecked);
通过AIDL,调用frameworks/base/wifi/java/android/net/wifi/WifiManager.java的setWifiEnabled:
mService.setWifiEnabled(enabled);
最终调用的是frameworks/base/services/java/com/android/server/WifiService.java的setWifiEnabled:
mWifiStateMachine.setWifiEnabled(enable);
mWifiStateMachine类在frameworks/base/wifi/java/android/net/wifi/WifiStateMachine.java:
743 public void setWifiEnabled(boolean enable) {
744 mLastEnableUid.set(Binder.getCallingUid());
745 if (enable) {
746 /* Argument is the state that is entered prior to load */
747 sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_STATE_ENABLING, 0));
748 sendMessage(CMD_START_SUPPLICANT);
749 } else {
750 sendMessage(CMD_STOP_SUPPLICANT);
751 /* Argument is the state that is entered upon success */
752 sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_DISABLED, 0));
753 }
754 }
这是一个WIFI状态机的管理类,管理着WIFI的各种状态切换,每个状态都是一个类,类中有enter和
processMessage两个函数用来处理该状态的事件,先看他的构造函数中有:
setInitialState(mInitialState);
它表示第一次进入该类时候的初始状态,这里我们第一次进入,所以会调用该类中的enter:
1994 class InitialState extends State{
1995 @Override
1996 //TODO: could move logging into a common class
1997 public void enter() {
1998 if (DBG) log(getName() + "\n");
1999 // [31-8] Reserved for future use
2000 // [7 - 0] HSM state change
2001 // 50021 wifi_state_changed (custom|1|5)
2002 EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
2003
2004 if (mWifiNative.isDriverLoaded()) {
2005 transitionTo(mDriverLoadedState);
2006 }
2007 else {
2008 transitionTo(mDriverUnloadedState);
2009 }
................
}
这里进入2008行,状态切换到了mDriverUnloadedState类中,所以进入该类看看:
enter函数没做什么,只是打印LOG,processMessage中状态切换到mDriverLoadingState:
在enter中看看:
2044 new Thread(new Runnable() {
2045 public void run() {
2046 mWakeLock.acquire();
2047 //enabling state
2048 switch(message.arg1) {
2049 case WIFI_STATE_ENABLING:
2050 setWifiState(WIFI_STATE_ENABLING);
2051 break;
2052 case WIFI_AP_STATE_ENABLING:
2053 setWifiApState(WIFI_AP_STATE_ENABLING);
2054 break;
2055 }
2056
2057 if(mWifiNative.loadDriver()) {
2058 if (DBG) log("Driver load successful");
2059 sendMessage(CMD_LOAD_DRIVER_SUCCESS);
2060 } else {
这里switch中会获得message.arg1为前面setWifiEnabled设置的WIFI_STATE_ENABLING,setWifiState函数会广播出去,应用中会收到该广播,暂时不管应用怎么处理该广播,
然后2057行加载驱动:
进入JNI层core/jni/android_net_wifi_Wifi.cpp:
return (jboolean)(::wifi_load_driver() == 0);
进入hardware/libhardware_legacy/wifi/wifi.c中,这里就是加载wifi的ko模块的地方了
回到2057行并且成功后发送CMD_LOAD_DRIVER_SUCCESS消息,这样进入processMessage函数,看看CMD_LOAD_DRIVER_SUCCESS分支:
case CMD_LOAD_DRIVER_SUCCESS:
transitionTo(mDriverLoadedState);
所以进入mDriverLoadedState类中,enter没做什么事情,看看processMessage函数,发现没有CMD_LOAD_DRIVER_SUCCESS分支,所以状态就停留在mDriverLoadedState中。
然后回到setWifiEnabled函数,还有一句sendMessage(CMD_START_SUPPLICANT),所以进入前面的停留状态mDriverLoadedState中:
2123 case CMD_START_SUPPLICANT:
2124 try {
2125 mNwService.wifiFirmwareReload(mInterfaceName, "STA");
2126 } catch (Exception e) {
2127 loge("Failed to reload STA firmware " + e);
2128 // continue
2129 }
2130 try {
2131 //A runtime crash can leave the interface up and
2132 //this affects connectivity when supplicant starts up.
2133 //Ensure interface is down before a supplicant start.
2134 mNwService.setInterfaceDown(mInterfaceName);
2135 //Set privacy extensions
2136 mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
2137 } catch (RemoteException re) {
2138 loge("Unable to change interface settings: " + re);
2139 } catch (IllegalStateException ie) {
2140 loge("Unable to change interface settings: " + ie);
2141 }
2142
2143 if(mWifiNative.startSupplicant(mP2pSupported)) {
2144 if (DBG) log("Supplicant start successful");
2145 mWifiMonitor.startMonitoring();
2146 transitionTo(mSupplicantStartingState);
2147 } else {
2148 loge("Failed to start supplicant!");
2149 sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_STATE_UNKNOWN, 0));
2150 }
2151 break;
看看2143行,流程和前面加载ko差不多,最后调用wifi.c中wifi_start_supplicant:
property_set("ctl.start", supplicant_name);
这里supplicant_name为wpa_supplicant,属性“ ctrl.start ”和“ ctrl.stop ”是用来启动和停止服务,所有服务必须在init.rc中定义,所以这里启动了init.rc中的:
service wpa_supplicant /system/bin/wpa_supplicant -Dwext -iwlan0 -c /data/misc/wifi/wpa_supplicant.conf
2145行启动监听,在frameworks/base/wifi/java/android/net/wifi/WifiMonitor.java中:
350 public void startMonitoring() {
351 new MonitorThread().start();
352 }
353
354 class MonitorThread extends Thread {
355 public MonitorThread() {
356 super("WifiMonitor");
357 }
...........................
359 public void run() {
360
361 if (connectToSupplicant()) {
362 // Send a message indicating that it is now possible to send commands
363 // to the supplicant
364 mStateMachine.sendMessage(SUP_CONNECTION_EVENT);
365 } else {
366 mStateMachine.sendMessage(SUP_DISCONNECTION_EVENT);
367 return;
368 }
369
370 //noinspection InfiniteLoopStatement
371 for (;;) {
372 String eventStr = mWifiNative.waitForEvent();
可以看到,是开启了一个线程,用来监听底层的wpa_supplicant事件通知,这里启动成功的话执行364行,状态变为SUP_CONNECTION_EVENT
回到前面的mDriverLoadedState状态2146行,来到mSupplicantStartingState中:
37 .
allwinner音频控制流程:
hal层的so库文件在device/softwinner/common/hardware/audio中编译生成,该路径下的audio_hw.c对上主要实现了android hal层so库的标准接口供audiofliger调用,对下主要通过调用android标准的tinymix接口来控制底层驱动,从而实现音量控制,音频通路的切换等,tinymix驱动路径在external/tinyalsa中,它会编译生成tinyalsa可执行文件和
libtinyalsa.so库文件,其中可执行文件可以用来在终端命令行直接控制底层音频(命令格式和方法看这篇笔记的第12条),而so库供提供库函数和audio_hw.c一起编译,从而实现通过audio_hw.c调用。
38.
原子位操作
为了实现位操作,内核提供了一组可原子地修改和测试单个位的函数。
原子位操作非常快,只要底层硬件允许,这种操作就可以使用单个机器指令来执行,并且不需要禁止中断。这些函数依赖于具体的架构,因此在<asm/bitops.h>中声明。即使是在SMP计算机上,这些函数也可确保为原子的,因此,能提供跨处理器的一致性。
这些函数使用的数据类型也是依赖于具体架构的。nr参数(用来描述要操作的位)通常被定义为int,但在少数架构上被定义为unsigned long。要修改的地址通常是指向unsigned long指针,但在某些架构上却使用void *来代替。
可用的位操作如下:
复制代码
void set_bit(nr, void *addr); /*设置第 nr 位在 addr 指向的数据项中。*/
void clear_bit(nr, void *addr); /*清除指定位在 addr 处的无符号长型数据.*/
void change_bit(nr, void *addr);/*翻转nr位.*/
test_bit(nr, void *addr); /*这个函数是唯一一个不需要是原子的位操作; 它简单地返回这个位的当前值.*/
/*以下原子操作如同前面列出的, 除了它们还返回这个位以前的值.*/
int test_and_set_bit(nr, void *addr);
int test_and_clear_bit(nr, void *addr);
int test_and_change_bit(nr, void *addr);
39. android 4.4.2安全模式分析:
在services/java/com/android/server/wm/WindowManagerService.java:
public boolean detectSafeMode() {
if (!mInputMonitor.waitForInputDevicesReady(
INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS)) {
Slog.w(TAG, "Devices still not ready after waiting "
+ INPUT_DEVICES_READY_FOR_SAFE_MODE_DETECTION_TIMEOUT_MILLIS
+ " milliseconds before attempting to detect safe mode.");
}
int menuState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY,
KeyEvent.KEYCODE_MENU);
int sState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, KeyEvent.KEYCODE_S);
int dpadState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_DPAD,
KeyEvent.KEYCODE_DPAD_CENTER);
int trackballState = mInputManager.getScanCodeState(-1, InputDevice.SOURCE_TRACKBALL,
InputManagerService.BTN_MOUSE);
int volumeDownState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY,
KeyEvent.KEYCODE_VOLUME_DOWN);
mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0
|| volumeDownState > 0;
try {
if (SystemProperties.getInt(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, 0) != 0) {
mSafeMode = true;
SystemProperties.set(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, "");
}
} catch (IllegalArgumentException e) {
}
if (mSafeMode) {
Log.i(TAG, "SAFE MODE ENABLED (menu=" + menuState + " s=" + sState
+ " dpad=" + dpadState + " trackball=" + trackballState + ")");
} else {
Log.i(TAG, "SAFE MODE not enabled");
}
mPolicy.setSafeMode(mSafeMode);
return mSafeMode;
}
可以看到,启动时候只要按着menu键、enter键、鼠标或者Volume down键都可以进入安全模式。
该函数是services/java/com/android/server/SystemServer.java调用的:
// Before things start rolling, be sure we have decided whether
// we are in safe mode.
final boolean safeMode = wm.detectSafeMode();
if (safeMode) {
ActivityManagerService.self().enterSafeMode();
// Post the safe mode state in the Zygote class
Zygote.systemInSafeMode = true;
// Disable the JIT for the system_server process
VMRuntime.getRuntime().disableJitCompilation();
} else {
// Enable the JIT for the system_server process
VMRuntime.getRuntime().startJitCompilation();
}
所以要禁用安全模式,把safeMode设为false即可
更多推荐
所有评论(0)