生活随笔
收集整理的這篇文章主要介紹了
Android RTC 自下往上浅析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1.首先搞清楚RTC在kernel內的作用: linux系統有兩個時鐘:一個是由主板電池驅動的“Real Time Clock”也叫做RTC或者叫CMOS時鐘,硬件時鐘。當操作系統關機的時候,用這個來記錄時間,但是對于運行的系統是不用這個時間的。
另一個時間是 “System clock”也叫內核時鐘或者軟件時鐘,是由軟件根據時間中斷來進行計數的,內核時鐘在系統關機的情況下是不存在的,所以,當操作系統啟動的時候,內核時鐘是要讀取RTC時間來進行時間同步。并且在系統關機的時候將系統時間寫回RTC中進行同步。 如前所述,Linux內核與RTC進行互操作的時機只有兩個:
1) 內核在啟動時從RTC中讀取啟動時的時間與日期;
2) 內核在需要時將時間與日期回寫到RTC中。 系統啟動時,內核通過讀取RTC來初始化內核時鐘,又叫墻上時間,該時間放在xtime變量中。
[cpp]?view plaincopy
The?current?time?of?day?(the?wall?time)?is?defined?in?kernel/timer.c:?? struct?timespec?xtime;?? The?timespec?data?structure?is?defined?in?<linux/time.h>?as:?? struct?timespec?{?? time_t?tv_sec;????????????????? long?tv_nsec;?????????????????? };??
最有可能讀取RTC設置內核時鐘的位置應該在arch/arm/kernel/time.c里的time_init函數內.
time.c為系統的時鐘驅動部分.
time_init函數會在系統初始化時,由init/main.c里的start_kernel函數內調用. ARM架構的time_init代碼如下:
/* arch/arm/kernel/time.c */
[cpp]?view plaincopy
void?__init?time_init(void)?? {?? ????system_timer?=?machine_desc->timer;?? ????system_timer->init();?? #ifdef?CONFIG_HAVE_SCHED_CLOCK?? ????sched_clock_postinit();?? #endif?? }??
2.RTC結構部分
[cpp]?view plaincopy
static?const?struct?rtc_class_ops?hym8563_rtc_ops?=?{?? ????.read_time??=?hym8563_rtc_read_time,?? ????.set_time???=?hym8563_rtc_set_time,?? ????.read_alarm?=?hym8563_rtc_read_alarm,?? ????.set_alarm??=?hym8563_rtc_set_alarm,?? ????.ioctl??????=?hym8563_rtc_ioctl,?? ????.proc???????=?hym8563_rtc_proc?? };?? ?? static?int?__devinit?hym8563_probe(struct?i2c_client?*client,?const?struct?i2c_device_id?*id)?? {?? ????int?rc?=?0;?? ????u8?reg?=?0;?? ????struct?hym8563?*hym8563;?? ????struct?rtc_device?*rtc?=?NULL;?? ????struct?rtc_time?tm_read,?tm?=?{?? ????????.tm_wday?=?6,?? ????????.tm_year?=?111,?? ????????.tm_mon?=?0,?? ????????.tm_mday?=?1,?? ????????.tm_hour?=?12,?? ????????.tm_min?=?0,?? ????????.tm_sec?=?0,?? ????};???? ?????? ????if?(!i2c_check_functionality(client->adapter,?I2C_FUNC_I2C))?? ????????return?-ENODEV;?? ?????????? ????hym8563?=?kzalloc(sizeof(struct?hym8563),?GFP_KERNEL);?? ????if?(!hym8563)?{?? ????????return?-ENOMEM;?? ????}?? ????gClient?=?client;????? ????hym8563->client?=?client;?? ????mutex_init(&hym8563->mutex);?? ????wake_lock_init(&hym8563->wake_lock,?WAKE_LOCK_SUSPEND,?"rtc_hym8563");?? ????INIT_WORK(&hym8563->work,?hym8563_work_func);?? ????i2c_set_clientdata(client,?hym8563);?? ?? ????hym8563_init_device(client);?? ?? ?????? ????hym8563_i2c_read_regs(client,RTC_SEC,?,1);?? ????if?(reg&0x80)?{?? ????????dev_info(&client->dev,?"clock/calendar?information?is?no?longer?guaranteed\n");?? ????????hym8563_set_time(client,?&tm);?? ????}?? ?? ????hym8563_read_datetime(client,?&tm_read);?????? ?????? ????if(((tm_read.tm_year?<?70)?|?(tm_read.tm_year?>?137?))?|?(tm_read.tm_mon?==?-1)?|?(rtc_valid_tm(&tm_read)?!=?0))??? ????{?? ????????hym8563_set_time(client,?&tm);???? ????}????? ?????? ????if(gpio_request(client->irq,?"rtc?gpio"))?? ????{?? ????????dev_err(&client->dev,?"gpio?request?fail\n");?? ????????gpio_free(client->irq);?? ????????goto?exit;?? ????}?? ?????? ????hym8563->irq?=?gpio_to_irq(client->irq);?? ????gpio_pull_updown(client->irq,GPIOPullUp);?? ????if?(request_irq(hym8563->irq,?hym8563_wakeup_irq,?IRQF_TRIGGER_FALLING,?client->dev.driver->name,?hym8563)?<?0)?? ????{?? ????????printk("unable?to?request?rtc?irq\n");?? ????????goto?exit;?? ????}????? ????enable_irq_wake(hym8563->irq);?? ?? ????rtc?=?rtc_device_register(client->name,?&client->dev,?? ??????????????????&hym8563_rtc_ops,?THIS_MODULE);?? ????if?(IS_ERR(rtc))?{?? ????????rc?=?PTR_ERR(rtc);?? ????????rtc?=?NULL;?? ????????goto?exit;?? ????}?? ????hym8563->rtc?=?rtc;?? ?? ????return?0;?? ?? exit:?? ????if?(rtc)?? ????????rtc_device_unregister(rtc);?? ????if?(hym8563)?? ????????kfree(hym8563);?? ????return?rc;?? }??
看這兩個結構體,我認為就已經達到目的,第2個結構體是平臺設備中的driver部分,也就是hym8563_probe,是個很重要的函數,在這里面,第1個結構體被順利注冊進rtc子系統。Rtc的所用到的結構體被定義在,LINUX/include/linux/rtc.h里面。
struct rtc_device這個結構體是核心部分,內核中就是靠它傳遞信息,不管在哪使用,都要靠它間接的調用底層信息。比如在alarm.c 中。
alarm_ioctl這個函數中,多次使用了rtc_set_time/rtc_get_time,這些函數雖然是定義在rtc目錄下的interface.c 中,但實質還是rtc-hym8563.c中結構體 rtc_class_ops所指過去的函數。
也就是說在和內核層以上的交互是通過alarm-dev.c里面的alarm_ioctl及其余的函數交互,但是在這個文件里面的rtc_set_time/rtc_get_time操作是為了設置RTC時間等的操作是調用alarm.c里面的函數,但是alarm.c驅動本身和硬件沒有關系,在這里屏蔽了RTC的硬件操作,比如HYM8563的時間I2C硬件驅動操作在rtc-HYM8563.c驅動里,只需要使用?rtc_class_ops進行注冊就可以了,完整的實現了硬件對平臺無關性的屏蔽。
那么我可以告訴你了,為什么多了一個alarm.c ,因為在android中它為了使得平臺無關性提高,因此大量的增加過渡代碼層,HAL就是這種性質的存在。alarm.c在用戶空間中會多一個/dev/alarm 節點,而rtc-hym8563.c.c 會產生/dev/rtc這樣的節點。
3.JNI層
[cpp]?view plaincopy
namespace?android?{?? ?? static?jint?android_server_AlarmManagerService_setKernelTimezone(JNIEnv*?env,?jobject?obj,?jint?fd,?jint?minswest)?? {?? ????struct?timezone?tz;?? ?? ????tz.tz_minuteswest?=?minswest;?? ????tz.tz_dsttime?=?0;?? ?? ????int?result?=?settimeofday(NULL,?&tz);?? ????if?(result?<?0)?{?? ????????LOGE("Unable?to?set?kernel?timezone?to?%d:?%s\n",?minswest,?strerror(errno));?? ????????return?-1;?? ????}?else?{?? ????????LOGD("Kernel?timezone?updated?to?%d?minutes?west?of?GMT\n",?minswest);?? ????}?? ?? ????return?0;?? }?? ?? static?jint?android_server_AlarmManagerService_init(JNIEnv*?env,?jobject?obj)?? {?? ????return?open("/dev/alarm",?O_RDWR);?? }?? ?? static?void?android_server_AlarmManagerService_close(JNIEnv*?env,?jobject?obj,?jint?fd)?? {?? ????close(fd);?? }?? ?? static?void?android_server_AlarmManagerService_set(JNIEnv*?env,?jobject?obj,?jint?fd,?jint?type,?jlong?seconds,?jlong?nanoseconds)?? {?? ????struct?timespec?ts;?? ????ts.tv_sec?=?seconds;?? ????ts.tv_nsec?=?nanoseconds;?? ?? ????int?result?=?ioctl(fd,?ANDROID_ALARM_SET(type),?&ts);?? ????if?(result?<?0)?? ????{?? ????????LOGE("Unable?to?set?alarm?to?%lld.%09lld:?%s\n",?seconds,?nanoseconds,?strerror(errno));?? ????}?? }?? ?? static?jint?android_server_AlarmManagerService_waitForAlarm(JNIEnv*?env,?jobject?obj,?jint?fd)?? {?? ????int?result?=?0;?? ?? ????do?? ????{?? ????????result?=?ioctl(fd,?ANDROID_ALARM_WAIT);?? ????}?while?(result?<?0?&&?errno?==?EINTR);?? ?? ????if?(result?<?0)?? ????{?? ????????LOGE("Unable?to?wait?on?alarm:?%s\n",?strerror(errno));?? ????????return?0;?? ????}?? ?? ????return?result;?? }?? ?? static?JNINativeMethod?sMethods[]?=?{?? ??????? ????{"init",?"()I",?(void*)android_server_AlarmManagerService_init},?? ????{"close",?"(I)V",?(void*)android_server_AlarmManagerService_close},?? ????{"set",?"(IIJJ)V",?(void*)android_server_AlarmManagerService_set},?? ????{"waitForAlarm",?"(I)I",?(void*)android_server_AlarmManagerService_waitForAlarm},?? ????{"setKernelTimezone",?"(II)I",?(void*)android_server_AlarmManagerService_setKernelTimezone},?? };?? ?? int?register_android_server_AlarmManagerService(JNIEnv*?env)?? {?? ????return?jniRegisterNativeMethods(env,?"com/android/server/AlarmManagerService",?? ????????????????????????????????????sMethods,?NELEM(sMethods));?? }?? ?? }???
其實在JNI層這里RTC就和其余的模塊一樣,直接去通過打開/關閉/設置/等待等來操作節點/dev/alarm和底層進行通信,不仔細解釋。
4、 framework層
frameworks/base/services/java/com/android/server/AlarmManagerService.java?
? ? frameworks/base/core/java/android/app/AlarmManager.java
下面的是直接提供給app層的API接口,它是AlarmManagerService.java的一個封裝。
這里只是簡單的解釋下service到底在此做什么了。
其實也沒做什么,僅僅是把上面分析的JNI拿來在此調用一下而已。然后包裝一下,將功能實現得更完美些。
5.App層
[cpp]?view plaincopy
package?android.app;?? import?android.content.Context;?? import?android.content.Intent;?? import?android.os.RemoteException;?? import?android.os.ServiceManager;?? public?class?AlarmManager?? {?? ????public?static?final?int?RTC_WAKEUP?=?0;?? ????public?static?final?int?RTC?=?1;?? ????public?static?final?int?ELAPSED_REALTIME_WAKEUP?=?2;?? ????public?static?final?int?ELAPSED_REALTIME?=?3;?? ????private?final?IAlarmManager?mService;?? ????AlarmManager(IAlarmManager?service)?{?? ????????mService?=?service;?? ????}?? ?????? ????public?void?set(int?type,?long?triggerAtTime,?PendingIntent?operation)?{?? ????????try?{?? ????????????mService.set(type,?triggerAtTime,?operation);?? ????????}?catch?(RemoteException?ex)?{?? ????????}?? ????}?? ?? ????public?void?setRepeating(int?type,?long?triggerAtTime,?long?interval,?? ????????????PendingIntent?operation)?{?? ????????try?{?? ????????????mService.setRepeating(type,?triggerAtTime,?interval,?operation);?? ????????}?catch?(RemoteException?ex)?{?? ????????}?? ????}?? ?? ????public?static?final?long?INTERVAL_FIFTEEN_MINUTES?=?15?*?60?*?1000;?? ????public?static?final?long?INTERVAL_HALF_HOUR?=?2*INTERVAL_FIFTEEN_MINUTES;?? ????public?static?final?long?INTERVAL_HOUR?=?2*INTERVAL_HALF_HOUR;?? ????public?static?final?long?INTERVAL_HALF_DAY?=?12*INTERVAL_HOUR;?? ????public?static?final?long?INTERVAL_DAY?=?2*INTERVAL_HALF_DAY;?? ?????? ????public?void?setInexactRepeating(int?type,?long?triggerAtTime,?long?interval,?? ????????????PendingIntent?operation)?{?? ????????try?{?? ????????????mService.setInexactRepeating(type,?triggerAtTime,?interval,?operation);?? ????????}?catch?(RemoteException?ex)?{?? ????????}?? ????}?? ?????? ????public?void?cancel(PendingIntent?operation)?{?? ????????try?{?? ????????????mService.remove(operation);?? ????????}?catch?(RemoteException?ex)?{?? ????????}?? ????}?? ?? ????public?void?setTime(long?millis)?{?? ????????try?{?? ????????????mService.setTime(millis);?? ????????}?catch?(RemoteException?ex)?{?? ????????}?? ????}?? ?? ????public?void?setTimeZone(String?timeZone)?{?? ????????try?{?? ????????????mService.setTimeZone(timeZone);?? ????????}?catch?(RemoteException?ex)?{?? ????????}?? ????}?? }??
frameworks\base\core\java\android\app?
這個目錄下,就是系統自帶定時器的源代碼,比如Alarms.java 中:第一個導入的包就是 import android.app.AlarmManager。
總結
以上是生活随笔為你收集整理的Android RTC 自下往上浅析的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。