stm32: Add support for flashing over USB and serial

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2022-05-11 19:40:28 -04:00 committed by Eric Callahan
parent 799fe5313b
commit ad566230c9
19 changed files with 1824 additions and 69 deletions

View File

@ -2,19 +2,47 @@
mainmenu "CanBoot Configuration" mainmenu "CanBoot Configuration"
config LOW_LEVEL_OPTIONS
bool
default y
config MACH_STM32 config MACH_STM32
bool bool
default y default y
source "src/stm32/Kconfig" source "src/stm32/Kconfig"
config CANSERIAL # Generic configuration options for serial ports
bool config SERIAL_BAUD
default y depends on SERIAL
int "Baud rate for serial port" if LOW_LEVEL_OPTIONS
default 250000
help
Specify the baud rate of the serial port. This should be set
to 250000. Read the FAQ before changing this value.
config CANBUS_FREQUENCY # Generic configuration options for USB
int "CAN bus speed" config USB_VENDOR_ID
default 500000 default 0x1d50
config USB_DEVICE_ID
default 0x614e
config USB_SERIAL_NUMBER_CHIPID
depends on HAVE_CHIPID
default y
config USB_SERIAL_NUMBER
default "12345"
menu "USB ids"
depends on USBSERIAL && LOW_LEVEL_OPTIONS
config USB_VENDOR_ID
hex "USB vendor ID"
config USB_DEVICE_ID
hex "USB device ID"
config USB_SERIAL_NUMBER_CHIPID
bool "USB serial number from CHIPID" if HAVE_CHIPID
config USB_SERIAL_NUMBER
string "USB serial number" if !USB_SERIAL_NUMBER_CHIPID
endmenu
config ENABLE_DOUBLE_RESET config ENABLE_DOUBLE_RESET
bool "Support bootloader entry on rapid double click of reset button" bool "Support bootloader entry on rapid double click of reset button"
@ -35,3 +63,9 @@ config ENABLE_LED
config STATUS_LED_PIN config STATUS_LED_PIN
string "Status LED GPIO Pin" string "Status LED GPIO Pin"
depends on ENABLE_LED depends on ENABLE_LED
# The HAVE_x options allow boards to disable support for some commands
# if the hardware does not support the feature.
config HAVE_CHIPID
bool
default n

View File

@ -82,8 +82,11 @@ command_dispatch(uint8_t *buf, uint_fast8_t msglen)
command_complete(data); command_complete(data);
break; break;
case CMD_GET_CANBUS_ID: case CMD_GET_CANBUS_ID:
command_get_canbus_id(data); if (CONFIG_CANSERIAL) {
break; command_get_canbus_id(data);
break;
}
// NO BREAK
default: default:
// Unknown command or gabage data, NACK it // Unknown command or gabage data, NACK it
command_respond_command_error(); command_respond_command_error();

122
src/generic/serial_irq.c Normal file
View File

@ -0,0 +1,122 @@
// Generic interrupt based serial uart helper code
//
// Copyright (C) 2016-2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include <string.h> // memmove
#include "autoconf.h" // CONFIG_SERIAL_BAUD
#include "board/io.h" // readb
#include "board/irq.h" // irq_save
#include "board/misc.h" // console_sendf
#include "board/pgm.h" // READP
#include "command.h" // DECL_CONSTANT
#include "sched.h" // sched_wake_tasks
#include "serial_irq.h" // serial_enable_tx_irq
#define RX_BUFFER_SIZE 192
static uint8_t receive_buf[RX_BUFFER_SIZE], receive_pos;
static uint8_t transmit_buf[96], transmit_pos, transmit_max;
DECL_CONSTANT("SERIAL_BAUD", CONFIG_SERIAL_BAUD);
DECL_CONSTANT("RECEIVE_WINDOW", RX_BUFFER_SIZE);
// Rx interrupt - store read data
void
serial_rx_byte(uint_fast8_t data)
{
if (data == MESSAGE_SYNC)
sched_wake_tasks();
if (receive_pos >= sizeof(receive_buf))
// Serial overflow - ignore it as crc error will force retransmit
return;
receive_buf[receive_pos++] = data;
}
// Tx interrupt - get next byte to transmit
int
serial_get_tx_byte(uint8_t *pdata)
{
if (transmit_pos >= transmit_max)
return -1;
*pdata = transmit_buf[transmit_pos++];
return 0;
}
// Remove from the receive buffer the given number of bytes
static void
console_pop_input(uint_fast8_t len)
{
uint_fast8_t copied = 0;
for (;;) {
uint_fast8_t rpos = readb(&receive_pos);
uint_fast8_t needcopy = rpos - len;
if (needcopy) {
memmove(&receive_buf[copied], &receive_buf[copied + len]
, needcopy - copied);
copied = needcopy;
sched_wake_tasks();
}
irqstatus_t flag = irq_save();
if (rpos != readb(&receive_pos)) {
// Raced with irq handler - retry
irq_restore(flag);
continue;
}
receive_pos = needcopy;
irq_restore(flag);
break;
}
}
// Process any incoming commands
void
console_task(void)
{
uint_fast8_t rpos = readb(&receive_pos), pop_count;
int_fast8_t ret = command_find_block(receive_buf, rpos, &pop_count);
if (ret > 0)
command_dispatch(receive_buf, pop_count);
if (ret) {
console_pop_input(pop_count);
if (ret > 0)
command_send_ack();
}
}
DECL_TASK(console_task);
// Encode and transmit a "response" message
void
console_sendf(const struct command_encoder *ce, va_list args)
{
// Verify space for message
uint_fast8_t tpos = readb(&transmit_pos), tmax = readb(&transmit_max);
if (tpos >= tmax) {
tpos = tmax = 0;
writeb(&transmit_max, 0);
writeb(&transmit_pos, 0);
}
uint_fast8_t max_size = READP(ce->max_size);
if (tmax + max_size > sizeof(transmit_buf)) {
if (tmax + max_size - tpos > sizeof(transmit_buf))
// Not enough space for message
return;
// Disable TX irq and move buffer
writeb(&transmit_max, 0);
tpos = readb(&transmit_pos);
tmax -= tpos;
memmove(&transmit_buf[0], &transmit_buf[tpos], tmax);
writeb(&transmit_pos, 0);
writeb(&transmit_max, tmax);
serial_enable_tx_irq();
}
// Generate message
uint8_t *buf = &transmit_buf[tmax];
uint_fast8_t msglen = command_encode_and_frame(buf, ce, args);
// Start message transmit
writeb(&transmit_max, tmax + msglen);
serial_enable_tx_irq();
}

13
src/generic/serial_irq.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef __GENERIC_SERIAL_IRQ_H
#define __GENERIC_SERIAL_IRQ_H
#include <stdint.h> // uint32_t
// callback provided by board specific code
void serial_enable_tx_irq(void);
// serial_irq.c
void serial_rx_byte(uint_fast8_t data);
int serial_get_tx_byte(uint8_t *pdata);
#endif // serial_irq.h

531
src/generic/usb_cdc.c Normal file
View File

