이름에서 알 수 있듯이, 현재 시간을 알려주는 시계입니다.
리눅스에서 date명령어를 입력할 때
RTC에서 HW시간을 바탕으로 현재 시간을 알려줍니다.
그래서 RTC로 측정한 시간을 HW time이라고도 부릅니다.
제가 RTC를 사용했던 건 모뎀의 알람을 맞추기 위해서였습니다.
구현해야할 기능은
특정 시간대에 모뎀이 (전원선이 연결되어있지만 전원이 OFF일 때)
알람처럼 켜지는 기능이었습니다.
예를들면 매일 아침 9시에 모뎀을 키는 식이죠.
그런데 여기서 '기기가 OFF되어있을때 켜진다'라는 것이 문제였습니다.
기기가 항상 켜있다면
'얼마 뒤에 알람을 울려라'라고 하면 되겠지만
아예 기계 자체가 OFF가 되어있다면
프로그램 자체가 동작하지 않는데
이를 어떻게 해야할지 막막했습니다.
이 때 사용했던 것이 RTC로써
HW내부의 RTC를 사용해서 알람기능을 구현했습니다.
RTC는 HW모듈이 꺼저있어도 시간을 측정할 수 있습니다.
메인 전원이 꺼저있어도 별도의 건전지로 동작합니다.
RTC 동작에 필요한 전력이 매우 작기 때문에
RTC는 메인전원과 별도로 움직일 수 있습니다.
그래서 모듈을 껏다가 킨 경우에도
그동안의 HW Time을 바탕으로 현재 시간을 계산할 수 있습니다.
가장 먼저는 시스템 Level에서 RTC를 사용할 수 있는지 여부를 알아봤습니다.
팀장님께 가이드를 받아보니
"RTC가 동작을 안할 수 있다. 다른 팀에 문의를 해서 커널단에서 지원되는지 봐야한다"
라고 말씀해주셨습니다.
그래서 해당 팀의 담당자 분께 RTC지원여부를 여쭤보셨습니다.
제가 회사에서 사용하는 모뎀이 퀄컴 칩 베이스로 동작하고
32bit/64bit 두 가지 타입으로 나뉘어져있기 때문에
퀄컴에서 해당 기능을 몇 비트 OS에서 지원되는지 여쭤봤습니다.
다행히도 해당 기능들이 작동하고 있음을 전달받았습니다.
그 다음에는 RTC관련 시스템 함수들을 사용했습니다.
회사에서 사용했던 코드 중 일부를 넣었습니다.
자세한 코드는 대외비 이슈로 생략했고
어떤 식으로 RTC를 사용했는지 중심으로 적었습니다.
int rtc_fd = open("/dev/rtc", O_RDONLY);
가장 먼저, RTC의 파일시스템을 오픈해야합니다.
위와 같이 open함수를 사용해 해당 지점에 저장된 rtc의 fd를 가져옵니다.
struct rtc_time time;
if (ioctl(rtc_fd, RTC_SET_TIME, &time) == -1 )
{
return FAIL;
}
rtc_time 구조체를 선언합니다.
이 구조체는 rtc로 얻은 시간정보를 받을 수 있습니다.
ioctl을 통해 현재 rtc정보를 읽어올 수 있습니다.
다음과 같이 fd에는 rtc_fd, Request에는 RTC_SET_TIME을 Input으로 넣는다면
time구조체에 rtc시간 정보가 입력됩니다.
만약 ioctl 결과값이 -1이라면 실패한 경우이므로 예외처리를 해야합니다.
struct rtc_time {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday; /* 사용 안 함 */
int tm_yday; /* 사용 안 함 */
int tm_isdst; /* 사용 안 함 */
};
struct rtc_time은 다음과 같은 구조를 가지고 있습니다.
초/분/시간/날짜/월/년도
각각의 데이터를 받아옵니다.
저같은 경우에는 알람시간을 설정할 때
현재의 rtc시간을 받은 후
'얼마 뒤에 알람이 울릴지'를 더했습니다.
만약 10초 뒤에 알람이 울릴 예정이라면
tm_sec에 10을 더하고
60을 넘기면 초에서 60을 빼주고
분을 하나 더하는 식으로요.
struct rtc_wkalrm {
unsigned char enabled;
unsigned char pending;
struct rtc_time time;
};
알람시간 정보를 담은 rtc_wkalrm 구조체입니다.
rtc_time 구조체를 받으면서
enabled,pending이라는 변수를 받습니다.
enabled 변수는 알람을 읽고 끄거나 현재상태를 알고자할때
pending 변수는 미처리 인터럽트를 확인할 때 사용이 됩니다.
if( ioctl( rtc_fd, RTC_WKALM_SET, &alarm ) == -1 )
{
return FAIL;
}
if( ioctl( rtc_fd, RTC_AIE_ON, 0 ) == -1 )
(
return FAIL;
}
RTC_WKALM_SET request는 이름에서 유추할 수 있듯이
알람을 Setting하는 인터페이스입니다.
RTC_AIE_ON은 알람 인터럽트를 켜는 인터페이스로
해당 인터페이스를 사용해야
알람시간이 되었을 때 알람 인터럽트가 발생합니다.
res = pthread_create( &handle->alarm_listen_thrd, &rtc_alarm_thrd_attr,
( void * )rtc_alarm_listener, ( void * )&listen );
알람인터럽트까지 대기하는 동작은 쓰레드로 구현했습니다.
프로세스로 구현할 경우, 알람시간까지 프로그램 전체가 대기를 해야합니다.
제가 회사에서 만드는 프로그램은 모뎀에서 동작하는 프로그램인데,
프로세스로 구현을 한다면 알람 하나 대기하다가 다른 동작을 할 수 없습니다.
프로세스로 구현하면 동기적으로 동작이 수행되기 때문에
알람호출까지 대기->알람->다른 동작 이렇게 순서가 됩니다.
이러면 모뎀에 다른 기능을 쓸 수가 없기 때문에
알람을 울리는 기능은 쓰레드로 구현했습니다.
unsigned long data;
read_res = read( rtc_fd, &data, sizeof( unsigned long ) );
if(read_res == -1)
{
/*에러리턴*/
}
else
{
/*알람울림*/
}
알람 인터럽트까지 대기하는 rtc_alarm_listener에서
알람까지 대기하는 부분입니다.
다음과 같이 read함수를 사용하면
알람시간이 되었을 때 인터럽트가 수행이 됩니다.
void rtc_alarm_read( M2MB_ATP_HANDLE atpHandle )
{
pthread_t init_thread;
pthread_attr_t rtc_alarm_thrd_attr;
FILE *fp = popen( "dmesg | grep -i 'PMIC PON log: PON Trigger'", "r" );
/*중략*/
printf( M2MB_TC_RTC, M2MB_TL_LOG, "Boot read_Start" );
if( fp == NULL )
{
printf( M2MB_TC_RTC, M2MB_TL_LOG, "Boot : Dmesg error" );
return;
}
while( fgets( buffer, sizeof( buffer ), fp ) != NULL )
{
if( strstr( buffer, "PON Trigger: RTC_ALARM" ) )
{
strncpy( last_trigger, "RTC_ALARM", sizeof( last_trigger ) - 1 );
}
else if( strstr( buffer, "PON Trigger: HARD_RESET" ) )
{
strncpy( last_trigger, "HARD_RESET", sizeof( last_trigger ) - 1 );
}
else if( strstr( buffer, "PON Trigger: KPDPWR_N") )
{
strncpy( last_trigger, "KPDPWR_N", sizeof( last_trigger ) - 1 );
}
}
if( strcmp( last_trigger, "RTC_ALARM" ) == 0 )
{
printf( M2MB_TC_RTC,M2MB_TL_LOG, "Boot Reason: RTC_ALARM detected (alarm wakeup)" );
fd = fopen( ALARM_FILE_PATH, "r" );
if( !fd )
{
printf( M2MB_TC_RTC, M2MB_TL_LOG, "Boot: Failed to open file" );
return ;
}
printf( M2MB_TC_RTC, M2MB_TL_LOG, "Boot: File open");
/*후략*/
Boot reason이 알람때문인지 아닌지 확인하는 코드입니다.
확인방법을 서칭하여 로그를 통한 확인 방법을 채택했습니다.
모듈에서 Boot가 될 때 log가 찍히는데
이때에 Boot reason이 함께 찍힌다는 걸 알고
알람으로 인한 REBOOT
REBOOT명령어에 의한 REBOOT
모듈 전원버튼 Push에 의한 REBOOT
총 3가지의 경우 어떤 메세지가 나오는지 파악한 후
RTC ALARM에 대한 REBOOT 메세지가 최종적으로 찍힌다면
알람에 의한 REBOOT이라고 판단하고 그에 따른 동작을 수행하도록 코드를 짰습니다.
if( ioctl( rtc_fd, RTC_WKALM_RD, &rtc_alarm ) != -1 )
{
rtc_alarm.enabled = 0;
if( ioctl( rtc_fd, RTC_WKALM_SET, &rtc_alarm ) != -1 )
{
ret = 0;
}
}
알람을 지우는 부분입니다.
rtc_wkalrm 구조체에 있는 enabled를 0으로 만들어
알람 인터럽트를 하지 않도록 만들 수 있습니다.
메모리 가상화(memory virtualization) (0) | 2024.04.29 |
---|---|
GIC (0) | 2024.04.18 |
스택카나리(Stack canary) (0) | 2024.04.10 |
시스템 콜의 ftrace 분석을 해보자! (0) | 2024.04.02 |
익셉션(Exception) (0) | 2024.03.15 |
댓글 영역