Add support for icE1usb interrupt endpoint error reporting

Related: OS#4674
Change-Id: I5b0bf3cf40c623c17f2e88292e880b545c36b7b6
Depends: osmo-e1-hardware If157fde9d4ca05910b09537e19f37603c6d925f0
This commit is contained in:
Harald Welte 2020-12-15 22:12:57 +01:00
parent 515be330cb
commit a74aaf172c
3 changed files with 196 additions and 3 deletions

View File

@ -21,6 +21,7 @@ libosmo_e1d_la_LIBADD = $(LIBOSMOCORE_LIBS)
noinst_HEADERS = \
e1d.h \
ice1usb_proto.h \
log.h \
$(NULL)

99
src/ice1usb_proto.h Normal file
View File

@ -0,0 +1,99 @@
#pragma once
/* Header file describing the USB protocol between the icE1usb firmware and the host
* software (currently really only osmo-e1d) */
/***********************************************************************
* Control Endpoint / Device Requests
***********************************************************************/
/*! 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
enum e1usb_dev_capability {
/*! Does this board have a GPS-DO */
ICE1USB_DEV_CAP_GPSDO,
};
/* Interface Requests */
/*! returns a bit-mask of optional device capabilities (see enum e1usb_intf_capability) */
#define ICE1USB_INTF_GET_CAPABILITIES 0x01
#define ICE1USB_INTF_SET_TX_CFG 0x02 /*!< struct ice1usb_tx_config */
#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 */
//enum e1usb_intf_capability { };
enum ice1usb_tx_mode {
ICE1USB_TX_MODE_TRANSP = 0,
ICE1USB_TX_MODE_TS0 = 1,
ICE1USB_TX_MODE_TS0_CRC4 = 2,
ICE1USB_TX_MODE_TS0_CRC4_E = 3,
};
enum ice1usb_tx_timing {
ICE1USB_TX_TIME_SRC_LOCAL = 0,
ICE1USB_TX_TIME_SRC_REMOTE = 1,
};
enum ice1usb_tx_ext_loopback {
ICE1USB_TX_EXT_LOOPBACK_OFF = 0,
ICE1USB_TX_EXT_LOOPBACK_SAME = 1,
ICE1USB_TX_EXT_LOOPBACK_CROSS = 2,
};
/* ICE1USB_INTF_{GET,SET}_TX_CFG */
struct ice1usb_tx_config {
uint8_t mode; /*!< enum ice1usb_tx_mode */
uint8_t timing; /*!< enum ice1usb_tx_timing */
uint8_t ext_loopback; /*!< enum ice1usb_tx_ext_loopback */
uint8_t alarm; /*!< 1 = transmit alarm; 0 = don't */
} __attribute__((packed));
enum ice1usb_rx_mode {
/*! transparent, unaligned bitstream */
ICE1USB_RX_MODE_TRANSP = 0,
/*! alignment to E1 frame */
ICE1USB_RX_MODE_FRAME = 2,
/*! alignment to E1 multiframe */
ICE1USB_RX_MODE_MULTIFRAME = 3,
};
/* ICE1USB_INTF_{GET,SET}_RX_CFG */
struct ice1usb_rx_config {
uint8_t mode; /*!< enum ice1usb_rx_mode */
} __attribute__((packed));
/***********************************************************************
* Interrupt Endpoint
***********************************************************************/
enum ice1usb_irq_type {
ICE1USB_IRQQ_T_ERRCNT = 1,
};
/* Ensue ro keep those in sync with e1.h */
#define ICE1USB_ERR_F_ALIGN_ERR 0x01
#define ICE1USB_ERR_F_TICK_ERR 0x02
struct ice1usb_irq_err {
/* 16-bit little-endian counters */
uint16_t crc;
uint16_t align;
uint16_t ovfl;
uint16_t unfl;
uint8_t flags;
} __attribute__((packed));
struct ice1usb_irq {
uint8_t type; /*!< enum ice1usb_irq_type */
union {
struct ice1usb_irq_err errors;
} u;
} __attribute__((packed));

View File

