Added USB Serial interface and HID Lighting stubs

main
noah metz 2026-07-09 13:30:02 -06:00
parent 61c912cec6
commit 9818707c06
4 changed files with 382 additions and 100 deletions

@ -149,7 +149,7 @@
* @brief Enables the SERIAL over USB subsystem.
*/
#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
#define HAL_USE_SERIAL_USB FALSE
#define HAL_USE_SERIAL_USB TRUE
#endif
/**

120
main.c

@ -1,20 +1,132 @@
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "ch.h"
#include "hal.h"
#include "usbcfg.h"
/*
* HID report sizes (data only, excluding the report ID byte prepended by the
* USB host). These match the LampArray HID descriptor in usbcfg.c.
*/
#define RPT1_LEN 22 /* LampArrayAttributesReport */
#define RPT3_LEN 28 /* LampAttributesResponseReport */
#define RPT6_LEN 1 /* LampArrayControlReport */
/*
* Called from ISR context fill buf with the response for report `id`.
* buf[0] must be set to the report ID; *len must be set to the total length.
* Return false to stall (unknown ID).
*/
bool isr_hid_get_report(uint8_t id, uint8_t *buf, size_t *len) {
switch (id) {
case 1: /* LampArrayAttributesReport */
*len = 1 + RPT1_LEN;
memset(buf, 0, *len);
buf[0] = 1;
/* LampCount, BoundingBox*, LampArrayKind, MinUpdateInterval all zero */
return true;
case 3: /* LampAttributesResponseReport */
*len = 1 + RPT3_LEN;
memset(buf, 0, *len);
buf[0] = 3;
/* All lamp attribute fields zero until real lamp state is added */
return true;
case 6: /* LampArrayControlReport */
*len = 1 + RPT6_LEN;
buf[0] = 6;
buf[1] = 0; /* AutonomousMode = 0 (host-controlled) */
return true;
default:
return false;
}
}
/*
* Called from ISR context process an incoming SET_REPORT payload.
* buf[0] is the report ID; len includes that byte.
*/
void isr_hid_set_report(uint8_t id, const uint8_t *buf, size_t len) {
(void)buf;
(void)len;
switch (id) {
case 2: /* LampAttributesRequestReport — which lamp to query next */
break;
case 4: /* LampMultiUpdateReport — set up to 8 lamps */
break;
case 5: /* LampRangeUpdateReport — set a range of lamps */
break;
case 6: /* LampArrayControlReport — autonomous mode flag */
break;
default:
break;
}
}
#define SERIAL_LINE_BUF 128
static THD_WORKING_AREA(waSerial, 512);
static THD_FUNCTION(SerialThread, arg) {
(void)arg;
chRegSetThreadName("serial");
char buf[SERIAL_LINE_BUF];
int len;
while (true) {
while (SDU1.config->usbp->state != USB_ACTIVE || !cdc_dtr)
chThdSleepMilliseconds(100);
len = 0;
chnWrite((BaseChannel *)&SDU1, (uint8_t *)"> ", 2);
while (SDU1.config->usbp->state == USB_ACTIVE && cdc_dtr) {
msg_t c = chnGetTimeout((BaseChannel *)&SDU1, TIME_MS2I(100));
if (c < MSG_OK)
continue;
uint8_t ch = (uint8_t)c;
if (ch == '\r' || ch == '\n') {
chnWrite((BaseChannel *)&SDU1, (uint8_t *)"\r\n", 2);
if (len > 0) {
chnWrite((BaseChannel *)&SDU1, (uint8_t *)buf, (size_t)len);
chnWrite((BaseChannel *)&SDU1, (uint8_t *)"\r\n", 2);
len = 0;
}
chnWrite((BaseChannel *)&SDU1, (uint8_t *)"> ", 2);
} else if (ch == 0x08 || ch == 0x7F) { /* backspace / DEL */
if (len > 0) {
len--;
chnWrite((BaseChannel *)&SDU1, (uint8_t *)"\b \b", 3);
}
} else if (ch >= 0x20 && ch < 0x7F) { /* printable ASCII */
if (len < SERIAL_LINE_BUF - 1) {
buf[len++] = (char)ch;
chnWrite((BaseChannel *)&SDU1, &ch, 1);
}
}
}
}
}
int main(void) {
halInit();
chSysInit();
sduObjectInit(&SDU1);
sduStart(&SDU1, &serusbcfg);
usbDisconnectBus(&USBD1);
chThdSleepMilliseconds(1500);
usbStart(&USBD1, &usbcfg);
usbConnectBus(&USBD1);
while(true) {
chThdSleepMilliseconds(1000);
}
chThdCreateStatic(waSerial, sizeof(waSerial), NORMALPRIO, SerialThread, NULL);
chThdSleep(TIME_INFINITE);
}

