]> git.armaanb.net Git - atreides.git/commitdiff
Add C firmware.
authorPhil Hagelberg <technomancy@gmail.com>
Fri, 25 Apr 2014 14:50:11 +0000 (07:50 -0700)
committerPhil Hagelberg <technomancy@gmail.com>
Fri, 25 Apr 2014 14:50:11 +0000 (07:50 -0700)
firmware/.gitignore [new file with mode: 0644]
firmware/Makefile [new file with mode: 0644]
firmware/atreus.c [new file with mode: 0644]
firmware/usb_keyboard.c [new file with mode: 0644]
firmware/usb_keyboard.h [new file with mode: 0644]

diff --git a/firmware/.gitignore b/firmware/.gitignore
new file mode 100644 (file)
index 0000000..a7c2cd1
--- /dev/null
@@ -0,0 +1,3 @@
+*.o
+atreus
+atreus.hex
\ No newline at end of file
diff --git a/firmware/Makefile b/firmware/Makefile
new file mode 100644 (file)
index 0000000..2ea7aee
--- /dev/null
@@ -0,0 +1,15 @@
+MCU=atmega32u4
+
+F_CPU=16000000
+
+TARGET=atreus
+
+build:
+       avr-gcc -std=gnu99 -Os -D F_CPU=$(F_CPU)UL -mmcu=$(MCU) -c -o $(TARGET).o $(TARGET).c
+       avr-gcc -std=gnu99 -Os -D F_CPU=$(F_CPU)UL -mmcu=$(MCU) -c -o usb_keyboard.o usb_keyboard.c
+       avr-gcc -mmcu=$(MCU) usb_keyboard.o $(TARGET).o -o $(TARGET)
+       avr-size $(TARGET)
+       avr-objcopy -O ihex -R .eeprom $(TARGET) $(TARGET).hex
+
+upload: build
+       teensy_loader_cli -w -mmcu=$(MCU) $(TARGET).hex
diff --git a/firmware/atreus.c b/firmware/atreus.c
new file mode 100644 (file)
index 0000000..4e2bb75
--- /dev/null
@@ -0,0 +1,131 @@
+#include <stdlib.h>
+#include <avr/io.h>
+#include <util/delay.h>
+#include "usb_keyboard.h"
+
+#define ROW_COUNT 4
+#define COL_COUNT 11
+
+#define FN_PRESSED (~PINB & 128)
+
+#define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
+
+void reset(void);
+
+\f
+
+// Outputs
+// |------------+----+----+----+----|
+// | row number |  0 |  1 |  2 |  3 |
+// |------------+----+----+----+----|
+// | pin number | D0 | D1 | D2 | D3 |
+// |------------+----+----+----+----|
+
+// Inputs
+// |---------------+----+----+----+----+----+----+----+----+----+----+----|
+// | column number |  0 |  1 |  2 |  3 |  4 |  5 |  6 |  7 |  8 |  9 | 10 |
+// |---------------+----+----+----+----+----+----+----+----+----+----+----|
+// | pin number    | B0 | B1 | B2 | B3 | B4 | B5 | B6 | B7 | F4 | F5 | F6 |
+// |---------------+----+----+----+----+----+----+----+----+----+----+----|
+
+\f
+
+// numbers under 100: normal keys
+// numbers between 100 and 200: shifted keys
+// numbers over 200: function invocations
+
+int base_layout[ROW_COUNT][COL_COUNT] = \
+  { {20, 26, 8,   21,  23,  0,  28, 24, 12, 18, 19},      \
+    {4,  22, 7,   9,   10,  0,  11, 13, 14, 15, 51},      \
+    {29, 27, 6,   25,  5,  101, 17, 16, 54, 55, 56},      \
+    {41, 43, 108, 102, 42, 104, 44, 0,  52, 47, 40} };
+
+int fn_layout[ROW_COUNT][COL_COUNT] = \
+  { {108+30, 108+31, 108+45, 108+46, 108 + 49, 0, 75, 36, 37, 38, 108+37}, \
+    {108+32, 108+33, 108+38, 108+39, 53, 0, 78, 33, 34, 35, 108+46},    \
+    {108 + 34, 108 + 35, 45, 46, 108+53, 101, 49, 30, 31, 32, 108+47},  \
+    {255, 108 + 73, 108, 102, 0, 104, 0, 0, 55, 39, 48} };
+
+int *current_row;
+
+int pressed_count = 0;
+
+\f
+
+void press(int keycode) {
+  if(!keycode || pressed_count >= 6) return;
+  if(keycode == 255 || keycode == 5) {
+    reset(); // TODO: make a table of codes -> functions
+  } else if(keycode > 108) {
+    keyboard_modifier_keys |= KEY_SHIFT;
+    keyboard_keys[pressed_count++] = (keycode - 108);
+  } else if(keycode > 100) {
+    keyboard_modifier_keys |= (keycode - 100);
+  } else {
+    keyboard_keys[pressed_count++] = keycode;
+  };
+};
+
+void activate_row(int row) {
+  PORTD = (char)(~(1 << row)) | 32; // leave the LED on
+};
+
+void scan_row(int row) {
+  int cols = (~(PINB + ((PINF >> 4) << 8))) & 2047;
+  for(int col = 0; col < 8; col++) {
+    if(cols & 1) {
+      press(current_row[col]);
+    };
+    cols = cols >> 1;
+  };
+};
+
+\f
+
+void init() {
+  CPU_PRESCALE(0);
+  DDRD = 255;
+  DDRB = 0;
+  DDRF = 0;
+  PORTB = 255;
+  PORTF = 255;
+  usb_init();
+  while (!usb_configured()) /* wait */ ;
+  _delay_ms(1000);
+};
+
+void clear_keys() {
+  keyboard_modifier_keys = 0;
+  pressed_count = 0;
+  for(int i = 0; i < 6; i++) {
+    keyboard_keys[i] = 0;
+  };
+};
+
+int main() {
+  init();
+  while(1) {
+    // 4th row is still active from last scan
+    current_row = FN_PRESSED ? fn_layout : base_layout;
+    for(int i = 0; i < ROW_COUNT; i++) {
+      activate_row(i);
+      scan_row(i);
+      current_row += COL_COUNT;
+      _delay_us(50);
+    };
+    usb_keyboard_send();
+    clear_keys();
+  };
+};
+
+void reset(void) {
+  UDCON = 1;
+  USBCON = (1<<FRZCLK);
+  UCSR1B = 0;
+  _delay_ms(5);
+  EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0;
+  TIMSK0 = 0; TIMSK1 = 0; TIMSK3 = 0; TIMSK4 = 0; UCSR1B = 0; TWCR = 0;
+  DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0; TWCR = 0;
+  PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0;
+  asm volatile("jmp 0x7E00");
+};
diff --git a/firmware/usb_keyboard.c b/firmware/usb_keyboard.c
new file mode 100644 (file)
index 0000000..0b80bdf
--- /dev/null
@@ -0,0 +1,590 @@
+/* USB Keyboard Example for Teensy USB Development Board
+ * http://www.pjrc.com/teensy/usb_keyboard.html
+ * Copyright (c) 2009 PJRC.COM, LLC
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+// Version 1.0: Initial Release
+// Version 1.1: Add support for Teensy 2.0
+
+#define USB_SERIAL_PRIVATE_INCLUDE
+#include "usb_keyboard.h"
+
+/**************************************************************************
+ *
+ *  Configurable Options
+ *
+ **************************************************************************/
+
+// You can change these to give your code its own name.
+#define STR_MANUFACTURER    L"Technomancy"
+#define STR_PRODUCT         L"Orestes"
+
+
+// Mac OS-X and Linux automatically load the correct drivers.  On
+// Windows, even though the driver is supplied by Microsoft, an
+// INF file is needed to load the driver.  These numbers need to
+// match the INF file.
+#define VENDOR_ID       0x16C0
+#define PRODUCT_ID      0x047C
+
+
+// USB devices are supposed to implment a halt feature, which is
+// rarely (if ever) used.  If you comment this line out, the halt
+// code will be removed, saving 102 bytes of space (gcc 4.3.0).
+// This is not strictly USB compliant, but works with all major
+// operating systems.
+#define SUPPORT_ENDPOINT_HALT
+
+
+
+/**************************************************************************
+ *
+ *  Endpoint Buffer Configuration
+ *
+ **************************************************************************/
+
+#define ENDPOINT0_SIZE      32
+
+#define KEYBOARD_INTERFACE  0
+#define KEYBOARD_ENDPOINT   3
+#define KEYBOARD_SIZE       8
+#define KEYBOARD_BUFFER     EP_DOUBLE_BUFFER
+
+static const uint8_t PROGMEM endpoint_config_table[] = {
+  0,
+  0,
+  1, EP_TYPE_INTERRUPT_IN,  EP_SIZE(KEYBOARD_SIZE) | KEYBOARD_BUFFER,
+  0
+};
+
+
+/**************************************************************************
+ *
+ *  Descriptor Data
+ *
+ **************************************************************************/
+
+// Descriptors are the data that your computer reads when it auto-detects
+// this USB device (called "enumeration" in USB lingo).  The most commonly
+// changed items are editable at the top of this file.  Changing things
+// in here should only be done by those who've read chapter 9 of the USB
+// spec and relevant portions of any USB class specifications!
+
+
+static uint8_t const PROGMEM device_descriptor[] = {
+  18,                 // bLength
+  1,                  // bDescriptorType
+  0x00, 0x02,             // bcdUSB
+  0,                  // bDeviceClass
+  0,                  // bDeviceSubClass
+  0,                  // bDeviceProtocol
+  ENDPOINT0_SIZE,             // bMaxPacketSize0
+  LSB(VENDOR_ID), MSB(VENDOR_ID),     // idVendor
+  LSB(PRODUCT_ID), MSB(PRODUCT_ID),   // idProduct
+  0x00, 0x01,             // bcdDevice
+  1,                  // iManufacturer
+  2,                  // iProduct
+  0,                  // iSerialNumber
+  1                   // bNumConfigurations
+};
+
+// Keyboard Protocol 1, HID 1.11 spec, Appendix B, page 59-60
+static uint8_t const PROGMEM keyboard_hid_report_desc[] = {
+  0x05, 0x01,          // Usage Page (Generic Desktop),
+  0x09, 0x06,          // Usage (Keyboard),
+  0xA1, 0x01,          // Collection (Application),
+  0x75, 0x01,          //   Report Size (1),
+  0x95, 0x08,          //   Report Count (8),
+  0x05, 0x07,          //   Usage Page (Key Codes),
+  0x19, 0xE0,          //   Usage Minimum (224),
+  0x29, 0xE7,          //   Usage Maximum (231),
+  0x15, 0x00,          //   Logical Minimum (0),
+  0x25, 0x01,          //   Logical Maximum (1),
+  0x81, 0x02,          //   Input (Data, Variable, Absolute), ;Modifier byte
+  0x95, 0x01,          //   Report Count (1),
+  0x75, 0x08,          //   Report Size (8),
+  0x81, 0x03,          //   Input (Constant),                 ;Reserved byte
+  0x95, 0x05,          //   Report Count (5),
+  0x75, 0x01,          //   Report Size (1),
+  0x05, 0x08,          //   Usage Page (LEDs),
+  0x19, 0x01,          //   Usage Minimum (1),
+  0x29, 0x05,          //   Usage Maximum (5),
+  0x91, 0x02,          //   Output (Data, Variable, Absolute), ;LED report
+  0x95, 0x01,          //   Report Count (1),
+  0x75, 0x03,          //   Report Size (3),
+  0x91, 0x03,          //   Output (Constant),                 ;LED report padding
+  0x95, 0x06,          //   Report Count (6),
+  0x75, 0x08,          //   Report Size (8),
+  0x15, 0x00,          //   Logical Minimum (0),
+  0x25, 0x68,          //   Logical Maximum(104),
+  0x05, 0x07,          //   Usage Page (Key Codes),
+  0x19, 0x00,          //   Usage Minimum (0),
+  0x29, 0x68,          //   Usage Maximum (104),
+  0x81, 0x00,          //   Input (Data, Array),
+  0xc0                 // End Collection
+};
+
+#define CONFIG1_DESC_SIZE        (9+9+9+7)
+#define KEYBOARD_HID_DESC_OFFSET (9+9)
+static uint8_t const PROGMEM config1_descriptor[CONFIG1_DESC_SIZE] = {
+  // configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10
+  9,                  // bLength;
+  2,                  // bDescriptorType;
+  LSB(CONFIG1_DESC_SIZE),         // wTotalLength
+  MSB(CONFIG1_DESC_SIZE),
+  1,                  // bNumInterfaces
+  1,                  // bConfigurationValue
+  0,                  // iConfiguration
+  0xC0,                   // bmAttributes
+  50,                 // bMaxPower
+  // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
+  9,                  // bLength
+  4,                  // bDescriptorType
+  KEYBOARD_INTERFACE,         // bInterfaceNumber
+  0,                  // bAlternateSetting
+  1,                  // bNumEndpoints
+  0x03,                   // bInterfaceClass (0x03 = HID)
+  0x01,                   // bInterfaceSubClass (0x01 = Boot)
+  0x01,                   // bInterfaceProtocol (0x01 = Keyboard)
+  0,                  // iInterface
+  // HID interface descriptor, HID 1.11 spec, section 6.2.1
+  9,                  // bLength
+  0x21,                   // bDescriptorType
+  0x11, 0x01,             // bcdHID
+  0,                  // bCountryCode
+  1,                  // bNumDescriptors
+  0x22,                   // bDescriptorType
+  sizeof(keyboard_hid_report_desc),   // wDescriptorLength
+  0,
+  // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
+  7,                  // bLength
+  5,                  // bDescriptorType
+  KEYBOARD_ENDPOINT | 0x80,       // bEndpointAddress
+  0x03,                   // bmAttributes (0x03=intr)
+  KEYBOARD_SIZE, 0,           // wMaxPacketSize
+  1                   // bInterval
+};
+
+// If you're desperate for a little extra code memory, these strings
+// can be completely removed if iManufacturer, iProduct, iSerialNumber
+// in the device desciptor are changed to zeros.
+struct usb_string_descriptor_struct {
+  uint8_t bLength;
+  uint8_t bDescriptorType;
+  int16_t wString[];
+};
+static struct usb_string_descriptor_struct const PROGMEM string0 = {
+  4,
+  3,
+  {0x0409}
+};
+static struct usb_string_descriptor_struct const PROGMEM string1 = {
+  sizeof(STR_MANUFACTURER),
+  3,
+  STR_MANUFACTURER
+};
+static struct usb_string_descriptor_struct const PROGMEM string2 = {
+  sizeof(STR_PRODUCT),
+  3,
+  STR_PRODUCT
+};
+
+// This table defines which descriptor data is sent for each specific
+// request from the host (in wValue and wIndex).
+static struct descriptor_list_struct {
+  uint16_t    wValue;
+  uint16_t    wIndex;
+  const uint8_t   *addr;
+  uint8_t     length;
+} const PROGMEM descriptor_list[] = {
+  {0x0100, 0x0000, device_descriptor, sizeof(device_descriptor)},
+  {0x0200, 0x0000, config1_descriptor, sizeof(config1_descriptor)},
+  {0x2200, KEYBOARD_INTERFACE, keyboard_hid_report_desc, sizeof(keyboard_hid_report_desc)},
+  {0x2100, KEYBOARD_INTERFACE, config1_descriptor+KEYBOARD_HID_DESC_OFFSET, 9},
+  {0x0300, 0x0000, (const uint8_t *)&string0, 4},
+  {0x0301, 0x0409, (const uint8_t *)&string1, sizeof(STR_MANUFACTURER)},
+  {0x0302, 0x0409, (const uint8_t *)&string2, sizeof(STR_PRODUCT)}
+};
+#define NUM_DESC_LIST (sizeof(descriptor_list)/sizeof(struct descriptor_list_struct))
+
+
+/**************************************************************************
+ *
+ *  Variables - these are the only non-stack RAM usage
+ *
+ **************************************************************************/
+
+// zero when we are not configured, non-zero when enumerated
+static volatile uint8_t usb_configuration=0;
+
+// which modifier keys are currently pressed
+// 1=left ctrl,    2=left shift,   4=left alt,    8=left gui
+// 16=right ctrl, 32=right shift, 64=right alt, 128=right gui
+uint8_t keyboard_modifier_keys=0;
+
+// which keys are currently pressed, up to 6 keys may be down at once
+uint8_t keyboard_keys[6]={0,0,0,0,0,0};
+
+// protocol setting from the host.  We use exactly the same report
+// either way, so this variable only stores the setting since we
+// are required to be able to report which setting is in use.
+static uint8_t keyboard_protocol=1;
+
+// the idle configuration, how often we send the report to the
+// host (ms * 4) even when it hasn't changed
+static uint8_t keyboard_idle_config=125;
+
+// count until idle timeout
+static uint8_t keyboard_idle_count=0;
+
+// 1=num lock, 2=caps lock, 4=scroll lock, 8=compose, 16=kana
+volatile uint8_t keyboard_leds=0;
+
+
+/**************************************************************************
+ *
+ *  Public Functions - these are the API intended for the user
+ *
+ **************************************************************************/
+
+
+// initialize USB
+void usb_init(void)
+{
+  HW_CONFIG();
+  USB_FREEZE();   // enable USB
+  PLL_CONFIG();               // config PLL
+  while (!(PLLCSR & (1<<PLOCK))) ;    // wait for PLL lock
+  USB_CONFIG();               // start USB clock
+  UDCON = 0;              // enable attach resistor
+  usb_configuration = 0;
+  UDIEN = (1<<EORSTE)|(1<<SOFE);
+  sei();
+}
+
+// return 0 if the USB is not configured, or the configuration
+// number selected by the HOST
+uint8_t usb_configured(void)
+{
+  return usb_configuration;
+}
+
+
+// perform a single keystroke
+int8_t usb_keyboard_press(uint8_t key, uint8_t modifier)
+{
+  int8_t r;
+
+  keyboard_modifier_keys = modifier;
+  keyboard_keys[0] = key;
+  r = usb_keyboard_send();
+  if (r) return r;
+  keyboard_modifier_keys = 0;
+  keyboard_keys[0] = 0;
+  return usb_keyboard_send();
+}
+
+// send the contents of keyboard_keys and keyboard_modifier_keys
+int8_t usb_keyboard_send(void)
+{
+  uint8_t i, intr_state, timeout;
+
+  if (!usb_configuration) return -1;
+  intr_state = SREG;
+  cli();
+  UENUM = KEYBOARD_ENDPOINT;
+  timeout = UDFNUML + 50;
+  while (1) {
+    // are we ready to transmit?
+    if (UEINTX & (1<<RWAL)) break;
+    SREG = intr_state;
+    // has the USB gone offline?
+    if (!usb_configuration) return -1;
+    // have we waited too long?
+    if (UDFNUML == timeout) return -1;
+    // get ready to try checking again
+    intr_state = SREG;
+    cli();
+    UENUM = KEYBOARD_ENDPOINT;
+  }
+  UEDATX = keyboard_modifier_keys;
+  UEDATX = 0;
+  for (i=0; i<6; i++) {
+    UEDATX = keyboard_keys[i];
+  }
+  UEINTX = 0x3A;
+  keyboard_idle_count = 0;
+  SREG = intr_state;
+  return 0;
+}
+
+/**************************************************************************
+ *
+ *  Private Functions - not intended for general user consumption....
+ *
+ **************************************************************************/
+
+
+
+// USB Device Interrupt - handle all device-level events
+// the transmit buffer flushing is triggered by the start of frame
+//
+ISR(USB_GEN_vect)
+{
+  uint8_t intbits, t, i;
+  static uint8_t div4=0;
+
+  intbits = UDINT;
+  UDINT = 0;
+  if (intbits & (1<<EORSTI)) {
+    UENUM = 0;
+    UECONX = 1;
+    UECFG0X = EP_TYPE_CONTROL;
+    UECFG1X = EP_SIZE(ENDPOINT0_SIZE) | EP_SINGLE_BUFFER;
+    UEIENX = (1<<RXSTPE);
+    usb_configuration = 0;
+  }
+  if ((intbits & (1<<SOFI)) && usb_configuration) {
+    if (keyboard_idle_config && (++div4 & 3) == 0) {
+      UENUM = KEYBOARD_ENDPOINT;
+      if (UEINTX & (1<<RWAL)) {
+        keyboard_idle_count++;
+        if (keyboard_idle_count == keyboard_idle_config) {
+          keyboard_idle_count = 0;
+          UEDATX = keyboard_modifier_keys;
+          UEDATX = 0;
+          for (i=0; i<6; i++) {
+            UEDATX = keyboard_keys[i];
+          }
+          UEINTX = 0x3A;
+        }
+      }
+    }
+  }
+}
+
+
+
+// Misc functions to wait for ready and send/receive packets
+static inline void usb_wait_in_ready(void)
+{
+  while (!(UEINTX & (1<<TXINI))) ;
+}
+static inline void usb_send_in(void)
+{
+  UEINTX = ~(1<<TXINI);
+}
+static inline void usb_wait_receive_out(void)
+{
+  while (!(UEINTX & (1<<RXOUTI))) ;
+}
+static inline void usb_ack_out(void)
+{
+  UEINTX = ~(1<<RXOUTI);
+}
+
+
+
+// USB Endpoint Interrupt - endpoint 0 is handled here.  The
+// other endpoints are manipulated by the user-callable
+// functions, and the start-of-frame interrupt.
+//
+ISR(USB_COM_vect)
+{
+  uint8_t intbits;
+  const uint8_t *list;
+  const uint8_t *cfg;
+  uint8_t i, n, len, en;
+  uint8_t bmRequestType;
+  uint8_t bRequest;
+  uint16_t wValue;
+  uint16_t wIndex;
+  uint16_t wLength;
+  uint16_t desc_val;
+  const uint8_t *desc_addr;
+  uint8_t desc_length;
+
+  UENUM = 0;
+  intbits = UEINTX;
+  if (intbits & (1<<RXSTPI)) {
+    bmRequestType = UEDATX;
+    bRequest = UEDATX;
+    wValue = UEDATX;
+    wValue |= (UEDATX << 8);
+    wIndex = UEDATX;
+    wIndex |= (UEDATX << 8);
+    wLength = UEDATX;
+    wLength |= (UEDATX << 8);
+    UEINTX = ~((1<<RXSTPI) | (1<<RXOUTI) | (1<<TXINI));
+    if (bRequest == GET_DESCRIPTOR) {
+      list = (const uint8_t *)descriptor_list;
+      for (i=0; ; i++) {
+        if (i >= NUM_DESC_LIST) {
+          UECONX = (1<<STALLRQ)|(1<<EPEN);  //stall
+          return;
+        }
+        desc_val = pgm_read_word(list);
+        if (desc_val != wValue) {
+          list += sizeof(struct descriptor_list_struct);
+          continue;
+        }
+        list += 2;
+        desc_val = pgm_read_word(list);
+        if (desc_val != wIndex) {
+          list += sizeof(struct descriptor_list_struct)-2;
+          continue;
+        }
+        list += 2;
+        desc_addr = (const uint8_t *)pgm_read_word(list);
+        list += 2;
+        desc_length = pgm_read_byte(list);
+        break;
+      }
+      len = (wLength < 256) ? wLength : 255;
+      if (len > desc_length) len = desc_length;
+      do {
+        // wait for host ready for IN packet
+        do {
+          i = UEINTX;
+        } while (!(i & ((1<<TXINI)|(1<<RXOUTI))));
+        if (i & (1<<RXOUTI)) return;    // abort
+        // send IN packet
+        n = len < ENDPOINT0_SIZE ? len : ENDPOINT0_SIZE;
+        for (i = n; i; i--) {
+          UEDATX = pgm_read_byte(desc_addr++);
+        }
+        len -= n;
+        usb_send_in();
+      } while (len || n == ENDPOINT0_SIZE);
+      return;
+    }
+    if (bRequest == SET_ADDRESS) {
+      usb_send_in();
+      usb_wait_in_ready();
+      UDADDR = wValue | (1<<ADDEN);
+      return;
+    }
+    if (bRequest == SET_CONFIGURATION && bmRequestType == 0) {
+      usb_configuration = wValue;
+      usb_send_in();
+      cfg = endpoint_config_table;
+      for (i=1; i<5; i++) {
+        UENUM = i;
+        en = pgm_read_byte(cfg++);
+        UECONX = en;
+        if (en) {
+          UECFG0X = pgm_read_byte(cfg++);
+          UECFG1X = pgm_read_byte(cfg++);
+        }
+      }
+      UERST = 0x1E;
+      UERST = 0;
+      return;
+    }
+    if (bRequest == GET_CONFIGURATION && bmRequestType == 0x80) {
+      usb_wait_in_ready();
+      UEDATX = usb_configuration;
+      usb_send_in();
+      return;
+    }
+
+    if (bRequest == GET_STATUS) {
+      usb_wait_in_ready();
+      i = 0;
+#ifdef SUPPORT_ENDPOINT_HALT
+      if (bmRequestType == 0x82) {
+        UENUM = wIndex;
+        if (UECONX & (1<<STALLRQ)) i = 1;
+        UENUM = 0;
+      }
+#endif
+      UEDATX = i;
+      UEDATX = 0;
+      usb_send_in();
+      return;
+    }
+#ifdef SUPPORT_ENDPOINT_HALT
+    if ((bRequest == CLEAR_FEATURE || bRequest == SET_FEATURE)
+        && bmRequestType == 0x02 && wValue == 0) {
+      i = wIndex & 0x7F;
+      if (i >= 1 && i <= MAX_ENDPOINT) {
+        usb_send_in();
+        UENUM = i;
+        if (bRequest == SET_FEATURE) {
+          UECONX = (1<<STALLRQ)|(1<<EPEN);
+        } else {
+          UECONX = (1<<STALLRQC)|(1<<RSTDT)|(1<<EPEN);
+          UERST = (1 << i);
+          UERST = 0;
+        }
+        return;
+      }
+    }
+#endif
+    if (wIndex == KEYBOARD_INTERFACE) {
+      if (bmRequestType == 0xA1) {
+        if (bRequest == HID_GET_REPORT) {
+          usb_wait_in_ready();
+          UEDATX = keyboard_modifier_keys;
+          UEDATX = 0;
+          for (i=0; i<6; i++) {
+            UEDATX = keyboard_keys[i];
+          }
+          usb_send_in();
+          return;
+        }
+        if (bRequest == HID_GET_IDLE) {
+          usb_wait_in_ready();
+          UEDATX = keyboard_idle_config;
+          usb_send_in();
+          return;
+        }
+        if (bRequest == HID_GET_PROTOCOL) {
+          usb_wait_in_ready();
+          UEDATX = keyboard_protocol;
+          usb_send_in();
+          return;
+        }
+      }
+      if (bmRequestType == 0x21) {
+        if (bRequest == HID_SET_REPORT) {
+          usb_wait_receive_out();
+          keyboard_leds = UEDATX;
+          usb_ack_out();
+          usb_send_in();
+          return;
+        }
+        if (bRequest == HID_SET_IDLE) {
+          keyboard_idle_config = (wValue >> 8);
+          keyboard_idle_count = 0;
+          usb_send_in();
+          return;
+        }
+        if (bRequest == HID_SET_PROTOCOL) {
+          keyboard_protocol = wValue;
+          usb_send_in();
+          return;
+        }
+      }
+    }
+  }
+  UECONX = (1<<STALLRQ) | (1<<EPEN);  // stall
+}
diff --git a/firmware/usb_keyboard.h b/firmware/usb_keyboard.h
new file mode 100644 (file)
index 0000000..b4ee6f4
--- /dev/null
@@ -0,0 +1,206 @@
+#ifndef usb_serial_h__
+#define usb_serial_h__
+
+#include <stdint.h>
+
+void usb_init(void);            // initialize everything
+uint8_t usb_configured(void);       // is the USB port configured
+
+int8_t usb_keyboard_press(uint8_t key, uint8_t modifier);
+int8_t usb_keyboard_send(void);
+extern uint8_t keyboard_modifier_keys;
+extern uint8_t keyboard_keys[6];
+extern volatile uint8_t keyboard_leds;
+
+// This file does not include the HID debug functions, so these empty
+// macros replace them with nothing, so users can compile code that
+// has calls to these functions.
+#define usb_debug_putchar(c)
+#define usb_debug_flush_output()
+
+
+#define KEY_CTRL    0x01
+#define KEY_SHIFT   0x02
+#define KEY_ALT     0x04
+#define KEY_GUI     0x08
+#define KEY_LEFT_CTRL   0x01
+#define KEY_LEFT_SHIFT  0x02
+#define KEY_LEFT_ALT    0x04
+#define KEY_LEFT_GUI    0x08
+#define KEY_RIGHT_CTRL  0x10
+#define KEY_RIGHT_SHIFT 0x20
+#define KEY_RIGHT_ALT   0x40
+#define KEY_RIGHT_GUI   0x80
+
+#define KEY_A       4
+#define KEY_B       5
+#define KEY_C       6
+#define KEY_D       7
+#define KEY_E       8
+#define KEY_F       9
+#define KEY_G       10
+#define KEY_H       11
+#define KEY_I       12
+#define KEY_J       13
+#define KEY_K       14
+#define KEY_L       15
+#define KEY_M       16
+#define KEY_N       17
+#define KEY_O       18
+#define KEY_P       19
+#define KEY_Q       20
+#define KEY_R       21
+#define KEY_S       22
+#define KEY_T       23
+#define KEY_U       24
+#define KEY_V       25
+#define KEY_W       26
+#define KEY_X       27
+#define KEY_Y       28
+#define KEY_Z       29
+#define KEY_1       30
+#define KEY_2       31
+#define KEY_3       32
+#define KEY_4       33
+#define KEY_5       34
+#define KEY_6       35
+#define KEY_7       36
+#define KEY_8       37
+#define KEY_9       38
+#define KEY_0       39
+#define KEY_ENTER   40
+#define KEY_ESC     41
+#define KEY_BACKSPACE   42
+#define KEY_TAB     43
+#define KEY_SPACE   44
+#define KEY_MINUS   45
+#define KEY_EQUAL   46
+#define KEY_LEFT_BRACE  47
+#define KEY_RIGHT_BRACE 48
+#define KEY_BACKSLASH   49
+#define KEY_NUMBER  50
+#define KEY_SEMICOLON   51
+#define KEY_QUOTE   52
+#define KEY_TILDE   53
+#define KEY_COMMA   54
+#define KEY_PERIOD  55
+#define KEY_SLASH   56
+#define KEY_CAPS_LOCK   57
+#define KEY_F1      58
+#define KEY_F2      59
+#define KEY_F3      60
+#define KEY_F4      61
+#define KEY_F5      62
+#define KEY_F6      63
+#define KEY_F7      64
+#define KEY_F8      65
+#define KEY_F9      66
+#define KEY_F10     67
+#define KEY_F11     68
+#define KEY_F12     69
+#define KEY_PRINTSCREEN 70
+#define KEY_SCROLL_LOCK 71
+#define KEY_PAUSE   72
+#define KEY_INSERT  73
+#define KEY_HOME    74
+#define KEY_PAGE_UP 75
+#define KEY_DELETE  76
+#define KEY_END     77
+#define KEY_PAGE_DOWN   78
+#define KEY_RIGHT   79
+#define KEY_LEFT    80
+#define KEY_DOWN    81
+#define KEY_UP      82
+#define KEY_NUM_LOCK    83
+#define KEYPAD_SLASH    84
+#define KEYPAD_ASTERIX  85
+#define KEYPAD_MINUS    86
+#define KEYPAD_PLUS 87
+#define KEYPAD_ENTER    88
+#define KEYPAD_1    89
+#define KEYPAD_2    90
+#define KEYPAD_3    91
+#define KEYPAD_4    92
+#define KEYPAD_5    93
+#define KEYPAD_6    94
+#define KEYPAD_7    95
+#define KEYPAD_8    96
+#define KEYPAD_9    97
+#define KEYPAD_0    98
+#define KEYPAD_PERIOD   99
+
+
+
+
+// Everything below this point is only intended for usb_serial.c
+#ifdef USB_SERIAL_PRIVATE_INCLUDE
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <avr/interrupt.h>
+
+#define EP_TYPE_CONTROL         0x00
+#define EP_TYPE_BULK_IN         0x81
+#define EP_TYPE_BULK_OUT        0x80
+#define EP_TYPE_INTERRUPT_IN        0xC1
+#define EP_TYPE_INTERRUPT_OUT       0xC0
+#define EP_TYPE_ISOCHRONOUS_IN      0x41
+#define EP_TYPE_ISOCHRONOUS_OUT     0x40
+
+#define EP_SINGLE_BUFFER        0x02
+#define EP_DOUBLE_BUFFER        0x06
+
+#define EP_SIZE(s)  ((s) == 64 ? 0x30 :         \
+                     ((s) == 32 ? 0x20 :        \
+                      ((s) == 16 ? 0x10 :       \
+                       0x00)))
+
+#define MAX_ENDPOINT        4
+
+#define LSB(n) (n & 255)
+#define MSB(n) ((n >> 8) & 255)
+
+#if defined(__AVR_AT90USB162__)
+#define HW_CONFIG()
+#define PLL_CONFIG() (PLLCSR = ((1<<PLLE)|(1<<PLLP0)))
+#define USB_CONFIG() (USBCON = (1<<USBE))
+#define USB_FREEZE() (USBCON = ((1<<USBE)|(1<<FRZCLK)))
+#elif defined(__AVR_ATmega32U4__)
+#define HW_CONFIG() (UHWCON = 0x01)
+#define PLL_CONFIG() (PLLCSR = 0x12)
+#define USB_CONFIG() (USBCON = ((1<<USBE)|(1<<OTGPADE)))
+#define USB_FREEZE() (USBCON = ((1<<USBE)|(1<<FRZCLK)))
+#elif defined(__AVR_AT90USB646__)
+#define HW_CONFIG() (UHWCON = 0x81)
+#define PLL_CONFIG() (PLLCSR = 0x1A)
+#define USB_CONFIG() (USBCON = ((1<<USBE)|(1<<OTGPADE)))
+#define USB_FREEZE() (USBCON = ((1<<USBE)|(1<<FRZCLK)))
+#elif defined(__AVR_AT90USB1286__)
+#define HW_CONFIG() (UHWCON = 0x81)
+#define PLL_CONFIG() (PLLCSR = 0x16)
+#define USB_CONFIG() (USBCON = ((1<<USBE)|(1<<OTGPADE)))
+#define USB_FREEZE() (USBCON = ((1<<USBE)|(1<<FRZCLK)))
+#endif
+
+// standard control endpoint request types
+#define GET_STATUS          0
+#define CLEAR_FEATURE           1
+#define SET_FEATURE         3
+#define SET_ADDRESS         5
+#define GET_DESCRIPTOR          6
+#define GET_CONFIGURATION       8
+#define SET_CONFIGURATION       9
+#define GET_INTERFACE           10
+#define SET_INTERFACE           11
+// HID (human interface device)
+#define HID_GET_REPORT          1
+#define HID_GET_IDLE            2
+#define HID_GET_PROTOCOL        3
+#define HID_SET_REPORT          9
+#define HID_SET_IDLE            10
+#define HID_SET_PROTOCOL        11
+// CDC (communication class device)
+#define CDC_SET_LINE_CODING     0x20
+#define CDC_GET_LINE_CODING     0x21
+#define CDC_SET_CONTROL_LINE_STATE  0x22
+#endif
+#endif