| /***************************************************************************** |
| * Copyright (c) 2013 IBM Corporation |
| * All rights reserved. |
| * This program and the accompanying materials |
| * are made available under the terms of the BSD License |
| * which accompanies this distribution, and is available at |
| * http://www.opensource.org/licenses/bsd-license.php |
| * |
| * Contributors: |
| * IBM Corporation - initial implementation |
| *****************************************************************************/ |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <termctrl.h> |
| |
| #include "usb-core.h" |
| #include "usb-key.h" |
| |
| /* |
| * HID Spec Version 1.11 |
| */ |
| |
| #define HID_REQ_GET_REPORT 0x01 |
| #define HID_REQ_GET_IDLE 0x02 |
| #define HID_REQ_GET_PROTOCOL 0x03 |
| #define HID_REQ_SET_REPORT 0x09 |
| #define HID_REQ_SET_IDLE 0x0A |
| #define HID_REQ_SET_PROTOCOL 0x0B |
| |
| //#define KEY_DEBUG |
| |
| /* HID SPEC - 7.2.6 Set_Protocol Request */ |
| static int usb_hid_set_protocol(struct usb_dev *dev, uint16_t value) |
| { |
| struct usb_dev_req req; |
| if (!dev) |
| return false; |
| req.bmRequestType = REQT_TYPE_CLASS | REQT_REC_INTERFACE | REQT_DIR_OUT; |
| req.bRequest = HID_REQ_SET_PROTOCOL; |
| req.wValue = cpu_to_le16(value); |
| req.wIndex = cpu_to_le16(dev->intf_num); |
| req.wLength = 0; |
| return usb_send_ctrl(dev->control, &req, NULL); |
| } |
| |
| /* HID SPEC - 7.2.4 Set_Idle Request */ |
| static int usb_hid_set_idle(struct usb_dev *dev, uint16_t ms_delay) |
| { |
| struct usb_dev_req req; |
| if (!dev) |
| return false; |
| req.bmRequestType = REQT_TYPE_CLASS | REQT_REC_INTERFACE | REQT_DIR_OUT; |
| req.bRequest = HID_REQ_SET_IDLE; |
| req.wValue = cpu_to_le16((ms_delay/4) << 8); |
| req.wIndex = cpu_to_le16(dev->intf_num); |
| req.wLength = 0; |
| return usb_send_ctrl(dev->control, &req, NULL); |
| } |
| |
| /* HID SPEC - 7.2.1 Get Report Request */ |
| static int usb_hid_get_report(struct usb_dev *dev, void *data, size_t size) |
| { |
| struct usb_dev_req req; |
| if (!dev) |
| return false; |
| req.bmRequestType = REQT_TYPE_CLASS | REQT_REC_INTERFACE | REQT_DIR_IN; |
| req.bRequest = HID_REQ_GET_REPORT; |
| req.wIndex = cpu_to_le16(dev->intf_num); |
| |
| req.wLength = cpu_to_le16((uint16_t)size); |
| req.wValue = cpu_to_le16(1 << 8); |
| return usb_send_ctrl(dev->control, &req, data); |
| } |
| |
| /* ring buffer with RD/WR indices for key buffering */ |
| static uint8_t keybuf[256]; /* size fixed to byte range ! */ |
| uint8_t r_ptr = 0; /* RD-index for Keyboard-Buffer */ |
| uint8_t w_ptr = 0; /* WR-index for Keyboard-Buffer */ |
| |
| /* variables for LED status */ |
| uint8_t set_leds; |
| const uint8_t *key_std = NULL; |
| const uint8_t *key_std_shift = NULL; |
| |
| /** |
| * read character from Keyboard-Buffer |
| * |
| * @param - |
| * @return > 0 Keycode |
| * = 0 if no key available |
| */ |
| static int read_key(void) |
| { |
| if (r_ptr != w_ptr) |
| return (int)keybuf[r_ptr++]; |
| else |
| return false; |
| } |
| |
| /** |
| * Store character into Keyboard-Buffer |
| * |
| * @param Key = detected ASCII-Key (> 0) |
| * @return - |
| */ |
| static void write_key(uint8_t key) |
| { |
| if ((w_ptr + 1) != r_ptr) |
| keybuf[w_ptr++] = key; |
| } |
| |
| /** |
| * Convert keyboard usage-ID to ANSI-Code |
| * |
| * @param Ctrl=Modifier Byte |
| * Key =Usage ID from USB Keyboard |
| * @return - |
| */ |
| static void get_char(uint8_t ctrl, uint8_t keypos) |
| { |
| uint8_t ch; |
| |
| #ifdef KEY_DEBUG |
| printf("pos %02X\n", keypos); |
| #endif |
| |
| if (set_leds & LED_CAPS_LOCK) /* is CAPS Lock set ? */ |
| ctrl |= MODIFIER_SHIFT; /* simulate shift */ |
| |
| if (ctrl == 0) { |
| ch = key_std[keypos]; |
| if (ch != 0) |
| write_key(ch); |
| return; |
| } |
| |
| if (ctrl & MODIFIER_SHIFT) { |
| ch = key_std_shift[keypos]; |
| if (ch != 0) |
| write_key(ch); |
| return; |
| } |
| |
| if (ctrl & MODIFIER_CTRL) { |
| ch = keycodes_ctrl[keypos]; |
| if (ch != 0) |
| write_key(ch); |
| return; |
| } |
| |
| if (ctrl == MODIFIER_ALT_GR) { |
| ch = keycodes_alt_GR[keypos]; |
| if (ch != 0) |
| write_key(ch); |
| return; |
| } |
| } |
| |
| static void check_key_code(uint8_t *buf) |
| { |
| static uint8_t key_last[6]; /* list of processed keys */ |
| uint8_t i, j, key_pos; |
| |
| /* set translation table to defaults */ |
| if ((key_std == NULL) || (key_std_shift == NULL)) { |
| key_std = keycodes_std_US; |
| key_std_shift = keycodes_shift_US; |
| } |
| |
| if (buf[0] & MODIFIER_SHIFT) /* any shift key pressed ? */ |
| set_leds &= ~LED_CAPS_LOCK; /* CAPS-LOCK-LED always off */ |
| |
| i = 2; /* skip modifier byte and reserved byte */ |
| while (i < 8) { |
| key_pos = buf[i]; |
| if ((key_pos != 0) && (key_pos <= 100)) { /* support for 101 keys */ |
| j = 0; |
| /* search if already processed */ |
| while ((j < 6) && (key_pos != key_last[j])) |
| j++; |
| |
| if (j >= 6) { /* not found (= not processed) */ |
| switch (key_pos) { |
| case 0x39: /* caps-lock key ? */ |
| case 0x32: /* caps-lock key ? */ |
| set_leds ^= LED_CAPS_LOCK; |
| break; |
| |
| case 0x3a: /* F1 */ |
| write_key(0x1b); |
| write_key(0x5b); |
| write_key(0x31); |
| write_key(0x31); |
| write_key(0x7e); |
| break; |
| |
| case 0x3b: /* F2 */ |
| write_key(0x1b); |
| write_key(0x5b); |
| write_key(0x31); |
| write_key(0x32); |
| write_key(0x7e); |
| break; |
| |
| case 0x3c: |
| write_key(0x1b); /* F3 */ |
| write_key(0x5b); |
| write_key(0x31); |
| write_key(0x33); |
| write_key(0x7e); |
| break; |
| |
| case 0x3d: |
| write_key(0x1b); /* F4 */ |
| write_key(0x5b); |
| write_key(0x31); |
| write_key(0x34); |
| write_key(0x7e); |
| break; |
| |
| case 0x3e: |
| write_key(0x1b); /* F5 */ |
| write_key(0x5b); |
| write_key(0x31); |
| write_key(0x35); |
| write_key(0x7e); |
| break; |
| |
| case 0x3f: |
| write_key(0x1b); /* F6 */ |
| write_key(0x5b); |
| write_key(0x31); |
| write_key(0x37); |
| write_key(0x7e); |
| break; |
| |
| case 0x40: |
| write_key(0x1b); /* F7 */ |
| write_key(0x5b); |
| write_key(0x31); |
| write_key(0x38); |
| write_key(0x7e); |
| break; |
| |
| case 0x41: |
| write_key(0x1b); /* F8 */ |
| write_key(0x5b); |
| write_key(0x31); |
| write_key(0x39); |
| write_key(0x7e); |
| break; |
| |
| case 0x42: |
| write_key(0x1b); /* F9 */ |
| write_key(0x5b); |
| write_key(0x31); |
| write_key(0x30); |
| write_key(0x7e); |
| break; |
| |
| case 0x43: |
| write_key(0x1b); /* F10 */ |
| write_key(0x5b); |
| write_key(0x31); |
| write_key(0x31); |
| write_key(0x7e); |
| break; |
| |
| case 0x44: |
| write_key(0x1b); /* F11 */ |
| write_key(0x5b); |
| write_key(0x31); |
| write_key(0x33); |
| write_key(0x7e); |
| break; |
| |
| case 0x45: |
| write_key(0x1b); /* F12 */ |
| write_key(0x5b); |
| write_key(0x31); |
| write_key(0x34); |
| write_key(0x7e); |
| break; |
| |
| case 0x47: /* scroll-lock key ? */ |
| set_leds ^= LED_SCROLL_LOCK; |
| break; |
| |
| case 0x49: |
| write_key(0x1b); /* INS */ |
| write_key(0x5b); |
| write_key(0x31); |
| write_key(0x7e); |
| break; |
| |
| case 0x4a: |
| write_key(0x1b); /* HOME */ |
| write_key(0x5b); |
| write_key(0x32); |
| write_key(0x7e); |
| break; |
| |
| case 0x4b: |
| write_key(0x1b); /* PgUp */ |
| write_key(0x5b); |
| write_key(0x33); |
| write_key(0x7e); |
| break; |
| |
| case 0x4c: |
| write_key(0x1b); /* DEL */ |
| write_key(0x5b); |
| write_key(0x34); |
| write_key(0x7e); |
| break; |
| |
| case 0x4d: |
| write_key(0x1b); /* END */ |
| write_key(0x5b); |
| write_key(0x35); |
| write_key(0x7e); |
| break; |
| |
| case 0x4e: |
| write_key(0x1b); /* PgDn */ |
| write_key(0x5b); |
| write_key(0x36); |
| write_key(0x7e); |
| break; |
| |
| case 0x4f: |
| write_key(0x1b); /* R-Arrow */ |
| write_key(0x5b); |
| write_key(0x43); |
| break; |
| |
| case 0x50: |
| write_key(0x1b); /* L-Arrow */ |
| write_key(0x5b); |
| write_key(0x44); |
| break; |
| |
| case 0x51: |
| write_key(0x1b); /* D-Arrow */ |
| write_key(0x5b); |
| write_key(0x42); |
| break; |
| |
| case 0x52: |
| write_key(0x1b); /* U-Arrow */ |
| write_key(0x5b); |
| write_key(0x41); |
| break; |
| |
| case 0x53: /* num-lock key ? */ |
| set_leds ^= LED_NUM_LOCK; |
| break; |
| |
| default: |
| /* convert key position to ASCII code */ |
| get_char(buf[0], key_pos); |
| break; |
| } |
| } |
| } |
| i++; |
| } |
| /*****************************************/ |
| /* all keys are processed, create a copy */ |
| /* to flag them as processed */ |
| /*****************************************/ |
| for (i = 2, j = 0; j < 6; i++, j++) |
| key_last[j] = buf[i]; /* copy all actual keys to last */ |
| } |
| |
| #define USB_HID_SIZE 128 |
| uint32_t *kbd_buffer; |
| |
| int usb_hid_kbd_init(struct usb_dev *dev) |
| { |
| int i; |
| uint8_t key[8]; |
| |
| usb_hid_set_protocol(dev, 0); |
| usb_hid_set_idle(dev, 500); |
| |
| memset(key, 0, 8); |
| if (usb_hid_get_report(dev, key, 8)) |
| check_key_code(key); |
| |
| kbd_buffer = SLOF_dma_alloc(USB_HID_SIZE); |
| if (!kbd_buffer) { |
| printf("%s: unable to allocate keyboard buffer\n", __func__); |
| return false; |
| } |
| |
| #ifdef KEY_DEBUG |
| printf("HID kbd init %d\n", dev->ep_cnt); |
| #endif |
| for (i = 0; i < dev->ep_cnt; i++) { |
| if ((dev->ep[i].bmAttributes & USB_EP_TYPE_MASK) |
| == USB_EP_TYPE_INTR) |
| usb_dev_populate_pipe(dev, &dev->ep[i], kbd_buffer, USB_HID_SIZE); |
| } |
| return true; |
| } |
| |
| int usb_hid_kbd_exit(struct usb_dev *dev) |
| { |
| if (dev->intr) { |
| usb_put_pipe(dev->intr); |
| dev->intr = NULL; |
| } |
| SLOF_dma_free(kbd_buffer, USB_HID_SIZE); |
| return true; |
| } |
| |
| unsigned char usb_key_available(void *dev) |
| { |
| if (dev && r_ptr != w_ptr) |
| return true; |
| else |
| return false; |
| } |
| |
| unsigned char usb_read_keyb(void *vdev) |
| { |
| struct usb_dev *dev = vdev; |
| uint8_t key[8]; |
| |
| if (!dev) |
| return false; |
| |
| memset(key, 0, 8); |
| while (usb_poll_intr(dev->intr, key)) { |
| check_key_code(key); |
| memset(key, 0, 8); |
| } |
| return read_key(); |
| } |