From 3eac885de205ef57c3d434a8a4e2eadfd4cedde7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 29 Jul 2015 10:05:09 -0700 Subject: [PATCH] greybus: svc: revert svc changes to keep things working for a while. The firmware for the svc changes isn't quite ready, so revert the whole set of patches in one hunk to get things back to a working state for the other firmware developers. The svc patches will be added back in a separate branch. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/Makefile | 1 + drivers/staging/greybus/ap.c | 351 ++++++++++++++++++++ drivers/staging/greybus/connection.c | 7 +- drivers/staging/greybus/core.c | 27 +- drivers/staging/greybus/es1.c | 106 +++++- drivers/staging/greybus/es2.c | 106 +++++- drivers/staging/greybus/greybus.h | 7 +- drivers/staging/greybus/greybus_protocols.h | 14 +- drivers/staging/greybus/interface.c | 6 +- drivers/staging/greybus/svc.c | 53 +-- drivers/staging/greybus/svc_msg.h | 157 +++++++++ 11 files changed, 741 insertions(+), 94 deletions(-) create mode 100644 drivers/staging/greybus/ap.c create mode 100644 drivers/staging/greybus/svc_msg.h diff --git a/drivers/staging/greybus/Makefile b/drivers/staging/greybus/Makefile index d14bf4d21983..1467c5b3fcd8 100644 --- a/drivers/staging/greybus/Makefile +++ b/drivers/staging/greybus/Makefile @@ -1,5 +1,6 @@ greybus-y := core.o \ debugfs.o \ + ap.o \ manifest.o \ endo.o \ module.o \ diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c new file mode 100644 index 000000000000..fc238170ad12 --- /dev/null +++ b/drivers/staging/greybus/ap.c @@ -0,0 +1,351 @@ +/* + * Greybus "AP" message loop handling + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 only. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include "svc_msg.h" +#include "greybus_manifest.h" +#include "greybus.h" + +struct ap_msg { + u8 *data; + size_t size; + struct greybus_host_device *hd; + struct work_struct event; +}; + +static struct workqueue_struct *ap_workqueue; + +static struct svc_msg *svc_msg_alloc(enum svc_function_id id) +{ + struct svc_msg *svc_msg; + + svc_msg = kzalloc((sizeof *svc_msg), GFP_KERNEL); + if (!svc_msg) + return NULL; + + // FIXME - verify we are only sending function IDs we should be + svc_msg->header.function_id = id; + return svc_msg; +} + +static void svc_msg_free(struct svc_msg *svc_msg) +{ + kfree(svc_msg); +} + +static int svc_msg_send(struct svc_msg *svc_msg, struct greybus_host_device *hd) +{ + int retval; + + // FIXME - Do we need to do more than just pass it to the hd and then + // free it? + retval = hd->driver->submit_svc(svc_msg, hd); + + svc_msg_free(svc_msg); + return retval; +} + +static void svc_handshake(struct svc_function_handshake *handshake, + int payload_length, struct greybus_host_device *hd) +{ + struct svc_msg *svc_msg; + + if (payload_length != sizeof(*handshake)) { + dev_err(hd->parent, + "Illegal size of svc handshake message %d\n", + payload_length); + return; + } + + /* A new SVC communication channel, let's verify a supported version */ + if ((handshake->version_major != GREYBUS_VERSION_MAJOR) || + (handshake->version_minor != GREYBUS_VERSION_MINOR)) { + dev_warn(hd->parent, + "received invalid greybus version %u.%u\n", + handshake->version_major, handshake->version_minor); + return; + } + + /* Validate that the handshake came from the SVC */ + if (handshake->handshake_type != SVC_HANDSHAKE_SVC_HELLO) { + /* we don't know what to do with this, log it and return */ + dev_dbg(hd->parent, "received invalid handshake type %d\n", + handshake->handshake_type); + return; + } + + /* Send back a AP_HELLO message */ + svc_msg = svc_msg_alloc(SVC_FUNCTION_HANDSHAKE); + if (!svc_msg) + return; + + svc_msg->header.message_type = SVC_MSG_DATA; + svc_msg->header.payload_length = + cpu_to_le16(sizeof(*handshake)); + svc_msg->handshake.version_major = GREYBUS_VERSION_MAJOR; + svc_msg->handshake.version_minor = GREYBUS_VERSION_MINOR; + svc_msg->handshake.handshake_type = SVC_HANDSHAKE_AP_HELLO; + + (void)svc_msg_send(svc_msg, hd); +} + +static void svc_management(struct svc_function_unipro_management *management, + int payload_length, struct greybus_host_device *hd) +{ + struct gb_interface *intf; + int ret; + + if (payload_length != sizeof(*management)) { + dev_err(hd->parent, + "Illegal size of svc management message %d\n", + payload_length); + return; + } + + switch (management->management_packet_type) { + case SVC_MANAGEMENT_AP_ID: + hd->device_id = management->ap_id.device_id; + break; + case SVC_MANAGEMENT_LINK_UP: + intf = gb_interface_find(hd, management->link_up.interface_id); + if (!intf) { + dev_err(hd->parent, "Interface ID %d not found\n", + management->link_up.interface_id); + return; + } + ret = gb_interface_init(intf, management->link_up.device_id); + if (ret) { + dev_err(hd->parent, + "error %d initializing interface %hhu\n", + ret, management->link_up.interface_id); + return; + } + break; + default: + dev_err(hd->parent, "Unhandled UniPro management message\n"); + } +} + +static void svc_hotplug(struct svc_function_hotplug *hotplug, + int payload_length, struct greybus_host_device *hd) +{ + u8 interface_id = hotplug->interface_id; + + switch (hotplug->hotplug_event) { + case SVC_HOTPLUG_EVENT: + /* Add a new interface to the system */ + if (payload_length != sizeof(*hotplug)) { + dev_err(hd->parent, + "Illegal size of svc hotplug message %d\n", + payload_length); + return; + } + dev_dbg(hd->parent, "interface id %d added\n", interface_id); + gb_interface_create(hd, interface_id); + break; + + case SVC_HOTUNPLUG_EVENT: + /* Remove a interface from the system */ + if (payload_length != sizeof(*hotplug)) { + dev_err(hd->parent, + "Illegal size of svc hotunplug message %d\n", + payload_length); + return; + } + dev_dbg(hd->parent, "interface id %d removed\n", interface_id); + gb_interface_remove(hd, interface_id); + break; + + default: + dev_err(hd->parent, + "Received invalid hotplug message type %d\n", + hotplug->hotplug_event); + break; + } +} + +static void svc_power(struct svc_function_power *power, + int payload_length, struct greybus_host_device *hd) +{ + u8 interface_id = power->interface_id; + + /* + * The AP is only allowed to get a Battery Status message, not a Battery + * Status Request + */ + if (power->power_type != SVC_POWER_BATTERY_STATUS) { + dev_err(hd->parent, "Received invalid power type %d\n", + power->power_type); + return; + } + + /* + * As struct struct svc_function_power_battery_status_request is 0 bytes + * big, we can just check the union of the whole structure to validate + * the size of this message. + */ + if (payload_length != sizeof(*power)) { + dev_err(hd->parent, + "Illegal size of svc power message %d\n", + payload_length); + return; + } + + dev_dbg(hd->parent, "power status for interface id %d is %d\n", + interface_id, power->status.status); + + // FIXME - do something with the power information, like update our + // battery information... +} + +static void svc_epm(struct svc_function_epm *epm, + int payload_length, struct greybus_host_device *hd) +{ + /* What? An AP should not get this message */ + dev_err(hd->parent, "Got an EPM message???\n"); +} + +static void svc_suspend(struct svc_function_suspend *suspend, + int payload_length, struct greybus_host_device *hd) +{ + /* What? An AP should not get this message */ + dev_err(hd->parent, "Got an suspend message???\n"); +} + +static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg) +{ + struct svc_msg *svc_msg; + struct svc_msg_header *header; + struct greybus_host_device *hd = ap_msg->hd; + + svc_msg = (struct svc_msg *)ap_msg->data; + header = &svc_msg->header; + + /* Validate the message type */ + if (header->message_type != SVC_MSG_DATA) { + dev_err(hd->parent, "message type %d received?\n", + header->message_type); + return NULL; + } + + /* + * The validation of the size of the message buffer happens in each + * svc_* function, due to the different types of messages, keeping the + * logic for each message only in one place. + */ + + return svc_msg; +} + +static void ap_process_event(struct work_struct *work) +{ + struct svc_msg *svc_msg; + struct greybus_host_device *hd; + struct ap_msg *ap_msg; + int payload_length; + + ap_msg = container_of(work, struct ap_msg, event); + hd = ap_msg->hd; + + /* Turn the "raw" data into a real message */ + svc_msg = convert_ap_message(ap_msg); + if (!svc_msg) + return; + + payload_length = le16_to_cpu(svc_msg->header.payload_length); + + /* Look at the message to figure out what to do with it */ + switch (svc_msg->header.function_id) { + case SVC_FUNCTION_HANDSHAKE: + svc_handshake(&svc_msg->handshake, payload_length, hd); + break; + case SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT: + svc_management(&svc_msg->management, payload_length, hd); + break; + case SVC_FUNCTION_HOTPLUG: + svc_hotplug(&svc_msg->hotplug, payload_length, hd); + break; + case SVC_FUNCTION_POWER: + svc_power(&svc_msg->power, payload_length, hd); + break; + case SVC_FUNCTION_EPM: + svc_epm(&svc_msg->epm, payload_length, hd); + break; + case SVC_FUNCTION_SUSPEND: + svc_suspend(&svc_msg->suspend, payload_length, hd); + break; + default: + dev_err(hd->parent, "received invalid SVC function ID %d\n", + svc_msg->header.function_id); + } + + /* clean the message up */ + kfree(ap_msg->data); + kfree(ap_msg); +} + +int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int size) +{ + struct ap_msg *ap_msg; + + /* + * Totally naive copy the message into a new structure that we slowly + * create and add it to the list. Let's get this working, the odds of + * this being any "slow path" for AP messages is really low at this + * point in time, but you never know, so this comment is here to point + * out that maybe we should use a slab allocator, or even just not copy + * the data, but use it directly and force the urbs to be "new" each + * time. + */ + + /* Note - this can, and will, be called in interrupt context. */ + ap_msg = kmalloc(sizeof(*ap_msg), GFP_ATOMIC); + if (!ap_msg) + return -ENOMEM; + ap_msg->data = kmalloc(size, GFP_ATOMIC); + if (!ap_msg->data) { + kfree(ap_msg); + return -ENOMEM; + } + memcpy(ap_msg->data, data, size); + ap_msg->size = size; + ap_msg->hd = hd; + + INIT_WORK(&ap_msg->event, ap_process_event); + queue_work(ap_workqueue, &ap_msg->event); + + return 0; +} +EXPORT_SYMBOL_GPL(greybus_svc_in); + +int __init gb_ap_init(void) +{ + ap_workqueue = alloc_ordered_workqueue("greybus_ap", 0); + if (!ap_workqueue) + return -ENOMEM; + + return 0; +} + +void gb_ap_exit(void) +{ + destroy_workqueue(ap_workqueue); + ap_workqueue = NULL; +} + + diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c index 1a657f706b93..b32da8af68f4 100644 --- a/drivers/staging/greybus/connection.c +++ b/drivers/staging/greybus/connection.c @@ -251,12 +251,7 @@ gb_connection_create_range(struct greybus_host_device *hd, spin_unlock_irq(&gb_connections_lock); - if (hd_cport_id != GB_SVC_CPORT_ID) { - gb_svc_connection_create(hd->svc, - hd->endo->ap_intf_id, hd_cport_id, - bundle->intf->interface_id, cport_id); - } - + /* XXX Will have to establish connections to get version */ gb_connection_bind_protocol(connection); if (!connection->protocol) dev_warn(&connection->dev, diff --git a/drivers/staging/greybus/core.c b/drivers/staging/greybus/core.c index 9f105fb12ede..225fa4fb5268 100644 --- a/drivers/staging/greybus/core.c +++ b/drivers/staging/greybus/core.c @@ -176,7 +176,8 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver * Validate that the driver implements all of the callbacks * so that we don't have to every time we make them. */ - if ((!driver->message_send) || (!driver->message_cancel)) { + if ((!driver->message_send) || (!driver->message_cancel) || + (!driver->submit_svc)) { pr_err("Must implement all greybus_host_driver callbacks!\n"); return ERR_PTR(-EINVAL); } @@ -208,21 +209,6 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver ida_init(&hd->cport_id_map); hd->buffer_size_max = buffer_size_max; - /* - * Initialize AP's SVC protocol connection: - * - * This is required as part of early initialization of the host device - * as we need this connection in order to start any kind of message - * exchange between the AP and the SVC. SVC will start with a - * 'get-version' request followed by a 'svc-hello' message and at that - * time we will create a fully initialized svc-connection, as we need - * endo-id and AP's interface id for that. - */ - if (!gb_ap_svc_connection_create(hd)) { - kref_put_mutex(&hd->kref, free_hd, &hd_mutex); - return ERR_PTR(-ENOMEM); - } - return hd; } EXPORT_SYMBOL_GPL(greybus_create_hd); @@ -284,6 +270,12 @@ static int __init gb_init(void) goto error_bus; } + retval = gb_ap_init(); + if (retval) { + pr_err("gb_ap_init failed (%d)\n", retval); + goto error_ap; + } + retval = gb_operation_init(); if (retval) { pr_err("gb_operation_init failed (%d)\n", retval); @@ -317,6 +309,8 @@ error_control: error_endo: gb_operation_exit(); error_operation: + gb_ap_exit(); +error_ap: bus_unregister(&greybus_bus_type); error_bus: gb_debugfs_cleanup(); @@ -331,6 +325,7 @@ static void __exit gb_exit(void) gb_control_protocol_exit(); gb_endo_exit(); gb_operation_exit(); + gb_ap_exit(); bus_unregister(&greybus_bus_type); gb_debugfs_cleanup(); } diff --git a/drivers/staging/greybus/es1.c b/drivers/staging/greybus/es1.c index c1fab375bb0b..0cb7a3c7ef72 100644 --- a/drivers/staging/greybus/es1.c +++ b/drivers/staging/greybus/es1.c @@ -14,9 +14,11 @@ #include #include "greybus.h" +#include "svc_msg.h" #include "kernel_ver.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ +#define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) #define ES1_GBUF_MSG_SIZE_MAX 2048 static const struct usb_device_id id_table[] = { @@ -56,8 +58,11 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE); * @usb_intf: pointer to the USB interface we are bound to. * @hd: pointer to our greybus_host_device structure * @control_endpoint: endpoint to send data to SVC + * @svc_endpoint: endpoint for SVC data in * @cport_in_endpoint: bulk in endpoint for CPort data * @cport-out_endpoint: bulk out endpoint for CPort data + * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint + * @svc_urb: urb for SVC messages coming in on @svc_endpoint * @cport_in_urb: array of urbs for the CPort in messages * @cport_in_buffer: array of buffers for the @cport_in_urb urbs * @cport_out_urb: array of urbs for the CPort out messages @@ -73,9 +78,13 @@ struct es1_ap_dev { struct greybus_host_device *hd; __u8 control_endpoint; + __u8 svc_endpoint; __u8 cport_in_endpoint; __u8 cport_out_endpoint; + u8 *svc_buffer; + struct urb *svc_urb; + struct urb *cport_in_urb[NUM_CPORT_IN_URB]; u8 *cport_in_buffer[NUM_CPORT_IN_URB]; struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; @@ -94,6 +103,26 @@ static void usb_log_enable(struct es1_ap_dev *es1); static void usb_log_disable(struct es1_ap_dev *es1); #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ +static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) +{ + struct es1_ap_dev *es1 = hd_to_es1(hd); + int retval; + + /* SVC messages go down our control pipe */ + retval = usb_control_msg(es1->usb_dev, + usb_sndctrlpipe(es1->usb_dev, + es1->control_endpoint), + REQUEST_SVC, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x00, 0x00, + (char *)svc_msg, + sizeof(*svc_msg), + ES1_TIMEOUT); + if (retval != sizeof(*svc_msg)) + return retval; + + return 0; +} static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask) { @@ -274,6 +303,7 @@ static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .message_send = message_send, .message_cancel = message_cancel, + .submit_svc = submit_svc, }; /* Common function to report consistent warnings based on URB status */ @@ -337,6 +367,12 @@ static void ap_disconnect(struct usb_interface *interface) es1->cport_in_buffer[i] = NULL; } + usb_kill_urb(es1->svc_urb); + usb_free_urb(es1->svc_urb); + es1->svc_urb = NULL; + kfree(es1->svc_buffer); + es1->svc_buffer = NULL; + usb_set_intfdata(interface, NULL); udev = es1->usb_dev; greybus_remove_hd(es1->hd); @@ -344,6 +380,33 @@ static void ap_disconnect(struct usb_interface *interface) usb_put_dev(udev); } +/* Callback for when we get a SVC message */ +static void svc_in_callback(struct urb *urb) +{ + struct greybus_host_device *hd = urb->context; + struct device *dev = &urb->dev->dev; + int status = check_urb_status(urb); + int retval; + + if (status) { + if ((status == -EAGAIN) || (status == -EPROTO)) + goto exit; + dev_err(dev, "urb svc in error %d (dropped)\n", status); + return; + } + + /* We have a message, create a new message structure, add it to the + * list, and wake up our thread that will process the messages. + */ + greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length); + +exit: + /* resubmit the urb to get more messages */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(dev, "Can not submit urb for AP data: %d\n", retval); +} + static void cport_in_callback(struct urb *urb) { struct greybus_host_device *hd = urb->context; @@ -547,10 +610,14 @@ static int ap_probe(struct usb_interface *interface, struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; + bool int_in_found = false; bool bulk_in_found = false; bool bulk_out_found = false; int retval = -ENOMEM; int i; + u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC + u8 ap_intf_id = 0x01; // FIXME - get endo "ID" from the SVC + u8 svc_interval = 0; /* We need to fit a CPort ID in one byte of a message header */ BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX); @@ -579,7 +646,11 @@ static int ap_probe(struct usb_interface *interface, for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_bulk_in(endpoint)) { + if (usb_endpoint_is_int_in(endpoint)) { + es1->svc_endpoint = endpoint->bEndpointAddress; + svc_interval = endpoint->bInterval; + int_in_found = true; + } else if (usb_endpoint_is_bulk_in(endpoint)) { es1->cport_in_endpoint = endpoint->bEndpointAddress; bulk_in_found = true; } else if (usb_endpoint_is_bulk_out(endpoint)) { @@ -591,12 +662,27 @@ static int ap_probe(struct usb_interface *interface, endpoint->bEndpointAddress); } } - if ((bulk_in_found == false) || + if ((int_in_found == false) || + (bulk_in_found == false) || (bulk_out_found == false)) { dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); goto error; } + /* Create our buffer and URB to get SVC messages, and start it up */ + es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL); + if (!es1->svc_buffer) + goto error; + + es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!es1->svc_urb) + goto error; + + usb_fill_int_urb(es1->svc_urb, udev, + usb_rcvintpipe(udev, es1->svc_endpoint), + es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, + hd, svc_interval); + /* Allocate buffers for our cport in messages and start them up */ for (i = 0; i < NUM_CPORT_IN_URB; ++i) { struct urb *urb; @@ -632,6 +718,22 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } + /* + * XXX Soon this will be initiated later, with a combination + * XXX of a Control protocol probe operation and a + * XXX subsequent Control protocol connected operation for + * XXX the SVC connection. At that point we know we're + * XXX properly connected to an Endo. + */ + retval = greybus_endo_setup(hd, endo_id, ap_intf_id); + if (retval) + goto error; + + /* Start up our svc urb, which allows events to start flowing */ + retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); + if (retval) + goto error; + apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", (S_IWUSR | S_IRUGO), gb_debugfs_get(), es1, diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c index 558345cd80af..323721a2e2aa 100644 --- a/drivers/staging/greybus/es2.c +++ b/drivers/staging/greybus/es2.c @@ -14,9 +14,11 @@ #include #include "greybus.h" +#include "svc_msg.h" #include "kernel_ver.h" /* Memory sizes for the buffers sent to/from the ES1 controller */ +#define ES1_SVC_MSG_SIZE (sizeof(struct svc_msg) + SZ_64K) #define ES1_GBUF_MSG_SIZE_MAX 2048 static const struct usb_device_id id_table[] = { @@ -83,7 +85,10 @@ struct es1_cport_out { * @usb_intf: pointer to the USB interface we are bound to. * @hd: pointer to our greybus_host_device structure * @control_endpoint: endpoint to send data to SVC + * @svc_endpoint: endpoint for SVC data in + * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint + * @svc_urb: urb for SVC messages coming in on @svc_endpoint * @cport_in: endpoint, urbs and buffer for cport in messages * @cport_out: endpoint for for cport out messages * @cport_out_urb: array of urbs for the CPort out messages @@ -99,6 +104,10 @@ struct es1_ap_dev { struct greybus_host_device *hd; __u8 control_endpoint; + __u8 svc_endpoint; + + u8 *svc_buffer; + struct urb *svc_urb; struct es1_cport_in cport_in[NUM_BULKS]; struct es1_cport_out cport_out[NUM_BULKS]; @@ -133,6 +142,26 @@ static int cport_to_ep(struct es1_ap_dev *es1, u16 cport_id) } #define ES1_TIMEOUT 500 /* 500 ms for the SVC to do something */ +static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd) +{ + struct es1_ap_dev *es1 = hd_to_es1(hd); + int retval; + + /* SVC messages go down our control pipe */ + retval = usb_control_msg(es1->usb_dev, + usb_sndctrlpipe(es1->usb_dev, + es1->control_endpoint), + REQUEST_SVC, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x00, 0x00, + (char *)svc_msg, + sizeof(*svc_msg), + ES1_TIMEOUT); + if (retval != sizeof(*svc_msg)) + return retval; + + return 0; +} static int ep_in_use(struct es1_ap_dev *es1, int bulk_ep_set) { @@ -370,6 +399,7 @@ static struct greybus_host_driver es1_driver = { .hd_priv_size = sizeof(struct es1_ap_dev), .message_send = message_send, .message_cancel = message_cancel, + .submit_svc = submit_svc, }; /* Common function to report consistent warnings based on URB status */ @@ -437,6 +467,12 @@ static void ap_disconnect(struct usb_interface *interface) } } + usb_kill_urb(es1->svc_urb); + usb_free_urb(es1->svc_urb); + es1->svc_urb = NULL; + kfree(es1->svc_buffer); + es1->svc_buffer = NULL; + usb_set_intfdata(interface, NULL); udev = es1->usb_dev; greybus_remove_hd(es1->hd); @@ -444,6 +480,33 @@ static void ap_disconnect(struct usb_interface *interface) usb_put_dev(udev); } +/* Callback for when we get a SVC message */ +static void svc_in_callback(struct urb *urb) +{ + struct greybus_host_device *hd = urb->context; + struct device *dev = &urb->dev->dev; + int status = check_urb_status(urb); + int retval; + + if (status) { + if ((status == -EAGAIN) || (status == -EPROTO)) + goto exit; + dev_err(dev, "urb svc in error %d (dropped)\n", status); + return; + } + + /* We have a message, create a new message structure, add it to the + * list, and wake up our thread that will process the messages. + */ + greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length); + +exit: + /* resubmit the urb to get more messages */ + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + dev_err(dev, "Can not submit urb for AP data: %d\n", retval); +} + static void cport_in_callback(struct urb *urb) { struct greybus_host_device *hd = urb->context; @@ -647,10 +710,14 @@ static int ap_probe(struct usb_interface *interface, struct usb_device *udev; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; + bool int_in_found = false; int bulk_in = 0; int bulk_out = 0; int retval = -ENOMEM; int i; + u16 endo_id = 0x4755; // FIXME - get endo "ID" from the SVC + u8 ap_intf_id = 0x01; // FIXME - get endo "ID" from the SVC + u8 svc_interval = 0; /* We need to fit a CPort ID in one byte of a message header */ BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX); @@ -679,7 +746,11 @@ static int ap_probe(struct usb_interface *interface, for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { endpoint = &iface_desc->endpoint[i].desc; - if (usb_endpoint_is_bulk_in(endpoint)) { + if (usb_endpoint_is_int_in(endpoint)) { + es1->svc_endpoint = endpoint->bEndpointAddress; + svc_interval = endpoint->bInterval; + int_in_found = true; + } else if (usb_endpoint_is_bulk_in(endpoint)) { es1->cport_in[bulk_in++].endpoint = endpoint->bEndpointAddress; } else if (usb_endpoint_is_bulk_out(endpoint)) { @@ -691,12 +762,27 @@ static int ap_probe(struct usb_interface *interface, endpoint->bEndpointAddress); } } - if ((bulk_in == 0) || + if ((int_in_found == false) || + (bulk_in == 0) || (bulk_out == 0)) { dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); goto error; } + /* Create our buffer and URB to get SVC messages, and start it up */ + es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL); + if (!es1->svc_buffer) + goto error; + + es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!es1->svc_urb) + goto error; + + usb_fill_int_urb(es1->svc_urb, udev, + usb_rcvintpipe(udev, es1->svc_endpoint), + es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback, + hd, svc_interval); + /* Allocate buffers for our cport in messages and start them up */ for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) { struct es1_cport_in *cport_in = &es1->cport_in[bulk_in]; @@ -736,6 +822,22 @@ static int ap_probe(struct usb_interface *interface, es1->cport_out_urb_busy[i] = false; /* just to be anal */ } + /* + * XXX Soon this will be initiated later, with a combination + * XXX of a Control protocol probe operation and a + * XXX subsequent Control protocol connected operation for + * XXX the SVC connection. At that point we know we're + * XXX properly connected to an Endo. + */ + retval = greybus_endo_setup(hd, endo_id, ap_intf_id); + if (retval) + goto error; + + /* Start up our svc urb, which allows events to start flowing */ + retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL); + if (retval) + goto error; + apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable", (S_IWUSR | S_IRUGO), gb_debugfs_get(), es1, diff --git a/drivers/staging/greybus/greybus.h b/drivers/staging/greybus/greybus.h index cdade1e9b4c7..2214f447df2b 100644 --- a/drivers/staging/greybus/greybus.h +++ b/drivers/staging/greybus/greybus.h @@ -73,6 +73,7 @@ */ struct greybus_host_device; +struct svc_msg; /* Greybus "Host driver" structure, needed by a host controller driver to be * able to handle both SVC control as well as "real" greybus messages @@ -83,6 +84,8 @@ struct greybus_host_driver { int (*message_send)(struct greybus_host_device *hd, u16 dest_cport_id, struct gb_message *message, gfp_t gfp_mask); void (*message_cancel)(struct gb_message *message); + int (*submit_svc)(struct svc_msg *svc_msg, + struct greybus_host_device *hd); }; struct greybus_host_device { @@ -100,7 +103,6 @@ struct greybus_host_device { struct gb_endo *endo; struct gb_connection *initial_svc_connection; - struct gb_svc *svc; /* Private data for the host driver */ unsigned long hd_priv[0] __aligned(sizeof(s64)); @@ -153,6 +155,9 @@ void greybus_deregister_driver(struct greybus_driver *driver); int greybus_disabled(void); +int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int length); +int gb_ap_init(void); +void gb_ap_exit(void); void gb_debugfs_init(void); void gb_debugfs_cleanup(void); struct dentry *gb_debugfs_get(void); diff --git a/drivers/staging/greybus/greybus_protocols.h b/drivers/staging/greybus/greybus_protocols.h index e2d38dfe06b6..61fe9dce6ec0 100644 --- a/drivers/staging/greybus/greybus_protocols.h +++ b/drivers/staging/greybus/greybus_protocols.h @@ -64,9 +64,9 @@ * CONTROL and SVC protocols for communication between AP and SVC. */ #define GB_SVC_BUNDLE_ID 0 -#define GB_SVC_CPORT_ID 0 +#define GB_SVC_CPORT_ID 2 #define GB_CONTROL_BUNDLE_ID 0 -#define GB_CONTROL_CPORT_ID 0 +#define GB_CONTROL_CPORT_ID 2 /* Control Protocol */ @@ -563,7 +563,6 @@ struct gb_spi_transfer_response { #define GB_SVC_TYPE_INTF_RESET 0x06 #define GB_SVC_TYPE_CONN_CREATE 0x07 #define GB_SVC_TYPE_CONN_DESTROY 0x08 -#define GB_SVC_TYPE_ROUTE_CREATE 0x0b /* SVC version request/response have same payload as gb_protocol_version_response */ @@ -606,8 +605,6 @@ struct gb_svc_conn_create_request { __u16 cport1_id; __u8 intf2_id; __u16 cport2_id; - __u8 tc; - __u8 flags; }; /* connection create response has no payload */ @@ -619,13 +616,6 @@ struct gb_svc_conn_destroy_request { }; /* connection destroy response has no payload */ -struct gb_svc_route_create_request { - __u8 intf1_id; - __u8 dev1_id; - __u8 intf2_id; - __u8 dev2_id; -}; - /* UART */ /* Version of the Greybus UART protocol we support */ diff --git a/drivers/staging/greybus/interface.c b/drivers/staging/greybus/interface.c index 4a26bf6f714c..f1e2956b25a7 100644 --- a/drivers/staging/greybus/interface.c +++ b/drivers/staging/greybus/interface.c @@ -183,7 +183,7 @@ put_module: /* * Tear down a previously set up module. */ -static void interface_destroy(struct gb_interface *intf) +void gb_interface_destroy(struct gb_interface *intf) { struct gb_module *module; struct gb_bundle *bundle; @@ -279,7 +279,7 @@ void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id) struct gb_interface *intf = gb_interface_find(hd, interface_id); if (intf) - interface_destroy(intf); + gb_interface_destroy(intf); else dev_err(hd->parent, "interface id %d not found\n", interface_id); @@ -290,5 +290,5 @@ void gb_interfaces_remove(struct greybus_host_device *hd) struct gb_interface *intf, *temp; list_for_each_entry_safe(intf, temp, &hd->interfaces, links) - interface_destroy(intf); + gb_interface_destroy(intf); } diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 784b7709b432..5f2b2b43b939 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -9,10 +9,6 @@ #include "greybus.h" -#define CPORT_FLAGS_E2EFC (1) -#define CPORT_FLAGS_CSD_N (2) -#define CPORT_FLAGS_CSV_N (4) - struct gb_svc { struct gb_connection *connection; u8 version_major; @@ -40,6 +36,7 @@ gb_ap_svc_connection_create(struct greybus_host_device *hd) return connection; } +EXPORT_SYMBOL_GPL(gb_ap_svc_connection_create); /* * We know endo-type and AP's interface id now, lets create a proper svc @@ -101,12 +98,6 @@ static int connection_create_operation(struct gb_svc *svc, request.cport1_id = cport1_id; request.intf2_id = intf2_id; request.cport2_id = cport2_id; - /* - * XXX: fix connections paramaters to TC0 and all CPort flags - * for now. - */ - request.tc = 0; - request.flags = CPORT_FLAGS_CSV_N | CPORT_FLAGS_E2EFC; return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE, &request, sizeof(request), NULL, 0); @@ -127,20 +118,6 @@ static int connection_destroy_operation(struct gb_svc *svc, &request, sizeof(request), NULL, 0); } -static int route_create_operation(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, - u8 intf2_id, u8 dev2_id) -{ - struct gb_svc_route_create_request request; - - request.intf1_id = intf1_id; - request.dev1_id = dev1_id; - request.intf2_id = intf2_id; - request.dev2_id = dev2_id; - - return gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_CREATE, - &request, sizeof(request), NULL, 0); -} - int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) { return intf_device_id_operation(svc, intf_id, device_id); @@ -171,13 +148,6 @@ int gb_svc_connection_destroy(struct gb_svc *svc, } EXPORT_SYMBOL_GPL(gb_svc_connection_destroy); -int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, - u8 intf2_id, u8 dev2_id) { - return route_create_operation(svc, intf1_id, dev1_id, - intf2_id, dev2_id); -} -EXPORT_SYMBOL_GPL(gb_svc_route_create); - static int gb_svc_version_request(struct gb_operation *op) { struct gb_connection *connection = op->connection; @@ -318,25 +288,6 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) goto ida_put; } - /* - * Create a two-way route between the AP and the new interface - */ - ret = route_create_operation(svc, hd->endo->ap_intf_id, - GB_DEVICE_ID_AP, intf_id, device_id); - if (ret) { - dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n", - __func__, intf_id, device_id, ret); - goto ida_put; - } - - ret = route_create_operation(svc, intf_id, device_id, - hd->endo->ap_intf_id, GB_DEVICE_ID_AP); - if (ret) { - dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n", - __func__, intf_id, device_id, ret); - goto ida_put; - } - ret = gb_interface_init(intf, device_id); if (ret) { dev_err(dev, "%s: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n", @@ -439,7 +390,6 @@ static int gb_svc_connection_init(struct gb_connection *connection) if (!svc) return -ENOMEM; - connection->hd->svc = svc; svc->connection = connection; connection->private = svc; @@ -455,7 +405,6 @@ static void gb_svc_connection_exit(struct gb_connection *connection) { struct gb_svc *svc = connection->private; - connection->hd->svc = NULL; connection->private = NULL; kfree(svc); } diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h new file mode 100644 index 000000000000..3c628c5d6e38 --- /dev/null +++ b/drivers/staging/greybus/svc_msg.h @@ -0,0 +1,157 @@ +/* + * Greybus AP <-> SVC message structure format. + * + * See "Greybus Application Protocol" document (version 0.1) for + * details on these values and structures. + * + * Copyright 2014 Google Inc. + * Copyright 2014 Linaro Ltd. + * + * Released under the GPLv2 and BSD license. + */ + +#ifndef __SVC_MSG_H +#define __SVC_MSG_H + +enum svc_function_id { + SVC_FUNCTION_HANDSHAKE = 0x00, + SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT = 0x01, + SVC_FUNCTION_HOTPLUG = 0x02, + SVC_FUNCTION_POWER = 0x03, + SVC_FUNCTION_EPM = 0x04, + SVC_FUNCTION_SUSPEND = 0x05, +}; + +enum svc_msg_type { + SVC_MSG_DATA = 0x00, + SVC_MSG_ERROR = 0xff, +}; + +struct svc_msg_header { + __u8 function_id; /* enum svc_function_id */ + __u8 message_type; + __le16 payload_length; +} __packed; + +enum svc_function_handshake_type { + SVC_HANDSHAKE_SVC_HELLO = 0x00, + SVC_HANDSHAKE_AP_HELLO = 0x01, + SVC_HANDSHAKE_MODULE_HELLO = 0x02, +}; + +struct svc_function_handshake { + __u8 version_major; + __u8 version_minor; + __u8 handshake_type; /* enum svc_function_handshake_type */ +} __packed; + +struct svc_function_unipro_set_route { + __u8 device_id; +} __packed; + +struct svc_function_unipro_link_up { + __u8 interface_id; /* Interface id within the Endo */ + __u8 device_id; +} __packed; + +struct svc_function_ap_id { + __u8 interface_id; + __u8 device_id; +} __packed; + +enum svc_function_management_event { + SVC_MANAGEMENT_AP_ID = 0x00, + SVC_MANAGEMENT_LINK_UP = 0x01, + SVC_MANAGEMENT_SET_ROUTE = 0x02, +}; + +struct svc_function_unipro_management { + __u8 management_packet_type; /* enum svc_function_management_event */ + union { + struct svc_function_ap_id ap_id; + struct svc_function_unipro_link_up link_up; + struct svc_function_unipro_set_route set_route; + }; +} __packed; + +enum svc_function_hotplug_event { + SVC_HOTPLUG_EVENT = 0x00, + SVC_HOTUNPLUG_EVENT = 0x01, +}; + +struct svc_function_hotplug { + __u8 hotplug_event; /* enum svc_function_hotplug_event */ + __u8 interface_id; /* Interface id within the Endo */ +} __packed; + +enum svc_function_power_type { + SVC_POWER_BATTERY_STATUS = 0x00, + SVC_POWER_BATTERY_STATUS_REQUEST = 0x01, +}; + +enum svc_function_battery_status { + SVC_BATTERY_UNKNOWN = 0x00, + SVC_BATTERY_CHARGING = 0x01, + SVC_BATTERY_DISCHARGING = 0x02, + SVC_BATTERY_NOT_CHARGING = 0x03, + SVC_BATTERY_FULL = 0x04, +}; + +struct svc_function_power_battery_status { + __le16 charge_full; + __le16 charge_now; + __u8 status; /* enum svc_function_battery_status */ +} __packed; + +struct svc_function_power_battery_status_request { +} __packed; + +/* XXX + * Each interface carries power, so it's possible these things + * are associated with each UniPro device and not just the module. + * For now it's safe to assume it's per-module. + */ +struct svc_function_power { + __u8 power_type; /* enum svc_function_power_type */ + __u8 interface_id; + union { + struct svc_function_power_battery_status status; + struct svc_function_power_battery_status_request request; + }; +} __packed; + +enum svc_function_epm_command_type { + SVC_EPM_ENABLE = 0x00, + SVC_EPM_DISABLE = 0x01, +}; + +/* EPM's are associated with the module */ +struct svc_function_epm { + __u8 epm_command_type; /* enum svc_function_epm_command_type */ + __u8 module_id; +} __packed; + +enum svc_function_suspend_command_type { + SVC_SUSPEND_FIXME_1 = 0x00, // FIXME + SVC_SUSPEND_FIXME_2 = 0x01, +}; + +/* We'll want independent control for multi-interface modules */ +struct svc_function_suspend { + __u8 suspend_command_type; /* enum function_suspend_command_type */ + __u8 device_id; +} __packed; + +struct svc_msg { + struct svc_msg_header header; + union { + struct svc_function_handshake handshake; + struct svc_function_unipro_management management; + struct svc_function_hotplug hotplug; + struct svc_function_power power; + struct svc_function_epm epm; + struct svc_function_suspend suspend; + }; +} __packed; + +#endif /* __SVC_MSG_H */ -- 2.39.5