@ -0,0 +1,531 @@
// Support for standard serial port over USB emulation
//
// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include <string.h> // memmove
#include "autoconf.h" // CONFIG_USB_VENDOR_ID
#include "board/pgm.h" // PROGMEM
#include "board/usb_cdc_ep.h" // USB_CDC_EP_BULK_IN
#include "byteorder.h" // cpu_to_le16
#include "command.h" // output
#include "generic/usbstd.h" // struct usb_device_descriptor
#include "generic/usbstd_cdc.h" // struct usb_cdc_header_descriptor
#include "sched.h" // sched_wake_task
#include "usb_cdc.h" // usb_notify_ep0
// To debug a USB connection over UART, uncomment the two macros
// below, alter the board KConfig to "select USBSERIAL" on a serial
// UART build (so both USB and UART are enabled in a single build),
// compile the code using serial UART, add output() calls to the USB
// code as needed, deploy the new binary, and then connect via
// console.py using UART to see those output() messages.
//#define console_sendf(ce,va) console_sendf_usb(ce,va)
//#define command_find_and_dispatch(rb, rp, pc) ({*(pc) = rp; 1;})
/****************************************************************
* Message block sending
****************************************************************/
static struct task_wake usb_bulk_in_wake;
static uint8_t transmit_buf[192], transmit_pos;
void
usb_notify_bulk_in(void)
{
sched_wake_task(&usb_bulk_in_wake);
}
void
usb_bulk_in_task(void)
{
if (!sched_check_wake(&usb_bulk_in_wake))
return;
uint_fast8_t tpos = transmit_pos;
if (!tpos)
return;
uint_fast8_t max_tpos = (tpos > USB_CDC_EP_BULK_IN_SIZE
? USB_CDC_EP_BULK_IN_SIZE : tpos);
int_fast8_t ret = usb_send_bulk_in(transmit_buf, max_tpos);
if (ret <= 0)
return;
uint_fast8_t needcopy = tpos - ret;
if (needcopy) {
memmove(transmit_buf, &transmit_buf[ret], needcopy);
usb_notify_bulk_in();
}
transmit_pos = needcopy;
}
DECL_TASK(usb_bulk_in_task);
// Encode and transmit a "response" message
void
console_sendf(const struct command_encoder *ce, va_list args)
{
// Verify space for message
uint_fast8_t tpos = transmit_pos, max_size = READP(ce->max_size);
if (tpos + max_size > sizeof(transmit_buf))
// Not enough space for message
return;
// Generate message
uint8_t *buf = &transmit_buf[tpos];
uint_fast8_t msglen = command_encode_and_frame(buf, ce, args);
// Start message transmit
transmit_pos = tpos + msglen;
usb_notify_bulk_in();
}
/****************************************************************
* Message block reading
****************************************************************/
static struct task_wake usb_bulk_out_wake;
static uint8_t receive_buf[128], receive_pos;
void
usb_notify_bulk_out(void)
{
sched_wake_task(&usb_bulk_out_wake);
}
void
usb_bulk_out_task(void)
{
if (!sched_check_wake(&usb_bulk_out_wake))
return;
// Read data
uint_fast8_t rpos = receive_pos, pop_count;
if (rpos + USB_CDC_EP_BULK_OUT_SIZE <= sizeof(receive_buf)) {
int_fast8_t ret = usb_read_bulk_out(
&receive_buf[rpos], USB_CDC_EP_BULK_OUT_SIZE);
if (ret > 0) {
rpos += ret;
usb_notify_bulk_out();
}
} else {
usb_notify_bulk_out();
}
// Process a message block
int_fast8_t ret = command_find_and_dispatch(receive_buf, rpos, &pop_count);
if (ret) {
// Move buffer
uint_fast8_t needcopy = rpos - pop_count;
if (needcopy) {
memmove(receive_buf, &receive_buf[pop_count], needcopy);
usb_notify_bulk_out();
}
rpos = needcopy;
}
receive_pos = rpos;
}
DECL_TASK(usb_bulk_out_task);
/****************************************************************
* USB descriptors
****************************************************************/
#define CONCAT1(a, b) a ## b
#define CONCAT(a, b) CONCAT1(a, b)
#define USB_STR_MANUFACTURER u"CanBoot"
#define USB_STR_PRODUCT CONCAT(u,CONFIG_MCU)
#define USB_STR_SERIAL CONCAT(u,CONFIG_USB_SERIAL_NUMBER)
// String descriptors
enum {
USB_STR_ID_MANUFACTURER = 1, USB_STR_ID_PRODUCT, USB_STR_ID_SERIAL,
};
#define SIZE_cdc_string_langids (sizeof(cdc_string_langids) + 2)
static const struct usb_string_descriptor cdc_string_langids PROGMEM = {
.bLength = SIZE_cdc_string_langids,
.bDescriptorType = USB_DT_STRING,
.data = { cpu_to_le16(USB_LANGID_ENGLISH_US) },
};
#define SIZE_cdc_string_manufacturer \
(sizeof(cdc_string_manufacturer) + sizeof(USB_STR_MANUFACTURER) - 2)
static const struct usb_string_descriptor cdc_string_manufacturer PROGMEM = {
.bLength = SIZE_cdc_string_manufacturer,
.bDescriptorType = USB_DT_STRING,
.data = USB_STR_MANUFACTURER,
};
#define SIZE_cdc_string_product \
(sizeof(cdc_string_product) + sizeof(USB_STR_PRODUCT) - 2)
static const struct usb_string_descriptor cdc_string_product PROGMEM = {
.bLength = SIZE_cdc_string_product,
.bDescriptorType = USB_DT_STRING,
.data = USB_STR_PRODUCT,
};
#define SIZE_cdc_string_serial \
(sizeof(cdc_string_serial) + sizeof(USB_STR_SERIAL) - 2)
static const struct usb_string_descriptor cdc_string_serial PROGMEM = {
.bLength = SIZE_cdc_string_serial,
.bDescriptorType = USB_DT_STRING,
.data = USB_STR_SERIAL,
};
// Device descriptor
static const struct usb_device_descriptor cdc_device_descriptor PROGMEM = {
.bLength = sizeof(cdc_device_descriptor),
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_COMM,
.bMaxPacketSize0 = USB_CDC_EP0_SIZE,
.idVendor = cpu_to_le16(CONFIG_USB_VENDOR_ID),
.idProduct = cpu_to_le16(CONFIG_USB_DEVICE_ID),
.bcdDevice = cpu_to_le16(0x0100),
.iManufacturer = USB_STR_ID_MANUFACTURER,
.iProduct = USB_STR_ID_PRODUCT,
.iSerialNumber = USB_STR_ID_SERIAL,
.bNumConfigurations = 1,
};
// Config descriptor
static const struct config_s {
struct usb_config_descriptor config;
struct usb_interface_descriptor iface0;
struct usb_cdc_header_descriptor cdc_hdr;
struct usb_cdc_acm_descriptor cdc_acm;
struct usb_cdc_union_descriptor cdc_union;
struct usb_endpoint_descriptor ep1;
struct usb_interface_descriptor iface1;
struct usb_endpoint_descriptor ep2;
struct usb_endpoint_descriptor ep3;
} PACKED cdc_config_descriptor PROGMEM = {
.config = {
.bLength = sizeof(cdc_config_descriptor.config),
.bDescriptorType = USB_DT_CONFIG,
.wTotalLength = cpu_to_le16(sizeof(cdc_config_descriptor)),
.bNumInterfaces = 2,
.bConfigurationValue = 1,
.bmAttributes = 0xC0,
.bMaxPower = 50,
},
.iface0 = {
.bLength = sizeof(cdc_config_descriptor.iface0),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
},
.cdc_hdr = {
.bLength = sizeof(cdc_config_descriptor.cdc_hdr),
.bDescriptorType = USB_CDC_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
.bcdCDC = 0x0110,
},
.cdc_acm = {
.bLength = sizeof(cdc_config_descriptor.cdc_acm),
.bDescriptorType = USB_CDC_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ACM_TYPE,
.bmCapabilities = 0x06,
},
.cdc_union = {
.bLength = sizeof(cdc_config_descriptor.cdc_union),
.bDescriptorType = USB_CDC_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_UNION_TYPE,
.bMasterInterface0 = 0,
.bSlaveInterface0 = 1,
},
.ep1 = {
.bLength = sizeof(cdc_config_descriptor.ep1),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_CDC_EP_ACM | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = cpu_to_le16(USB_CDC_EP_ACM_SIZE),
.bInterval = 255,
},
.iface1 = {
.bLength = sizeof(cdc_config_descriptor.iface1),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 1,
.bNumEndpoints = 2,
.bInterfaceClass = 0x0A,
},
.ep2 = {
.bLength = sizeof(cdc_config_descriptor.ep2),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_CDC_EP_BULK_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(USB_CDC_EP_BULK_OUT_SIZE),
},
.ep3 = {
.bLength = sizeof(cdc_config_descriptor.ep3),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_CDC_EP_BULK_IN | USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(USB_CDC_EP_BULK_IN_SIZE),
},
};
// List of available descriptors
static const struct descriptor_s {
uint_fast16_t wValue;
uint_fast16_t wIndex;
const void *desc;
uint_fast8_t size;
} cdc_descriptors[] PROGMEM = {
{ USB_DT_DEVICE<<8, 0x0000,
&cdc_device_descriptor, sizeof(cdc_device_descriptor) },
{ USB_DT_CONFIG<<8, 0x0000,
&cdc_config_descriptor, sizeof(cdc_config_descriptor) },
{ USB_DT_STRING<<8, 0x0000,
&cdc_string_langids, SIZE_cdc_string_langids },
{ (USB_DT_STRING<<8) | USB_STR_ID_MANUFACTURER, USB_LANGID_ENGLISH_US,
&cdc_string_manufacturer, SIZE_cdc_string_manufacturer },
{ (USB_DT_STRING<<8) | USB_STR_ID_PRODUCT, USB_LANGID_ENGLISH_US,
&cdc_string_product, SIZE_cdc_string_product },
#if !CONFIG_USB_SERIAL_NUMBER_CHIPID
{ (USB_DT_STRING<<8) | USB_STR_ID_SERIAL, USB_LANGID_ENGLISH_US,
&cdc_string_serial, SIZE_cdc_string_serial },
#endif
};
// Fill in a USB serial string descriptor from a chip id
void
usb_fill_serial(struct usb_string_descriptor *desc, int strlen, void *id)
{
desc->bLength = sizeof(*desc) + strlen * sizeof(desc->data[0]);
desc->bDescriptorType = USB_DT_STRING;
uint8_t *src = id;
int i;
for (i = 0; i < strlen; i++) {
uint8_t c = i & 1 ? src[i/2] & 0x0f : src[i/2] >> 4;
desc->data[i] = c < 10 ? c + '0' : c - 10 + 'A';
}
}
/****************************************************************
* USB endpoint 0 control message handling
****************************************************************/
// State tracking
enum {
UX_READ = 1<<0, UX_SEND = 1<<1, UX_SEND_PROGMEM = 1<<2, UX_SEND_ZLP = 1<<3
};
static void *usb_xfer_data;
static uint8_t usb_xfer_size, usb_xfer_flags;
// Set the USB "stall" condition
static void
usb_do_stall(void)
{
usb_stall_ep0();
usb_xfer_flags = 0;
}
// Transfer data on the usb endpoint 0
static void
usb_do_xfer(void *data, uint_fast8_t size, uint_fast8_t flags)
{
for (;;) {
uint_fast8_t xs = size;
if (xs > USB_CDC_EP0_SIZE)
xs = USB_CDC_EP0_SIZE;
int_fast8_t ret;
if (flags & UX_READ)
ret = usb_read_ep0(data, xs);
else if (NEED_PROGMEM && flags & UX_SEND_PROGMEM)
ret = usb_send_ep0_progmem(data, xs);
else
ret = usb_send_ep0(data, xs);
if (ret == xs) {
// Success
data += xs;
size -= xs;
if (!size) {
// Entire transfer completed successfully
if (flags & UX_READ) {
// Send status packet at end of read
flags = UX_SEND;
continue;
}
if (xs == USB_CDC_EP0_SIZE && flags & UX_SEND_ZLP)
// Must send zero-length-packet
continue;
usb_xfer_flags = 0;
usb_notify_ep0();
return;
}
continue;
}
if (ret == -1) {
// Interface busy - retry later
usb_xfer_data = data;
usb_xfer_size = size;
usb_xfer_flags = flags;
return;
}
// Error
usb_do_stall();
return;
}
}
static void
usb_req_get_descriptor(struct usb_ctrlrequest *req)
{
if (req->bRequestType != USB_DIR_IN)
goto fail;
void *desc = NULL;
uint_fast8_t flags, size, i;
for (i=0; i<ARRAY_SIZE(cdc_descriptors); i++) {
const struct descriptor_s *d = &cdc_descriptors[i];
if (READP(d->wValue) == req->wValue
&& READP(d->wIndex) == req->wIndex) {
flags = NEED_PROGMEM ? UX_SEND_PROGMEM : UX_SEND;
size = READP(d->size);
desc = (void*)READP(d->desc);
}
}
if (CONFIG_USB_SERIAL_NUMBER_CHIPID
&& req->wValue == ((USB_DT_STRING<<8) | USB_STR_ID_SERIAL)
&& req->wIndex == USB_LANGID_ENGLISH_US) {
struct usb_string_descriptor *usbserial_serialid;
usbserial_serialid = usbserial_get_serialid();
flags = UX_SEND;
size = usbserial_serialid->bLength;
desc = (void*)usbserial_serialid;
}
if (desc) {
if (size > req->wLength)
size = req->wLength;
else if (size < req->wLength)
flags |= UX_SEND_ZLP;
usb_do_xfer(desc, size, flags);
return;
}
fail:
usb_do_stall();
}
static void
usb_req_set_address(struct usb_ctrlrequest *req)
{
if (req->bRequestType || req->wIndex || req->wLength) {
usb_do_stall();
return;
}
usb_set_address(req->wValue);
}
static void
usb_req_set_configuration(struct usb_ctrlrequest *req)
{
if (req->bRequestType || req->wValue != 1 || req->wIndex || req->wLength) {
usb_do_stall();
return;
}
usb_set_configure();
usb_notify_bulk_in();
usb_notify_bulk_out();
usb_do_xfer(NULL, 0, UX_SEND);
}
static struct usb_cdc_line_coding line_coding;
static uint8_t line_control_state;
static void
check_reboot(void)
{
}
static void
usb_req_set_line_coding(struct usb_ctrlrequest *req)
{
if (req->bRequestType != 0x21 || req->wValue || req->wIndex
|| req->wLength != sizeof(line_coding)) {
usb_do_stall();
return;
}
usb_do_xfer(&line_coding, sizeof(line_coding), UX_READ);
check_reboot();
}
static void
usb_req_get_line_coding(struct usb_ctrlrequest *req)
{
if (req->bRequestType != 0xa1 || req->wValue || req->wIndex
|| req->wLength < sizeof(line_coding)) {
usb_do_stall();
return;
}
usb_do_xfer(&line_coding, sizeof(line_coding), UX_SEND);
}
static void
usb_req_set_line(struct usb_ctrlrequest *req)
{
if (req->bRequestType != 0x21 || req->wIndex || req->wLength) {
usb_do_stall();
return;
}
line_control_state = req->wValue;
usb_do_xfer(NULL, 0, UX_SEND);
check_reboot();
}
static void
usb_state_ready(void)
{
struct usb_ctrlrequest req;
int_fast8_t ret = usb_read_ep0_setup(&req, sizeof(req));
if (ret != sizeof(req))
return;
switch (req.bRequest) {
case USB_REQ_GET_DESCRIPTOR: usb_req_get_descriptor(&req); break;
case USB_REQ_SET_ADDRESS: usb_req_set_address(&req); break;
case USB_REQ_SET_CONFIGURATION: usb_req_set_configuration(&req); break;
case USB_CDC_REQ_SET_LINE_CODING: usb_req_set_line_coding(&req); break;
case USB_CDC_REQ_GET_LINE_CODING: usb_req_get_line_coding(&req); break;
case USB_CDC_REQ_SET_CONTROL_LINE_STATE: usb_req_set_line(&req); break;
default: usb_do_stall(); break;
}
}
// State tracking dispatch
static struct task_wake usb_ep0_wake;
void
usb_notify_ep0(void)
{
sched_wake_task(&usb_ep0_wake);
}
void
usb_ep0_task(void)
{
if (!sched_check_wake(&usb_ep0_wake))
return;
if (usb_xfer_flags)
usb_do_xfer(usb_xfer_data, usb_xfer_size, usb_xfer_flags);
else
usb_state_ready();
}
DECL_TASK(usb_ep0_task);
void
usb_shutdown(void)
{
usb_notify_bulk_in();
usb_notify_bulk_out();
usb_notify_ep0();
}
DECL_SHUTDOWN(usb_shutdown);

