//-------------------------------------------------------------------------- // // LS RDK KBM Keyboard application // //-------------------------------------------------------------------------- // $Archive: /WirelessUSB/WUSB Kits/CY4632 LS KBM RDK/DocSrc/CD_Root/Firmware/Source Code/RDK Keyboard/keyboard.c $ // $Modtime: 6/23/04 10:34a9/30/04 12:01p $ // $Revision: 89 $ //-------------------------------------------------------------------------- // // Copyright 2003-2004, Cypress Semiconductor Corporation. // // This software is owned by Cypress Semiconductor Corporation (Cypress) // and is protected by and subject to worldwide patent protection (United // States and foreign), United States copyright laws and international // treaty provisions. Cypress hereby grants to licensee a personal, // non-exclusive, non-transferable license to copy, use, modify, create // derivative works of, and compile the Cypress Source Code and derivative // works for the sole purpose of creating custom software in support of // licensee product to be used only in conjunction with a Cypress integrated // circuit as specified in the applicable agreement. Any reproduction, // modification, translation, compilation, or representation of this // software except as specified above is prohibited without the express // written permission of Cypress. // // Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND,EXPRESS OR IMPLIED, // WITH REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // Cypress reserves the right to make changes without further notice to the // materials described herein. Cypress does not assume any liability arising // out of the application or use of any product or circuit described herein. // Cypress does not authorize its products for use as critical components in // life-support systems where a malfunction or failure may reasonably be // expected to result in significant injury to the user. The inclusion of // Cypress’ product in a life-support systems application implies that the // manufacturer assumes all risk of such use and in doing so indemnifies // Cypress against all charges. // // Use may be limited by and subject to the applicable Cypress software // license agreement. // //-------------------------------------------------------------------------- /* Device Class Definition for Human Interface Devices (HID) Version 1.11 Page 62 (72 absolute). Appendix C: Keyboard Implementation - Reports must contain a list of keys currently pressed and not make/break codes (relative data). - If two or more keys are reported in one report, their order is indeterminate. */ #include "ls_config.h" #include "m8c.h" #include "kdefs.h" #include "protocol.h" #include "bind.h" #include "radio.h" #include "battery.h" // Keyboard structure HID_APP hid; // Report packet pointer APP_TX_PACKET *report_packet; // Timestamps TIME_STAMP ts; TIME_STAMP last_transfer_ts; #ifdef KEYBOARD_TEST_MODES TEST_STATE test_mode = TEST_OFF; UINT8 sentence_table_index = 0; #else #ifdef MFG_ENTER_BY_KEY_NOT_PIN TEST_STATE test_mode = TEST_OFF; #endif // MFG_ENTER_BY_KEY_NOT_PIN #endif // KEYBOARD_TEST_MODES #ifdef MOUSE_EMULATION_MODE UINT8 mouse_emulation = FALSE; #endif // MOUSE_EMULATION_MODE //-------------------------------------------------------------------------- // bind_button_isr //-------------------------------------------------------------------------- void bind_button_isr(void) { #ifndef BIND_BASIC if (SW1_MASK & ~RADIO_PORT) { hid.status |= BIND_BUTTON; // Wait for button to be released. while (SW1_MASK & ~RADIO_PORT) { RADIO_PORT |= SW1_MASK; } } #endif // BIND_BASIC RADIO_PORT |= SW1_MASK; } //-------------------------------------------------------------------------- // keyboard_isr //-------------------------------------------------------------------------- void keyboard_isr(void) { UINT8 temp; // Read to clear interrupt temp = KB_ROW_PORT; // Indicate wakeup hid.status &= ~KEYBOARD_SLEEP; } //-------------------------------------------------------------------------- // key_queue_init //-------------------------------------------------------------------------- void key_queue_init() { UINT8 i; hid.status &= ~(GENERATE_STD_REPORT | GENERATE_HOT_REPORT | GENERATE_PWR_REPORT); for (i = 0; i < KEY_QUEUE_LEN; ++i) { hid.key_queue[i].index = INVALID_INDEX; } } //-------------------------------------------------------------------------- // add_or_update_key_queue // Returns TRUE is key was added or updated; otherwise, FALSE for overflow //-------------------------------------------------------------------------- BOOL add_or_update_key_queue(KEY key) { UINT8 i; if (key.state & (KEY_STATE_UP | KEY_STATE_MOD_UP)) { // Check for update for (i = 0; i < KEY_QUEUE_LEN; ++i) { if ((hid.key_queue[i].index == key.index) && !(hid.key_queue[i].state & (KEY_STATE_UP | KEY_STATE_MOD_UP | KEY_STATE_REMOVE))) { //Update state hid.key_queue[i].state |= key.state; if (key.state & KEY_STATE_MOD_UP) { // Need to add the up key as a separate key entry break; } return TRUE; } } // Update modifier key state if (key.state & KEY_STATE_MOD_UP) { key.state = KEY_STATE_UP; } } // Add key to queue for (i = 0; i < KEY_QUEUE_LEN; ++i) { if (hid.key_queue[i].index == INVALID_INDEX) { // Add key hid.key_queue[i].index = key.index; hid.key_queue[i].state = key.state; return TRUE; } } // Key queue is full return FALSE; } //-------------------------------------------------------------------------- // sort_key_queue //-------------------------------------------------------------------------- void sort_key_queue(void) { UINT8 i; UINT8 j; UINT8 index; UINT8 device; UINT8 state; UINT8 std_count = 0; UINT8 hot_count = 0; UINT8 pwr_count = 0; // Shift key entry(s) to beginning of the key queue for (i = 0; i < KEY_QUEUE_LEN; ++i) { // Remove completed keys if ((hid.key_queue[i].index != INVALID_INDEX) && (hid.key_queue[i].state & KEY_STATE_REMOVE)) { hid.key_queue[i].index = INVALID_INDEX; } // Search for replacement entry if invalid if (hid.key_queue[i].index == INVALID_INDEX) { for (j = (i+1); j < KEY_QUEUE_LEN; ++j) { // Check for valid key entry if ((hid.key_queue[j].index != INVALID_INDEX) && (hid.key_queue[j].state != KEY_STATE_REMOVE)) { // Move key entry hid.key_queue[i].index = hid.key_queue[j].index; hid.key_queue[i].state = hid.key_queue[j].state; // Remove original entry hid.key_queue[j].index = INVALID_INDEX; break; } } } index = hid.key_queue[i].index; device = default_keyboard_scan_table[index].device & DEVICE_MASK; state = hid.key_queue[i].state; // Check for pending keys that can be sent if (index != INVALID_INDEX) { #ifndef MOUSE_EMULATION_MODE if (device == DEVICE_1) #else if ((device == DEVICE_1) || (device == DEVICE_4) || (device == DEVICE_5)) #endif // MOUSE_EMULATION_MODE { if (state != KEY_STATE_UP) { ++std_count; } if (((std_count <= KEYBOARD_REPORT_NUM_KEYCODES) && (state & KEY_STATE_DOWN)) || (state & (KEY_STATE_UP | KEY_STATE_MOD_UP))) { hid.status |= GENERATE_STD_REPORT; } } #ifdef KEYBOARD_MULTIMEDIA_SUPPORT else if (device == DEVICE_2) { if (state != KEY_STATE_UP) { ++hot_count; } if (((hot_count <= KEYBOARD_REPORT_NUM_HOTCODES) && (state & KEY_STATE_DOWN)) || (state == KEY_STATE_UP)) { hid.status |= GENERATE_HOT_REPORT; } } else if (device == DEVICE_3) { if (state != KEY_STATE_UP) { ++ pwr_count; } if (((pwr_count <= KEYBOARD_REPORT_NUM_PWRCODES) && (state & KEY_STATE_DOWN)) || (state == KEY_STATE_UP)) { hid.status |= GENERATE_PWR_REPORT; } } #endif // KEYBOARD_MULTIMEDIA_SUPPORT } } } //-------------------------------------------------------------------------- // debounce_init //-------------------------------------------------------------------------- void debounce_init(void) { UINT8 i; for (i = 0; i < (DEBOUNCE_QUEUE_LENGTH); ++i) { hid.debounce[i].value = 0x00; hid.debounce[i].index = INVALID_INDEX; } } //-------------------------------------------------------------------------- // age_debounce_queue //-------------------------------------------------------------------------- void age_debounce_queue(void) { UINT8 i; UINT8 value; for (i = 0; i < (DEBOUNCE_QUEUE_LENGTH); ++i) { value = hid.debounce[i].value; if (0x00 < value > 0x00) { // Decrement debounce value hid.debounce[i].value = --value; // If value reaches 0x00 ignore the key if (value == 0x00) { // Remove index hid.debounce[i].index = INVALID_INDEX; } } } } //-------------------------------------------------------------------------- // debounce //-------------------------------------------------------------------------- BOOL debounce(UINT8 index) { UINT8 i; // Check for index for (i = 0; i < (DEBOUNCE_QUEUE_LENGTH); ++i) { if (index == hid.debounce[i].index) { #ifdef DEBOUNCE_BEFORE_REPORT if (hid.debounce[i].value <= 0x1) return TRUE; else return FALSE; #else return FALSE; #endif } } // Add index (if space) for (i = 0; i < (DEBOUNCE_QUEUE_LENGTH); ++i) { if (hid.debounce[i].index == INVALID_INDEX) { // Add index to first available entry hid.debounce[i].index = index; hid.debounce[i].value = KEYBOARD_DEBOUNCE_COUNT; #ifdef DEBOUNCE_BEFORE_REPORT return FALSE; #else return TRUE; #endif } } // No free entry return FALSE; } //-------------------------------------------------------------------------- // ghost // look for more than 1 active row. // if a column has more than 1 active row and the current // active row matches, return FALSE //-------------------------------------------------------------------------- BOOL ghost(KEY_STATE state, UINT8 column_index, UINT8 this_column, UINT8 this_row_bit, UINT8 * current_key_state_ptr) { UINT8 num_down_keys; UINT8 column_state; UINT8 mask; UINT8 col, row; UINT8 combined; if (state & (KEY_STATE_UP | KEY_STATE_MOD_UP)) { return TRUE; // ignore up key events } for (col = 0; col < COLUMNS; ++col) { if (col != column_index) // exclude currently selected column { num_down_keys = 0; column_state = *(current_key_state_ptr + col); combined = column_state & this_column; if (combined) { // There are key downs in the same row // Now we need to count. // If more than 2 than we've got a ghost key. for (row = 0; row < ROWS; ++row) { mask = 0x01 << row; if (column_state & mask) { num_down_keys++; // count the number of down keys } } } if (1 < num_down_keys > 1) // more than 1 key down in this column. { // Now we need to figure out if the key in question is in the ghost situation. mask = 0x01 << this_row_bit; // look for any match with the asserted (key down) row if (combined & mask) // If it's one of the ghost keys { return FALSE; } } } } return TRUE; } //-------------------------------------------------------------------------- // send_keyboard_report //-------------------------------------------------------------------------- void send_keyboard_report(UINT8 report_size) { UINT8 keyboard_report[sizeof(APP_TX_PACKET)]; UINT8 i; // Save Keyboard report if (report_size == 0x0) { #ifdef NULL_PACKET_DATA // Handle NULL Packets keyboard_report[0x0] = *((UINT8 *) report_packet); #else // Nothing to send return; #endif } else { #ifdef ENCRYPT_DATA // Do not encrypt battery reports, keep alive, or up-key only packets if ((((*((UINT8 *) report_packet) != KEYBOARD_BATT_REPORT_TYPE) && (*((UINT8 *) report_packet) != KEYBOARD_ALIVE_REPORT_TYPE)) && !((*((UINT8 *) report_packet) == 0x00) && (report_size == 0x1)) )) { #ifdef MOUSE_EMULATION_MODE if (protocol_device_type != MOUSE_DEVICE_TYPE) #endif // MOUSE_EMULATION_MODE { // Encrypt data packet report_size = encrypt_data(report_size); } } #endif //ENCRYPT_DATA // Store Keyboard report for (i = 0; i < report_size; i++) { keyboard_report[i] = *((UINT8 *) report_packet + i); } } // Send Keyboard Report #ifdef KEYBOARD_TEST_MODES // Set status to skip the test mode during scanning in this loop hid.status |= SKIP_TEST_MODE; while (!(hid.status & BIND_BUTTON) && (!timer_time_elapsed(ts, KEYBOARD_TX_TIMEOUT) || ((test_mode != TEST_OFF) && !(test_mode & TEST_QUIT)))) #else while (!(hid.status & BIND_BUTTON) && !timer_time_elapsed(ts, KEYBOARD_TX_TIMEOUT)) #endif { // Clear Watchdog and Sleep M8C_ClearWDTAndSleep; // Transmit Keyboard Report i = protocol_send_packet(report_size); // Check for timeout or reconnect if (i == TX_TIMEOUT_ERR) { // Note: A reconnect destroys the content of the original report // Restore Keyboard report if (report_size == 0x0) { // Handle NULL Packets *((UINT8 *) report_packet) = keyboard_report[0x0]; } else { // Restore Keyboard report for (i = 0; i < report_size; i++) { *((UINT8 *) report_packet + i) = keyboard_report[i]; } } #ifdef TIMER_CAL // Disable bind interrupt isr_disable(INT_BIND); ISR_DISABLE(GPIO_ISR_BIND_IE_PORT, GPIO_ISR_BIND_INT); // Calibrate the tick timer (Assumes a reconnect) timer_calibrate_timer(TRUE); // Enable bind interrupt isr_enable(INT_BIND); ISR_ENABLE(GPIO_ISR_BIND_IE_PORT, GPIO_ISR_BIND_INT); #endif } if (i == ACK_DATA) { #ifdef KEYBOARD_BATTERY_VOLTAGE_SUPPORT // Set Send Battery Report hid.status |= BATT_REPORT_REQ; #endif break; } if (i == DATA_SENT) { break; } } #ifdef KEYBOARD_TEST_MODES // Clear status to skip the test mode hid.status &= ~SKIP_TEST_MODE; #endif } //-------------------------------------------------------------------------- // gen_alive_packet //-------------------------------------------------------------------------- void generate_alive_packet(void) { #ifdef NULL_PACKET_DATA // Indicate that is a keep alive packet report_packet->scan_report.modifier_keys = 0x02; // Send a keep alive report send_keyboard_report(0x0); #else // Indicate that is a keep alive packet report_packet->scan_report.modifier_keys = KEYBOARD_ALIVE_REPORT_TYPE; // Send a keep alive report send_keyboard_report(0x1); #endif } #ifdef KEYBOARD_MULTIMEDIA_SUPPORT //-------------------------------------------------------------------------- // generate_pwr_report //-------------------------------------------------------------------------- void generate_pwr_report(void) { UINT8 i; BOOL send_report = FALSE; // Initialize report report_packet->pwr_report.report_type = KEYBOARD_PWR_REPORT_TYPE; for (i=0; i<KEYBOARD_REPORT_NUM_PWRCODES; i++) { report_packet->pwr_report.pwrcode[i] = 0; } { KEY this_key; UINT8 device; UINT8 scan_code; UINT8 num_entries = 0; for (i = 0; i < KEY_QUEUE_LEN; ++i) { this_key.index = hid.key_queue[i].index; if (this_key.index == INVALID_INDEX) { continue; // don't do anything else this iteration. } device = default_keyboard_scan_table[this_key.index].device; if ((device & DEVICE_MASK) == DEVICE_3) { scan_code = 0; // Check for duplicate keys for (device = 0; device < i; device ++) { if (this_key.index == hid.key_queue[device].index) { scan_code = 1; } } // Exit loop if duplicate key if (scan_code == 1) { break; } this_key.state = hid.key_queue[i].state; device = default_keyboard_scan_table[this_key.index].device; scan_code = default_keyboard_scan_table[this_key.index].scan_code; if ((device & DEVICE_MASK) == DEVICE_3) { // Valid Hot key send_report = TRUE; //---- Process Hot Keys ---------------------------------- if (this_key.state == KEY_STATE_UP) { hid.key_queue[i].state = KEY_STATE_REMOVE; } else // KEY_STATE_DOWN { if (num_entries < KEYBOARD_REPORT_NUM_PWRCODES) { hid.key_queue[i].state &= ~KEY_STATE_DOWN; report_packet->pwr_report.pwrcode[num_entries] = scan_code; ++num_entries; } } } } // Eliminate trailing zeros in the report count // Note: Each entry is one byte and the report type is one byte i = num_entries + 1; } // Send report if (send_report) { // Send report send_keyboard_report(i); } } //-------------------------------------------------------------------------- // generate_hot_report //-------------------------------------------------------------------------- void generate_hot_report(void) { UINT8 i; BOOL send_report = FALSE; // Initialize report report_packet->hot_report.report_type = KEYBOARD_HOT_REPORT_TYPE; for (i=0; i<KEYBOARD_REPORT_NUM_HOTCODES; i++) { report_packet->hot_report.hotcode[i] = 0; } { KEY this_key; UINT8 device; UINT8 scan_code; UINT8 num_entries = 0; for (i = 0; i < KEY_QUEUE_LEN; ++i) { this_key.index = hid.key_queue[i].index; if (this_key.index == INVALID_INDEX) { continue; // don't do anything else this iteration. } device = default_keyboard_scan_table[this_key.index].device; if ((device & DEVICE_MASK) == DEVICE_2) { scan_code = 0; // Check for duplicate keys for (device = 0; device < i; device ++) { if (this_key.index == hid.key_queue[device].index) { scan_code = 1; } } // Exit loop if duplicate key if (scan_code == 1) { break; } this_key.state = hid.key_queue[i].state; device = default_keyboard_scan_table[this_key.index].device; scan_code = default_keyboard_scan_table[this_key.index].scan_code; if ((device & DEVICE_MASK) == DEVICE_2) { // Valid Hot key send_report = TRUE; //---- Process Hot Keys ---------------------------------- if (this_key.state == KEY_STATE_UP) { hid.key_queue[i].state = KEY_STATE_REMOVE; } else // KEY_STATE_DOWN { if (num_entries < KEYBOARD_REPORT_NUM_HOTCODES) { hid.key_queue[i].state &= ~KEY_STATE_DOWN; report_packet->hot_report.hotcode[num_entries] = device_2_keyboard_scan_table[scan_code]; ++num_entries; } } } } // Eliminate trailing zeros in the report count // Note: Each entry is two bytes and the report type is one byte i = (num_entries * 2) + 1; } // Send report if (send_report) { // Send report send_keyboard_report(i); } } #endif //KEYBOARD_MULTIMEDIA_SUPPORT #ifdef KEYBOARD_BATTERY_VOLTAGE_SUPPORT //-------------------------------------------------------------------------- // generate_batt_report //-------------------------------------------------------------------------- void generate_batt_report(void) { // Battery Report Requested? if (hid.status & BATT_REPORT_REQ) { // Clear Battery Request hid.status &= ~BATT_REPORT_REQ; // Disable Bind interrupt ISR_DISABLE(GPIO_ISR_BIND_IE_PORT, GPIO_ISR_BIND_INT); // Initialize report report_packet->batt_report.report_type = KEYBOARD_BATT_REPORT_TYPE; // Disable Bind interrupt isr_disable(INT_BIND); // Set battery voltage level value (upper nibble of status) report_packet->batt_report.battery_voltage_level = battery_status(); // Enable bind interrupt isr_enable(INT_BIND); ISR_ENABLE(GPIO_ISR_BIND_IE_PORT, GPIO_ISR_BIND_INT); // Transmit Keyboard Report send_keyboard_report(sizeof(KEYBOARD_BATT_REPORT)); } } #endif //KEYBOARD_BATTERY_VOLTAGE_SUPPORT //-------------------------------------------------------------------------- // generate_standard_report //-------------------------------------------------------------------------- void generate_standard_report() { UINT8 i; BOOL send_report = FALSE; // Initialize report report_packet->scan_report.modifier_keys = 0; for (i=0; i<KEYBOARD_REPORT_NUM_KEYCODES; i++) { report_packet->scan_report.keycode[i] = 0; } { KEY this_key; UINT8 device; UINT8 scan_code; UINT8 num_entries = 0; UINT8 modifiers = 0; for (i = 0; i < KEY_QUEUE_LEN; ++i) { this_key.index = hid.key_queue[i].index; if (this_key.index == INVALID_INDEX) { continue; // don't do anything else this iteration. } device = default_keyboard_scan_table[this_key.index].device; // Check for Standard 101 key #ifndef MOUSE_EMULATION_MODE if ( (device & DEVICE_MASK) == DEVICE_1 ) #else if (((device & DEVICE_MASK) == DEVICE_1) || (((device & DEVICE_MASK) == DEVICE_5) && !mouse_emulation)) #endif // MOUSE_EMULATION_MODE { //Check for down key entries before modifier key if ((0 < num_entries > 0) && (device & MODIFIER_KEY)) { // Down key entries located before a modifier must be sent before a modifier change break; } scan_code = 0; // Check for duplicate keys for (device = 0; device < i; device ++) { if (this_key.index == hid.key_queue[device].index) { // Check for the same modifier up and down key if (hid.key_queue[device].state & KEY_STATE_MOD_UP) { // Remove modifier down key hid.key_queue[device].state = KEY_STATE_REMOVE; } scan_code = 1; break; } } // Exit loop if duplicate key if (scan_code == 1) { break; } this_key.state = hid.key_queue[i].state; device = default_keyboard_scan_table[this_key.index].device; scan_code = default_keyboard_scan_table[this_key.index].scan_code; if ((device & DEVICE_MASK) == DEVICE_1) { // Valid Standard 101 key send_report = TRUE; //---- Process Modifier List ----------------------------- // check whether index is a modifier if (device & MODIFIER_KEY) { #ifdef KEYBOARD_TEST_MODES // Do not allow modifiers during tests if (test_mode != TEST_OFF) { hid.key_queue[i].state = KEY_STATE_REMOVE; } else #endif { if (this_key.state == KEY_STATE_UP) { modifiers &= ~scan_code; hid.key_queue[i].state = KEY_STATE_REMOVE; } else // KEY_STATE_DOWN { hid.key_queue[i].state &= ~KEY_STATE_DOWN; modifiers |= scan_code; } } report_packet->scan_report.modifier_keys = modifiers; } else { //---- Process Keyboard Scan ----------------------------- if (this_key.state == KEY_STATE_UP) { hid.key_queue[i].state = KEY_STATE_REMOVE; } else // KEY_STATE_DOWN { if (num_entries < KEYBOARD_REPORT_NUM_KEYCODES) { hid.key_queue[i].state &= ~KEY_STATE_DOWN; report_packet->scan_report.keycode[num_entries] = scan_code; ++num_entries; } } } } } // Swap Modifier and Scan Code 1 bytes // Note: this assumes the most common down key is a single // scan code without a modifier key. modifiers = report_packet->scan_report.modifier_keys; report_packet->scan_report.modifier_keys = report_packet->scan_report.keycode[0]; report_packet->scan_report.keycode[0] = modifiers; // Eliminate trailing zeros from the report count if ((num_entries == 0) || (num_entries == 1)) { i = num_entries; if (modifiers != 0x0) { i = 2; } } else { i = num_entries + 1; } } // Send report if (send_report) { #ifndef NULL_PACKET_DATA if (i == 0) { // Send a one byte packet (bytes is already 0x00) i = 1; } #endif // Transmit Keyboard Report send_keyboard_report(i); } } #ifdef MOUSE_EMULATION_MODE //-------------------------------------------------------------------------- // generate_mouse_report //-------------------------------------------------------------------------- void generate_mouse_report(void) { UINT8 i; BOOL send_report = FALSE; // Initialize report report_packet->mouse_report.x = 0; report_packet->mouse_report.y = 0; report_packet->mouse_report.combi = 0; { KEY this_key; UINT8 device; UINT8 scan_code; INT8 scroll_value = 0; for (i = 0; i < KEY_QUEUE_LEN; ++i) { this_key.index = hid.key_queue[i].index; if (this_key.index == INVALID_INDEX) { continue; // don't do anything else this iteration. } this_key.state = hid.key_queue[i].state; device = default_keyboard_scan_table[this_key.index].device; scan_code = default_keyboard_scan_table[this_key.index].scan_code; // Check for Scroll lock (the only DEVICE_4 key) to toggle mouse emulation mode if ((device & DEVICE_MASK) == DEVICE_4) { // Only change state once on a down key if (this_key.state == KEY_STATE_UP) { hid.key_queue[i].state = KEY_STATE_REMOVE; } else if (this_key.state == KEY_STATE_DOWN) { // Toggle mouse emulation mode mouse_emulation = !mouse_emulation; hid.key_queue[i].state &= ~KEY_STATE_DOWN; } } // Check for valid mouse "keys" if (((device & DEVICE_MASK) == DEVICE_5) && (mouse_emulation)) { // Valid mouse "key" send_report = TRUE; //---- Process Mouse "Keys" ---------------------------------- if (this_key.state == KEY_STATE_UP) { hid.key_queue[i].state = KEY_STATE_REMOVE; } else // KEY_STATE_DOWN { hid.key_queue[i].state &= ~KEY_STATE_DOWN; switch(scan_code) { case UP_ARROW_KEY: // Subtract Y movement report_packet->mouse_report.y -= 1; break; case DOWN_ARROW_KEY: // Accumulate Y movement report_packet->mouse_report.y += 1; break; case LEFT_ARROW_KEY: // Subtract X movement report_packet->mouse_report.x -= 1; break; case RIGHT_ARROW_KEY: // Accumulate X movement report_packet->mouse_report.x += 1; break; case PAGE_UP_KEY: // Scroll Up ++scroll_value; break; case PAGE_DOWN_KEY: // Scroll Down --scroll_value; break; case DELETE_KEY: // Buttons report_packet->mouse_report.combi |= MOUSE_LEFT_BUTTON; break; case END_KEY: // Buttons report_packet->mouse_report.combi |= MOUSE_RIGHT_BUTTON; break; default: // Remove Unknown key hid.key_queue[i].state = KEY_STATE_REMOVE; break; } } } } if ((scroll_value != 0x00) || (report_packet->mouse_report.combi != 0x00)) { report_packet->mouse_report.combi |= scroll_value & 0x1F; // Set report length to 3 i = 3; } else { // Set report length to 2 i = 2; } } // Send report if (send_report) { // Update timestamp last_transfer_ts = ts; // Change Device Type to Mouse protocol_device_type = MOUSE_DEVICE_TYPE; // Send report send_keyboard_report(i); // Change Device Type to Keyboard protocol_device_type = KEYBOARD_DEVICE_TYPE; } // Sort the key queue sort_key_queue(); } #endif // MOUSE_EMULATION_MODE //-------------------------------------------------------------------------- // generate_reports //-------------------------------------------------------------------------- BOOL void generate_reports(void) { if ( hid.status & (GENERATE_STD_REPORT | GENERATE_HOT_REPORT | GENERATE_PWR_REPORT) ) #ifdef MOUSE_EMULATION_MODE // Build and send mouse report generate_mouse_report(); #endif // MOUSE_EMULATION_MODE // Check for keyboard reports if (hid.status & (GENERATE_STD_REPORT | GENERATE_HOT_REPORT | GENERATE_PWR_REPORT)) { // Update timestamp last_transfer_ts = ts; if (hid.status & GENERATE_STD_REPORT) { // Reset generate standard report flag hid.status &= ~GENERATE_STD_REPORT; // Build and send Standard 101 report generate_standard_report(); } #ifdef KEYBOARD_MULTIMEDIA_SUPPORT if (hid.status & GENERATE_HOT_REPORT) { // Reset generate hot report flag hid.status &= ~GENERATE_HOT_REPORT; // Build and send Hot report generate_hot_report(); } if (hid.status & GENERATE_PWR_REPORT) { // Reset generate power report flag hid.status &= ~GENERATE_PWR_REPORT; // Build and send Power report generate_pwr_report(); } #endif // KEYBOARD_MULTIMEDIA_SUPPORT #ifdef KEYBOARD_BATTERY_VOLTAGE_SUPPORT // Build and send Battery Voltage report generate_batt_report(); #endif // KEYBOARD_BATTERY_VOLTAGE_SUPPORT // Sort the key queue sort_key_queue(); // return TRUE; } // return FALSE; } //-------------------------------------------------------------------------- // process_column //-------------------------------------------------------------------------- void process_column(UINT8 column_index, UINT8 * current_key_state_ptr) { UINT8 row; UINT8 last_row; UINT8 row_bit; UINT8 mask; KEY key; UINT8 device; row = *(current_key_state_ptr + column_index); last_row = hid.prior_key_state[column_index]; if (row == last_row) { return; } for (mask = 0x01, row_bit = 0; row_bit < 8; ++row_bit, mask <<= 1) { if ((row & mask) == (last_row & mask)) { continue; // No change } // Key change hid.status &= ~KEYBOARD_SLEEP; key.index = (column_index * 8) + row_bit; // calculate the index device = default_keyboard_scan_table[key.index].device; if (row & mask) // down key { key.state = KEY_STATE_DOWN; } else // up key { if (device & MODIFIER_KEY) { key.state = KEY_STATE_MOD_UP; } else { key.state = KEY_STATE_UP; } } #ifndef KEYBOARD_MULTIMEDIA_SUPPORT // Ignore HOT and Power keys if (((device & DEVICE_MASK) == DEVICE_2) || ((device & DEVICE_MASK) == DEVICE_3) || ((device & DEVICE_MASK) == NO_DEVICE)) { // Ignore Hot and Power keys continue; } #else // Ignore No Device keys if ((device & DEVICE_MASK) == NO_DEVICE) { continue; } #endif if (!debounce(key.index)) { continue; } if (!ghost(key.state, column_index, row, row_bit, current_key_state_ptr)) { continue; } if (!add_or_update_key_queue(key)) { continue; } // only update the state if everthing ok if (key.state & KEY_STATE_DOWN) { last_row |= mask; } else // KEY_STATE_UP { last_row &= ~mask; } #ifndef MOUSE_EMULATION_MODE if ((device & DEVICE_MASK) == DEVICE_1) #else if (((device & DEVICE_MASK) == DEVICE_1) || ((device & DEVICE_MASK) == DEVICE_4) || ((device & DEVICE_MASK) == DEVICE_5)) #endif // MOUSE_EMULATION_MODE { hid.status |= GENERATE_STD_REPORT; } #ifdef KEYBOARD_MULTIMEDIA_SUPPORT else if ((device & DEVICE_MASK) == DEVICE_2) { hid.status |= GENERATE_HOT_REPORT; } else if ((device & DEVICE_MASK) == DEVICE_3) { hid.status |= GENERATE_PWR_REPORT; } #endif hid.prior_key_state[column_index] = last_row; } // for } #ifdef KEYBOARD_TEST_MODES //-------------------------------------------------------------------------- // test_modes //-------------------------------------------------------------------------- UINT8 test_modes(void) { KEY key; if (test_mode != TEST_OFF) { if (!(hid.status & SKIP_TEST_MODE)) { // Do not let keyboard sleep hid.status &= ~KEYBOARD_SLEEP; // We need to process each character twice. Once as a down // key and once as an up key. if (test_mode & TEST_PANGRAM) key.index = sentence_table[sentence_table_index >> 1]; else key.index = X_INDEX; if (sentence_table_index & 0x01) { hid.status &= ~KEY_DOWN; key.state = KEY_STATE_UP; } else { hid.status |= KEY_DOWN; key.state = KEY_STATE_DOWN; } // Make sure a key is not down before stopping a test mode if (( test_mode & TEST_QUIT ) && ( hid.status & KEY_DOWN )) { test_mode = TEST_OFF; sentence_table_index = 0; } else { add_or_update_key_queue(key); // Increment and store the index (handle wrap if necessary) sentence_table_index = (++sentence_table_index == (sizeof(sentence_table) * 2)) ? 0 : sentence_table_index; hid.status |= GENERATE_STD_REPORT; } } return 1; } else { return 0; } } #endif // KEYBOARD_TEST_MODES //-------------------------------------------------------------------------- // scan_keyboard //-------------------------------------------------------------------------- void scan_keyboard(void) { UINT8 mask; UINT8 column_index; UINT8 current_key_state[COLUMNS]; #ifdef KEYBOARD_FAST_SCAN UINT8 lo_col = 0x00; UINT8 mid_col = 0x00; UINT8 hi_col = 0x00; // Drive the KB Rows low KB_ROW_PORT = 0x00; // Set the KB Columns high (pull-up Resistor) KB_LOW_COL_PORT = COL1_MASK | COL2_MASK | COL3_MASK | COL4_MASK | COL5_MASK | COL6_MASK | COL7_MASK | COL8_MASK; KB_MID_COL_PORT = COL9_MASK | COL10_MASK | COL11_MASK | COL12_MASK | COL13_MASK | COL14_MASK | COL15_MASK | COL16_MASK; KB_HI_COL_PORT = COL17_MASK | COL18_MASK; // Wait 10uS timer_delay_10_usec(); // Read Columns lo_col = ~KB_LOW_COL_PORT; mid_col = ~KB_MID_COL_PORT; hi_col = ~KB_HI_COL_PORT; // Set the KB Column values to low KB_LOW_COL_PORT = 0; KB_MID_COL_PORT = 0; KB_HI_COL_PORT = 0; // Set the KB Rows high (pull-up Resistor) KB_ROW_PORT = 0xFF; #endif // Change Columns to High-z KB_LOW_COL_PORT_DM0 = 0x0; KB_MID_COL_PORT_DM0 = 0x0; KB_HI_COL_PORT_DM0 = KB_HI_COL_PORT_DRV0 & ~(COL17_MASK | COL18_MASK); // Loop through Columns for (column_index = 0; column_index < COLUMNS; ++column_index) { #ifdef KEYBOARD_FAST_SCAN current_key_state[column_index] = 0x00; #endif if (column_index < 8) { // Columns 1 - 8 mask = 0x1 << column_index; #ifdef KEYBOARD_FAST_SCAN // Check prescan if (lo_col & mask) #endif { // Drive Column low KB_LOW_COL_PORT_DM0 = mask; // Wait 10uS timer_delay_10_usec(); // Read Row current_key_state[column_index] = ~KB_ROW_PORT; // Change Column back to High-z KB_LOW_COL_PORT_DM0 = 0x0; } } else if (column_index < 16) { // Columns 9 - 16 mask = 0x1 << (column_index - 8); #ifdef KEYBOARD_FAST_SCAN // Check prescan if (mid_col & mask) #endif { // Drive Column low KB_MID_COL_PORT_DM0 = mask; // Wait 10uS timer_delay_10_usec(); // Read Row current_key_state[column_index] = ~KB_ROW_PORT; // Change Column back to High-z KB_MID_COL_PORT_DM0 = 0x0; } } else { // Columns 17 and 18 mask = 0x1 << (column_index - 16); #ifdef KEYBOARD_FAST_SCAN // Check prescan if (hi_col & mask) #endif { // Drive Column low KB_HI_COL_PORT_DM0 = mask; // Wait 10uS timer_delay_10_usec(); // Read Row current_key_state[column_index] = ~KB_ROW_PORT; // Change Column back to High-z KB_HI_COL_PORT_DM0 = KB_HI_COL_PORT_DRV0 & ~(COL17_MASK | COL18_MASK); } } } // Change Columns back to resistive pullup KB_LOW_COL_PORT_DM0 = KB_LOW_COL_PORT_DRV0; KB_MID_COL_PORT_DM0 = KB_MID_COL_PORT_DRV0; KB_HI_COL_PORT_DM0 = KB_HI_COL_PORT_DRV0; mask = KB_ROW_PORT; #ifdef KEYBOARD_TEST_MODES // Check for both right and left CTRL and ALT keys if (((current_key_state[ALT_COL] & ALT_ROW_MASK) == ALT_ROW_MASK) && ((current_key_state[CTRL_COL] & CTRL_ROW_MASK) == CTRL_ROW_MASK)) { // Check for "F1" to enable the pangram test mode if (current_key_state[F1_COL] & F1_ROW_MASK) { if (test_mode == TEST_OFF) { // Turn pangram test mode on test_mode = TEST_PANGRAM; } // Clear key current_key_state[F1_COL] &= ~F1_ROW_MASK; } // Check for "F3" to enable the fixed character test mode if (current_key_state[F3_COL] & F3_ROW_MASK) { if (test_mode == TEST_OFF) { // Turn fixed character test mode on test_mode = TEST_FIXED_CHAR; } // Clear key current_key_state[F3_COL] &= ~F3_ROW_MASK; } // Check for "F2" key to disable the test modes if (current_key_state[F2_COL] & F2_ROW_MASK) { // Turn test modes off test_mode |= TEST_QUIT; // Clear key current_key_state[F2_COL] &= ~F2_ROW_MASK; } } // Check for "Escape" key to disable the test modes if (test_mode != TEST_OFF) { if (current_key_state[ESC_COL] & ESC_ROW_MASK) { // Turn test modes off test_mode |= TEST_QUIT; // Clear key current_key_state[ESC_COL] &= ~ESC_ROW_MASK; } } // Process the newly read key map if (!test_modes()) #endif //KEYBOARD_TEST_MODES { for (column_index = 0; column_index < COLUMNS; ++column_index) { process_column(column_index, ¤t_key_state[0]); if (hid.prior_key_state[column_index] != 0x00) { hid.status |= KEY_DOWN; hid.status &= ~KEYBOARD_SLEEP; } } } } //-------------------------------------------------------------------------- // keyboard_init //-------------------------------------------------------------------------- void keyboard_init(void) { UINT8 col; // Initialize isr routine isr_init(); // Initialize tick timer timer_init(); // Initialize Bind button RADIO_PORT |= SW1_MASK; // Set the KB Rows high (pull-up Resistor) KB_ROW_PORT = ROW1_MASK | ROW2_MASK | ROW3_MASK | ROW4_MASK | ROW5_MASK | ROW6_MASK | ROW7_MASK | ROW8_MASK; // Drive the KB Columns low KB_LOW_COL_PORT = 0; KB_MID_COL_PORT = 0; KB_HI_COL_PORT = 0; // reset the saved state for (col = 0; col < COLUMNS; ++col) { hid.prior_key_state[col] = 0x00; } //---- Setup keyboard initial state -------------------------- // intialize keyboard locals key_queue_init(); // Initialize debounce queue debounce_init(); // Initialize keyboard status hid.status = 0x0; // Initialize Protocol protocol_init(); // Get transfer packet report_packet = (APP_TX_PACKET *)protocol_get_tx_pkt(); #ifdef MFG_ENTER_BY_KEY_NOT_PIN // Change Columns to High-z KB_LOW_COL_PORT_DM0 = 0x0; KB_MID_COL_PORT_DM0 = 0x0; KB_HI_COL_PORT_DM0 = KB_HI_COL_PORT_DRV0 & ~(COL17_MASK | COL18_MASK); col = SLEEP_COL; // Check for system sleep key if (col < 8) { // Drive Column low KB_LOW_COL_PORT_DM0 = 0x1 << col; // Wait 10uS timer_delay_10_usec(); // Read Row col = ~KB_ROW_PORT; // Change Column back to High-z KB_LOW_COL_PORT_DM0 = 0x0; } else if (col < 16) { // Drive Column low KB_MID_COL_PORT_DM0 = 0x1 << (col - 8); // Wait 10uS timer_delay_10_usec(); // Read Row col = ~KB_ROW_PORT; // Change Column back to High-z KB_MID_COL_PORT_DM0 = 0x0; } else { // Drive Column low KB_HI_COL_PORT_DM0 = 0x1 << (col - 16); // Wait 10uS timer_delay_10_usec(); // Read Row col = ~KB_ROW_PORT; // Change Column back to High-z KB_HI_COL_PORT_DM0 = KB_HI_COL_PORT_DRV0 & ~(COL17_MASK | COL18_MASK); } // Change Columns back to resistive pullup KB_LOW_COL_PORT_DM0 = KB_LOW_COL_PORT_DRV0; KB_MID_COL_PORT_DM0 = KB_MID_COL_PORT_DRV0; KB_HI_COL_PORT_DM0 = KB_HI_COL_PORT_DRV0; // Check for bind button and system sleep key on power up if ((SW1_MASK & ~RADIO_PORT) && (col & SLEEP_ROW_MASK)) { // Manufacturing test (Does not return) MFG_TEST(); } #else // Check for Manufacturing Pin MFG_PIN_CHECK(); #endif #ifdef KEYBOARD_POWER_ON_BIND { SYS_PARAMETERS systmp; NVRAM_READ( &systmp, sizeof(SYS_PARAMETERS) ); if (systmp.networkId.signature != SIGNATURE_BYTE) { hid.status |= BIND_BUTTON; } } #endif // Enable bind interrupt isr_enable(INT_BIND); ISR_ENABLE(GPIO_ISR_BIND_IE_PORT, GPIO_ISR_BIND_INT); } //-------------------------------------------------------------------------- // main //-------------------------------------------------------------------------- void main(void) { // Keyboard Initialization keyboard_init(); // Initialize last transfer time stamp last_transfer_ts = timer_get_time_stamp(); // Main loop for(;;) { // Disable key interrupt isr_disable(INT_KEYS); ISR_DISABLE(GPIO_ISR_KEYS_IE_PORT, GPIO_ISR_KEYS_INT); // Disable Sleep interrupt mask M8C_DisableIntMask(INT_MSK0, INT_MSK0_SLEEP); // Clear Watchdog and Sleep M8C_ClearWDTAndSleep; hid.status &= ~KEY_DOWN; hid.status |= KEYBOARD_SLEEP; // Get current timestamp ts = timer_get_time_stamp(); #ifndef BIND_BASIC // Check for bind button if ( hid.status & BIND_BUTTON ) { // Call protocol bind // Note: This routine can take 5 or more seconds. protocol_bind(); // Clear bind flag hid.status &= ~BIND_BUTTON; } #endif // BIND_BASIC // Disable Sleep interrupt mask M8C_DisableIntMask(INT_MSK0, INT_MSK0_SLEEP); // Clear Watchdog and Sleep M8C_ClearWDTAndSleep; // Scan Keyboard scan_keyboard(); //---------------------------------------- Generate Report generate_reports(); #if 0 if( generate_reports() ) { // Set last transfer time stamp last_transfer_ts = timer_get_time_stamp(); } #endif //--------------------------------------------- Keep Alive if ( hid.status & KEY_DOWN ) { // Determine if it is necessary to send a Keep Alive if (timer_time_elapsed(last_transfer_ts, KEYBOARD_KEEP_ALIVE_TIMEOUT)) { // Generate keep alive generate_alive_packet(); #ifdef KEYBOARD_BATTERY_VOLTAGE_SUPPORT // Build and send Battery Voltage report generate_batt_report(); #endif // KEYBOARD_BATTERY_VOLTAGE_SUPPORT // Prevent "Tick" rollover by turning the timer on/off ts = 0; last_transfer_ts = 0; timer_timer_off(); timer_timer_on(); } } // age the debounce queue age_debounce_queue(); // Enable Sleep interrupt mask M8C_EnableIntMask(INT_MSK0, INT_MSK0_SLEEP); // Enable key interrupt before trying to sleep isr_enable(INT_KEYS); ISR_ENABLE(GPIO_ISR_KEYS_IE_PORT, GPIO_ISR_KEYS_INT); // Try to sleep between down keys if (( hid.status & KEYBOARD_SLEEP ) && !( hid.status & (GENERATE_STD_REPORT | GENERATE_HOT_REPORT | GENERATE_PWR_REPORT) )) { // Disable timer timer_timer_off(); // Clear transfer time stamp last_transfer_ts = 0; // Sleep until an interrupt (key press) while ( hid.status & KEYBOARD_SLEEP) { // Clear Watchdog and Sleep M8C_ClearWDTAndSleep; // Sleep M8C_Sleep; asm("nop"); } // Enable timer timer_timer_on(); } else { #ifdef KEYBOARD_TEST_MODES if (test_mode != TEST_OFF) { // Sleep until sample period expires timer_delay_incremental( ts, KEYBOARD_TEST_MODE_PERIOD ); // Prevent "Tick" rollover by turning the timer on/off last_transfer_ts = 0; timer_timer_off(); timer_timer_on(); } else #endif //KEYBOARD_TEST_MODES { // Sleep until sample period expires timer_delay_incremental( ts, KEY_DOWN_DELAY_SAMPLE_PERIOD ); } } } }