/** * 스캔 코드를 내부적으로 사용하는 키 데이터로 바꾼 후 키 큐에 삽입 */ BOOL kConvertScanCodeAndPutQueue( BYTE bScanCode ) { KEYDATA stData; BOOL bResult = FALSE; BOOL bPreviousInterrupt; // 스캔 코드를 키 데이터에 삽입 stData.bScanCode = bScanCode; // 스캔 코드를 ASCII 코드와 키 상태로 변환하여 키 데이터에 삽입 if( kConvertScanCodeToASCIICode( bScanCode, &( stData.bASCIICode ), &( stData.bFlags ) ) == TRUE ) { // 인터럽트 불가 bPreviousInterrupt = kSetInterruptFlag( FALSE ); // 키 큐에 삽입 bResult = kPutQueue( &gs_stKeyQueue, &stData ); // 이전 인터럽트 플래그 복원 kSetInterruptFlag( bPreviousInterrupt ); } return bResult; }
/** * 키보드를 활성화 함 */ BOOL kActivateKeyboard( void ) { int i, j; BOOL bPreviousInterrupt; BOOL bResult; // 인터럽트 불가 bPreviousInterrupt = kSetInterruptFlag( FALSE ); // 컨트롤 레지스터(포트 0x64)에 키보드 활성화 커맨드(0xAE)를 전달하여 키보드 디바이스 활성화 kOutPortByte( 0x64, 0xAE ); // 입력 버퍼(포트 0x60)가 빌 때까지 기다렸다가 키보드에 활성화 커맨드를 전송 // 0xFFFF만큼 루프를 수행할 시간이면 충분히 커맨드가 전송될 수 있음 // 0xFFFF 루프를 수행한 이후에도 입력 버퍼(포트 0x60)가 비지 않으면 무시하고 전송 for( i = 0 ; i < 0xFFFF ; i++ ) { // 입력 버퍼(포트 0x60)가 비어있으면 키보드 커맨드 전송 가능 if( kIsInputBufferFull() == FALSE ) { break; } } // 입력 버퍼(포트 0x60)로 키보드 활성화(0xF4) 커맨드를 전달하여 키보드로 전송 kOutPortByte( 0x60, 0xF4 ); // ACK가 올 때까지 대기함 bResult = kWaitForACKAndPutOtherScanCode(); // 이전 인터럽트 상태 복원 kSetInterruptFlag( bPreviousInterrupt ); return bResult; }
BOOL kActivateKeyboard(void) { int i; int j; BOOL bPreviousInterrupt; BOOL bResult; // 인터럽트 막아놓고 원래의 상태를 저장 bPreviousInterrupt = kSetInterruptFlag( FALSE ); kOutPortByte(0x64, 0xAE); // 0x64(컨트롤(쓰기)레지스터에 0xAE 신호 발송 (키보드 디바이스 활성화) ) for( i = 0 ; i < 0xFFFF ; i++ ) { if (!kIsInputBufferFull()) // inputbuffer가 비었으면 키보드 활성화(디바이스X) 신호 보낼것 { break; } } kOutPortByte(0x60, 0xF4); // 0x60에 0xF4 전송 (입력버퍼로 키보드 활성화 키 전송) // ACK가 올때까지 기다림 bResult = kWaitForACKAndPutOtherScanCode(); // 이전 인터럽트 상태 복원 kSetInterruptFlag(bPreviousInterrupt); return bResult; }
// 태스크 사이에서 사용하는 데이터를 위한 잠근 해제 함수 void kUnlock(MUTEX* pstMutex) { BOOL bInterruptFlag; // 인터럽트를 비활성화 bInterruptFlag = kSetInterruptFlag(FALSE); // 뮤텍스를 잠근 태스크가 아니면 실패 if((pstMutex->bLockFlag==FALSE)||(pstMutex->qwTaskID!=kGetRunningTask(kGetAPICID())->stLink.qwID)) { // 인터럽트를 복원 kSetInterruptFlag(bInterruptFlag); return ; } // 뮤텍스를 중복으로 잠갔으면 잠긴 횟수만 감소 if(pstMutex->dwLockCount>1) { pstMutex->dwLockCount--; } else { // 해제된 것으로 설정, 잠긴 플래그는 가장 나중에 해제해야함 pstMutex->qwTaskID = TASK_INVALIDID; pstMutex->dwLockCount = 0; pstMutex->bLockFlag=FALSE; } // 인터럽트를 복원 kSetInterruptFlag(bInterruptFlag); }
// 시스템 전역에서 사용하는 데이터를 위한 잠금 해제 함수 void kUnlockForSpinLock(SPINLOCK* pstSpinLock) { BOOL bInterruptFlag; // 인터럽트를 먼저 비활성화 bInterruptFlag = kSetInterruptFlag(FALSE); // 스핀락을 잠근 태스크가 아니면 실패 if((pstSpinLock->bLockFlag==FALSE)||(pstSpinLock->bAPICID!=kGetAPICID())) { kSetInterruptFlag(bInterruptFlag); return; } // 스핀락을 중복으로 잠갔으면 잠김 횟수만 감소 if(pstSpinLock->dwLockCount>1) { pstSpinLock->dwLockCount--; return; } // 스핀락을 해제된 것으로 설정하고 인터럽트 플래그를 복원 // 인터럽트 플래그는 미리 저장해두었다가 사용 bInterruptFlag = pstSpinLock->bInterruptFlag; pstSpinLock->bAPICID = 0xFF; pstSpinLock->dwLockCount = 0; pstSpinLock->bInterruptFlag = FALSE; pstSpinLock->bLockFlag = FALSE; kSetInterruptFlag(bInterruptFlag); }
/** * 키보드 LED의 ON/OFF를 변경 */ BOOL kChangeKeyboardLED( BOOL bCapsLockOn, BOOL bNumLockOn, BOOL bScrollLockOn ) { int i, j; BOOL bPreviousInterrupt; BOOL bResult; BYTE bData; // 인터럽트 불가 bPreviousInterrupt = kSetInterruptFlag( FALSE ); // 키보드에 LED 변경 커맨드 전송하고 커맨드가 처리될 때까지 대기 for( i = 0 ; i < 0xFFFF ; i++ ) { // 출력 버퍼(포트 0x60)가 비었으면 커맨드 전송 가능 if( kIsInputBufferFull() == FALSE ) { break; } } // 출력 버퍼(포트 0x60)로 LED 상태 변경 커맨드(0xED) 전송 kOutPortByte( 0x60, 0xED ); for( i = 0 ; i < 0xFFFF ; i++ ) { // 입력 버퍼(포트 0x60)가 비어있으면 키보드가 커맨드를 가져간 것임 if( kIsInputBufferFull() == FALSE ) { break; } } // ACK가 올때까지 대기함 bResult = kWaitForACKAndPutOtherScanCode(); if( bResult == FALSE ) { // 이전 인터럽트 상태 복원 kSetInterruptFlag( bPreviousInterrupt ); return FALSE; } // LED 변경 값을 키보드로 전송하고 데이터가 처리가 완료될 때까지 대기 kOutPortByte( 0x60, ( bCapsLockOn << 2 ) | ( bNumLockOn << 1 ) | bScrollLockOn ); for( i = 0 ; i < 0xFFFF ; i++ ) { // 입력 버퍼(포트 0x60)가 비어있으면 키보드가 LED 데이터를 가져간 것임 if( kIsInputBufferFull() == FALSE ) { break; } } // ACK가 올 때까지 대기함 bResult = kWaitForACKAndPutOtherScanCode(); // 이전 인터럽트 상태 복원 kSetInterruptFlag( bPreviousInterrupt ); return bResult; }
BOOL kChangeKeyboardLED( BOOL bCapsLockOn, BOOL bNumLockOn, BOOL bScrollLockOn ) { int i, j; BOOL bPreviousInterrupt; BOOL bResult; BYTE bData; // 인터럽트 불가 bPreviousInterrupt = kSetInterruptFlag( FALSE ); for( i = 0 ; i < 0xFFFF ; i++ ) { if( kIsInputBufferFull() == FALSE ) // 입력버퍼가 빌때까지 기다림 { break; } } // 출력 버퍼에 LED 상태 변경 한다는 커맨드 전송(0xED) kOutPortByte(0x60, 0xED); for( i = 0 ; i < 0xFFFF ; i++ ) { if(kIsInputBufferFull() == FALSE) // 입력 버퍼가 빌때까지 기다림 break; } // ACK를 기다림 bResult = kWaitForACKAndPutOtherScanCode(); if( bResult == FALSE ) { kSetInterruptFlag( bPreviousInterrupt ); return FALSE; } if ( j>= 100 ) // 일정 시간 이상 ACK가 안옴 return FALSE; // 2비트 , 1비트, 0비트에 따라 온을 시켜줌 kOutPortByte( 0x60, (bCapsLockOn << 2) | (bNumLockOn << 1) | bScrollLockOn ); for( i = 0 ; i < 0xFFFF ; i++ ) { if(kIsInputBufferFull() == FALSE) { break; } } bResult = kWaitForACKAndPutOtherScanCode(); kSetInterruptFlag(bPreviousInterrupt); return bResult; }
// 시스템 전역에서 사용하는 데이터를 위한 잠금 함수 void kLockForSpinLock(SPINLOCK* pstSpinLock) { BOOL bInterruptFlag; // 인터럽트를 먼저 비활성화 bInterruptFlag = kSetInterruptFlag(FALSE); // 이미 잠겨 있다면 내가 잠갔는지 확인하고 그렇다면 잠금 횟수를 증가시킨 뒤 종료 if(kTestAndSet(&(pstSpinLock->bLockFlag),0,1)==FALSE) { // 자신이 잠갔다면 횟수만 증가시킴 if(pstSpinLock->bAPICID == kGetAPICID()) { pstSpinLock->dwLockCount++; return; } // 자신이 아닌 경우는 잠긴 것이 해제될 때까지 대기 while(kTestAndSet(&(pstSpinLock->bLockFlag),0,1)==FALSE) { // kTestAndSet() 함수르 계속 호출하여 메모리 버스가 Lock 되는 것을 방지 while(pstSpinLock->bLockFlag == TRUE) { kPause(); } } } // 잠김 설정, 잠김 플래그는 위의 kTestAndSet() 함수에서 처리 pstSpinLock->dwLockCount = 1; pstSpinLock->bAPICID = kGetAPICID(); // 인터럽트 플래그를 저장하여 Unlock 수행 시 복원 pstSpinLock->bInterruptFlag = bInterruptFlag; }
/** * 키 큐에서 키 데이터를 제거 */ BOOL kGetKeyFromKeyQueue( KEYDATA* pstData ) { BOOL bResult; BOOL bPreviousInterrupt; // 큐가 비었으면 키 데이터를 꺼낼 수 없음 if( kIsQueueEmpty( &gs_stKeyQueue ) == TRUE ) { return FALSE; } // 인터럽트 불가 bPreviousInterrupt = kSetInterruptFlag( FALSE ); // 키 큐에서 키 데이터를 제거 bResult = kGetQueue( &gs_stKeyQueue, pstData ); // 이전 인터럽트 플래그 복원 kSetInterruptFlag( bPreviousInterrupt ); return bResult; }
// 태스크 사이에서 사용하는 데이터를 위한 잠금 함수 void kLock(MUTEX* pstMutex) { BYTE bCurrentAPICID; BOOL bInterruptFlag; // 인터럽트를 비활성화 bInterruptFlag = kSetInterruptFlag(FALSE); // 현재 코어의 로컬 APIC ID를 확인 bCurrentAPICID = kGetAPICID(); // 이미 잠겨 있다면 내가 잠갔는지 확인하고 잠근 횟수를 증가시킨 뒤 종료 if(kTestAndSet(&(pstMutex->bLockFlag),0,1)==FALSE) { // 자신이 잠겼다면 횟수만 증가시킴 if(pstMutex->qwTaskID==kGetRunningTask(bCurrentAPICID)->stLink.qwID) { // 인터럽트를 복원 kSetInterruptFlag(bInterruptFlag); pstMutex->dwLockCount++; return; } // 자신이 아닌 경우는 잠긴 것이 해제될 때까지 대기 while(kTestAndSet(&(pstMutex->bLockFlag),0,1)==FALSE) { kSchedule(); } } // 잠금 설정, 잠긴 플래그는 위의 kTestAndSet() 함수에서 처리함 pstMutex->dwLockCount =1; pstMutex->qwTaskID=kGetRunningTask(bCurrentAPICID)->stLink.qwID; // 인터럽트를 복원 kSetInterruptFlag(bInterruptFlag); }
/** * 다른 태스크를 찾아서 전환 * 인터럽트나 예외가 발생했을 때 호출하면 안됨 * 단, 인터럽트나 예외에서 태스크를 종료하는 경우는 사용 가능 */ BOOL kSchedule( void ) { TCB* pstRunningTask, * pstNextTask; BOOL bPreviousInterrupt; BYTE bCurrentAPICID; // 전환하는 도중 인터럽트가 발생하여 태스크 전환이 또 일어나면 곤란하므로 전환하는 // 동안 인터럽트가 발생하지 못하도록 설정 bPreviousInterrupt = kSetInterruptFlag( FALSE ); // 현재 로컬 APIC ID 확인 bCurrentAPICID = kGetAPICID(); // 전환할 태스크가 있어야 함 if( kGetReadyTaskCount( bCurrentAPICID ) < 1 ) { kSetInterruptFlag( bPreviousInterrupt ); return FALSE; } // 임계 영역 시작 kLockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) ); // 실행할 다음 태스크를 얻음 pstNextTask = kGetNextTaskToRun( bCurrentAPICID ); if( pstNextTask == NULL ) { // 임계 영역 끝 kUnlockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) ); kSetInterruptFlag( bPreviousInterrupt ); return FALSE; } // 현재 수행중인 태스크의 정보를 수정한 뒤 콘텍스트 전환 pstRunningTask = gs_vstScheduler[ bCurrentAPICID ].pstRunningTask; gs_vstScheduler[ bCurrentAPICID ].pstRunningTask = pstNextTask; // 유휴 태스크에서 전환되었다면 사용한 프로세서 시간을 증가시킴 if( ( pstRunningTask->qwFlags & TASK_FLAGS_IDLE ) == TASK_FLAGS_IDLE ) { gs_vstScheduler[ bCurrentAPICID ].qwSpendProcessorTimeInIdleTask += TASK_PROCESSORTIME - gs_vstScheduler[ bCurrentAPICID ].iProcessorTime; } // 다음에 수행할 태스크가 FPU를 쓴 태스크가 아니라면 TS 비트를 설정 if( gs_vstScheduler[ bCurrentAPICID ].qwLastFPUUsedTaskID != pstNextTask->stLink.qwID ) { kSetTS(); } else { kClearTS(); } // 태스크 종료 플래그가 설정된 경우 콘텍스트를 저장할 필요가 없으므로, 대기 리스트에 // 삽입하고 콘텍스트 전환 if( pstRunningTask->qwFlags & TASK_FLAGS_ENDTASK ) { kAddListToTail( &( gs_vstScheduler[ bCurrentAPICID ].stWaitList ), pstRunningTask ); // 임계 영역 끝 kUnlockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) ); // 태스크 전환 kSwitchContext( NULL, &( pstNextTask->stContext ) ); } else { kAddTaskToReadyList( bCurrentAPICID, pstRunningTask ); // 임계 영역 끝 kUnlockForSpinLock( &( gs_vstScheduler[ bCurrentAPICID ].stSpinLock ) ); // 태스크 전환 kSwitchContext( &( pstRunningTask->stContext ), &( pstNextTask->stContext ) ); } // 프로세서 사용 시간을 업데이트 gs_vstScheduler[ bCurrentAPICID ].iProcessorTime = TASK_PROCESSORTIME; // 인터럽트 플래그 복원 kSetInterruptFlag( bPreviousInterrupt ); return FALSE; }
/** * 시스템 전역에서 사용하는 데이터를 위한 잠금 해제 함수 */ void kUnlockForSystemData( BOOL bInterruptFlag ) { kSetInterruptFlag( bInterruptFlag ); }
/** * 시스템 전역에서 사용하는 데이터를 위한 잠금 함수 */ BOOL kLockForSystemData( void ) { return kSetInterruptFlag( FALSE ); }
/** * 멀티코어 프로세서 또는 멀티 프로세서 모드로 전환하는 함수 */ BOOL kChangeToMultiCoreMode( void ) { MPCONFIGRUATIONMANAGER* pstMPManager; BOOL bInterruptFlag; int i; // Application Processor 활성화 if( kStartUpApplicationProcessor() == FALSE ) { return FALSE; } //-------------------------------------------------------------------------- // 대칭 I/O 모드로 전환 //-------------------------------------------------------------------------- // MP 설정 매니저를 찾아서 PIC 모드인가 확인 pstMPManager = kGetMPConfigurationManager(); if( pstMPManager->bUsePICMode == TRUE ) { // PIC 모드이면 I/O 포트 어드레스 0x22에 0x70을 먼저 전송하고 // I/O 포트 어드레스 0x23에 0x01을 전송하는 방법으로 IMCR 레지스터에 접근하여 // PIC 모드 비활성화 kOutPortByte( 0x22, 0x70 ); kOutPortByte( 0x23, 0x01 ); } // PIC 컨트롤러의 인터럽트를 모두 마스크하여 인터럽트가 발생할 수 없도록 함 kMaskPICInterrupt( 0xFFFF ); // 프로세서 전체의 로컬 APIC를 활성화 kEnableGlobalLocalAPIC(); // 현재 코어의 로컬 APIC를 활성화 kEnableSoftwareLocalAPIC(); // 인터럽트를 불가로 설정 bInterruptFlag = kSetInterruptFlag( FALSE ); // 모든 인터럽트를 수신할 수 있도록 태스크 우선 순위 레지스터를 0으로 설정 kSetTaskPriority( 0 ); // 로컬 APIC의 로컬 벡터 테이블을 초기화 kInitializeLocalVectorTable(); // 대칭 I/O 모드로 변경되었음을 설정 kSetSymmetricIOMode( TRUE ); // I/O APIC 초기화 kInitializeIORedirectionTable(); // 이전 인터럽트 플래그를 복원 kSetInterruptFlag( bInterruptFlag ); // 인터럽트 부하 분산 기능 활성화 kSetInterruptLoadBalancing( TRUE ); // 태스크 부하 분산 기능 활성화 for( i = 0 ; i < MAXPROCESSORCOUNT ; i++ ) { kSetTaskLoadBalancing( i, TRUE ); } return TRUE; }