diff options
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/mei/bus.c | 613 |
1 files changed, 351 insertions, 262 deletions
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 18c37af22f07..6ea8a408f477 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -30,263 +30,16 @@ #define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver) #define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev) -static int mei_cl_device_match(struct device *dev, struct device_driver *drv) -{ - struct mei_cl_device *cldev = to_mei_cl_device(dev); - struct mei_cl_driver *cldrv = to_mei_cl_driver(drv); - const struct mei_cl_device_id *id; - const uuid_le *uuid; - const char *name; - - if (!cldev) - return 0; - - uuid = mei_me_cl_uuid(cldev->me_cl); - name = cldev->name; - - if (!cldrv || !cldrv->id_table) - return 0; - - id = cldrv->id_table; - - while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) { - - if (!uuid_le_cmp(*uuid, id->uuid)) { - if (id->name[0]) { - if (!strncmp(name, id->name, sizeof(id->name))) - return 1; - } else { - return 1; - } - } - - id++; - } - - return 0; -} - -static int mei_cl_device_probe(struct device *dev) -{ - struct mei_cl_device *cldev = to_mei_cl_device(dev); - struct mei_cl_driver *cldrv; - struct mei_cl_device_id id; - - if (!cldev) - return 0; - - cldrv = to_mei_cl_driver(dev->driver); - if (!cldrv || !cldrv->probe) - return -ENODEV; - - dev_dbg(dev, "Device probe\n"); - - strlcpy(id.name, cldev->name, sizeof(id.name)); - - return cldrv->probe(cldev, &id); -} - -static int mei_cl_device_remove(struct device *dev) -{ - struct mei_cl_device *cldev = to_mei_cl_device(dev); - struct mei_cl_driver *cldrv; - - if (!cldev || !dev->driver) - return 0; - - if (cldev->event_cb) { - cldev->event_cb = NULL; - cancel_work_sync(&cldev->event_work); - } - - cldrv = to_mei_cl_driver(dev->driver); - if (!cldrv->remove) { - dev->driver = NULL; - - return 0; - } - - return cldrv->remove(cldev); -} - -static ssize_t name_show(struct device *dev, struct device_attribute *a, - char *buf) -{ - struct mei_cl_device *cldev = to_mei_cl_device(dev); - size_t len; - - len = snprintf(buf, PAGE_SIZE, "%s", cldev->name); - - return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; -} -static DEVICE_ATTR_RO(name); - -static ssize_t uuid_show(struct device *dev, struct device_attribute *a, - char *buf) -{ - struct mei_cl_device *cldev = to_mei_cl_device(dev); - const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); - size_t len; - - len = snprintf(buf, PAGE_SIZE, "%pUl", uuid); - - return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; -} -static DEVICE_ATTR_RO(uuid); - -static ssize_t modalias_show(struct device *dev, struct device_attribute *a, - char *buf) -{ - struct mei_cl_device *cldev = to_mei_cl_device(dev); - 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)); - - return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; -} -static DEVICE_ATTR_RO(modalias); - -static struct attribute *mei_cl_dev_attrs[] = { - &dev_attr_name.attr, - &dev_attr_uuid.attr, - &dev_attr_modalias.attr, - NULL, -}; -ATTRIBUTE_GROUPS(mei_cl_dev); - -static int mei_cl_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); - - if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid)) - return -ENOMEM; - - 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))) - return -ENOMEM; - - return 0; -} - -static struct bus_type mei_cl_bus_type = { - .name = "mei", - .dev_groups = mei_cl_dev_groups, - .match = mei_cl_device_match, - .probe = mei_cl_device_probe, - .remove = mei_cl_device_remove, - .uevent = mei_cl_uevent, -}; - -static void mei_cl_dev_release(struct device *dev) -{ - struct mei_cl_device *cldev = to_mei_cl_device(dev); - - if (!cldev) - return; - - mei_me_cl_put(cldev->me_cl); - kfree(cldev); -} - -static struct device_type mei_cl_device_type = { - .release = mei_cl_dev_release, -}; - -struct mei_cl *mei_cl_bus_find_cl_by_uuid(struct mei_device *bus, - uuid_le uuid) -{ - struct mei_cl *cl; - - list_for_each_entry(cl, &bus->device_list, device_link) { - if (cl->cldev && cl->cldev->me_cl && - !uuid_le_cmp(uuid, *mei_me_cl_uuid(cl->cldev->me_cl))) - return cl; - } - - return NULL; -} - -struct mei_cl_device *mei_cl_add_device(struct mei_device *bus, - struct mei_me_client *me_cl, - struct mei_cl *cl, - char *name) -{ - struct mei_cl_device *cldev; - int status; - - cldev = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); - if (!cldev) - return NULL; - - cldev->me_cl = mei_me_cl_get(me_cl); - if (!cldev->me_cl) { - kfree(cldev); - return NULL; - } - - cldev->cl = cl; - cldev->dev.parent = bus->dev; - cldev->dev.bus = &mei_cl_bus_type; - cldev->dev.type = &mei_cl_device_type; - - strlcpy(cldev->name, name, sizeof(cldev->name)); - - dev_set_name(&cldev->dev, "mei:%s:%pUl", name, mei_me_cl_uuid(me_cl)); - - status = device_register(&cldev->dev); - if (status) { - dev_err(bus->dev, "Failed to register MEI device\n"); - mei_me_cl_put(cldev->me_cl); - kfree(cldev); - return NULL; - } - - cl->cldev = cldev; - - dev_dbg(&cldev->dev, "client %s registered\n", name); - - return cldev; -} -EXPORT_SYMBOL_GPL(mei_cl_add_device); - -void mei_cl_remove_device(struct mei_cl_device *cldev) -{ - device_unregister(&cldev->dev); -} -EXPORT_SYMBOL_GPL(mei_cl_remove_device); - -int __mei_cl_driver_register(struct mei_cl_driver *cldrv, struct module *owner) -{ - int err; - - cldrv->driver.name = cldrv->name; - cldrv->driver.owner = owner; - cldrv->driver.bus = &mei_cl_bus_type; - - err = driver_register(&cldrv->driver); - if (err) - return err; - - pr_debug("mei: driver [%s] registered\n", cldrv->driver.name); - - return 0; -} -EXPORT_SYMBOL_GPL(__mei_cl_driver_register); - -void mei_cl_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); - +/** + * __mei_cl_send - internal client send (write) + * + * @cl: host client + * @buf: buffer to send + * @length: buffer length + * @blocking: wait for write completion + * + * Return: written size bytes or < 0 on error + */ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bool blocking) { @@ -334,6 +87,15 @@ out: return rets; } +/** + * __mei_cl_recv - internal client receive (read) + * + * @cl: host client + * @buf: buffer to send + * @length: buffer length + * + * Return: read size in bytes of < 0 on error + */ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) { struct mei_device *bus; @@ -356,6 +118,7 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) if (rets && rets != -EBUSY) goto out; + /* wait on event only if there is no other waiter */ if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) { mutex_unlock(&bus->device_lock); @@ -401,6 +164,15 @@ out: return rets; } +/** + * mei_cl_send - me device send (write) + * + * @cldev: me client device + * @buf: buffer to send + * @length: buffer length + * + * Return: written size in bytes or < 0 on error + */ ssize_t mei_cl_send(struct mei_cl_device *cldev, u8 *buf, size_t length) { struct mei_cl *cl = cldev->cl; @@ -412,6 +184,15 @@ ssize_t mei_cl_send(struct mei_cl_device *cldev, u8 *buf, size_t length) } EXPORT_SYMBOL_GPL(mei_cl_send); +/** + * mei_cl_recv - client receive (read) + * + * @cldev: me client device + * @buf: buffer to send + * @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) { struct mei_cl *cl = cldev->cl; @@ -423,6 +204,12 @@ ssize_t mei_cl_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) } EXPORT_SYMBOL_GPL(mei_cl_recv); +/** + * mei_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) { struct mei_cl_device *cldev; @@ -438,6 +225,34 @@ static void mei_bus_event_work(struct work_struct *work) mei_cl_read_start(cldev->cl, 0, NULL); } +/** + * mei_cl_bus_rx_event - schedule rx evenet + * + * @cl: host client + */ +void mei_cl_bus_rx_event(struct mei_cl *cl) +{ + struct mei_cl_device *cldev = cl->cldev; + + if (!cldev || !cldev->event_cb) + return; + + set_bit(MEI_CL_EVENT_RX, &cldev->events); + + schedule_work(&cldev->event_work); +} + +/** + * mei_cl_register_event_cb - register event callback + * + * @cldev: me client devices + * @event_cb: callback function + * @context: driver context data + * + * Return: 0 on success + * -EALREADY if an callback is already registered + * <0 on other errors + */ int mei_cl_register_event_cb(struct mei_cl_device *cldev, mei_cl_event_cb_t event_cb, void *context) { @@ -455,18 +270,39 @@ int mei_cl_register_event_cb(struct mei_cl_device *cldev, } EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); +/** + * mei_cl_get_drvdata - driver data getter + * + * @cldev: mei client device + * + * Return: driver private data + */ void *mei_cl_get_drvdata(const struct mei_cl_device *cldev) { return dev_get_drvdata(&cldev->dev); } EXPORT_SYMBOL_GPL(mei_cl_get_drvdata); +/** + * mei_cl_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) { dev_set_drvdata(&cldev->dev, data); } EXPORT_SYMBOL_GPL(mei_cl_set_drvdata); +/** + * mei_cl_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 err; @@ -503,6 +339,14 @@ int mei_cl_enable_device(struct mei_cl_device *cldev) } EXPORT_SYMBOL_GPL(mei_cl_enable_device); +/** + * mei_cl_disable_device - 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 err; @@ -540,17 +384,262 @@ out: } EXPORT_SYMBOL_GPL(mei_cl_disable_device); -void mei_cl_bus_rx_event(struct mei_cl *cl) +static int mei_cl_device_match(struct device *dev, struct device_driver *drv) { - struct mei_cl_device *cldev = cl->cldev; + struct mei_cl_device *cldev = to_mei_cl_device(dev); + struct mei_cl_driver *cldrv = to_mei_cl_driver(drv); + const struct mei_cl_device_id *id; + const uuid_le *uuid; + const char *name; - if (!cldev || !cldev->event_cb) + if (!cldev) + return 0; + + uuid = mei_me_cl_uuid(cldev->me_cl); + name = cldev->name; + + if (!cldrv || !cldrv->id_table) + return 0; + + id = cldrv->id_table; + + while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) { + + if (!uuid_le_cmp(*uuid, id->uuid)) { + if (id->name[0]) { + if (!strncmp(name, id->name, sizeof(id->name))) + return 1; + } else { + return 1; + } + } + + id++; + } + + return 0; +} + +static int mei_cl_device_probe(struct device *dev) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + struct mei_cl_driver *cldrv; + struct mei_cl_device_id id; + + if (!cldev) + return 0; + + cldrv = to_mei_cl_driver(dev->driver); + if (!cldrv || !cldrv->probe) + return -ENODEV; + + dev_dbg(dev, "Device probe\n"); + + strlcpy(id.name, cldev->name, sizeof(id.name)); + + return cldrv->probe(cldev, &id); +} + +static int mei_cl_device_remove(struct device *dev) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + struct mei_cl_driver *cldrv; + + if (!cldev || !dev->driver) + return 0; + + if (cldev->event_cb) { + cldev->event_cb = NULL; + cancel_work_sync(&cldev->event_work); + } + + cldrv = to_mei_cl_driver(dev->driver); + if (!cldrv->remove) { + dev->driver = NULL; + + return 0; + } + + return cldrv->remove(cldev); +} + +static ssize_t name_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + size_t len; + + len = snprintf(buf, PAGE_SIZE, "%s", cldev->name); + + return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; +} +static DEVICE_ATTR_RO(name); + +static ssize_t uuid_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); + size_t len; + + len = snprintf(buf, PAGE_SIZE, "%pUl", uuid); + + return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; +} +static DEVICE_ATTR_RO(uuid); + +static ssize_t modalias_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + 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)); + + return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; +} +static DEVICE_ATTR_RO(modalias); + +static struct attribute *mei_cl_dev_attrs[] = { + &dev_attr_name.attr, + &dev_attr_uuid.attr, + &dev_attr_modalias.attr, + NULL, +}; +ATTRIBUTE_GROUPS(mei_cl_dev); + +static int mei_cl_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); + + if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid)) + return -ENOMEM; + + 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))) + return -ENOMEM; + + return 0; +} + +static struct bus_type mei_cl_bus_type = { + .name = "mei", + .dev_groups = mei_cl_dev_groups, + .match = mei_cl_device_match, + .probe = mei_cl_device_probe, + .remove = mei_cl_device_remove, + .uevent = mei_cl_uevent, +}; + +static void mei_cl_dev_release(struct device *dev) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + + if (!cldev) return; - set_bit(MEI_CL_EVENT_RX, &cldev->events); + mei_me_cl_put(cldev->me_cl); + kfree(cldev); +} - schedule_work(&cldev->event_work); +static struct device_type mei_cl_device_type = { + .release = mei_cl_dev_release, +}; + +struct mei_cl *mei_cl_bus_find_cl_by_uuid(struct mei_device *bus, + uuid_le uuid) +{ + struct mei_cl *cl; + + list_for_each_entry(cl, &bus->device_list, device_link) { + if (cl->cldev && cl->cldev->me_cl && + !uuid_le_cmp(uuid, *mei_me_cl_uuid(cl->cldev->me_cl))) + return cl; + } + + return NULL; +} + +struct mei_cl_device *mei_cl_add_device(struct mei_device *bus, + struct mei_me_client *me_cl, + struct mei_cl *cl, + char *name) +{ + struct mei_cl_device *cldev; + int status; + + cldev = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); + if (!cldev) + return NULL; + + cldev->me_cl = mei_me_cl_get(me_cl); + if (!cldev->me_cl) { + kfree(cldev); + return NULL; + } + + cldev->cl = cl; + cldev->dev.parent = bus->dev; + cldev->dev.bus = &mei_cl_bus_type; + cldev->dev.type = &mei_cl_device_type; + + strlcpy(cldev->name, name, sizeof(cldev->name)); + + dev_set_name(&cldev->dev, "mei:%s:%pUl", name, mei_me_cl_uuid(me_cl)); + + status = device_register(&cldev->dev); + if (status) { + dev_err(bus->dev, "Failed to register MEI device\n"); + mei_me_cl_put(cldev->me_cl); + kfree(cldev); + return NULL; + } + + cl->cldev = cldev; + + dev_dbg(&cldev->dev, "client %s registered\n", name); + + return cldev; +} +EXPORT_SYMBOL_GPL(mei_cl_add_device); + +void mei_cl_remove_device(struct mei_cl_device *cldev) +{ + device_unregister(&cldev->dev); +} +EXPORT_SYMBOL_GPL(mei_cl_remove_device); + +int __mei_cl_driver_register(struct mei_cl_driver *cldrv, struct module *owner) +{ + int err; + + cldrv->driver.name = cldrv->name; + cldrv->driver.owner = owner; + cldrv->driver.bus = &mei_cl_bus_type; + + err = driver_register(&cldrv->driver); + if (err) + return err; + + pr_debug("mei: driver [%s] registered\n", cldrv->driver.name); + + return 0; } +EXPORT_SYMBOL_GPL(__mei_cl_driver_register); + +void mei_cl_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); int __init mei_cl_bus_init(void) { |