33
src/generic/usb_cdc.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef __GENERIC_USB_CDC_H
#define __GENERIC_USB_CDC_H
#include <stdint.h> // uint_fast8_t
// endpoint sizes
enum {
USB_CDC_EP0_SIZE = 16,
USB_CDC_EP_ACM_SIZE = 8,
USB_CDC_EP_BULK_OUT_SIZE = 64,
USB_CDC_EP_BULK_IN_SIZE = 64,
};
// callbacks provided by board specific code
int_fast8_t usb_read_bulk_out(void *data, uint_fast8_t max_len);
int_fast8_t usb_send_bulk_in(void *data, uint_fast8_t len);
int_fast8_t usb_read_ep0(void *data, uint_fast8_t max_len);
int_fast8_t usb_read_ep0_setup(void *data, uint_fast8_t max_len);
int_fast8_t usb_send_ep0(const void *data, uint_fast8_t len);
int_fast8_t usb_send_ep0_progmem(const void *data, uint_fast8_t len);
void usb_stall_ep0(void);
void usb_set_address(uint_fast8_t addr);
void usb_set_configure(void);
void usb_request_bootloader(void);
struct usb_string_descriptor *usbserial_get_serialid(void);
// usb_cdc.c
void usb_fill_serial(struct usb_string_descriptor *desc, int strlen, void *id);
void usb_notify_bulk_in(void);
void usb_notify_bulk_out(void);
void usb_notify_ep0(void);
#endif // usb_cdc.h

11
src/generic/usb_cdc_ep.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef __GENERIC_USB_CDC_EP_H
#define __GENERIC_USB_CDC_EP_H
// Default USB endpoint ids
enum {
USB_CDC_EP_ACM = 1,
USB_CDC_EP_BULK_OUT = 2,
USB_CDC_EP_BULK_IN = 3,
};
#endif // usb_cdc_ep.h

122
src/generic/usbstd.h Normal file
View File

@ -0,0 +1,122 @@
// Standard definitions for USB commands and data structures
#ifndef __GENERIC_USBSTD_H
#define __GENERIC_USBSTD_H
#include <stdint.h> // uint8_t
#include "compiler.h" // PACKED
#define USB_DIR_OUT 0 /* to device */
#define USB_DIR_IN 0x80 /* to host */
#define USB_REQ_GET_STATUS 0x00
#define USB_REQ_CLEAR_FEATURE 0x01
#define USB_REQ_SET_FEATURE 0x03
#define USB_REQ_SET_ADDRESS 0x05
#define USB_REQ_GET_DESCRIPTOR 0x06
#define USB_REQ_SET_DESCRIPTOR 0x07
#define USB_REQ_GET_CONFIGURATION 0x08
#define USB_REQ_SET_CONFIGURATION 0x09
#define USB_REQ_GET_INTERFACE 0x0A
#define USB_REQ_SET_INTERFACE 0x0B
#define USB_REQ_SYNCH_FRAME 0x0C
struct usb_ctrlrequest {
uint8_t bRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} PACKED;
#define USB_DT_DEVICE 0x01
#define USB_DT_CONFIG 0x02
#define USB_DT_STRING 0x03
#define USB_DT_INTERFACE 0x04
#define USB_DT_ENDPOINT 0x05
#define USB_DT_DEVICE_QUALIFIER 0x06
#define USB_DT_OTHER_SPEED_CONFIG 0x07
#define USB_DT_ENDPOINT_COMPANION 0x30
struct usb_device_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
uint8_t bNumConfigurations;
} PACKED;
#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */
#define USB_CLASS_AUDIO 1
#define USB_CLASS_COMM 2
#define USB_CLASS_HID 3
#define USB_CLASS_PHYSICAL 5
#define USB_CLASS_STILL_IMAGE 6
#define USB_CLASS_PRINTER 7
#define USB_CLASS_MASS_STORAGE 8
#define USB_CLASS_HUB 9
struct usb_config_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
} PACKED;
struct usb_interface_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface;
} PACKED;
struct usb_endpoint_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
} PACKED;
#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */
#define USB_ENDPOINT_DIR_MASK 0x80
#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
#define USB_ENDPOINT_XFER_BULK 2
#define USB_ENDPOINT_XFER_INT 3
#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80
struct usb_string_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
//uint16_t data[];
typeof(*u"") data[];
} PACKED;
#define USB_LANGID_ENGLISH_US 0x0409
#endif // usbstd.h

49
src/generic/usbstd_cdc.h Normal file
View File

@ -0,0 +1,49 @@
// Standard definitions for USB CDC devices
#ifndef __GENERIC_USBSTD_CDC_H
#define __GENERIC_USBSTD_CDC_H
#define USB_CDC_SUBCLASS_ACM 0x02
#define USB_CDC_ACM_PROTO_AT_V25TER 1
struct usb_cdc_header_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint16_t bcdCDC;
} PACKED;
#define USB_CDC_HEADER_TYPE 0x00
#define USB_CDC_ACM_TYPE 0x02
#define USB_CDC_UNION_TYPE 0x06
#define USB_CDC_CS_INTERFACE 0x24
#define USB_CDC_CS_ENDPOINT 0x25
struct usb_cdc_acm_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bmCapabilities;
} PACKED;
struct usb_cdc_union_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bMasterInterface0;
uint8_t bSlaveInterface0;
} PACKED;
#define USB_CDC_REQ_SET_LINE_CODING 0x20
#define USB_CDC_REQ_GET_LINE_CODING 0x21
#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22
struct usb_cdc_line_coding {
uint32_t dwDTERate;
uint8_t bCharFormat;
uint8_t bParityType;
uint8_t bDataBits;
} PACKED;
#endif // usbstd_cdc.h

