blob: 7eefaa55950d2f9d1e76c3f4a67074e365372361 [file] [log] [blame]
#include <app/zedmon/usb.h>
#include <assert.h>
#include <app/zedmon/ina.h>
#include <app/zedmon/usb_proto.h>
#include <compiler.h>
#include <debug.h>
#include <dev/udc.h>
#include <dev/usb.h>
#include <dev/usbc.h>
#include <err.h>
#include <kernel/mutex.h>
#include <kernel/port.h>
#include <kernel/thread.h>
#include <lib/cbuf.h>
#include <lib/console.h>
#include <list.h>
#include <platform.h>
#include <stdint.h>
#include <string.h>
#include <target/parameters.h>
#include <trace.h>
#define DATA_IN_EP_ADDR_OFFSET (0x0B)
#define DATA_OUT_EP_ADDR_OFFSET (0x12)
#define W(w) (w & 0xff), (w >> 8)
static uint8_t zedmon_if_descriptor[] = {
0x09, /* length */
INTERFACE, /* type */
0x00, /* interface num */
0x00, /* alternates */
0x02, /* endpoint count */
0xff, /* interface class */
0xff, /* interface subclass */
0x00, /* interface protocol */
0x00, /* string index */
/* endpoint 1 IN */
0x07, /* length */
ENDPOINT, /* type */
0x83, /* address: 1 IN */
0x02, /* type: bulk */
W(64), /* max packet size: 64 */
00, /* interval */
/* endpoint 1 OUT */
0x07, /* length */
ENDPOINT, /* type */
0x03, /* address: 1 OUT */
0x02, /* type: bulk */
W(64), /* max packet size: 64 */
00, /* interval */
};
static zedmon_usb_report_format_t zedmon_report_formats[] = {
{
.packet_type = ZEDMON_USB_PACKET_REPORT_FORMAT,
.index = 0,
.type = ZEDMON_USB_TYPE_INT16,
.unit = ZEDMON_USB_UNIT_VOLTS,
.scale = 2.5e-6f,
.name = "v_shunt",
},
{
.packet_type = ZEDMON_USB_PACKET_REPORT_FORMAT,
.index = 1,
.type = ZEDMON_USB_TYPE_INT16,
.unit = ZEDMON_USB_UNIT_VOLTS,
.scale = 1.25e-3f,
.name = "v_bus",
},
{
.packet_type = ZEDMON_USB_PACKET_REPORT_FORMAT,
.index = 0xff,
.type = 0,
.unit = 0,
.scale =0.0f,
.name = "",
},
};
static zedmon_usb_parameter_value_t zedmon_parameter_values[] = {
{
.packet_type = ZEDMON_USB_PACKET_PARAMETER_VALUE,
.name = "shunt_resistance",
.data_type = ZEDMON_USB_TYPE_FLOAT32,
.data.float_value = SHUNT_RESISTANCE,
},
{
.packet_type = ZEDMON_USB_PACKET_PARAMETER_VALUE,
.name = "",
.data_type = ZEDMON_USB_TYPE_UINT8,
.data.bytes = {0},
}
};
typedef struct __attribute__((packed)) {
lk_bigtime_t timestamp;
uint16_t v_shunt;
uint16_t v_bus;
} zedmon_sample_t;
static_assert(sizeof(zedmon_sample_t) == 12);
static_assert(offsetof(zedmon_sample_t, timestamp) == 0);
static_assert(offsetof(zedmon_sample_t, v_shunt) == 8);
static_assert(offsetof(zedmon_sample_t, v_bus) == 10);
typedef struct __attribute__((packed)) {
uint8_t packet_type;
zedmon_sample_t samples[5];
} zedmon_report_packet_t;
static_assert(sizeof(zedmon_report_packet_t) <= 64);
typedef struct __attribute__((packed)) {
uint8_t packet_type;
uint64_t timestamp;
} zedmon_timestamp_packet_t;
static cbuf_t zedmon_sample_cbuf;
static int zedmon_ep;
typedef enum {
ZEDMON_PORT_CTX_USB_RX = 0x0,
ZEDMON_PORT_CTX_USB_TX = 0x1,
ZEDMON_PORT_CTX_USB_SAMPLE = 0x2,
} zedmon_port_context_t;
static port_t zedmon_usb_rx_port;
static port_t zedmon_usb_tx_port;
static port_t zedmon_usb_sample_port;
static port_t zedmon_usb_port_group;
static thread_t *zedmon_usb_thread;
static bool zedmon_tx_busy;
static bool zedmon_usb_reporting_enabled;
static zedmon_report_packet_t zedmon_usb_report_packet;
static zedmon_timestamp_packet_t zedmon_usb_timestamp_packet;
static status_t ep_cb_rx(ep_t endpoint, usbc_transfer_t *t);
static status_t ep_cb_tx(ep_t endpoint, usbc_transfer_t *t);
static void queue_rx(void)
{
static usbc_transfer_t transfer;
static uint8_t buf[512];
transfer.callback = &ep_cb_rx;
transfer.result = 0;
transfer.buf = &buf;
transfer.buflen = sizeof(buf);
transfer.bufpos = 0;
transfer.extra = 0;
usbc_queue_rx(zedmon_ep, &transfer);
}
static status_t queue_tx(void *data, size_t len)
{
if (zedmon_tx_busy) {
return ERR_BUSY;
}
static usbc_transfer_t transfer;
transfer.callback = &ep_cb_tx;
transfer.result = 0;
transfer.buf = data;
transfer.buflen = len;
transfer.bufpos = 0;
transfer.extra = 0;
zedmon_tx_busy = true;
usbc_queue_tx(zedmon_ep, &transfer);
return NO_ERROR;
}
static status_t ep_cb_rx(ep_t endpoint, usbc_transfer_t *t)
{
#if LOCAL_TRACE
LTRACEF("ep %u transfer %p\n", endpoint, t);
usbc_dump_transfer(t);
if (t->result >= 0) {
hexdump8(t->buf, t->bufpos);
}
#endif
//printf("ep %u transfer %p\n", endpoint, t);
if (t->result >= 0) {
if (t->bufpos >= 1) {
port_packet_t packet;
int command_length =
t->bufpos < sizeof(packet.value) ? t->bufpos : sizeof(packet.value);
memcpy(&packet.value, t->buf, command_length);
port_write(zedmon_usb_rx_port, &packet, 1);
}
queue_rx();
} else {
printf("bad rx\n");
}
return NO_ERROR;
}
static status_t ep_cb_tx(ep_t endpoint, usbc_transfer_t *t)
{
//printf("ep %u transfer %p\n", endpoint, t);
#if LOCAL_TRACE
LTRACEF("ep %u transfer %p\n", endpoint, t);
usbc_dump_transfer(t);
#endif
if (t->result >= 0) {
port_packet_t packet;
packet.value[0] = 0x0;
port_write(zedmon_usb_tx_port, &packet, 1);
} else {
printf("bad tx\n");
}
return NO_ERROR;
}
static status_t zedmon_usb_callback(void *cookie, usb_callback_op_t op,
const union usb_callback_args *args)
{
#if LOCAL_TRACE
LTRACEF("cookie %p, op %u, args %p\n", cookie, op, args);
#endif
if (op == USB_CB_ONLINE) {
usbc_setup_endpoint(zedmon_ep, USB_IN, 0x40, USB_BULK);
usbc_setup_endpoint(zedmon_ep, USB_OUT, 0x40, USB_BULK);
queue_rx();
} else if (op == USB_CB_SETUP_MSG) {
zedmon_usb_reporting_enabled = false;
}
return NO_ERROR;
}
static void zedmon_usb_handle_query_report_format_packet(const uint8_t values[7]) {
uint8_t index = values[0];
if (index >= countof(zedmon_report_formats)) {
index = countof(zedmon_report_formats) - 1;
}
queue_tx(&zedmon_report_formats[index], sizeof(zedmon_report_formats[index]));
}
static void zedmon_usb_handle_query_parameter_value_packet(const uint8_t values[7]) {
uint8_t index = values[0];
if (index >= countof(zedmon_parameter_values)) {
index = countof(zedmon_parameter_values) - 1;
}
queue_tx(&zedmon_parameter_values[index], sizeof(zedmon_parameter_values[index]));
}
static void zedmon_usb_handle_set_output_packet(const uint8_t values[7]) {
zedmon_usb_set_target_out(values[0], values[1]);
}
static void zedmon_usb_handle_usb_rx_port(const port_packet_t *packet) {
zedmon_usb_packet_type_t type = (zedmon_usb_packet_type_t)packet->value[0];
switch (type) {
case ZEDMON_USB_PACKET_QUERY_REPORT_FORMAT:
zedmon_usb_handle_query_report_format_packet((const uint8_t *)packet->value + 1);
break;
case ZEDMON_USB_PACKET_QUERY_TIME:
zedmon_usb_timestamp_packet.packet_type = ZEDMON_USB_PACKET_TIMESTAMP;
zedmon_usb_timestamp_packet.timestamp = ina_get_current_time();
queue_tx(&zedmon_usb_timestamp_packet, sizeof(zedmon_usb_timestamp_packet));
break;
case ZEDMON_USB_PACKET_QUERY_PARAMETER_VALUE:
zedmon_usb_handle_query_parameter_value_packet((const uint8_t *)packet->value + 1);
break;
case ZEDMON_USB_PACKET_ENABLE_REPORTING:
zedmon_usb_reporting_enabled = true;
break;
case ZEDMON_USB_PACKET_DISABLE_REPORTING:
zedmon_usb_reporting_enabled = false;
break;
case ZEDMON_USB_PACKET_SET_OUTPUT:
zedmon_usb_handle_set_output_packet((const uint8_t *)packet->value + 1);
break;
// These are Device -> Host packets which we should never see.
case ZEDMON_USB_PACKET_REPORT_FORMAT:
break;
case ZEDMON_USB_PACKET_REPORT:
break;
case ZEDMON_USB_PACKET_TIMESTAMP:
break;
case ZEDMON_USB_PACKET_PARAMETER_VALUE:
break;
}
}
static void zedmon_usb_check_for_repot_tx(void) {
if (zedmon_tx_busy) {
return;
}
zedmon_report_packet_t *packet = &zedmon_usb_report_packet;
if (cbuf_space_used(&zedmon_sample_cbuf) >= sizeof(packet->samples)) {
size_t read_len =
cbuf_read(&zedmon_sample_cbuf, &packet->samples, sizeof(packet->samples), false);
assert(read_len == sizeof(packet->samples));
if (zedmon_usb_reporting_enabled) {
queue_tx(packet, sizeof(*packet));
}
}
}
static int zedmon_usb_thread_entry(void *arg) {
while (true) {
port_result_t result;
port_read(zedmon_usb_port_group, INFINITE_TIME, &result);
zedmon_port_context_t context = (zedmon_port_context_t)result.ctx;
switch(context) {
case ZEDMON_PORT_CTX_USB_RX:
zedmon_usb_handle_usb_rx_port(&result.packet);
break;
case ZEDMON_PORT_CTX_USB_TX:
zedmon_tx_busy = false;
zedmon_usb_check_for_repot_tx();
break;
case ZEDMON_PORT_CTX_USB_SAMPLE:
zedmon_usb_check_for_repot_tx();
break;
}
}
return 0;
}
void zedmon_usb_add_sample(lk_bigtime_t timestamp, uint16_t v_shunt, uint16_t v_bus) {
zedmon_sample_t sample = {
.timestamp = timestamp,
.v_shunt = v_shunt,
.v_bus = v_bus,
};
// If there's no space, drop the sample.
if (cbuf_space_avail(&zedmon_sample_cbuf) < sizeof(sample)) {
return;
}
size_t write_len = cbuf_write(&zedmon_sample_cbuf, &sample, sizeof(sample), false);
assert(write_len == sizeof(sample));
port_packet_t packet;
packet.value[0] = 0x0;
port_write(zedmon_usb_sample_port, &packet, 1);
}
void __WEAK zedmon_usb_set_target_out(int index, bool value) {
}
void zedmon_usb_init(int data_ep_addr) {
cbuf_initialize(&zedmon_sample_cbuf, 128);
zedmon_usb_report_packet.packet_type = ZEDMON_USB_PACKET_REPORT;
status_t ret;
ret = port_create("zm_usb_rx", PORT_MODE_UNICAST, &zedmon_usb_rx_port);
if (ret != NO_ERROR) {
printf("error creating zm_usb_rx: %d\n", ret);
}
port_t usb_rx_reader;
ret = port_open("zm_usb_rx", (void *)ZEDMON_PORT_CTX_USB_RX, &usb_rx_reader);
if (ret != NO_ERROR) {
printf("error opening zm_usb_rx: %d\n", ret);
}
ret = port_create("zm_usb_tx", PORT_MODE_UNICAST, &zedmon_usb_tx_port);
if (ret != NO_ERROR) {
printf("error creating zm_usb_rx: %d\n", ret);
}
port_t usb_tx_reader;
ret = port_open("zm_usb_tx", (void *)ZEDMON_PORT_CTX_USB_TX, &usb_tx_reader);
if (ret != NO_ERROR) {
printf("error opening zm_usb_tx: %d\n", ret);
}
ret = port_create("zm_usb_sam", PORT_MODE_UNICAST, &zedmon_usb_sample_port);
if (ret != NO_ERROR) {
printf("error creating zm_usb_sample: %d\n", ret);
}
port_t usb_sample_reader;
ret = port_open("zm_usb_sam", (void *)ZEDMON_PORT_CTX_USB_SAMPLE, &usb_sample_reader);
if (ret != NO_ERROR) {
printf("error opening zm_usb_sample: %d\n", ret);
}
port_t ports[] = {usb_rx_reader, usb_tx_reader, usb_sample_reader};
ret = port_group(ports, countof(ports), &zedmon_usb_port_group);
zedmon_ep = data_ep_addr;
zedmon_if_descriptor[DATA_IN_EP_ADDR_OFFSET] = data_ep_addr | 0x80;
zedmon_if_descriptor[DATA_OUT_EP_ADDR_OFFSET] = data_ep_addr;
usb_append_interface_lowspeed(zedmon_if_descriptor, sizeof(zedmon_if_descriptor));
usb_append_interface_highspeed(zedmon_if_descriptor, sizeof(zedmon_if_descriptor));
usb_register_callback(&zedmon_usb_callback, NULL);
zedmon_tx_busy = false;
zedmon_usb_thread = thread_create("zedmon_usb", &zedmon_usb_thread_entry, 0x0,
DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_detach(zedmon_usb_thread);
thread_resume(zedmon_usb_thread);
}
#include "stm32f0xx_hal.h"
static void print_epr(int i, uint16_t epr) {
printf("ep%dr: ea:%d stat_tx:%d dtog_tx:%d ctr_tx:%d ep_kind:%d ep_type:%d\n"
" setup:%d stat_rx:%d dtog_rx:%d ctr_rx:%d\n",
i,
epr & 0xf,
(epr >> 4) & 0x3,
(epr >> 6) & 0x1,
(epr >> 7) & 0x1,
(epr >> 8) & 0x1,
(epr >> 9) & 0x3,
(epr >> 11) & 0x1,
(epr >> 12) & 0x3,
(epr >> 14) & 0x1,
(epr >> 15) & 0x1);
}
static int cmd_usb_dump(int argc, const cmd_args *argv) {
print_epr(0, USB->EP0R);
print_epr(1, USB->EP1R);
print_epr(2, USB->EP2R);
print_epr(3, USB->EP3R);
print_epr(4, USB->EP4R);
print_epr(5, USB->EP5R);
print_epr(6, USB->EP6R);
print_epr(7, USB->EP7R);
return NO_ERROR;
}
STATIC_COMMAND_START
STATIC_COMMAND("usb_dump", "usb dump", &cmd_usb_dump)
STATIC_COMMAND_END(usb);