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.