diff options
Diffstat (limited to 'drivers/misc/mei/bus.c')
-rw-r--r-- | drivers/misc/mei/bus.c | 218 |
1 files changed, 154 insertions, 64 deletions
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index eef1c6b46ad8..0b05aa938799 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -91,7 +91,7 @@ out: * __mei_cl_recv - internal client receive (read) * * @cl: host client - * @buf: buffer to send + * @buf: buffer to receive * @length: buffer length * * Return: read size in bytes of < 0 on error @@ -165,7 +165,7 @@ out: } /** - * mei_cl_send - me device send (write) + * mei_cldev_send - me device send (write) * * @cldev: me client device * @buf: buffer to send @@ -173,7 +173,7 @@ out: * * Return: written size in bytes or < 0 on error */ -ssize_t mei_cl_send(struct mei_cl_device *cldev, u8 *buf, size_t length) +ssize_t mei_cldev_send(struct mei_cl_device *cldev, u8 *buf, size_t length) { struct mei_cl *cl = cldev->cl; @@ -182,18 +182,18 @@ ssize_t mei_cl_send(struct mei_cl_device *cldev, u8 *buf, size_t length) return __mei_cl_send(cl, buf, length, 1); } -EXPORT_SYMBOL_GPL(mei_cl_send); +EXPORT_SYMBOL_GPL(mei_cldev_send); /** - * mei_cl_recv - client receive (read) + * mei_cldev_recv - client receive (read) * * @cldev: me client device - * @buf: buffer to send + * @buf: buffer to receive * @length: buffer length * * Return: read size in bytes of < 0 on error */ -ssize_t mei_cl_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) +ssize_t mei_cldev_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) { struct mei_cl *cl = cldev->cl; @@ -202,15 +202,15 @@ ssize_t mei_cl_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) return __mei_cl_recv(cl, buf, length); } -EXPORT_SYMBOL_GPL(mei_cl_recv); +EXPORT_SYMBOL_GPL(mei_cldev_recv); /** - * mei_bus_event_work - dispatch rx event for a bus device + * mei_cl_bus_event_work - dispatch rx event for a bus device * and schedule new work * * @work: work */ -static void mei_bus_event_work(struct work_struct *work) +static void mei_cl_bus_event_work(struct work_struct *work) { struct mei_cl_device *cldev; @@ -272,7 +272,7 @@ void mei_cl_bus_rx_event(struct mei_cl *cl) } /** - * mei_cl_register_event_cb - register event callback + * mei_cldev_register_event_cb - register event callback * * @cldev: me client devices * @event_cb: callback function @@ -283,9 +283,9 @@ void mei_cl_bus_rx_event(struct mei_cl *cl) * -EALREADY if an callback is already registered * <0 on other errors */ -int mei_cl_register_event_cb(struct mei_cl_device *cldev, - unsigned long events_mask, - mei_cl_event_cb_t event_cb, void *context) +int mei_cldev_register_event_cb(struct mei_cl_device *cldev, + unsigned long events_mask, + mei_cldev_event_cb_t event_cb, void *context) { int ret; @@ -296,7 +296,7 @@ int mei_cl_register_event_cb(struct mei_cl_device *cldev, cldev->events_mask = events_mask; cldev->event_cb = event_cb; cldev->event_context = context; - INIT_WORK(&cldev->event_work, mei_bus_event_work); + INIT_WORK(&cldev->event_work, mei_cl_bus_event_work); if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) { ret = mei_cl_read_start(cldev->cl, 0, NULL); @@ -314,42 +314,81 @@ int mei_cl_register_event_cb(struct mei_cl_device *cldev, return 0; } -EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); +EXPORT_SYMBOL_GPL(mei_cldev_register_event_cb); /** - * mei_cl_get_drvdata - driver data getter + * mei_cldev_get_drvdata - driver data getter * * @cldev: mei client device * * Return: driver private data */ -void *mei_cl_get_drvdata(const struct mei_cl_device *cldev) +void *mei_cldev_get_drvdata(const struct mei_cl_device *cldev) { return dev_get_drvdata(&cldev->dev); } -EXPORT_SYMBOL_GPL(mei_cl_get_drvdata); +EXPORT_SYMBOL_GPL(mei_cldev_get_drvdata); /** - * mei_cl_set_drvdata - driver data setter + * mei_cldev_set_drvdata - driver data setter * * @cldev: mei client device * @data: data to store */ -void mei_cl_set_drvdata(struct mei_cl_device *cldev, void *data) +void mei_cldev_set_drvdata(struct mei_cl_device *cldev, void *data) { dev_set_drvdata(&cldev->dev, data); } -EXPORT_SYMBOL_GPL(mei_cl_set_drvdata); +EXPORT_SYMBOL_GPL(mei_cldev_set_drvdata); + +/** + * mei_cldev_uuid - return uuid of the underlying me client + * + * @cldev: mei client device + * + * Return: me client uuid + */ +const uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev) +{ + return mei_me_cl_uuid(cldev->me_cl); +} +EXPORT_SYMBOL_GPL(mei_cldev_uuid); + +/** + * mei_cldev_ver - return protocol version of the underlying me client + * + * @cldev: mei client device + * + * Return: me client protocol version + */ +u8 mei_cldev_ver(const struct mei_cl_device *cldev) +{ + return mei_me_cl_ver(cldev->me_cl); +} +EXPORT_SYMBOL_GPL(mei_cldev_ver); + +/** + * mei_cldev_enabled - check whether the device is enabled + * + * @cldev: mei client device + * + * Return: true if me client is initialized and connected + */ +bool mei_cldev_enabled(struct mei_cl_device *cldev) +{ + return cldev->cl && mei_cl_is_connected(cldev->cl); +} +EXPORT_SYMBOL_GPL(mei_cldev_enabled); /** - * mei_cl_enable_device - enable me client device + * mei_cldev_enable_device - enable me client device * create connection with me client * * @cldev: me client device * * Return: 0 on success and < 0 on error */ -int mei_cl_enable_device(struct mei_cl_device *cldev) +int mei_cldev_enable(struct mei_cl_device *cldev) { struct mei_device *bus = cldev->bus; struct mei_cl *cl; @@ -389,17 +428,17 @@ out: return ret; } -EXPORT_SYMBOL_GPL(mei_cl_enable_device); +EXPORT_SYMBOL_GPL(mei_cldev_enable); /** - * mei_cl_disable_device - disable me client device + * mei_cldev_disable - disable me client device * disconnect form the me client * * @cldev: me client device * * Return: 0 on success and < 0 on error */ -int mei_cl_disable_device(struct mei_cl_device *cldev) +int mei_cldev_disable(struct mei_cl_device *cldev) { struct mei_device *bus; struct mei_cl *cl; @@ -437,7 +476,7 @@ out: mutex_unlock(&bus->device_lock); return err; } -EXPORT_SYMBOL_GPL(mei_cl_disable_device); +EXPORT_SYMBOL_GPL(mei_cldev_disable); /** * mei_cl_device_find - find matching entry in the driver id table @@ -453,17 +492,26 @@ struct mei_cl_device_id *mei_cl_device_find(struct mei_cl_device *cldev, { const struct mei_cl_device_id *id; const uuid_le *uuid; + u8 version; + bool match; uuid = mei_me_cl_uuid(cldev->me_cl); + version = mei_me_cl_ver(cldev->me_cl); id = cldrv->id_table; while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) { if (!uuid_le_cmp(*uuid, id->uuid)) { + match = true; - if (!cldev->name[0]) - return id; + if (cldev->name[0]) + if (strncmp(cldev->name, id->name, + sizeof(id->name))) + match = false; - if (!strncmp(cldev->name, id->name, sizeof(id->name))) + if (id->version != MEI_CL_VERSION_ANY) + if (id->version != version) + match = false; + if (match) return id; } @@ -590,6 +638,19 @@ static ssize_t uuid_show(struct device *dev, struct device_attribute *a, } static DEVICE_ATTR_RO(uuid); +static ssize_t version_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + u8 version = mei_me_cl_ver(cldev->me_cl); + size_t len; + + len = snprintf(buf, PAGE_SIZE, "%02X", version); + + return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; +} +static DEVICE_ATTR_RO(version); + static ssize_t modalias_show(struct device *dev, struct device_attribute *a, char *buf) { @@ -597,20 +658,19 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a, const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); size_t len; - len = snprintf(buf, PAGE_SIZE, "mei:%s:" MEI_CL_UUID_FMT ":", - cldev->name, MEI_CL_UUID_ARGS(uuid->b)); - + len = snprintf(buf, PAGE_SIZE, "mei:%s:%pUl:", cldev->name, uuid); return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; } static DEVICE_ATTR_RO(modalias); -static struct attribute *mei_cl_dev_attrs[] = { +static struct attribute *mei_cldev_attrs[] = { &dev_attr_name.attr, &dev_attr_uuid.attr, + &dev_attr_version.attr, &dev_attr_modalias.attr, NULL, }; -ATTRIBUTE_GROUPS(mei_cl_dev); +ATTRIBUTE_GROUPS(mei_cldev); /** * mei_cl_device_uevent - me client bus uevent handler @@ -624,6 +684,10 @@ static int mei_cl_device_uevent(struct device *dev, struct kobj_uevent_env *env) { struct mei_cl_device *cldev = to_mei_cl_device(dev); const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); + u8 version = mei_me_cl_ver(cldev->me_cl); + + if (add_uevent_var(env, "MEI_CL_VERSION=%d", version)) + return -ENOMEM; if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid)) return -ENOMEM; @@ -631,8 +695,8 @@ static int mei_cl_device_uevent(struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "MEI_CL_NAME=%s", cldev->name)) return -ENOMEM; - if (add_uevent_var(env, "MODALIAS=mei:%s:" MEI_CL_UUID_FMT ":", - cldev->name, MEI_CL_UUID_ARGS(uuid->b))) + if (add_uevent_var(env, "MODALIAS=mei:%s:%pUl:%02X:", + cldev->name, uuid, version)) return -ENOMEM; return 0; @@ -640,7 +704,7 @@ static int mei_cl_device_uevent(struct device *dev, struct kobj_uevent_env *env) static struct bus_type mei_cl_bus_type = { .name = "mei", - .dev_groups = mei_cl_dev_groups, + .dev_groups = mei_cldev_groups, .match = mei_cl_device_match, .probe = mei_cl_device_probe, .remove = mei_cl_device_remove, @@ -661,7 +725,7 @@ static void mei_dev_bus_put(struct mei_device *bus) put_device(bus->dev); } -static void mei_cl_dev_release(struct device *dev) +static void mei_cl_bus_dev_release(struct device *dev) { struct mei_cl_device *cldev = to_mei_cl_device(dev); @@ -674,19 +738,32 @@ static void mei_cl_dev_release(struct device *dev) } static struct device_type mei_cl_device_type = { - .release = mei_cl_dev_release, + .release = mei_cl_bus_dev_release, }; /** - * mei_cl_dev_alloc - initialize and allocate mei client device + * mei_cl_bus_set_name - set device name for me client device + * + * @cldev: me client device + */ +static inline void mei_cl_bus_set_name(struct mei_cl_device *cldev) +{ + dev_set_name(&cldev->dev, "mei:%s:%pUl:%02X", + cldev->name, + mei_me_cl_uuid(cldev->me_cl), + mei_me_cl_ver(cldev->me_cl)); +} + +/** + * mei_cl_bus_dev_alloc - initialize and allocate mei client device * * @bus: mei device * @me_cl: me client * * Return: allocated device structur or NULL on allocation failure */ -static struct mei_cl_device *mei_cl_dev_alloc(struct mei_device *bus, - struct mei_me_client *me_cl) +static struct mei_cl_device *mei_cl_bus_dev_alloc(struct mei_device *bus, + struct mei_me_client *me_cl) { struct mei_cl_device *cldev; @@ -700,6 +777,7 @@ static struct mei_cl_device *mei_cl_dev_alloc(struct mei_device *bus, cldev->dev.type = &mei_cl_device_type; cldev->bus = mei_dev_bus_get(bus); cldev->me_cl = mei_me_cl_get(me_cl); + mei_cl_bus_set_name(cldev); cldev->is_added = 0; INIT_LIST_HEAD(&cldev->bus_list); @@ -715,15 +793,15 @@ static struct mei_cl_device *mei_cl_dev_alloc(struct mei_device *bus, * * Return: true if the device is eligible for enumeration */ -static bool mei_cl_dev_setup(struct mei_device *bus, - struct mei_cl_device *cldev) +static bool mei_cl_bus_dev_setup(struct mei_device *bus, + struct mei_cl_device *cldev) { cldev->do_match = 1; - mei_cl_dev_fixup(cldev); + mei_cl_bus_dev_fixup(cldev); + /* the device name can change during fix up */ if (cldev->do_match) - dev_set_name(&cldev->dev, "mei:%s:%pUl", - cldev->name, mei_me_cl_uuid(cldev->me_cl)); + mei_cl_bus_set_name(cldev); return cldev->do_match == 1; } @@ -739,7 +817,9 @@ static int mei_cl_bus_dev_add(struct mei_cl_device *cldev) { int ret; - dev_dbg(cldev->bus->dev, "adding %pUL\n", mei_me_cl_uuid(cldev->me_cl)); + dev_dbg(cldev->bus->dev, "adding %pUL:%02X\n", + mei_me_cl_uuid(cldev->me_cl), + mei_me_cl_ver(cldev->me_cl)); ret = device_add(&cldev->dev); if (!ret) cldev->is_added = 1; @@ -762,17 +842,20 @@ static void mei_cl_bus_dev_stop(struct mei_cl_device *cldev) * mei_cl_bus_dev_destroy - destroy me client devices object * * @cldev: me client device + * + * Locking: called under "dev->cl_bus_lock" lock */ static void mei_cl_bus_dev_destroy(struct mei_cl_device *cldev) { + + WARN_ON(!mutex_is_locked(&cldev->bus->cl_bus_lock)); + if (!cldev->is_added) return; device_del(&cldev->dev); - mutex_lock(&cldev->bus->cl_bus_lock); list_del_init(&cldev->bus_list); - mutex_unlock(&cldev->bus->cl_bus_lock); cldev->is_added = 0; put_device(&cldev->dev); @@ -798,35 +881,40 @@ void mei_cl_bus_remove_devices(struct mei_device *bus) { struct mei_cl_device *cldev, *next; + mutex_lock(&bus->cl_bus_lock); list_for_each_entry_safe(cldev, next, &bus->device_list, bus_list) mei_cl_bus_remove_device(cldev); + mutex_unlock(&bus->cl_bus_lock); } /** - * mei_cl_dev_init - allocate and initializes an mei client devices + * mei_cl_bus_dev_init - allocate and initializes an mei client devices * based on me client * * @bus: mei device * @me_cl: me client + * + * Locking: called under "dev->cl_bus_lock" lock */ -static void mei_cl_dev_init(struct mei_device *bus, struct mei_me_client *me_cl) +static void mei_cl_bus_dev_init(struct mei_device *bus, + struct mei_me_client *me_cl) { struct mei_cl_device *cldev; + WARN_ON(!mutex_is_locked(&bus->cl_bus_lock)); + dev_dbg(bus->dev, "initializing %pUl", mei_me_cl_uuid(me_cl)); if (me_cl->bus_added) return; - cldev = mei_cl_dev_alloc(bus, me_cl); + cldev = mei_cl_bus_dev_alloc(bus, me_cl); if (!cldev) return; - mutex_lock(&cldev->bus->cl_bus_lock); me_cl->bus_added = true; list_add_tail(&cldev->bus_list, &bus->device_list); - mutex_unlock(&cldev->bus->cl_bus_lock); } @@ -841,12 +929,13 @@ void mei_cl_bus_rescan(struct mei_device *bus) struct mei_cl_device *cldev, *n; struct mei_me_client *me_cl; + mutex_lock(&bus->cl_bus_lock); + down_read(&bus->me_clients_rwsem); list_for_each_entry(me_cl, &bus->me_clients, list) - mei_cl_dev_init(bus, me_cl); + mei_cl_bus_dev_init(bus, me_cl); up_read(&bus->me_clients_rwsem); - mutex_lock(&bus->cl_bus_lock); list_for_each_entry_safe(cldev, n, &bus->device_list, bus_list) { if (!mei_me_cl_is_active(cldev->me_cl)) { @@ -857,7 +946,7 @@ void mei_cl_bus_rescan(struct mei_device *bus) if (cldev->is_added) continue; - if (mei_cl_dev_setup(bus, cldev)) + if (mei_cl_bus_dev_setup(bus, cldev)) mei_cl_bus_dev_add(cldev); else { list_del_init(&cldev->bus_list); @@ -869,7 +958,8 @@ void mei_cl_bus_rescan(struct mei_device *bus) dev_dbg(bus->dev, "rescan end"); } -int __mei_cl_driver_register(struct mei_cl_driver *cldrv, struct module *owner) +int __mei_cldev_driver_register(struct mei_cl_driver *cldrv, + struct module *owner) { int err; @@ -885,15 +975,15 @@ int __mei_cl_driver_register(struct mei_cl_driver *cldrv, struct module *owner) return 0; } -EXPORT_SYMBOL_GPL(__mei_cl_driver_register); +EXPORT_SYMBOL_GPL(__mei_cldev_driver_register); -void mei_cl_driver_unregister(struct mei_cl_driver *cldrv) +void mei_cldev_driver_unregister(struct mei_cl_driver *cldrv) { driver_unregister(&cldrv->driver); pr_debug("mei: driver [%s] unregistered\n", cldrv->driver.name); } -EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); +EXPORT_SYMBOL_GPL(mei_cldev_driver_unregister); int __init mei_cl_bus_init(void) |