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:
parent
7a24a3f6ce
commit
d3d4484067
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue