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 } 111e5354107SSamuel Ortiz 112e5354107SSamuel Ortiz static struct device_attribute mei_cl_dev_attrs[] = { 113e5354107SSamuel Ortiz __ATTR_RO(modalias), 114e5354107SSamuel Ortiz __ATTR_NULL, 115e5354107SSamuel Ortiz }; 116e5354107SSamuel Ortiz 117e5354107SSamuel Ortiz static int mei_cl_uevent(struct device *dev, struct kobj_uevent_env *env) 118e5354107SSamuel Ortiz { 119e5354107SSamuel Ortiz if (add_uevent_var(env, "MODALIAS=mei:%s", dev_name(dev))) 120e5354107SSamuel Ortiz return -ENOMEM; 121e5354107SSamuel Ortiz 122e5354107SSamuel Ortiz return 0; 123e5354107SSamuel Ortiz } 124e5354107SSamuel Ortiz 125e5354107SSamuel Ortiz static struct bus_type mei_cl_bus_type = { 126e5354107SSamuel Ortiz .name = "mei", 127e5354107SSamuel Ortiz .dev_attrs = mei_cl_dev_attrs, 128e5354107SSamuel Ortiz .match = mei_cl_device_match, 129e5354107SSamuel Ortiz .probe = mei_cl_device_probe, 130e5354107SSamuel Ortiz .remove = mei_cl_device_remove, 131e5354107SSamuel Ortiz .uevent = mei_cl_uevent, 132e5354107SSamuel Ortiz }; 133e5354107SSamuel Ortiz 134e5354107SSamuel Ortiz static void mei_cl_dev_release(struct device *dev) 135e5354107SSamuel Ortiz { 136e5354107SSamuel Ortiz kfree(to_mei_cl_device(dev)); 137e5354107SSamuel Ortiz } 138e5354107SSamuel Ortiz 139e5354107SSamuel Ortiz static struct device_type mei_cl_device_type = { 140e5354107SSamuel Ortiz .release = mei_cl_dev_release, 141e5354107SSamuel Ortiz }; 142e5354107SSamuel Ortiz 143a7b71bc0SSamuel Ortiz static struct mei_cl *mei_bus_find_mei_cl_by_uuid(struct mei_device *dev, 144a7b71bc0SSamuel Ortiz uuid_le uuid) 145a7b71bc0SSamuel Ortiz { 146a7b71bc0SSamuel Ortiz struct mei_cl *cl, *next; 147a7b71bc0SSamuel Ortiz 148a7b71bc0SSamuel Ortiz list_for_each_entry_safe(cl, next, &dev->device_list, device_link) { 149a7b71bc0SSamuel Ortiz if (!uuid_le_cmp(uuid, cl->device_uuid)) 150a7b71bc0SSamuel Ortiz return cl; 151a7b71bc0SSamuel Ortiz } 152a7b71bc0SSamuel Ortiz 153a7b71bc0SSamuel Ortiz return NULL; 154a7b71bc0SSamuel Ortiz } 155a7b71bc0SSamuel Ortiz struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, 156e46980a1SSamuel Ortiz uuid_le uuid, char *name, 157e46980a1SSamuel Ortiz struct mei_cl_ops *ops) 158e5354107SSamuel Ortiz { 159e5354107SSamuel Ortiz struct mei_cl_device *device; 160a7b71bc0SSamuel Ortiz struct mei_cl *cl; 161e5354107SSamuel Ortiz int status; 162e5354107SSamuel Ortiz 163a7b71bc0SSamuel Ortiz cl = mei_bus_find_mei_cl_by_uuid(dev, uuid); 164a7b71bc0SSamuel Ortiz if (cl == NULL) 165a7b71bc0SSamuel Ortiz return NULL; 166a7b71bc0SSamuel Ortiz 167e5354107SSamuel Ortiz device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); 168e5354107SSamuel Ortiz if (!device) 169e5354107SSamuel Ortiz return NULL; 170e5354107SSamuel Ortiz 171a7b71bc0SSamuel Ortiz device->cl = cl; 172e46980a1SSamuel Ortiz device->ops = ops; 173a7b71bc0SSamuel Ortiz 174a7b71bc0SSamuel Ortiz device->dev.parent = &dev->pdev->dev; 175e5354107SSamuel Ortiz device->dev.bus = &mei_cl_bus_type; 176e5354107SSamuel Ortiz device->dev.type = &mei_cl_device_type; 177e5354107SSamuel Ortiz 178e5354107SSamuel Ortiz dev_set_name(&device->dev, "%s", name); 179e5354107SSamuel Ortiz 180e5354107SSamuel Ortiz status = device_register(&device->dev); 181a7b71bc0SSamuel Ortiz if (status) { 182a7b71bc0SSamuel Ortiz dev_err(&dev->pdev->dev, "Failed to register MEI device\n"); 183a7b71bc0SSamuel Ortiz kfree(device); 184a7b71bc0SSamuel Ortiz return NULL; 185a7b71bc0SSamuel Ortiz } 186a7b71bc0SSamuel Ortiz 187a7b71bc0SSamuel Ortiz cl->device = device; 188e5354107SSamuel Ortiz 189e5354107SSamuel Ortiz dev_dbg(&device->dev, "client %s registered\n", name); 190e5354107SSamuel Ortiz 191e5354107SSamuel Ortiz return device; 192e5354107SSamuel Ortiz } 193e5354107SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_add_device); 194e5354107SSamuel Ortiz 195e5354107SSamuel Ortiz void mei_cl_remove_device(struct mei_cl_device *device) 196e5354107SSamuel Ortiz { 197e5354107SSamuel Ortiz device_unregister(&device->dev); 198e5354107SSamuel Ortiz } 199e5354107SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_remove_device); 200333e4ee0SSamuel Ortiz 201333e4ee0SSamuel Ortiz int __mei_cl_driver_register(struct mei_cl_driver *driver, struct module *owner) 202333e4ee0SSamuel Ortiz { 203333e4ee0SSamuel Ortiz int err; 204333e4ee0SSamuel Ortiz 205333e4ee0SSamuel Ortiz driver->driver.name = driver->name; 206333e4ee0SSamuel Ortiz driver->driver.owner = owner; 207333e4ee0SSamuel Ortiz driver->driver.bus = &mei_cl_bus_type; 208333e4ee0SSamuel Ortiz 209333e4ee0SSamuel Ortiz err = driver_register(&driver->driver); 210333e4ee0SSamuel Ortiz if (err) 211333e4ee0SSamuel Ortiz return err; 212333e4ee0SSamuel Ortiz 213333e4ee0SSamuel Ortiz pr_debug("mei: driver [%s] registered\n", driver->driver.name); 214333e4ee0SSamuel Ortiz 215333e4ee0SSamuel Ortiz return 0; 216333e4ee0SSamuel Ortiz } 217333e4ee0SSamuel Ortiz EXPORT_SYMBOL_GPL(__mei_cl_driver_register); 218333e4ee0SSamuel Ortiz 219333e4ee0SSamuel Ortiz void mei_cl_driver_unregister(struct mei_cl_driver *driver) 220333e4ee0SSamuel Ortiz { 221333e4ee0SSamuel Ortiz driver_unregister(&driver->driver); 222333e4ee0SSamuel Ortiz 223333e4ee0SSamuel Ortiz pr_debug("mei: driver [%s] unregistered\n", driver->driver.name); 224333e4ee0SSamuel Ortiz } 225333e4ee0SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); 2263e833295SSamuel Ortiz 22744d88d91SSamuel Ortiz static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, 22844d88d91SSamuel Ortiz bool blocking) 2293e833295SSamuel Ortiz { 2303e833295SSamuel Ortiz struct mei_device *dev; 2313e833295SSamuel Ortiz struct mei_cl_cb *cb; 2324234a6deSTomas Winkler int id; 2334234a6deSTomas Winkler int rets; 2343e833295SSamuel Ortiz 2353e833295SSamuel Ortiz if (WARN_ON(!cl || !cl->dev)) 2363e833295SSamuel Ortiz return -ENODEV; 2373e833295SSamuel Ortiz 2384234a6deSTomas Winkler dev = cl->dev; 2394234a6deSTomas Winkler 2403e833295SSamuel Ortiz if (cl->state != MEI_FILE_CONNECTED) 2413e833295SSamuel Ortiz return -ENODEV; 2423e833295SSamuel Ortiz 2434234a6deSTomas Winkler /* Check if we have an ME client device */ 2444234a6deSTomas Winkler id = mei_me_cl_by_id(dev, cl->me_client_id); 2454234a6deSTomas Winkler if (id < 0) 2464234a6deSTomas Winkler return -ENODEV; 2474234a6deSTomas Winkler 2484234a6deSTomas Winkler if (length > dev->me_clients[id].props.max_msg_length) 2494234a6deSTomas Winkler return -EINVAL; 2504234a6deSTomas Winkler 2513e833295SSamuel Ortiz cb = mei_io_cb_init(cl, NULL); 2523e833295SSamuel Ortiz if (!cb) 2533e833295SSamuel Ortiz return -ENOMEM; 2543e833295SSamuel Ortiz 2554234a6deSTomas Winkler rets = mei_io_cb_alloc_req_buf(cb, length); 2564234a6deSTomas Winkler if (rets < 0) { 2573e833295SSamuel Ortiz mei_io_cb_free(cb); 2584234a6deSTomas Winkler return rets; 2593e833295SSamuel Ortiz } 2603e833295SSamuel Ortiz 2613e833295SSamuel Ortiz memcpy(cb->request_buffer.data, buf, length); 2623e833295SSamuel Ortiz 2633e833295SSamuel Ortiz mutex_lock(&dev->device_lock); 2643e833295SSamuel Ortiz 2654234a6deSTomas Winkler rets = mei_cl_write(cl, cb, blocking); 2663e833295SSamuel Ortiz 2673e833295SSamuel Ortiz mutex_unlock(&dev->device_lock); 2684234a6deSTomas Winkler if (rets < 0) 2693e833295SSamuel Ortiz mei_io_cb_free(cb); 2703e833295SSamuel Ortiz 2714234a6deSTomas Winkler return rets; 2723e833295SSamuel Ortiz } 2733e833295SSamuel Ortiz 2743e833295SSamuel Ortiz int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) 2753e833295SSamuel Ortiz { 2763e833295SSamuel Ortiz struct mei_device *dev; 2773e833295SSamuel Ortiz struct mei_cl_cb *cb; 2783e833295SSamuel Ortiz size_t r_length; 2793e833295SSamuel Ortiz int err; 2803e833295SSamuel Ortiz 2813e833295SSamuel Ortiz if (WARN_ON(!cl || !cl->dev)) 2823e833295SSamuel Ortiz return -ENODEV; 2833e833295SSamuel Ortiz 2843e833295SSamuel Ortiz dev = cl->dev; 2853e833295SSamuel Ortiz 2863e833295SSamuel Ortiz mutex_lock(&dev->device_lock); 2873e833295SSamuel Ortiz 2883e833295SSamuel Ortiz if (!cl->read_cb) { 289*fcb136e1STomas Winkler err = mei_cl_read_start(cl, length); 2903e833295SSamuel Ortiz if (err < 0) { 2913e833295SSamuel Ortiz mutex_unlock(&dev->device_lock); 2923e833295SSamuel Ortiz return err; 2933e833295SSamuel Ortiz } 2943e833295SSamuel Ortiz } 2953e833295SSamuel Ortiz 2963e833295SSamuel Ortiz if (cl->reading_state != MEI_READ_COMPLETE && 2973e833295SSamuel Ortiz !waitqueue_active(&cl->rx_wait)) { 2983e833295SSamuel Ortiz mutex_unlock(&dev->device_lock); 2993e833295SSamuel Ortiz 3003e833295SSamuel Ortiz if (wait_event_interruptible(cl->rx_wait, 3013e833295SSamuel Ortiz (MEI_READ_COMPLETE == cl->reading_state))) { 3023e833295SSamuel Ortiz if (signal_pending(current)) 3033e833295SSamuel Ortiz return -EINTR; 3043e833295SSamuel Ortiz return -ERESTARTSYS; 3053e833295SSamuel Ortiz } 3063e833295SSamuel Ortiz 3073e833295SSamuel Ortiz mutex_lock(&dev->device_lock); 3083e833295SSamuel Ortiz } 3093e833295SSamuel Ortiz 3103e833295SSamuel Ortiz cb = cl->read_cb; 3113e833295SSamuel Ortiz 3123e833295SSamuel Ortiz if (cl->reading_state != MEI_READ_COMPLETE) { 3133e833295SSamuel Ortiz r_length = 0; 3143e833295SSamuel Ortiz goto out; 3153e833295SSamuel Ortiz } 3163e833295SSamuel Ortiz 3173e833295SSamuel Ortiz r_length = min_t(size_t, length, cb->buf_idx); 3183e833295SSamuel Ortiz 3193e833295SSamuel Ortiz memcpy(buf, cb->response_buffer.data, r_length); 3203e833295SSamuel Ortiz 3213e833295SSamuel Ortiz mei_io_cb_free(cb); 3223e833295SSamuel Ortiz cl->reading_state = MEI_IDLE; 3233e833295SSamuel Ortiz cl->read_cb = NULL; 3243e833295SSamuel Ortiz 3253e833295SSamuel Ortiz out: 3263e833295SSamuel Ortiz mutex_unlock(&dev->device_lock); 3273e833295SSamuel Ortiz 3283e833295SSamuel Ortiz return r_length; 3293e833295SSamuel Ortiz } 3303e833295SSamuel Ortiz 33144d88d91SSamuel Ortiz inline int __mei_cl_async_send(struct mei_cl *cl, u8 *buf, size_t length) 33244d88d91SSamuel Ortiz { 33344d88d91SSamuel Ortiz return ___mei_cl_send(cl, buf, length, 0); 33444d88d91SSamuel Ortiz } 33544d88d91SSamuel Ortiz 33644d88d91SSamuel Ortiz inline int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length) 33744d88d91SSamuel Ortiz { 33844d88d91SSamuel Ortiz return ___mei_cl_send(cl, buf, length, 1); 33944d88d91SSamuel Ortiz } 34044d88d91SSamuel Ortiz 3413e833295SSamuel Ortiz int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length) 3423e833295SSamuel Ortiz { 343a7b71bc0SSamuel Ortiz struct mei_cl *cl = device->cl; 3443e833295SSamuel Ortiz 345a7b71bc0SSamuel Ortiz if (cl == NULL) 346a7b71bc0SSamuel Ortiz return -ENODEV; 3473e833295SSamuel Ortiz 3483e833295SSamuel Ortiz if (device->ops && device->ops->send) 3493e833295SSamuel Ortiz return device->ops->send(device, buf, length); 3503e833295SSamuel Ortiz 3513e833295SSamuel Ortiz return __mei_cl_send(cl, buf, length); 3523e833295SSamuel Ortiz } 3533e833295SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_send); 3543e833295SSamuel Ortiz 3553e833295SSamuel Ortiz int mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length) 3563e833295SSamuel Ortiz { 357a7b71bc0SSamuel Ortiz struct mei_cl *cl = device->cl; 3583e833295SSamuel Ortiz 359a7b71bc0SSamuel Ortiz if (cl == NULL) 360a7b71bc0SSamuel Ortiz return -ENODEV; 3613e833295SSamuel Ortiz 3623e833295SSamuel Ortiz if (device->ops && device->ops->recv) 3633e833295SSamuel Ortiz return device->ops->recv(device, buf, length); 3643e833295SSamuel Ortiz 3653e833295SSamuel Ortiz return __mei_cl_recv(cl, buf, length); 3663e833295SSamuel Ortiz } 3673e833295SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_recv); 3683e833295SSamuel Ortiz 3693e833295SSamuel Ortiz static void mei_bus_event_work(struct work_struct *work) 3703e833295SSamuel Ortiz { 3713e833295SSamuel Ortiz struct mei_cl_device *device; 3723e833295SSamuel Ortiz 3733e833295SSamuel Ortiz device = container_of(work, struct mei_cl_device, event_work); 3743e833295SSamuel Ortiz 3753e833295SSamuel Ortiz if (device->event_cb) 3763e833295SSamuel Ortiz device->event_cb(device, device->events, device->event_context); 3773e833295SSamuel Ortiz 3783e833295SSamuel Ortiz device->events = 0; 3793e833295SSamuel Ortiz 3803e833295SSamuel Ortiz /* Prepare for the next read */ 381*fcb136e1STomas Winkler mei_cl_read_start(device->cl, 0); 3823e833295SSamuel Ortiz } 3833e833295SSamuel Ortiz 3843e833295SSamuel Ortiz int mei_cl_register_event_cb(struct mei_cl_device *device, 3853e833295SSamuel Ortiz mei_cl_event_cb_t event_cb, void *context) 3863e833295SSamuel Ortiz { 3873e833295SSamuel Ortiz if (device->event_cb) 3883e833295SSamuel Ortiz return -EALREADY; 3893e833295SSamuel Ortiz 3903e833295SSamuel Ortiz device->events = 0; 3913e833295SSamuel Ortiz device->event_cb = event_cb; 3923e833295SSamuel Ortiz device->event_context = context; 3933e833295SSamuel Ortiz INIT_WORK(&device->event_work, mei_bus_event_work); 3943e833295SSamuel Ortiz 395*fcb136e1STomas Winkler mei_cl_read_start(device->cl, 0); 3963e833295SSamuel Ortiz 3973e833295SSamuel Ortiz return 0; 3983e833295SSamuel Ortiz } 3993e833295SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); 400cf3baefbSSamuel Ortiz 401aa6aef21SSamuel Ortiz void *mei_cl_get_drvdata(const struct mei_cl_device *device) 402aa6aef21SSamuel Ortiz { 403aa6aef21SSamuel Ortiz return dev_get_drvdata(&device->dev); 404aa6aef21SSamuel Ortiz } 405aa6aef21SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_get_drvdata); 406aa6aef21SSamuel Ortiz 407aa6aef21SSamuel Ortiz void mei_cl_set_drvdata(struct mei_cl_device *device, void *data) 408aa6aef21SSamuel Ortiz { 409aa6aef21SSamuel Ortiz dev_set_drvdata(&device->dev, data); 410aa6aef21SSamuel Ortiz } 411aa6aef21SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_set_drvdata); 412aa6aef21SSamuel Ortiz 413e46980a1SSamuel Ortiz int mei_cl_enable_device(struct mei_cl_device *device) 414e46980a1SSamuel Ortiz { 415e46980a1SSamuel Ortiz int err; 416e46980a1SSamuel Ortiz struct mei_device *dev; 417e46980a1SSamuel Ortiz struct mei_cl *cl = device->cl; 418e46980a1SSamuel Ortiz 419e46980a1SSamuel Ortiz if (cl == NULL) 420e46980a1SSamuel Ortiz return -ENODEV; 421e46980a1SSamuel Ortiz 422e46980a1SSamuel Ortiz dev = cl->dev; 423e46980a1SSamuel Ortiz 424e46980a1SSamuel Ortiz mutex_lock(&dev->device_lock); 425e46980a1SSamuel Ortiz 426e46980a1SSamuel Ortiz cl->state = MEI_FILE_CONNECTING; 427e46980a1SSamuel Ortiz 428e46980a1SSamuel Ortiz err = mei_cl_connect(cl, NULL); 429e46980a1SSamuel Ortiz if (err < 0) { 430e46980a1SSamuel Ortiz mutex_unlock(&dev->device_lock); 431e46980a1SSamuel Ortiz dev_err(&dev->pdev->dev, "Could not connect to the ME client"); 432e46980a1SSamuel Ortiz 433e46980a1SSamuel Ortiz return err; 434e46980a1SSamuel Ortiz } 435e46980a1SSamuel Ortiz 436e46980a1SSamuel Ortiz mutex_unlock(&dev->device_lock); 437e46980a1SSamuel Ortiz 438e46980a1SSamuel Ortiz if (device->event_cb && !cl->read_cb) 439*fcb136e1STomas Winkler mei_cl_read_start(device->cl, 0); 440e46980a1SSamuel Ortiz 441e46980a1SSamuel Ortiz if (!device->ops || !device->ops->enable) 442e46980a1SSamuel Ortiz return 0; 443e46980a1SSamuel Ortiz 444e46980a1SSamuel Ortiz return device->ops->enable(device); 445e46980a1SSamuel Ortiz } 446e46980a1SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_enable_device); 447e46980a1SSamuel Ortiz 448e46980a1SSamuel Ortiz int mei_cl_disable_device(struct mei_cl_device *device) 449e46980a1SSamuel Ortiz { 450e46980a1SSamuel Ortiz int err; 451e46980a1SSamuel Ortiz struct mei_device *dev; 452e46980a1SSamuel Ortiz struct mei_cl *cl = device->cl; 453e46980a1SSamuel Ortiz 454e46980a1SSamuel Ortiz if (cl == NULL) 455e46980a1SSamuel Ortiz return -ENODEV; 456e46980a1SSamuel Ortiz 457e46980a1SSamuel Ortiz dev = cl->dev; 458e46980a1SSamuel Ortiz 459e46980a1SSamuel Ortiz mutex_lock(&dev->device_lock); 460e46980a1SSamuel Ortiz 461e46980a1SSamuel Ortiz if (cl->state != MEI_FILE_CONNECTED) { 462e46980a1SSamuel Ortiz mutex_unlock(&dev->device_lock); 463e46980a1SSamuel Ortiz dev_err(&dev->pdev->dev, "Already disconnected"); 464e46980a1SSamuel Ortiz 465e46980a1SSamuel Ortiz return 0; 466e46980a1SSamuel Ortiz } 467e46980a1SSamuel Ortiz 468e46980a1SSamuel Ortiz cl->state = MEI_FILE_DISCONNECTING; 469e46980a1SSamuel Ortiz 470e46980a1SSamuel Ortiz err = mei_cl_disconnect(cl); 471e46980a1SSamuel Ortiz if (err < 0) { 472e46980a1SSamuel Ortiz mutex_unlock(&dev->device_lock); 473e46980a1SSamuel Ortiz dev_err(&dev->pdev->dev, 474e46980a1SSamuel Ortiz "Could not disconnect from the ME client"); 475e46980a1SSamuel Ortiz 476e46980a1SSamuel Ortiz return err; 477e46980a1SSamuel Ortiz } 478e46980a1SSamuel Ortiz 479e46980a1SSamuel Ortiz /* Flush queues and remove any pending read */ 480e46980a1SSamuel Ortiz mei_cl_flush_queues(cl); 481e46980a1SSamuel Ortiz 482e46980a1SSamuel Ortiz if (cl->read_cb) { 483e46980a1SSamuel Ortiz struct mei_cl_cb *cb = NULL; 484e46980a1SSamuel Ortiz 485e46980a1SSamuel Ortiz cb = mei_cl_find_read_cb(cl); 486e46980a1SSamuel Ortiz /* Remove entry from read list */ 487e46980a1SSamuel Ortiz if (cb) 488e46980a1SSamuel Ortiz list_del(&cb->list); 489e46980a1SSamuel Ortiz 490e46980a1SSamuel Ortiz cb = cl->read_cb; 491e46980a1SSamuel Ortiz cl->read_cb = NULL; 492e46980a1SSamuel Ortiz 493e46980a1SSamuel Ortiz if (cb) { 494e46980a1SSamuel Ortiz mei_io_cb_free(cb); 495e46980a1SSamuel Ortiz cb = NULL; 496e46980a1SSamuel Ortiz } 497e46980a1SSamuel Ortiz } 498e46980a1SSamuel Ortiz 499e46980a1SSamuel Ortiz mutex_unlock(&dev->device_lock); 500e46980a1SSamuel Ortiz 501e46980a1SSamuel Ortiz if (!device->ops || !device->ops->disable) 502e46980a1SSamuel Ortiz return 0; 503e46980a1SSamuel Ortiz 504e46980a1SSamuel Ortiz return device->ops->disable(device); 505e46980a1SSamuel Ortiz } 506e46980a1SSamuel Ortiz EXPORT_SYMBOL_GPL(mei_cl_disable_device); 507e46980a1SSamuel Ortiz 508cf3baefbSSamuel Ortiz void mei_cl_bus_rx_event(struct mei_cl *cl) 509cf3baefbSSamuel Ortiz { 510cf3baefbSSamuel Ortiz struct mei_cl_device *device = cl->device; 511cf3baefbSSamuel Ortiz 512cf3baefbSSamuel Ortiz if (!device || !device->event_cb) 513cf3baefbSSamuel Ortiz return; 514cf3baefbSSamuel Ortiz 515cf3baefbSSamuel Ortiz set_bit(MEI_CL_EVENT_RX, &device->events); 516cf3baefbSSamuel Ortiz 517cf3baefbSSamuel Ortiz schedule_work(&device->event_work); 518cf3baefbSSamuel Ortiz } 519cf3baefbSSamuel Ortiz 520cf3baefbSSamuel Ortiz int __init mei_cl_bus_init(void) 521cf3baefbSSamuel Ortiz { 522cf3baefbSSamuel Ortiz return bus_register(&mei_cl_bus_type); 523cf3baefbSSamuel Ortiz } 524cf3baefbSSamuel Ortiz 525cf3baefbSSamuel Ortiz void __exit mei_cl_bus_exit(void) 526cf3baefbSSamuel Ortiz { 527cf3baefbSSamuel Ortiz bus_unregister(&mei_cl_bus_type); 528cf3baefbSSamuel Ortiz } 529