View File

@ -9,6 +9,12 @@
#include "bootentry.h" // bootentry_check #include "bootentry.h" // bootentry_check
#include "sched.h" // sched_check_periodic #include "sched.h" // sched_check_periodic
// Wrapper for Klipper compatibility
void
sched_wake_tasks(void)
{
}
// Note that a task is ready to run // Note that a task is ready to run
void void
sched_wake_task(struct task_wake *w) sched_wake_task(struct task_wake *w)

View File

@ -17,6 +17,7 @@ struct task_wake {
}; };
// sched.c // sched.c
void sched_wake_tasks(void);
void sched_wake_task(struct task_wake *w); void sched_wake_task(struct task_wake *w);
uint8_t sched_check_wake(struct task_wake *w); uint8_t sched_check_wake(struct task_wake *w);
void sched_main(void); void sched_main(void);

View File

@ -2,44 +2,77 @@
if MACH_STM32 if MACH_STM32
config ENABLE_STM32F4 config STM32_SELECT
bool bool
default y
select HAVE_GPIO
select HAVE_GPIO_ADC
select HAVE_GPIO_I2C if !(MACH_STM32F031 || MACH_STM32H7)
select HAVE_GPIO_SPI if !MACH_STM32F031
select HAVE_GPIO_HARD_PWM if MACH_STM32F1 || MACH_STM32F4 || MACH_STM32H7
select HAVE_GPIO_BITBANGING if !MACH_STM32F031
select HAVE_STRICT_TIMING
select HAVE_CHIPID
select HAVE_STEPPER_BOTH_EDGE
config BOARD_DIRECTORY config BOARD_DIRECTORY
string string
default "stm32" default "stm32"
######################################################################
# Chip selection
######################################################################
choice choice
prompt "Processor model" prompt "Processor model"
config MACH_STM32F042
bool "STM32F042"
select MACH_STM32F0
select MACH_STM32F0x2
config MACH_STM32F072
bool "STM32F072"
select MACH_STM32F0
select MACH_STM32F0x2
config MACH_STM32F103 config MACH_STM32F103
bool "STM32F103" bool "STM32F103"
select MACH_STM32F1 select MACH_STM32F1
config MACH_STM32F207 config MACH_STM32F207
bool "STM32F207" if ENABLE_STM32F4 bool "STM32F207" if 0
select MACH_STM32F2 select MACH_STM32F2
config MACH_STM32F401
bool "STM32F401" if 0
select MACH_STM32F4
config MACH_STM32F405 config MACH_STM32F405
bool "STM32F405" if ENABLE_STM32F4 bool "STM32F405" if 0
select MACH_STM32F4 select MACH_STM32F4
select MACH_STM32F4x5 select MACH_STM32F4x5
config MACH_STM32F407 config MACH_STM32F407
bool "STM32F407" if ENABLE_STM32F4 bool "STM32F407" if 0
select MACH_STM32F4 select MACH_STM32F4
select MACH_STM32F4x5 select MACH_STM32F4x5
config MACH_STM32F429 config MACH_STM32F429
bool "STM32F429" if ENABLE_STM32F4 bool "STM32F429" if 0
select MACH_STM32F4 select MACH_STM32F4
select MACH_STM32F4x5 select MACH_STM32F4x5
config MACH_STM32F446 config MACH_STM32F446
bool "STM32F446" if ENABLE_STM32F4 bool "STM32F446" if 0
select MACH_STM32F4 select MACH_STM32F4
config MACH_STM32F031
bool "STM32F031" if 0
select MACH_STM32F0
config MACH_STM32F042
bool "STM32F042"
select MACH_STM32F0
select MACH_STM32F0x2
config MACH_STM32F070
bool "STM32F070" if 0
select MACH_STM32F0
config MACH_STM32F072
bool "STM32F072"
select MACH_STM32F0
select MACH_STM32F0x2
config MACH_STM32G0B1
bool "STM32G0B1" if 0
select MACH_STM32G0
config MACH_STM32H743
bool "STM32H743" if 0
select MACH_STM32H7
config MACH_STM32H750
bool "STM32H750" if 0
select MACH_STM32H7
endchoice endchoice
config MACH_STM32F0 config MACH_STM32F0
@ -50,17 +83,40 @@ config MACH_STM32F2
bool bool
config MACH_STM32F4 config MACH_STM32F4
bool bool
config MACH_STM32G0
bool
config MACH_STM32H7
bool
config MACH_STM32F0x2 # F042, F072 series
bool
config MACH_STM32F4x5 # F405, F407, F429 series
bool
config HAVE_STM32_USBFS
bool
default y if MACH_STM32F103 || MACH_STM32F0x2 || MACH_STM32F070 || MACH_STM32G0
config HAVE_STM32_USBOTG
bool
default y if MACH_STM32F2 || MACH_STM32F4 || MACH_STM32H7
config HAVE_STM32_CANBUS
bool
default y if MACH_STM32F1 || MACH_STM32F2 || MACH_STM32F4x5 || MACH_STM32F446 || MACH_STM32F0x2
config MCU config MCU
string string
default "stm32f031x6" if MACH_STM32F031
default "stm32f042x6" if MACH_STM32F042 default "stm32f042x6" if MACH_STM32F042
default "stm32f070xb" if MACH_STM32F070
default "stm32f072xb" if MACH_STM32F072 default "stm32f072xb" if MACH_STM32F072
default "stm32f103xe" if MACH_STM32F103 default "stm32f103xe" if MACH_STM32F103
default "stm32f207xx" if MACH_STM32F207 default "stm32f207xx" if MACH_STM32F207
default "stm32f401xc" if MACH_STM32F401
default "stm32f405xx" if MACH_STM32F405 default "stm32f405xx" if MACH_STM32F405
default "stm32f407xx" if MACH_STM32F407 default "stm32f407xx" if MACH_STM32F407
default "stm32f429xx" if MACH_STM32F429 default "stm32f429xx" if MACH_STM32F429
default "stm32f446xx" if MACH_STM32F446 default "stm32f446xx" if MACH_STM32F446
default "stm32g0b1xx" if MACH_STM32G0B1
default "stm32h743xx" if MACH_STM32H743
default "stm32h750xx" if MACH_STM32H750
config CLOCK_FREQ config CLOCK_FREQ
int int
@ -68,50 +124,49 @@ config CLOCK_FREQ
default 64000000 if MACH_STM32F103 && STM32_CLOCK_REF_INTERNAL default 64000000 if MACH_STM32F103 && STM32_CLOCK_REF_INTERNAL
default 72000000 if MACH_STM32F103 default 72000000 if MACH_STM32F103
default 120000000 if MACH_STM32F207 default 120000000 if MACH_STM32F207
default 84000000 if MACH_STM32F401
default 168000000 if MACH_STM32F4x5 default 168000000 if MACH_STM32F4x5
default 180000000 if MACH_STM32F446 default 180000000 if MACH_STM32F446
default 64000000 if MACH_STM32G0
default 400000000 if MACH_STM32H7 # 400Mhz is max Klipper currently supports
config FLASH_SIZE config FLASH_SIZE
hex hex
default 0x4000 if MACH_STM32F031
default 0x8000 if MACH_STM32F042 default 0x8000 if MACH_STM32F042
default 0x20000 if MACH_STM32F072 default 0x20000 if MACH_STM32F070 || MACH_STM32F072
default 0x10000 if MACH_STM32F103 # Flash size of stm32f103x8 (64KiB) default 0x10000 if MACH_STM32F103 # Flash size of stm32f103x8 (64KiB)
default 0x40000 if MACH_STM32F2 default 0x40000 if MACH_STM32F2 || MACH_STM32F401
default 0x80000 if MACH_STM32F4 default 0x80000 if MACH_STM32F4x5 || MACH_STM32F446
default 0x20000 if MACH_STM32G0B1
default 0x20000 if MACH_STM32H750
default 0x200000 if MACH_STM32H743
config RAM_START config RAM_START
hex hex
default 0x24000000 if MACH_STM32H743
default 0x20000000 default 0x20000000
config RAM_SIZE config RAM_SIZE
hex hex
default 0x1000 if MACH_STM32F031
default 0x1800 if MACH_STM32F042 default 0x1800 if MACH_STM32F042
default 0x4000 if MACH_STM32F072 default 0x4000 if MACH_STM32F070 || MACH_STM32F072
default 0x5000 if MACH_STM32F103 # Ram size of stm32f103x8 (20KiB) default 0x5000 if MACH_STM32F103 # Ram size of stm32f103x8 (20KiB)
default 0x20000 if MACH_STM32F207 default 0x20000 if MACH_STM32F207
default 0x20000 if MACH_STM32F4 default 0x10000 if MACH_STM32F401
default 0x20000 if MACH_STM32F4x5 || MACH_STM32F446
default 0x24000 if MACH_STM32G0B1
default 0x20000 if MACH_STM32H750
default 0x80000 if MACH_STM32H743
config STACK_SIZE config STACK_SIZE
int int
default 512 default 512
config FLASH_START
hex
default 0x8000000
config APPLICATION_START
hex
default 0x8002000 if MACH_STM32F103
default 0x8002000
config ARMCM_RAM_VECTORTABLE
bool
default y if MACH_STM32F0 && FLASH_START != 0x8000000
default n
config STM32F103GD_DISABLE_SWD config STM32F103GD_DISABLE_SWD
bool "Disable SWD at startup (for GigaDevice stm32f103 clones)" bool "Disable SWD at startup (for GigaDevice stm32f103 clones)"
depends on MACH_STM32F103 depends on MACH_STM32F103 && LOW_LEVEL_OPTIONS
default n default n
help help
The GigaDevice clone of the STM32F103 may not be able to The GigaDevice clone of the STM32F103 may not be able to
@ -119,8 +174,56 @@ config STM32F103GD_DISABLE_SWD
and PA14 pins from being available. Selecting this option and PA14 pins from being available. Selecting this option
disables SWD at startup and thus makes these pins available. disables SWD at startup and thus makes these pins available.
######################################################################
# Bootloader
######################################################################
choice choice
prompt "Clock Reference" prompt "Bootloader offset" if 0
config STM32_FLASH_START_2000
bool "8KiB bootloader" if MACH_STM32F103 || MACH_STM32F070 || MACH_STM32G0 || MACH_STM32F0x2
config STM32_FLASH_START_5000
bool "20KiB bootloader" if MACH_STM32F103
config STM32_FLASH_START_7000
bool "28KiB bootloader" if MACH_STM32F103
config STM32_FLASH_START_8000
bool "32KiB bootloader" if MACH_STM32F1 || MACH_STM32F2 || MACH_STM32F4
config STM32_FLASH_START_8800
bool "34KiB bootloader (Chitu v6 Bootloader)" if MACH_STM32F103
config STM32_FLASH_START_20200
bool "128KiB bootloader with 512 byte offset (Prusa Buddy)" if MACH_STM32F4x5
config STM32_FLASH_START_C000
bool "48KiB bootloader (MKS Robin Nano V3)" if MACH_STM32F4x5
config STM32_FLASH_START_10000
bool "64KiB bootloader" if MACH_STM32F103 || MACH_STM32F446 || MACH_STM32F401
config STM32_FLASH_START_800
bool "2KiB bootloader (HID Bootloader)" if MACH_STM32F103
config STM32_FLASH_START_4000
bool "16KiB bootloader (HID Bootloader)" if MACH_STM32F207 || MACH_STM32F401 || MACH_STM32F4x5 || MACH_STM32F103 || MACH_STM32F072
config STM32_FLASH_START_20000
bool "128KiB bootloader (SKR SE BX v2.0)" if MACH_STM32H743
config STM32_FLASH_START_0000
bool "No bootloader"
endchoice
config FLASH_START
hex
default 0x8000000
config ARMCM_RAM_VECTORTABLE
bool
default y if MACH_STM32F0 && FLASH_START != 0x8000000
default n
######################################################################
# Clock
######################################################################
choice
prompt "Clock Reference" if LOW_LEVEL_OPTIONS
config STM32_CLOCK_REF_8M config STM32_CLOCK_REF_8M
bool "8 MHz crystal" bool "8 MHz crystal"
config STM32_CLOCK_REF_12M config STM32_CLOCK_REF_12M
@ -140,26 +243,8 @@ config CLOCK_REF_FREQ
default 1 if STM32_CLOCK_REF_INTERNAL default 1 if STM32_CLOCK_REF_INTERNAL
default 8000000 default 8000000
choice
prompt "CAN pins"
config STM32_CANBUS_PA11_PA12
bool "Pins PA11(rx) and PA12(tx)"
config STM32_CANBUS_PA11_PA12_REMAP
bool "Pins on PA9(rx) and PA10(tx)" if MACH_STM32F042
config STM32_CANBUS_PB8_PB9
bool "Pins PB8(rx) and PB9(tx)"
config STM32_CANBUS_PI9_PH13
bool "Pins PI9(rx) and PH13(tx)" if MACH_STM32F4
config STM32_CANBUS_PB5_PB6
bool "Pins PB5(rx) and PB6(tx)" if MACH_STM32F4
config STM32_CANBUS_PB12_PB13
bool "Pins PB12(rx) and PB13(tx)" if MACH_STM32F4
config STM32_CANBUS_PD0_PD1
bool "Pins PD0(rx) and PD1(tx)"
endchoice
config STM32F0_TRIM config STM32F0_TRIM
int "Internal clock trim override" if MACH_STM32F0 && STM32_CLOCK_REF_INTERNAL int "Internal clock trim override" if LOW_LEVEL_OPTIONS && MACH_STM32F0 && STM32_CLOCK_REF_INTERNAL && !USBSERIAL
default 16 default 16
help help
Specify the internal clock trim value. Setting this can be Specify the internal clock trim value. Setting this can be
@ -167,6 +252,100 @@ config STM32F0_TRIM
Default is 16 (use factory default). Each increment increases Default is 16 (use factory default). Each increment increases
the clock rate by ~240KHz. the clock rate by ~240KHz.
######################################################################
# Communication inteface
######################################################################
config USBSERIAL
bool
config SERIAL
bool
config CANSERIAL
bool
choice
prompt "Communication interface"
config STM32_USB_PA11_PA12
bool "USB (on PA11/PA12)" if HAVE_STM32_USBFS || HAVE_STM32_USBOTG
select USBSERIAL
config STM32_USB_PA11_PA12_REMAP
bool "USB (on PA9/PA10)" if LOW_LEVEL_OPTIONS && MACH_STM32F042
select USBSERIAL
config STM32_USB_PB14_PB15
bool "USB (on PB14/PB15)"
depends on MACH_STM32H7
select USBSERIAL
config STM32_SERIAL_USART1
bool "Serial (on USART1 PA10/PA9)"
select SERIAL
config STM32_SERIAL_USART1_ALT_PB7_PB6
bool "Serial (on USART1 PB7/PB6)" if LOW_LEVEL_OPTIONS
select SERIAL
config STM32_SERIAL_USART2
bool "Serial (on USART2 PA3/PA2)" if LOW_LEVEL_OPTIONS
select SERIAL
config STM32_SERIAL_USART2_ALT_PA15_PA14
bool "Serial (on USART2 PA15/PA14)" if LOW_LEVEL_OPTIONS && MACH_STM32F0
select SERIAL
config STM32_SERIAL_USART2_ALT_PD6_PD5
bool "Serial (on USART2 PD6/PD5)" if LOW_LEVEL_OPTIONS && !MACH_STM32F0
select SERIAL
config STM32_SERIAL_USART3
bool "Serial (on USART3 PB11/PB10)" if LOW_LEVEL_OPTIONS
depends on !MACH_STM32F0 && !MACH_STM32F401
select SERIAL
config STM32_SERIAL_USART3_ALT_PD9_PD8
bool "Serial (on USART3 PD9/PD8)" if LOW_LEVEL_OPTIONS
depends on !MACH_STM32F0 && !MACH_STM32F401
select SERIAL
config STM32_SERIAL_UART4
bool "Serial (on UART4 PA0/PA1)"
depends on MACH_STM32H7
select SERIAL
config STM32_CANBUS_PA11_PA12
bool "CAN bus (on PA11/PA12)"
depends on HAVE_STM32_CANBUS
select CANSERIAL
config STM32_CANBUS_PA11_PA12_REMAP
bool "CAN bus (on PA9/PA10)" if LOW_LEVEL_OPTIONS
depends on HAVE_STM32_CANBUS && MACH_STM32F042
select CANSERIAL
config STM32_CANBUS_PB8_PB9
bool "CAN bus (on PB8/PB9)" if LOW_LEVEL_OPTIONS
depends on HAVE_STM32_CANBUS
select CANSERIAL
config STM32_CANBUS_PI9_PH13
bool "CAN bus (on PI9/PH13)" if LOW_LEVEL_OPTIONS
depends on HAVE_STM32_CANBUS && MACH_STM32F4
select CANSERIAL
config STM32_CANBUS_PB5_PB6
bool "CAN bus (on PB5/PB6)" if LOW_LEVEL_OPTIONS
depends on HAVE_STM32_CANBUS && MACH_STM32F4
select CANSERIAL
config STM32_CANBUS_PB12_PB13
bool "CAN bus (on PB12/PB13)" if LOW_LEVEL_OPTIONS
depends on HAVE_STM32_CANBUS && MACH_STM32F4
select CANSERIAL
config STM32_CANBUS_PD0_PD1
bool "CAN bus (on PD0/PD1)" if LOW_LEVEL_OPTIONS
depends on HAVE_STM32_CANBUS
select CANSERIAL
endchoice
config CANBUS_FREQUENCY
int "CAN bus speed" if LOW_LEVEL_OPTIONS && CANSERIAL
default 500000
######################################################################
# Flash settings
######################################################################
config APPLICATION_START
hex
default 0x8002000 if MACH_STM32F103
default 0x8002000
config MAX_FLASH_PAGE_SIZE config MAX_FLASH_PAGE_SIZE
hex hex
default 0x400 if MACH_STM32F042 default 0x400 if MACH_STM32F042

