#define ES2_GBUF_MSG_SIZE_MAX 2048
static const struct usb_device_id id_table[] = {
- { USB_DEVICE(0xffff, 0x0002) }, /* Made up number, delete once firmware is fixed to use real number */
{ USB_DEVICE(0x18d1, 0x1eaf) },
{ },
};
*/
#define NUM_CPORT_OUT_URB (8 * NUM_BULKS)
-/* vendor request APB1 log */
-#define REQUEST_LOG 0x02
-
-/* vendor request to map a cport to bulk in and bulk out endpoints */
-#define REQUEST_EP_MAPPING 0x03
-
-/* vendor request to get the number of cports available */
-#define REQUEST_CPORT_COUNT 0x04
-
-/* vendor request to reset a cport state */
-#define REQUEST_RESET_CPORT 0x05
-
-/* vendor request to time the latency of messages on a given cport */
-#define REQUEST_LATENCY_TAG_EN 0x06
-#define REQUEST_LATENCY_TAG_DIS 0x07
-
/*
* @endpoint: bulk in endpoint for CPort data
* @urb: array of urbs for the CPort in messages
retval = usb_control_msg(es2->usb_dev,
usb_sndctrlpipe(es2->usb_dev, 0),
- REQUEST_EP_MAPPING,
+ GB_APB_REQUEST_EP_MAPPING,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
0x00, 0x00,
(char *)cport_to_ep,
}
#endif
+static int output_sync(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd)
+{
+ struct usb_device *udev = es2->usb_dev;
+ u8 *data;
+ int retval;
+
+ data = kmalloc(size, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ memcpy(data, req, size);
+
+ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ cmd,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_INTERFACE,
+ 0, 0, data, size, ES2_TIMEOUT);
+ if (retval < 0)
+ dev_err(&udev->dev, "%s: return error %d\n", __func__, retval);
+ else
+ retval = 0;
+
+ kfree(data);
+ return retval;
+}
+
+static void ap_urb_complete(struct urb *urb)
+{
+ struct usb_ctrlrequest *dr = urb->context;
+
+ kfree(dr);
+ usb_free_urb(urb);
+}
+
+static int output_async(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd)
+{
+ struct usb_device *udev = es2->usb_dev;
+ struct urb *urb;
+ struct usb_ctrlrequest *dr;
+ u8 *buf;
+ int retval;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb)
+ return -ENOMEM;
+
+ dr = kmalloc(sizeof(*dr) + size, GFP_ATOMIC);
+ if (!dr) {
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ buf = (u8 *)dr + sizeof(*dr);
+ memcpy(buf, req, size);
+
+ dr->bRequest = cmd;
+ dr->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE;
+ dr->wValue = 0;
+ dr->wIndex = 0;
+ dr->wLength = cpu_to_le16(size);
+
+ usb_fill_control_urb(urb, udev, usb_sndctrlpipe(udev, 0),
+ (unsigned char *)dr, buf, size,
+ ap_urb_complete, dr);
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval) {
+ usb_free_urb(urb);
+ kfree(dr);
+ }
+ return retval;
+}
+
+static int output(struct gb_host_device *hd, void *req, u16 size, u8 cmd,
+ bool async)
+{
+ struct es2_ap_dev *es2 = hd_to_es2(hd);
+
+ if (async)
+ return output_async(es2, req, size, cmd);
+
+ return output_sync(es2, req, size, cmd);
+}
+
+static int es2_cport_in_enable(struct es2_ap_dev *es2,
+ struct es2_cport_in *cport_in)
+{
+ struct urb *urb;
+ int ret;
+ int i;
+
+ for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
+ urb = cport_in->urb[i];
+
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret) {
+ dev_err(&es2->usb_dev->dev,
+ "failed to submit in-urb: %d\n", ret);
+ goto err_kill_urbs;
+ }
+ }
+
+ return 0;
+
+err_kill_urbs:
+ for (--i; i >= 0; --i) {
+ urb = cport_in->urb[i];
+ usb_kill_urb(urb);
+ }
+
+ return ret;
+}
+
+static void es2_cport_in_disable(struct es2_ap_dev *es2,
+ struct es2_cport_in *cport_in)
+{
+ struct urb *urb;
+ int i;
+
+ for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
+ urb = cport_in->urb[i];
+ usb_kill_urb(urb);
+ }
+}
+
static struct urb *next_free_urb(struct es2_ap_dev *es2, gfp_t gfp_mask)
{
struct urb *urb = NULL;
* Crap, pool is empty, complain to the syslog and go allocate one
* dynamically as we have to succeed.
*/
- dev_err(&es2->usb_dev->dev,
+ dev_dbg(&es2->usb_dev->dev,
"No free CPort OUT urbs, having to dynamically allocate one!\n");
return usb_alloc_urb(0, gfp_mask);
}
* the target CPort id before filling it in.
*/
if (!cport_id_valid(hd, cport_id)) {
- dev_err(&udev->dev, "invalid destination cport 0x%02x\n",
- cport_id);
+ dev_err(&udev->dev, "invalid cport %u\n", cport_id);
return -EINVAL;
}
int retval;
retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- REQUEST_RESET_CPORT,
+ GB_APB_REQUEST_RESET_CPORT,
USB_DIR_OUT | USB_TYPE_VENDOR |
- USB_RECIP_INTERFACE, 0, cport_id,
+ USB_RECIP_INTERFACE, cport_id, 0,
NULL, 0, ES2_TIMEOUT);
if (retval < 0) {
- dev_err(&udev->dev, "failed to reset cport %hu: %d\n", cport_id,
+ dev_err(&udev->dev, "failed to reset cport %u: %d\n", cport_id,
retval);
return retval;
}
struct usb_device *udev = es2->usb_dev;
if (!cport_id_valid(hd, cport_id)) {
- dev_err(&udev->dev, "invalid destination cport 0x%02x\n",
- cport_id);
+ dev_err(&udev->dev, "invalid cport %u\n", cport_id);
return -EINVAL;
}
retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- REQUEST_LATENCY_TAG_EN,
+ GB_APB_REQUEST_LATENCY_TAG_EN,
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_INTERFACE, cport_id, 0, NULL,
0, ES2_TIMEOUT);
struct usb_device *udev = es2->usb_dev;
if (!cport_id_valid(hd, cport_id)) {
- dev_err(&udev->dev, "invalid destination cport 0x%02x\n",
- cport_id);
+ dev_err(&udev->dev, "invalid cport %u\n", cport_id);
return -EINVAL;
}
retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
- REQUEST_LATENCY_TAG_DIS,
+ GB_APB_REQUEST_LATENCY_TAG_DIS,
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_INTERFACE, cport_id, 0, NULL,
0, ES2_TIMEOUT);
return retval;
}
+static int fct_flow_enable(struct gb_host_device *hd, u16 cport_id)
+{
+ 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),
+ GB_APB_REQUEST_FCT_FLOW_EN,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_INTERFACE, cport_id, 0, NULL,
+ 0, ES2_TIMEOUT);
+ if (retval < 0)
+ dev_err(&udev->dev, "Cannot enable FCT flow for cport %u: %d\n",
+ cport_id, retval);
+ return retval;
+}
+
+static int fct_flow_disable(struct gb_host_device *hd, u16 cport_id)
+{
+ 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),
+ GB_APB_REQUEST_FCT_FLOW_DIS,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_INTERFACE, cport_id, 0, NULL,
+ 0, ES2_TIMEOUT);
+ if (retval < 0)
+ dev_err(&udev->dev,
+ "Cannot disable FCT flow for cport %u: %d\n",
+ cport_id, retval);
+ return retval;
+}
+
static struct gb_hd_driver es2_driver = {
.hd_priv_size = sizeof(struct es2_ap_dev),
.message_send = message_send,
.cport_enable = cport_enable,
.latency_tag_enable = latency_tag_enable,
.latency_tag_disable = latency_tag_disable,
+ .output = output,
+ .fct_flow_enable = fct_flow_enable,
+ .fct_flow_disable = fct_flow_disable,
};
/* Common function to report consistent warnings based on URB status */
return -EAGAIN;
}
-static void ap_disconnect(struct usb_interface *interface)
+static void es2_destroy(struct es2_ap_dev *es2)
{
- struct es2_ap_dev *es2;
struct usb_device *udev;
int bulk_in;
int i;
- es2 = usb_get_intfdata(interface);
- if (!es2)
- return;
-
+ debugfs_remove(es2->apb_log_enable_dentry);
usb_log_disable(es2);
/* Tear down everything! */
if (!urb)
break;
- usb_kill_urb(urb);
usb_free_urb(urb);
kfree(cport_in->buffer[i]);
cport_in->buffer[i] = NULL;
}
}
- usb_set_intfdata(interface, NULL);
- udev = es2->usb_dev;
- greybus_remove_hd(es2->hd);
kfree(es2->cport_to_ep);
+ udev = es2->usb_dev;
+ gb_hd_put(es2->hd);
+
usb_put_dev(udev);
}
+static void ap_disconnect(struct usb_interface *interface)
+{
+ struct es2_ap_dev *es2 = usb_get_intfdata(interface);
+ int i;
+
+ for (i = 0; i < NUM_BULKS; ++i)
+ es2_cport_in_disable(es2, &es2->cport_in[i]);
+
+ gb_hd_del(es2->hd);
+
+ es2_destroy(es2);
+}
+
static void cport_in_callback(struct urb *urb)
{
struct gb_host_device *hd = urb->context;
greybus_data_rcvd(hd, cport_id, urb->transfer_buffer,
urb->actual_length);
} else {
- dev_err(dev, "invalid cport id 0x%02x received\n", cport_id);
+ dev_err(dev, "invalid cport id %u received\n", cport_id);
}
exit:
/* put our urb back in the request pool */
do {
retval = usb_control_msg(es2->usb_dev,
usb_rcvctrlpipe(es2->usb_dev, 0),
- REQUEST_LOG,
+ GB_APB_REQUEST_LOG,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
0x00, 0x00,
buf,
int retval;
__le16 *cport_count;
- cport_count = kmalloc(sizeof(*cport_count), GFP_KERNEL);
+ cport_count = kzalloc(sizeof(*cport_count), GFP_KERNEL);
if (!cport_count)
return -ENOMEM;
retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
- REQUEST_CPORT_COUNT,
+ GB_APB_REQUEST_CPORT_COUNT,
USB_DIR_IN | USB_TYPE_VENDOR |
USB_RECIP_INTERFACE, 0, 0, cport_count,
sizeof(*cport_count), ES2_TIMEOUT);
- if (retval < 0) {
+ if (retval != sizeof(*cport_count)) {
dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n",
retval);
+
+ if (retval >= 0)
+ retval = -EIO;
+
goto out;
}
int retval = -ENOMEM;
int i;
int num_cports;
+ int cport_id;
udev = usb_get_dev(interface_to_usbdev(interface));
return num_cports;
}
- hd = greybus_create_hd(&es2_driver, &udev->dev, ES2_GBUF_MSG_SIZE_MAX,
- num_cports);
+ hd = gb_hd_create(&es2_driver, &udev->dev, ES2_GBUF_MSG_SIZE_MAX,
+ num_cports);
if (IS_ERR(hd)) {
usb_put_dev(udev);
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;
endpoint->bEndpointAddress;
} else {
dev_err(&udev->dev,
- "Unknown endpoint type found, address %x\n",
+ "Unknown endpoint type found, address 0x%02x\n",
endpoint->bEndpointAddress);
}
}
- if ((bulk_in == 0) ||
- (bulk_out == 0)) {
+ if (bulk_in != NUM_BULKS || bulk_out != NUM_BULKS) {
dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n");
goto error;
}
- /* Allocate buffers for our cport in messages and start them up */
+ /* Allocate buffers for our cport in messages */
for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) {
struct es2_cport_in *cport_in = &es2->cport_in[bulk_in];
cport_in_callback, hd);
cport_in->urb[i] = urb;
cport_in->buffer[i] = buffer;
- retval = usb_submit_urb(urb, GFP_KERNEL);
- if (retval)
- goto error;
}
}
(S_IWUSR | S_IRUGO),
gb_debugfs_get(), es2,
&apb_log_enable_fops);
+
+ retval = gb_hd_add(hd);
+ if (retval)
+ goto error;
+
+ for (i = 0; i < NUM_BULKS; ++i) {
+ retval = es2_cport_in_enable(es2, &es2->cport_in[i]);
+ if (retval)
+ goto err_disable_cport_in;
+ }
+
return 0;
+
+err_disable_cport_in:
+ for (--i; i >= 0; --i)
+ es2_cport_in_disable(es2, &es2->cport_in[i]);
+ gb_hd_del(hd);
error:
- ap_disconnect(interface);
+ es2_destroy(es2);
return retval;
}