icE1usb fw: USB control request handling
This introduces a number of vendor-specific control requests for
configuration of the icE1usb from the host software.
Closes: OS#4675
Change-Id: I9d28566ba21a2a78def5e4a0ba07ecbc4a583aa9
diff --git a/firmware/ice40-riscv/icE1usb/e1.c b/firmware/ice40-riscv/icE1usb/e1.c
index 1d8fd30..16c8c80 100644
--- a/firmware/ice40-riscv/icE1usb/e1.c
+++ b/firmware/ice40-riscv/icE1usb/e1.c
@@ -238,7 +238,7 @@
void
-e1_init(bool clk_mode)
+e1_init(uint16_t rx_cr, uint16_t tx_cr)
{
/* Global state init */
memset(&g_e1, 0x00, sizeof(g_e1));
@@ -248,14 +248,11 @@
e1f_reset(&g_e1.tx.fifo, 128, 128);
/* Enable Rx */
- g_e1.rx.cr = E1_RX_CR_MODE_MFA |
- E1_RX_CR_ENABLE;
+ g_e1.rx.cr = E1_RX_CR_ENABLE | rx_cr;
e1_regs->rx.csr = E1_RX_CR_OVFL_CLR | g_e1.rx.cr;
/* Enable Tx */
- g_e1.tx.cr = (clk_mode ? E1_TX_CR_TICK_REMOTE : E1_TX_CR_TICK_LOCAL) |
- E1_TX_CR_MODE_TS0_CRC_E |
- E1_TX_CR_ENABLE;
+ g_e1.tx.cr = E1_TX_CR_ENABLE | tx_cr;
e1_regs->tx.csr = E1_TX_CR_UNFL_CLR | g_e1.tx.cr;
/* State */
diff --git a/firmware/ice40-riscv/icE1usb/e1.h b/firmware/ice40-riscv/icE1usb/e1.h
index 05ce19a..fcd4284 100644
--- a/firmware/ice40-riscv/icE1usb/e1.h
+++ b/firmware/ice40-riscv/icE1usb/e1.h
@@ -7,7 +7,7 @@
#pragma once
-void e1_init(bool clk_mode);
+void e1_init(uint16_t rx_cr, uint16_t tx_cr);
void e1_poll(void);
void e1_debug_print(bool data);
diff --git a/firmware/ice40-riscv/icE1usb/fw_app.c b/firmware/ice40-riscv/icE1usb/fw_app.c
index 62f4b0e..eebc21f 100644
--- a/firmware/ice40-riscv/icE1usb/fw_app.c
+++ b/firmware/ice40-riscv/icE1usb/fw_app.c
@@ -102,7 +102,7 @@
usb_e1_init();
/* Start */
- e1_init(false); // local tick
+ e1_init(0, 0);
e1_active = true;
led_state(true);
usb_connect();
@@ -138,16 +138,6 @@
case 'O':
e1_debug_print(true);
break;
- case 'e':
- e1_init(true);
- e1_active = true;
- led_state(true);
- break;
- case 'E':
- e1_init(false);
- e1_active = true;
- led_state(true);
- break;
case 'c':
usb_connect();
break;
diff --git a/firmware/ice40-riscv/icE1usb/ice1usb_proto.h b/firmware/ice40-riscv/icE1usb/ice1usb_proto.h
new file mode 100644
index 0000000..61e12ad
--- /dev/null
+++ b/firmware/ice40-riscv/icE1usb/ice1usb_proto.h
@@ -0,0 +1,77 @@
+/*
+ * ice1usb_proto.h
+ *
+ * Copyright (C) 2020 Harald Welte <laforge@osmocom.org>
+ * SPDX-License-Identifier: MIT
+ *
+ * Header file describing the USB protocol between the icE1usb firmware and the host
+ * software (currently really only osmo-e1d)
+ */
+
+#pragma once
+
+/***********************************************************************
+ * Control Endpoint / Device Requests
+ ***********************************************************************/
+
+/*! returns a bit-mask of optional device capabilities (see enum e1usb_dev_capability) */
+#define ICE1USB_DEV_GET_CAPABILITIES 0x01
+#define ICE1USB_DEV_GET_FW_BUILD 0x02
+
+enum e1usb_dev_capability {
+ /*! Does this board have a GPS-DO */
+ ICE1USB_DEV_CAP_GPSDO,
+};
+
+
+/* Interface Requests */
+
+/*! returns a bit-mask of optional device capabilities (see enum e1usb_intf_capability) */
+#define ICE1USB_INTF_GET_CAPABILITIES 0x01
+#define ICE1USB_INTF_SET_TX_CFG 0x02 /*!< struct ice1usb_tx_config */
+#define ICE1USB_INTF_GET_TX_CFG 0x03 /*!< struct ice1usb_tx_config */
+#define ICE1USB_INTF_SET_RX_CFG 0x04 /*!< struct ice1usb_rx_config */
+#define ICE1USB_INTF_GET_RX_CFG 0x05 /*!< struct ice1usb_rx_config */
+
+//enum e1usb_intf_capability { };
+
+enum ice1usb_tx_mode {
+ ICE1USB_TX_MODE_TRANSP = 0,
+ ICE1USB_TX_MODE_TS0 = 1,
+ ICE1USB_TX_MODE_TS0_CRC4 = 2,
+ ICE1USB_TX_MODE_TS0_CRC4_E = 3,
+};
+
+enum ice1usb_tx_timing {
+ ICE1USB_TX_TIME_SRC_LOCAL = 0,
+ ICE1USB_TX_TIME_SRC_REMOTE = 1,
+};
+
+enum ice1usb_tx_ext_loopback {
+ ICE1USB_TX_EXT_LOOPBACK_OFF = 0,
+ ICE1USB_TX_EXT_LOOPBACK_SAME = 1,
+ ICE1USB_TX_EXT_LOOPBACK_CROSS = 2,
+};
+
+/* ICE1USB_INTF_{GET,SET}_TX_CFG */
+struct ice1usb_tx_config {
+ uint8_t mode; /*!< enum ice1usb_tx_mode */
+ uint8_t timing; /*!< enum ice1usb_tx_timing */
+ uint8_t ext_loopback; /*!< enum ice1usb_tx_ext_loopback */
+ uint8_t alarm; /*!< 1 = transmit alarm; 0 = don't */
+} __attribute__((packed));
+
+
+enum ice1usb_rx_mode {
+ /*! transparent, unaligned bitstream */
+ ICE1USB_RX_MODE_TRANSP = 0,
+ /*! alignment to E1 frame */
+ ICE1USB_RX_MODE_FRAME = 2,
+ /*! alignment to E1 multiframe */
+ ICE1USB_RX_MODE_MULTIFRAME = 3,
+};
+
+/* ICE1USB_INTF_{GET,SET}_RX_CFG */
+struct ice1usb_rx_config {
+ uint8_t mode; /*!< enum ice1usb_rx_mode */
+} __attribute__((packed));
diff --git a/firmware/ice40-riscv/icE1usb/usb_e1.c b/firmware/ice40-riscv/icE1usb/usb_e1.c
index e37edc3..0698de5 100644
--- a/firmware/ice40-riscv/icE1usb/usb_e1.c
+++ b/firmware/ice40-riscv/icE1usb/usb_e1.c
@@ -15,13 +15,30 @@
#include "console.h"
#include "misc.h"
#include "e1.h"
+#include "e1_hw.h"
+
+#include "ice1usb_proto.h"
struct {
bool running; /* are we running (transceiving USB data)? */
int out_bdi; /* buffer descriptor index for OUT EP */
int in_bdi; /* buffer descriptor index for IN EP */
+ struct ice1usb_tx_config tx_cfg;
+ struct ice1usb_rx_config rx_cfg;
} g_usb_e1;
+/* default configuration at power-up */
+static const struct ice1usb_tx_config tx_cfg_default = {
+ .mode = ICE1USB_TX_MODE_TS0_CRC4_E,
+ .timing = ICE1USB_TX_TIME_SRC_LOCAL,
+ .ext_loopback = ICE1USB_TX_EXT_LOOPBACK_OFF,
+ .alarm = 0,
+};
+
+static const struct ice1usb_rx_config rx_cfg_default = {
+ .mode = ICE1USB_RX_MODE_MULTIFRAME,
+};
+
/* Hack */
unsigned int e1_rx_need_data(unsigned int usb_addr, unsigned int max_len, unsigned int *pos);
@@ -200,6 +217,21 @@
return USB_FND_SUCCESS;
}
+static void _perform_tx_config(void)
+{
+ const struct ice1usb_tx_config *cfg = &g_usb_e1.tx_cfg;
+ e1_tx_config( ((cfg->mode & 3) << 1) |
+ ((cfg->timing & 1) << 3) |
+ ((cfg->alarm & 1) << 4) |
+ ((cfg->ext_loopback & 3) << 5) );
+}
+
+static void _perform_rx_config(void)
+{
+ const struct ice1usb_rx_config *cfg = &g_usb_e1.rx_cfg;
+ e1_rx_config((cfg->mode << 1));
+}
+
static enum usb_fnd_resp
_e1_set_intf(const struct usb_intf_desc *base, const struct usb_intf_desc *sel)
{
@@ -213,6 +245,9 @@
if (g_usb_e1.running)
return USB_FND_SUCCESS;
+ _perform_rx_config();
+ _perform_tx_config();
+
g_usb_e1.running = true;
/* Configure EP1 OUT / EP2 IN */
@@ -253,15 +288,122 @@
return USB_FND_SUCCESS;
}
+static bool
+_set_tx_mode_done(struct usb_xfer *xfer)
+{
+ const struct ice1usb_tx_config *cfg = (const struct ice1usb_tx_config *) xfer->data;
+ printf("set_tx_mode %02x%02x%02x%02x\r\n",
+ xfer->data[0], xfer->data[1], xfer->data[2], xfer->data[3]);
+ g_usb_e1.tx_cfg = *cfg;
+ _perform_tx_config();
+ return true;
+}
+
+static bool
+_set_rx_mode_done(struct usb_xfer *xfer)
+{
+ const struct ice1usb_rx_config *cfg = (const struct ice1usb_rx_config *) xfer->data;
+ printf("set_rx_mode %02x\r\n", xfer->data[0]);
+ g_usb_e1.rx_cfg = *cfg;
+ _perform_rx_config();
+ return true;
+}
+
+/* per-interface requests */
+static enum usb_fnd_resp
+_e1_ctrl_req_intf(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+ unsigned int i;
+
+ switch (req->bRequest) {
+ case ICE1USB_INTF_GET_CAPABILITIES:
+ /* no optional capabilities yet */
+ xfer->len = 0;
+ break;
+ case ICE1USB_INTF_SET_TX_CFG:
+ if (req->wLength < sizeof(struct ice1usb_tx_config))
+ return USB_FND_ERROR;
+ xfer->cb_done = _set_tx_mode_done;
+ xfer->cb_ctx = req;
+ xfer->len = sizeof(struct ice1usb_tx_config);
+ break;
+ case ICE1USB_INTF_GET_TX_CFG:
+ if (req->wLength < sizeof(struct ice1usb_tx_config))
+ return USB_FND_ERROR;
+ memcpy(xfer->data, &g_usb_e1.tx_cfg, sizeof(struct ice1usb_tx_config));
+ xfer->len = sizeof(struct ice1usb_tx_config);
+ break;
+ case ICE1USB_INTF_SET_RX_CFG:
+ if (req->wLength < sizeof(struct ice1usb_rx_config))
+ return USB_FND_ERROR;
+ xfer->cb_done = _set_rx_mode_done;
+ xfer->cb_ctx = req;
+ xfer->len = sizeof(struct ice1usb_rx_config);
+ break;
+ case ICE1USB_INTF_GET_RX_CFG:
+ if (req->wLength < sizeof(struct ice1usb_rx_config))
+ return USB_FND_ERROR;
+ memcpy(xfer->data, &g_usb_e1.rx_cfg, sizeof(struct ice1usb_rx_config));
+ xfer->len = sizeof(struct ice1usb_rx_config);
+ break;
+ default:
+ return USB_FND_ERROR;
+ }
+
+ return USB_FND_SUCCESS;
+}
+
+/* device-global requests */
+static enum usb_fnd_resp
+_e1_ctrl_req_dev(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+ switch (req->bRequest) {
+ case ICE1USB_DEV_GET_CAPABILITIES:
+ xfer->data[0] = (1 << ICE1USB_DEV_CAP_GPSDO);
+ xfer->len = 1;
+ break;
+ default:
+ return USB_FND_ERROR;
+ }
+
+ return USB_FND_SUCCESS;
+}
+
+
+/* USB host issues a control request to us */
+static enum usb_fnd_resp
+_e1_ctrl_req(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+ if (USB_REQ_TYPE(req) != USB_REQ_TYPE_VENDOR)
+ return USB_FND_CONTINUE;
+
+ switch (USB_REQ_RCPT(req)) {
+ case USB_REQ_RCPT_DEV:
+ return _e1_ctrl_req_dev(req, xfer);
+ case USB_REQ_RCPT_INTF:
+ if (req->wIndex != 0)
+ return USB_FND_ERROR;
+ return _e1_ctrl_req_intf(req, xfer);
+ case USB_REQ_RCPT_EP:
+ case USB_REQ_RCPT_OTHER:
+ default:
+ return USB_FND_ERROR;
+ }
+}
+
+
static struct usb_fn_drv _e1_drv = {
.set_conf = _e1_set_conf,
.set_intf = _e1_set_intf,
.get_intf = _e1_get_intf,
+ .ctrl_req = _e1_ctrl_req,
};
void
usb_e1_init(void)
{
memset(&g_usb_e1, 0x00, sizeof(g_usb_e1));
+ g_usb_e1.tx_cfg = tx_cfg_default;
+ g_usb_e1.rx_cfg = rx_cfg_default;
usb_register_function_driver(&_e1_drv);
}