View File

@ -41,8 +41,17 @@ src-$(CONFIG_MACH_STM32F4) += ../lib/stm32f4/system_stm32f4xx.c
src-$(CONFIG_MACH_STM32F4) += stm32/stm32f4.c generic/armcm_timer.c src-$(CONFIG_MACH_STM32F4) += stm32/stm32f4.c generic/armcm_timer.c
src-$(CONFIG_MACH_STM32F4) += stm32/gpioperiph.c src-$(CONFIG_MACH_STM32F4) += stm32/gpioperiph.c
can-src-$(CONFIG_CANSERIAL) += stm32/can.c ../lib/fast-hash/fasthash.c usb-src-$(CONFIG_HAVE_STM32_USBFS) := stm32/usbfs.c
src-$(CONFIG_CANSERIAL) += $(can-src-y) generic/canbus.c usb-src-$(CONFIG_HAVE_STM32_USBOTG) := stm32/usbotg.c
src-$(CONFIG_USBSERIAL) += $(usb-src-y) stm32/chipid.c generic/usb_cdc.c
serial-src-y := stm32/serial.c
serial-src-$(CONFIG_MACH_STM32F0) := stm32/stm32f0_serial.c
serial-src-$(CONFIG_MACH_STM32G0) := stm32/stm32f0_serial.c
serial-src-$(CONFIG_MACH_STM32H7) := stm32/stm32h7_serial.c
src-$(CONFIG_SERIAL) += $(serial-src-y) generic/serial_irq.c
src-$(CONFIG_CANSERIAL) += stm32/can.c ../lib/fast-hash/fasthash.c
src-$(CONFIG_CANSERIAL) += generic/canbus.c
dirs-$(CONFIG_CANSERIAL) += lib/fast-hash dirs-$(CONFIG_CANSERIAL) += lib/fast-hash
# Binary output file rules # Binary output file rules