@ -36,6 +36,7 @@
#include "e1d.h"
#include "log.h"
#include "ice1usb_proto.h"
#define USB_VID 0x1d50
@ -57,6 +58,7 @@ struct e1_usb_line_data {
uint8_t ep_in;
uint8_t ep_out;
uint8_t ep_fb;
uint8_t ep_int;
/* Max packet size */
int pkt_size;
@ -66,6 +68,12 @@ struct e1_usb_line_data {
struct e1_usb_flow *flow_out;
struct e1_usb_flow *flow_fb;
/* Interrupt */
struct {
uint8_t buf[10];
struct ice1usb_irq_err last_errcnt;
} irq;
/* Rate regulation */
uint32_t r_acc;
uint32_t r_sw;
@ -98,8 +106,6 @@ struct e1_usb_flow {
struct e1_usb_flow_entry *entries;
};
// ---------------------------------------------------------------------------
// USB data transfer
// ---------------------------------------------------------------------------
@ -275,6 +281,88 @@ e1uf_start(struct e1_usb_flow *flow)
return 0;
}
// ---------------------------------------------------------------------------
// USB interrupt
// ---------------------------------------------------------------------------
static int resubmit_irq(struct e1_line *line);
static void rx_interrupt_errcnt(struct e1_line *line, const struct ice1usb_irq_err *errcnt)
{
struct e1_usb_line_data *ld = (struct e1_usb_line_data *) line->drv_data;
struct ice1usb_irq_err *last = &ld->irq.last_errcnt;
if (errcnt->crc != last->crc) {
LOGPLI(line, DE1D, LOGL_ERROR, "CRC error count %d (was %d)\n",
errcnt->crc, last->crc);
}
if (errcnt->align != last->align) {
LOGPLI(line, DE1D, LOGL_ERROR, "ALIGNMENT error count %d (was %d)\n",
errcnt->align, last->align);
}
if (errcnt->ovfl != last->ovfl) {
LOGPLI(line, DE1D, LOGL_ERROR, "OVERFLOW error count %d (was %d)\n",
errcnt->ovfl, last->ovfl);
}
if (errcnt->unfl != last->unfl) {
LOGPLI(line, DE1D, LOGL_ERROR, "UNDERFLOW error count %d (was %d)\n",
errcnt->unfl, last->unfl);
}
if ((errcnt->flags & ICE1USB_ERR_F_ALIGN_ERR) != (last->flags & ICE1USB_ERR_F_ALIGN_ERR)) {
LOGPLI(line, DE1D, LOGL_ERROR, "ALIGNMENT %s\n",
errcnt->flags & ICE1USB_ERR_F_ALIGN_ERR ? "LOST" : "REGAINED");
}
if ((errcnt->flags & ICE1USB_ERR_F_TICK_ERR) != (last->flags & ICE1USB_ERR_F_TICK_ERR)) {
LOGPLI(line, DE1D, LOGL_ERROR, "Rx Clock %s\n",
errcnt->flags & ICE1USB_ERR_F_TICK_ERR ? "LOST" : "REGAINED");
}
ld->irq.last_errcnt = *errcnt;
}
static void interrupt_ep_cb(struct libusb_transfer *xfer)
{
struct e1_line *line = (struct e1_line *) xfer->user_data;
const struct ice1usb_irq *irq = (const struct ice1usb_irq *) xfer->buffer;
if (!xfer->actual_length) {
LOGPLI(line, DE1D, LOGL_ERROR, "Zero-Length Interrupt transfer\n");
goto out;
}
switch (irq->type) {
case ICE1USB_IRQQ_T_ERRCNT:
if (xfer->actual_length < sizeof(*irq)) {
LOGPLI(line, DE1D, LOGL_ERROR, "Short ERRCNT interrupt: %u<%zu\n",
xfer->actual_length, sizeof(*irq));
break;
}
rx_interrupt_errcnt(line, &irq->u.errors);
break;
default:
LOGPLI(line, DE1D, LOGL_INFO, "Unsupported interrupt 0x%02x\n", irq->type);
break;
}
out:
resubmit_irq(line);
}
static int resubmit_irq(struct e1_line *line)
{
struct e1_usb_line_data *ld = (struct e1_usb_line_data *) line->drv_data;
struct e1_usb_intf_data *id = (struct e1_usb_intf_data *) line->intf->drv_data;
struct libusb_transfer *xfr = libusb_alloc_transfer(0);
libusb_fill_interrupt_transfer(xfr, id->devh, ld->ep_int, ld->irq.buf, sizeof(ld->irq.buf),
interrupt_ep_cb, line, 0);
return libusb_submit_transfer(xfr);
}
// ---------------------------------------------------------------------------
// Init / Probing
@ -316,7 +404,7 @@ _e1_usb_open_device(struct e1_daemon *e1d, struct libusb_device *dev)
continue;
id = &cd->interface[i].altsetting[1];
if ((id->bInterfaceClass != 0xff) || (id->bInterfaceSubClass != 0xe1) || (id->bNumEndpoints != 3))
if ((id->bInterfaceClass != 0xff) || (id->bInterfaceSubClass != 0xe1) || (id->bNumEndpoints < 3))
continue;
/* Get interface and set it up */
@ -353,6 +441,8 @@ _e1_usb_open_device(struct e1_daemon *e1d, struct libusb_device *dev)
else if (line_data->pkt_size != id->endpoint[j].wMaxPacketSize)
LOGP(DE1D, LOGL_ERROR, "Inconsistent max packet size %d vs %d\n",
line_data->pkt_size, (int)id->endpoint[j].wMaxPacketSize);
} else if (id->endpoint[j].bmAttributes == 0x03) {
line_data->ep_int = id->endpoint[j].bEndpointAddress;
} else {
LOGP(DE1D, LOGL_ERROR, "Invalid EP %02x\n", id->endpoint[j].bEndpointAddress);
}
@ -372,6 +462,9 @@ _e1_usb_open_device(struct e1_daemon *e1d, struct libusb_device *dev)
e1uf_start(line_data->flow_in);
e1uf_start(line_data->flow_out);
e1uf_start(line_data->flow_fb);
if (line_data->ep_int)
resubmit_irq(line);
}
return 0;