1e5354107SSamuel Ortiz /* 2e5354107SSamuel Ortiz * Intel Management Engine Interface (Intel MEI) Linux driver 3e5354107SSamuel Ortiz * Copyright (c) 2012-2013, Intel Corporation. 4e5354107SSamuel Ortiz * 5e5354107SSamuel Ortiz * This program is free software; you can redistribute it and/or modify it 6e5354107SSamuel Ortiz * under the terms and conditions of the GNU General Public License, 7e5354107SSamuel Ortiz * version 2, as published by the Free Software Foundation. 8e5354107SSamuel Ortiz * 9e5354107SSamuel Ortiz * This program is distributed in the hope it will be useful, but WITHOUT 10e5354107SSamuel Ortiz * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11e5354107SSamuel Ortiz * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12e5354107SSamuel Ortiz * more details. 13e5354107SSamuel Ortiz * 14e5354107SSamuel Ortiz */ 15e5354107SSamuel Ortiz 16e5354107SSamuel Ortiz #include <linux/module.h> 17e5354107SSamuel Ortiz #include <linux/device.h> 18e5354107SSamuel Ortiz #include <linux/kernel.h> 193e833295SSamuel Ortiz #include <linux/sched.h> 20e5354107SSamuel Ortiz #include <linux/init.h> 21e5354107SSamuel Ortiz #include <linux/errno.h> 22e5354107SSamuel Ortiz #include <linux/slab.h> 23e5354107SSamuel Ortiz #include <linux/mutex.h> 24e5354107SSamuel Ortiz #include <linux/interrupt.h> 25e5354107SSamuel Ortiz #include <linux/pci.h> 26e5354107SSamuel Ortiz #include <linux/mei_cl_bus.h> 27e5354107SSamuel Ortiz 28e5354107SSamuel Ortiz #include "mei_dev.h" 293e833295SSamuel Ortiz #include "hw-me.h" 303e833295SSamuel Ortiz #include "client.h" 31e5354107SSamuel Ortiz 32e5354107SSamuel Ortiz #define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver) 33e5354107SSamuel Ortiz #define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev) 34e5354107SSamuel Ortiz 35e5354107SSamuel Ortiz static int mei_cl_device_match(struct device *dev, struct device_driver *drv) 36e5354107SSamuel Ortiz { 37e5354107SSamuel Ortiz struct mei_cl_device *device = to_mei_cl_device(dev); 38e5354107SSamuel Ortiz struct mei_cl_driver *driver = to_mei_cl_driver(drv); 39e5354107SSamuel Ortiz const struct mei_cl_device_id *id; 40e5354107SSamuel Ortiz 41e5354107SSamuel Ortiz if (!device) 42e5354107SSamuel Ortiz return 0; 43e5354107SSamuel Ortiz 44e5354107SSamuel Ortiz if (!driver || !driver->id_table) 45e5354107SSamuel Ortiz return 0; 46e5354107SSamuel Ortiz 47e5354107SSamuel Ortiz id = driver->id_table; 48e5354107SSamuel Ortiz 49e5354107SSamuel Ortiz while (id->name[0]) { 50e5354107SSamuel Ortiz if (!strcmp(dev_name(dev), id->name)) 51e5354107SSamuel Ortiz return 1; 52e5354107SSamuel Ortiz 53e5354107SSamuel Ortiz id++; 54e5354107SSamuel Ortiz } 55e5354107SSamuel Ortiz 56e5354107SSamuel Ortiz return 0; 57e5354107SSamuel Ortiz } 58e5354107SSamuel Ortiz 59e5354107SSamuel Ortiz static int mei_cl_device_probe(struct device *dev) 60e5354107SSamuel Ortiz { 61e5354107SSamuel Ortiz struct mei_cl_device *device = to_mei_cl_device(dev); 62e5354107SSamuel Ortiz struct mei_cl_driver *driver; 63e5354107SSamuel Ortiz struct mei_cl_device_id id; 64e5354107SSamuel Ortiz 65e5354107SSamuel Ortiz if (!device) 66e5354107SSamuel Ortiz return 0; 67e5354107SSamuel Ortiz 68e5354107SSamuel Ortiz driver = to_mei_cl_driver(dev->driver); 69e5354107SSamuel Ortiz if (!driver || !driver->probe) 70e5354107SSamuel Ortiz return -ENODEV; 71e5354107SSamuel Ortiz 72e5354107SSamuel Ortiz dev_dbg(dev, "Device probe\n"); 73e5354107SSamuel Ortiz 74e5354107SSamuel Ortiz strncpy(id.name, dev_name(dev), MEI_CL_NAME_SIZE); 75e5354107SSamuel Ortiz 76e5354107SSamuel Ortiz return driver->probe(device, &id); 77e5354107SSamuel Ortiz } 78e5354107SSamuel Ortiz 79e5354107SSamuel Ortiz static int mei_cl_device_remove(struct device *dev) 80e5354107SSamuel Ortiz { 81e5354107SSamuel Ortiz struct mei_cl_device *device = to_mei_cl_device(dev); 82e5354107SSamuel Ortiz struct mei_cl_driver *driver; 83e5354107SSamuel Ortiz 84e5354107SSamuel Ortiz if (!device || !dev->driver) 85e5354107SSamuel Ortiz return 0; 86e5354107SSamuel Ortiz 873e833295SSamuel Ortiz if (device->event_cb) { 883e833295SSamuel Ortiz device->event_cb = NULL; 893e833295SSamuel Ortiz cancel_work_sync(&device->event_work); 903e833295SSamuel Ortiz } 913e833295SSamuel Ortiz 92e5354107SSamuel Ortiz driver = to_mei_cl_driver(dev->driver); 93e5354107SSamuel Ortiz if (!driver->remove) { 94e5354107SSamuel Ortiz dev->driver = NULL; 95e5354107SSamuel Ortiz 96e5354107SSamuel Ortiz return 0; 97e5354107SSamuel Ortiz } 98e5354107SSamuel Ortiz 99e5354107SSamuel Ortiz return driver->remove(device); 100e5354107SSamuel Ortiz } 101e5354107SSamuel Ortiz 102e5354107SSamuel Ortiz static ssize_t modalias_show(struct device *dev, struct device_attribute *a, 103e5354107SSamuel Ortiz char *buf) 104e5354107SSamuel Ortiz { 105e5354107SSamuel Ortiz int len; 106e5354107SSamuel Ortiz 107e5354107SSamuel Ortiz len = snprintf(buf, PAGE_SIZE, "mei:%s\n", dev_name(dev)); 108e5354107SSamuel Ortiz 109e5354107SSamuel Ortiz return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; 110e5354107SSamuel Ortiz } 111*32f389ecSGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias); 112e5354107SSamuel Ortiz 113*32f389ecSGreg Kroah-Hartman static struct attribute *mei_cl_dev_attrs[] = { 114*32f389ecSGreg Kroah-Hartman &dev_attr_modalias.attr, 115*32f389ecSGreg Kroah-Hartman NULL, 116e5354107SSamuel Ortiz }; 117*32f389ecSGreg Kroah-Hartman ATTRIBUTE_GROUPS(mei_cl_dev); 118e5354107SSamuel Ortiz 119e5354107SSamuel Ortiz static int mei_cl_uevent(struct device *dev, struct kobj_uevent_env *env) 120e5354107SSamuel Ortiz { 121e5354107SSamuel Ortiz if (add_uevent_var(env, "MODALIAS=mei:%s", dev_name(dev))) 122e5354107SSamuel Ortiz return -ENOMEM; 123e5354107SSamuel Ortiz 124e5354107SSamuel Ortiz return 0; 125e5354107SSamuel Ortiz } 126e5354107SSamuel Ortiz 127e5354107SSamuel Ortiz static struct bus_type mei_cl_bus_type = { 128e5354107SSamuel Ortiz .name = "mei", 129*32f389ecSGreg Kroah-Hartman .dev_groups = mei_cl_dev_groups, 130e5354107SSamuel Ortiz .match = mei_cl_device_match, 131e5354107SSamuel Ortiz .probe = mei_cl_device_probe, 132e5354107SSamuel Ortiz .remove = mei_cl_device_remove, 133e5354107SSamuel Ortiz .uevent = mei_cl_uevent, 134e5354107SSamuel Ortiz }; 135e5354107SSamuel Ortiz 136e5354107SSamuel Ortiz static void mei_cl_dev_release(struct device *dev) 137e5354107SSamuel Ortiz { 138e5354107SSamuel Ortiz kfree(to_mei_cl_device(dev)); 139e5354107SSamuel Ortiz } 140e5354107SSamuel Ortiz 141e5354107SSamuel Ortiz static struct device_type mei_cl_device_type = { 142e5354107SSamuel Ortiz .release = mei_cl_dev_release, 143e5354107SSamuel Ortiz }; 144e5354107SSamuel Ortiz 145a7b71bc0SSamuel Ortiz static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *dev, 146a7b71bc0SSamuel Ortiz uuid_le uuid) 147a7b71bc0SSamuel Ortiz { 148a7b71bc0SSamuel Ortiz struct mei_cl *cl, *next; 149a7b71bc0SSamuel Ortiz 150a7b71bc0SSamuel Ortiz list_for_each_entry_safe(cl, next, &dev->device_list, device_link) { 151a7b71bc0SSamuel Ortiz if (!uuid_le_cmp(uuid, cl->device_uuid)) 152a7b71bc0SSamuel Ortiz return cl; 153a7b71bc0SSamuel Ortiz } 154a7b71bc0SSamuel Ortiz 155a7b71bc0SSamuel Ortiz return NULL; 156a7b71bc0SSamuel Ortiz } 157a7b71bc0SSamuel Ortiz struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, 158e46980a1SSamuel Ortiz uuid_le uuid, char *name, 159e46980a1SSamuel Ortiz struct mei_cl_ops *ops) 160e5354107SSamuel Ortiz { 161e5354107SSamuel Ortiz struct mei_cl_device *device; 162a7b71bc0SSamuel Ortiz struct mei_cl *cl; 163e5354107SSamuel Ortiz int status; 164e5354107SSamuel Ortiz 165a7b71bc0SSamuel Ortiz cl = mei_bus_find_mei_cl_by_uuid(dev, uuid); 166a7b71bc0SSamuel Ortiz if (cl == NULL) 167a7b71bc0SSamuel Ortiz return NULL; 168a7b71bc0SSamuel Ortiz 169e5354107SSamuel Ortiz device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); 170e5354107SSamuel Ortiz if (!device) 171e5354107SSamuel Ortiz return NULL; 172e5354107SSamuel Ortiz 173a7b71bc0SSamuel Ortiz device->cl = cl; 174e46980a1SSamuel Ortiz device->ops = ops; 175a7b71bc0SSamuel Ortiz 176a7b71bc0SSamuel Ortiz device->dev.parent = &dev->pdev->dev; 177e5354107SSamuel Ortiz device->dev.bus = &mei_cl_bus_type; 178e5354107SSamuel Ortiz device->dev.type = &mei_cl_device_type; 179e5354107SSamuel Ortiz 180e5354107SSamuel Ortiz dev_set_name(&device->dev, "%s", name); 181e5354107SSamuel Ortiz 182e5354107SSamuel Ortiz status = device_register(&device->dev); 183a7b71bc0SSamuel Ortiz if (status) { 184a7b71bc0SSamuel Ortiz dev_err(&dev->pdev->dev, "Failed to register MEI device\n"); 185a7b71bc0SSamuel Ortiz kfree(device); 186a7b71bc0SSamuel Ortiz return NULL; 187a7b71bc0SSamuel Ortiz } 188a7b71bc0SSamuel Ortiz 189a7b71bc0SSamuel Ortiz cl->device = device; 190e5354107SSamuel Ortiz 191e5354107SSamuel Ortiz dev_dbg(&device->dev, "client %s registered\n", name); 192e5354107SSamuel Ortiz 193e5354107SSamuel Ortiz return device; 194e5354107SSamuel Ortiz } 195e5354107SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_add_device); 196e5354107SSamuel Ortiz 197e5354107SSamuel Ortiz void mei_cl_remove_device(struct mei_cl_device *device) 198e5354107SSamuel Ortiz { 199e5354107SSamuel Ortiz device_unregister(&device->dev); 200e5354107SSamuel Ortiz } 201e5354107SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_remove_device); 202333e4ee0SSamuel Ortiz 203333e4ee0SSamuel Ortiz int __mei_cl_driver_register(struct mei_cl_driver *driver, struct module *owner) 204333e4ee0SSamuel Ortiz { 205333e4ee0SSamuel Ortiz int err; 206333e4ee0SSamuel Ortiz 207333e4ee0SSamuel Ortiz driver->driver.name = driver->name; 208333e4ee0SSamuel Ortiz driver->driver.owner = owner; 209333e4ee0SSamuel Ortiz driver->driver.bus = &mei_cl_bus_type; 210333e4ee0SSamuel Ortiz 211333e4ee0SSamuel Ortiz err = driver_register(&driver->driver); 212333e4ee0SSamuel Ortiz if (err) 213333e4ee0SSamuel Ortiz return err; 214333e4ee0SSamuel Ortiz 215333e4ee0SSamuel Ortiz pr_debug("mei: driver [%s] registered\n", driver->driver.name); 216333e4ee0SSamuel Ortiz 217333e4ee0SSamuel Ortiz return 0; 218333e4ee0SSamuel Ortiz } 219333e4ee0SSamuel Ortiz EXPORT_SYMBOL_GPL(__mei_cl_driver_register); 220333e4ee0SSamuel Ortiz 221333e4ee0SSamuel Ortiz void mei_cl_driver_unregister(struct mei_cl_driver *driver) 222333e4ee0SSamuel Ortiz { 223333e4ee0SSamuel Ortiz driver_unregister(&driver->driver); 224333e4ee0SSamuel Ortiz 225333e4ee0SSamuel Ortiz pr_debug("mei: driver [%s] unregistered\n", driver->driver.name); 226333e4ee0SSamuel Ortiz } 227333e4ee0SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); 2283e833295SSamuel Ortiz 22944d88d91SSamuel Ortiz static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, 23044d88d91SSamuel Ortiz bool blocking) 2313e833295SSamuel Ortiz { 2323e833295SSamuel Ortiz struct mei_device *dev; 2333e833295SSamuel Ortiz struct mei_cl_cb *cb; 2344234a6deSTomas Winkler int id; 2354234a6deSTomas Winkler int rets; 2363e833295SSamuel Ortiz 2373e833295SSamuel Ortiz if (WARN_ON(!cl || !cl->dev)) 2383e833295SSamuel Ortiz return -ENODEV; 2393e833295SSamuel Ortiz 2404234a6deSTomas Winkler dev = cl->dev; 2414234a6deSTomas Winkler 2423e833295SSamuel Ortiz if (cl->state != MEI_FILE_CONNECTED) 2433e833295SSamuel Ortiz return -ENODEV; 2443e833295SSamuel Ortiz 2454234a6deSTomas Winkler /* Check if we have an ME client device */ 2464234a6deSTomas Winkler id = mei_me_cl_by_id(dev, cl->me_client_id); 2474234a6deSTomas Winkler if (id < 0) 2484234a6deSTomas Winkler return -ENODEV; 2494234a6deSTomas Winkler 2504234a6deSTomas Winkler if (length > dev->me_clients[id].props.max_msg_length) 2514234a6deSTomas Winkler return -EINVAL; 2524234a6deSTomas Winkler 2533e833295SSamuel Ortiz cb = mei_io_cb_init(cl, NULL); 2543e833295SSamuel Ortiz if (!cb) 2553e833295SSamuel Ortiz return -ENOMEM; 2563e833295SSamuel Ortiz 2574234a6deSTomas Winkler rets = mei_io_cb_alloc_req_buf(cb, length); 2584234a6deSTomas Winkler if (rets < 0) { 2593e833295SSamuel Ortiz mei_io_cb_free(cb); 2604234a6deSTomas Winkler return rets; 2613e833295SSamuel Ortiz } 2623e833295SSamuel Ortiz 2633e833295SSamuel Ortiz memcpy(cb->request_buffer.data, buf, length); 2643e833295SSamuel Ortiz 2653e833295SSamuel Ortiz mutex_lock(&dev->device_lock); 2663e833295SSamuel Ortiz 2674234a6deSTomas Winkler rets = mei_cl_write(cl, cb, blocking); 2683e833295SSamuel Ortiz 2693e833295SSamuel Ortiz mutex_unlock(&dev->device_lock); 2704234a6deSTomas Winkler if (rets < 0) 2713e833295SSamuel Ortiz mei_io_cb_free(cb); 2723e833295SSamuel Ortiz 2734234a6deSTomas Winkler return rets; 2743e833295SSamuel Ortiz } 2753e833295SSamuel Ortiz 2763e833295SSamuel Ortiz int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) 2773e833295SSamuel Ortiz { 2783e833295SSamuel Ortiz struct mei_device *dev; 2793e833295SSamuel Ortiz struct mei_cl_cb *cb; 2803e833295SSamuel Ortiz size_t r_length; 2813e833295SSamuel Ortiz int err; 2823e833295SSamuel Ortiz 2833e833295SSamuel Ortiz if (WARN_ON(!cl || !cl->dev)) 2843e833295SSamuel Ortiz return -ENODEV; 2853e833295SSamuel Ortiz 2863e833295SSamuel Ortiz dev = cl->dev; 2873e833295SSamuel Ortiz 2883e833295SSamuel Ortiz mutex_lock(&dev->device_lock); 2893e833295SSamuel Ortiz 2903e833295SSamuel Ortiz if (!cl->read_cb) { 291fcb136e1STomas Winkler err = mei_cl_read_start(cl, length); 2923e833295SSamuel Ortiz if (err < 0) { 2933e833295SSamuel Ortiz mutex_unlock(&dev->device_lock); 2943e833295SSamuel Ortiz return err; 2953e833295SSamuel Ortiz } 2963e833295SSamuel Ortiz } 2973e833295SSamuel Ortiz 2983e833295SSamuel Ortiz if (cl->reading_state != MEI_READ_COMPLETE && 2993e833295SSamuel Ortiz !waitqueue_active(&cl->rx_wait)) { 3003e833295SSamuel Ortiz mutex_unlock(&dev->device_lock); 3013e833295SSamuel Ortiz 3023e833295SSamuel Ortiz if (wait_event_interruptible(cl->rx_wait, 3033e833295SSamuel Ortiz (MEI_READ_COMPLETE == cl->reading_state))) { 3043e833295SSamuel Ortiz if (signal_pending(current)) 3053e833295SSamuel Ortiz return -EINTR; 3063e833295SSamuel Ortiz return -ERESTARTSYS; 3073e833295SSamuel Ortiz } 3083e833295SSamuel Ortiz 3093e833295SSamuel Ortiz mutex_lock(&dev->device_lock); 3103e833295SSamuel Ortiz } 3113e833295SSamuel Ortiz 3123e833295SSamuel Ortiz cb = cl->read_cb; 3133e833295SSamuel Ortiz 3143e833295SSamuel Ortiz if (cl->reading_state != MEI_READ_COMPLETE) { 3153e833295SSamuel Ortiz r_length = 0; 3163e833295SSamuel Ortiz goto out; 3173e833295SSamuel Ortiz } 3183e833295SSamuel Ortiz 3193e833295SSamuel Ortiz r_length = min_t(size_t, length, cb->buf_idx); 3203e833295SSamuel Ortiz 3213e833295SSamuel Ortiz memcpy(buf, cb->response_buffer.data, r_length); 3223e833295SSamuel Ortiz 3233e833295SSamuel Ortiz mei_io_cb_free(cb); 3243e833295SSamuel Ortiz cl->reading_state = MEI_IDLE; 3253e833295SSamuel Ortiz cl->read_cb = NULL; 3263e833295SSamuel Ortiz 3273e833295SSamuel Ortiz out: 3283e833295SSamuel Ortiz mutex_unlock(&dev->device_lock); 3293e833295SSamuel Ortiz 3303e833295SSamuel Ortiz return r_length; 3313e833295SSamuel Ortiz } 3323e833295SSamuel Ortiz 33344d88d91SSamuel Ortiz inline int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length) 33444d88d91SSamuel Ortiz { 33544d88d91SSamuel Ortiz return ___mei_cl_send(cl, buf, length, 0); 33644d88d91SSamuel Ortiz } 33744d88d91SSamuel Ortiz 33844d88d91SSamuel Ortiz inline int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length) 33944d88d91SSamuel Ortiz { 34044d88d91SSamuel Ortiz return ___mei_cl_send(cl, buf, length, 1); 34144d88d91SSamuel Ortiz } 34244d88d91SSamuel Ortiz 3433e833295SSamuel Ortiz int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length) 3443e833295SSamuel Ortiz { 345a7b71bc0SSamuel Ortiz struct mei_cl *cl = device->cl; 3463e833295SSamuel Ortiz 347a7b71bc0SSamuel Ortiz if (cl == NULL) 348a7b71bc0SSamuel Ortiz return -ENODEV; 3493e833295SSamuel Ortiz 3503e833295SSamuel Ortiz if (device->ops && device->ops->send) 3513e833295SSamuel Ortiz return device->ops->send(device, buf, length); 3523e833295SSamuel Ortiz 3533e833295SSamuel Ortiz return __mei_cl_send(cl, buf, length); 3543e833295SSamuel Ortiz } 3553e833295SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_send); 3563e833295SSamuel Ortiz 3573e833295SSamuel Ortiz int mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length) 3583e833295SSamuel Ortiz { 359a7b71bc0SSamuel Ortiz struct mei_cl *cl = device->cl; 3603e833295SSamuel Ortiz 361a7b71bc0SSamuel Ortiz if (cl == NULL) 362a7b71bc0SSamuel Ortiz return -ENODEV; 3633e833295SSamuel Ortiz 3643e833295SSamuel Ortiz if (device->ops && device->ops->recv) 3653e833295SSamuel Ortiz return device->ops->recv(device, buf, length); 3663e833295SSamuel Ortiz 3673e833295SSamuel Ortiz return __mei_cl_recv(cl, buf, length); 3683e833295SSamuel Ortiz } 3693e833295SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_recv); 3703e833295SSamuel Ortiz 3713e833295SSamuel Ortiz static void mei_bus_event_work(struct work_struct *work) 3723e833295SSamuel Ortiz { 3733e833295SSamuel Ortiz struct mei_cl_device *device; 3743e833295SSamuel Ortiz 3753e833295SSamuel Ortiz device = container_of(work, struct mei_cl_device, event_work); 3763e833295SSamuel Ortiz 3773e833295SSamuel Ortiz if (device->event_cb) 3783e833295SSamuel Ortiz device->event_cb(device, device->events, device->event_context); 3793e833295SSamuel Ortiz 3803e833295SSamuel Ortiz device->events = 0; 3813e833295SSamuel Ortiz 3823e833295SSamuel Ortiz /* Prepare for the next read */ 383fcb136e1STomas Winkler mei_cl_read_start(device->cl, 0); 3843e833295SSamuel Ortiz } 3853e833295SSamuel Ortiz 3863e833295SSamuel Ortiz int mei_cl_register_event_cb(struct mei_cl_device *device, 3873e833295SSamuel Ortiz mei_cl_event_cb_t event_cb, void *context) 3883e833295SSamuel Ortiz { 3893e833295SSamuel Ortiz if (device->event_cb) 3903e833295SSamuel Ortiz return -EALREADY; 3913e833295SSamuel Ortiz 3923e833295SSamuel Ortiz device->events = 0; 3933e833295SSamuel Ortiz device->event_cb = event_cb; 3943e833295SSamuel Ortiz device->event_context = context; 3953e833295SSamuel Ortiz INIT_WORK(&device->event_work, mei_bus_event_work); 3963e833295SSamuel Ortiz 397fcb136e1STomas Winkler mei_cl_read_start(device->cl, 0); 3983e833295SSamuel Ortiz 3993e833295SSamuel Ortiz return 0; 4003e833295SSamuel Ortiz } 4013e833295SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); 402cf3baefbSSamuel Ortiz 403aa6aef21SSamuel Ortiz void *mei_cl_get_drvdata(const struct mei_cl_device *device) 404aa6aef21SSamuel Ortiz { 405aa6aef21SSamuel Ortiz return dev_get_drvdata(&device->dev); 406aa6aef21SSamuel Ortiz } 407aa6aef21SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_get_drvdata); 408aa6aef21SSamuel Ortiz 409aa6aef21SSamuel Ortiz void mei_cl_set_drvdata(struct mei_cl_device *device, void *data) 410aa6aef21SSamuel Ortiz { 411aa6aef21SSamuel Ortiz dev_set_drvdata(&device->dev, data); 412aa6aef21SSamuel Ortiz } 413aa6aef21SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_set_drvdata); 414aa6aef21SSamuel Ortiz 415e46980a1SSamuel Ortiz int mei_cl_enable_device(struct mei_cl_device *device) 416e46980a1SSamuel Ortiz { 417e46980a1SSamuel Ortiz int err; 418e46980a1SSamuel Ortiz struct mei_device *dev; 419e46980a1SSamuel Ortiz struct mei_cl *cl = device->cl; 420e46980a1SSamuel Ortiz 421e46980a1SSamuel Ortiz if (cl == NULL) 422e46980a1SSamuel Ortiz return -ENODEV; 423e46980a1SSamuel Ortiz 424e46980a1SSamuel Ortiz dev = cl->dev; 425e46980a1SSamuel Ortiz 426e46980a1SSamuel Ortiz mutex_lock(&dev->device_lock); 427e46980a1SSamuel Ortiz 428e46980a1SSamuel Ortiz cl->state = MEI_FILE_CONNECTING; 429e46980a1SSamuel Ortiz 430e46980a1SSamuel Ortiz err = mei_cl_connect(cl, NULL); 431e46980a1SSamuel Ortiz if (err < 0) { 432e46980a1SSamuel Ortiz mutex_unlock(&dev->device_lock); 433e46980a1SSamuel Ortiz dev_err(&dev->pdev->dev, "Could not connect to the ME client"); 434e46980a1SSamuel Ortiz 435e46980a1SSamuel Ortiz return err; 436e46980a1SSamuel Ortiz } 437e46980a1SSamuel Ortiz 438e46980a1SSamuel Ortiz mutex_unlock(&dev->device_lock); 439e46980a1SSamuel Ortiz 440e46980a1SSamuel Ortiz if (device->event_cb && !cl->read_cb) 441fcb136e1STomas Winkler mei_cl_read_start(device->cl, 0); 442e46980a1SSamuel Ortiz 443e46980a1SSamuel Ortiz if (!device->ops || !device->ops->enable) 444e46980a1SSamuel Ortiz return 0; 445e46980a1SSamuel Ortiz 446e46980a1SSamuel Ortiz return device->ops->enable(device); 447e46980a1SSamuel Ortiz } 448e46980a1SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_enable_device); 449e46980a1SSamuel Ortiz 450e46980a1SSamuel Ortiz int mei_cl_disable_device(struct mei_cl_device *device) 451e46980a1SSamuel Ortiz { 452e46980a1SSamuel Ortiz int err; 453e46980a1SSamuel Ortiz struct mei_device *dev; 454e46980a1SSamuel Ortiz struct mei_cl *cl = device->cl; 455e46980a1SSamuel Ortiz 456e46980a1SSamuel Ortiz if (cl == NULL) 457e46980a1SSamuel Ortiz return -ENODEV; 458e46980a1SSamuel Ortiz 459e46980a1SSamuel Ortiz dev = cl->dev; 460e46980a1SSamuel Ortiz 461e46980a1SSamuel Ortiz mutex_lock(&dev->device_lock); 462e46980a1SSamuel Ortiz 463e46980a1SSamuel Ortiz if (cl->state != MEI_FILE_CONNECTED) { 464e46980a1SSamuel Ortiz mutex_unlock(&dev->device_lock); 465e46980a1SSamuel Ortiz dev_err(&dev->pdev->dev, "Already disconnected"); 466e46980a1SSamuel Ortiz 467e46980a1SSamuel Ortiz return 0; 468e46980a1SSamuel Ortiz } 469e46980a1SSamuel Ortiz 470e46980a1SSamuel Ortiz cl->state = MEI_FILE_DISCONNECTING; 471e46980a1SSamuel Ortiz 472e46980a1SSamuel Ortiz err = mei_cl_disconnect(cl); 473e46980a1SSamuel Ortiz if (err < 0) { 474e46980a1SSamuel Ortiz mutex_unlock(&dev->device_lock); 475e46980a1SSamuel Ortiz dev_err(&dev->pdev->dev, 476e46980a1SSamuel Ortiz "Could not disconnect from the ME client"); 477e46980a1SSamuel Ortiz 478e46980a1SSamuel Ortiz return err; 479e46980a1SSamuel Ortiz } 480e46980a1SSamuel Ortiz 481e46980a1SSamuel Ortiz /* Flush queues and remove any pending read */ 482e46980a1SSamuel Ortiz mei_cl_flush_queues(cl); 483e46980a1SSamuel Ortiz 484e46980a1SSamuel Ortiz if (cl->read_cb) { 485e46980a1SSamuel Ortiz struct mei_cl_cb *cb = NULL; 486e46980a1SSamuel Ortiz 487e46980a1SSamuel Ortiz cb = mei_cl_find_read_cb(cl); 488e46980a1SSamuel Ortiz /* Remove entry from read list */ 489e46980a1SSamuel Ortiz if (cb) 490e46980a1SSamuel Ortiz list_del(&cb->list); 491e46980a1SSamuel Ortiz 492e46980a1SSamuel Ortiz cb = cl->read_cb; 493e46980a1SSamuel Ortiz cl->read_cb = NULL; 494e46980a1SSamuel Ortiz 495e46980a1SSamuel Ortiz if (cb) { 496e46980a1SSamuel Ortiz mei_io_cb_free(cb); 497e46980a1SSamuel Ortiz cb = NULL; 498e46980a1SSamuel Ortiz } 499e46980a1SSamuel Ortiz } 500e46980a1SSamuel Ortiz 501bbedf2fcSSamuel Ortiz device->event_cb = NULL; 502bbedf2fcSSamuel Ortiz 503e46980a1SSamuel Ortiz mutex_unlock(&dev->device_lock); 504e46980a1SSamuel Ortiz 505e46980a1SSamuel Ortiz if (!device->ops || !device->ops->disable) 506e46980a1SSamuel Ortiz return 0; 507e46980a1SSamuel Ortiz 508e46980a1SSamuel Ortiz return device->ops->disable(device); 509e46980a1SSamuel Ortiz } 510e46980a1SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_disable_device); 511e46980a1SSamuel Ortiz 512cf3baefbSSamuel Ortiz void mei_cl_bus_rx_event(struct mei_cl *cl) 513cf3baefbSSamuel Ortiz { 514cf3baefbSSamuel Ortiz struct mei_cl_device *device = cl->device; 515cf3baefbSSamuel Ortiz 516cf3baefbSSamuel Ortiz if (!device || !device->event_cb) 517cf3baefbSSamuel Ortiz return; 518cf3baefbSSamuel Ortiz 519cf3baefbSSamuel Ortiz set_bit(MEI_CL_EVENT_RX, &device->events); 520cf3baefbSSamuel Ortiz 521cf3baefbSSamuel Ortiz schedule_work(&device->event_work); 522cf3baefbSSamuel Ortiz } 523cf3baefbSSamuel Ortiz 524cf3baefbSSamuel Ortiz int __init mei_cl_bus_init(void) 525cf3baefbSSamuel Ortiz { 526cf3baefbSSamuel Ortiz return bus_register(&mei_cl_bus_type); 527cf3baefbSSamuel Ortiz } 528cf3baefbSSamuel Ortiz 529cf3baefbSSamuel Ortiz void __exit mei_cl_bus_exit(void) 530cf3baefbSSamuel Ortiz { 531cf3baefbSSamuel Ortiz bus_unregister(&mei_cl_bus_type); 532cf3baefbSSamuel Ortiz } 533