@ -1,16 +1,45 @@
#include "hal.h"
#include "hal_usb.h"
#include "hal_usb_cdc.h"
#include "hal_serial_usb.h"
#include "usbcfg.h"
SerialUSBDriver SDU1;
volatile bool cdc_dtr = false;
#define HID_EP 1
#define CDC_INT_EP 2
#define CDC_DATA_EP 3
#define HID_EP_SIZE 0x0020U
#define CDC_INT_SIZE 0x0010U
#define CDC_DATA_SIZE 0x0040U
#define HID_IF_DESC_SIZE \
(USB_DESC_INTERFACE_SIZE + 9U + (USB_DESC_ENDPOINT_SIZE * 2U))
#define CDC_FUNC_DESC_SIZE (5U + 5U + 4U + 5U)
#define CDC_IF_DESC_SIZE \
(USB_DESC_INTERFACE_SIZE + CDC_FUNC_DESC_SIZE + \
USB_DESC_ENDPOINT_SIZE + \
USB_DESC_INTERFACE_SIZE + (USB_DESC_ENDPOINT_SIZE * 2U))
#define IAD_CDC_SIZE \
(USB_DESC_INTERFACE_ASSOCIATION_SIZE + CDC_IF_DESC_SIZE)
#define CONFIG_TOTAL_SIZE \
(USB_DESC_CONFIGURATION_SIZE + HID_IF_DESC_SIZE + IAD_CDC_SIZE)
/*
* USB Device Descriptor.
*/
static const uint8_t device_descriptor_data[18] = {
USB_DESC_DEVICE (0x0200, /* bcdUSB (2.0). */
0x00, /* bDeviceClass (Unknown). */
0x00, /* bDeviceSubClass. */
0x00, /* bDeviceProtocol. */
USB_DESC_DEVICE (0x0200,
0xEF, /* bDeviceClass (Miscellaneous). */
0x02, /* bDeviceSubClass (Common). */
0x01, /* bDeviceProtocol (IAD). */
0x40, /* bMaxPacketSize. */
0xEDDE, /* idVendor (ST). */
0xEDDE, /* idVendor. */
0x2025, /* idProduct. */
0x0200, /* bcdDevice. */
1, /* iManufacturer. */
@ -19,53 +48,114 @@ static const uint8_t device_descriptor_data[18] = {
1) /* bNumConfigurations. */
};
/*
* Device Descriptor wrapper.
*/
static const USBDescriptor device_descriptor = {
sizeof device_descriptor_data,
device_descriptor_data
};
/* Configuration Descriptor tree for an HID Lighting Array.*/
static const uint8_t configuration_descriptor_data[41] = {
/* Configuration Descriptor.*/
USB_DESC_CONFIGURATION(41, /* wTotalLength. */
0x01, /* bNumInterfaces. */
/*
* Configuration Descriptor tree for HID LampArray + CDC ACM.
*/
static const uint8_t configuration_descriptor_data[] = {
/* Configuration Descriptor */
USB_DESC_CONFIGURATION(CONFIG_TOTAL_SIZE,
3, /* bNumInterfaces (HID + CDC ctrl + CDC data). */
0x01, /* bConfigurationValue. */
0, /* iConfiguration. */
0xC0, /* bmAttributes (self powered). */
50), /* bMaxPower (100mA). */
/* Interface Descriptor.*/
/* HID Interface (0) */
USB_DESC_INTERFACE (0x00, /* bInterfaceNumber. */
0x00, /* bAlternateSetting. */
0x02, /* bNumEndpoints. */
0x03, /* bInterfaceClass (HID). */
0x00, /* bInterfaceSubClass */
0x00, /* bInterfaceProtocol */
0x00, /* bInterfaceSubClass. */
0x00, /* bInterfaceProtocol. */
0), /* iInterface. */
/* HID Descriptor (HID Section 6.2.1).*/
/* HID Class Descriptor */
USB_DESC_BYTE (0x09), /* bLength. */
USB_DESC_BYTE (0x21), /* bDescriptorType (HID). */
USB_DESC_BCD (0x111), /* bcdHID(1.1) */
USB_DESC_BYTE (0x00), /* bCountryCode(None) */
USB_DESC_BYTE (0x01), /* bNumDescriptors */
USB_DESC_BYTE (0x22), /* bDescriptorType */
USB_DESC_WORD (292), /* wDescriptorLength */
/* Endpoint Descriptors */
USB_DESC_ENDPOINT (0x81, /* bEndpointAddress */
0x03, /* bmAttributes */
0x0020, /* wMaxPacketSize. */
0xFF), /* bInterval. */
USB_DESC_ENDPOINT (0x01, /* bEndpointAddress */
0x03, /* bmAttributes */
0x0020, /* wMaxPacketSize. */
0xFF), /* bInterval. */
USB_DESC_BCD (0x111), /* bcdHID (1.1). */
USB_DESC_BYTE (0x00), /* bCountryCode (None). */
USB_DESC_BYTE (0x01), /* bNumDescriptors. */
USB_DESC_BYTE (0x22), /* bDescriptorType (Report). */
USB_DESC_WORD (292), /* wDescriptorLength. */
/* HID EP1 IN */
USB_DESC_ENDPOINT (USB_ENDPOINT_IN(HID_EP),
USB_EP_MODE_TYPE_INTR,
HID_EP_SIZE,
0xFF),
/* HID EP1 OUT */
USB_DESC_ENDPOINT (USB_ENDPOINT_OUT(HID_EP),
USB_EP_MODE_TYPE_INTR,
HID_EP_SIZE,
0xFF),
/* IAD grouping the two CDC interfaces */
USB_DESC_INTERFACE_ASSOCIATION(
0x01, /* bFirstInterface. */
2, /* bInterfaceCount. */
CDC_COMMUNICATION_INTERFACE_CLASS,
CDC_ABSTRACT_CONTROL_MODEL,
0x01, /* bFunctionProtocol (AT commands). */
0), /* iFunction. */
/* CDC Control Interface (1) */
USB_DESC_INTERFACE (0x01,
0x00,
0x01, /* bNumEndpoints (interrupt IN). */
CDC_COMMUNICATION_INTERFACE_CLASS,
CDC_ABSTRACT_CONTROL_MODEL,
0x01,
0),
/* Header Functional Descriptor */
USB_DESC_BYTE(5), USB_DESC_BYTE(CDC_CS_INTERFACE), USB_DESC_BYTE(CDC_HEADER),
USB_DESC_BCD(0x0110),
/* Call Management Functional Descriptor */
USB_DESC_BYTE(5), USB_DESC_BYTE(CDC_CS_INTERFACE), USB_DESC_BYTE(CDC_CALL_MANAGEMENT),
USB_DESC_BYTE(0x03), /* bmCapabilities. */
USB_DESC_BYTE(0x02), /* bDataInterface. */
/* Abstract Control Management Functional Descriptor */
USB_DESC_BYTE(4), USB_DESC_BYTE(CDC_CS_INTERFACE),
USB_DESC_BYTE(CDC_ABSTRACT_CONTROL_MANAGEMENT),
USB_DESC_BYTE(0x02), /* bmCapabilities. */
/* Union Functional Descriptor */
USB_DESC_BYTE(5), USB_DESC_BYTE(CDC_CS_INTERFACE), USB_DESC_BYTE(CDC_UNION),
USB_DESC_BYTE(0x01), /* bMasterInterface. */
USB_DESC_BYTE(0x02), /* bSlaveInterface. */
/* CDC Interrupt IN EP2 */
USB_DESC_ENDPOINT (USB_ENDPOINT_IN(CDC_INT_EP),
USB_EP_MODE_TYPE_INTR,
CDC_INT_SIZE,
0x01),
/* CDC Data Interface (2) */
USB_DESC_INTERFACE (0x02,
0x00,
0x02, /* bNumEndpoints (bulk IN + OUT). */
CDC_DATA_INTERFACE_CLASS,
0x00,
0x00,
0),
/* CDC Bulk OUT EP3 */
USB_DESC_ENDPOINT (USB_ENDPOINT_OUT(CDC_DATA_EP),
USB_EP_MODE_TYPE_BULK,
CDC_DATA_SIZE,
0x00),
/* CDC Bulk IN EP3 */
USB_DESC_ENDPOINT (USB_ENDPOINT_IN(CDC_DATA_EP),
USB_EP_MODE_TYPE_BULK,
CDC_DATA_SIZE,
0x00),
};
static const USBDescriptor configuration_descriptor = {
@ -221,48 +311,32 @@ static const USBDescriptor hid_report_descriptor = {
hid_report_descriptor_data
};
/*
* U.S. English language identifier.
*/
static const uint8_t usb_string0[] = {
USB_DESC_BYTE(4), /* bLength. */
USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
USB_DESC_WORD(0x0409) /* wLANGID (U.S. English). */
USB_DESC_BYTE(4),
USB_DESC_BYTE(USB_DESCRIPTOR_STRING),
USB_DESC_WORD(0x0409)
};
/*
* Vendor string.
*/
static const uint8_t usb_string1[] = {
USB_DESC_BYTE(16), /* bLength. */
USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
USB_DESC_BYTE(16),
USB_DESC_BYTE(USB_DESCRIPTOR_STRING),
'M', 0, 'e', 0, 't', 0, 'z', 0, 'N', 0, 'e', 0, 't', 0
};
/*
* Device Description string.
*/
static const uint8_t usb_string2[] = {
USB_DESC_BYTE(30), /* bLength. */
USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
USB_DESC_BYTE(30),
USB_DESC_BYTE(USB_DESCRIPTOR_STRING),
'T', 0, 'w', 0, 'i', 0, 'n', 0, 'k', 0, 'l', 0, 'e', 0,
'S', 0, 't', 0, 'i', 0, 'c', 0, 'k', 0, 'V', 0, '2', 0
};
/*
* Serial Number string.
*/
static const uint8_t usb_string3[] = {
USB_DESC_BYTE(8), /* bLength. */
USB_DESC_BYTE(USB_DESCRIPTOR_STRING), /* bDescriptorType. */
'0' + CH_KERNEL_MAJOR, 0,
'0' + CH_KERNEL_MINOR, 0,
'0' + CH_KERNEL_PATCH, 0
USB_DESC_BYTE(2 + 12*2), /* bLength */
USB_DESC_BYTE(USB_DESCRIPTOR_STRING),
'T', 0, 'w', 0, 'i', 0, 'n', 0, 'k', 0, 'l', 0,
'e', 0, 'S', 0, 't', 0, 'i', 0, 'c', 0, 'k', 0
};
/*
* Strings wrappers array.
*/
static const USBDescriptor usb_strings[] = {
{sizeof usb_string0, usb_string0},
{sizeof usb_string1, usb_string1},
@ -270,7 +344,8 @@ static const USBDescriptor usb_strings[] = {
{sizeof usb_string3, usb_string3}
};
static const USBDescriptor *get_descriptor(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t lang) {
static const USBDescriptor *get_descriptor(USBDriver *usbp, uint8_t dtype,
uint8_t dindex, uint16_t lang) {
(void)usbp;
(void)lang;
switch (dtype) {
@ -288,25 +363,109 @@ static const USBDescriptor *get_descriptor(USBDriver *usbp, uint8_t dtype, uint8
return NULL;
}
#define HID_GET_REPORT 0x01U
#define HID_SET_REPORT 0x09U
#define HID_SET_IDLE 0x0AU
#define HID_REPORT_BUF_SIZE 64U
static uint8_t hid_get_buf[HID_REPORT_BUF_SIZE];
static uint8_t hid_set_buf[HID_REPORT_BUF_SIZE];
static struct {
uint8_t id;
uint16_t len;
} hid_set_ctx;
static void hid_set_cb(USBDriver *usbp) {
(void)usbp;
isr_hid_set_report(hid_set_ctx.id, hid_set_buf, hid_set_ctx.len);
}
/* EP1: HID interrupt IN/OUT */
static USBInEndpointState ep1instate;
static USBOutEndpointState ep1outstate;
static const USBEndpointConfig ep1config = {
USB_EP_MODE_TYPE_INTR,
NULL, NULL, NULL,
HID_EP_SIZE, HID_EP_SIZE,
&ep1instate, &ep1outstate,
1, NULL
};
/* EP2: CDC interrupt IN (notifications) */
static USBInEndpointState ep2instate;
static const USBEndpointConfig ep2config = {
USB_EP_MODE_TYPE_INTR,
NULL,
sduInterruptTransmitted,
NULL,
CDC_INT_SIZE, 0x0000,
&ep2instate, NULL,
1, NULL
};
/* EP3: CDC bulk IN/OUT (data) */
static USBInEndpointState ep3instate;
static USBOutEndpointState ep3outstate;
static const USBEndpointConfig ep3config = {
USB_EP_MODE_TYPE_BULK,
NULL,
0x0020,
0x0020,
&ep1instate,
&ep1outstate,
1,
NULL
sduDataTransmitted,
sduDataReceived,
CDC_DATA_SIZE, CDC_DATA_SIZE,
&ep3instate, &ep3outstate,
1, NULL
};
static bool usb_request(USBDriver *usbp) {
(void)usbp;
uint8_t rtype = usbp->setup[0];
uint8_t req = usbp->setup[1];
if ((rtype & USB_RTYPE_RECIPIENT_MASK) == USB_RTYPE_RECIPIENT_INTERFACE &&
req == USB_REQ_SET_INTERFACE) {
usbSetupTransfer(usbp, NULL, 0, NULL);
return true;
}
/* HID class requests targeted at interface 0 */
if ((rtype & (USB_RTYPE_TYPE_MASK | USB_RTYPE_RECIPIENT_MASK)) ==
(USB_RTYPE_TYPE_CLASS | USB_RTYPE_RECIPIENT_INTERFACE) &&
usbp->setup[4] == 0x00U) {
uint8_t report_id = usbp->setup[2];
uint16_t wlength = (uint16_t)usbp->setup[6] |
((uint16_t)usbp->setup[7] << 8);
switch (req) {
case HID_GET_REPORT: {
size_t len = HID_REPORT_BUF_SIZE;
if (!isr_hid_get_report(report_id, hid_get_buf, &len))
return false;
usbSetupTransfer(usbp, hid_get_buf,
len < wlength ? len : wlength, NULL);
return true;
}
case HID_SET_REPORT:
hid_set_ctx.id = report_id;
hid_set_ctx.len = wlength < HID_REPORT_BUF_SIZE
? wlength : HID_REPORT_BUF_SIZE;
usbSetupTransfer(usbp, hid_set_buf, hid_set_ctx.len, hid_set_cb);
return true;
case HID_SET_IDLE:
usbSetupTransfer(usbp, NULL, 0, NULL);
return true;
default:
return false;
}
}
if ((rtype & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS &&
req == CDC_SET_CONTROL_LINE_STATE)
cdc_dtr = (usbp->setup[2] & 0x01U) != 0U;
return sduRequestsHook(usbp);
}
static void usb_event(USBDriver *usbp, usbevent_t event) {
switch (event) {
@ -314,27 +473,39 @@ static void usb_event(USBDriver *usbp, usbevent_t event) {
return;
case USB_EVENT_CONFIGURED:
chSysLockFromISR();
usbInitEndpointI(usbp, 0x01, &ep1config);
if (usbp->state == USB_ACTIVE) {
usbInitEndpointI(usbp, HID_EP, &ep1config);
usbInitEndpointI(usbp, CDC_INT_EP, &ep2config);
usbInitEndpointI(usbp, CDC_DATA_EP, &ep3config);
sduConfigureHookI(&SDU1);
} else if (usbp->state == USB_SELECTED) {
usbDisableEndpointsI(usbp);
}
chSysUnlockFromISR();
return;
case USB_EVENT_RESET:
/* Falls into.*/
case USB_EVENT_UNCONFIGURED:
/* Falls into.*/
case USB_EVENT_SUSPEND:
cdc_dtr = false;
chSysLockFromISR();
sduSuspendHookI(&SDU1);
chSysUnlockFromISR();
return;
case USB_EVENT_WAKEUP:
chSysLockFromISR();
sduWakeupHookI(&SDU1);
chSysUnlockFromISR();
return;
case USB_EVENT_STALLED:
return;
}
return;
}
static void sof_handler(USBDriver *usbp) {
(void)usbp;
osalSysLockFromISR();
sduSOFHookI(&SDU1);
osalSysUnlockFromISR();
}
const USBConfig usbcfg = {
@ -343,3 +514,10 @@ const USBConfig usbcfg = {
usb_request,
sof_handler
};
const SerialUSBConfig serusbcfg = {
&USBD1,
CDC_DATA_EP, /* bulk_in */
CDC_DATA_EP, /* bulk_out */
CDC_INT_EP /* int_in */
};

@ -1,24 +1,16 @@
/*
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef USBCFG_H
#define USBCFG_H
extern const USBConfig usbcfg;
extern const SerialUSBConfig serusbcfg;
extern SerialUSBDriver SDU1;
extern volatile bool cdc_dtr;
#endif /* USBCFG_H */
/* Called from ISR context to fill a GET_REPORT response.
Returns false to stall the request (unknown report id). */
bool isr_hid_get_report(uint8_t id, uint8_t *buf, size_t *len);
/* Called from ISR context when a SET_REPORT payload has arrived. */
void isr_hid_set_report(uint8_t id, const uint8_t *buf, size_t len);
/** @} */
#endif /* USBCFG_H */