WIP: USB control request handling
Change-Id: I9d28566ba21a2a78def5e4a0ba07ecbc4a583aa9
diff --git a/firmware/ice40-riscv/icE1usb/usb_e1.c b/firmware/ice40-riscv/icE1usb/usb_e1.c
index a7ab7fb..d90e405 100644
--- a/firmware/ice40-riscv/icE1usb/usb_e1.c
+++ b/firmware/ice40-riscv/icE1usb/usb_e1.c
@@ -16,11 +16,26 @@
#include "misc.h"
#include "e1.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 */
-} g_usb_e1;
+ struct ice1usb_tx_config tx_cfg;
+ struct ice1usb_rx_config rx_cfg;
+} g_usb_e1 = {
+ /* default configuration at power-up */
+ .tx_cfg = {
+ .mode = ICE1USB_TX_MODE_TS0_CRC4_E,
+ .timing = ICE1USB_TX_TIME_SRC_REMOTE,
+ .ext_loopback = ICE1USB_TX_EXT_LOOPBACK_OFF,
+ .alarm = 0,
+ },
+ .rx_cfg = {
+ .mode = ICE1USB_RX_MODE_MULTIFRAME,
+ },
+};
/* Hack */
@@ -261,10 +276,115 @@
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;
+ /* FIXME: actually change E1 core 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;
+ /* FIXME: actually change E1 core 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