File syn_usb.c¶
File List > drivers > syn_usb.c
Go to the documentation of this file
#include "syntropic/drivers/syn_usb.h"
#include <string.h>
static const uint8_t CONFIG_HEADER_TEMPLATE[9] = {
0x09, /* bLength */
0x02, /* bDescriptorType = Configuration */
0x09, 0x00, /* wTotalLength (updated dynamically) */
0x00, /* bNumInterfaces (updated dynamically) */
0x01, /* bConfigurationValue = 1 */
0x00, /* iConfiguration = 0 */
0xC0, /* bmAttributes = Self-Powered */
0x32 /* bMaxPower = 100mA */
};
SYN_Status syn_usb_init(SYN_USB_Device *dev, const uint8_t *device_desc)
{
if (!dev || !device_desc) {
return SYN_INVALID_PARAM;
}
memset(dev, 0, sizeof(*dev));
dev->state = SYN_USB_STATE_DEFAULT;
dev->device_desc = device_desc;
/* Initialize auto-assembly configuration buffer with header template */
memcpy(dev->config_buf, CONFIG_HEADER_TEMPLATE, sizeof(CONFIG_HEADER_TEMPLATE));
dev->config_buf_used = sizeof(CONFIG_HEADER_TEMPLATE);
dev->config_desc = dev->config_buf;
dev->config_desc_len = dev->config_buf_used;
return SYN_OK;
}
SYN_Status syn_usb_register_class(SYN_USB_Device *dev, const SYN_USB_ClassDriver *cls,
const uint8_t *iface_desc, uint16_t iface_desc_len)
{
if (!dev || !cls) {
return SYN_INVALID_PARAM;
}
if (dev->class_count >= SYN_USB_MAX_CLASSES) {
return SYN_ERROR;
}
dev->classes[dev->class_count] = *cls;
dev->class_count++;
if (iface_desc && iface_desc_len > 0 && !dev->raw_config_override) {
if (dev->config_buf_used + iface_desc_len > SYN_USB_MAX_CONFIG_DESC) {
return SYN_ERROR;
}
memcpy(&dev->config_buf[dev->config_buf_used], iface_desc, iface_desc_len);
dev->config_buf_used += iface_desc_len;
/* Update Total Length (bytes 2..3) and Num Interfaces (byte 4) in header */
dev->config_buf[2] = (uint8_t)(dev->config_buf_used & 0xFF);
dev->config_buf[3] = (uint8_t)((dev->config_buf_used >> 8) & 0xFF);
dev->config_buf[4] += cls->iface_count;
dev->config_desc_len = dev->config_buf_used;
}
return SYN_OK;
}
SYN_Status syn_usb_set_string_desc(SYN_USB_Device *dev, uint8_t index, const uint8_t *desc)
{
if (!dev || !desc) {
return SYN_INVALID_PARAM;
}
if (index >= SYN_USB_MAX_STRING_DESCS) {
return SYN_INVALID_PARAM;
}
dev->string_descs[index] = desc;
if (index >= dev->string_desc_count) {
dev->string_desc_count = index + 1U;
}
return SYN_OK;
}
SYN_Status syn_usb_set_raw_config_desc(SYN_USB_Device *dev, const uint8_t *desc, uint16_t len)
{
if (!dev || !desc || len < 9U) {
return SYN_INVALID_PARAM;
}
dev->config_desc = desc;
dev->config_desc_len = len;
dev->raw_config_override = true;
return SYN_OK;
}
SYN_Status syn_usb_process_setup(SYN_USB_Device *dev, const SYN_USB_SetupPacket *pkt, uint8_t *resp,
uint16_t *rlen)
{
if (!dev || !pkt || !resp || !rlen) {
return SYN_INVALID_PARAM;
}
*rlen = 0;
uint8_t req_type = pkt->bmRequestType & 0x60U; /* Bits 6..5: Type (0=Standard, 1=Class) */
uint8_t recipient = pkt->bmRequestType & 0x1FU; /* Bits 4..0: Recipient */
/* Handle Class / Interface Specific Requests */
if (req_type == 0x20U || recipient == 0x01U) {
uint8_t iface = (uint8_t)(pkt->wIndex & 0xFF);
for (uint8_t i = 0U; i < dev->class_count; i++) {
SYN_USB_ClassDriver *cls = &dev->classes[i];
if (iface >= cls->iface_start && iface < (cls->iface_start + cls->iface_count)) {
if (cls->setup) {
return cls->setup(cls->ctx, pkt, resp, rlen);
}
}
}
}
/* Standard Device Requests */
switch (pkt->bRequest) {
case SYN_USB_REQ_SET_ADDRESS:
dev->dev_address = (uint8_t)(pkt->wValue & 0x7FU);
if (dev->dev_address != 0U) {
dev->state = SYN_USB_STATE_ADDRESS;
} else {
dev->state = SYN_USB_STATE_DEFAULT;
}
return SYN_OK;
case SYN_USB_REQ_SET_CONFIGURATION:
dev->config_value = (uint8_t)(pkt->wValue & 0xFFU);
if (dev->config_value != 0U) {
dev->state = SYN_USB_STATE_CONFIGURED;
} else {
dev->state = SYN_USB_STATE_ADDRESS;
}
for (uint8_t i = 0U; i < dev->class_count; i++) {
if (dev->classes[i].configured) {
dev->classes[i].configured(dev->classes[i].ctx, dev->config_value);
}
}
return SYN_OK;
case SYN_USB_REQ_GET_CONFIGURATION:
resp[0] = dev->config_value;
*rlen = 1U;
return SYN_OK;
case SYN_USB_REQ_GET_STATUS:
resp[0] = 0x01U; /* Self-powered */
resp[1] = 0x00U;
*rlen = 2U;
return SYN_OK;
case SYN_USB_REQ_GET_DESCRIPTOR: {
uint8_t desc_type = (uint8_t)(pkt->wValue >> 8);
uint8_t desc_idx = (uint8_t)(pkt->wValue & 0xFF);
if (desc_type == SYN_USB_DESC_TYPE_DEVICE) {
uint16_t len = (pkt->wLength < 18U) ? pkt->wLength : 18U;
memcpy(resp, dev->device_desc, len);
*rlen = len;
return SYN_OK;
} else if (desc_type == SYN_USB_DESC_TYPE_CONFIGURATION) {
uint16_t len =
(pkt->wLength < dev->config_desc_len) ? pkt->wLength : dev->config_desc_len;
memcpy(resp, dev->config_desc, len);
*rlen = len;
return SYN_OK;
} else if (desc_type == SYN_USB_DESC_TYPE_STRING) {
if (desc_idx < SYN_USB_MAX_STRING_DESCS && dev->string_descs[desc_idx]) {
const uint8_t *sdesc = dev->string_descs[desc_idx];
uint16_t slen = sdesc[0];
if (pkt->wLength < slen) {
slen = pkt->wLength;
}
memcpy(resp, sdesc, slen);
*rlen = slen;
return SYN_OK;
}
}
return SYN_ERROR;
}
default:
return SYN_OK;
}
}
bool syn_usb_is_configured(const SYN_USB_Device *dev)
{
return (dev != NULL) && (dev->state == SYN_USB_STATE_CONFIGURED);
}