Add support for icE1usb interrupt endpoint error reporting
Related: OS#4674 Change-Id: I5b0bf3cf40c623c17f2e88292e880b545c36b7b6 Depends: osmo-e1-hardware If157fde9d4ca05910b09537e19f37603c6d925f0
This commit is contained in:
parent
515be330cb
commit
a74aaf172c
|
@ -21,6 +21,7 @@ libosmo_e1d_la_LIBADD = $(LIBOSMOCORE_LIBS)
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
e1d.h \
|
e1d.h \
|
||||||
|
ice1usb_proto.h \
|
||||||
log.h \
|
log.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
|
|
@ -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));
|
99
src/usb.c
99
src/usb.c
|
@ -36,6 +36,7 @@
|
||||||
|
|
||||||
#include "e1d.h"
|
#include "e1d.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "ice1usb_proto.h"
|
||||||
|
|
||||||
|
|
||||||
#define USB_VID 0x1d50
|
#define USB_VID 0x1d50
|
||||||
|
@ -57,6 +58,7 @@ struct e1_usb_line_data {
|
||||||
uint8_t ep_in;
|
uint8_t ep_in;
|
||||||
uint8_t ep_out;
|
uint8_t ep_out;
|
||||||
uint8_t ep_fb;
|
uint8_t ep_fb;
|
||||||
|
uint8_t ep_int;
|
||||||
|
|
||||||
/* Max packet size */
|
/* Max packet size */
|
||||||
int pkt_size;
|
int pkt_size;
|
||||||
|
@ -66,6 +68,12 @@ struct e1_usb_line_data {
|
||||||
struct e1_usb_flow *flow_out;
|
struct e1_usb_flow *flow_out;
|
||||||
struct e1_usb_flow *flow_fb;
|
struct e1_usb_flow *flow_fb;
|
||||||
|
|
||||||
|
/* Interrupt */
|
||||||
|
struct {
|
||||||
|
uint8_t buf[10];
|
||||||
|
struct ice1usb_irq_err last_errcnt;
|
||||||
|
} irq;
|
||||||
|
|
||||||
/* Rate regulation */
|
/* Rate regulation */
|
||||||
uint32_t r_acc;
|
uint32_t r_acc;
|
||||||
uint32_t r_sw;
|
uint32_t r_sw;
|
||||||
|
@ -98,8 +106,6 @@ struct e1_usb_flow {
|
||||||
struct e1_usb_flow_entry *entries;
|
struct e1_usb_flow_entry *entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// USB data transfer
|
// USB data transfer
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -275,6 +281,88 @@ e1uf_start(struct e1_usb_flow *flow)
|
||||||
return 0;
|
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
|
// Init / Probing
|
||||||
|
@ -316,7 +404,7 @@ _e1_usb_open_device(struct e1_daemon *e1d, struct libusb_device *dev)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
id = &cd->interface[i].altsetting[1];
|
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;
|
continue;
|
||||||
|
|
||||||
/* Get interface and set it up */
|
/* 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)
|
else if (line_data->pkt_size != id->endpoint[j].wMaxPacketSize)
|
||||||
LOGP(DE1D, LOGL_ERROR, "Inconsistent max packet size %d vs %d\n",
|
LOGP(DE1D, LOGL_ERROR, "Inconsistent max packet size %d vs %d\n",
|
||||||
line_data->pkt_size, (int)id->endpoint[j].wMaxPacketSize);
|
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 {
|
} else {
|
||||||
LOGP(DE1D, LOGL_ERROR, "Invalid EP %02x\n", id->endpoint[j].bEndpointAddress);
|
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_in);
|
||||||
e1uf_start(line_data->flow_out);
|
e1uf_start(line_data->flow_out);
|
||||||
e1uf_start(line_data->flow_fb);
|
e1uf_start(line_data->flow_fb);
|
||||||
|
|
||||||
|
if (line_data->ep_int)
|
||||||
|
resubmit_irq(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue