507 lines
11 KiB
C
507 lines
11 KiB
C
|
#include <stm32l0xx.h>
|
||
|
#include <stdint.h>
|
||
|
#include <stddef.h>
|
||
|
#include "usb.h"
|
||
|
#include "unique-id.h"
|
||
|
#include "eeprom.h"
|
||
|
#include <string.h>
|
||
|
|
||
|
volatile unsigned int i = 0x12345678;
|
||
|
unsigned char c = 2;
|
||
|
unsigned int my_static_var;
|
||
|
|
||
|
volatile uint32_t tick = 0;
|
||
|
|
||
|
uint8_t ep0_buff[64];
|
||
|
|
||
|
uint8_t dev_descriptor[] = {
|
||
|
18, // bLength
|
||
|
1, // bDescriptorType
|
||
|
0x00, 0x02, // bcdUSB
|
||
|
0x00, // bDeviceClass = Use info in interface descriptor
|
||
|
0x00, // bDeviceSubClass = Use info in interface
|
||
|
0x00, // bDeviceProtocol = Use info in interface
|
||
|
64, // bMaxPacketSize0
|
||
|
0xAD, 0xDE, // idVendor
|
||
|
0xEF, 0xBE, // idProduct
|
||
|
0x00, 0x01, // bcdDevice
|
||
|
1, // iManufacturer
|
||
|
2, // iProduct
|
||
|
3, // iSerialNumber
|
||
|
1 // bNumConfigurations
|
||
|
};
|
||
|
|
||
|
|
||
|
/* See: https://github.com/anszom/avr-vusb-keyboard/blob/master/sw/hid_descriptor.h */
|
||
|
const uint8_t hid_report_descriptor[] = {
|
||
|
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||
|
0x09, 0x06, // USAGE (Keyboard)
|
||
|
0xa1, 0x01, // COLLECTION (Application)
|
||
|
0x05, 0x07, // USAGE_PAGE (Keyboard)
|
||
|
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
|
||
|
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
|
||
|
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||
|
0x25, 0x01, // LOGICAL_MAXIMUM (1)
|
||
|
0x75, 0x01, // REPORT_SIZE (1)
|
||
|
0x95, 0x08, // REPORT_COUNT (8)
|
||
|
0x81, 0x02, // INPUT (Data,Var,Abs) //1 byte
|
||
|
|
||
|
0x95, 0x01, // REPORT_COUNT (1)
|
||
|
0x75, 0x08, // REPORT_SIZE (8)
|
||
|
0x81, 0x03, // INPUT (Cnst,Var,Abs) //1 byte
|
||
|
|
||
|
0x95, 0x06, // REPORT_COUNT (6)
|
||
|
0x75, 0x08, // REPORT_SIZE (8)
|
||
|
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||
|
0x25, 0x65, // LOGICAL_MAXIMUM (101)
|
||
|
0x05, 0x07, // USAGE_PAGE (Keyboard)
|
||
|
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
|
||
|
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
|
||
|
0x81, 0x00, // INPUT (Data,Ary,Abs) //6 bytes
|
||
|
|
||
|
0xc0 // END_COLLECTION
|
||
|
};
|
||
|
|
||
|
struct hid_report {
|
||
|
uint8_t modifier;
|
||
|
uint8_t reserved;
|
||
|
uint8_t keycodes[6];
|
||
|
};
|
||
|
|
||
|
const uint8_t config_desc[34] = {
|
||
|
9, /* bLenght */
|
||
|
2, /* bDescriptorType */
|
||
|
34, 0, /* wTotalLength */
|
||
|
1, /* bNumInterfaces */
|
||
|
1, /* bConfigurationValue */
|
||
|
0, /* iConfiguration */
|
||
|
0x80, /* bmAttributes */
|
||
|
50, /* bMaxPower */
|
||
|
/* Interface Descriptor */
|
||
|
0x09, /* Length = 9 */
|
||
|
0x04, /* Type = Interface Descriptor */
|
||
|
0x00, /* Interface Number */
|
||
|
0x00, /* bAlternateSetting */
|
||
|
0x01, /* bNumEndpoints */
|
||
|
0x03, /* bInterfaceClass (3 = HID) */
|
||
|
0x00, /* bInterfaceSubClass */
|
||
|
0x00, /* bInterfaceProtocol */
|
||
|
0x00, /* iInterface */
|
||
|
/* Endpoint Descriptor: EP1 Interrupt IN */
|
||
|
0x07, /* bLength */
|
||
|
0x05, /* bDescriptorType (5 = endpoint) */
|
||
|
0x81, /* bEndpointAddress */
|
||
|
0x03, /* bmAttributes (3 = Interrupt transfer */
|
||
|
64, 0x00, /* MaxPacketSize */
|
||
|
10, /* bInterval */
|
||
|
|
||
|
/* HID Descriptor */
|
||
|
9, /* bLength */
|
||
|
0x21, /* Descriptor Type: 0x21 = HID */
|
||
|
0x11, 0x01, /* BCD HID */
|
||
|
0x00, /* bCountryCode */
|
||
|
1, /* bNumDescriptors */
|
||
|
0x22, /* bDescriptorType 0x22=report */
|
||
|
sizeof(hid_report_descriptor), 0x00
|
||
|
};
|
||
|
|
||
|
const uint8_t lang_zero_desc[4] = {
|
||
|
4, /* bLength */
|
||
|
3, /* Descriptor Type = string */
|
||
|
0x09, 0x04 /* Language ID = English (US) */
|
||
|
};
|
||
|
|
||
|
const uint8_t manufacturer_string_desc[] =
|
||
|
{
|
||
|
2 + 8 * 2, /* Length */
|
||
|
3, /* Type = String descriptor */
|
||
|
'S', 0x00,
|
||
|
'h', 0x00,
|
||
|
'i', 0x00,
|
||
|
'm', 0x00,
|
||
|
'a', 0x00,
|
||
|
't', 0x00,
|
||
|
't', 0x00,
|
||
|
'a', 0x00,
|
||
|
};
|
||
|
|
||
|
const uint8_t product_string_desc[] =
|
||
|
{
|
||
|
2 + 22 * 2, /* Length */
|
||
|
3, /* Type = String descriptor */
|
||
|
'S', 0x00,
|
||
|
'u', 0x00,
|
||
|
's', 0x00,
|
||
|
't', 0x00,
|
||
|
'a', 0x00,
|
||
|
'i', 0x00,
|
||
|
'n', 0x00,
|
||
|
' ', 0x00,
|
||
|
'P', 0x00,
|
||
|
'e', 0x00,
|
||
|
'd', 0x00,
|
||
|
'a', 0x00,
|
||
|
'l', 0x00,
|
||
|
' ', 0x00,
|
||
|
'K', 0x00,
|
||
|
'e', 0x00,
|
||
|
'y', 0x00,
|
||
|
'b', 0x00,
|
||
|
'o', 0x00,
|
||
|
'a', 0x00,
|
||
|
'r', 0x00,
|
||
|
'd', 0x00,
|
||
|
};
|
||
|
|
||
|
uint8_t serial_string_desc[25 * 2 + 10];
|
||
|
|
||
|
struct usb_descriptor_entry desc_table[] = {
|
||
|
{0x0000, 0x0100, dev_descriptor, sizeof(dev_descriptor)},
|
||
|
{0x0000, 0x0200, config_desc, sizeof(config_desc)},
|
||
|
{0x0000, 0x0300, lang_zero_desc, sizeof(lang_zero_desc)},
|
||
|
{0x0409, 0x0301, manufacturer_string_desc, sizeof(manufacturer_string_desc)},
|
||
|
{0x0409, 0x0302, product_string_desc, sizeof(product_string_desc)},
|
||
|
{0x0409, 0x0303, serial_string_desc, sizeof(serial_string_desc)},
|
||
|
{0x0000, 0x2200, hid_report_descriptor, sizeof(hid_report_descriptor)},
|
||
|
{0x0001, 0x2200, hid_report_descriptor, sizeof(hid_report_descriptor)},
|
||
|
/* sentinel */
|
||
|
{0x00, 0x0, NULL, 0},
|
||
|
};
|
||
|
|
||
|
static void wait_for_ticks(uint32_t ticks)
|
||
|
{
|
||
|
tick = 0;
|
||
|
while (tick < ticks);
|
||
|
}
|
||
|
|
||
|
static void uint_to_hex(uint64_t num, uint8_t hex_digits, char *out)
|
||
|
{
|
||
|
int pos;
|
||
|
int string_idx;
|
||
|
uint64_t mask;
|
||
|
|
||
|
if (!out || !hex_digits)
|
||
|
return;
|
||
|
|
||
|
if (hex_digits > 16)
|
||
|
return;
|
||
|
|
||
|
for (pos = hex_digits - 1, string_idx = 0; pos >= 0; pos--, string_idx++) {
|
||
|
mask = num & ((uint64_t)0xFULL << (pos * 4));
|
||
|
mask >>= pos * 4;
|
||
|
|
||
|
if (mask <= 0x9) {
|
||
|
out[string_idx] = 0x30 + (char)mask;
|
||
|
} else if (mask > 0x9) {
|
||
|
out[string_idx] = 0x37 + mask;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
static void create_sn_from_unique_id(void)
|
||
|
{
|
||
|
uint64_t lot;
|
||
|
uint32_t uid;
|
||
|
char id_string[25];
|
||
|
int i;
|
||
|
int j;
|
||
|
|
||
|
struct usb_descriptor_entry *desc_entry;
|
||
|
|
||
|
unique_id_get(&lot, &uid);
|
||
|
uint_to_hex(lot, 16, id_string);
|
||
|
id_string[16] = ':';
|
||
|
uint_to_hex(uid, 8, &id_string[17]);
|
||
|
|
||
|
for (i = 0; desc_table[i].descriptor; i++) {
|
||
|
desc_entry = &desc_table[i];
|
||
|
if (desc_entry->descriptor == serial_string_desc) {
|
||
|
for (j = 0; j < 25; j++) {
|
||
|
serial_string_desc[2 * j + 2] = id_string[j];
|
||
|
serial_string_desc[2 * j + 1 + 2] = 0x00;
|
||
|
}
|
||
|
desc_entry->size = 25 * 2 + 2;
|
||
|
serial_string_desc[0] = 25 * 2 + 2;
|
||
|
serial_string_desc[1] = 3;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool expect_report;
|
||
|
bool expect_eeprom_data;
|
||
|
uint16_t eep_offset;
|
||
|
uint16_t eep_len;
|
||
|
|
||
|
static void ep_rx_data_callback(uint8_t endpoint, const uint8_t *buffer, uint32_t len)
|
||
|
{
|
||
|
if (endpoint == 0) {
|
||
|
if (expect_report) {
|
||
|
usb_endpoint_send_status_stage(0);
|
||
|
} else if (expect_eeprom_data) {
|
||
|
if (len > eep_len) {
|
||
|
usb_endpoint_stall(0, true, true);
|
||
|
} else {
|
||
|
data_eeprom_write(eep_offset, buffer, len);
|
||
|
eep_len -= len;
|
||
|
eep_offset += len;
|
||
|
if (eep_len == 0) {
|
||
|
usb_endpoint_send_status_stage(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void append_pedal_keycodes_to_report(struct hid_report *r, uint32_t word)
|
||
|
{
|
||
|
int i, j;
|
||
|
uint8_t keycode;
|
||
|
|
||
|
if (word & 0xFF) {
|
||
|
r->modifier |= word & 0xFF;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < 3; i++) {
|
||
|
word >>= 8;
|
||
|
keycode = word & 0xFF;
|
||
|
if (!keycode)
|
||
|
continue;
|
||
|
for (j = 0; j < 6; j++) {
|
||
|
if (r->keycodes[j] == 0) {
|
||
|
r->keycodes[j] = keycode;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void build_report(struct hid_report *r)
|
||
|
{
|
||
|
uint32_t idr;
|
||
|
const uint32_t *pedal1_word = (const uint32_t *)0x08080000UL;
|
||
|
const uint32_t *pedal2_word = (const uint32_t *)0x08080004UL;
|
||
|
|
||
|
idr = GPIOB->IDR;
|
||
|
|
||
|
memset(r, 0, sizeof(struct hid_report));
|
||
|
|
||
|
if (!(idr & (1<<7))) {
|
||
|
append_pedal_keycodes_to_report(r, *pedal1_word);
|
||
|
}
|
||
|
|
||
|
if (!(idr & (1<<6))) {
|
||
|
append_pedal_keycodes_to_report(r, *pedal2_word);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static uint16_t idle;
|
||
|
|
||
|
static enum control_state ep_rx_setup_received_callback(uint8_t endpoint, const struct setup_packet *setup_pkg)
|
||
|
{
|
||
|
enum control_state ret_state = CONTROL_NOT_HANDLED;
|
||
|
static struct hid_report r;
|
||
|
|
||
|
expect_report = false;
|
||
|
expect_eeprom_data = false;
|
||
|
eep_len = 0;
|
||
|
eep_offset = 0;
|
||
|
|
||
|
if (endpoint != 0) {
|
||
|
return ret_state;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (setup_pkg->bm_req_type & 0x80) {
|
||
|
/* Device to host transfer */
|
||
|
if ((setup_pkg->bm_req_type & 0xE0) == 0x20) {
|
||
|
/* Class transfer */
|
||
|
switch (setup_pkg->b_request) {
|
||
|
case 0x01:
|
||
|
/* Get report */
|
||
|
build_report(&r);
|
||
|
usb_endpoint_send(0, (uint8_t *)&r, sizeof(r));
|
||
|
ret_state = CONTROL_DATA_TX;
|
||
|
break;
|
||
|
case 0x02:
|
||
|
/* Get idle */
|
||
|
usb_endpoint_send(0, (uint8_t *)&idle, 2);
|
||
|
break;
|
||
|
}
|
||
|
} else if (setup_pkg->bm_req_type & (1<<6)) {
|
||
|
switch (setup_pkg->b_request) {
|
||
|
case 0x2:
|
||
|
usb_endpoint_send(0, (uint8_t *)0x08080000, setup_pkg->w_length);
|
||
|
ret_state = CONTROL_DATA_TX;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
/* Host to device transfer */
|
||
|
if ((setup_pkg->bm_req_type & 0xE0) == 0x20) {
|
||
|
/* Class transfer */
|
||
|
switch (setup_pkg->b_request) {
|
||
|
case 0xA:
|
||
|
/* Set idle */
|
||
|
idle = setup_pkg->w_value;
|
||
|
ret_state = CONTROL_STATUS_TX;
|
||
|
break;
|
||
|
case 0x09:
|
||
|
/* Set report */
|
||
|
expect_report = true;
|
||
|
usb_endpoint_prepare_receive(0, ep0_buff, sizeof(ep0_buff));
|
||
|
ret_state = CONTROL_DATA_RX;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
} else if (setup_pkg->bm_req_type == 0x1 && setup_pkg->b_request == 11) {
|
||
|
/* Set interface */
|
||
|
/* Nothing to do. Just ack it */
|
||
|
ret_state = CONTROL_STATUS_TX;
|
||
|
} else if (setup_pkg->bm_req_type == (1<<6)) {
|
||
|
/* Vendor Access */
|
||
|
switch (setup_pkg->b_request) {
|
||
|
case 0x1:
|
||
|
if (setup_pkg->w_value)
|
||
|
GPIOA->ODR |= (1<<setup_pkg->w_index);
|
||
|
else
|
||
|
GPIOA->ODR &= ~(1<<setup_pkg->w_index);
|
||
|
ret_state = CONTROL_STATUS_TX;
|
||
|
break;
|
||
|
case 0x2:
|
||
|
expect_eeprom_data = true;
|
||
|
eep_len = setup_pkg->w_length;
|
||
|
eep_offset = setup_pkg->w_index;
|
||
|
usb_endpoint_prepare_receive(0, ep0_buff, sizeof(ep0_buff));
|
||
|
ret_state = CONTROL_DATA_RX;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret_state;
|
||
|
}
|
||
|
|
||
|
static volatile bool ep1_tx_pending;
|
||
|
|
||
|
static void ep_tx_complete_callback(uint8_t endpoint)
|
||
|
{
|
||
|
if (endpoint == 1) {
|
||
|
ep1_tx_pending = false;
|
||
|
} else if (endpoint == 0) {
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void usb_sof_callback(void)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
static volatile bool configured = false;
|
||
|
|
||
|
static void usb_reset_callback(void)
|
||
|
{
|
||
|
configured = false;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static void usb_configured_callback(uint16_t config_idx)
|
||
|
{
|
||
|
GPIOA->ODR &= ~0xF;
|
||
|
usb_endpoint_config(EP_INTERRUPT, 1, false, true);
|
||
|
configured = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
const struct usb_callbacks my_callbacks = {
|
||
|
.ep_rx_data_callback = ep_rx_data_callback,
|
||
|
.ep_rx_setup_received_callback = ep_rx_setup_received_callback,
|
||
|
.ep_tx_complete_callback = ep_tx_complete_callback,
|
||
|
.usb_configured_callback = usb_configured_callback,
|
||
|
.usb_reset_callback = usb_reset_callback,
|
||
|
.usb_sof_callback = usb_sof_callback,
|
||
|
};
|
||
|
|
||
|
static void patch_vid_pid()
|
||
|
{
|
||
|
const uint32_t * const vid_pid_ptr = (const uint32_t *)0x08080008;
|
||
|
const uint32_t vid_pid_word = *vid_pid_ptr;
|
||
|
uint16_t vid;
|
||
|
uint16_t pid;
|
||
|
|
||
|
vid = (vid_pid_word >> 16) & 0xFFFFU;
|
||
|
pid = vid_pid_word & 0xFFFF;
|
||
|
|
||
|
if (vid != 0U && pid != 0U) {
|
||
|
dev_descriptor[8] = (uint8_t)(vid & 0xFF);
|
||
|
dev_descriptor[9] = (uint8_t)((vid >> 8) & 0xFF);
|
||
|
dev_descriptor[10] = (uint8_t)(pid & 0xFF);
|
||
|
dev_descriptor[11] = (uint8_t)((pid >> 8) & 0xFF);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int main(void)
|
||
|
{
|
||
|
bool status_changed = false;
|
||
|
struct hid_report report;
|
||
|
struct hid_report report_send_buff;
|
||
|
|
||
|
RCC->IOPENR |= RCC_IOPENR_IOPAEN;
|
||
|
GPIOA->MODER &= ~(3<<(0*2)) & ~(3<<(1*2)) & ~(3<<(2*2)) & ~(3<<(3*2));
|
||
|
GPIOA->MODER |= (1<<(0*2)) | (1<<(1*2)) | (1<<(2*2)) | (1<<(3*2));
|
||
|
GPIOA->ODR |= (1<<0);
|
||
|
SysTick_Config(3200000);
|
||
|
//setup_usb_clock();
|
||
|
__enable_irq();
|
||
|
|
||
|
create_sn_from_unique_id();
|
||
|
patch_vid_pid();
|
||
|
|
||
|
usb_init(&my_callbacks);
|
||
|
wait_for_ticks(2);
|
||
|
usb_enable(desc_table);
|
||
|
|
||
|
NVIC_SetPriority(SysTick_IRQn, 0);
|
||
|
NVIC_SetPriority(USB_IRQn, 1);
|
||
|
|
||
|
|
||
|
RCC->IOPENR |= RCC_IOPENR_GPIOBEN;
|
||
|
GPIOB->MODER &= ~((0x3<<(6*2)) | (0x3<<(7*2)));
|
||
|
GPIOB->PUPDR |= (1<<(6*2)) | (1<<(7*2));
|
||
|
|
||
|
|
||
|
while (1) {
|
||
|
|
||
|
while (!configured) {
|
||
|
ep1_tx_pending = false;
|
||
|
}
|
||
|
|
||
|
build_report(&report);
|
||
|
|
||
|
if (memcmp(&report, &report_send_buff, sizeof(struct hid_report)))
|
||
|
status_changed = true;
|
||
|
|
||
|
if (status_changed && !ep1_tx_pending) {
|
||
|
memcpy(&report_send_buff, &report, sizeof(struct hid_report));
|
||
|
ep1_tx_pending = true;
|
||
|
status_changed = false;
|
||
|
usb_endpoint_send(1, (uint8_t *)&report_send_buff, sizeof(struct hid_report));
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SysTick_Handler(void)
|
||
|
{
|
||
|
tick++;
|
||
|
}
|