33
src/stm32/chipid.c Normal file
View File

@ -0,0 +1,33 @@
// Support for extracting the hardware chip id on stm32
//
// Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "generic/usb_cdc.h" // usb_fill_serial
#include "generic/usbstd.h" // usb_string_descriptor
#include "internal.h" // UID_BASE
#include "sched.h" // DECL_INIT
#define CHIP_UID_LEN 12
static struct {
struct usb_string_descriptor desc;
uint16_t data[CHIP_UID_LEN * 2];
} cdc_chipid;
struct usb_string_descriptor *
usbserial_get_serialid(void)
{
return &cdc_chipid.desc;
}
void
chipid_init(void)
{
if (!CONFIG_USB_SERIAL_NUMBER_CHIPID)
return;
usb_fill_serial(&cdc_chipid.desc, ARRAY_SIZE(cdc_chipid.data)
, (void*)UID_BASE);
}
DECL_INIT(chipid_init);

93
src/stm32/serial.c Normal file
View File

@ -0,0 +1,93 @@
// STM32 serial
//
// Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "autoconf.h" // CONFIG_SERIAL_BAUD
#include "board/armcm_boot.h" // armcm_enable_irq
#include "board/serial_irq.h" // serial_rx_byte
#include "command.h" // DECL_CONSTANT_STR
#include "internal.h" // enable_pclock
#include "sched.h" // DECL_INIT
// Select the configured serial port
#if CONFIG_STM32_SERIAL_USART1
DECL_CONSTANT_STR("RESERVE_PINS_serial", "PA10,PA9");
#define GPIO_Rx GPIO('A', 10)
#define GPIO_Tx GPIO('A', 9)
#define USARTx USART1
#define USARTx_IRQn USART1_IRQn
#elif CONFIG_STM32_SERIAL_USART1_ALT_PB7_PB6
DECL_CONSTANT_STR("RESERVE_PINS_serial", "PB7,PB6");
#define GPIO_Rx GPIO('B', 7)
#define GPIO_Tx GPIO('B', 6)
#define USARTx USART1
#define USARTx_IRQn USART1_IRQn
#elif CONFIG_STM32_SERIAL_USART2
DECL_CONSTANT_STR("RESERVE_PINS_serial", "PA3,PA2");
#define GPIO_Rx GPIO('A', 3)
#define GPIO_Tx GPIO('A', 2)
#define USARTx USART2
#define USARTx_IRQn USART2_IRQn
#elif CONFIG_STM32_SERIAL_USART2_ALT_PD6_PD5
DECL_CONSTANT_STR("RESERVE_PINS_serial", "PD6,PD5");
#define GPIO_Rx GPIO('D', 6)
#define GPIO_Tx GPIO('D', 5)
#define USARTx USART2
#define USARTx_IRQn USART2_IRQn
#elif CONFIG_STM32_SERIAL_USART3
DECL_CONSTANT_STR("RESERVE_PINS_serial", "PB11,PB10");
#define GPIO_Rx GPIO('B', 11)
#define GPIO_Tx GPIO('B', 10)
#define USARTx USART3
#define USARTx_IRQn USART3_IRQn
#elif CONFIG_STM32_SERIAL_USART3_ALT_PD9_PD8
DECL_CONSTANT_STR("RESERVE_PINS_serial", "PD9,PD8");
#define GPIO_Rx GPIO('D', 9)
#define GPIO_Tx GPIO('D', 8)
#define USARTx USART3
#define USARTx_IRQn USART3_IRQn
#endif
#define CR1_FLAGS (USART_CR1_UE | USART_CR1_RE | USART_CR1_TE \
| USART_CR1_RXNEIE)
void
USARTx_IRQHandler(void)
{
uint32_t sr = USARTx->SR;
if (sr & (USART_SR_RXNE | USART_SR_ORE))
serial_rx_byte(USARTx->DR);
if (sr & USART_SR_TXE && USARTx->CR1 & USART_CR1_TXEIE) {
uint8_t data;
int ret = serial_get_tx_byte(&data);
if (ret)
USARTx->CR1 = CR1_FLAGS;
else
USARTx->DR = data;
}
}
void
serial_enable_tx_irq(void)
{
USARTx->CR1 = CR1_FLAGS | USART_CR1_TXEIE;
}
void
serial_init(void)
{
enable_pclock((uint32_t)USARTx);
uint32_t pclk = get_pclock_frequency((uint32_t)USARTx);
uint32_t div = DIV_ROUND_CLOSEST(pclk, CONFIG_SERIAL_BAUD);
USARTx->BRR = (((div / 16) << USART_BRR_DIV_Mantissa_Pos)
| ((div % 16) << USART_BRR_DIV_Fraction_Pos));
USARTx->CR1 = CR1_FLAGS;
armcm_enable_irq(USARTx_IRQHandler, USARTx_IRQn, 0);
gpio_peripheral(GPIO_Rx, GPIO_FUNCTION(7), 1);
gpio_peripheral(GPIO_Tx, GPIO_FUNCTION(7), 0);
}
DECL_INIT(serial_init);

View File

