diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/misc/mei/bus-fixup.c | 288 | ||||
-rw-r--r-- | drivers/misc/mei/bus.c | 213 | ||||
-rw-r--r-- | drivers/misc/mei/client.c | 9 | ||||
-rw-r--r-- | drivers/misc/mei/init.c | 2 | ||||
-rw-r--r-- | drivers/misc/mei/mei_dev.h | 11 |
5 files changed, 213 insertions, 310 deletions
diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index a4c6719d36b4..3e536ca85f7d 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -30,6 +30,11 @@ #define MEI_UUID_NFC_INFO UUID_LE(0xd2de1625, 0x382d, 0x417d, \ 0x48, 0xa4, 0xef, 0xab, 0xba, 0x8a, 0x12, 0x06) +static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO; + +#define MEI_UUID_NFC_HCI UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, \ + 0x94, 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c) + #define MEI_UUID_ANY NULL_UUID_LE /** @@ -93,68 +98,10 @@ struct mei_nfc_if_version { u8 radio_type; } __packed; -struct mei_nfc_connect { - u8 fw_ivn; - u8 vendor_id; -} __packed; - -struct mei_nfc_connect_resp { - u8 fw_ivn; - u8 vendor_id; - u16 me_major; - u16 me_minor; - u16 me_hotfix; - u16 me_build; -} __packed; - -struct mei_nfc_hci_hdr { - u8 cmd; - u8 status; - u16 req_id; - u32 reserved; - u16 data_size; -} __packed; #define MEI_NFC_CMD_MAINTENANCE 0x00 -#define MEI_NFC_CMD_HCI_SEND 0x01 -#define MEI_NFC_CMD_HCI_RECV 0x02 - -#define MEI_NFC_SUBCMD_CONNECT 0x00 #define MEI_NFC_SUBCMD_IF_VERSION 0x01 -#define MEI_NFC_HEADER_SIZE 10 - -/** - * struct mei_nfc_dev - NFC mei device - * - * @me_cl: NFC me client - * @cl: NFC host client - * @cl_info: NFC info host client - * @init_work: perform connection to the info client - * @fw_ivn: NFC Interface Version Number - * @vendor_id: NFC manufacturer ID - * @radio_type: NFC radio type - * @bus_name: bus name - * - */ -struct mei_nfc_dev { - struct mei_me_client *me_cl; - struct mei_cl *cl; - struct mei_cl *cl_info; - struct work_struct init_work; - u8 fw_ivn; - u8 vendor_id; - u8 radio_type; - const char *bus_name; -}; - -/* UUIDs for NFC F/W clients */ -const uuid_le mei_nfc_guid = UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, - 0x94, 0xd4, 0x50, 0x26, - 0x67, 0x23, 0x77, 0x5c); - -static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO; - /* Vendors */ #define MEI_NFC_VENDOR_INSIDE 0x00 #define MEI_NFC_VENDOR_NXP 0x01 @@ -163,27 +110,6 @@ static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO; #define MEI_NFC_VENDOR_INSIDE_UREAD 0x00 #define MEI_NFC_VENDOR_NXP_PN544 0x01 -static void mei_nfc_free(struct mei_nfc_dev *ndev) -{ - if (!ndev) - return; - - if (ndev->cl) { - list_del(&ndev->cl->device_link); - mei_cl_unlink(ndev->cl); - kfree(ndev->cl); - } - - if (ndev->cl_info) { - list_del(&ndev->cl_info->device_link); - mei_cl_unlink(ndev->cl_info); - kfree(ndev->cl_info); - } - - mei_me_cl_put(ndev->me_cl); - kfree(ndev); -} - /** * mei_nfc_if_version - get NFC interface version * @@ -264,177 +190,86 @@ static const char *mei_nfc_radio_name(struct mei_nfc_if_version *ver) return NULL; } -static void mei_nfc_init(struct work_struct *work) +/** + * mei_nfc - The nfc fixup function. The function retrieves nfc radio + * name and set is as device attribute so we can load + * the proper device driver for it + * + * @cldev: me client device (nfc) + */ +static void mei_nfc(struct mei_cl_device *cldev) { struct mei_device *bus; - struct mei_cl_device *cldev; - struct mei_nfc_dev *ndev; - struct mei_cl *cl_info; - struct mei_me_client *me_cl_info; - struct mei_nfc_if_version version; + struct mei_cl *cl; + struct mei_me_client *me_cl = NULL; + struct mei_nfc_if_version ver; + const char *radio_name = NULL; + int ret; - ndev = container_of(work, struct mei_nfc_dev, init_work); + bus = cldev->bus; - cl_info = ndev->cl_info; - bus = cl_info->dev; + dev_dbg(bus->dev, "running hook %s: %pUl match=%d\n", + __func__, mei_me_cl_uuid(cldev->me_cl), cldev->do_match); mutex_lock(&bus->device_lock); - - /* check for valid client id */ - me_cl_info = mei_me_cl_by_uuid(bus, &mei_nfc_info_guid); - if (!me_cl_info) { - mutex_unlock(&bus->device_lock); - dev_info(bus->dev, "nfc: failed to find the info client\n"); - goto err; - } - - if (mei_cl_connect(cl_info, me_cl_info, NULL) < 0) { - mei_me_cl_put(me_cl_info); - mutex_unlock(&bus->device_lock); - dev_err(bus->dev, "Could not connect to the NFC INFO ME client"); - - goto err; + /* we need to connect to INFO GUID */ + cl = mei_cl_alloc_linked(bus, MEI_HOST_CLIENT_ID_ANY); + if (IS_ERR(cl)) { + ret = PTR_ERR(cl); + cl = NULL; + dev_err(bus->dev, "nfc hook alloc failed %d\n", ret); + goto out; } - mei_me_cl_put(me_cl_info); - mutex_unlock(&bus->device_lock); - if (mei_nfc_if_version(cl_info, &version) < 0) { - dev_err(bus->dev, "Could not get the NFC interface version"); - - goto err; + me_cl = mei_me_cl_by_uuid(bus, &mei_nfc_info_guid); + if (!me_cl) { + ret = -ENOTTY; + dev_err(bus->dev, "Cannot find nfc info %d\n", ret); + goto out; } - ndev->fw_ivn = version.fw_ivn; - ndev->vendor_id = version.vendor_id; - ndev->radio_type = version.radio_type; - - dev_info(bus->dev, "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n", - ndev->fw_ivn, ndev->vendor_id, ndev->radio_type); - - mutex_lock(&bus->device_lock); - - if (mei_cl_disconnect(cl_info) < 0) { - mutex_unlock(&bus->device_lock); - dev_err(bus->dev, "Could not disconnect the NFC INFO ME client"); - - goto err; + ret = mei_cl_connect(cl, me_cl, NULL); + if (ret < 0) { + dev_err(&cldev->dev, "Can't connect to the NFC INFO ME ret = %d\n", + ret); + goto out; } mutex_unlock(&bus->device_lock); - ndev->bus_name = mei_nfc_radio_name(&version); + ret = mei_nfc_if_version(cl, &ver); + if (ret) + goto disconnect; - if (!ndev->bus_name) { - dev_err(bus->dev, "Could not build the bus ID name\n"); - return; - } + radio_name = mei_nfc_radio_name(&ver); - cldev = mei_cl_add_device(bus, ndev->me_cl, ndev->cl, - ndev->bus_name); - if (!cldev) { - dev_err(bus->dev, "Could not add the NFC device to the MEI bus\n"); - - goto err; + if (!radio_name) { + ret = -ENOENT; + dev_err(&cldev->dev, "Can't get the NFC interface version ret = %d\n", + ret); + goto disconnect; } - cldev->priv_data = ndev; - - - return; + dev_dbg(bus->dev, "nfc radio %s\n", radio_name); + strlcpy(cldev->name, radio_name, sizeof(cldev->name)); -err: +disconnect: mutex_lock(&bus->device_lock); - mei_nfc_free(ndev); - mutex_unlock(&bus->device_lock); + if (mei_cl_disconnect(cl) < 0) + dev_err(bus->dev, "Can't disconnect the NFC INFO ME\n"); -} + mei_cl_flush_queues(cl, NULL); +out: + mei_cl_unlink(cl); + mutex_unlock(&bus->device_lock); + mei_me_cl_put(me_cl); + kfree(cl); -int mei_nfc_host_init(struct mei_device *bus, struct mei_me_client *me_cl) -{ - struct mei_nfc_dev *ndev; - struct mei_cl *cl_info, *cl; - int ret; - - - /* in case of internal reset bail out - * as the device is already setup - */ - cl = mei_cl_bus_find_cl_by_uuid(bus, mei_nfc_guid); - if (cl) - return 0; - - ndev = kzalloc(sizeof(struct mei_nfc_dev), GFP_KERNEL); - if (!ndev) { - ret = -ENOMEM; - goto err; - } - - ndev->me_cl = mei_me_cl_get(me_cl); - if (!ndev->me_cl) { - ret = -ENODEV; - goto err; - } - - cl_info = mei_cl_alloc_linked(bus, MEI_HOST_CLIENT_ID_ANY); - if (IS_ERR(cl_info)) { - ret = PTR_ERR(cl_info); - goto err; - } - - list_add_tail(&cl_info->device_link, &bus->device_list); - - ndev->cl_info = cl_info; - - cl = mei_cl_alloc_linked(bus, MEI_HOST_CLIENT_ID_ANY); - if (IS_ERR(cl)) { - ret = PTR_ERR(cl); - goto err; - } - - list_add_tail(&cl->device_link, &bus->device_list); - - ndev->cl = cl; - - INIT_WORK(&ndev->init_work, mei_nfc_init); - schedule_work(&ndev->init_work); - - return 0; - -err: - mei_nfc_free(ndev); - - return ret; -} - -void mei_nfc_host_exit(struct mei_device *bus) -{ - struct mei_nfc_dev *ndev; - struct mei_cl *cl; - struct mei_cl_device *cldev; - - cl = mei_cl_bus_find_cl_by_uuid(bus, mei_nfc_guid); - if (!cl) - return; - - cldev = cl->cldev; - if (!cldev) - return; - - ndev = (struct mei_nfc_dev *)cldev->priv_data; - if (ndev) - cancel_work_sync(&ndev->init_work); - - cldev->priv_data = NULL; - - /* Need to remove the device here - * since mei_nfc_free will unlink the clients - */ - mei_cl_remove_device(cldev); + if (ret) + cldev->do_match = 0; - mutex_lock(&bus->device_lock); - mei_nfc_free(ndev); - mutex_unlock(&bus->device_lock); + dev_dbg(bus->dev, "end of fixup match = %d\n", cldev->do_match); } #define MEI_FIXUP(_uuid, _hook) { _uuid, _hook } @@ -446,6 +281,7 @@ static struct mei_fixup { } mei_fixups[] = { MEI_FIXUP(MEI_UUID_ANY, number_of_connections), MEI_FIXUP(MEI_UUID_NFC_INFO, blacklist), + MEI_FIXUP(MEI_UUID_NFC_HCI, mei_nfc), }; /** diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 7e51700515f4..3ab08e522fb8 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -309,34 +309,43 @@ EXPORT_SYMBOL_GPL(mei_cl_set_drvdata); */ int mei_cl_enable_device(struct mei_cl_device *cldev) { - int err; - struct mei_device *bus; - struct mei_cl *cl = cldev->cl; + struct mei_device *bus = cldev->bus; + struct mei_cl *cl; + int ret; - if (cl == NULL) - return -ENODEV; + cl = cldev->cl; - bus = cl->dev; + if (!cl) { + mutex_lock(&bus->device_lock); + cl = mei_cl_alloc_linked(bus, MEI_HOST_CLIENT_ID_ANY); + mutex_unlock(&bus->device_lock); + if (IS_ERR(cl)) + return PTR_ERR(cl); + /* update pointers */ + cldev->cl = cl; + cl->cldev = cldev; + } mutex_lock(&bus->device_lock); - if (mei_cl_is_connected(cl)) { - mutex_unlock(&bus->device_lock); - dev_warn(bus->dev, "Already connected"); - return -EBUSY; + ret = 0; + goto out; } - 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"); - - return err; + if (!mei_me_cl_is_active(cldev->me_cl)) { + dev_err(&cldev->dev, "me client is not active\n"); + ret = -ENOTTY; + goto out; } + ret = mei_cl_connect(cl, cldev->me_cl, NULL); + if (ret < 0) + dev_err(&cldev->dev, "cannot connect\n"); + +out: mutex_unlock(&bus->device_lock); - return 0; + return ret; } EXPORT_SYMBOL_GPL(mei_cl_enable_device); @@ -350,14 +359,16 @@ EXPORT_SYMBOL_GPL(mei_cl_enable_device); */ int mei_cl_disable_device(struct mei_cl_device *cldev) { - int err; struct mei_device *bus; - struct mei_cl *cl = cldev->cl; + struct mei_cl *cl; + int err; - if (cl == NULL) + if (!cldev || !cldev->cl) return -ENODEV; - bus = cl->dev; + cl = cldev->cl; + + bus = cldev->bus; cldev->event_cb = NULL; @@ -370,18 +381,19 @@ int mei_cl_disable_device(struct mei_cl_device *cldev) } err = mei_cl_disconnect(cl); - if (err < 0) { + if (err < 0) dev_err(bus->dev, "Could not disconnect from the ME client"); - goto out; - } +out: /* Flush queues and remove any pending read */ mei_cl_flush_queues(cl, NULL); + mei_cl_unlink(cl); + + kfree(cl); + cldev->cl = NULL; -out: mutex_unlock(&bus->device_lock); return err; - } EXPORT_SYMBOL_GPL(mei_cl_disable_device); @@ -623,20 +635,6 @@ 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; -} - /** * mei_cl_dev_alloc - initialize and allocate mei client device * @@ -707,45 +705,127 @@ static int mei_cl_bus_dev_add(struct mei_cl_device *cldev) return ret; } -struct mei_cl_device *mei_cl_add_device(struct mei_device *bus, - struct mei_me_client *me_cl, - struct mei_cl *cl, - const char *name) +/** + * mei_cl_bus_dev_stop - stop the driver + * + * @cldev: me client device + */ +static void mei_cl_bus_dev_stop(struct mei_cl_device *cldev) +{ + if (cldev->is_added) + device_release_driver(&cldev->dev); +} + +/** + * mei_cl_bus_dev_destroy - destroy me client devices object + * + * @cldev: me client device + */ +static void mei_cl_bus_dev_destroy(struct mei_cl_device *cldev) +{ + 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); +} + +/** + * mei_cl_bus_remove_device - remove a devices form the bus + * + * @cldev: me client device + */ +static void mei_cl_bus_remove_device(struct mei_cl_device *cldev) +{ + mei_cl_bus_dev_stop(cldev); + mei_cl_bus_dev_destroy(cldev); +} + +/** + * mei_cl_bus_remove_devices - remove all devices form the bus + * + * @bus: mei device + */ +void mei_cl_bus_remove_devices(struct mei_device *bus) +{ + struct mei_cl_device *cldev, *next; + + list_for_each_entry_safe(cldev, next, &bus->device_list, bus_list) + mei_cl_bus_remove_device(cldev); +} + + +/** + * mei_cl_dev_init - allocate and initializes an mei client devices + * based on me client + * + * @bus: mei device + * @me_cl: me client + */ +static void mei_cl_dev_init(struct mei_device *bus, struct mei_me_client *me_cl) { struct mei_cl_device *cldev; - int status; + + 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); if (!cldev) - return NULL; + return; - cldev->cl = cl; - strlcpy(cldev->name, name, sizeof(cldev->name)); + 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); - mei_cl_dev_setup(bus, cldev); +} - status = mei_cl_bus_dev_add(cldev); - if (status) { - dev_err(bus->dev, "Failed to register MEI device\n"); - mei_me_cl_put(cldev->me_cl); - mei_dev_bus_put(bus); - kfree(cldev); - return NULL; - } +/** + * mei_cl_bus_rescan - scan me clients list and add create + * devices for eligible clients + * + * @bus: mei device + */ +void mei_cl_bus_rescan(struct mei_device *bus) +{ + struct mei_cl_device *cldev, *n; + struct mei_me_client *me_cl; - cl->cldev = cldev; + down_read(&bus->me_clients_rwsem); + list_for_each_entry(me_cl, &bus->me_clients, list) + mei_cl_dev_init(bus, me_cl); + up_read(&bus->me_clients_rwsem); - dev_dbg(&cldev->dev, "client %s registered\n", name); + mutex_lock(&bus->cl_bus_lock); + list_for_each_entry_safe(cldev, n, &bus->device_list, bus_list) { - return cldev; -} -EXPORT_SYMBOL_GPL(mei_cl_add_device); + if (!mei_me_cl_is_active(cldev->me_cl)) { + mei_cl_bus_remove_device(cldev); + continue; + } -void mei_cl_remove_device(struct mei_cl_device *cldev) -{ - device_unregister(&cldev->dev); + if (cldev->is_added) + continue; + + if (mei_cl_dev_setup(bus, cldev)) + mei_cl_bus_dev_add(cldev); + else { + list_del_init(&cldev->bus_list); + put_device(&cldev->dev); + } + } + mutex_unlock(&bus->cl_bus_lock); + + dev_dbg(bus->dev, "rescan end"); } -EXPORT_SYMBOL_GPL(mei_cl_remove_device); int __mei_cl_driver_register(struct mei_cl_driver *cldrv, struct module *owner) { @@ -773,6 +853,7 @@ void mei_cl_driver_unregister(struct mei_cl_driver *cldrv) } EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); + int __init mei_cl_bus_init(void) { return bus_register(&mei_cl_bus_type); diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 1987a201c7f3..9dacea7a9a60 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -558,7 +558,6 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) INIT_LIST_HEAD(&cl->rd_completed); INIT_LIST_HEAD(&cl->rd_pending); INIT_LIST_HEAD(&cl->link); - INIT_LIST_HEAD(&cl->device_link); cl->writing_state = MEI_IDLE; cl->state = MEI_FILE_INITIALIZING; cl->dev = dev; @@ -690,16 +689,12 @@ void mei_host_client_init(struct work_struct *work) mei_wd_host_init(dev, me_cl); mei_me_cl_put(me_cl); - me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_guid); - if (me_cl) - mei_nfc_host_init(dev, me_cl); - mei_me_cl_put(me_cl); - - dev->dev_state = MEI_DEV_ENABLED; dev->reset_count = 0; mutex_unlock(&dev->device_lock); + mei_cl_bus_rescan(dev); + pm_runtime_mark_last_busy(dev->dev); dev_dbg(dev->dev, "rpm: autosuspend\n"); pm_runtime_autosuspend(dev->dev); diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 15000e9231b1..e374661652cd 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -331,7 +331,7 @@ void mei_stop(struct mei_device *dev) mei_cancel_work(dev); - mei_nfc_host_exit(dev); + mei_cl_bus_remove_devices(dev); mutex_lock(&dev->device_lock); diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 7098dcaccc96..71c55f4cabb4 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -241,7 +241,6 @@ struct mei_cl_cb { * @rd_completed: completed read * * @cldev: device on the mei client bus - * @device_link: link to bus clients */ struct mei_cl { struct list_head link; @@ -260,9 +259,7 @@ struct mei_cl { struct list_head rd_pending; struct list_head rd_completed; - /* MEI CL bus data */ struct mei_cl_device *cldev; - struct list_head device_link; }; /** struct mei_hw_ops @@ -329,12 +326,7 @@ struct mei_hw_ops { }; /* MEI bus API*/ - -struct mei_cl_device *mei_cl_add_device(struct mei_device *bus, - struct mei_me_client *me_cl, - struct mei_cl *cl, - const char *name); -void mei_cl_remove_device(struct mei_cl_device *cldev); +void mei_cl_bus_rescan(struct mei_device *bus); void mei_cl_dev_fixup(struct mei_cl_device *dev); ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bool blocking); @@ -343,7 +335,6 @@ void mei_cl_bus_rx_event(struct mei_cl *cl); void mei_cl_bus_remove_devices(struct mei_device *bus); int mei_cl_bus_init(void); void mei_cl_bus_exit(void); -struct mei_cl *mei_cl_bus_find_cl_by_uuid(struct mei_device *bus, uuid_le uuid); /** * enum mei_pg_event - power gating transition events |