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
This commit is contained in:
parent
f5362e9217
commit
7bc8404f63
|
@ -23,8 +23,54 @@ enum e1usb_dev_capability {
|
|||
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 @@ enum e1usb_dev_capability {
|
|||
#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 { };
|
||||
|
||||
|
|
250
src/usb.c
250
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_line_data {
|
|||
|
||||
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 @@ static int resubmit_irq(struct e1_line *line)
|
|||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Control transfers
|
||||
// Control transfers (USB interface == E1 line level)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct e1_usb_ctrl_xfer {
|
||||
|
@ -445,6 +455,10 @@ ctrl_xfer_compl_cb(struct libusb_transfer *xfr)
|
|||
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 @@ e1_usb_ctrl_set_rx_cfg(struct e1_line *line, enum ice1usb_rx_mode mode)
|
|||
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 @@ _e1_usb_open_device(struct e1_daemon *e1d, struct libusb_device *dev)
|
|||
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 @@ next_interface:
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,4 +12,10 @@ int e1_usb_ctrl_set_tx_cfg(struct e1_line *line, enum ice1usb_tx_mode mode,
|
|||
|
||||
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);
|
||||
|
|
13
src/vty.c
13
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 const char *intf_serno(const struct e1_intf *intf)
|
|||
|
||||
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>]",
|
||||
|
|
Loading…
Reference in New Issue