#include "connection.h"
#include "greybus_trace.h"
+
+/* Fixed CPort numbers */
+#define ES2_CPORT_CDSI0 16
+#define ES2_CPORT_CDSI1 17
+
/* Memory sizes for the buffers sent to/from the ES2 controller */
#define ES2_GBUF_MSG_SIZE_MAX 2048
bool cport_out_urb_cancelled[NUM_CPORT_OUT_URB];
spinlock_t cport_out_urb_lock;
+ bool cdsi1_in_use;
+
int *cport_to_ep;
struct task_struct *apb_log_task;
__u8 endpoint_out;
};
+/**
+ * timesync_enable_request - Enable timesync in an APBridge
+ * @count: number of TimeSync Pulses to expect
+ * @frame_time: the initial FrameTime at the first TimeSync Pulse
+ * @strobe_delay: the expected delay in microseconds between each TimeSync Pulse
+ * @refclk: The AP mandated reference clock to run FrameTime at
+ */
+struct timesync_enable_request {
+ __u8 count;
+ __le64 frame_time;
+ __le32 strobe_delay;
+ __le32 refclk;
+} __packed;
+
+/**
+ * timesync_authoritative_request - Transmit authoritative FrameTime to APBridge
+ * @frame_time: An array of authoritative FrameTimes provided by the SVC
+ * and relayed to the APBridge by the AP
+ */
+struct timesync_authoritative_request {
+ __le64 frame_time[GB_TIMESYNC_MAX_STROBES];
+} __packed;
+
static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd)
{
return (struct es2_ap_dev *)&hd->hd_priv;
struct usb_device *udev = es2->usb_dev;
int retval;
+ switch (cport_id) {
+ case GB_SVC_CPORT_ID:
+ case ES2_CPORT_CDSI0:
+ case ES2_CPORT_CDSI1:
+ return 0;
+ }
+
retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
GB_APB_REQUEST_RESET_CPORT,
USB_DIR_OUT | USB_TYPE_VENDOR |
return 0;
}
+static int es2_cport_allocate(struct gb_host_device *hd, int cport_id,
+ unsigned long flags)
+{
+ struct es2_ap_dev *es2 = hd_to_es2(hd);
+ struct ida *id_map = &hd->cport_id_map;
+ int ida_start, ida_end;
+
+ switch (cport_id) {
+ case ES2_CPORT_CDSI0:
+ case ES2_CPORT_CDSI1:
+ dev_err(&hd->dev, "cport %d not available\n", cport_id);
+ return -EBUSY;
+ }
+
+ if (flags & GB_CONNECTION_FLAG_OFFLOADED &&
+ flags & GB_CONNECTION_FLAG_CDSI1) {
+ if (es2->cdsi1_in_use) {
+ dev_err(&hd->dev, "CDSI1 already in use\n");
+ return -EBUSY;
+ }
+
+ es2->cdsi1_in_use = true;
+
+ return ES2_CPORT_CDSI1;
+ }
+
+ if (cport_id < 0) {
+ ida_start = 0;
+ ida_end = hd->num_cports;
+ } else if (cport_id < hd->num_cports) {
+ ida_start = cport_id;
+ ida_end = cport_id + 1;
+ } else {
+ dev_err(&hd->dev, "cport %d not available\n", cport_id);
+ return -EINVAL;
+ }
+
+ return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL);
+}
+
+static void es2_cport_release(struct gb_host_device *hd, u16 cport_id)
+{
+ struct es2_ap_dev *es2 = hd_to_es2(hd);
+
+ switch (cport_id) {
+ case ES2_CPORT_CDSI1:
+ es2->cdsi1_in_use = false;
+ return;
+ }
+
+ ida_simple_remove(&hd->cport_id_map, cport_id);
+}
+
static int cport_enable(struct gb_host_device *hd, u16 cport_id)
{
int retval;
- if (cport_id != GB_SVC_CPORT_ID) {
- retval = cport_reset(hd, cport_id);
- if (retval)
- return retval;
- }
+ retval = cport_reset(hd, cport_id);
+ if (retval)
+ return retval;
return 0;
}
struct es2_ap_dev *es2 = hd_to_es2(hd);
struct usb_device *udev = es2->usb_dev;
- if (!cport_id_valid(hd, cport_id)) {
- dev_err(&udev->dev, "invalid cport %u\n", cport_id);
- return -EINVAL;
- }
-
retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
GB_APB_REQUEST_LATENCY_TAG_EN,
USB_DIR_OUT | USB_TYPE_VENDOR |
struct es2_ap_dev *es2 = hd_to_es2(hd);
struct usb_device *udev = es2->usb_dev;
- if (!cport_id_valid(hd, cport_id)) {
- dev_err(&udev->dev, "invalid cport %u\n", cport_id);
- return -EINVAL;
- }
-
retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
GB_APB_REQUEST_LATENCY_TAG_DIS,
USB_DIR_OUT | USB_TYPE_VENDOR |
return retval;
}
+static int timesync_enable(struct gb_host_device *hd, u8 count,
+ u64 frame_time, u32 strobe_delay, u32 refclk)
+{
+ int retval;
+ struct es2_ap_dev *es2 = hd_to_es2(hd);
+ struct usb_device *udev = es2->usb_dev;
+ struct gb_control_timesync_enable_request *request;
+
+ request = kzalloc(sizeof(*request), GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+
+ request->count = count;
+ request->frame_time = cpu_to_le64(frame_time);
+ request->strobe_delay = cpu_to_le32(strobe_delay);
+ request->refclk = cpu_to_le32(refclk);
+ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ REQUEST_TIMESYNC_ENABLE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_INTERFACE, 0, 0, request,
+ sizeof(*request), ES2_TIMEOUT);
+ if (retval < 0)
+ dev_err(&udev->dev, "Cannot enable timesync %d\n", retval);
+
+ kfree(request);
+ return retval;
+}
+
+static int timesync_disable(struct gb_host_device *hd)
+{
+ int retval;
+ struct es2_ap_dev *es2 = hd_to_es2(hd);
+ struct usb_device *udev = es2->usb_dev;
+
+ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ REQUEST_TIMESYNC_DISABLE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_INTERFACE, 0, 0, NULL,
+ 0, ES2_TIMEOUT);
+ if (retval < 0)
+ dev_err(&udev->dev, "Cannot disable timesync %d\n", retval);
+
+ return retval;
+}
+
+static int timesync_authoritative(struct gb_host_device *hd, u64 *frame_time)
+{
+ int retval, i;
+ struct es2_ap_dev *es2 = hd_to_es2(hd);
+ struct usb_device *udev = es2->usb_dev;
+ struct timesync_authoritative_request *request;
+
+ request = kzalloc(sizeof(*request), GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+
+ for (i = 0; i < GB_TIMESYNC_MAX_STROBES; i++)
+ request->frame_time[i] = cpu_to_le64(frame_time[i]);
+
+ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ REQUEST_TIMESYNC_AUTHORITATIVE,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_INTERFACE, 0, 0, request,
+ sizeof(*request), ES2_TIMEOUT);
+ if (retval < 0)
+ dev_err(&udev->dev, "Cannot timesync authoritative out %d\n", retval);
+
+ kfree(request);
+ return retval;
+}
+
+static int timesync_get_last_event(struct gb_host_device *hd, u64 *frame_time)
+{
+ int retval;
+ struct es2_ap_dev *es2 = hd_to_es2(hd);
+ struct usb_device *udev = es2->usb_dev;
+ __le64 *response_frame_time;
+
+ response_frame_time = kzalloc(sizeof(*response_frame_time), GFP_KERNEL);
+ if (!response_frame_time)
+ return -ENOMEM;
+
+ retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ REQUEST_TIMESYNC_GET_LAST_EVENT,
+ USB_DIR_IN | USB_TYPE_VENDOR |
+ USB_RECIP_INTERFACE, 0, 0, response_frame_time,
+ sizeof(*response_frame_time), ES2_TIMEOUT);
+
+ if (retval != sizeof(*response_frame_time)) {
+ dev_err(&udev->dev, "Cannot get last TimeSync event: %d\n",
+ retval);
+
+ if (retval >= 0)
+ retval = -EIO;
+
+ goto out;
+ }
+ *frame_time = le64_to_cpu(*response_frame_time);
+ retval = 0;
+out:
+ kfree(response_frame_time);
+ return retval;
+}
+
static struct gb_hd_driver es2_driver = {
- .hd_priv_size = sizeof(struct es2_ap_dev),
- .message_send = message_send,
- .message_cancel = message_cancel,
- .cport_enable = cport_enable,
- .latency_tag_enable = latency_tag_enable,
- .latency_tag_disable = latency_tag_disable,
- .output = output,
- .cport_features_enable = cport_features_enable,
- .cport_features_disable = cport_features_disable,
+ .hd_priv_size = sizeof(struct es2_ap_dev),
+ .message_send = message_send,
+ .message_cancel = message_cancel,
+ .cport_allocate = es2_cport_allocate,
+ .cport_release = es2_cport_release,
+ .cport_enable = cport_enable,
+ .latency_tag_enable = latency_tag_enable,
+ .latency_tag_disable = latency_tag_disable,
+ .output = output,
+ .cport_features_enable = cport_features_enable,
+ .cport_features_disable = cport_features_disable,
+ .timesync_enable = timesync_enable,
+ .timesync_disable = timesync_disable,
+ .timesync_authoritative = timesync_authoritative,
+ .timesync_get_last_event = timesync_get_last_event,
};
/* Common function to report consistent warnings based on URB status */
usb_put_dev(udev);
}
-static void ap_disconnect(struct usb_interface *interface)
-{
- struct es2_ap_dev *es2 = usb_get_intfdata(interface);
- int i;
-
- gb_hd_del(es2->hd);
-
- for (i = 0; i < NUM_BULKS; ++i)
- es2_cport_in_disable(es2, &es2->cport_in[i]);
-
- es2_destroy(es2);
-}
-
static void cport_in_callback(struct urb *urb)
{
struct gb_host_device *hd = urb->context;
if (status) {
if ((status == -EAGAIN) || (status == -EPROTO))
goto exit;
+
+ /* The urb is being unlinked */
+ if (status == -ENOENT || status == -ESHUTDOWN)
+ return;
+
dev_err(dev, "urb cport in error %d (dropped)\n", status);
return;
}
struct usb_endpoint_descriptor *endpoint;
int bulk_in = 0;
int bulk_out = 0;
- int retval = -ENOMEM;
+ int retval;
int i;
int num_cports;
- int cport_id;
udev = usb_get_dev(interface_to_usbdev(interface));
return PTR_ERR(hd);
}
- /*
- * CPorts 16 and 17 are reserved for CDSI0 and CDSI1, make sure they
- * won't be allocated dynamically.
- */
- do {
- cport_id = ida_simple_get(&hd->cport_id_map, 16, 18, GFP_KERNEL);
- } while (cport_id > 0);
-
es2 = hd_to_es2(hd);
es2->hd = hd;
es2->usb_intf = interface;
INIT_KFIFO(es2->apb_log_fifo);
usb_set_intfdata(interface, es2);
+ /*
+ * Reserve the CDSI0 and CDSI1 CPorts so they won't be allocated
+ * dynamically.
+ */
+ retval = gb_hd_cport_reserve(hd, ES2_CPORT_CDSI0);
+ if (retval)
+ goto error;
+ retval = gb_hd_cport_reserve(hd, ES2_CPORT_CDSI1);
+ if (retval)
+ goto error;
+
es2->cport_to_ep = kcalloc(hd->num_cports, sizeof(*es2->cport_to_ep),
GFP_KERNEL);
if (!es2->cport_to_ep) {
}
if (bulk_in != NUM_BULKS || bulk_out != NUM_BULKS) {
dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n");
+ retval = -ENODEV;
goto error;
}
u8 *buffer;
urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb)
+ if (!urb) {
+ retval = -ENOMEM;
goto error;
+ }
buffer = kmalloc(ES2_GBUF_MSG_SIZE_MAX, GFP_KERNEL);
- if (!buffer)
+ if (!buffer) {
+ retval = -ENOMEM;
goto error;
+ }
usb_fill_bulk_urb(urb, udev,
usb_rcvbulkpipe(udev,
struct urb *urb;
urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb)
+ if (!urb) {
+ retval = -ENOMEM;
goto error;
+ }
es2->cport_out_urb[i] = urb;
es2->cport_out_urb_busy[i] = false; /* just to be anal */
return retval;
}
+static void ap_disconnect(struct usb_interface *interface)
+{
+ struct es2_ap_dev *es2 = usb_get_intfdata(interface);
+ int i;
+
+ gb_hd_del(es2->hd);
+
+ for (i = 0; i < NUM_BULKS; ++i)
+ es2_cport_in_disable(es2, &es2->cport_in[i]);
+
+ es2_destroy(es2);
+}
+
static struct usb_driver es2_ap_driver = {
.name = "es2_ap_driver",
.probe = ap_probe,