@ -8,9 +8,15 @@
#include "board/armcm_boot.h" // armcm_main #include "board/armcm_boot.h" // armcm_main
#include "board/irq.h" // irq_disable #include "board/irq.h" // irq_disable
#include "board/misc.h" // timer_init #include "board/misc.h" // timer_init
#include "command.h" // DECL_CONSTANT_STR
#include "internal.h" // enable_pclock #include "internal.h" // enable_pclock
#include "sched.h" // sched_main #include "sched.h" // sched_main
/****************************************************************
* Clock setup
****************************************************************/
#define FREQ_PERIPH 48000000 #define FREQ_PERIPH 48000000
// Map a peripheral address to its enable bits // Map a peripheral address to its enable bits
@ -45,6 +51,10 @@ gpio_clock_enable(GPIO_TypeDef *regs)
RCC->AHBENR; RCC->AHBENR;
} }
#if !CONFIG_STM32_CLOCK_REF_INTERNAL
DECL_CONSTANT_STR("RESERVE_PINS_crystal", "PF0,PF1");
#endif
// Configure and enable the PLL as clock source // Configure and enable the PLL as clock source
static void static void
pll_setup(void) pll_setup(void)
@ -75,10 +85,39 @@ pll_setup(void)
// Setup CFGR3 register // Setup CFGR3 register
uint32_t cfgr3 = RCC_CFGR3_I2C1SW; uint32_t cfgr3 = RCC_CFGR3_I2C1SW;
#if CONFIG_USBSERIAL
// Select PLL as source for USB clock
cfgr3 |= RCC_CFGR3_USBSW;
#endif
RCC->CFGR3 = cfgr3; RCC->CFGR3 = cfgr3;
} }
// Configure and enable internal 48Mhz clock on the stm32f042
static void
hsi48_setup(void)
{
#if CONFIG_MACH_STM32F0x2
// Enable HSI48
RCC->CR2 |= RCC_CR2_HSI48ON;
while (!(RCC->CR2 & RCC_CR2_HSI48RDY))
;
// Switch system clock to HSI48
RCC->CFGR = RCC_CFGR_SW_HSI48;
while ((RCC->CFGR & RCC_CFGR_SWS_Msk) != RCC_CFGR_SWS_HSI48)
;
// Enable USB clock recovery
if (CONFIG_USBSERIAL) {
enable_pclock(CRS_BASE);
CRS->CR |= CRS_CR_AUTOTRIMEN | CRS_CR_CEN;
}
// Setup I2C1 clock
RCC->CFGR3 = RCC_CFGR3_I2C1SW;
#endif
}
// Enable high speed internal 14Mhz clock for ADC // Enable high speed internal 14Mhz clock for ADC
static void static void
hsi14_setup(void) hsi14_setup(void)
@ -89,27 +128,35 @@ hsi14_setup(void)
; ;
} }
/****************************************************************
* Startup
****************************************************************/
// Main entry point - called from armcm_boot.c:ResetHandler() // Main entry point - called from armcm_boot.c:ResetHandler()
void void
armcm_main(void) armcm_main(void)
{ {
SystemInit(); SystemInit();
enable_pclock(SYSCFG_BASE);
// Set flash latency // Set flash latency
FLASH->ACR = (1 << FLASH_ACR_LATENCY_Pos) | FLASH_ACR_PRFTBE; FLASH->ACR = (1 << FLASH_ACR_LATENCY_Pos) | FLASH_ACR_PRFTBE;
// Configure main clock // Configure main clock
pll_setup(); if (CONFIG_MACH_STM32F0x2 && CONFIG_STM32_CLOCK_REF_INTERNAL
&& CONFIG_USBSERIAL)
hsi48_setup();
else
pll_setup();
// Turn on hsi14 oscillator for ADC // Turn on hsi14 oscillator for ADC
hsi14_setup(); hsi14_setup();
// Support pin remapping USB/CAN pins on low pinout stm32f042 // Support pin remapping USB/CAN pins on low pinout stm32f042
#ifdef SYSCFG_CFGR1_PA11_PA12_RMP #ifdef SYSCFG_CFGR1_PA11_PA12_RMP
if (CONFIG_STM32_CANBUS_PA11_PA12_REMAP) { if (CONFIG_STM32_USB_PA11_PA12_REMAP || CONFIG_STM32_CANBUS_PA11_PA12_REMAP)
enable_pclock(SYSCFG_BASE);
SYSCFG->CFGR1 |= SYSCFG_CFGR1_PA11_PA12_RMP; SYSCFG->CFGR1 |= SYSCFG_CFGR1_PA11_PA12_RMP;
}
#endif #endif
timer_init(); timer_init();

102
src/stm32/stm32f0_serial.c Normal file
View File

@ -0,0 +1,102 @@
// STM32F0 serial
//
// Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "autoconf.h" // CONFIG_SERIAL_BAUD
#include "board/armcm_boot.h" // armcm_enable_irq
#include "board/serial_irq.h" // serial_rx_byte
#include "command.h" // DECL_CONSTANT_STR
#include "internal.h" // enable_pclock
#include "sched.h" // DECL_INIT
// Select the configured serial port
#if CONFIG_STM32_SERIAL_USART1
DECL_CONSTANT_STR("RESERVE_PINS_serial", "PA10,PA9");
#define GPIO_Rx GPIO('A', 10)
#define GPIO_Tx GPIO('A', 9)
#define USARTx_FUNCTION GPIO_FUNCTION(1)
#define USARTx USART1
#define USARTx_IRQn USART1_IRQn
#elif CONFIG_STM32_SERIAL_USART1_ALT_PB7_PB6
DECL_CONSTANT_STR("RESERVE_PINS_serial", "PB7,PB6");
#define GPIO_Rx GPIO('B', 7)
#define GPIO_Tx GPIO('B', 6)
#define USARTx_FUNCTION GPIO_FUNCTION(0)
#define USARTx USART1
#define USARTx_IRQn USART1_IRQn
#elif CONFIG_STM32_SERIAL_USART2
DECL_CONSTANT_STR("RESERVE_PINS_serial", "PA3,PA2");
#define GPIO_Rx GPIO('A', 3)
#define GPIO_Tx GPIO('A', 2)
#define USARTx_FUNCTION GPIO_FUNCTION(1)
#define USARTx USART2
#define USARTx_IRQn USART2_IRQn
#elif CONFIG_STM32_SERIAL_USART2_ALT_PA15_PA14
DECL_CONSTANT_STR("RESERVE_PINS_serial", "PA15,PA14");
#define GPIO_Rx GPIO('A', 15)
#define GPIO_Tx GPIO('A', 14)
#define USARTx_FUNCTION GPIO_FUNCTION(1)
#define USARTx USART2
#define USARTx_IRQn USART2_IRQn
#endif
#if CONFIG_MACH_STM32F031
// The stm32f031 has same pins for USART2, but everything is routed to USART1
#define USART2 USART1
#define USART2_IRQn USART1_IRQn
#endif
#if CONFIG_MACH_STM32G0
// The stm32g0 has slightly different register names
#define USART2_IRQn USART2_LPUART2_IRQn
#define USART_CR1_RXNEIE USART_CR1_RXNEIE_RXFNEIE
#define USART_CR1_TXEIE USART_CR1_TXEIE_TXFNFIE
#define USART_ISR_RXNE USART_ISR_RXNE_RXFNE
#define USART_ISR_TXE USART_ISR_TXE_TXFNF
#define USART_BRR_DIV_MANTISSA_Pos 4
#define USART_BRR_DIV_FRACTION_Pos 0
#endif
#define CR1_FLAGS (USART_CR1_UE | USART_CR1_RE | USART_CR1_TE \
| USART_CR1_RXNEIE)
void
USARTx_IRQHandler(void)
{
uint32_t sr = USARTx->ISR;
if (sr & (USART_ISR_RXNE | USART_ISR_ORE))
serial_rx_byte(USARTx->RDR);
if (sr & USART_ISR_TXE && USARTx->CR1 & USART_CR1_TXEIE) {
uint8_t data;
int ret = serial_get_tx_byte(&data);
if (ret)
USARTx->CR1 = CR1_FLAGS;
else
USARTx->TDR = data;
}
}
void
serial_enable_tx_irq(void)
{
USARTx->CR1 = CR1_FLAGS | USART_CR1_TXEIE;
}
void
serial_init(void)
{
enable_pclock((uint32_t)USARTx);
uint32_t pclk = get_pclock_frequency((uint32_t)USARTx);
uint32_t div = DIV_ROUND_CLOSEST(pclk, CONFIG_SERIAL_BAUD);
USARTx->BRR = (((div / 16) << USART_BRR_DIV_MANTISSA_Pos)
| ((div % 16) << USART_BRR_DIV_FRACTION_Pos));
USARTx->CR1 = CR1_FLAGS;
armcm_enable_irq(USARTx_IRQHandler, USARTx_IRQn, 0);
gpio_peripheral(GPIO_Rx, USARTx_FUNCTION, 1);
gpio_peripheral(GPIO_Tx, USARTx_FUNCTION, 0);
}
DECL_INIT(serial_init);

View File

@ -7,9 +7,15 @@
#include "autoconf.h" // CONFIG_CLOCK_REF_FREQ #include "autoconf.h" // CONFIG_CLOCK_REF_FREQ
#include "board/armcm_boot.h" // VectorTable #include "board/armcm_boot.h" // VectorTable
#include "board/irq.h" // irq_disable #include "board/irq.h" // irq_disable
#include "board/usb_cdc.h" // usb_request_bootloader
#include "internal.h" // enable_pclock #include "internal.h" // enable_pclock
#include "sched.h" // sched_main #include "sched.h" // sched_main
/****************************************************************
* Clock setup
****************************************************************/
#define FREQ_PERIPH (CONFIG_CLOCK_FREQ / 2) #define FREQ_PERIPH (CONFIG_CLOCK_FREQ / 2)
// Map a peripheral address to its enable bits // Map a peripheral address to its enable bits
@ -204,6 +210,7 @@ gpio_peripheral(uint32_t gpio, uint32_t mode, int pullup)
} }
} }
/**************************************************************** /****************************************************************
* Startup * Startup
****************************************************************/ ****************************************************************/

360
src/stm32/usbfs.c Normal file
View File

