From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753235AbbFMTNn (ORCPT ); Sat, 13 Jun 2015 15:13:43 -0400 Received: from mga01.intel.com ([192.55.52.88]:63169 "EHLO mga01.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751319AbbFMTM4 (ORCPT ); Sat, 13 Jun 2015 15:12:56 -0400 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.13,608,1427785200"; d="scan'208";a="710414581" From: Tomas Winkler To: gregkh@linuxfoundation.org Cc: arnd@arndb.de, linux-kernel@vger.kernel.org, Tomas Winkler Subject: [char-misc-next 05/17] mei: bus: move driver api functions at the start of the file Date: Sat, 13 Jun 2015 22:11:37 +0300 Message-Id: <1434222709-10966-6-git-send-email-tomas.winkler@intel.com> X-Mailer: git-send-email 2.4.2 In-Reply-To: <1434222709-10966-1-git-send-email-tomas.winkler@intel.com> References: <1434222709-10966-1-git-send-email-tomas.winkler@intel.com> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org To make the file more organize move mei client driver api to the start of the file and add Kdoc There are no functional changes in this patch Signed-off-by: Tomas Winkler --- drivers/misc/mei/bus.c | 798 +++++++++++++++++++++++++++---------------------- 1 file changed, 443 insertions(+), 355 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 2ae7e521f029..b14662787d7e 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -30,527 +30,615 @@ #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) +/** + * __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) { - 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; + struct mei_device *bus; + struct mei_cl_cb *cb = NULL; + ssize_t rets; - uuid = mei_me_cl_uuid(cldev->me_cl); - name = cldev->name; + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; - if (!cldrv || !cldrv->id_table) - return 0; + bus = cl->dev; - id = cldrv->id_table; + mutex_lock(&bus->device_lock); + if (!mei_cl_is_connected(cl)) { + rets = -ENODEV; + goto out; + } - while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) { + /* Check if we have an ME client device */ + if (!mei_me_cl_is_active(cl->me_cl)) { + rets = -ENOTTY; + goto out; + } - if (!uuid_le_cmp(*uuid, id->uuid)) { - if (id->name[0]) { - if (!strncmp(name, id->name, sizeof(id->name))) - return 1; - } else { - return 1; - } - } + if (length > mei_cl_mtu(cl)) { + rets = -EFBIG; + goto out; + } - id++; + cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL); + if (!cb) { + rets = -ENOMEM; + goto out; } - return 0; + memcpy(cb->buf.data, buf, length); + + rets = mei_cl_write(cl, cb, blocking); + +out: + mutex_unlock(&bus->device_lock); + if (rets < 0) + mei_io_cb_free(cb); + + return rets; } -static int mei_cl_device_probe(struct device *dev) +/** + * __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_cl_device *cldev = to_mei_cl_device(dev); - struct mei_cl_driver *cldrv; - struct mei_cl_device_id id; - - if (!cldev) - return 0; + struct mei_device *bus; + struct mei_cl_cb *cb; + size_t r_length; + ssize_t rets; - cldrv = to_mei_cl_driver(dev->driver); - if (!cldrv || !cldrv->probe) + if (WARN_ON(!cl || !cl->dev)) return -ENODEV; - dev_dbg(dev, "Device probe\n"); + bus = cl->dev; - strlcpy(id.name, cldev->name, sizeof(id.name)); + mutex_lock(&bus->device_lock); - return cldrv->probe(cldev, &id); -} + cb = mei_cl_read_cb(cl, NULL); + if (cb) + goto copy; -static int mei_cl_device_remove(struct device *dev) -{ - struct mei_cl_device *cldev = to_mei_cl_device(dev); - struct mei_cl_driver *cldrv; + rets = mei_cl_read_start(cl, length, NULL); + if (rets && rets != -EBUSY) + goto out; - if (!cldev || !dev->driver) - return 0; + if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) { - if (cldev->event_cb) { - cldev->event_cb = NULL; - cancel_work_sync(&cldev->event_work); - } + mutex_unlock(&bus->device_lock); - cldrv = to_mei_cl_driver(dev->driver); - if (!cldrv->remove) { - dev->driver = NULL; + if (wait_event_interruptible(cl->rx_wait, + (!list_empty(&cl->rd_completed)) || + (!mei_cl_is_connected(cl)))) { - return 0; + if (signal_pending(current)) + return -EINTR; + return -ERESTARTSYS; + } + + mutex_lock(&bus->device_lock); + + if (!mei_cl_is_connected(cl)) { + rets = -EBUSY; + goto out; + } } - return cldrv->remove(cldev); -} + cb = mei_cl_read_cb(cl, NULL); + if (!cb) { + rets = 0; + goto out; + } -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; +copy: + if (cb->status) { + rets = cb->status; + goto free; + } - len = snprintf(buf, PAGE_SIZE, "%s", cldev->name); + r_length = min_t(size_t, length, cb->buf_idx); + memcpy(buf, cb->buf.data, r_length); + rets = r_length; - return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; +free: + mei_io_cb_free(cb); +out: + mutex_unlock(&bus->device_lock); + + return rets; } -static DEVICE_ATTR_RO(name); -static ssize_t uuid_show(struct device *dev, struct device_attribute *a, - char *buf) +/** + * 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_device *cldev = to_mei_cl_device(dev); - const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); - size_t len; + struct mei_cl *cl = cldev->cl; - len = snprintf(buf, PAGE_SIZE, "%pUl", uuid); + if (cl == NULL) + return -ENODEV; - return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; + return __mei_cl_send(cl, buf, length, 1); } -static DEVICE_ATTR_RO(uuid); +EXPORT_SYMBOL_GPL(mei_cl_send); -static ssize_t modalias_show(struct device *dev, struct device_attribute *a, - char *buf) +/** + * 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_device *cldev = to_mei_cl_device(dev); - const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); - size_t len; + struct mei_cl *cl = cldev->cl; - len = snprintf(buf, PAGE_SIZE, "mei:%s:" MEI_CL_UUID_FMT ":", - cldev->name, MEI_CL_UUID_ARGS(uuid->b)); + if (cl == NULL) + return -ENODEV; - return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; + return __mei_cl_recv(cl, buf, length); } -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); +EXPORT_SYMBOL_GPL(mei_cl_recv); -static int mei_cl_uevent(struct device *dev, struct kobj_uevent_env *env) +/** + * 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 = to_mei_cl_device(dev); - const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); + struct mei_cl_device *cldev; - if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid)) - return -ENOMEM; + cldev = container_of(work, struct mei_cl_device, event_work); - if (add_uevent_var(env, "MEI_CL_NAME=%s", cldev->name)) - return -ENOMEM; + if (cldev->event_cb) + cldev->event_cb(cldev, cldev->events, cldev->event_context); - if (add_uevent_var(env, "MODALIAS=mei:%s:" MEI_CL_UUID_FMT ":", - cldev->name, MEI_CL_UUID_ARGS(uuid->b))) - return -ENOMEM; + cldev->events = 0; - return 0; + /* Prepare for the next read */ + mei_cl_read_start(cldev->cl, 0, NULL); } -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) +/** + * 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 = to_mei_cl_device(dev); + struct mei_cl_device *cldev = cl->cldev; - if (!cldev) + if (!cldev || !cldev->event_cb) 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->devices, device_link) { - if (cl->cldev && cl->cldev->me_cl && - !uuid_le_cmp(uuid, *mei_me_cl_uuid(cl->cldev->me_cl))) - return cl; - } + set_bit(MEI_CL_EVENT_RX, &cldev->events); - return NULL; + schedule_work(&cldev->event_work); } -struct mei_cl_device *mei_cl_add_device(struct mei_device *bus, - struct mei_me_client *me_cl, - struct mei_cl *cl, - char *name) +/** + * 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) { - 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)); + if (cldev->event_cb) + return -EALREADY; - 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; - } + cldev->events = 0; + cldev->event_cb = event_cb; + cldev->event_context = context; + INIT_WORK(&cldev->event_work, mei_bus_event_work); - cl->cldev = cldev; + mei_cl_read_start(cldev->cl, 0, NULL); - dev_dbg(&cldev->dev, "client %s registered\n", name); + return 0; +} +EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); - return cldev; +/** + * 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_add_device); +EXPORT_SYMBOL_GPL(mei_cl_get_drvdata); -void mei_cl_remove_device(struct mei_cl_device *cldev) +/** + * 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) { - device_unregister(&cldev->dev); + dev_set_drvdata(&cldev->dev, data); } -EXPORT_SYMBOL_GPL(mei_cl_remove_device); +EXPORT_SYMBOL_GPL(mei_cl_set_drvdata); -int __mei_cl_driver_register(struct mei_cl_driver *cldrv, struct module *owner) +/** + * 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; + struct mei_device *bus; + struct mei_cl *cl = cldev->cl; - cldrv->driver.name = cldrv->name; - cldrv->driver.owner = owner; - cldrv->driver.bus = &mei_cl_bus_type; + if (cl == NULL) + return -ENODEV; - err = driver_register(&cldrv->driver); - if (err) - return err; + bus = cl->dev; - pr_debug("mei: driver [%s] registered\n", cldrv->driver.name); + mutex_lock(&bus->device_lock); - return 0; -} -EXPORT_SYMBOL_GPL(__mei_cl_driver_register); + if (mei_cl_is_connected(cl)) { + mutex_unlock(&bus->device_lock); + dev_warn(bus->dev, "Already connected"); + return -EBUSY; + } -void mei_cl_driver_unregister(struct mei_cl_driver *cldrv) -{ - driver_unregister(&cldrv->driver); + err = mei_cl_connect(cl, cldev->me_cl, NULL); + if (err < 0) { + mutex_unlock(&bus->device_lock); + dev_err(bus->dev, "Could not connect to the ME client"); - pr_debug("mei: driver [%s] unregistered\n", cldrv->driver.name); + return err; + } + + mutex_unlock(&bus->device_lock); + + if (cldev->event_cb) + mei_cl_read_start(cldev->cl, 0, NULL); + + return 0; } -EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); +EXPORT_SYMBOL_GPL(mei_cl_enable_device); -ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, - bool blocking) +/** + * 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; struct mei_device *bus; - struct mei_cl_cb *cb = NULL; - ssize_t rets; + struct mei_cl *cl = cldev->cl; - if (WARN_ON(!cl || !cl->dev)) + if (cl == NULL) return -ENODEV; bus = cl->dev; - mutex_lock(&bus->device_lock); - if (!mei_cl_is_connected(cl)) { - rets = -ENODEV; - goto out; - } + cldev->event_cb = NULL; - /* Check if we have an ME client device */ - if (!mei_me_cl_is_active(cl->me_cl)) { - rets = -ENOTTY; - goto out; - } + mutex_lock(&bus->device_lock); - if (length > mei_cl_mtu(cl)) { - rets = -EFBIG; + if (!mei_cl_is_connected(cl)) { + dev_err(bus->dev, "Already disconnected"); + err = 0; goto out; } - cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL); - if (!cb) { - rets = -ENOMEM; + err = mei_cl_disconnect(cl); + if (err < 0) { + dev_err(bus->dev, "Could not disconnect from the ME client"); goto out; } - memcpy(cb->buf.data, buf, length); - - rets = mei_cl_write(cl, cb, blocking); + /* Flush queues and remove any pending read */ + mei_cl_flush_queues(cl, NULL); out: mutex_unlock(&bus->device_lock); - if (rets < 0) - mei_io_cb_free(cb); + return err; - return rets; } +EXPORT_SYMBOL_GPL(mei_cl_disable_device); -ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) +static int mei_cl_device_match(struct device *dev, struct device_driver *drv) { - struct mei_device *bus; - struct mei_cl_cb *cb; - size_t r_length; - ssize_t rets; + 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 (WARN_ON(!cl || !cl->dev)) - return -ENODEV; + if (!cldev) + return 0; - bus = cl->dev; + uuid = mei_me_cl_uuid(cldev->me_cl); + name = cldev->name; - mutex_lock(&bus->device_lock); + if (!cldrv || !cldrv->id_table) + return 0; - cb = mei_cl_read_cb(cl, NULL); - if (cb) - goto copy; + id = cldrv->id_table; - rets = mei_cl_read_start(cl, length, NULL); - if (rets && rets != -EBUSY) - goto out; + while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) { - if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) { + if (!uuid_le_cmp(*uuid, id->uuid)) { + if (id->name[0]) { + if (!strncmp(name, id->name, sizeof(id->name))) + return 1; + } else { + return 1; + } + } - mutex_unlock(&bus->device_lock); + id++; + } - if (wait_event_interruptible(cl->rx_wait, - (!list_empty(&cl->rd_completed)) || - (!mei_cl_is_connected(cl)))) { + return 0; +} - if (signal_pending(current)) - return -EINTR; - return -ERESTARTSYS; - } +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; - mutex_lock(&bus->device_lock); + if (!cldev) + return 0; - if (!mei_cl_is_connected(cl)) { - rets = -EBUSY; - goto out; - } - } + 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); +} - cb = mei_cl_read_cb(cl, NULL); - if (!cb) { - rets = 0; - goto out; - } +static int mei_cl_device_remove(struct device *dev) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + struct mei_cl_driver *cldrv; -copy: - if (cb->status) { - rets = cb->status; - goto free; + if (!cldev || !dev->driver) + return 0; + + if (cldev->event_cb) { + cldev->event_cb = NULL; + cancel_work_sync(&cldev->event_work); } - r_length = min_t(size_t, length, cb->buf_idx); - memcpy(buf, cb->buf.data, r_length); - rets = r_length; + cldrv = to_mei_cl_driver(dev->driver); + if (!cldrv->remove) { + dev->driver = NULL; -free: - mei_io_cb_free(cb); -out: - mutex_unlock(&bus->device_lock); + return 0; + } - return rets; + return cldrv->remove(cldev); } -ssize_t mei_cl_send(struct mei_cl_device *cldev, u8 *buf, size_t length) +static ssize_t name_show(struct device *dev, struct device_attribute *a, + char *buf) { - struct mei_cl *cl = cldev->cl; + struct mei_cl_device *cldev = to_mei_cl_device(dev); + size_t len; - if (cl == NULL) - return -ENODEV; + len = snprintf(buf, PAGE_SIZE, "%s", cldev->name); - return __mei_cl_send(cl, buf, length, 1); + return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; } -EXPORT_SYMBOL_GPL(mei_cl_send); +static DEVICE_ATTR_RO(name); -ssize_t mei_cl_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) +static ssize_t uuid_show(struct device *dev, struct device_attribute *a, + char *buf) { - struct mei_cl *cl = cldev->cl; + struct mei_cl_device *cldev = to_mei_cl_device(dev); + const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); + size_t len; - if (cl == NULL) - return -ENODEV; + len = snprintf(buf, PAGE_SIZE, "%pUl", uuid); - return __mei_cl_recv(cl, buf, length); + return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; } -EXPORT_SYMBOL_GPL(mei_cl_recv); +static DEVICE_ATTR_RO(uuid); -static void mei_bus_event_work(struct work_struct *work) +static ssize_t modalias_show(struct device *dev, struct device_attribute *a, + char *buf) { - struct mei_cl_device *cldev; - - cldev = container_of(work, struct mei_cl_device, event_work); - - if (cldev->event_cb) - cldev->event_cb(cldev, cldev->events, cldev->event_context); + struct mei_cl_device *cldev = to_mei_cl_device(dev); + const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); + size_t len; - cldev->events = 0; + len = snprintf(buf, PAGE_SIZE, "mei:%s:" MEI_CL_UUID_FMT ":", + cldev->name, MEI_CL_UUID_ARGS(uuid->b)); - /* Prepare for the next read */ - mei_cl_read_start(cldev->cl, 0, NULL); + return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; } +static DEVICE_ATTR_RO(modalias); -int mei_cl_register_event_cb(struct mei_cl_device *cldev, - mei_cl_event_cb_t event_cb, void *context) +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) { - if (cldev->event_cb) - return -EALREADY; + struct mei_cl_device *cldev = to_mei_cl_device(dev); + const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); - cldev->events = 0; - cldev->event_cb = event_cb; - cldev->event_context = context; - INIT_WORK(&cldev->event_work, mei_bus_event_work); + if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid)) + return -ENOMEM; - mei_cl_read_start(cldev->cl, 0, NULL); + 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; } -EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); -void *mei_cl_get_drvdata(const struct mei_cl_device *cldev) +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) { - return dev_get_drvdata(&cldev->dev); + struct mei_cl_device *cldev = to_mei_cl_device(dev); + + if (!cldev) + return; + + mei_me_cl_put(cldev->me_cl); + kfree(cldev); } -EXPORT_SYMBOL_GPL(mei_cl_get_drvdata); -void mei_cl_set_drvdata(struct mei_cl_device *cldev, void *data) +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) { - dev_set_drvdata(&cldev->dev, data); + struct mei_cl *cl; + + list_for_each_entry(cl, &bus->devices, 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; } -EXPORT_SYMBOL_GPL(mei_cl_set_drvdata); -int mei_cl_enable_device(struct mei_cl_device *cldev) +struct mei_cl_device *mei_cl_add_device(struct mei_device *bus, + struct mei_me_client *me_cl, + struct mei_cl *cl, + char *name) { - int err; - struct mei_device *bus; - struct mei_cl *cl = cldev->cl; + struct mei_cl_device *cldev; + int status; - if (cl == NULL) - return -ENODEV; + cldev = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); + if (!cldev) + return NULL; - bus = cl->dev; + cldev->me_cl = mei_me_cl_get(me_cl); + if (!cldev->me_cl) { + kfree(cldev); + return NULL; + } - mutex_lock(&bus->device_lock); + cldev->cl = cl; + cldev->dev.parent = bus->dev; + cldev->dev.bus = &mei_cl_bus_type; + cldev->dev.type = &mei_cl_device_type; - if (mei_cl_is_connected(cl)) { - mutex_unlock(&bus->device_lock); - dev_warn(bus->dev, "Already connected"); - return -EBUSY; - } + strlcpy(cldev->name, name, sizeof(cldev->name)); - err = mei_cl_connect(cl, cldev->me_cl, NULL); - if (err < 0) { - mutex_unlock(&bus->device_lock); - dev_err(bus->dev, "Could not connect to the ME client"); + dev_set_name(&cldev->dev, "mei:%s:%pUl", name, mei_me_cl_uuid(me_cl)); - return err; + 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; } - mutex_unlock(&bus->device_lock); + cl->cldev = cldev; - if (cldev->event_cb) - mei_cl_read_start(cldev->cl, 0, NULL); + dev_dbg(&cldev->dev, "client %s registered\n", name); - return 0; + return cldev; } -EXPORT_SYMBOL_GPL(mei_cl_enable_device); +EXPORT_SYMBOL_GPL(mei_cl_add_device); -int mei_cl_disable_device(struct mei_cl_device *cldev) +void mei_cl_remove_device(struct mei_cl_device *cldev) { - int err; - struct mei_device *bus; - struct mei_cl *cl = cldev->cl; - - if (cl == NULL) - return -ENODEV; - - bus = cl->dev; - - cldev->event_cb = NULL; - - mutex_lock(&bus->device_lock); + device_unregister(&cldev->dev); +} +EXPORT_SYMBOL_GPL(mei_cl_remove_device); - if (!mei_cl_is_connected(cl)) { - dev_err(bus->dev, "Already disconnected"); - err = 0; - goto out; - } +int __mei_cl_driver_register(struct mei_cl_driver *cldrv, struct module *owner) +{ + int err; - err = mei_cl_disconnect(cl); - if (err < 0) { - dev_err(bus->dev, "Could not disconnect from the ME client"); - goto out; - } + cldrv->driver.name = cldrv->name; + cldrv->driver.owner = owner; + cldrv->driver.bus = &mei_cl_bus_type; - /* Flush queues and remove any pending read */ - mei_cl_flush_queues(cl, NULL); + err = driver_register(&cldrv->driver); + if (err) + return err; -out: - mutex_unlock(&bus->device_lock); - return err; + pr_debug("mei: driver [%s] registered\n", cldrv->driver.name); + return 0; } -EXPORT_SYMBOL_GPL(mei_cl_disable_device); +EXPORT_SYMBOL_GPL(__mei_cl_driver_register); -void mei_cl_bus_rx_event(struct mei_cl *cl) +void mei_cl_driver_unregister(struct mei_cl_driver *cldrv) { - struct mei_cl_device *cldev = cl->cldev; - - if (!cldev || !cldev->event_cb) - return; - - set_bit(MEI_CL_EVENT_RX, &cldev->events); + driver_unregister(&cldrv->driver); - schedule_work(&cldev->event_work); + pr_debug("mei: driver [%s] unregistered\n", cldrv->driver.name); } +EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); void mei_cl_bus_remove_devices(struct mei_device *bus) { -- 2.4.2