/* * Number of CPort IN urbs in flight at any point in time. * Adjust if we are having stalls in the USB buffer due to not enough urbs in * flight.
*/ #define NUM_CPORT_IN_URB 4
/* Number of CPort OUT urbs in flight at any point in time. * Adjust if we get messages saying we are out of urbs in the system log.
*/ #define NUM_CPORT_OUT_URB 8
/* * Number of ARPC in urbs in flight at any point in time.
*/ #define NUM_ARPC_IN_URB 2
/* * @endpoint: bulk in endpoint for CPort data * @urb: array of urbs for the CPort in messages * @buffer: array of buffers for the @cport_in_urb urbs
*/ struct es2_cport_in {
__u8 endpoint; struct urb *urb[NUM_CPORT_IN_URB];
u8 *buffer[NUM_CPORT_IN_URB];
};
/** * struct es2_ap_dev - ES2 USB Bridge to AP structure * @usb_dev: pointer to the USB device we are. * @usb_intf: pointer to the USB interface we are bound to. * @hd: pointer to our gb_host_device structure * * @cport_in: endpoint, urbs and buffer for cport in messages * @cport_out_endpoint: endpoint for cport out messages * @cport_out_urb: array of urbs for the CPort out messages * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or * not. * @cport_out_urb_cancelled: array of flags indicating whether the * corresponding @cport_out_urb is being cancelled * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" * @cdsi1_in_use: true if cport CDSI1 is in use * @apb_log_task: task pointer for logging thread * @apb_log_dentry: file system entry for the log file interface * @apb_log_enable_dentry: file system entry for enabling logging * @apb_log_fifo: kernel FIFO to carry logged data * @arpc_urb: array of urbs for the ARPC in messages * @arpc_buffer: array of buffers for the @arpc_urb urbs * @arpc_endpoint_in: bulk in endpoint for APBridgeA RPC * @arpc_id_cycle: gives an unique id to ARPC * @arpc_lock: locks ARPC list * @arpcs: list of in progress ARPCs
*/ struct es2_ap_dev { struct usb_device *usb_dev; struct usb_interface *usb_intf; struct gb_host_device *hd;
/* Look in our pool of allocated urbs first, as that's the "fastest" */ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { if (!es2->cport_out_urb_busy[i] &&
!es2->cport_out_urb_cancelled[i]) {
es2->cport_out_urb_busy[i] = true;
urb = es2->cport_out_urb[i]; break;
}
}
spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); if (urb) return urb;
/* * Crap, pool is empty, complain to the syslog and go allocate one * dynamically as we have to succeed.
*/
dev_dbg(&es2->usb_dev->dev, "No free CPort OUT urbs, having to dynamically allocate one!\n"); return usb_alloc_urb(0, gfp_mask);
}
staticvoid free_urb(struct es2_ap_dev *es2, struct urb *urb)
{ unsignedlong flags; int i; /* * See if this was an urb in our pool, if so mark it "free", otherwise * we need to free it ourselves.
*/
spin_lock_irqsave(&es2->cport_out_urb_lock, flags); for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { if (urb == es2->cport_out_urb[i]) {
es2->cport_out_urb_busy[i] = false;
urb = NULL; break;
}
}
spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags);
/* If urb is not NULL, then we need to free this urb */
usb_free_urb(urb);
}
/* * We (ab)use the operation-message header pad bytes to transfer the * cport id in order to minimise overhead.
*/ staticvoid
gb_message_cport_pack(struct gb_operation_msg_hdr *header, u16 cport_id)
{
header->pad[0] = cport_id;
}
/* Clear the pad bytes used for the CPort id */ staticvoid gb_message_cport_clear(struct gb_operation_msg_hdr *header)
{
header->pad[0] = 0;
}
/* Extract the CPort id packed into the header, and clear it */ static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header)
{
u16 cport_id = header->pad[0];
gb_message_cport_clear(header);
return cport_id;
}
/* * Returns zero if the message was successfully queued, or a negative errno * otherwise.
*/ staticint message_send(struct gb_host_device *hd, u16 cport_id, struct gb_message *message, gfp_t gfp_mask)
{ struct es2_ap_dev *es2 = hd_to_es2(hd); struct usb_device *udev = es2->usb_dev;
size_t buffer_size; int retval; struct urb *urb; unsignedlong flags;
/* * The data actually transferred will include an indication * of where the data should be sent. Do one last check of * the target CPort id before filling it in.
*/ if (!cport_id_valid(hd, cport_id)) {
dev_err(&udev->dev, "invalid cport %u\n", cport_id); return -EINVAL;
}
/* Find a free urb */
urb = next_free_urb(es2, gfp_mask); if (!urb) return -ENOMEM;
/* * Can not be called in atomic context.
*/ staticvoid message_cancel(struct gb_message *message)
{ struct gb_host_device *hd = message->operation->connection->hd; struct es2_ap_dev *es2 = hd_to_es2(hd); struct urb *urb; int i;
/* Prevent dynamically allocated urb from being deallocated. */
usb_get_urb(urb);
/* Prevent pre-allocated urb from being reused. */ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { if (urb == es2->cport_out_urb[i]) {
es2->cport_out_urb_cancelled[i] = true; break;
}
}
spin_unlock_irq(&es2->cport_out_urb_lock);
usb_kill_urb(urb);
if (i < NUM_CPORT_OUT_URB) {
spin_lock_irq(&es2->cport_out_urb_lock);
es2->cport_out_urb_cancelled[i] = false;
spin_unlock_irq(&es2->cport_out_urb_lock);
}
usb_free_urb(urb);
}
staticint es2_cport_allocate(struct gb_host_device *hd, int cport_id, unsignedlong 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;
}
req.cport_id = cpu_to_le16(cport_id);
ret = arpc_sync(es2, ARPC_TYPE_CPORT_CONNECTED, &req, sizeof(req),
NULL, ES2_ARPC_CPORT_TIMEOUT); if (ret) {
dev_err(dev, "failed to set connected state for cport %u: %d\n",
cport_id, ret); return ret;
}
/* Common function to report consistent warnings based on URB status */ staticint check_urb_status(struct urb *urb)
{ struct device *dev = &urb->dev->dev; int status = urb->status;
switch (status) { case 0: return 0;
case -EOVERFLOW:
dev_err(dev, "%s: overflow actual length is %d\n",
__func__, urb->actual_length);
fallthrough; case -ECONNRESET: case -ENOENT: case -ESHUTDOWN: case -EILSEQ: case -EPROTO: /* device is gone, stop sending */ return status;
}
dev_err(dev, "%s: unknown status %d\n", __func__, status);
/* Tear down everything! */ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
urb = es2->cport_out_urb[i];
usb_kill_urb(urb);
usb_free_urb(urb);
es2->cport_out_urb[i] = NULL;
es2->cport_out_urb_busy[i] = false; /* just to be anal */
}
for (i = 0; i < NUM_ARPC_IN_URB; ++i) {
usb_free_urb(es2->arpc_urb[i]);
kfree(es2->arpc_buffer[i]);
es2->arpc_buffer[i] = NULL;
}
for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
usb_free_urb(es2->cport_in.urb[i]);
kfree(es2->cport_in.buffer[i]);
es2->cport_in.buffer[i] = NULL;
}
/* Extract the CPort id, which is packed in the message header */
header = urb->transfer_buffer;
cport_id = gb_message_cport_unpack(header);
if (cport_id_valid(hd, cport_id)) {
greybus_data_rcvd(hd, cport_id, urb->transfer_buffer,
urb->actual_length);
} else {
dev_err(dev, "invalid cport id %u received\n", cport_id);
} exit: /* put our urb back in the request pool */
retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval)
dev_err(dev, "failed to resubmit in-urb: %d\n", retval);
}
exit: /* put our urb back in the request pool */
retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval)
dev_err(dev, "failed to resubmit arpc in-urb: %d\n", retval);
}
staticvoid usb_log_enable(struct es2_ap_dev *es2)
{ if (!IS_ERR_OR_NULL(es2->apb_log_task)) return;
/* get log from APB1 */
es2->apb_log_task = kthread_run(apb_log_poll, es2, "apb_log"); if (IS_ERR(es2->apb_log_task)) return; /* XXX We will need to rename this per APB */
es2->apb_log_dentry = debugfs_create_file("apb_log", 0444,
gb_debugfs_get(), es2,
&apb_log_fops);
}
staticvoid usb_log_disable(struct es2_ap_dev *es2)
{ if (IS_ERR_OR_NULL(es2->apb_log_task)) return;
/* We need to fit a CPort ID in one byte of a message header */ if (retval > U8_MAX) {
retval = U8_MAX;
dev_warn(&udev->dev, "Limiting number of CPorts to U8_MAX\n");
}
out:
kfree(cport_count); return retval;
}
/* * The ES2 USB Bridge device has 15 endpoints * 1 Control - usual USB stuff + AP -> APBridgeA messages * 7 Bulk IN - CPort data in * 7 Bulk OUT - CPort data out
*/ staticint ap_probe(struct usb_interface *interface, conststruct usb_device_id *id)
{ struct es2_ap_dev *es2; struct gb_host_device *hd; struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint;
__u8 ep_addr; int retval; int i; int num_cports; bool bulk_out_found = false; bool bulk_in_found = false; bool arpc_in_found = false;
/* * 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;
/* find all bulk endpoints */
iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
ep_addr = endpoint->bEndpointAddress;
if (usb_endpoint_is_bulk_in(endpoint)) { if (!bulk_in_found) {
es2->cport_in.endpoint = ep_addr;
bulk_in_found = true;
} elseif (!arpc_in_found) {
es2->arpc_endpoint_in = ep_addr;
arpc_in_found = true;
} else {
dev_warn(&udev->dev, "Unused bulk IN endpoint found: 0x%02x\n",
ep_addr);
} continue;
} if (usb_endpoint_is_bulk_out(endpoint)) { if (!bulk_out_found) {
es2->cport_out_endpoint = ep_addr;
bulk_out_found = true;
} else {
dev_warn(&udev->dev, "Unused bulk OUT endpoint found: 0x%02x\n",
ep_addr);
} continue;
}
dev_warn(&udev->dev, "Unknown endpoint type found, address 0x%02x\n",
ep_addr);
} if (!bulk_in_found || !arpc_in_found || !bulk_out_found) {
dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n");
retval = -ENODEV; goto error;
}
/* Allocate buffers for our cport in messages */ for (i = 0; i < NUM_CPORT_IN_URB; ++i) { struct urb *urb;
u8 *buffer;
es2->cport_out_urb[i] = urb;
es2->cport_out_urb_busy[i] = false; /* just to be anal */
}
/* XXX We will need to rename this per APB */
es2->apb_log_enable_dentry = debugfs_create_file("apb_log_enable",
0644,
gb_debugfs_get(), es2,
&apb_log_enable_fops);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.