http://blog.csdn.net/crycheng/article/details/7802502
1.首先搞清楚RTC在kernel內的作用: linux系統有兩個時鐘:一個是由主板電池驅動的“Real Time Clock”也叫做RTC或者叫CMOS時鐘,硬件時鐘。當操作系統關機的時候,用這個來記錄時間,但是對於運行的系統是不用這個時間的。
另一個時間是 “System clock”也叫內核時鐘或者軟件時鐘,是由軟件根據時間中斷來進行計數的,內核時鐘在系統關機的情況下是不存在的,所以,當操作系統啓動的時候,內核時鐘是要讀取RTC時間來進行時間同步。並且在系統關機的時候將系統時間寫回RTC中進行同步。 如前所述,Linux內核與RTC進行互操作的時機只有兩個:
1) 內核在啓動時從RTC中讀取啓動時的時間與日期;
2) 內核在需要時將時間與日期回寫到RTC中。 系統啓動時,內核通過讀取RTC來初始化內核時鐘,又叫牆上時間,該時間放在xtime變量中。
- 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; /* seconds */
- long tv_nsec; /* nanoseconds */
- };
最有可能讀取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 */
- void __init time_init(void)
- {
- system_timer = machine_desc->timer;
- system_timer->init();
- #ifdef CONFIG_HAVE_SCHED_CLOCK
- sched_clock_postinit();
- #endif
- }
2.RTC結構部分
- 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);
- // check power down
- 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); //read time from hym8563
- if(((tm_read.tm_year < 70) | (tm_read.tm_year > 137 )) | (tm_read.tm_mon == -1) | (rtc_valid_tm(&tm_read) != 0)) //if the hym8563 haven't initialized
- {
- hym8563_set_time(client, &tm); //initialize the hym8563
- }
- 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.c ,因爲在android中它爲了使得平臺無關性提高,因此大量的增加過渡代碼層,HAL就是這種性質的存在。alarm.c在用戶空間中會多一個/dev/alarm 節點,而rtc-hym8563.c.c 會產生/dev/rtc這樣的節點。
3.JNI層
- 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[] = {
- /* name, signature, funcPtr */
- {"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));
- }
- } /* namespace android */
其實在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層- 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。