forked from forks/qmk_firmware
99f3df2893
* Add support for 8 buttons to mouse report This includes support for 8 buttons in mousekeys. However, this does move the keys around due to the fact that the last mousekey keycode is already 0xFF, so any past that would not work with register_code and the like, breaking them for tap hold keys, encoders, and other features. * Update mouse key docs * Add changes based on feedback * Fix VUSB report size comment Because drashna red gud * Fix typo in action.c * Fix IS_MOUSE_BUTTON check * Change start range for mousekeys so that the end is 0xFF properly * condense mousekeys check
943 lines
30 KiB
C
943 lines
30 KiB
C
/*
|
|
Copyright 2011 Jun Wako <wakojun@gmail.com>
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <avr/wdt.h>
|
|
|
|
#include <usbdrv/usbdrv.h>
|
|
|
|
#include "usbconfig.h"
|
|
#include "host.h"
|
|
#include "report.h"
|
|
#include "host_driver.h"
|
|
#include "vusb.h"
|
|
#include "print.h"
|
|
#include "debug.h"
|
|
#include "wait.h"
|
|
#include "usb_descriptor_common.h"
|
|
|
|
#ifdef RAW_ENABLE
|
|
# include "raw_hid.h"
|
|
#endif
|
|
|
|
#if defined(CONSOLE_ENABLE)
|
|
# define RBUF_SIZE 128
|
|
# include "ring_buffer.h"
|
|
#endif
|
|
|
|
#define NEXT_INTERFACE __COUNTER__
|
|
|
|
/*
|
|
* Interface indexes
|
|
*/
|
|
enum usb_interfaces {
|
|
#ifndef KEYBOARD_SHARED_EP
|
|
KEYBOARD_INTERFACE = NEXT_INTERFACE,
|
|
#else
|
|
SHARED_INTERFACE = NEXT_INTERFACE,
|
|
# define KEYBOARD_INTERFACE SHARED_INTERFACE
|
|
#endif
|
|
|
|
// It is important that the Raw HID interface is at a constant
|
|
// interface number, to support Linux/OSX platforms and chrome.hid
|
|
// If Raw HID is enabled, let it be always 1.
|
|
#ifdef RAW_ENABLE
|
|
RAW_INTERFACE = NEXT_INTERFACE,
|
|
#endif
|
|
|
|
#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP)
|
|
SHARED_INTERFACE = NEXT_INTERFACE,
|
|
#endif
|
|
|
|
#ifdef CONSOLE_ENABLE
|
|
CONSOLE_INTERFACE = NEXT_INTERFACE,
|
|
#endif
|
|
|
|
TOTAL_INTERFACES = NEXT_INTERFACE
|
|
};
|
|
|
|
#define MAX_INTERFACES 3
|
|
|
|
#if (NEXT_INTERFACE - 1) > MAX_INTERFACES
|
|
# error There are not enough available interfaces to support all functions. Please disable one or more of the following: Mouse Keys, Extra Keys, Raw HID, Console
|
|
#endif
|
|
|
|
#if (defined(MOUSE_ENABLE) || defined(EXTRAKEY_ENABLE)) && CONSOLE_ENABLE
|
|
# error Mouse/Extra Keys share an endpoint with Console. Please disable one of the two.
|
|
#endif
|
|
|
|
static uint8_t keyboard_led_state = 0;
|
|
static uint8_t vusb_idle_rate = 0;
|
|
|
|
/* Keyboard report send buffer */
|
|
#define KBUF_SIZE 16
|
|
static report_keyboard_t kbuf[KBUF_SIZE];
|
|
static uint8_t kbuf_head = 0;
|
|
static uint8_t kbuf_tail = 0;
|
|
|
|
static report_keyboard_t keyboard_report_sent;
|
|
|
|
#define VUSB_TRANSFER_KEYBOARD_MAX_TRIES 10
|
|
|
|
/* transfer keyboard report from buffer */
|
|
void vusb_transfer_keyboard(void) {
|
|
for (int i = 0; i < VUSB_TRANSFER_KEYBOARD_MAX_TRIES; i++) {
|
|
if (usbInterruptIsReady()) {
|
|
if (kbuf_head != kbuf_tail) {
|
|
#ifndef KEYBOARD_SHARED_EP
|
|
usbSetInterrupt((void *)&kbuf[kbuf_tail], sizeof(report_keyboard_t));
|
|
#else
|
|
// Ugly hack! :(
|
|
usbSetInterrupt((void *)&kbuf[kbuf_tail], sizeof(report_keyboard_t) - 1);
|
|
while (!usbInterruptIsReady()) {
|
|
usbPoll();
|
|
}
|
|
usbSetInterrupt((void *)(&(kbuf[kbuf_tail].keys[5])), 1);
|
|
#endif
|
|
kbuf_tail = (kbuf_tail + 1) % KBUF_SIZE;
|
|
if (debug_keyboard) {
|
|
dprintf("V-USB: kbuf[%d->%d](%02X)\n", kbuf_tail, kbuf_head, (kbuf_head < kbuf_tail) ? (KBUF_SIZE - kbuf_tail + kbuf_head) : (kbuf_head - kbuf_tail));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
usbPoll();
|
|
wait_ms(1);
|
|
}
|
|
}
|
|
|
|
/*------------------------------------------------------------------*
|
|
* RAW HID
|
|
*------------------------------------------------------------------*/
|
|
#ifdef RAW_ENABLE
|
|
# define RAW_BUFFER_SIZE 32
|
|
# define RAW_EPSIZE 8
|
|
|
|
static uint8_t raw_output_buffer[RAW_BUFFER_SIZE];
|
|
static uint8_t raw_output_received_bytes = 0;
|
|
|
|
void raw_hid_send(uint8_t *data, uint8_t length) {
|
|
if (length != RAW_BUFFER_SIZE) {
|
|
return;
|
|
}
|
|
|
|
uint8_t *temp = data;
|
|
for (uint8_t i = 0; i < 4; i++) {
|
|
while (!usbInterruptIsReady4()) {
|
|
usbPoll();
|
|
}
|
|
usbSetInterrupt4(temp, 8);
|
|
temp += 8;
|
|
}
|
|
while (!usbInterruptIsReady4()) {
|
|
usbPoll();
|
|
}
|
|
usbSetInterrupt4(0, 0);
|
|
}
|
|
|
|
__attribute__((weak)) void raw_hid_receive(uint8_t *data, uint8_t length) {
|
|
// Users should #include "raw_hid.h" in their own code
|
|
// and implement this function there. Leave this as weak linkage
|
|
// so users can opt to not handle data coming in.
|
|
}
|
|
|
|
void raw_hid_task(void) {
|
|
if (raw_output_received_bytes == RAW_BUFFER_SIZE) {
|
|
raw_hid_receive(raw_output_buffer, RAW_BUFFER_SIZE);
|
|
raw_output_received_bytes = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*------------------------------------------------------------------*
|
|
* Console
|
|
*------------------------------------------------------------------*/
|
|
#ifdef CONSOLE_ENABLE
|
|
# define CONSOLE_BUFFER_SIZE 32
|
|
# define CONSOLE_EPSIZE 8
|
|
|
|
int8_t sendchar(uint8_t c) {
|
|
rbuf_enqueue(c);
|
|
return 0;
|
|
}
|
|
|
|
static inline bool usbSendData3(char *data, uint8_t len) {
|
|
uint8_t retries = 5;
|
|
while (!usbInterruptIsReady3()) {
|
|
if (!(retries--)) {
|
|
return false;
|
|
}
|
|
usbPoll();
|
|
}
|
|
|
|
usbSetInterrupt3((unsigned char *)data, len);
|
|
return true;
|
|
}
|
|
|
|
void console_task(void) {
|
|
if (!usbConfiguration) {
|
|
return;
|
|
}
|
|
|
|
if (!rbuf_has_data()) {
|
|
return;
|
|
}
|
|
|
|
// Send in chunks of 8 padded to 32
|
|
char send_buf[CONSOLE_BUFFER_SIZE] = {0};
|
|
uint8_t send_buf_count = 0;
|
|
while (rbuf_has_data() && send_buf_count < CONSOLE_EPSIZE) {
|
|
send_buf[send_buf_count++] = rbuf_dequeue();
|
|
}
|
|
|
|
char *temp = send_buf;
|
|
for (uint8_t i = 0; i < 4; i++) {
|
|
if (!usbSendData3(temp, 8)) {
|
|
break;
|
|
}
|
|
temp += 8;
|
|
}
|
|
|
|
usbSendData3(0, 0);
|
|
usbPoll();
|
|
}
|
|
#endif
|
|
|
|
/*------------------------------------------------------------------*
|
|
* Host driver
|
|
*------------------------------------------------------------------*/
|
|
static uint8_t keyboard_leds(void);
|
|
static void send_keyboard(report_keyboard_t *report);
|
|
static void send_mouse(report_mouse_t *report);
|
|
static void send_system(uint16_t data);
|
|
static void send_consumer(uint16_t data);
|
|
|
|
static host_driver_t driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer};
|
|
|
|
host_driver_t *vusb_driver(void) { return &driver; }
|
|
|
|
static uint8_t keyboard_leds(void) { return keyboard_led_state; }
|
|
|
|
static void send_keyboard(report_keyboard_t *report) {
|
|
uint8_t next = (kbuf_head + 1) % KBUF_SIZE;
|
|
if (next != kbuf_tail) {
|
|
kbuf[kbuf_head] = *report;
|
|
kbuf_head = next;
|
|
} else {
|
|
dprint("kbuf: full\n");
|
|
}
|
|
|
|
// NOTE: send key strokes of Macro
|
|
usbPoll();
|
|
vusb_transfer_keyboard();
|
|
keyboard_report_sent = *report;
|
|
}
|
|
|
|
#ifndef KEYBOARD_SHARED_EP
|
|
# define usbInterruptIsReadyShared usbInterruptIsReady3
|
|
# define usbSetInterruptShared usbSetInterrupt3
|
|
#else
|
|
# define usbInterruptIsReadyShared usbInterruptIsReady
|
|
# define usbSetInterruptShared usbSetInterrupt
|
|
#endif
|
|
|
|
static void send_mouse(report_mouse_t *report) {
|
|
#ifdef MOUSE_ENABLE
|
|
if (usbInterruptIsReadyShared()) {
|
|
usbSetInterruptShared((void *)report, sizeof(report_mouse_t));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef EXTRAKEY_ENABLE
|
|
static void send_extra(uint8_t report_id, uint16_t data) {
|
|
static uint8_t last_id = 0;
|
|
static uint16_t last_data = 0;
|
|
if ((report_id == last_id) && (data == last_data)) return;
|
|
last_id = report_id;
|
|
last_data = data;
|
|
|
|
report_extra_t report = {.report_id = report_id, .usage = data};
|
|
if (usbInterruptIsReadyShared()) {
|
|
usbSetInterruptShared((void *)&report, sizeof(report_extra_t));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void send_system(uint16_t data) {
|
|
#ifdef EXTRAKEY_ENABLE
|
|
send_extra(REPORT_ID_SYSTEM, data);
|
|
#endif
|
|
}
|
|
|
|
static void send_consumer(uint16_t data) {
|
|
#ifdef EXTRAKEY_ENABLE
|
|
send_extra(REPORT_ID_CONSUMER, data);
|
|
#endif
|
|
}
|
|
|
|
/*------------------------------------------------------------------*
|
|
* Request from host *
|
|
*------------------------------------------------------------------*/
|
|
static struct {
|
|
uint16_t len;
|
|
enum { NONE, SET_LED } kind;
|
|
} last_req;
|
|
|
|
usbMsgLen_t usbFunctionSetup(uchar data[8]) {
|
|
usbRequest_t *rq = (void *)data;
|
|
|
|
if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) { /* class request type */
|
|
if (rq->bRequest == USBRQ_HID_GET_REPORT) {
|
|
dprint("GET_REPORT:");
|
|
if (rq->wIndex.word == KEYBOARD_INTERFACE) {
|
|
usbMsgPtr = (usbMsgPtr_t)&keyboard_report_sent;
|
|
return sizeof(keyboard_report_sent);
|
|
}
|
|
} else if (rq->bRequest == USBRQ_HID_GET_IDLE) {
|
|
dprint("GET_IDLE:");
|
|
usbMsgPtr = (usbMsgPtr_t)&vusb_idle_rate;
|
|
return 1;
|
|
} else if (rq->bRequest == USBRQ_HID_SET_IDLE) {
|
|
vusb_idle_rate = rq->wValue.bytes[1];
|
|
dprintf("SET_IDLE: %02X", vusb_idle_rate);
|
|
} else if (rq->bRequest == USBRQ_HID_SET_REPORT) {
|
|
dprint("SET_REPORT:");
|
|
// Report Type: 0x02(Out)/ReportID: 0x00(none) && Interface: 0(keyboard)
|
|
if (rq->wValue.word == 0x0200 && rq->wIndex.word == KEYBOARD_INTERFACE) {
|
|
dprint("SET_LED:");
|
|
last_req.kind = SET_LED;
|
|
last_req.len = rq->wLength.word;
|
|
}
|
|
return USB_NO_MSG; // to get data in usbFunctionWrite
|
|
} else {
|
|
dprint("UNKNOWN:");
|
|
}
|
|
} else {
|
|
dprint("VENDOR:");
|
|
/* no vendor specific requests implemented */
|
|
}
|
|
dprint("\n");
|
|
return 0; /* default for not implemented requests: return no data back to host */
|
|
}
|
|
|
|
uchar usbFunctionWrite(uchar *data, uchar len) {
|
|
if (last_req.len == 0) {
|
|
return -1;
|
|
}
|
|
switch (last_req.kind) {
|
|
case SET_LED:
|
|
dprintf("SET_LED: %02X\n", data[0]);
|
|
keyboard_led_state = data[0];
|
|
last_req.len = 0;
|
|
return 1;
|
|
break;
|
|
case NONE:
|
|
default:
|
|
return -1;
|
|
break;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void usbFunctionWriteOut(uchar *data, uchar len) {
|
|
#ifdef RAW_ENABLE
|
|
// Data from host must be divided every 8bytes
|
|
if (len != 8) {
|
|
dprint("RAW: invalid length\n");
|
|
raw_output_received_bytes = 0;
|
|
return;
|
|
}
|
|
|
|
if (raw_output_received_bytes + len > RAW_BUFFER_SIZE) {
|
|
dprint("RAW: buffer full\n");
|
|
raw_output_received_bytes = 0;
|
|
} else {
|
|
for (uint8_t i = 0; i < 8; i++) {
|
|
raw_output_buffer[raw_output_received_bytes + i] = data[i];
|
|
}
|
|
raw_output_received_bytes += len;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*------------------------------------------------------------------*
|
|
* Descriptors *
|
|
*------------------------------------------------------------------*/
|
|
|
|
#ifdef KEYBOARD_SHARED_EP
|
|
const PROGMEM uchar shared_hid_report[] = {
|
|
# define SHARED_REPORT_STARTED
|
|
#else
|
|
const PROGMEM uchar keyboard_hid_report[] = {
|
|
#endif
|
|
0x05, 0x01, // Usage Page (Generic Desktop)
|
|
0x09, 0x06, // Usage (Keyboard)
|
|
0xA1, 0x01, // Collection (Application)
|
|
#ifdef KEYBOARD_SHARED_EP
|
|
0x85, REPORT_ID_KEYBOARD, // Report ID
|
|
#endif
|
|
// Modifiers (8 bits)
|
|
0x05, 0x07, // Usage Page (Keyboard/Keypad)
|
|
0x19, 0xE0, // Usage Minimum (Keyboard Left Control)
|
|
0x29, 0xE7, // Usage Maximum (Keyboard Right GUI)
|
|
0x15, 0x00, // Logical Minimum (0)
|
|
0x25, 0x01, // Logical Maximum (1)
|
|
0x95, 0x08, // Report Count (8)
|
|
0x75, 0x01, // Report Size (1)
|
|
0x81, 0x02, // Input (Data, Variable, Absolute)
|
|
// Reserved (1 byte)
|
|
0x95, 0x01, // Report Count (1)
|
|
0x75, 0x08, // Report Size (8)
|
|
0x81, 0x03, // Input (Constant)
|
|
// Keycodes (6 bytes)
|
|
0x05, 0x07, // Usage Page (Keyboard/Keypad)
|
|
0x19, 0x00, // Usage Minimum (0)
|
|
0x29, 0xFF, // Usage Maximum (255)
|
|
0x15, 0x00, // Logical Minimum (0)
|
|
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
|
0x95, 0x06, // Report Count (6)
|
|
0x75, 0x08, // Report Size (8)
|
|
0x81, 0x00, // Input (Data, Array, Absolute)
|
|
|
|
// Status LEDs (5 bits)
|
|
0x05, 0x08, // Usage Page (LED)
|
|
0x19, 0x01, // Usage Minimum (Num Lock)
|
|
0x29, 0x05, // Usage Maximum (Kana)
|
|
0x95, 0x05, // Report Count (5)
|
|
0x75, 0x01, // Report Size (1)
|
|
0x91, 0x02, // Output (Data, Variable, Absolute)
|
|
// LED padding (3 bits)
|
|
0x95, 0x01, // Report Count (1)
|
|
0x75, 0x03, // Report Size (3)
|
|
0x91, 0x03, // Output (Constant)
|
|
0xC0, // End Collection
|
|
#ifndef KEYBOARD_SHARED_EP
|
|
};
|
|
#endif
|
|
|
|
#if defined(SHARED_EP_ENABLE) && !defined(SHARED_REPORT_STARTED)
|
|
const PROGMEM uchar shared_hid_report[] = {
|
|
# define SHARED_REPORT_STARTED
|
|
#endif
|
|
|
|
#ifdef MOUSE_ENABLE
|
|
// Mouse report descriptor
|
|
0x05, 0x01, // Usage Page (Generic Desktop)
|
|
0x09, 0x02, // Usage (Mouse)
|
|
0xA1, 0x01, // Collection (Application)
|
|
0x85, REPORT_ID_MOUSE, // Report ID
|
|
0x09, 0x01, // Usage (Pointer)
|
|
0xA1, 0x00, // Collection (Physical)
|
|
// Buttons (8 bits)
|
|
0x05, 0x09, // Usage Page (Button)
|
|
0x19, 0x01, // Usage Minimum (Button 1)
|
|
0x29, 0x08, // Usage Maximum (Button 8)
|
|
0x15, 0x00, // Logical Minimum (0)
|
|
0x25, 0x01, // Logical Maximum (1)
|
|
0x95, 0x08, // Report Count (8)
|
|
0x75, 0x01, // Report Size (1)
|
|
0x81, 0x02, // Input (Data, Variable, Absolute)
|
|
|
|
// X/Y position (2 bytes)
|
|
0x05, 0x01, // Usage Page (Generic Desktop)
|
|
0x09, 0x30, // Usage (X)
|
|
0x09, 0x31, // Usage (Y)
|
|
0x15, 0x81, // Logical Minimum (-127)
|
|
0x25, 0x7F, // Logical Maximum (127)
|
|
0x95, 0x02, // Report Count (2)
|
|
0x75, 0x08, // Report Size (8)
|
|
0x81, 0x06, // Input (Data, Variable, Relative)
|
|
|
|
// Vertical wheel (1 byte)
|
|
0x09, 0x38, // Usage (Wheel)
|
|
0x15, 0x81, // Logical Minimum (-127)
|
|
0x25, 0x7F, // Logical Maximum (127)
|
|
0x95, 0x01, // Report Count (1)
|
|
0x75, 0x08, // Report Size (8)
|
|
0x81, 0x06, // Input (Data, Variable, Relative)
|
|
// Horizontal wheel (1 byte)
|
|
0x05, 0x0C, // Usage Page (Consumer)
|
|
0x0A, 0x38, 0x02, // Usage (AC Pan)
|
|
0x15, 0x81, // Logical Minimum (-127)
|
|
0x25, 0x7F, // Logical Maximum (127)
|
|
0x95, 0x01, // Report Count (1)
|
|
0x75, 0x08, // Report Size (8)
|
|
0x81, 0x06, // Input (Data, Variable, Relative)
|
|
0xC0, // End Collection
|
|
0xC0, // End Collection
|
|
#endif
|
|
|
|
#ifdef EXTRAKEY_ENABLE
|
|
// Extrakeys report descriptor
|
|
0x05, 0x01, // Usage Page (Generic Desktop)
|
|
0x09, 0x80, // Usage (System Control)
|
|
0xA1, 0x01, // Collection (Application)
|
|
0x85, REPORT_ID_SYSTEM, // Report ID
|
|
0x19, 0x01, // Usage Minimum (Pointer)
|
|
0x2A, 0xB7, 0x00, // Usage Maximum (System Display LCD Autoscale)
|
|
0x15, 0x01, // Logical Minimum
|
|
0x26, 0xB7, 0x00, // Logical Maximum
|
|
0x95, 0x01, // Report Count (1)
|
|
0x75, 0x10, // Report Size (16)
|
|
0x81, 0x00, // Input (Data, Array, Absolute)
|
|
0xC0, // End Collection
|
|
|
|
0x05, 0x0C, // Usage Page (Consumer)
|
|
0x09, 0x01, // Usage (Consumer Control)
|
|
0xA1, 0x01, // Collection (Application)
|
|
0x85, REPORT_ID_CONSUMER, // Report ID
|
|
0x19, 0x01, // Usage Minimum (Consumer Control)
|
|
0x2A, 0xA0, 0x02, // Usage Maximum (AC Desktop Show All Applications)
|
|
0x15, 0x01, // Logical Minimum
|
|
0x26, 0xA0, 0x02, // Logical Maximum
|
|
0x95, 0x01, // Report Count (1)
|
|
0x75, 0x10, // Report Size (16)
|
|
0x81, 0x00, // Input (Data, Array, Absolute)
|
|
0xC0 // End Collection
|
|
#endif
|
|
#ifdef SHARED_EP_ENABLE
|
|
};
|
|
#endif
|
|
|
|
#ifdef RAW_ENABLE
|
|
const PROGMEM uchar raw_hid_report[] = {
|
|
0x06, RAW_USAGE_PAGE_LO, RAW_USAGE_PAGE_HI, // Usage Page (Vendor Defined)
|
|
0x09, RAW_USAGE_ID, // Usage (Vendor Defined)
|
|
0xA1, 0x01, // Collection (Application)
|
|
// Data to host
|
|
0x09, 0x62, // Usage (Vendor Defined)
|
|
0x15, 0x00, // Logical Minimum (0)
|
|
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
|
0x95, RAW_BUFFER_SIZE, // Report Count
|
|
0x75, 0x08, // Report Size (8)
|
|
0x81, 0x02, // Input (Data, Variable, Absolute)
|
|
// Data from host
|
|
0x09, 0x63, // Usage (Vendor Defined)
|
|
0x15, 0x00, // Logical Minimum (0)
|
|
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
|
0x95, RAW_BUFFER_SIZE, // Report Count
|
|
0x75, 0x08, // Report Size (8)
|
|
0x91, 0x02, // Output (Data, Variable, Absolute)
|
|
0xC0 // End Collection
|
|
};
|
|
#endif
|
|
|
|
#if defined(CONSOLE_ENABLE)
|
|
const PROGMEM uchar console_hid_report[] = {
|
|
0x06, 0x31, 0xFF, // Usage Page (Vendor Defined - PJRC Teensy compatible)
|
|
0x09, 0x74, // Usage (Vendor Defined - PJRC Teensy compatible)
|
|
0xA1, 0x01, // Collection (Application)
|
|
// Data to host
|
|
0x09, 0x75, // Usage (Vendor Defined)
|
|
0x15, 0x00, // Logical Minimum (0x00)
|
|
0x26, 0xFF, 0x00, // Logical Maximum (0x00FF)
|
|
0x95, CONSOLE_BUFFER_SIZE, // Report Count
|
|
0x75, 0x08, // Report Size (8)
|
|
0x81, 0x02, // Input (Data, Variable, Absolute)
|
|
// Data from host
|
|
0x09, 0x76, // Usage (Vendor Defined)
|
|
0x15, 0x00, // Logical Minimum (0x00)
|
|
0x26, 0xFF, 0x00, // Logical Maximum (0x00FF)
|
|
0x95, CONSOLE_BUFFER_SIZE, // Report Count
|
|
0x75, 0x08, // Report Size (8)
|
|
0x91, 0x02, // Output (Data)
|
|
0xC0 // End Collection
|
|
};
|
|
#endif
|
|
|
|
#ifndef USB_MAX_POWER_CONSUMPTION
|
|
# define USB_MAX_POWER_CONSUMPTION 500
|
|
#endif
|
|
|
|
// TODO: change this to 10ms to match LUFA
|
|
#ifndef USB_POLLING_INTERVAL_MS
|
|
# define USB_POLLING_INTERVAL_MS 1
|
|
#endif
|
|
|
|
// clang-format off
|
|
const PROGMEM usbStringDescriptor_t usbStringDescriptorZero = {
|
|
.header = {
|
|
.bLength = USB_STRING_LEN(1),
|
|
.bDescriptorType = USBDESCR_STRING
|
|
},
|
|
.bString = {0x0409} // US English
|
|
};
|
|
|
|
const PROGMEM usbStringDescriptor_t usbStringDescriptorManufacturer = {
|
|
.header = {
|
|
.bLength = USB_STRING_LEN(sizeof(STR(MANUFACTURER)) - 1),
|
|
.bDescriptorType = USBDESCR_STRING
|
|
},
|
|
.bString = LSTR(MANUFACTURER)
|
|
};
|
|
|
|
const PROGMEM usbStringDescriptor_t usbStringDescriptorProduct = {
|
|
.header = {
|
|
.bLength = USB_STRING_LEN(sizeof(STR(PRODUCT)) - 1),
|
|
.bDescriptorType = USBDESCR_STRING
|
|
},
|
|
.bString = LSTR(PRODUCT)
|
|
};
|
|
|
|
#if defined(SERIAL_NUMBER)
|
|
const PROGMEM usbStringDescriptor_t usbStringDescriptorSerial = {
|
|
.header = {
|
|
.bLength = USB_STRING_LEN(sizeof(STR(SERIAL_NUMBER)) - 1),
|
|
.bDescriptorType = USBDESCR_STRING
|
|
},
|
|
.bString = LSTR(SERIAL_NUMBER)
|
|
};
|
|
#endif
|
|
|
|
/*
|
|
* Device descriptor
|
|
*/
|
|
const PROGMEM usbDeviceDescriptor_t usbDeviceDescriptor = {
|
|
.header = {
|
|
.bLength = sizeof(usbDeviceDescriptor_t),
|
|
.bDescriptorType = USBDESCR_DEVICE
|
|
},
|
|
.bcdUSB = 0x0110,
|
|
.bDeviceClass = 0x00,
|
|
.bDeviceSubClass = 0x00,
|
|
.bDeviceProtocol = 0x00,
|
|
.bMaxPacketSize0 = 8,
|
|
.idVendor = VENDOR_ID,
|
|
.idProduct = PRODUCT_ID,
|
|
.bcdDevice = DEVICE_VER,
|
|
.iManufacturer = 0x01,
|
|
.iProduct = 0x02,
|
|
#if defined(SERIAL_NUMBER)
|
|
.iSerialNumber = 0x03,
|
|
#else
|
|
.iSerialNumber = 0x00,
|
|
#endif
|
|
.bNumConfigurations = 1
|
|
};
|
|
|
|
/*
|
|
* Configuration descriptors
|
|
*/
|
|
const PROGMEM usbConfigurationDescriptor_t usbConfigurationDescriptor = {
|
|
.header = {
|
|
.header = {
|
|
.bLength = sizeof(usbConfigurationDescriptorHeader_t),
|
|
.bDescriptorType = USBDESCR_CONFIG
|
|
},
|
|
.wTotalLength = sizeof(usbConfigurationDescriptor_t),
|
|
.bNumInterfaces = TOTAL_INTERFACES,
|
|
.bConfigurationValue = 0x01,
|
|
.iConfiguration = 0x00,
|
|
.bmAttributes = (1 << 7) | USBATTR_REMOTEWAKE,
|
|
.bMaxPower = USB_MAX_POWER_CONSUMPTION / 2
|
|
},
|
|
|
|
# ifndef KEYBOARD_SHARED_EP
|
|
/*
|
|
* Keyboard
|
|
*/
|
|
.keyboardInterface = {
|
|
.header = {
|
|
.bLength = sizeof(usbInterfaceDescriptor_t),
|
|
.bDescriptorType = USBDESCR_INTERFACE
|
|
},
|
|
.bInterfaceNumber = KEYBOARD_INTERFACE,
|
|
.bAlternateSetting = 0x00,
|
|
.bNumEndpoints = 1,
|
|
.bInterfaceClass = 0x03,
|
|
.bInterfaceSubClass = 0x01,
|
|
.bInterfaceProtocol = 0x01,
|
|
.iInterface = 0x00
|
|
},
|
|
.keyboardHID = {
|
|
.header = {
|
|
.bLength = sizeof(usbHIDDescriptor_t),
|
|
.bDescriptorType = USBDESCR_HID
|
|
},
|
|
.bcdHID = 0x0101,
|
|
.bCountryCode = 0x00,
|
|
.bNumDescriptors = 1,
|
|
.bDescriptorType = USBDESCR_HID_REPORT,
|
|
.wDescriptorLength = sizeof(keyboard_hid_report)
|
|
},
|
|
.keyboardINEndpoint = {
|
|
.header = {
|
|
.bLength = sizeof(usbEndpointDescriptor_t),
|
|
.bDescriptorType = USBDESCR_ENDPOINT
|
|
},
|
|
.bEndpointAddress = (USBRQ_DIR_DEVICE_TO_HOST | 1),
|
|
.bmAttributes = 0x03,
|
|
.wMaxPacketSize = 8,
|
|
.bInterval = USB_POLLING_INTERVAL_MS
|
|
},
|
|
# endif
|
|
|
|
# if defined(RAW_ENABLE)
|
|
/*
|
|
* RAW HID
|
|
*/
|
|
.rawInterface = {
|
|
.header = {
|
|
.bLength = sizeof(usbInterfaceDescriptor_t),
|
|
.bDescriptorType = USBDESCR_INTERFACE
|
|
},
|
|
.bInterfaceNumber = RAW_INTERFACE,
|
|
.bAlternateSetting = 0x00,
|
|
.bNumEndpoints = 2,
|
|
.bInterfaceClass = 0x03,
|
|
.bInterfaceSubClass = 0x00,
|
|
.bInterfaceProtocol = 0x00,
|
|
.iInterface = 0x00
|
|
},
|
|
.rawHID = {
|
|
.header = {
|
|
.bLength = sizeof(usbHIDDescriptor_t),
|
|
.bDescriptorType = USBDESCR_HID
|
|
},
|
|
.bcdHID = 0x0101,
|
|
.bCountryCode = 0x00,
|
|
.bNumDescriptors = 1,
|
|
.bDescriptorType = USBDESCR_HID_REPORT,
|
|
.wDescriptorLength = sizeof(raw_hid_report)
|
|
},
|
|
.rawINEndpoint = {
|
|
.header = {
|
|
.bLength = sizeof(usbEndpointDescriptor_t),
|
|
.bDescriptorType = USBDESCR_ENDPOINT
|
|
},
|
|
.bEndpointAddress = (USBRQ_DIR_DEVICE_TO_HOST | USB_CFG_EP4_NUMBER),
|
|
.bmAttributes = 0x03,
|
|
.wMaxPacketSize = RAW_EPSIZE,
|
|
.bInterval = USB_POLLING_INTERVAL_MS
|
|
},
|
|
.rawOUTEndpoint = {
|
|
.header = {
|
|
.bLength = sizeof(usbEndpointDescriptor_t),
|
|
.bDescriptorType = USBDESCR_ENDPOINT
|
|
},
|
|
.bEndpointAddress = (USBRQ_DIR_HOST_TO_DEVICE | USB_CFG_EP4_NUMBER),
|
|
.bmAttributes = 0x03,
|
|
.wMaxPacketSize = RAW_EPSIZE,
|
|
.bInterval = USB_POLLING_INTERVAL_MS
|
|
},
|
|
# endif
|
|
|
|
# ifdef SHARED_EP_ENABLE
|
|
/*
|
|
* Shared
|
|
*/
|
|
.sharedInterface = {
|
|
.header = {
|
|
.bLength = sizeof(usbInterfaceDescriptor_t),
|
|
.bDescriptorType = USBDESCR_INTERFACE
|
|
},
|
|
.bInterfaceNumber = SHARED_INTERFACE,
|
|
.bAlternateSetting = 0x00,
|
|
.bNumEndpoints = 1,
|
|
.bInterfaceClass = 0x03,
|
|
# ifdef KEYBOARD_SHARED_EP
|
|
.bInterfaceSubClass = 0x01,
|
|
.bInterfaceProtocol = 0x01,
|
|
# else
|
|
.bInterfaceSubClass = 0x00,
|
|
.bInterfaceProtocol = 0x00,
|
|
# endif
|
|
.iInterface = 0x00
|
|
},
|
|
.sharedHID = {
|
|
.header = {
|
|
.bLength = sizeof(usbHIDDescriptor_t),
|
|
.bDescriptorType = USBDESCR_HID
|
|
},
|
|
.bcdHID = 0x0101,
|
|
.bCountryCode = 0x00,
|
|
.bNumDescriptors = 1,
|
|
.bDescriptorType = USBDESCR_HID_REPORT,
|
|
.wDescriptorLength = sizeof(shared_hid_report)
|
|
},
|
|
.sharedINEndpoint = {
|
|
.header = {
|
|
.bLength = sizeof(usbEndpointDescriptor_t),
|
|
.bDescriptorType = USBDESCR_ENDPOINT
|
|
},
|
|
# ifdef KEYBOARD_SHARED_EP
|
|
.bEndpointAddress = (USBRQ_DIR_DEVICE_TO_HOST | 1),
|
|
# else
|
|
.bEndpointAddress = (USBRQ_DIR_DEVICE_TO_HOST | USB_CFG_EP3_NUMBER),
|
|
# endif
|
|
.bmAttributes = 0x03,
|
|
.wMaxPacketSize = 8,
|
|
.bInterval = USB_POLLING_INTERVAL_MS
|
|
},
|
|
# endif
|
|
|
|
# if defined(CONSOLE_ENABLE)
|
|
/*
|
|
* Console
|
|
*/
|
|
.consoleInterface = {
|
|
.header = {
|
|
.bLength = sizeof(usbInterfaceDescriptor_t),
|
|
.bDescriptorType = USBDESCR_INTERFACE
|
|
},
|
|
.bInterfaceNumber = CONSOLE_INTERFACE,
|
|
.bAlternateSetting = 0x00,
|
|
.bNumEndpoints = 2,
|
|
.bInterfaceClass = 0x03,
|
|
.bInterfaceSubClass = 0x00,
|
|
.bInterfaceProtocol = 0x00,
|
|
.iInterface = 0x00
|
|
},
|
|
.consoleHID = {
|
|
.header = {
|
|
.bLength = sizeof(usbHIDDescriptor_t),
|
|
.bDescriptorType = USBDESCR_HID
|
|
},
|
|
.bcdHID = 0x0111,
|
|
.bCountryCode = 0x00,
|
|
.bNumDescriptors = 1,
|
|
.bDescriptorType = USBDESCR_HID_REPORT,
|
|
.wDescriptorLength = sizeof(console_hid_report)
|
|
},
|
|
.consoleINEndpoint = {
|
|
.header = {
|
|
.bLength = sizeof(usbEndpointDescriptor_t),
|
|
.bDescriptorType = USBDESCR_ENDPOINT
|
|
},
|
|
.bEndpointAddress = (USBRQ_DIR_DEVICE_TO_HOST | USB_CFG_EP3_NUMBER),
|
|
.bmAttributes = 0x03,
|
|
.wMaxPacketSize = CONSOLE_EPSIZE,
|
|
.bInterval = 0x01
|
|
},
|
|
.consoleOUTEndpoint = {
|
|
.header = {
|
|
.bLength = sizeof(usbEndpointDescriptor_t),
|
|
.bDescriptorType = USBDESCR_ENDPOINT
|
|
},
|
|
.bEndpointAddress = (USBRQ_DIR_HOST_TO_DEVICE | USB_CFG_EP3_NUMBER),
|
|
.bmAttributes = 0x03,
|
|
.wMaxPacketSize = CONSOLE_EPSIZE,
|
|
.bInterval = 0x01
|
|
}
|
|
# endif
|
|
};
|
|
|
|
// clang-format on
|
|
|
|
USB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq) {
|
|
usbMsgLen_t len = 0;
|
|
|
|
switch (rq->wValue.bytes[1]) {
|
|
case USBDESCR_DEVICE:
|
|
usbMsgPtr = (usbMsgPtr_t)&usbDeviceDescriptor;
|
|
len = sizeof(usbDeviceDescriptor_t);
|
|
break;
|
|
case USBDESCR_CONFIG:
|
|
usbMsgPtr = (usbMsgPtr_t)&usbConfigurationDescriptor;
|
|
len = sizeof(usbConfigurationDescriptor_t);
|
|
break;
|
|
case USBDESCR_STRING:
|
|
switch (rq->wValue.bytes[0]) {
|
|
case 0:
|
|
usbMsgPtr = (usbMsgPtr_t)&usbStringDescriptorZero;
|
|
len = usbStringDescriptorZero.header.bLength;
|
|
break;
|
|
case 1: // iManufacturer
|
|
usbMsgPtr = (usbMsgPtr_t)&usbStringDescriptorManufacturer;
|
|
len = usbStringDescriptorManufacturer.header.bLength;
|
|
break;
|
|
case 2: // iProduct
|
|
usbMsgPtr = (usbMsgPtr_t)&usbStringDescriptorProduct;
|
|
len = usbStringDescriptorProduct.header.bLength;
|
|
break;
|
|
#if defined(SERIAL_NUMBER)
|
|
case 3: // iSerialNumber
|
|
usbMsgPtr = (usbMsgPtr_t)&usbStringDescriptorSerial;
|
|
len = usbStringDescriptorSerial.header.bLength;
|
|
break;
|
|
#endif
|
|
}
|
|
break;
|
|
case USBDESCR_HID:
|
|
switch (rq->wValue.bytes[0]) {
|
|
#ifndef KEYBOARD_SHARED_EP
|
|
case KEYBOARD_INTERFACE:
|
|
usbMsgPtr = (usbMsgPtr_t)&usbConfigurationDescriptor.keyboardHID;
|
|
len = sizeof(usbHIDDescriptor_t);
|
|
break;
|
|
#endif
|
|
|
|
#if defined(RAW_ENABLE)
|
|
case RAW_INTERFACE:
|
|
usbMsgPtr = (usbMsgPtr_t)&usbConfigurationDescriptor.rawHID;
|
|
len = sizeof(usbHIDDescriptor_t);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef SHARED_EP_ENABLE
|
|
case SHARED_INTERFACE:
|
|
usbMsgPtr = (usbMsgPtr_t)&usbConfigurationDescriptor.sharedHID;
|
|
len = sizeof(usbHIDDescriptor_t);
|
|
break;
|
|
#endif
|
|
|
|
#if defined(CONSOLE_ENABLE)
|
|
case CONSOLE_INTERFACE:
|
|
usbMsgPtr = (usbMsgPtr_t)&usbConfigurationDescriptor.consoleHID;
|
|
len = sizeof(usbHIDDescriptor_t);
|
|
break;
|
|
#endif
|
|
}
|
|
break;
|
|
case USBDESCR_HID_REPORT:
|
|
/* interface index */
|
|
switch (rq->wIndex.word) {
|
|
#ifndef KEYBOARD_SHARED_EP
|
|
case KEYBOARD_INTERFACE:
|
|
usbMsgPtr = (usbMsgPtr_t)keyboard_hid_report;
|
|
len = sizeof(keyboard_hid_report);
|
|
break;
|
|
#endif
|
|
|
|
#if defined(RAW_ENABLE)
|
|
case RAW_INTERFACE:
|
|
usbMsgPtr = (usbMsgPtr_t)raw_hid_report;
|
|
len = sizeof(raw_hid_report);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef SHARED_EP_ENABLE
|
|
case SHARED_INTERFACE:
|
|
usbMsgPtr = (usbMsgPtr_t)shared_hid_report;
|
|
len = sizeof(shared_hid_report);
|
|
break;
|
|
#endif
|
|
|
|
#if defined(CONSOLE_ENABLE)
|
|
case CONSOLE_INTERFACE:
|
|
usbMsgPtr = (usbMsgPtr_t)console_hid_report;
|
|
len = sizeof(console_hid_report);
|
|
break;
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
return len;
|
|
}
|