icE1usb: Move GPS-DO USB control to separate USB interface
doing so significantly simplifies the development of a Linux kernel
driver, as the GPS-DO only exists once (not twice, like the per-E1-line
interface), and Linux kernel USB drivers typically are for an interface.
There is an option to write a usb_device_driver, but doing so will
exclude the per-interface drivers from still being probed in their usual
fashion.
While we introduce this new USB Interface for the GPS-DO, we also
move the related control endpoint requests from the device level to the
interface level.
Finally, some naming inconsistency between "enum
ice1usb_gpsdo_antenna_state" vs. the member name antenna_status is
resolved.
Change-Id: Icd6555a14896c38626fb147b78af44ff719f2254
diff --git a/firmware/ice40-riscv/icE1usb/Makefile b/firmware/ice40-riscv/icE1usb/Makefile
index 25c65d1..ed765f6 100644
--- a/firmware/ice40-riscv/icE1usb/Makefile
+++ b/firmware/ice40-riscv/icE1usb/Makefile
@@ -56,6 +56,7 @@
usb_dev.h \
usb_e1.h \
usb_gps.h \
+ usb_gpsdo.h \
usb_str_app.gen.h \
$(NULL)
@@ -69,6 +70,7 @@
usb_dev.c \
usb_e1.c \
usb_gps.c \
+ usb_gpsdo.c \
$(NULL)
diff --git a/firmware/ice40-riscv/icE1usb/fw_app.c b/firmware/ice40-riscv/icE1usb/fw_app.c
index c1fe295..540eda2 100644
--- a/firmware/ice40-riscv/icE1usb/fw_app.c
+++ b/firmware/ice40-riscv/icE1usb/fw_app.c
@@ -23,6 +23,7 @@
#include "usb_dev.h"
#include "usb_e1.h"
#include "usb_gps.h"
+#include "usb_gpsdo.h"
#include "utils.h"
@@ -109,6 +110,7 @@
usb_dfu_rt_init();
usb_e1_init();
usb_gps_init();
+ usb_gpsdo_init();
/* Start */
led_state(true);
diff --git a/firmware/ice40-riscv/icE1usb/gpsdo.c b/firmware/ice40-riscv/icE1usb/gpsdo.c
index ae131d4..8e32627 100644
--- a/firmware/ice40-riscv/icE1usb/gpsdo.c
+++ b/firmware/ice40-riscv/icE1usb/gpsdo.c
@@ -96,7 +96,7 @@
};
status->state = state_map[g_gpsdo.state];
- status->antenna_status = ant_map[gps_antenna_status()];
+ status->antenna_state = ant_map[gps_antenna_status()];
status->valid_fix = gps_has_valid_fix();
status->mode = (g_gpsdo.state == STATE_DISABLED) ? ICE1USB_GPSDO_MODE_DISABLED : ICE1USB_GPSDO_MODE_AUTO;
status->tune.coarse = g_gpsdo.tune.coarse;
diff --git a/firmware/ice40-riscv/icE1usb/ice1usb_proto.h b/firmware/ice40-riscv/icE1usb/ice1usb_proto.h
index a249537..3170d55 100644
--- a/firmware/ice40-riscv/icE1usb/ice1usb_proto.h
+++ b/firmware/ice40-riscv/icE1usb/ice1usb_proto.h
@@ -17,17 +17,22 @@
/*! 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
-#define ICE1USB_DEV_GET_GPSDO_STATUS 0x10
-#define ICE1USB_DEV_GET_GPSDO_MODE 0x12 /*!< uint8_t */
-#define ICE1USB_DEV_SET_GPSDO_MODE 0x13 /*!< wValue = mode */
-#define ICE1USB_DEV_GET_GPSDO_TUNE 0x14 /*!< data = struct e1usb_gpsdo_tune */
-#define ICE1USB_DEV_SET_GPSDO_TUNE 0x15 /*!< data = struct e1usb_gpsdo_tune */
enum e1usb_dev_capability {
/*! Does this board have a GPS-DO */
ICE1USB_DEV_CAP_GPSDO,
};
+/***********************************************************************
+ * Control Endpoint / GPS-DO Interface Requests
+ ***********************************************************************/
+
+#define ICE1USB_INTF_GET_GPSDO_STATUS 0x10
+#define ICE1USB_INTF_GET_GPSDO_MODE 0x12 /*!< uint8_t */
+#define ICE1USB_INTF_SET_GPSDO_MODE 0x13 /*!< wValue = mode */
+#define ICE1USB_INTF_GET_GPSDO_TUNE 0x14 /*!< data = struct e1usb_gpsdo_tune */
+#define ICE1USB_INTF_SET_GPSDO_TUNE 0x15 /*!< data = struct e1usb_gpsdo_tune */
+
enum ice1usb_gpsdo_mode {
ICE1USB_GPSDO_MODE_DISABLED = 0,
ICE1USB_GPSDO_MODE_AUTO = 1,
@@ -55,7 +60,7 @@
struct e1usb_gpsdo_status {
uint8_t state;
- uint8_t antenna_status; /*!< Antenna status */
+ uint8_t antenna_state; /*!< Antenna state */
uint8_t valid_fix; /*!< Valid GPS Fix (0/1) */
uint8_t mode; /*!< Current configured operating mode */
struct e1usb_gpsdo_tune tune; /*!< Current VCXO tuning values */
@@ -63,7 +68,9 @@
} __attribute__((packed));
-/* Interface Requests */
+/***********************************************************************
+ * Control Endpoint / E1 Interface Requests
+ ***********************************************************************/
/*! returns a bit-mask of optional device capabilities (see enum e1usb_intf_capability) */
#define ICE1USB_INTF_GET_CAPABILITIES 0x01
diff --git a/firmware/ice40-riscv/icE1usb/usb_desc_app.c b/firmware/ice40-riscv/icE1usb/usb_desc_app.c
index e0edf80..25472e0 100644
--- a/firmware/ice40-riscv/icE1usb/usb_desc_app.c
+++ b/firmware/ice40-riscv/icE1usb/usb_desc_app.c
@@ -53,6 +53,11 @@
struct usb_ep_desc ep_data_in;
} __attribute__ ((packed)) cdc;
+ /* GPS-DO (control EP only) */
+ struct {
+ struct usb_intf_desc intf;
+ } __attribute__ ((packed)) gpsdo;
+
/* DFU Runtime */
struct {
struct usb_intf_desc intf;
@@ -275,6 +280,19 @@
.bInterval = 0x00,
},
},
+ .gpsdo = {
+ .intf = {
+ .bLength = sizeof(struct usb_intf_desc),
+ .bDescriptorType = USB_DT_INTF,
+ .bInterfaceNumber = USB_INTF_GPSDO,
+ .bAlternateSetting = 0,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = 0xff,
+ .bInterfaceSubClass = 0xe1,
+ .bInterfaceProtocol = 0xd0,
+ .iInterface = 11,
+ }
+ },
.dfu = {
.intf = {
.bLength = sizeof(struct usb_intf_desc),
@@ -285,7 +303,7 @@
.bInterfaceClass = 0xfe,
.bInterfaceSubClass = 0x01,
.bInterfaceProtocol = 0x01,
- .iInterface = 11,
+ .iInterface = 12,
},
.func = {
.bLength = sizeof(struct usb_dfu_func_desc),
diff --git a/firmware/ice40-riscv/icE1usb/usb_desc_ids.h b/firmware/ice40-riscv/icE1usb/usb_desc_ids.h
index dadf4c6..192092e 100644
--- a/firmware/ice40-riscv/icE1usb/usb_desc_ids.h
+++ b/firmware/ice40-riscv/icE1usb/usb_desc_ids.h
@@ -10,8 +10,9 @@
#define USB_INTF_E1(p) (0 + (p))
#define USB_INTF_GPS_CDC_CTL 2
#define USB_INTF_GPS_CDC_DATA 3
-#define USB_INTF_DFU 4
-#define USB_INTF_NUM 5
+#define USB_INTF_GPSDO 4
+#define USB_INTF_DFU 5
+#define USB_INTF_NUM 6
#define USB_EP_E1_IN(p) (0x82 + (3 * (p)))
#define USB_EP_E1_OUT(p) (0x01 + (3 * (p)))
diff --git a/firmware/ice40-riscv/icE1usb/usb_dev.c b/firmware/ice40-riscv/icE1usb/usb_dev.c
index 2997a33..e19d9a0 100644
--- a/firmware/ice40-riscv/icE1usb/usb_dev.c
+++ b/firmware/ice40-riscv/icE1usb/usb_dev.c
@@ -12,7 +12,6 @@
#include <no2usb/usb_proto.h>
#include "console.h"
-#include "gpsdo.h"
#include "misc.h"
#include "ice1usb_proto.h"
@@ -21,61 +20,6 @@
const char *fw_build_str = BUILD_INFO;
-static void
-_get_gpsdo_status(struct usb_ctrl_req *req, struct usb_xfer *xfer)
-{
- struct e1usb_gpsdo_status status;
-
- gpsdo_get_status(&status);
-
- memcpy(xfer->data, &status, sizeof(struct e1usb_gpsdo_status));
- xfer->len = sizeof(struct e1usb_gpsdo_status);
-}
-
-static void
-_get_gpsdo_mode(struct usb_ctrl_req *req, struct usb_xfer *xfer)
-{
- xfer->data[0] = gpsdo_enabled() ? ICE1USB_GPSDO_MODE_DISABLED : ICE1USB_GPSDO_MODE_AUTO;
- xfer->len = 1;
-}
-
-static void
-_set_gpsdo_mode(struct usb_ctrl_req *req, struct usb_xfer *xfer)
-{
- gpsdo_enable(req->wValue != ICE1USB_GPSDO_MODE_DISABLED);
-}
-
-static void
-_get_gpsdo_tune(struct usb_ctrl_req *req, struct usb_xfer *xfer)
-{
- uint16_t coarse, fine;
- struct e1usb_gpsdo_tune tune;
-
- gpsdo_get_tune(&coarse, &fine);
- tune.coarse = coarse;
- tune.fine = fine;
-
- memcpy(xfer->data, &tune, sizeof(struct e1usb_gpsdo_tune));
- xfer->len = sizeof(struct e1usb_gpsdo_tune);
-}
-
-static bool
-_set_gpsdo_tune_done(struct usb_xfer *xfer)
-{
- const struct e1usb_gpsdo_tune *tune = (const void *) xfer->data;
- gpsdo_set_tune(tune->coarse, tune->fine);
- return true;
-}
-
-static void
-_set_gpsdo_tune(struct usb_ctrl_req *req, struct usb_xfer *xfer)
-{
- xfer->cb_done = _set_gpsdo_tune_done;
- xfer->cb_ctx = req;
- xfer->len = sizeof(struct e1usb_gpsdo_tune);
-}
-
-
static enum usb_fnd_resp
_usb_dev_ctrl_req(struct usb_ctrl_req *req, struct usb_xfer *xfer)
{
@@ -93,21 +37,6 @@
xfer->data = (void*) fw_build_str;
xfer->len = strlen(fw_build_str);
break;
- case ICE1USB_DEV_GET_GPSDO_STATUS:
- _get_gpsdo_status(req, xfer);
- break;
- case ICE1USB_DEV_GET_GPSDO_MODE:
- _get_gpsdo_mode(req, xfer);
- break;
- case ICE1USB_DEV_SET_GPSDO_MODE:
- _set_gpsdo_mode(req, xfer);
- break;
- case ICE1USB_DEV_GET_GPSDO_TUNE:
- _get_gpsdo_tune(req, xfer);
- break;
- case ICE1USB_DEV_SET_GPSDO_TUNE:
- _set_gpsdo_tune(req, xfer);
- break;
default:
return USB_FND_ERROR;
}
diff --git a/firmware/ice40-riscv/icE1usb/usb_gpsdo.c b/firmware/ice40-riscv/icE1usb/usb_gpsdo.c
new file mode 100644
index 0000000..4dd8913
--- /dev/null
+++ b/firmware/ice40-riscv/icE1usb/usb_gpsdo.c
@@ -0,0 +1,120 @@
+/*
+ * usb_gpsdo.c
+ *
+ * Copyright (C) 2019-2022 Sylvain Munaut <tnt@246tNt.com>
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <no2usb/usb.h>
+#include <no2usb/usb_hw.h>
+#include <no2usb/usb_priv.h>
+
+#include <no2usb/usb_proto.h>
+
+#include "usb_desc_ids.h"
+#include "gpsdo.h"
+
+#include "ice1usb_proto.h"
+
+static void
+_get_gpsdo_status(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+ struct e1usb_gpsdo_status status;
+
+ gpsdo_get_status(&status);
+
+ memcpy(xfer->data, &status, sizeof(struct e1usb_gpsdo_status));
+ xfer->len = sizeof(struct e1usb_gpsdo_status);
+}
+
+static void
+_get_gpsdo_mode(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+ xfer->data[0] = gpsdo_enabled() ? ICE1USB_GPSDO_MODE_DISABLED : ICE1USB_GPSDO_MODE_AUTO;
+ xfer->len = 1;
+}
+
+static void
+_set_gpsdo_mode(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+ gpsdo_enable(req->wValue != ICE1USB_GPSDO_MODE_DISABLED);
+}
+
+static void
+_get_gpsdo_tune(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+ uint16_t coarse, fine;
+ struct e1usb_gpsdo_tune tune;
+
+ gpsdo_get_tune(&coarse, &fine);
+ tune.coarse = coarse;
+ tune.fine = fine;
+
+ memcpy(xfer->data, &tune, sizeof(struct e1usb_gpsdo_tune));
+ xfer->len = sizeof(struct e1usb_gpsdo_tune);
+}
+
+static bool
+_set_gpsdo_tune_done(struct usb_xfer *xfer)
+{
+ const struct e1usb_gpsdo_tune *tune = (const void *) xfer->data;
+ gpsdo_set_tune(tune->coarse, tune->fine);
+ return true;
+}
+
+static void
+_set_gpsdo_tune(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+ xfer->cb_done = _set_gpsdo_tune_done;
+ xfer->cb_ctx = req;
+ xfer->len = sizeof(struct e1usb_gpsdo_tune);
+}
+
+
+static enum usb_fnd_resp
+_gpsdo_ctrl_req(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+ /* Check it's for an interface */
+ if (USB_REQ_TYPE_RCPT(req) != (USB_REQ_TYPE_VENDOR | USB_REQ_RCPT_INTF))
+ return USB_FND_CONTINUE;
+
+ /* Check it's for the GPS-DO interface */
+ if (req->wIndex != USB_INTF_GPSDO)
+ return USB_FND_CONTINUE;
+
+ switch (req->bRequest) {
+ case ICE1USB_INTF_GET_GPSDO_STATUS:
+ _get_gpsdo_status(req, xfer);
+ break;
+ case ICE1USB_INTF_GET_GPSDO_MODE:
+ _get_gpsdo_mode(req, xfer);
+ break;
+ case ICE1USB_INTF_SET_GPSDO_MODE:
+ _set_gpsdo_mode(req, xfer);
+ break;
+ case ICE1USB_INTF_GET_GPSDO_TUNE:
+ _get_gpsdo_tune(req, xfer);
+ break;
+ case ICE1USB_INTF_SET_GPSDO_TUNE:
+ _set_gpsdo_tune(req, xfer);
+ break;
+ default:
+ return USB_FND_ERROR;
+ }
+
+ return USB_FND_SUCCESS;
+}
+
+static struct usb_fn_drv _gpsdo_drv = {
+ .ctrl_req = _gpsdo_ctrl_req,
+};
+
+void
+usb_gpsdo_init(void)
+{
+ usb_register_function_driver(&_gpsdo_drv);
+}
diff --git a/firmware/ice40-riscv/icE1usb/usb_gpsdo.h b/firmware/ice40-riscv/icE1usb/usb_gpsdo.h
new file mode 100644
index 0000000..4cf4203
--- /dev/null
+++ b/firmware/ice40-riscv/icE1usb/usb_gpsdo.h
@@ -0,0 +1,10 @@
+/*
+ * usb_gpsdo.h
+ *
+ * Copyright (C) 2022 Harald Welte <laforge@osmocom.org>
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+void usb_gpsdo_init(void);
diff --git a/firmware/ice40-riscv/icE1usb/usb_str_app.txt b/firmware/ice40-riscv/icE1usb/usb_str_app.txt
index 3aa6b4a..5f062d3 100644
--- a/firmware/ice40-riscv/icE1usb/usb_str_app.txt
+++ b/firmware/ice40-riscv/icE1usb/usb_str_app.txt
@@ -8,4 +8,5 @@
E1 port 1
GPS (CDC control)
GPS (CDC data)
+GPS-DO control
DFU runtime