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/mei_cl_bus.h> 26e5354107SSamuel Ortiz 27e5354107SSamuel Ortiz #include "mei_dev.h" 283e833295SSamuel Ortiz #include "client.h" 29e5354107SSamuel Ortiz 30e5354107SSamuel Ortiz #define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver) 31e5354107SSamuel Ortiz #define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev) 32e5354107SSamuel Ortiz 3362382997STomas Winkler /** 3462382997STomas Winkler * __mei_cl_send - internal client send (write) 3562382997STomas Winkler * 3662382997STomas Winkler * @cl: host client 3762382997STomas Winkler * @buf: buffer to send 3862382997STomas Winkler * @length: buffer length 3962382997STomas Winkler * @blocking: wait for write completion 4062382997STomas Winkler * 4162382997STomas Winkler * Return: written size bytes or < 0 on error 4262382997STomas Winkler */ 4362382997STomas Winkler ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, 4462382997STomas Winkler bool blocking) 4562382997STomas Winkler { 4662382997STomas Winkler struct mei_device *bus; 4762382997STomas Winkler struct mei_cl_cb *cb = NULL; 4862382997STomas Winkler ssize_t rets; 4962382997STomas Winkler 5062382997STomas Winkler if (WARN_ON(!cl || !cl->dev)) 5162382997STomas Winkler return -ENODEV; 5262382997STomas Winkler 5362382997STomas Winkler bus = cl->dev; 5462382997STomas Winkler 5562382997STomas Winkler mutex_lock(&bus->device_lock); 5615c13dfcSAlexander Usyskin if (bus->dev_state != MEI_DEV_ENABLED) { 5715c13dfcSAlexander Usyskin rets = -ENODEV; 5815c13dfcSAlexander Usyskin goto out; 5915c13dfcSAlexander Usyskin } 6015c13dfcSAlexander Usyskin 6162382997STomas Winkler if (!mei_cl_is_connected(cl)) { 6262382997STomas Winkler rets = -ENODEV; 6362382997STomas Winkler goto out; 6462382997STomas Winkler } 6562382997STomas Winkler 6662382997STomas Winkler /* Check if we have an ME client device */ 6762382997STomas Winkler if (!mei_me_cl_is_active(cl->me_cl)) { 6862382997STomas Winkler rets = -ENOTTY; 6962382997STomas Winkler goto out; 7062382997STomas Winkler } 7162382997STomas Winkler 7262382997STomas Winkler if (length > mei_cl_mtu(cl)) { 7362382997STomas Winkler rets = -EFBIG; 7462382997STomas Winkler goto out; 7562382997STomas Winkler } 7662382997STomas Winkler 7762382997STomas Winkler cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL); 7862382997STomas Winkler if (!cb) { 7962382997STomas Winkler rets = -ENOMEM; 8062382997STomas Winkler goto out; 8162382997STomas Winkler } 8262382997STomas Winkler 8362382997STomas Winkler memcpy(cb->buf.data, buf, length); 8462382997STomas Winkler 8562382997STomas Winkler rets = mei_cl_write(cl, cb, blocking); 8662382997STomas Winkler 8762382997STomas Winkler out: 8862382997STomas Winkler mutex_unlock(&bus->device_lock); 8962382997STomas Winkler if (rets < 0) 9062382997STomas Winkler mei_io_cb_free(cb); 9162382997STomas Winkler 9262382997STomas Winkler return rets; 9362382997STomas Winkler } 9462382997STomas Winkler 9562382997STomas Winkler /** 9662382997STomas Winkler * __mei_cl_recv - internal client receive (read) 9762382997STomas Winkler * 9862382997STomas Winkler * @cl: host client 99df7f5447STomas Winkler * @buf: buffer to receive 10062382997STomas Winkler * @length: buffer length 10162382997STomas Winkler * 10262382997STomas Winkler * Return: read size in bytes of < 0 on error 10362382997STomas Winkler */ 10462382997STomas Winkler ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) 10562382997STomas Winkler { 10662382997STomas Winkler struct mei_device *bus; 10762382997STomas Winkler struct mei_cl_cb *cb; 10862382997STomas Winkler size_t r_length; 10962382997STomas Winkler ssize_t rets; 11062382997STomas Winkler 11162382997STomas Winkler if (WARN_ON(!cl || !cl->dev)) 11262382997STomas Winkler return -ENODEV; 11362382997STomas Winkler 11462382997STomas Winkler bus = cl->dev; 11562382997STomas Winkler 11662382997STomas Winkler mutex_lock(&bus->device_lock); 11715c13dfcSAlexander Usyskin if (bus->dev_state != MEI_DEV_ENABLED) { 11815c13dfcSAlexander Usyskin rets = -ENODEV; 11915c13dfcSAlexander Usyskin goto out; 12015c13dfcSAlexander Usyskin } 12162382997STomas Winkler 12262382997STomas Winkler cb = mei_cl_read_cb(cl, NULL); 12362382997STomas Winkler if (cb) 12462382997STomas Winkler goto copy; 12562382997STomas Winkler 12662382997STomas Winkler rets = mei_cl_read_start(cl, length, NULL); 12762382997STomas Winkler if (rets && rets != -EBUSY) 12862382997STomas Winkler goto out; 12962382997STomas Winkler 13062382997STomas Winkler /* wait on event only if there is no other waiter */ 13162382997STomas Winkler if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) { 13262382997STomas Winkler 13362382997STomas Winkler mutex_unlock(&bus->device_lock); 13462382997STomas Winkler 13562382997STomas Winkler if (wait_event_interruptible(cl->rx_wait, 13662382997STomas Winkler (!list_empty(&cl->rd_completed)) || 13762382997STomas Winkler (!mei_cl_is_connected(cl)))) { 13862382997STomas Winkler 13962382997STomas Winkler if (signal_pending(current)) 14062382997STomas Winkler return -EINTR; 14162382997STomas Winkler return -ERESTARTSYS; 14262382997STomas Winkler } 14362382997STomas Winkler 14462382997STomas Winkler mutex_lock(&bus->device_lock); 14562382997STomas Winkler 14662382997STomas Winkler if (!mei_cl_is_connected(cl)) { 14762382997STomas Winkler rets = -EBUSY; 14862382997STomas Winkler goto out; 14962382997STomas Winkler } 15062382997STomas Winkler } 15162382997STomas Winkler 15262382997STomas Winkler cb = mei_cl_read_cb(cl, NULL); 15362382997STomas Winkler if (!cb) { 15462382997STomas Winkler rets = 0; 15562382997STomas Winkler goto out; 15662382997STomas Winkler } 15762382997STomas Winkler 15862382997STomas Winkler copy: 15962382997STomas Winkler if (cb->status) { 16062382997STomas Winkler rets = cb->status; 16162382997STomas Winkler goto free; 16262382997STomas Winkler } 16362382997STomas Winkler 16462382997STomas Winkler r_length = min_t(size_t, length, cb->buf_idx); 16562382997STomas Winkler memcpy(buf, cb->buf.data, r_length); 16662382997STomas Winkler rets = r_length; 16762382997STomas Winkler 16862382997STomas Winkler free: 16962382997STomas Winkler mei_io_cb_free(cb); 17062382997STomas Winkler out: 17162382997STomas Winkler mutex_unlock(&bus->device_lock); 17262382997STomas Winkler 17362382997STomas Winkler return rets; 17462382997STomas Winkler } 17562382997STomas Winkler 17662382997STomas Winkler /** 177d49dc5e7STomas Winkler * mei_cldev_send - me device send (write) 17862382997STomas Winkler * 17962382997STomas Winkler * @cldev: me client device 18062382997STomas Winkler * @buf: buffer to send 18162382997STomas Winkler * @length: buffer length 18262382997STomas Winkler * 18362382997STomas Winkler * Return: written size in bytes or < 0 on error 18462382997STomas Winkler */ 185d49dc5e7STomas Winkler ssize_t mei_cldev_send(struct mei_cl_device *cldev, u8 *buf, size_t length) 18662382997STomas Winkler { 18762382997STomas Winkler struct mei_cl *cl = cldev->cl; 18862382997STomas Winkler 18962382997STomas Winkler if (cl == NULL) 19062382997STomas Winkler return -ENODEV; 19162382997STomas Winkler 19262382997STomas Winkler return __mei_cl_send(cl, buf, length, 1); 19362382997STomas Winkler } 194d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_send); 19562382997STomas Winkler 19662382997STomas Winkler /** 197d49dc5e7STomas Winkler * mei_cldev_recv - client receive (read) 19862382997STomas Winkler * 19962382997STomas Winkler * @cldev: me client device 200df7f5447STomas Winkler * @buf: buffer to receive 20162382997STomas Winkler * @length: buffer length 20262382997STomas Winkler * 20362382997STomas Winkler * Return: read size in bytes of < 0 on error 20462382997STomas Winkler */ 205d49dc5e7STomas Winkler ssize_t mei_cldev_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) 20662382997STomas Winkler { 20762382997STomas Winkler struct mei_cl *cl = cldev->cl; 20862382997STomas Winkler 20962382997STomas Winkler if (cl == NULL) 21062382997STomas Winkler return -ENODEV; 21162382997STomas Winkler 21262382997STomas Winkler return __mei_cl_recv(cl, buf, length); 21362382997STomas Winkler } 214d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_recv); 21562382997STomas Winkler 21662382997STomas Winkler /** 217d49dc5e7STomas Winkler * mei_cl_bus_event_work - dispatch rx event for a bus device 21862382997STomas Winkler * and schedule new work 21962382997STomas Winkler * 22062382997STomas Winkler * @work: work 22162382997STomas Winkler */ 222d49dc5e7STomas Winkler static void mei_cl_bus_event_work(struct work_struct *work) 22362382997STomas Winkler { 22462382997STomas Winkler struct mei_cl_device *cldev; 22562382997STomas Winkler 22662382997STomas Winkler cldev = container_of(work, struct mei_cl_device, event_work); 22762382997STomas Winkler 22862382997STomas Winkler if (cldev->event_cb) 22962382997STomas Winkler cldev->event_cb(cldev, cldev->events, cldev->event_context); 23062382997STomas Winkler 23162382997STomas Winkler cldev->events = 0; 23262382997STomas Winkler 23362382997STomas Winkler /* Prepare for the next read */ 234bb2ef9c3SAlexander Usyskin if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) 23562382997STomas Winkler mei_cl_read_start(cldev->cl, 0, NULL); 23662382997STomas Winkler } 23762382997STomas Winkler 23862382997STomas Winkler /** 239bb2ef9c3SAlexander Usyskin * mei_cl_bus_notify_event - schedule notify cb on bus client 240bb2ef9c3SAlexander Usyskin * 241bb2ef9c3SAlexander Usyskin * @cl: host client 242850f8940STomas Winkler * 243850f8940STomas Winkler * Return: true if event was scheduled 244850f8940STomas Winkler * false if the client is not waiting for event 245bb2ef9c3SAlexander Usyskin */ 246850f8940STomas Winkler bool mei_cl_bus_notify_event(struct mei_cl *cl) 247bb2ef9c3SAlexander Usyskin { 248bb2ef9c3SAlexander Usyskin struct mei_cl_device *cldev = cl->cldev; 249bb2ef9c3SAlexander Usyskin 250bb2ef9c3SAlexander Usyskin if (!cldev || !cldev->event_cb) 251850f8940STomas Winkler return false; 252bb2ef9c3SAlexander Usyskin 253bb2ef9c3SAlexander Usyskin if (!(cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF))) 254850f8940STomas Winkler return false; 255bb2ef9c3SAlexander Usyskin 256bb2ef9c3SAlexander Usyskin if (!cl->notify_ev) 257850f8940STomas Winkler return false; 258bb2ef9c3SAlexander Usyskin 259bb2ef9c3SAlexander Usyskin set_bit(MEI_CL_EVENT_NOTIF, &cldev->events); 260bb2ef9c3SAlexander Usyskin 261bb2ef9c3SAlexander Usyskin schedule_work(&cldev->event_work); 262bb2ef9c3SAlexander Usyskin 263bb2ef9c3SAlexander Usyskin cl->notify_ev = false; 264850f8940STomas Winkler 265850f8940STomas Winkler return true; 266bb2ef9c3SAlexander Usyskin } 267bb2ef9c3SAlexander Usyskin 268bb2ef9c3SAlexander Usyskin /** 269a1f9ae2bSTomas Winkler * mei_cl_bus_rx_event - schedule rx event 27062382997STomas Winkler * 27162382997STomas Winkler * @cl: host client 272a1f9ae2bSTomas Winkler * 273a1f9ae2bSTomas Winkler * Return: true if event was scheduled 274a1f9ae2bSTomas Winkler * false if the client is not waiting for event 27562382997STomas Winkler */ 276a1f9ae2bSTomas Winkler bool mei_cl_bus_rx_event(struct mei_cl *cl) 27762382997STomas Winkler { 27862382997STomas Winkler struct mei_cl_device *cldev = cl->cldev; 27962382997STomas Winkler 28062382997STomas Winkler if (!cldev || !cldev->event_cb) 281a1f9ae2bSTomas Winkler return false; 28262382997STomas Winkler 283bb2ef9c3SAlexander Usyskin if (!(cldev->events_mask & BIT(MEI_CL_EVENT_RX))) 284a1f9ae2bSTomas Winkler return false; 285bb2ef9c3SAlexander Usyskin 28662382997STomas Winkler set_bit(MEI_CL_EVENT_RX, &cldev->events); 28762382997STomas Winkler 28862382997STomas Winkler schedule_work(&cldev->event_work); 289a1f9ae2bSTomas Winkler 290a1f9ae2bSTomas Winkler return true; 29162382997STomas Winkler } 29262382997STomas Winkler 29362382997STomas Winkler /** 294d49dc5e7STomas Winkler * mei_cldev_register_event_cb - register event callback 29562382997STomas Winkler * 29662382997STomas Winkler * @cldev: me client devices 29762382997STomas Winkler * @event_cb: callback function 298bb2ef9c3SAlexander Usyskin * @events_mask: requested events bitmask 29962382997STomas Winkler * @context: driver context data 30062382997STomas Winkler * 30162382997STomas Winkler * Return: 0 on success 30262382997STomas Winkler * -EALREADY if an callback is already registered 30362382997STomas Winkler * <0 on other errors 30462382997STomas Winkler */ 305d49dc5e7STomas Winkler int mei_cldev_register_event_cb(struct mei_cl_device *cldev, 306bb2ef9c3SAlexander Usyskin unsigned long events_mask, 307d49dc5e7STomas Winkler mei_cldev_event_cb_t event_cb, void *context) 30862382997STomas Winkler { 30948168f45STomas Winkler int ret; 31048168f45STomas Winkler 31162382997STomas Winkler if (cldev->event_cb) 31262382997STomas Winkler return -EALREADY; 31362382997STomas Winkler 31462382997STomas Winkler cldev->events = 0; 315bb2ef9c3SAlexander Usyskin cldev->events_mask = events_mask; 31662382997STomas Winkler cldev->event_cb = event_cb; 31762382997STomas Winkler cldev->event_context = context; 318d49dc5e7STomas Winkler INIT_WORK(&cldev->event_work, mei_cl_bus_event_work); 31962382997STomas Winkler 320bb2ef9c3SAlexander Usyskin if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) { 32148168f45STomas Winkler ret = mei_cl_read_start(cldev->cl, 0, NULL); 32248168f45STomas Winkler if (ret && ret != -EBUSY) 32348168f45STomas Winkler return ret; 324bb2ef9c3SAlexander Usyskin } 325bb2ef9c3SAlexander Usyskin 326bb2ef9c3SAlexander Usyskin if (cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF)) { 327bb2ef9c3SAlexander Usyskin mutex_lock(&cldev->cl->dev->device_lock); 328bb2ef9c3SAlexander Usyskin ret = mei_cl_notify_request(cldev->cl, NULL, event_cb ? 1 : 0); 329bb2ef9c3SAlexander Usyskin mutex_unlock(&cldev->cl->dev->device_lock); 330bb2ef9c3SAlexander Usyskin if (ret) 331bb2ef9c3SAlexander Usyskin return ret; 332bb2ef9c3SAlexander Usyskin } 33362382997STomas Winkler 33462382997STomas Winkler return 0; 33562382997STomas Winkler } 336d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_register_event_cb); 33762382997STomas Winkler 33862382997STomas Winkler /** 339d49dc5e7STomas Winkler * mei_cldev_get_drvdata - driver data getter 34062382997STomas Winkler * 34162382997STomas Winkler * @cldev: mei client device 34262382997STomas Winkler * 34362382997STomas Winkler * Return: driver private data 34462382997STomas Winkler */ 345d49dc5e7STomas Winkler void *mei_cldev_get_drvdata(const struct mei_cl_device *cldev) 34662382997STomas Winkler { 34762382997STomas Winkler return dev_get_drvdata(&cldev->dev); 34862382997STomas Winkler } 349d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_get_drvdata); 35062382997STomas Winkler 35162382997STomas Winkler /** 352d49dc5e7STomas Winkler * mei_cldev_set_drvdata - driver data setter 35362382997STomas Winkler * 35462382997STomas Winkler * @cldev: mei client device 35562382997STomas Winkler * @data: data to store 35662382997STomas Winkler */ 357d49dc5e7STomas Winkler void mei_cldev_set_drvdata(struct mei_cl_device *cldev, void *data) 35862382997STomas Winkler { 35962382997STomas Winkler dev_set_drvdata(&cldev->dev, data); 36062382997STomas Winkler } 361d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_set_drvdata); 36262382997STomas Winkler 36362382997STomas Winkler /** 364baeacd03STomas Winkler * mei_cldev_uuid - return uuid of the underlying me client 365baeacd03STomas Winkler * 366baeacd03STomas Winkler * @cldev: mei client device 367baeacd03STomas Winkler * 368baeacd03STomas Winkler * Return: me client uuid 369baeacd03STomas Winkler */ 370baeacd03STomas Winkler const uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev) 371baeacd03STomas Winkler { 372baeacd03STomas Winkler return mei_me_cl_uuid(cldev->me_cl); 373baeacd03STomas Winkler } 374baeacd03STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_uuid); 375baeacd03STomas Winkler 376baeacd03STomas Winkler /** 377baeacd03STomas Winkler * mei_cldev_ver - return protocol version of the underlying me client 378baeacd03STomas Winkler * 379baeacd03STomas Winkler * @cldev: mei client device 380baeacd03STomas Winkler * 381baeacd03STomas Winkler * Return: me client protocol version 382baeacd03STomas Winkler */ 383baeacd03STomas Winkler u8 mei_cldev_ver(const struct mei_cl_device *cldev) 384baeacd03STomas Winkler { 385baeacd03STomas Winkler return mei_me_cl_ver(cldev->me_cl); 386baeacd03STomas Winkler } 387baeacd03STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_ver); 388baeacd03STomas Winkler 389baeacd03STomas Winkler /** 39001a14edeSTomas Winkler * mei_cldev_enabled - check whether the device is enabled 39101a14edeSTomas Winkler * 39201a14edeSTomas Winkler * @cldev: mei client device 39301a14edeSTomas Winkler * 39401a14edeSTomas Winkler * Return: true if me client is initialized and connected 39501a14edeSTomas Winkler */ 39601a14edeSTomas Winkler bool mei_cldev_enabled(struct mei_cl_device *cldev) 39701a14edeSTomas Winkler { 39801a14edeSTomas Winkler return cldev->cl && mei_cl_is_connected(cldev->cl); 39901a14edeSTomas Winkler } 40001a14edeSTomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_enabled); 40101a14edeSTomas Winkler 40201a14edeSTomas Winkler /** 403d49dc5e7STomas Winkler * mei_cldev_enable_device - enable me client device 40462382997STomas Winkler * create connection with me client 40562382997STomas Winkler * 40662382997STomas Winkler * @cldev: me client device 40762382997STomas Winkler * 40862382997STomas Winkler * Return: 0 on success and < 0 on error 40962382997STomas Winkler */ 410d49dc5e7STomas Winkler int mei_cldev_enable(struct mei_cl_device *cldev) 41162382997STomas Winkler { 4126009595aSTomas Winkler struct mei_device *bus = cldev->bus; 4136009595aSTomas Winkler struct mei_cl *cl; 4146009595aSTomas Winkler int ret; 41562382997STomas Winkler 4166009595aSTomas Winkler cl = cldev->cl; 41762382997STomas Winkler 4186009595aSTomas Winkler if (!cl) { 4196009595aSTomas Winkler mutex_lock(&bus->device_lock); 420*7851e008SAlexander Usyskin cl = mei_cl_alloc_linked(bus); 4216009595aSTomas Winkler mutex_unlock(&bus->device_lock); 4226009595aSTomas Winkler if (IS_ERR(cl)) 4236009595aSTomas Winkler return PTR_ERR(cl); 4246009595aSTomas Winkler /* update pointers */ 4256009595aSTomas Winkler cldev->cl = cl; 4266009595aSTomas Winkler cl->cldev = cldev; 4276009595aSTomas Winkler } 42862382997STomas Winkler 42962382997STomas Winkler mutex_lock(&bus->device_lock); 43062382997STomas Winkler if (mei_cl_is_connected(cl)) { 4316009595aSTomas Winkler ret = 0; 4326009595aSTomas Winkler goto out; 43362382997STomas Winkler } 43462382997STomas Winkler 4356009595aSTomas Winkler if (!mei_me_cl_is_active(cldev->me_cl)) { 4366009595aSTomas Winkler dev_err(&cldev->dev, "me client is not active\n"); 4376009595aSTomas Winkler ret = -ENOTTY; 4386009595aSTomas Winkler goto out; 43962382997STomas Winkler } 44062382997STomas Winkler 4416009595aSTomas Winkler ret = mei_cl_connect(cl, cldev->me_cl, NULL); 4426009595aSTomas Winkler if (ret < 0) 4436009595aSTomas Winkler dev_err(&cldev->dev, "cannot connect\n"); 4446009595aSTomas Winkler 4456009595aSTomas Winkler out: 44662382997STomas Winkler mutex_unlock(&bus->device_lock); 44762382997STomas Winkler 4486009595aSTomas Winkler return ret; 44962382997STomas Winkler } 450d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_enable); 45162382997STomas Winkler 45262382997STomas Winkler /** 453d49dc5e7STomas Winkler * mei_cldev_disable - disable me client device 45462382997STomas Winkler * disconnect form the me client 45562382997STomas Winkler * 45662382997STomas Winkler * @cldev: me client device 45762382997STomas Winkler * 45862382997STomas Winkler * Return: 0 on success and < 0 on error 45962382997STomas Winkler */ 460d49dc5e7STomas Winkler int mei_cldev_disable(struct mei_cl_device *cldev) 46162382997STomas Winkler { 46262382997STomas Winkler struct mei_device *bus; 4636009595aSTomas Winkler struct mei_cl *cl; 4646009595aSTomas Winkler int err; 46562382997STomas Winkler 4666009595aSTomas Winkler if (!cldev || !cldev->cl) 46762382997STomas Winkler return -ENODEV; 46862382997STomas Winkler 4696009595aSTomas Winkler cl = cldev->cl; 4706009595aSTomas Winkler 4716009595aSTomas Winkler bus = cldev->bus; 47262382997STomas Winkler 47362382997STomas Winkler cldev->event_cb = NULL; 47462382997STomas Winkler 47562382997STomas Winkler mutex_lock(&bus->device_lock); 47662382997STomas Winkler 47762382997STomas Winkler if (!mei_cl_is_connected(cl)) { 47862382997STomas Winkler dev_err(bus->dev, "Already disconnected"); 47962382997STomas Winkler err = 0; 48062382997STomas Winkler goto out; 48162382997STomas Winkler } 48262382997STomas Winkler 48362382997STomas Winkler err = mei_cl_disconnect(cl); 4846009595aSTomas Winkler if (err < 0) 48562382997STomas Winkler dev_err(bus->dev, "Could not disconnect from the ME client"); 48662382997STomas Winkler 48762382997STomas Winkler out: 4886009595aSTomas Winkler /* Flush queues and remove any pending read */ 4896009595aSTomas Winkler mei_cl_flush_queues(cl, NULL); 4906009595aSTomas Winkler mei_cl_unlink(cl); 4916009595aSTomas Winkler 4926009595aSTomas Winkler kfree(cl); 4936009595aSTomas Winkler cldev->cl = NULL; 4946009595aSTomas Winkler 49562382997STomas Winkler mutex_unlock(&bus->device_lock); 49662382997STomas Winkler return err; 49762382997STomas Winkler } 498d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_disable); 49962382997STomas Winkler 500688a9cceSTomas Winkler /** 501688a9cceSTomas Winkler * mei_cl_device_find - find matching entry in the driver id table 502688a9cceSTomas Winkler * 503688a9cceSTomas Winkler * @cldev: me client device 504688a9cceSTomas Winkler * @cldrv: me client driver 505688a9cceSTomas Winkler * 506688a9cceSTomas Winkler * Return: id on success; NULL if no id is matching 507688a9cceSTomas Winkler */ 508688a9cceSTomas Winkler static const 509688a9cceSTomas Winkler struct mei_cl_device_id *mei_cl_device_find(struct mei_cl_device *cldev, 510688a9cceSTomas Winkler struct mei_cl_driver *cldrv) 511e5354107SSamuel Ortiz { 512e5354107SSamuel Ortiz const struct mei_cl_device_id *id; 513c93b76b3STomas Winkler const uuid_le *uuid; 514b26864caSTomas Winkler u8 version; 515b26864caSTomas Winkler bool match; 516e5354107SSamuel Ortiz 517b37719c3STomas Winkler uuid = mei_me_cl_uuid(cldev->me_cl); 518b26864caSTomas Winkler version = mei_me_cl_ver(cldev->me_cl); 519e5354107SSamuel Ortiz 520b37719c3STomas Winkler id = cldrv->id_table; 521b144ce2dSGreg Kroah-Hartman while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) { 522b144ce2dSGreg Kroah-Hartman if (!uuid_le_cmp(*uuid, id->uuid)) { 523b26864caSTomas Winkler match = true; 524688a9cceSTomas Winkler 525b26864caSTomas Winkler if (cldev->name[0]) 526b26864caSTomas Winkler if (strncmp(cldev->name, id->name, 527b26864caSTomas Winkler sizeof(id->name))) 528b26864caSTomas Winkler match = false; 529688a9cceSTomas Winkler 530b26864caSTomas Winkler if (id->version != MEI_CL_VERSION_ANY) 531b26864caSTomas Winkler if (id->version != version) 532b26864caSTomas Winkler match = false; 533b26864caSTomas Winkler if (match) 534688a9cceSTomas Winkler return id; 535c93b76b3STomas Winkler } 536e5354107SSamuel Ortiz 537e5354107SSamuel Ortiz id++; 538e5354107SSamuel Ortiz } 539e5354107SSamuel Ortiz 540688a9cceSTomas Winkler return NULL; 541688a9cceSTomas Winkler } 542688a9cceSTomas Winkler 543688a9cceSTomas Winkler /** 544688a9cceSTomas Winkler * mei_cl_device_match - device match function 545688a9cceSTomas Winkler * 546688a9cceSTomas Winkler * @dev: device 547688a9cceSTomas Winkler * @drv: driver 548688a9cceSTomas Winkler * 549688a9cceSTomas Winkler * Return: 1 if matching device was found 0 otherwise 550688a9cceSTomas Winkler */ 551688a9cceSTomas Winkler static int mei_cl_device_match(struct device *dev, struct device_driver *drv) 552688a9cceSTomas Winkler { 553688a9cceSTomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 554688a9cceSTomas Winkler struct mei_cl_driver *cldrv = to_mei_cl_driver(drv); 555688a9cceSTomas Winkler const struct mei_cl_device_id *found_id; 556688a9cceSTomas Winkler 557688a9cceSTomas Winkler if (!cldev) 558688a9cceSTomas Winkler return 0; 559688a9cceSTomas Winkler 56071ce7891STomas Winkler if (!cldev->do_match) 56171ce7891STomas Winkler return 0; 56271ce7891STomas Winkler 563688a9cceSTomas Winkler if (!cldrv || !cldrv->id_table) 564688a9cceSTomas Winkler return 0; 565688a9cceSTomas Winkler 566688a9cceSTomas Winkler found_id = mei_cl_device_find(cldev, cldrv); 567688a9cceSTomas Winkler if (found_id) 568688a9cceSTomas Winkler return 1; 569688a9cceSTomas Winkler 570e5354107SSamuel Ortiz return 0; 571e5354107SSamuel Ortiz } 572e5354107SSamuel Ortiz 573feb8cd0fSTomas Winkler /** 574feb8cd0fSTomas Winkler * mei_cl_device_probe - bus probe function 575feb8cd0fSTomas Winkler * 576feb8cd0fSTomas Winkler * @dev: device 577feb8cd0fSTomas Winkler * 578feb8cd0fSTomas Winkler * Return: 0 on success; < 0 otherwise 579feb8cd0fSTomas Winkler */ 580e5354107SSamuel Ortiz static int mei_cl_device_probe(struct device *dev) 581e5354107SSamuel Ortiz { 582feb8cd0fSTomas Winkler struct mei_cl_device *cldev; 583b37719c3STomas Winkler struct mei_cl_driver *cldrv; 584feb8cd0fSTomas Winkler const struct mei_cl_device_id *id; 585feb8cd0fSTomas Winkler 586feb8cd0fSTomas Winkler cldev = to_mei_cl_device(dev); 587feb8cd0fSTomas Winkler cldrv = to_mei_cl_driver(dev->driver); 588e5354107SSamuel Ortiz 589b37719c3STomas Winkler if (!cldev) 590e5354107SSamuel Ortiz return 0; 591e5354107SSamuel Ortiz 592b37719c3STomas Winkler if (!cldrv || !cldrv->probe) 593e5354107SSamuel Ortiz return -ENODEV; 594e5354107SSamuel Ortiz 595feb8cd0fSTomas Winkler id = mei_cl_device_find(cldev, cldrv); 596feb8cd0fSTomas Winkler if (!id) 597feb8cd0fSTomas Winkler return -ENODEV; 598e5354107SSamuel Ortiz 599feb8cd0fSTomas Winkler __module_get(THIS_MODULE); 600e5354107SSamuel Ortiz 601feb8cd0fSTomas Winkler return cldrv->probe(cldev, id); 602e5354107SSamuel Ortiz } 603e5354107SSamuel Ortiz 604feb8cd0fSTomas Winkler /** 605feb8cd0fSTomas Winkler * mei_cl_device_remove - remove device from the bus 606feb8cd0fSTomas Winkler * 607feb8cd0fSTomas Winkler * @dev: device 608feb8cd0fSTomas Winkler * 609feb8cd0fSTomas Winkler * Return: 0 on success; < 0 otherwise 610feb8cd0fSTomas Winkler */ 611e5354107SSamuel Ortiz static int mei_cl_device_remove(struct device *dev) 612e5354107SSamuel Ortiz { 613b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 614b37719c3STomas Winkler struct mei_cl_driver *cldrv; 615feb8cd0fSTomas Winkler int ret = 0; 616e5354107SSamuel Ortiz 617b37719c3STomas Winkler if (!cldev || !dev->driver) 618e5354107SSamuel Ortiz return 0; 619e5354107SSamuel Ortiz 620b37719c3STomas Winkler if (cldev->event_cb) { 621b37719c3STomas Winkler cldev->event_cb = NULL; 622b37719c3STomas Winkler cancel_work_sync(&cldev->event_work); 6233e833295SSamuel Ortiz } 6243e833295SSamuel Ortiz 625b37719c3STomas Winkler cldrv = to_mei_cl_driver(dev->driver); 626feb8cd0fSTomas Winkler if (cldrv->remove) 627feb8cd0fSTomas Winkler ret = cldrv->remove(cldev); 628feb8cd0fSTomas Winkler 629feb8cd0fSTomas Winkler module_put(THIS_MODULE); 630e5354107SSamuel Ortiz dev->driver = NULL; 631feb8cd0fSTomas Winkler return ret; 632e5354107SSamuel Ortiz 633e5354107SSamuel Ortiz } 634e5354107SSamuel Ortiz 635007d64ebSTomas Winkler static ssize_t name_show(struct device *dev, struct device_attribute *a, 636007d64ebSTomas Winkler char *buf) 637007d64ebSTomas Winkler { 638b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 639007d64ebSTomas Winkler size_t len; 640007d64ebSTomas Winkler 641b37719c3STomas Winkler len = snprintf(buf, PAGE_SIZE, "%s", cldev->name); 642007d64ebSTomas Winkler 643007d64ebSTomas Winkler return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; 644007d64ebSTomas Winkler } 645007d64ebSTomas Winkler static DEVICE_ATTR_RO(name); 646007d64ebSTomas Winkler 647007d64ebSTomas Winkler static ssize_t uuid_show(struct device *dev, struct device_attribute *a, 648007d64ebSTomas Winkler char *buf) 649007d64ebSTomas Winkler { 650b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 651b37719c3STomas Winkler const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); 652007d64ebSTomas Winkler size_t len; 653007d64ebSTomas Winkler 654007d64ebSTomas Winkler len = snprintf(buf, PAGE_SIZE, "%pUl", uuid); 655007d64ebSTomas Winkler 656007d64ebSTomas Winkler return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; 657007d64ebSTomas Winkler } 658007d64ebSTomas Winkler static DEVICE_ATTR_RO(uuid); 659007d64ebSTomas Winkler 66040b7320eSTomas Winkler static ssize_t version_show(struct device *dev, struct device_attribute *a, 66140b7320eSTomas Winkler char *buf) 66240b7320eSTomas Winkler { 66340b7320eSTomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 66440b7320eSTomas Winkler u8 version = mei_me_cl_ver(cldev->me_cl); 66540b7320eSTomas Winkler size_t len; 66640b7320eSTomas Winkler 66740b7320eSTomas Winkler len = snprintf(buf, PAGE_SIZE, "%02X", version); 66840b7320eSTomas Winkler 66940b7320eSTomas Winkler return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; 67040b7320eSTomas Winkler } 67140b7320eSTomas Winkler static DEVICE_ATTR_RO(version); 67240b7320eSTomas Winkler 673e5354107SSamuel Ortiz static ssize_t modalias_show(struct device *dev, struct device_attribute *a, 674e5354107SSamuel Ortiz char *buf) 675e5354107SSamuel Ortiz { 676b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 677b37719c3STomas Winkler const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); 678c93b76b3STomas Winkler size_t len; 679e5354107SSamuel Ortiz 68059796edcSPrarit Bhargava len = snprintf(buf, PAGE_SIZE, "mei:%s:%pUl:", cldev->name, uuid); 681e5354107SSamuel Ortiz return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; 682e5354107SSamuel Ortiz } 68332f389ecSGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias); 684e5354107SSamuel Ortiz 685d49dc5e7STomas Winkler static struct attribute *mei_cldev_attrs[] = { 686007d64ebSTomas Winkler &dev_attr_name.attr, 687007d64ebSTomas Winkler &dev_attr_uuid.attr, 68840b7320eSTomas Winkler &dev_attr_version.attr, 68932f389ecSGreg Kroah-Hartman &dev_attr_modalias.attr, 69032f389ecSGreg Kroah-Hartman NULL, 691e5354107SSamuel Ortiz }; 692d49dc5e7STomas Winkler ATTRIBUTE_GROUPS(mei_cldev); 693e5354107SSamuel Ortiz 69438d3c00dSTomas Winkler /** 69538d3c00dSTomas Winkler * mei_cl_device_uevent - me client bus uevent handler 69638d3c00dSTomas Winkler * 69738d3c00dSTomas Winkler * @dev: device 69838d3c00dSTomas Winkler * @env: uevent kobject 69938d3c00dSTomas Winkler * 70038d3c00dSTomas Winkler * Return: 0 on success -ENOMEM on when add_uevent_var fails 70138d3c00dSTomas Winkler */ 70238d3c00dSTomas Winkler static int mei_cl_device_uevent(struct device *dev, struct kobj_uevent_env *env) 703e5354107SSamuel Ortiz { 704b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 705b37719c3STomas Winkler const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); 70640b7320eSTomas Winkler u8 version = mei_me_cl_ver(cldev->me_cl); 70740b7320eSTomas Winkler 70840b7320eSTomas Winkler if (add_uevent_var(env, "MEI_CL_VERSION=%d", version)) 70940b7320eSTomas Winkler return -ENOMEM; 710c93b76b3STomas Winkler 711007d64ebSTomas Winkler if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid)) 712007d64ebSTomas Winkler return -ENOMEM; 713007d64ebSTomas Winkler 714b37719c3STomas Winkler if (add_uevent_var(env, "MEI_CL_NAME=%s", cldev->name)) 715007d64ebSTomas Winkler return -ENOMEM; 716007d64ebSTomas Winkler 717b26864caSTomas Winkler if (add_uevent_var(env, "MODALIAS=mei:%s:%pUl:%02X:", 718b26864caSTomas Winkler cldev->name, uuid, version)) 719e5354107SSamuel Ortiz return -ENOMEM; 720e5354107SSamuel Ortiz 721e5354107SSamuel Ortiz return 0; 722e5354107SSamuel Ortiz } 723e5354107SSamuel Ortiz 724e5354107SSamuel Ortiz static struct bus_type mei_cl_bus_type = { 725e5354107SSamuel Ortiz .name = "mei", 726d49dc5e7STomas Winkler .dev_groups = mei_cldev_groups, 727e5354107SSamuel Ortiz .match = mei_cl_device_match, 728e5354107SSamuel Ortiz .probe = mei_cl_device_probe, 729e5354107SSamuel Ortiz .remove = mei_cl_device_remove, 73038d3c00dSTomas Winkler .uevent = mei_cl_device_uevent, 731e5354107SSamuel Ortiz }; 732e5354107SSamuel Ortiz 733512f64d9STomas Winkler static struct mei_device *mei_dev_bus_get(struct mei_device *bus) 734512f64d9STomas Winkler { 735512f64d9STomas Winkler if (bus) 736512f64d9STomas Winkler get_device(bus->dev); 737512f64d9STomas Winkler 738512f64d9STomas Winkler return bus; 739512f64d9STomas Winkler } 740512f64d9STomas Winkler 741512f64d9STomas Winkler static void mei_dev_bus_put(struct mei_device *bus) 742512f64d9STomas Winkler { 743512f64d9STomas Winkler if (bus) 744512f64d9STomas Winkler put_device(bus->dev); 745512f64d9STomas Winkler } 746512f64d9STomas Winkler 747ae48d74dSTomas Winkler static void mei_cl_bus_dev_release(struct device *dev) 748e5354107SSamuel Ortiz { 749b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 750d49ed64aSAlexander Usyskin 751b37719c3STomas Winkler if (!cldev) 752d49ed64aSAlexander Usyskin return; 753d49ed64aSAlexander Usyskin 754b37719c3STomas Winkler mei_me_cl_put(cldev->me_cl); 755512f64d9STomas Winkler mei_dev_bus_put(cldev->bus); 756b37719c3STomas Winkler kfree(cldev); 757e5354107SSamuel Ortiz } 758e5354107SSamuel Ortiz 759e5354107SSamuel Ortiz static struct device_type mei_cl_device_type = { 760ae48d74dSTomas Winkler .release = mei_cl_bus_dev_release, 761e5354107SSamuel Ortiz }; 762e5354107SSamuel Ortiz 76371ce7891STomas Winkler /** 764213dd193STomas Winkler * mei_cl_bus_set_name - set device name for me client device 765213dd193STomas Winkler * 766213dd193STomas Winkler * @cldev: me client device 767213dd193STomas Winkler */ 768213dd193STomas Winkler static inline void mei_cl_bus_set_name(struct mei_cl_device *cldev) 769213dd193STomas Winkler { 770213dd193STomas Winkler dev_set_name(&cldev->dev, "mei:%s:%pUl:%02X", 771213dd193STomas Winkler cldev->name, 772213dd193STomas Winkler mei_me_cl_uuid(cldev->me_cl), 773213dd193STomas Winkler mei_me_cl_ver(cldev->me_cl)); 774213dd193STomas Winkler } 775213dd193STomas Winkler 776213dd193STomas Winkler /** 777ae48d74dSTomas Winkler * mei_cl_bus_dev_alloc - initialize and allocate mei client device 77871ce7891STomas Winkler * 77971ce7891STomas Winkler * @bus: mei device 78071ce7891STomas Winkler * @me_cl: me client 78171ce7891STomas Winkler * 78271ce7891STomas Winkler * Return: allocated device structur or NULL on allocation failure 78371ce7891STomas Winkler */ 784ae48d74dSTomas Winkler static struct mei_cl_device *mei_cl_bus_dev_alloc(struct mei_device *bus, 78571ce7891STomas Winkler struct mei_me_client *me_cl) 78671ce7891STomas Winkler { 78771ce7891STomas Winkler struct mei_cl_device *cldev; 78871ce7891STomas Winkler 78971ce7891STomas Winkler cldev = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); 79071ce7891STomas Winkler if (!cldev) 79171ce7891STomas Winkler return NULL; 79271ce7891STomas Winkler 79371ce7891STomas Winkler device_initialize(&cldev->dev); 79471ce7891STomas Winkler cldev->dev.parent = bus->dev; 79571ce7891STomas Winkler cldev->dev.bus = &mei_cl_bus_type; 79671ce7891STomas Winkler cldev->dev.type = &mei_cl_device_type; 79771ce7891STomas Winkler cldev->bus = mei_dev_bus_get(bus); 79871ce7891STomas Winkler cldev->me_cl = mei_me_cl_get(me_cl); 799213dd193STomas Winkler mei_cl_bus_set_name(cldev); 80071ce7891STomas Winkler cldev->is_added = 0; 80171ce7891STomas Winkler INIT_LIST_HEAD(&cldev->bus_list); 80271ce7891STomas Winkler 80371ce7891STomas Winkler return cldev; 80471ce7891STomas Winkler } 80571ce7891STomas Winkler 80671ce7891STomas Winkler /** 80771ce7891STomas Winkler * mei_cl_dev_setup - setup me client device 80871ce7891STomas Winkler * run fix up routines and set the device name 80971ce7891STomas Winkler * 81071ce7891STomas Winkler * @bus: mei device 81171ce7891STomas Winkler * @cldev: me client device 81271ce7891STomas Winkler * 81371ce7891STomas Winkler * Return: true if the device is eligible for enumeration 81471ce7891STomas Winkler */ 815ae48d74dSTomas Winkler static bool mei_cl_bus_dev_setup(struct mei_device *bus, 81671ce7891STomas Winkler struct mei_cl_device *cldev) 81771ce7891STomas Winkler { 81871ce7891STomas Winkler cldev->do_match = 1; 819ae48d74dSTomas Winkler mei_cl_bus_dev_fixup(cldev); 82071ce7891STomas Winkler 821213dd193STomas Winkler /* the device name can change during fix up */ 82271ce7891STomas Winkler if (cldev->do_match) 823213dd193STomas Winkler mei_cl_bus_set_name(cldev); 82471ce7891STomas Winkler 82571ce7891STomas Winkler return cldev->do_match == 1; 82671ce7891STomas Winkler } 82771ce7891STomas Winkler 82871ce7891STomas Winkler /** 82971ce7891STomas Winkler * mei_cl_bus_dev_add - add me client devices 83071ce7891STomas Winkler * 83171ce7891STomas Winkler * @cldev: me client device 83271ce7891STomas Winkler * 83371ce7891STomas Winkler * Return: 0 on success; < 0 on failre 83471ce7891STomas Winkler */ 83571ce7891STomas Winkler static int mei_cl_bus_dev_add(struct mei_cl_device *cldev) 83671ce7891STomas Winkler { 83771ce7891STomas Winkler int ret; 83871ce7891STomas Winkler 839b26864caSTomas Winkler dev_dbg(cldev->bus->dev, "adding %pUL:%02X\n", 840b26864caSTomas Winkler mei_me_cl_uuid(cldev->me_cl), 841b26864caSTomas Winkler mei_me_cl_ver(cldev->me_cl)); 84271ce7891STomas Winkler ret = device_add(&cldev->dev); 84371ce7891STomas Winkler if (!ret) 84471ce7891STomas Winkler cldev->is_added = 1; 84571ce7891STomas Winkler 84671ce7891STomas Winkler return ret; 84771ce7891STomas Winkler } 84871ce7891STomas Winkler 8496009595aSTomas Winkler /** 8506009595aSTomas Winkler * mei_cl_bus_dev_stop - stop the driver 8516009595aSTomas Winkler * 8526009595aSTomas Winkler * @cldev: me client device 8536009595aSTomas Winkler */ 8546009595aSTomas Winkler static void mei_cl_bus_dev_stop(struct mei_cl_device *cldev) 8556009595aSTomas Winkler { 8566009595aSTomas Winkler if (cldev->is_added) 8576009595aSTomas Winkler device_release_driver(&cldev->dev); 8586009595aSTomas Winkler } 8596009595aSTomas Winkler 8606009595aSTomas Winkler /** 8616009595aSTomas Winkler * mei_cl_bus_dev_destroy - destroy me client devices object 8626009595aSTomas Winkler * 8636009595aSTomas Winkler * @cldev: me client device 8642da55cfdSTomas Winkler * 8652da55cfdSTomas Winkler * Locking: called under "dev->cl_bus_lock" lock 8666009595aSTomas Winkler */ 8676009595aSTomas Winkler static void mei_cl_bus_dev_destroy(struct mei_cl_device *cldev) 8686009595aSTomas Winkler { 8692da55cfdSTomas Winkler 8702da55cfdSTomas Winkler WARN_ON(!mutex_is_locked(&cldev->bus->cl_bus_lock)); 8712da55cfdSTomas Winkler 8726009595aSTomas Winkler if (!cldev->is_added) 8736009595aSTomas Winkler return; 8746009595aSTomas Winkler 8756009595aSTomas Winkler device_del(&cldev->dev); 8766009595aSTomas Winkler 8776009595aSTomas Winkler list_del_init(&cldev->bus_list); 8786009595aSTomas Winkler 8796009595aSTomas Winkler cldev->is_added = 0; 8806009595aSTomas Winkler put_device(&cldev->dev); 8816009595aSTomas Winkler } 8826009595aSTomas Winkler 8836009595aSTomas Winkler /** 8846009595aSTomas Winkler * mei_cl_bus_remove_device - remove a devices form the bus 8856009595aSTomas Winkler * 8866009595aSTomas Winkler * @cldev: me client device 8876009595aSTomas Winkler */ 8886009595aSTomas Winkler static void mei_cl_bus_remove_device(struct mei_cl_device *cldev) 8896009595aSTomas Winkler { 8906009595aSTomas Winkler mei_cl_bus_dev_stop(cldev); 8916009595aSTomas Winkler mei_cl_bus_dev_destroy(cldev); 8926009595aSTomas Winkler } 8936009595aSTomas Winkler 8946009595aSTomas Winkler /** 8956009595aSTomas Winkler * mei_cl_bus_remove_devices - remove all devices form the bus 8966009595aSTomas Winkler * 8976009595aSTomas Winkler * @bus: mei device 8986009595aSTomas Winkler */ 8996009595aSTomas Winkler void mei_cl_bus_remove_devices(struct mei_device *bus) 9006009595aSTomas Winkler { 9016009595aSTomas Winkler struct mei_cl_device *cldev, *next; 9026009595aSTomas Winkler 9032da55cfdSTomas Winkler mutex_lock(&bus->cl_bus_lock); 9046009595aSTomas Winkler list_for_each_entry_safe(cldev, next, &bus->device_list, bus_list) 9056009595aSTomas Winkler mei_cl_bus_remove_device(cldev); 9062da55cfdSTomas Winkler mutex_unlock(&bus->cl_bus_lock); 9076009595aSTomas Winkler } 9086009595aSTomas Winkler 9096009595aSTomas Winkler 9106009595aSTomas Winkler /** 911ae48d74dSTomas Winkler * mei_cl_bus_dev_init - allocate and initializes an mei client devices 9126009595aSTomas Winkler * based on me client 9136009595aSTomas Winkler * 9146009595aSTomas Winkler * @bus: mei device 9156009595aSTomas Winkler * @me_cl: me client 9162da55cfdSTomas Winkler * 9172da55cfdSTomas Winkler * Locking: called under "dev->cl_bus_lock" lock 9186009595aSTomas Winkler */ 919ae48d74dSTomas Winkler static void mei_cl_bus_dev_init(struct mei_device *bus, 920ae48d74dSTomas Winkler struct mei_me_client *me_cl) 921e5354107SSamuel Ortiz { 922b37719c3STomas Winkler struct mei_cl_device *cldev; 9236009595aSTomas Winkler 9242da55cfdSTomas Winkler WARN_ON(!mutex_is_locked(&bus->cl_bus_lock)); 9252da55cfdSTomas Winkler 9266009595aSTomas Winkler dev_dbg(bus->dev, "initializing %pUl", mei_me_cl_uuid(me_cl)); 9276009595aSTomas Winkler 9286009595aSTomas Winkler if (me_cl->bus_added) 9296009595aSTomas Winkler return; 930e5354107SSamuel Ortiz 931ae48d74dSTomas Winkler cldev = mei_cl_bus_dev_alloc(bus, me_cl); 932b37719c3STomas Winkler if (!cldev) 9336009595aSTomas Winkler return; 934e5354107SSamuel Ortiz 9356009595aSTomas Winkler me_cl->bus_added = true; 9366009595aSTomas Winkler list_add_tail(&cldev->bus_list, &bus->device_list); 937c93b76b3STomas Winkler 938a7b71bc0SSamuel Ortiz } 939a7b71bc0SSamuel Ortiz 9406009595aSTomas Winkler /** 9416009595aSTomas Winkler * mei_cl_bus_rescan - scan me clients list and add create 9426009595aSTomas Winkler * devices for eligible clients 9436009595aSTomas Winkler * 9446009595aSTomas Winkler * @bus: mei device 9456009595aSTomas Winkler */ 9466009595aSTomas Winkler void mei_cl_bus_rescan(struct mei_device *bus) 947e5354107SSamuel Ortiz { 9486009595aSTomas Winkler struct mei_cl_device *cldev, *n; 9496009595aSTomas Winkler struct mei_me_client *me_cl; 9506009595aSTomas Winkler 9512da55cfdSTomas Winkler mutex_lock(&bus->cl_bus_lock); 9522da55cfdSTomas Winkler 9536009595aSTomas Winkler down_read(&bus->me_clients_rwsem); 9546009595aSTomas Winkler list_for_each_entry(me_cl, &bus->me_clients, list) 955ae48d74dSTomas Winkler mei_cl_bus_dev_init(bus, me_cl); 9566009595aSTomas Winkler up_read(&bus->me_clients_rwsem); 9576009595aSTomas Winkler 9586009595aSTomas Winkler list_for_each_entry_safe(cldev, n, &bus->device_list, bus_list) { 9596009595aSTomas Winkler 9606009595aSTomas Winkler if (!mei_me_cl_is_active(cldev->me_cl)) { 9616009595aSTomas Winkler mei_cl_bus_remove_device(cldev); 9626009595aSTomas Winkler continue; 963e5354107SSamuel Ortiz } 9646009595aSTomas Winkler 9656009595aSTomas Winkler if (cldev->is_added) 9666009595aSTomas Winkler continue; 9676009595aSTomas Winkler 968ae48d74dSTomas Winkler if (mei_cl_bus_dev_setup(bus, cldev)) 9696009595aSTomas Winkler mei_cl_bus_dev_add(cldev); 9706009595aSTomas Winkler else { 9716009595aSTomas Winkler list_del_init(&cldev->bus_list); 9726009595aSTomas Winkler put_device(&cldev->dev); 9736009595aSTomas Winkler } 9746009595aSTomas Winkler } 9756009595aSTomas Winkler mutex_unlock(&bus->cl_bus_lock); 9766009595aSTomas Winkler 9776009595aSTomas Winkler dev_dbg(bus->dev, "rescan end"); 9786009595aSTomas Winkler } 979333e4ee0SSamuel Ortiz 980d49dc5e7STomas Winkler int __mei_cldev_driver_register(struct mei_cl_driver *cldrv, 981d49dc5e7STomas Winkler struct module *owner) 982333e4ee0SSamuel Ortiz { 983333e4ee0SSamuel Ortiz int err; 984333e4ee0SSamuel Ortiz 985b37719c3STomas Winkler cldrv->driver.name = cldrv->name; 986b37719c3STomas Winkler cldrv->driver.owner = owner; 987b37719c3STomas Winkler cldrv->driver.bus = &mei_cl_bus_type; 988333e4ee0SSamuel Ortiz 989b37719c3STomas Winkler err = driver_register(&cldrv->driver); 990333e4ee0SSamuel Ortiz if (err) 991333e4ee0SSamuel Ortiz return err; 992333e4ee0SSamuel Ortiz 993b37719c3STomas Winkler pr_debug("mei: driver [%s] registered\n", cldrv->driver.name); 994333e4ee0SSamuel Ortiz 995333e4ee0SSamuel Ortiz return 0; 996333e4ee0SSamuel Ortiz } 997d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(__mei_cldev_driver_register); 998333e4ee0SSamuel Ortiz 999d49dc5e7STomas Winkler void mei_cldev_driver_unregister(struct mei_cl_driver *cldrv) 1000333e4ee0SSamuel Ortiz { 1001b37719c3STomas Winkler driver_unregister(&cldrv->driver); 1002333e4ee0SSamuel Ortiz 1003b37719c3STomas Winkler pr_debug("mei: driver [%s] unregistered\n", cldrv->driver.name); 1004333e4ee0SSamuel Ortiz } 1005d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_driver_unregister); 10063e833295SSamuel Ortiz 10076009595aSTomas Winkler 1008cf3baefbSSamuel Ortiz int __init mei_cl_bus_init(void) 1009cf3baefbSSamuel Ortiz { 1010cf3baefbSSamuel Ortiz return bus_register(&mei_cl_bus_type); 1011cf3baefbSSamuel Ortiz } 1012cf3baefbSSamuel Ortiz 1013cf3baefbSSamuel Ortiz void __exit mei_cl_bus_exit(void) 1014cf3baefbSSamuel Ortiz { 1015cf3baefbSSamuel Ortiz bus_unregister(&mei_cl_bus_type); 1016cf3baefbSSamuel Ortiz } 1017