greybus: core: add module abstraction
Add Greybus module abstraction that will be used to implement controlled module removal (eject) and represent module geometry. Greybus module devices correspond to physical modules and have one or more interfaces. Modules have an id that is identical to the id of their primary interface, which in turn is the interface with lowest numbered id. The module name is constructed from the bus and module id: <bus_id>-<module_id> Interfaces, bundles, and control devices are consequently renamed as <bus_id>-<module_id>.<interface_id> <bus_id>-<module_id>.<interface_id>.<bundle_id> <bus_id>-<module_id>.<interface_id>.ctrl As before, interface ids (and therefore in a sense now also module ids) correspond to physical interface positions on the frame. Modules have the following attributes: module_id num_interfaces where module_id is the id of the module and num_interface the number of interfaces the module has. Note that until SVC module-size detection has been implemented, all interfaces are considered to be part of 1x2 modules. Specifically, the two interfaces of a 2x2 module will be presented as two 1x2 modules for now. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
parent
844fcbfeb6
commit
b15d97d770
|
@ -6,22 +6,45 @@ Description:
|
|||
The "root" greybus device for the Greybus device tree, or bus,
|
||||
where N is a dynamically assigned 1-based id.
|
||||
|
||||
What: /sys/bus/greybus/device/N-I
|
||||
What: /sys/bus/greybus/device/N-M
|
||||
Date: March 2016
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
Description:
|
||||
A Module M on the bus N, where M is the 1-byte interface
|
||||
ID of the module's primary interface.
|
||||
|
||||
What: /sys/bus/greybus/device/N-M/module_id
|
||||
Date: March 2016
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
Description:
|
||||
The ID of a Greybus module, corresponding to the ID of its
|
||||
primary interface.
|
||||
|
||||
What: /sys/bus/greybus/device/N-M/num_interfaces
|
||||
Date: March 2016
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
Description:
|
||||
The number of interfaces of a module.
|
||||
|
||||
What: /sys/bus/greybus/device/N-M.I
|
||||
Date: October 2015
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
Description:
|
||||
An Interface I on the bus N, where I is the 1-byte interface
|
||||
ID.
|
||||
An Interface I on the bus N and module N-M, where I is the
|
||||
1-byte interface ID.
|
||||
|
||||
What: /sys/bus/greybus/device/N-I/current_now
|
||||
What: /sys/bus/greybus/device/N-M.I/current_now
|
||||
Date: March 2016
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
Description:
|
||||
Current measurement of the interface in microamps (uA)
|
||||
|
||||
What: /sys/bus/greybus/device/N-I/ddbl1_manufacturer_id
|
||||
What: /sys/bus/greybus/device/N-M.I/ddbl1_manufacturer_id
|
||||
Date: October 2015
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
|
@ -29,7 +52,7 @@ Description:
|
|||
Unipro Device Descriptor Block Level 1 manufacturer ID for the
|
||||
greybus Interface.
|
||||
|
||||
What: /sys/bus/greybus/device/N-I/ddbl1_product_id
|
||||
What: /sys/bus/greybus/device/N-M.I/ddbl1_product_id
|
||||
Date: October 2015
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
|
@ -37,28 +60,28 @@ Description:
|
|||
Unipro Device Descriptor Block Level 1 product ID for the
|
||||
greybus Interface.
|
||||
|
||||
What: /sys/bus/greybus/device/N-I/interface_id
|
||||
What: /sys/bus/greybus/device/N-M.I/interface_id
|
||||
Date: October 2015
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
Description:
|
||||
The ID of a Greybus interface.
|
||||
|
||||
What: /sys/bus/greybus/device/N-I/power_now
|
||||
What: /sys/bus/greybus/device/N-M.I/power_now
|
||||
Date: March 2016
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
Description:
|
||||
Power measurement of the interface in microwatts (uW)
|
||||
|
||||
What: /sys/bus/greybus/device/N-I/product_id
|
||||
What: /sys/bus/greybus/device/N-M.I/product_id
|
||||
Date: October 2015
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
Description:
|
||||
Product ID of a Greybus interface.
|
||||
|
||||
What: /sys/bus/greybus/device/N-I/serial_number
|
||||
What: /sys/bus/greybus/device/N-M.I/serial_number
|
||||
Date: October 2015
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
|
@ -66,14 +89,14 @@ Description:
|
|||
Serial Number of the Greybus interface, represented by a 64 bit
|
||||
hexadecimal number.
|
||||
|
||||
What: /sys/bus/greybus/device/N-I/vendor_id
|
||||
What: /sys/bus/greybus/device/N-M.I/vendor_id
|
||||
Date: October 2015
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
Description:
|
||||
Vendor ID of a Greybus interface.
|
||||
|
||||
What: /sys/bus/greybus/device/N-I/version
|
||||
What: /sys/bus/greybus/device/N-M.I/version
|
||||
Date: October 2015
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
|
@ -81,14 +104,14 @@ Description:
|
|||
Interface version represented as <16 bit major number>.<16 bit
|
||||
minor number>.
|
||||
|
||||
What: /sys/bus/greybus/device/N-I/voltage_now
|
||||
What: /sys/bus/greybus/device/N-M.I/voltage_now
|
||||
Date: March 2016
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
Description:
|
||||
Voltage measurement of the interface in microvolts (uV)
|
||||
|
||||
What: /sys/bus/greybus/device/N-I.ctrl
|
||||
What: /sys/bus/greybus/device/N-M.I.ctrl
|
||||
Date: October 2015
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
|
@ -96,21 +119,21 @@ Description:
|
|||
Abstract control device for interface I that represents the
|
||||
current mode of an enumerated Greybus interface.
|
||||
|
||||
What: /sys/bus/greybus/device/N-I.ctrl/product_string
|
||||
What: /sys/bus/greybus/device/N-M.I.ctrl/product_string
|
||||
Date: October 2015
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
Description:
|
||||
Product ID string of a Greybus interface.
|
||||
|
||||
What: /sys/bus/greybus/device/N-I.ctrl/vendor_string
|
||||
What: /sys/bus/greybus/device/N-M.I.ctrl/vendor_string
|
||||
Date: October 2015
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
Description:
|
||||
Vendor ID string of a Greybus interface.
|
||||
|
||||
What: /sys/bus/greybus/device/N-I.B
|
||||
What: /sys/bus/greybus/device/N-M.I.B
|
||||
Date: October 2015
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
|
@ -118,21 +141,21 @@ Description:
|
|||
A bundle B on the Interface I, B is replaced by a 1-byte
|
||||
number representing the bundle.
|
||||
|
||||
What: /sys/bus/greybus/device/N-I.B/bundle_class
|
||||
What: /sys/bus/greybus/device/N-M.I.B/bundle_class
|
||||
Date: October 2015
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
Description:
|
||||
The greybus class of the bundle B.
|
||||
|
||||
What: /sys/bus/greybus/device/N-I.B/bundle_id
|
||||
What: /sys/bus/greybus/device/N-M.I.B/bundle_id
|
||||
Date: October 2015
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
Description:
|
||||
The interface-unique id of the bundle B.
|
||||
|
||||
What: /sys/bus/greybus/device/N-I.B/state
|
||||
What: /sys/bus/greybus/device/N-M.I.B/state
|
||||
Date: October 2015
|
||||
KernelVersion: 4.XX
|
||||
Contact: Greg Kroah-Hartman <greg@kroah.com>
|
||||
|
|
|
@ -2,6 +2,7 @@ greybus-y := core.o \
|
|||
debugfs.o \
|
||||
hd.o \
|
||||
manifest.o \
|
||||
module.o \
|
||||
interface.o \
|
||||
bundle.o \
|
||||
connection.o \
|
||||
|
|
|
@ -85,6 +85,7 @@ static int greybus_module_match(struct device *dev, struct device_driver *drv)
|
|||
static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
struct gb_host_device *hd;
|
||||
struct gb_module *module = NULL;
|
||||
struct gb_interface *intf = NULL;
|
||||
struct gb_control *control = NULL;
|
||||
struct gb_bundle *bundle = NULL;
|
||||
|
@ -92,8 +93,12 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|||
|
||||
if (is_gb_host_device(dev)) {
|
||||
hd = to_gb_host_device(dev);
|
||||
} else if (is_gb_module(dev)) {
|
||||
module = to_gb_module(dev);
|
||||
hd = module->hd;
|
||||
} else if (is_gb_interface(dev)) {
|
||||
intf = to_gb_interface(dev);
|
||||
module = intf->module;
|
||||
hd = intf->hd;
|
||||
} else if (is_gb_control(dev)) {
|
||||
control = to_gb_control(dev);
|
||||
|
@ -102,6 +107,7 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|||
} else if (is_gb_bundle(dev)) {
|
||||
bundle = to_gb_bundle(dev);
|
||||
intf = bundle->intf;
|
||||
module = intf->module;
|
||||
hd = intf->hd;
|
||||
} else if (is_gb_svc(dev)) {
|
||||
svc = to_gb_svc(dev);
|
||||
|
@ -114,6 +120,11 @@ static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|||
if (add_uevent_var(env, "BUS=%u", hd->bus_id))
|
||||
return -ENOMEM;
|
||||
|
||||
if (module) {
|
||||
if (add_uevent_var(env, "MODULE=%u", module->module_id))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (intf) {
|
||||
if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id))
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "hd.h"
|
||||
#include "svc.h"
|
||||
#include "control.h"
|
||||
#include "module.h"
|
||||
#include "interface.h"
|
||||
#include "bundle.h"
|
||||
#include "connection.h"
|
||||
|
@ -112,6 +113,7 @@ struct dentry *gb_debugfs_get(void);
|
|||
extern struct bus_type greybus_bus_type;
|
||||
|
||||
extern struct device_type greybus_hd_type;
|
||||
extern struct device_type greybus_module_type;
|
||||
extern struct device_type greybus_interface_type;
|
||||
extern struct device_type greybus_control_type;
|
||||
extern struct device_type greybus_bundle_type;
|
||||
|
@ -122,6 +124,11 @@ static inline int is_gb_host_device(const struct device *dev)
|
|||
return dev->type == &greybus_hd_type;
|
||||
}
|
||||
|
||||
static inline int is_gb_module(const struct device *dev)
|
||||
{
|
||||
return dev->type == &greybus_module_type;
|
||||
}
|
||||
|
||||
static inline int is_gb_interface(const struct device *dev)
|
||||
{
|
||||
return dev->type == &greybus_interface_type;
|
||||
|
|
|
@ -89,7 +89,7 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver,
|
|||
hd->bus_id = ret;
|
||||
|
||||
hd->driver = driver;
|
||||
INIT_LIST_HEAD(&hd->interfaces);
|
||||
INIT_LIST_HEAD(&hd->modules);
|
||||
INIT_LIST_HEAD(&hd->connections);
|
||||
ida_init(&hd->cport_id_map);
|
||||
hd->buffer_size_max = buffer_size_max;
|
||||
|
|
|
@ -34,7 +34,7 @@ struct gb_host_device {
|
|||
int bus_id;
|
||||
const struct gb_hd_driver *driver;
|
||||
|
||||
struct list_head interfaces;
|
||||
struct list_head modules;
|
||||
struct list_head connections;
|
||||
struct ida cport_id_map;
|
||||
|
||||
|
|
|
@ -333,21 +333,6 @@ static struct attribute *interface_attrs[] = {
|
|||
};
|
||||
ATTRIBUTE_GROUPS(interface);
|
||||
|
||||
|
||||
// FIXME, odds are you don't want to call this function, rework the caller to
|
||||
// not need it please.
|
||||
struct gb_interface *gb_interface_find(struct gb_host_device *hd,
|
||||
u8 interface_id)
|
||||
{
|
||||
struct gb_interface *intf;
|
||||
|
||||
list_for_each_entry(intf, &hd->interfaces, links)
|
||||
if (intf->interface_id == interface_id)
|
||||
return intf;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void gb_interface_release(struct device *dev)
|
||||
{
|
||||
struct gb_interface *intf = to_gb_interface(dev);
|
||||
|
@ -371,13 +356,11 @@ struct device_type greybus_interface_type = {
|
|||
*
|
||||
* Returns a pointer to the new interfce or a null pointer if a
|
||||
* failure occurs due to memory exhaustion.
|
||||
*
|
||||
* Locking: Caller ensures serialisation with gb_interface_remove and
|
||||
* gb_interface_find.
|
||||
*/
|
||||
struct gb_interface *gb_interface_create(struct gb_host_device *hd,
|
||||
struct gb_interface *gb_interface_create(struct gb_module *module,
|
||||
u8 interface_id)
|
||||
{
|
||||
struct gb_host_device *hd = module->hd;
|
||||
struct gb_interface *intf;
|
||||
|
||||
intf = kzalloc(sizeof(*intf), GFP_KERNEL);
|
||||
|
@ -385,6 +368,7 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd,
|
|||
return NULL;
|
||||
|
||||
intf->hd = hd; /* XXX refcount? */
|
||||
intf->module = module;
|
||||
intf->interface_id = interface_id;
|
||||
INIT_LIST_HEAD(&intf->bundles);
|
||||
INIT_LIST_HEAD(&intf->manifest_descs);
|
||||
|
@ -392,15 +376,14 @@ struct gb_interface *gb_interface_create(struct gb_host_device *hd,
|
|||
/* Invalid device id to start with */
|
||||
intf->device_id = GB_INTERFACE_DEVICE_ID_BAD;
|
||||
|
||||
intf->dev.parent = &hd->dev;
|
||||
intf->dev.parent = &module->dev;
|
||||
intf->dev.bus = &greybus_bus_type;
|
||||
intf->dev.type = &greybus_interface_type;
|
||||
intf->dev.groups = interface_groups;
|
||||
intf->dev.dma_mask = hd->dev.dma_mask;
|
||||
intf->dev.dma_mask = module->dev.dma_mask;
|
||||
device_initialize(&intf->dev);
|
||||
dev_set_name(&intf->dev, "%d-%d", hd->bus_id, interface_id);
|
||||
|
||||
list_add(&intf->links, &hd->interfaces);
|
||||
dev_set_name(&intf->dev, "%s.%u", dev_name(&module->dev),
|
||||
interface_id);
|
||||
|
||||
return intf;
|
||||
}
|
||||
|
@ -579,15 +562,16 @@ int gb_interface_add(struct gb_interface *intf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Deregister an interface and drop its reference. */
|
||||
void gb_interface_remove(struct gb_interface *intf)
|
||||
/* Deregister an interface. */
|
||||
void gb_interface_del(struct gb_interface *intf)
|
||||
{
|
||||
if (device_is_registered(&intf->dev)) {
|
||||
device_del(&intf->dev);
|
||||
dev_info(&intf->dev, "Interface removed\n");
|
||||
}
|
||||
}
|
||||
|
||||
list_del(&intf->links);
|
||||
|
||||
void gb_interface_put(struct gb_interface *intf)
|
||||
{
|
||||
put_device(&intf->dev);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ struct gb_interface {
|
|||
struct gb_control *control;
|
||||
|
||||
struct list_head bundles;
|
||||
struct list_head links; /* gb_host_device->interfaces */
|
||||
struct list_head module_node;
|
||||
struct list_head manifest_descs;
|
||||
u8 interface_id; /* Physical location within the Endo */
|
||||
u8 device_id;
|
||||
|
@ -35,6 +35,7 @@ struct gb_interface {
|
|||
u16 version_minor;
|
||||
|
||||
struct gb_host_device *hd;
|
||||
struct gb_module *module;
|
||||
|
||||
unsigned long quirks;
|
||||
|
||||
|
@ -46,13 +47,14 @@ struct gb_interface {
|
|||
struct gb_interface *gb_interface_find(struct gb_host_device *hd,
|
||||
u8 interface_id);
|
||||
|
||||
struct gb_interface *gb_interface_create(struct gb_host_device *hd,
|
||||
struct gb_interface *gb_interface_create(struct gb_module *module,
|
||||
u8 interface_id);
|
||||
int gb_interface_activate(struct gb_interface *intf);
|
||||
void gb_interface_deactivate(struct gb_interface *intf);
|
||||
int gb_interface_enable(struct gb_interface *intf);
|
||||
void gb_interface_disable(struct gb_interface *intf);
|
||||
int gb_interface_add(struct gb_interface *intf);
|
||||
void gb_interface_remove(struct gb_interface *intf);
|
||||
void gb_interface_del(struct gb_interface *intf);
|
||||
void gb_interface_put(struct gb_interface *intf);
|
||||
|
||||
#endif /* __INTERFACE_H */
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* Greybus Module code
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* Copyright 2016 Linaro Ltd.
|
||||
*
|
||||
* Released under the GPLv2 only.
|
||||
*/
|
||||
|
||||
#include "greybus.h"
|
||||
|
||||
|
||||
static ssize_t module_id_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct gb_module *module = to_gb_module(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", module->module_id);
|
||||
}
|
||||
static DEVICE_ATTR_RO(module_id);
|
||||
|
||||
static ssize_t num_interfaces_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct gb_module *module = to_gb_module(dev);
|
||||
|
||||
return sprintf(buf, "%zu\n", module->num_interfaces);
|
||||
}
|
||||
static DEVICE_ATTR_RO(num_interfaces);
|
||||
|
||||
static struct attribute *module_attrs[] = {
|
||||
&dev_attr_module_id.attr,
|
||||
&dev_attr_num_interfaces.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(module);
|
||||
|
||||
static void gb_module_release(struct device *dev)
|
||||
{
|
||||
struct gb_module *module = to_gb_module(dev);
|
||||
|
||||
kfree(module);
|
||||
}
|
||||
|
||||
struct device_type greybus_module_type = {
|
||||
.name = "greybus_module",
|
||||
.release = gb_module_release,
|
||||
};
|
||||
|
||||
struct gb_module *gb_module_create(struct gb_host_device *hd, u8 module_id,
|
||||
size_t num_interfaces)
|
||||
{
|
||||
struct gb_interface *intf;
|
||||
struct gb_module *module;
|
||||
int i;
|
||||
|
||||
module = kzalloc(sizeof(*module) + num_interfaces * sizeof(intf),
|
||||
GFP_KERNEL);
|
||||
if (!module)
|
||||
return NULL;
|
||||
|
||||
module->hd = hd;
|
||||
module->module_id = module_id;
|
||||
module->num_interfaces = num_interfaces;
|
||||
|
||||
module->dev.parent = &hd->dev;
|
||||
module->dev.bus = &greybus_bus_type;
|
||||
module->dev.type = &greybus_module_type;
|
||||
module->dev.groups = module_groups;
|
||||
module->dev.dma_mask = hd->dev.dma_mask;
|
||||
device_initialize(&module->dev);
|
||||
dev_set_name(&module->dev, "%d-%u", hd->bus_id, module_id);
|
||||
|
||||
for (i = 0; i < num_interfaces; ++i) {
|
||||
intf = gb_interface_create(module, module_id + i);
|
||||
if (!intf) {
|
||||
dev_err(&module->dev, "failed to create interface %u\n",
|
||||
module_id + i);
|
||||
goto err_put_interfaces;
|
||||
}
|
||||
module->interfaces[i] = intf;
|
||||
}
|
||||
|
||||
return module;
|
||||
|
||||
err_put_interfaces:
|
||||
for (--i; i > 0; --i)
|
||||
gb_interface_put(module->interfaces[i]);
|
||||
|
||||
put_device(&module->dev);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Register and enable an interface after first attempting to activate it.
|
||||
*/
|
||||
static void gb_module_register_interface(struct gb_interface *intf)
|
||||
{
|
||||
struct gb_module *module = intf->module;
|
||||
u8 intf_id = intf->interface_id;
|
||||
int ret;
|
||||
|
||||
ret = gb_interface_activate(intf);
|
||||
if (ret) {
|
||||
dev_err(&module->dev, "failed to activate interface %u: %d\n",
|
||||
intf_id, ret);
|
||||
gb_interface_add(intf);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = gb_interface_add(intf);
|
||||
if (ret)
|
||||
goto err_interface_deactivate;
|
||||
|
||||
ret = gb_interface_enable(intf);
|
||||
if (ret) {
|
||||
dev_err(&module->dev, "failed to enable interface %u: %d\n",
|
||||
intf_id, ret);
|
||||
goto err_interface_deactivate;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err_interface_deactivate:
|
||||
gb_interface_deactivate(intf);
|
||||
}
|
||||
|
||||
static void gb_module_deregister_interface(struct gb_interface *intf)
|
||||
{
|
||||
/* Mark as disconnected to prevent I/O during disable. */
|
||||
if (intf->module->disconnected)
|
||||
intf->disconnected = true;
|
||||
|
||||
gb_interface_disable(intf);
|
||||
gb_interface_deactivate(intf);
|
||||
|
||||
gb_interface_del(intf);
|
||||
}
|
||||
|
||||
/* Register a module and its interfaces. */
|
||||
int gb_module_add(struct gb_module *module)
|
||||
{
|
||||
size_t i;
|
||||
int ret;
|
||||
|
||||
ret = device_add(&module->dev);
|
||||
if (ret) {
|
||||
dev_err(&module->dev, "failed to register module: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < module->num_interfaces; ++i)
|
||||
gb_module_register_interface(module->interfaces[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Deregister a module and its interfaces. */
|
||||
void gb_module_del(struct gb_module *module)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < module->num_interfaces; ++i)
|
||||
gb_module_deregister_interface(module->interfaces[i]);
|
||||
|
||||
device_del(&module->dev);
|
||||
}
|
||||
|
||||
void gb_module_put(struct gb_module *module)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < module->num_interfaces; ++i)
|
||||
gb_interface_put(module->interfaces[i]);
|
||||
|
||||
put_device(&module->dev);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Greybus Module code
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* Copyright 2016 Linaro Ltd.
|
||||
*
|
||||
* Released under the GPLv2 only.
|
||||
*/
|
||||
|
||||
#ifndef __MODULE_H
|
||||
#define __MODULE_H
|
||||
|
||||
struct gb_module {
|
||||
struct device dev;
|
||||
struct gb_host_device *hd;
|
||||
|
||||
struct list_head hd_node;
|
||||
|
||||
u8 module_id;
|
||||
size_t num_interfaces;
|
||||
|
||||
bool disconnected;
|
||||
|
||||
struct gb_interface *interfaces[0];
|
||||
};
|
||||
#define to_gb_module(d) container_of(d, struct gb_module, dev)
|
||||
|
||||
struct gb_module *gb_module_create(struct gb_host_device *hd, u8 module_id,
|
||||
size_t num_interfaces);
|
||||
int gb_module_add(struct gb_module *module);
|
||||
void gb_module_del(struct gb_module *module);
|
||||
void gb_module_put(struct gb_module *module);
|
||||
|
||||
#endif /* __MODULE_H */
|
|
@ -665,6 +665,19 @@ static int gb_svc_hello(struct gb_operation *op)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct gb_module *gb_svc_module_lookup(struct gb_svc *svc, u8 module_id)
|
||||
{
|
||||
struct gb_host_device *hd = svc->hd;
|
||||
struct gb_module *module;
|
||||
|
||||
list_for_each_entry(module, &hd->modules, hd_node) {
|
||||
if (module->module_id == module_id)
|
||||
return module;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void gb_svc_intf_reenable(struct gb_svc *svc, struct gb_interface *intf)
|
||||
{
|
||||
int ret;
|
||||
|
@ -689,7 +702,7 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation)
|
|||
struct gb_connection *connection = operation->connection;
|
||||
struct gb_svc *svc = gb_connection_get_data(connection);
|
||||
struct gb_host_device *hd = connection->hd;
|
||||
struct gb_interface *intf;
|
||||
struct gb_module *module;
|
||||
u8 intf_id;
|
||||
int ret;
|
||||
|
||||
|
@ -699,52 +712,35 @@ static void gb_svc_process_intf_hotplug(struct gb_operation *operation)
|
|||
|
||||
dev_dbg(&svc->dev, "%s - id = %u\n", __func__, intf_id);
|
||||
|
||||
intf = gb_interface_find(hd, intf_id);
|
||||
if (intf) {
|
||||
/* All modules are considered 1x2 for now */
|
||||
module = gb_svc_module_lookup(svc, intf_id);
|
||||
if (module) {
|
||||
dev_info(&svc->dev, "mode switch detected on interface %u\n",
|
||||
intf_id);
|
||||
|
||||
return gb_svc_intf_reenable(svc, intf);
|
||||
return gb_svc_intf_reenable(svc, module->interfaces[0]);
|
||||
}
|
||||
|
||||
intf = gb_interface_create(hd, intf_id);
|
||||
if (!intf) {
|
||||
dev_err(&svc->dev, "failed to create interface %u\n",
|
||||
intf_id);
|
||||
module = gb_module_create(hd, intf_id, 1);
|
||||
if (!module) {
|
||||
dev_err(&svc->dev, "failed to create module\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ret = gb_interface_activate(intf);
|
||||
ret = gb_module_add(module);
|
||||
if (ret) {
|
||||
dev_err(&svc->dev, "failed to activate interface %u: %d\n",
|
||||
intf_id, ret);
|
||||
gb_interface_add(intf);
|
||||
gb_module_put(module);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = gb_interface_add(intf);
|
||||
if (ret)
|
||||
goto err_interface_deactivate;
|
||||
|
||||
ret = gb_interface_enable(intf);
|
||||
if (ret) {
|
||||
dev_err(&svc->dev, "failed to enable interface %u: %d\n",
|
||||
intf_id, ret);
|
||||
goto err_interface_deactivate;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err_interface_deactivate:
|
||||
gb_interface_deactivate(intf);
|
||||
list_add(&module->hd_node, &hd->modules);
|
||||
}
|
||||
|
||||
static void gb_svc_process_intf_hot_unplug(struct gb_operation *operation)
|
||||
{
|
||||
struct gb_svc *svc = gb_connection_get_data(operation->connection);
|
||||
struct gb_svc_intf_hot_unplug_request *request;
|
||||
struct gb_host_device *hd = operation->connection->hd;
|
||||
struct gb_interface *intf;
|
||||
struct gb_module *module;
|
||||
u8 intf_id;
|
||||
|
||||
/* The request message size has already been verified. */
|
||||
|
@ -753,19 +749,19 @@ static void gb_svc_process_intf_hot_unplug(struct gb_operation *operation)
|
|||
|
||||
dev_dbg(&svc->dev, "%s - id = %u\n", __func__, intf_id);
|
||||
|
||||
intf = gb_interface_find(hd, intf_id);
|
||||
if (!intf) {
|
||||
/* All modules are considered 1x2 for now */
|
||||
module = gb_svc_module_lookup(svc, intf_id);
|
||||
if (!module) {
|
||||
dev_warn(&svc->dev, "could not find hot-unplug interface %u\n",
|
||||
intf_id);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mark as disconnected to prevent I/O during disable. */
|
||||
intf->disconnected = true;
|
||||
module->disconnected = true;
|
||||
|
||||
gb_interface_disable(intf);
|
||||
gb_interface_deactivate(intf);
|
||||
gb_interface_remove(intf);
|
||||
gb_module_del(module);
|
||||
list_del(&module->hd_node);
|
||||
gb_module_put(module);
|
||||
}
|
||||
|
||||
static void gb_svc_process_deferred_request(struct work_struct *work)
|
||||
|
@ -1104,14 +1100,15 @@ int gb_svc_add(struct gb_svc *svc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void gb_svc_remove_interfaces(struct gb_svc *svc)
|
||||
static void gb_svc_remove_modules(struct gb_svc *svc)
|
||||
{
|
||||
struct gb_interface *intf, *tmp;
|
||||
struct gb_host_device *hd = svc->hd;
|
||||
struct gb_module *module, *tmp;
|
||||
|
||||
list_for_each_entry_safe(intf, tmp, &svc->hd->interfaces, links) {
|
||||
gb_interface_disable(intf);
|
||||
gb_interface_deactivate(intf);
|
||||
gb_interface_remove(intf);
|
||||
list_for_each_entry_safe(module, tmp, &hd->modules, hd_node) {
|
||||
gb_module_del(module);
|
||||
list_del(&module->hd_node);
|
||||
gb_module_put(module);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1132,7 +1129,7 @@ void gb_svc_del(struct gb_svc *svc)
|
|||
|
||||
flush_workqueue(svc->wq);
|
||||
|
||||
gb_svc_remove_interfaces(svc);
|
||||
gb_svc_remove_modules(svc);
|
||||
}
|
||||
|
||||
void gb_svc_put(struct gb_svc *svc)
|
||||
|
|
Loading…
Reference in New Issue