greybus: svc: Add helpers to create AP<->SVC connection

SVC connection is required before the AP knows its position on the endo
and type of endo. To enable message processing between the AP and SVC at
this time, we need a partially initialized connection which can handle
these messages.

Once the AP receives more information from the SVC, it can discard this
partially initialized connection and create a proper one, tied to a
bundle and interface.

Destroying the partially initialized connection is a bit tricky, as it
is required to send a response to svc-hello. That part will be properly
fixed separately.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Viresh Kumar 2015-07-21 17:44:18 +05:30 committed by Greg Kroah-Hartman
parent 7a24a3f6ce
commit d3d4484067
5 changed files with 90 additions and 5 deletions

View File

@ -102,6 +102,7 @@ struct greybus_host_device {
size_t buffer_size_max;
struct gb_endo *endo;
struct gb_connection *initial_svc_connection;
/* Private data for the host driver */
unsigned long hd_priv[0] __aligned(sizeof(s64));

View File

@ -183,7 +183,7 @@ put_module:
/*
* Tear down a previously set up module.
*/
static void gb_interface_destroy(struct gb_interface *intf)
void gb_interface_destroy(struct gb_interface *intf)
{
struct gb_module *module;
struct gb_bundle *bundle;

View File

@ -53,6 +53,7 @@ struct gb_interface *gb_interface_find(struct greybus_host_device *hd,
struct gb_interface *gb_interface_create(struct greybus_host_device *hd,
u8 interface_id);
int gb_interface_init(struct gb_interface *intf, u8 device_id);
void gb_interface_destroy(struct gb_interface *intf);
void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id);
void gb_interfaces_remove(struct greybus_host_device *hd);

View File

@ -12,6 +12,66 @@
/* Define get_version() routine */
define_get_version(gb_svc, SVC);
/*
* AP's SVC cport is required early to get messages from the SVC. This happens
* even before the Endo is created and hence any modules or interfaces.
*
* This is a temporary connection, used only at initial bootup.
*/
struct gb_connection *
gb_ap_svc_connection_create(struct greybus_host_device *hd)
{
struct gb_connection *connection;
connection = gb_connection_create_range(hd, NULL, hd->parent,
GB_SVC_CPORT_ID,
GREYBUS_PROTOCOL_SVC,
GB_SVC_CPORT_ID,
GB_SVC_CPORT_ID + 1);
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
* connection (and its interface/bundle) now and get rid of the initial
* 'partially' initialized one svc connection.
*/
static struct gb_interface *
gb_ap_interface_create(struct greybus_host_device *hd,
struct gb_connection *connection, u8 interface_id)
{
struct gb_interface *intf;
struct device *dev = &hd->endo->dev;
int ret;
intf = gb_interface_create(hd, interface_id);
if (!intf) {
dev_err(dev, "%s: Failed to create interface with id %hhu\n",
__func__, interface_id);
return NULL;
}
intf->device_id = GB_DEVICE_ID_AP;
/*
* XXX: Disable the initial svc connection here, but don't destroy it
* yet. We do need to send a response of 'svc-hello message' on that.
*/
/* Establish new control CPort connection */
ret = gb_create_bundle_connection(intf, GREYBUS_CLASS_SVC);
if (ret) {
dev_err(&intf->dev, "%s: Failed to create svc connection (%d %d)\n",
__func__, interface_id, ret);
gb_interface_destroy(intf);
intf = NULL;
}
return intf;
}
static int intf_device_id_operation(struct gb_svc *svc,
u8 intf_id, u8 device_id)
{
@ -207,6 +267,22 @@ static int gb_svc_connection_init(struct gb_connection *connection)
svc->connection = connection;
connection->private = svc;
/*
* SVC connection is created twice:
* - before the interface-id of the AP and the endo type is known.
* - after receiving endo type and interface-id of the AP from the SVC.
*
* We should do light-weight initialization for the first case.
*/
if (!connection->bundle) {
WARN_ON(connection->hd->initial_svc_connection);
connection->hd->initial_svc_connection = connection;
return 0;
}
ida_init(&greybus_svc_device_id_map);
ret = gb_svc_device_setup(svc);
if (ret)
kfree(svc);
@ -221,11 +297,15 @@ static void gb_svc_connection_exit(struct gb_connection *connection)
{
struct gb_svc *svc = connection->private;
if (WARN_ON(connection->bundle->intf->svc != svc))
return;
connection->bundle->intf->svc = NULL;
if (connection->hd->initial_svc_connection == connection) {
connection->hd->initial_svc_connection = NULL;
} else {
if (WARN_ON(connection->bundle->intf->svc != svc))
return;
connection->bundle->intf->svc = NULL;
}
connection->private = NULL;
kfree(svc);
}

View File

@ -26,4 +26,7 @@ int gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id,
int gb_svc_protocol_init(void);
void gb_svc_protocol_exit(void);
struct gb_connection *
gb_ap_svc_connection_create(struct greybus_host_device *hd);
#endif /* __SVC_H */