@ -0,0 +1,360 @@
// Hardware interface to "fullspeed USB controller"
//
// Copyright (C) 2018-2021 Kevin O'Connor <kevin@koconnor.net>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include <string.h> // NULL
#include "board/armcm_boot.h" // armcm_enable_irq
#include "board/armcm_timer.h" // udelay
#include "board/gpio.h" // gpio_out_setup
#include "board/io.h" // writeb
#include "board/usb_cdc.h" // usb_notify_ep0
#include "board/usb_cdc_ep.h" // USB_CDC_EP_BULK_IN
#include "command.h" // DECL_CONSTANT_STR
#include "internal.h" // GPIO
#include "sched.h" // DECL_INIT
#if CONFIG_MACH_STM32F103
// Transfer memory is accessed with 32bits, but contains only 16bits of data
typedef volatile uint32_t epmword_t;
#define WSIZE 2
#define USBx_IRQn USB_LP_IRQn
#elif CONFIG_MACH_STM32F0
// Transfer memory is accessed with 16bits and contains 16bits of data
typedef volatile uint16_t epmword_t;
#define WSIZE 2
#define USBx_IRQn USB_IRQn
#elif CONFIG_MACH_STM32G0
// Transfer memory is accessed with 32bits and contains 32bits of data
typedef volatile uint32_t epmword_t;
#define WSIZE 4
#define USBx_IRQn USB_UCPD1_2_IRQn
// The stm32g0 has slightly different register names
#define USB USB_DRD_FS
#define USB_PMAADDR USB_DRD_PMAADDR
#define USB_EPADDR_FIELD USB_CHEP_ADDR
#define USB_EP_CTR_RX USB_EP_VTRX
#define USB_EP_CTR_TX USB_EP_VTTX
#define USB_EPRX_STAT USB_EP_RX_STRX
#define USB_EPTX_STAT USB_EP_TX_STTX
#define USB_ISTR_EP_ID USB_ISTR_IDN
#define USB_CNTR_FRES USB_CNTR_USBRST
#endif
/****************************************************************
* USB transfer memory
****************************************************************/
// Layout of the USB transfer memory
#define EPM ((epmword_t*)USB_PMAADDR)
#define EPM_EP_DESC(ep) (&EPM[(ep) * (8 / WSIZE)])
#define EPM_BUF_OFFSET 0x10
#define EPM_EP_BUF_SIZE (64 / WSIZE + 1)
#define EPM_EP_TX_BUF(ep) (&EPM[EPM_BUF_OFFSET + (ep)*2*EPM_EP_BUF_SIZE])
#define EPM_EP_RX_BUF(ep) (&EPM[EPM_BUF_OFFSET + (1+(ep)*2)*EPM_EP_BUF_SIZE])
// Configure the usb descriptor for an endpoint
static void
epm_ep_desc_setup(int ep, int rx_size)
{
uint32_t addr_tx = (EPM_EP_TX_BUF(ep) - EPM) * WSIZE, count_tx = 0;
uint32_t addr_rx = (EPM_EP_RX_BUF(ep) - EPM) * WSIZE;
uint32_t count_rx = (rx_size <= 30 ? DIV_ROUND_UP(rx_size, 2) << 10
: ((DIV_ROUND_UP(rx_size, 32) - 1) << 10) | 0x8000);
epmword_t *desc = EPM_EP_DESC(ep);
if (WSIZE == 2) {
desc[0] = addr_tx;
desc[1] = count_tx;
desc[2] = addr_rx;
desc[3] = count_rx;
} else {
desc[0] = addr_tx | (count_tx << 16);
desc[1] = addr_rx | (count_rx << 16);
}
}
// Return number of read bytes on an rx endpoint
static uint32_t
epm_get_ep_count_rx(int ep)
{
epmword_t *desc = EPM_EP_DESC(ep);
if (WSIZE == 2)
return desc[3] & 0x3ff;
return (desc[1] >> 16) & 0x3ff;
}
// Set number of bytes ready to be transmitted on a tx endpoint
static void
epm_set_ep_count_tx(int ep, uint32_t count)
{
epmword_t *desc = EPM_EP_DESC(ep);
if (WSIZE == 2) {
desc[1] = count;
} else {
uint32_t addr_tx = (EPM_EP_TX_BUF(ep) - EPM) * WSIZE;
desc[0] = addr_tx | (count << 16);
}
}
// Setup the transfer descriptors in dedicated usb memory
static void
btable_configure(void)
{
epm_ep_desc_setup(0, USB_CDC_EP0_SIZE);
epm_ep_desc_setup(USB_CDC_EP_ACM, 0);
epm_ep_desc_setup(USB_CDC_EP_BULK_OUT, USB_CDC_EP_BULK_OUT_SIZE);
epm_ep_desc_setup(USB_CDC_EP_BULK_IN, 0);
}
// Read a packet stored in dedicated usb memory
static uint32_t
btable_read_packet(int ep, uint8_t *dest, int max_len)
{
epmword_t *src = EPM_EP_RX_BUF(ep);
uint32_t count = epm_get_ep_count_rx(ep);
if (count > max_len)
count = max_len;
int i;
for (i=0; i<count/WSIZE; i++) {
uint32_t d = *src++;
*dest++ = d;
*dest++ = d >> 8;
if (WSIZE == 4) {
*dest++ = d >> 16;
*dest++ = d >> 24;
}
}
if (count & (WSIZE-1)) {
uint32_t d = *src;
*dest++ = d;
if ((count & (WSIZE-1)) > 1)
*dest++ = d >> 8;
if ((count & (WSIZE-1)) > 2)
*dest++ = d >> 16;
}
return count;
}
// Write a packet to dedicated usb memory
static void
btable_write_packet(int ep, const uint8_t *src, int count)
{
epmword_t *dest = EPM_EP_TX_BUF(ep);
int i;
for (i=0; i<count/WSIZE; i++) {
uint8_t b1 = *src++, b2 = *src++, b3 = 0, b4 = 0;
if (WSIZE == 4) {
b3 = *src++;
b4 = *src++;
}
*dest++ = b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
}
if (count & (WSIZE-1)) {
uint32_t d = *src++;
if ((count & (WSIZE-1)) > 1)
d |= *src++ << 8;
if ((count & (WSIZE-1)) > 2)
d |= *src++ << 16;
*dest = d;
}
epm_set_ep_count_tx(ep, count);
}
/****************************************************************
* USB endpoint register
****************************************************************/
#define USB_EPR ((volatile uint32_t *)USB_BASE)
#define EPR_RWBITS (USB_EPADDR_FIELD | USB_EP_KIND | USB_EP_TYPE_MASK)
#define EPR_RWCBITS (USB_EP_CTR_RX | USB_EP_CTR_TX)
static uint32_t
set_stat_rx_bits(uint32_t epr, uint32_t bits)
{
return ((epr & (EPR_RWBITS | USB_EPRX_STAT)) ^ bits) | EPR_RWCBITS;
}
static uint32_t
set_stat_tx_bits(uint32_t epr, uint32_t bits)
{
return ((epr & (EPR_RWBITS | USB_EPTX_STAT)) ^ bits) | EPR_RWCBITS;
}
static uint32_t
set_stat_rxtx_bits(uint32_t epr, uint32_t bits)
{
uint32_t mask = EPR_RWBITS | USB_EPRX_STAT | USB_EPTX_STAT;
return ((epr & mask) ^ bits) | EPR_RWCBITS;
}
/****************************************************************
* USB interface
****************************************************************/
int_fast8_t
usb_read_bulk_out(void *data, uint_fast8_t max_len)
{
uint32_t epr = USB_EPR[USB_CDC_EP_BULK_OUT];
if ((epr & USB_EPRX_STAT) == USB_EP_RX_VALID)
// No data ready
return -1;
uint32_t count = btable_read_packet(USB_CDC_EP_BULK_OUT, data, max_len);
USB_EPR[USB_CDC_EP_BULK_OUT] = set_stat_rx_bits(epr, USB_EP_RX_VALID);
return count;
}
int_fast8_t
usb_send_bulk_in(void *data, uint_fast8_t len)
{
uint32_t epr = USB_EPR[USB_CDC_EP_BULK_IN];
if ((epr & USB_EPTX_STAT) != USB_EP_TX_NAK)
// No buffer space available
return -1;
btable_write_packet(USB_CDC_EP_BULK_IN, data, len);
USB_EPR[USB_CDC_EP_BULK_IN] = set_stat_tx_bits(epr, USB_EP_TX_VALID);
return len;
}
int_fast8_t
usb_read_ep0(void *data, uint_fast8_t max_len)
{
uint32_t epr = USB_EPR[0];
if ((epr & USB_EPRX_STAT) != USB_EP_RX_NAK)
// No data ready
return -1;
uint32_t count = btable_read_packet(0, data, max_len);
USB_EPR[0] = set_stat_rxtx_bits(epr, USB_EP_RX_VALID | USB_EP_TX_NAK);
return count;
}
int_fast8_t
usb_read_ep0_setup(void *data, uint_fast8_t max_len)
{
return usb_read_ep0(data, max_len);
}
int_fast8_t
usb_send_ep0(const void *data, uint_fast8_t len)
{
uint32_t epr = USB_EPR[0];
if ((epr & USB_EPRX_STAT) != USB_EP_RX_VALID)
// Transfer interrupted
return -2;
if ((epr & USB_EPTX_STAT) != USB_EP_TX_NAK)
// No buffer space available
return -1;
btable_write_packet(0, data, len);
USB_EPR[0] = set_stat_tx_bits(epr, USB_EP_TX_VALID);
return len;
}
void
usb_stall_ep0(void)
{
USB_EPR[0] = set_stat_rxtx_bits(USB_EPR[0]
, USB_EP_RX_STALL | USB_EP_TX_STALL);
}
static uint8_t set_address;
void
usb_set_address(uint_fast8_t addr)
{
writeb(&set_address, addr | USB_DADDR_EF);
usb_send_ep0(NULL, 0);
}
void
usb_set_configure(void)
{
}
/****************************************************************
* Setup and interrupts
****************************************************************/
// Configure interface after a USB reset event
static void
usb_reset(void)
{
USB_EPR[0] = 0 | USB_EP_CONTROL | USB_EP_RX_VALID | USB_EP_TX_NAK;
USB_EPR[USB_CDC_EP_ACM] = (USB_CDC_EP_ACM | USB_EP_INTERRUPT
| USB_EP_RX_NAK | USB_EP_TX_NAK);
USB_EPR[USB_CDC_EP_BULK_OUT] = (USB_CDC_EP_BULK_OUT | USB_EP_BULK
| USB_EP_RX_VALID | USB_EP_TX_NAK);
USB_EPR[USB_CDC_EP_BULK_IN] = (USB_CDC_EP_BULK_IN | USB_EP_BULK
| USB_EP_RX_NAK | USB_EP_TX_NAK);
USB->CNTR = USB_CNTR_CTRM | USB_CNTR_RESETM;
USB->DADDR = USB_DADDR_EF;
}
// Main irq handler
void
USB_IRQHandler(void)
{
uint32_t istr = USB->ISTR;
if (istr & USB_ISTR_CTR) {
// Endpoint activity
uint32_t ep = istr & USB_ISTR_EP_ID;
uint32_t epr = USB_EPR[ep];
USB_EPR[ep] = epr & EPR_RWBITS;
if (ep == 0) {
usb_notify_ep0();
if (epr & USB_EP_CTR_TX && set_address) {
// Apply address after last "in" message transmitted
USB->DADDR = set_address;
set_address = 0;
}
} else if (ep == USB_CDC_EP_BULK_OUT) {
usb_notify_bulk_out();
} else if (ep == USB_CDC_EP_BULK_IN) {
usb_notify_bulk_in();
}
}
if (istr & USB_ISTR_RESET) {
// USB Reset
USB->ISTR = (uint16_t)~USB_ISTR_RESET;
usb_reset();
}
}
DECL_CONSTANT_STR("RESERVE_PINS_USB", "PA11,PA12");
// Initialize the usb controller
void
usb_init(void)
{
if (CONFIG_MACH_STM32F1) {
// Pull the D+ pin low briefly to signal a new connection
gpio_out_setup(GPIO('A', 12), 0);
udelay(5000);
gpio_in_setup(GPIO('A', 12), 0);
}
// Enable USB clock
enable_pclock(USB_BASE);
// Setup USB packet memory
btable_configure();
// Enable USB pullup
#ifdef USB_BCDR_DPPU
USB->BCDR = USB_BCDR_DPPU;
#endif
// Reset usb controller and enable interrupts
USB->CNTR = USB_CNTR_FRES;
USB->DADDR = 0;
USB->CNTR = USB_CNTR_RESETM;
USB->ISTR = 0;
armcm_enable_irq(USB_IRQHandler, USBx_IRQn, 1);
}
DECL_INIT(usb_init);