sustain-pedal-hid-sw/firmware/main.c
2023-08-10 23:00:40 +02:00

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++;
}