laforge has submitted this change. (
https://gerrit.osmocom.org/c/osmo-e1d/+/27020 )
Change subject: GPS-DO support for icE1usb hardware
......................................................................
GPS-DO support for icE1usb hardware
This adds support for monitoring the GPS-DO that is built-in to the
icE1usb device. It assumes a very recent firmware with GPS-DO control
moved to a separate USB interface, i.e. after osmo-e1-hardware.git
Change-Id Icd6555a14896c38626fb147b78af44ff719f2254 is merged.
Change-Id: If5e2a6b2dae0290ce3186009e68f618049ebf5ff
---
M src/ice1usb_proto.h
M src/usb.c
M src/usb.h
M src/vty.c
4 files changed, 316 insertions(+), 2 deletions(-)
Approvals:
Jenkins Builder: Verified
laforge: Looks good to me, approved
pespin: Looks good to me, but someone else must approve
diff --git a/src/ice1usb_proto.h b/src/ice1usb_proto.h
index d0d7304..3170d55 100644
--- a/src/ice1usb_proto.h
+++ b/src/ice1usb_proto.h
@@ -23,8 +23,54 @@
ICE1USB_DEV_CAP_GPSDO,
};
+/***********************************************************************
+ * Control Endpoint / GPS-DO Interface Requests
+ ***********************************************************************/
-/* 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,
+};
+
+enum ice1usb_gpsdo_antenna_state {
+ ICE1USB_GPSDO_ANT_UNKNOWN = 0,
+ ICE1USB_GPSDO_ANT_OK = 1,
+ ICE1USB_GPSDO_ANT_OPEN = 2,
+ ICE1USB_GPSDO_ANT_SHORT = 3,
+};
+
+enum ice1usb_gpsdo_state {
+ ICE1USB_GPSDO_STATE_DISABLED = 0,
+ ICE1USB_GPSDO_STATE_CALIBRATE = 1,
+ ICE1USB_GPSDO_STATE_HOLD_OVER = 2,
+ ICE1USB_GPSDO_STATE_TUNE_COARSE = 3,
+ ICE1USB_GPSDO_STATE_TUNE_FINE = 4,
+};
+
+struct e1usb_gpsdo_tune {
+ uint16_t coarse;
+ uint16_t fine;
+} __attribute__((packed));
+
+struct e1usb_gpsdo_status {
+ uint8_t state;
+ 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 */
+ uint32_t freq_est; /*!< Latest frequency estimate measurement */
+} __attribute__((packed));
+
+
+/***********************************************************************
+ * Control Endpoint / E1 Interface Requests
+ ***********************************************************************/
/*! returns a bit-mask of optional device capabilities (see enum e1usb_intf_capability)
*/
#define ICE1USB_INTF_GET_CAPABILITIES 0x01
@@ -32,6 +78,7 @@
#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 */
+#define ICE1USB_INTF_GET_ERRORS 0x06 /*!< struct ice1usb_irq_err */
//enum e1usb_intf_capability { };
diff --git a/src/usb.c b/src/usb.c
index c8920e5..8194faf 100644
--- a/src/usb.c
+++ b/src/usb.c
@@ -30,6 +30,7 @@
#include <osmocom/core/isdnhdlc.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/bit32gen.h>
#include <osmocom/usb/libusb.h>
#include <libusb.h>
@@ -84,6 +85,15 @@
struct e1_usb_intf_data {
libusb_device_handle *devh;
+
+ struct {
+ uint8_t if_num;
+ struct osmo_timer_list poll_timer;
+ struct e1usb_gpsdo_status last_status;
+ } gpsdo;
+
+ /* list of in-progress CTRL operations */
+ struct llist_head ctrl_inprogress;
};
@@ -415,7 +425,7 @@
}
// ---------------------------------------------------------------------------
-// Control transfers
+// Control transfers (USB interface == E1 line level)
// ---------------------------------------------------------------------------
struct e1_usb_ctrl_xfer {
@@ -445,6 +455,10 @@
libusb_free_transfer(xfr);
}
+// ---------------------------------------------------------------------------
+// Control transfers (USB device == E1 interface level)
+// ---------------------------------------------------------------------------
+
/* generic helper for async transmission of control endpoint requests */
static int
_e1_usb_line_send_ctrl(struct e1_line *line, uint8_t bmReqType, uint8_t bReq, uint16_t
wValue,
@@ -518,6 +532,224 @@
sizeof(rx_cfg));
}
+struct e1_usb_ctrl_xfer_intf {
+ struct e1_intf *intf;
+ struct llist_head list;
+ /* 8 bytes control setup packet, remainder for data */
+ uint8_t buffer[8 + sizeof(struct e1usb_gpsdo_status)];
+};
+
+static void _e1_usb_intf_gpsdo_status_cb(struct e1_intf *intf, const uint8_t *data,
size_t len);
+
+static void
+ctrl_xfer_intf_compl_cb(struct libusb_transfer *xfr)
+{
+ struct e1_usb_ctrl_xfer_intf *ucx = xfr->user_data;
+ struct libusb_control_setup *setup;
+
+ switch (xfr->status) {
+ case LIBUSB_TRANSFER_COMPLETED:
+ setup = (struct libusb_control_setup *) ucx->buffer;
+ LOGPIF(ucx->intf, DE1D, LOGL_DEBUG, "CTRL transfer completed successfully:
%s\n",
+ osmo_hexdump(ucx->buffer, 8+xfr->actual_length));
+ switch (setup->bRequest) {
+ case ICE1USB_INTF_GET_GPSDO_STATUS:
+ _e1_usb_intf_gpsdo_status_cb(ucx->intf, ucx->buffer+8, xfr->actual_length);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ LOGPIF(ucx->intf, DE1D, LOGL_ERROR, "CTRL transfer completed unsuccessfully
%d\n",
+ xfr->status);
+ break;
+ }
+ llist_del(&ucx->list);
+ talloc_free(ucx);
+ libusb_free_transfer(xfr);
+}
+
+
+/* generic helper for async transmission of control endpoint requests */
+static int
+_e1_usb_intf_send_ctrl(struct e1_intf *intf, uint8_t bmReqType, uint8_t bReq, uint16_t
wValue,
+ const uint8_t *data, size_t data_len)
+{
+ struct e1_usb_ctrl_xfer_intf *ucx = talloc_zero(intf, struct e1_usb_ctrl_xfer_intf);
+ struct e1_usb_intf_data *id = (struct e1_usb_intf_data *) intf->drv_data;
+ struct libusb_transfer *xfr;
+ int rc;
+
+ if (!ucx)
+ return -ENOMEM;
+
+ OSMO_ASSERT(sizeof(ucx->buffer) >= 8+data_len);
+ ucx->intf = intf;
+ libusb_fill_control_setup(ucx->buffer, bmReqType, bReq, wValue, id->gpsdo.if_num,
data_len);
+ if (data && data_len)
+ memcpy(ucx->buffer+8, data, data_len);
+
+ xfr = libusb_alloc_transfer(0);
+ if (!xfr) {
+ rc = -ENOMEM;
+ goto free_ucx;
+ }
+
+ libusb_fill_control_transfer(xfr, id->devh, ucx->buffer, ctrl_xfer_intf_compl_cb,
ucx, 3000);
+ rc = libusb_submit_transfer(xfr);
+ if (rc != 0)
+ goto free_xfr;
+
+ llist_add_tail(&ucx->list, &id->ctrl_inprogress);
+
+ return 0;
+
+free_xfr:
+ libusb_free_transfer(xfr);
+free_ucx:
+ talloc_free(ucx);
+
+ return rc;
+}
+
+int
+e1_usb_ctrl_set_gpsdo_mode(struct e1_intf *intf, enum ice1usb_gpsdo_mode gpsdo_mode)
+{
+ const uint16_t bmReqType = LIBUSB_RECIPIENT_INTERFACE | LIBUSB_REQUEST_TYPE_VENDOR |
+ LIBUSB_ENDPOINT_OUT;
+ return _e1_usb_intf_send_ctrl(intf, bmReqType, ICE1USB_INTF_SET_GPSDO_MODE, 0,
+ (uint8_t *)&gpsdo_mode, sizeof(gpsdo_mode));
+}
+
+int
+e1_usb_ctrl_set_gpsdo_tune(struct e1_intf *intf, const struct e1usb_gpsdo_tune
*gpsdo_tune)
+{
+ const uint16_t bmReqType = LIBUSB_RECIPIENT_INTERFACE | LIBUSB_REQUEST_TYPE_VENDOR |
+ LIBUSB_ENDPOINT_OUT;
+ return _e1_usb_intf_send_ctrl(intf, bmReqType, ICE1USB_INTF_SET_GPSDO_TUNE, 0,
+ (uint8_t *)gpsdo_tune, sizeof(gpsdo_tune));
+}
+
+int
+e1_usb_ctrl_get_gpsdo_status(struct e1_intf *intf)
+{
+ const uint16_t bmReqType = LIBUSB_RECIPIENT_INTERFACE | LIBUSB_REQUEST_TYPE_VENDOR |
+ LIBUSB_ENDPOINT_IN;
+ return _e1_usb_intf_send_ctrl(intf, bmReqType, ICE1USB_INTF_GET_GPSDO_STATUS, 0,
+ NULL, sizeof(struct e1usb_gpsdo_status));
+}
+
+// ---------------------------------------------------------------------------
+// GPS-DO
+// ---------------------------------------------------------------------------
+
+static const struct value_string ice1usb_gpsdo_mode_str[] = {
+ { ICE1USB_GPSDO_MODE_DISABLED, "DISABLED" },
+ { ICE1USB_GPSDO_MODE_AUTO, "AUTO" },
+ { 0, NULL }
+};
+
+static const struct value_string ice1usb_gpsdo_antenna_state_str[] = {
+ { ICE1USB_GPSDO_ANT_UNKNOWN, "UNKNOWN" },
+ { ICE1USB_GPSDO_ANT_OK, "OK" },
+ { ICE1USB_GPSDO_ANT_OPEN, "OPEN" },
+ { ICE1USB_GPSDO_ANT_SHORT, "SHORT" },
+ { 0, NULL }
+};
+
+static const struct value_string ice1usb_gpsdo_state_str[] = {
+ { ICE1USB_GPSDO_STATE_DISABLED, "DISABLED" },
+ { ICE1USB_GPSDO_STATE_CALIBRATE, "CALIBRATE" },
+ { ICE1USB_GPSDO_STATE_HOLD_OVER, "HOLD_OVER" },
+ { ICE1USB_GPSDO_STATE_TUNE_COARSE, "TUNE_COARSE" },
+ { ICE1USB_GPSDO_STATE_TUNE_FINE, "TUNE_FINE" },
+ { 0, NULL }
+};
+
+int
+e1_usb_intf_gpsdo_state_string(char *buf, size_t len, const struct e1_intf *intf)
+{
+ struct e1_usb_intf_data *id = intf->drv_data;
+ struct e1usb_gpsdo_status *last_st = &id->gpsdo.last_status;
+
+ OSMO_ASSERT(intf->drv == E1_DRIVER_USB);
+
+ return snprintf(buf, len, "mode=%s, fix=%s, state=%s antenna=%s, tune=%u/%u,
freq_est=%u",
+ get_value_string(ice1usb_gpsdo_mode_str, last_st->mode),
+ last_st->valid_fix ? "TRUE" : "FALSE",
+ get_value_string(ice1usb_gpsdo_state_str, last_st->state),
+ get_value_string(ice1usb_gpsdo_antenna_state_str, last_st->antenna_state),
+ libusb_le16_to_cpu(last_st->tune.coarse),
libusb_le16_to_cpu(last_st->tune.fine),
+ osmo_load32le(&last_st->freq_est));
+}
+
+static void
+_e1_usb_intf_gpsdo_status_cb(struct e1_intf *intf, const uint8_t *data, size_t len)
+{
+ struct e1_usb_intf_data *id = intf->drv_data;
+ struct e1usb_gpsdo_status *last_st = &id->gpsdo.last_status;
+ const struct e1usb_gpsdo_status *st;
+
+ if (len < sizeof(*st)) {
+ LOGPIF(intf, DE1D, LOGL_ERROR, "GPSDO status %zu < %zu!\n", len,
sizeof(*st));
+ return;
+ }
+ st = (const struct e1usb_gpsdo_status *) data;
+
+ if (st->state != last_st->state) {
+ LOGPIF(intf, DE1D, LOGL_NOTICE, "GPSDO state change: %s -> %s\n",
+ get_value_string(ice1usb_gpsdo_state_str, last_st->state),
+ get_value_string(ice1usb_gpsdo_state_str, st->state));
+ }
+
+ if (st->antenna_state != last_st->antenna_state) {
+ int level = LOGL_NOTICE;
+ switch (st->antenna_state) {
+ case ICE1USB_GPSDO_ANT_OPEN:
+ case ICE1USB_GPSDO_ANT_SHORT:
+ level = LOGL_ERROR;
+ break;
+ default:
+ level = LOGL_NOTICE;
+ }
+ LOGPIF(intf, DE1D, level, "GPS antenna status change: %s -> %s\n",
+ get_value_string(ice1usb_gpsdo_antenna_state_str, last_st->antenna_state),
+ get_value_string(ice1usb_gpsdo_antenna_state_str, st->antenna_state));
+ }
+
+ if (st->valid_fix != last_st->valid_fix) {
+ if (st->valid_fix)
+ LOGPIF(intf, DE1D, LOGL_NOTICE, "GPS Fix achieved\n");
+ else
+ LOGPIF(intf, DE1D, LOGL_ERROR, "GPS Fix LOST\n");
+ }
+
+ /* update our state */
+ memcpy(last_st, st, sizeof(*last_st));
+}
+
+static void
+_e1_usb_gpsdo_poll_cb(void *data)
+{
+ struct e1_intf *intf = (struct e1_intf *) data;
+ struct e1_usb_intf_data *id = intf->drv_data;
+
+ /* issue a control endpoint request, further processing is when it completes */
+ e1_usb_ctrl_get_gpsdo_status(intf);
+
+ osmo_timer_schedule(&id->gpsdo.poll_timer, 1, 0);
+}
+
+static void
+_e1_usb_gpsdo_init(struct e1_intf *intf)
+{
+ struct e1_usb_intf_data *id = intf->drv_data;
+
+ osmo_timer_setup(&id->gpsdo.poll_timer, &_e1_usb_gpsdo_poll_cb, intf);
+ osmo_timer_schedule(&id->gpsdo.poll_timer, 1, 0);
+}
+
// ---------------------------------------------------------------------------
// Init / Probing
// ---------------------------------------------------------------------------
@@ -587,6 +819,8 @@
osmo_talloc_replace_string(intf, &intf->usb.serial_str, serial_str);
}
+ INIT_LLIST_HEAD(&intf_data->ctrl_inprogress);
+
ret = libusb_get_active_config_descriptor(dev, &cd);
if (ret) {
LOGP(DE1D, LOGL_ERROR, "Failed to talk to usb device\n");
@@ -690,6 +924,20 @@
line_nr++;
}
+ /* find the GPS-DO interface (if any) */
+ for (i = 0; i < cd->bNumInterfaces; i++) {
+ if (cd->interface[i].num_altsetting != 1)
+ continue;
+
+ id = &cd->interface[i].altsetting[0];
+ if ((id->bInterfaceClass == 0xff) && (id->bInterfaceSubClass == 0xe1)
&&
+ (id->bInterfaceProtocol == 0xd0)) {
+ intf_data->gpsdo.if_num = id->bInterfaceNumber;
+ _e1_usb_gpsdo_init(intf);
+ break;
+ }
+ }
+
return 0;
}
diff --git a/src/usb.h b/src/usb.h
index 7142686..41b4b2e 100644
--- a/src/usb.h
+++ b/src/usb.h
@@ -12,4 +12,10 @@
int e1_usb_ctrl_set_rx_cfg(struct e1_line *line, enum ice1usb_rx_mode mode);
+int e1_usb_ctrl_set_gpsdo_mode(struct e1_intf *intf, enum ice1usb_gpsdo_mode
gpsdo_mode);
+int e1_usb_ctrl_set_gpsdo_tune(struct e1_intf *intf, const struct e1usb_gpsdo_tune
*gpsdo_tune);
+int e1_usb_ctrl_get_gpsdo_status(struct e1_intf *intf);
+
+int e1_usb_intf_gpsdo_state_string(char *buf, size_t len, const struct e1_intf *intf);
+
int e1_usb_probe(struct e1_daemon *e1d);
diff --git a/src/vty.c b/src/vty.c
index fca56a4..9d518d4 100644
--- a/src/vty.c
+++ b/src/vty.c
@@ -38,6 +38,7 @@
#include <osmocom/e1d/proto.h>
#include "e1d.h"
+#include "usb.h"
struct e1_daemon *vty_e1d;
@@ -75,8 +76,20 @@
static void vty_dump_intf(struct vty *vty, const struct e1_intf *intf)
{
+ char buf[128];
+
vty_out(vty, "Interface #%u (%s), Driver: %s%s", intf->id,
intf_serno(intf),
get_value_string(e1_driver_names, intf->drv), VTY_NEWLINE);
+
+ /* TODO: put this behind some call-back */
+ switch (intf->drv) {
+ case E1_DRIVER_USB:
+ e1_usb_intf_gpsdo_state_string(buf, sizeof(buf), intf);
+ vty_out(vty, " GPS-DO: %s%s", buf, VTY_NEWLINE);
+ break;
+ default:
+ break;
+ }
}
DEFUN(show_intf, show_intf_cmd, "show interface [<0-255>]",
5 is the latest approved patch-set.
No files were changed between the latest approved patch-set and the submitted one.
--
To view, visit
https://gerrit.osmocom.org/c/osmo-e1d/+/27020
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-e1d
Gerrit-Branch: master
Gerrit-Change-Id: If5e2a6b2dae0290ce3186009e68f618049ebf5ff
Gerrit-Change-Number: 27020
Gerrit-PatchSet: 6
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
Gerrit-Reviewer: roox <mardnh(a)gmx.de>
Gerrit-Reviewer: tnt <tnt(a)246tNt.com>
Gerrit-MessageType: merged