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; 476cbb097fSAlexander Usyskin struct mei_cl_cb *cb; 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 9062382997STomas Winkler return rets; 9162382997STomas Winkler } 9262382997STomas Winkler 9362382997STomas Winkler /** 9462382997STomas Winkler * __mei_cl_recv - internal client receive (read) 9562382997STomas Winkler * 9662382997STomas Winkler * @cl: host client 97df7f5447STomas Winkler * @buf: buffer to receive 9862382997STomas Winkler * @length: buffer length 9962382997STomas Winkler * 10062382997STomas Winkler * Return: read size in bytes of < 0 on error 10162382997STomas Winkler */ 10262382997STomas Winkler ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) 10362382997STomas Winkler { 10462382997STomas Winkler struct mei_device *bus; 10562382997STomas Winkler struct mei_cl_cb *cb; 10662382997STomas Winkler size_t r_length; 10762382997STomas Winkler ssize_t rets; 10862382997STomas Winkler 10962382997STomas Winkler if (WARN_ON(!cl || !cl->dev)) 11062382997STomas Winkler return -ENODEV; 11162382997STomas Winkler 11262382997STomas Winkler bus = cl->dev; 11362382997STomas Winkler 11462382997STomas Winkler mutex_lock(&bus->device_lock); 11515c13dfcSAlexander Usyskin if (bus->dev_state != MEI_DEV_ENABLED) { 11615c13dfcSAlexander Usyskin rets = -ENODEV; 11715c13dfcSAlexander Usyskin goto out; 11815c13dfcSAlexander Usyskin } 11962382997STomas Winkler 12062382997STomas Winkler cb = mei_cl_read_cb(cl, NULL); 12162382997STomas Winkler if (cb) 12262382997STomas Winkler goto copy; 12362382997STomas Winkler 12462382997STomas Winkler rets = mei_cl_read_start(cl, length, NULL); 12562382997STomas Winkler if (rets && rets != -EBUSY) 12662382997STomas Winkler goto out; 12762382997STomas Winkler 12862382997STomas Winkler /* wait on event only if there is no other waiter */ 1291eb5bd4dSAlexander Usyskin /* synchronized under device mutex */ 1301eb5bd4dSAlexander Usyskin if (!waitqueue_active(&cl->rx_wait)) { 13162382997STomas Winkler 13262382997STomas Winkler mutex_unlock(&bus->device_lock); 13362382997STomas Winkler 13462382997STomas Winkler if (wait_event_interruptible(cl->rx_wait, 13562382997STomas Winkler (!list_empty(&cl->rd_completed)) || 13662382997STomas Winkler (!mei_cl_is_connected(cl)))) { 13762382997STomas Winkler 13862382997STomas Winkler if (signal_pending(current)) 13962382997STomas Winkler return -EINTR; 14062382997STomas Winkler return -ERESTARTSYS; 14162382997STomas Winkler } 14262382997STomas Winkler 14362382997STomas Winkler mutex_lock(&bus->device_lock); 14462382997STomas Winkler 14562382997STomas Winkler if (!mei_cl_is_connected(cl)) { 1462d4d5481STomas Winkler rets = -ENODEV; 14762382997STomas Winkler goto out; 14862382997STomas Winkler } 14962382997STomas Winkler } 15062382997STomas Winkler 15162382997STomas Winkler cb = mei_cl_read_cb(cl, NULL); 15262382997STomas Winkler if (!cb) { 15362382997STomas Winkler rets = 0; 15462382997STomas Winkler goto out; 15562382997STomas Winkler } 15662382997STomas Winkler 15762382997STomas Winkler copy: 15862382997STomas Winkler if (cb->status) { 15962382997STomas Winkler rets = cb->status; 16062382997STomas Winkler goto free; 16162382997STomas Winkler } 16262382997STomas Winkler 16362382997STomas Winkler r_length = min_t(size_t, length, cb->buf_idx); 16462382997STomas Winkler memcpy(buf, cb->buf.data, r_length); 16562382997STomas Winkler rets = r_length; 16662382997STomas Winkler 16762382997STomas Winkler free: 16862382997STomas Winkler mei_io_cb_free(cb); 16962382997STomas Winkler out: 17062382997STomas Winkler mutex_unlock(&bus->device_lock); 17162382997STomas Winkler 17262382997STomas Winkler return rets; 17362382997STomas Winkler } 17462382997STomas Winkler 17562382997STomas Winkler /** 176d49dc5e7STomas Winkler * mei_cldev_send - me device send (write) 17762382997STomas Winkler * 17862382997STomas Winkler * @cldev: me client device 17962382997STomas Winkler * @buf: buffer to send 18062382997STomas Winkler * @length: buffer length 18162382997STomas Winkler * 18262382997STomas Winkler * Return: written size in bytes or < 0 on error 18362382997STomas Winkler */ 184d49dc5e7STomas Winkler ssize_t mei_cldev_send(struct mei_cl_device *cldev, u8 *buf, size_t length) 18562382997STomas Winkler { 18662382997STomas Winkler struct mei_cl *cl = cldev->cl; 18762382997STomas Winkler 18862382997STomas Winkler if (cl == NULL) 18962382997STomas Winkler return -ENODEV; 19062382997STomas Winkler 19162382997STomas Winkler return __mei_cl_send(cl, buf, length, 1); 19262382997STomas Winkler } 193d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_send); 19462382997STomas Winkler 19562382997STomas Winkler /** 196d49dc5e7STomas Winkler * mei_cldev_recv - client receive (read) 19762382997STomas Winkler * 19862382997STomas Winkler * @cldev: me client device 199df7f5447STomas Winkler * @buf: buffer to receive 20062382997STomas Winkler * @length: buffer length 20162382997STomas Winkler * 20262382997STomas Winkler * Return: read size in bytes of < 0 on error 20362382997STomas Winkler */ 204d49dc5e7STomas Winkler ssize_t mei_cldev_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) 20562382997STomas Winkler { 20662382997STomas Winkler struct mei_cl *cl = cldev->cl; 20762382997STomas Winkler 20862382997STomas Winkler if (cl == NULL) 20962382997STomas Winkler return -ENODEV; 21062382997STomas Winkler 21162382997STomas Winkler return __mei_cl_recv(cl, buf, length); 21262382997STomas Winkler } 213d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_recv); 21462382997STomas Winkler 21562382997STomas Winkler /** 216d49dc5e7STomas Winkler * mei_cl_bus_event_work - dispatch rx event for a bus device 21762382997STomas Winkler * and schedule new work 21862382997STomas Winkler * 21962382997STomas Winkler * @work: work 22062382997STomas Winkler */ 221d49dc5e7STomas Winkler static void mei_cl_bus_event_work(struct work_struct *work) 22262382997STomas Winkler { 22362382997STomas Winkler struct mei_cl_device *cldev; 224bc46b45aSAlexander Usyskin struct mei_device *bus; 22562382997STomas Winkler 22662382997STomas Winkler cldev = container_of(work, struct mei_cl_device, event_work); 22762382997STomas Winkler 228bc46b45aSAlexander Usyskin bus = cldev->bus; 229bc46b45aSAlexander Usyskin 23062382997STomas Winkler if (cldev->event_cb) 23162382997STomas Winkler cldev->event_cb(cldev, cldev->events, cldev->event_context); 23262382997STomas Winkler 23362382997STomas Winkler cldev->events = 0; 23462382997STomas Winkler 23562382997STomas Winkler /* Prepare for the next read */ 236bc46b45aSAlexander Usyskin if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) { 237bc46b45aSAlexander Usyskin mutex_lock(&bus->device_lock); 238*3030dc05STomas Winkler mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL); 239bc46b45aSAlexander Usyskin mutex_unlock(&bus->device_lock); 240bc46b45aSAlexander Usyskin } 24162382997STomas Winkler } 24262382997STomas Winkler 24362382997STomas Winkler /** 244bb2ef9c3SAlexander Usyskin * mei_cl_bus_notify_event - schedule notify cb on bus client 245bb2ef9c3SAlexander Usyskin * 246bb2ef9c3SAlexander Usyskin * @cl: host client 247850f8940STomas Winkler * 248850f8940STomas Winkler * Return: true if event was scheduled 249850f8940STomas Winkler * false if the client is not waiting for event 250bb2ef9c3SAlexander Usyskin */ 251850f8940STomas Winkler bool mei_cl_bus_notify_event(struct mei_cl *cl) 252bb2ef9c3SAlexander Usyskin { 253bb2ef9c3SAlexander Usyskin struct mei_cl_device *cldev = cl->cldev; 254bb2ef9c3SAlexander Usyskin 255bb2ef9c3SAlexander Usyskin if (!cldev || !cldev->event_cb) 256850f8940STomas Winkler return false; 257bb2ef9c3SAlexander Usyskin 258bb2ef9c3SAlexander Usyskin if (!(cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF))) 259850f8940STomas Winkler return false; 260bb2ef9c3SAlexander Usyskin 261bb2ef9c3SAlexander Usyskin if (!cl->notify_ev) 262850f8940STomas Winkler return false; 263bb2ef9c3SAlexander Usyskin 264bb2ef9c3SAlexander Usyskin set_bit(MEI_CL_EVENT_NOTIF, &cldev->events); 265bb2ef9c3SAlexander Usyskin 266bb2ef9c3SAlexander Usyskin schedule_work(&cldev->event_work); 267bb2ef9c3SAlexander Usyskin 268bb2ef9c3SAlexander Usyskin cl->notify_ev = false; 269850f8940STomas Winkler 270850f8940STomas Winkler return true; 271bb2ef9c3SAlexander Usyskin } 272bb2ef9c3SAlexander Usyskin 273bb2ef9c3SAlexander Usyskin /** 274a1f9ae2bSTomas Winkler * mei_cl_bus_rx_event - schedule rx event 27562382997STomas Winkler * 27662382997STomas Winkler * @cl: host client 277a1f9ae2bSTomas Winkler * 278a1f9ae2bSTomas Winkler * Return: true if event was scheduled 279a1f9ae2bSTomas Winkler * false if the client is not waiting for event 28062382997STomas Winkler */ 281a1f9ae2bSTomas Winkler bool mei_cl_bus_rx_event(struct mei_cl *cl) 28262382997STomas Winkler { 28362382997STomas Winkler struct mei_cl_device *cldev = cl->cldev; 28462382997STomas Winkler 28562382997STomas Winkler if (!cldev || !cldev->event_cb) 286a1f9ae2bSTomas Winkler return false; 28762382997STomas Winkler 288bb2ef9c3SAlexander Usyskin if (!(cldev->events_mask & BIT(MEI_CL_EVENT_RX))) 289a1f9ae2bSTomas Winkler return false; 290bb2ef9c3SAlexander Usyskin 29162382997STomas Winkler set_bit(MEI_CL_EVENT_RX, &cldev->events); 29262382997STomas Winkler 29362382997STomas Winkler schedule_work(&cldev->event_work); 294a1f9ae2bSTomas Winkler 295a1f9ae2bSTomas Winkler return true; 29662382997STomas Winkler } 29762382997STomas Winkler 29862382997STomas Winkler /** 299d49dc5e7STomas Winkler * mei_cldev_register_event_cb - register event callback 30062382997STomas Winkler * 30162382997STomas Winkler * @cldev: me client devices 30262382997STomas Winkler * @event_cb: callback function 303bb2ef9c3SAlexander Usyskin * @events_mask: requested events bitmask 30462382997STomas Winkler * @context: driver context data 30562382997STomas Winkler * 30662382997STomas Winkler * Return: 0 on success 30762382997STomas Winkler * -EALREADY if an callback is already registered 30862382997STomas Winkler * <0 on other errors 30962382997STomas Winkler */ 310d49dc5e7STomas Winkler int mei_cldev_register_event_cb(struct mei_cl_device *cldev, 311bb2ef9c3SAlexander Usyskin unsigned long events_mask, 312d49dc5e7STomas Winkler mei_cldev_event_cb_t event_cb, void *context) 31362382997STomas Winkler { 314bc46b45aSAlexander Usyskin struct mei_device *bus = cldev->bus; 31548168f45STomas Winkler int ret; 31648168f45STomas Winkler 31762382997STomas Winkler if (cldev->event_cb) 31862382997STomas Winkler return -EALREADY; 31962382997STomas Winkler 32062382997STomas Winkler cldev->events = 0; 321bb2ef9c3SAlexander Usyskin cldev->events_mask = events_mask; 32262382997STomas Winkler cldev->event_cb = event_cb; 32362382997STomas Winkler cldev->event_context = context; 324d49dc5e7STomas Winkler INIT_WORK(&cldev->event_work, mei_cl_bus_event_work); 32562382997STomas Winkler 326bb2ef9c3SAlexander Usyskin if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) { 327bc46b45aSAlexander Usyskin mutex_lock(&bus->device_lock); 328*3030dc05STomas Winkler ret = mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL); 329bc46b45aSAlexander Usyskin mutex_unlock(&bus->device_lock); 33048168f45STomas Winkler if (ret && ret != -EBUSY) 33148168f45STomas Winkler return ret; 332bb2ef9c3SAlexander Usyskin } 333bb2ef9c3SAlexander Usyskin 334bb2ef9c3SAlexander Usyskin if (cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF)) { 335bc46b45aSAlexander Usyskin mutex_lock(&bus->device_lock); 336bb2ef9c3SAlexander Usyskin ret = mei_cl_notify_request(cldev->cl, NULL, event_cb ? 1 : 0); 337bc46b45aSAlexander Usyskin mutex_unlock(&bus->device_lock); 338bb2ef9c3SAlexander Usyskin if (ret) 339bb2ef9c3SAlexander Usyskin return ret; 340bb2ef9c3SAlexander Usyskin } 34162382997STomas Winkler 34262382997STomas Winkler return 0; 34362382997STomas Winkler } 344d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_register_event_cb); 34562382997STomas Winkler 34662382997STomas Winkler /** 347d49dc5e7STomas Winkler * mei_cldev_get_drvdata - driver data getter 34862382997STomas Winkler * 34962382997STomas Winkler * @cldev: mei client device 35062382997STomas Winkler * 35162382997STomas Winkler * Return: driver private data 35262382997STomas Winkler */ 353d49dc5e7STomas Winkler void *mei_cldev_get_drvdata(const struct mei_cl_device *cldev) 35462382997STomas Winkler { 35562382997STomas Winkler return dev_get_drvdata(&cldev->dev); 35662382997STomas Winkler } 357d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_get_drvdata); 35862382997STomas Winkler 35962382997STomas Winkler /** 360d49dc5e7STomas Winkler * mei_cldev_set_drvdata - driver data setter 36162382997STomas Winkler * 36262382997STomas Winkler * @cldev: mei client device 36362382997STomas Winkler * @data: data to store 36462382997STomas Winkler */ 365d49dc5e7STomas Winkler void mei_cldev_set_drvdata(struct mei_cl_device *cldev, void *data) 36662382997STomas Winkler { 36762382997STomas Winkler dev_set_drvdata(&cldev->dev, data); 36862382997STomas Winkler } 369d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_set_drvdata); 37062382997STomas Winkler 37162382997STomas Winkler /** 372baeacd03STomas Winkler * mei_cldev_uuid - return uuid of the underlying me client 373baeacd03STomas Winkler * 374baeacd03STomas Winkler * @cldev: mei client device 375baeacd03STomas Winkler * 376baeacd03STomas Winkler * Return: me client uuid 377baeacd03STomas Winkler */ 378baeacd03STomas Winkler const uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev) 379baeacd03STomas Winkler { 380baeacd03STomas Winkler return mei_me_cl_uuid(cldev->me_cl); 381baeacd03STomas Winkler } 382baeacd03STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_uuid); 383baeacd03STomas Winkler 384baeacd03STomas Winkler /** 385baeacd03STomas Winkler * mei_cldev_ver - return protocol version of the underlying me client 386baeacd03STomas Winkler * 387baeacd03STomas Winkler * @cldev: mei client device 388baeacd03STomas Winkler * 389baeacd03STomas Winkler * Return: me client protocol version 390baeacd03STomas Winkler */ 391baeacd03STomas Winkler u8 mei_cldev_ver(const struct mei_cl_device *cldev) 392baeacd03STomas Winkler { 393baeacd03STomas Winkler return mei_me_cl_ver(cldev->me_cl); 394baeacd03STomas Winkler } 395baeacd03STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_ver); 396baeacd03STomas Winkler 397baeacd03STomas Winkler /** 39801a14edeSTomas Winkler * mei_cldev_enabled - check whether the device is enabled 39901a14edeSTomas Winkler * 40001a14edeSTomas Winkler * @cldev: mei client device 40101a14edeSTomas Winkler * 40201a14edeSTomas Winkler * Return: true if me client is initialized and connected 40301a14edeSTomas Winkler */ 40401a14edeSTomas Winkler bool mei_cldev_enabled(struct mei_cl_device *cldev) 40501a14edeSTomas Winkler { 40601a14edeSTomas Winkler return cldev->cl && mei_cl_is_connected(cldev->cl); 40701a14edeSTomas Winkler } 40801a14edeSTomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_enabled); 40901a14edeSTomas Winkler 41001a14edeSTomas Winkler /** 411d49dc5e7STomas Winkler * mei_cldev_enable_device - enable me client device 41262382997STomas Winkler * create connection with me client 41362382997STomas Winkler * 41462382997STomas Winkler * @cldev: me client device 41562382997STomas Winkler * 41662382997STomas Winkler * Return: 0 on success and < 0 on error 41762382997STomas Winkler */ 418d49dc5e7STomas Winkler int mei_cldev_enable(struct mei_cl_device *cldev) 41962382997STomas Winkler { 4206009595aSTomas Winkler struct mei_device *bus = cldev->bus; 4216009595aSTomas Winkler struct mei_cl *cl; 4226009595aSTomas Winkler int ret; 42362382997STomas Winkler 4246009595aSTomas Winkler cl = cldev->cl; 42562382997STomas Winkler 4266009595aSTomas Winkler if (!cl) { 4276009595aSTomas Winkler mutex_lock(&bus->device_lock); 4287851e008SAlexander Usyskin cl = mei_cl_alloc_linked(bus); 4296009595aSTomas Winkler mutex_unlock(&bus->device_lock); 4306009595aSTomas Winkler if (IS_ERR(cl)) 4316009595aSTomas Winkler return PTR_ERR(cl); 4326009595aSTomas Winkler /* update pointers */ 4336009595aSTomas Winkler cldev->cl = cl; 4346009595aSTomas Winkler cl->cldev = cldev; 4356009595aSTomas Winkler } 43662382997STomas Winkler 43762382997STomas Winkler mutex_lock(&bus->device_lock); 43862382997STomas Winkler if (mei_cl_is_connected(cl)) { 4396009595aSTomas Winkler ret = 0; 4406009595aSTomas Winkler goto out; 44162382997STomas Winkler } 44262382997STomas Winkler 4436009595aSTomas Winkler if (!mei_me_cl_is_active(cldev->me_cl)) { 4446009595aSTomas Winkler dev_err(&cldev->dev, "me client is not active\n"); 4456009595aSTomas Winkler ret = -ENOTTY; 4466009595aSTomas Winkler goto out; 44762382997STomas Winkler } 44862382997STomas Winkler 4496009595aSTomas Winkler ret = mei_cl_connect(cl, cldev->me_cl, NULL); 4506009595aSTomas Winkler if (ret < 0) 4516009595aSTomas Winkler dev_err(&cldev->dev, "cannot connect\n"); 4526009595aSTomas Winkler 4536009595aSTomas Winkler out: 45462382997STomas Winkler mutex_unlock(&bus->device_lock); 45562382997STomas Winkler 4566009595aSTomas Winkler return ret; 45762382997STomas Winkler } 458d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_enable); 45962382997STomas Winkler 46062382997STomas Winkler /** 461d49dc5e7STomas Winkler * mei_cldev_disable - disable me client device 46262382997STomas Winkler * disconnect form the me client 46362382997STomas Winkler * 46462382997STomas Winkler * @cldev: me client device 46562382997STomas Winkler * 46662382997STomas Winkler * Return: 0 on success and < 0 on error 46762382997STomas Winkler */ 468d49dc5e7STomas Winkler int mei_cldev_disable(struct mei_cl_device *cldev) 46962382997STomas Winkler { 47062382997STomas Winkler struct mei_device *bus; 4716009595aSTomas Winkler struct mei_cl *cl; 4726009595aSTomas Winkler int err; 47362382997STomas Winkler 4746009595aSTomas Winkler if (!cldev || !cldev->cl) 47562382997STomas Winkler return -ENODEV; 47662382997STomas Winkler 4776009595aSTomas Winkler cl = cldev->cl; 4786009595aSTomas Winkler 4796009595aSTomas Winkler bus = cldev->bus; 48062382997STomas Winkler 48162382997STomas Winkler cldev->event_cb = NULL; 48262382997STomas Winkler 48362382997STomas Winkler mutex_lock(&bus->device_lock); 48462382997STomas Winkler 48562382997STomas Winkler if (!mei_cl_is_connected(cl)) { 48662382997STomas Winkler dev_err(bus->dev, "Already disconnected"); 48762382997STomas Winkler err = 0; 48862382997STomas Winkler goto out; 48962382997STomas Winkler } 49062382997STomas Winkler 49162382997STomas Winkler err = mei_cl_disconnect(cl); 4926009595aSTomas Winkler if (err < 0) 49362382997STomas Winkler dev_err(bus->dev, "Could not disconnect from the ME client"); 49462382997STomas Winkler 49562382997STomas Winkler out: 4966009595aSTomas Winkler /* Flush queues and remove any pending read */ 4976009595aSTomas Winkler mei_cl_flush_queues(cl, NULL); 4986009595aSTomas Winkler mei_cl_unlink(cl); 4996009595aSTomas Winkler 5006009595aSTomas Winkler kfree(cl); 5016009595aSTomas Winkler cldev->cl = NULL; 5026009595aSTomas Winkler 50362382997STomas Winkler mutex_unlock(&bus->device_lock); 50462382997STomas Winkler return err; 50562382997STomas Winkler } 506d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_disable); 50762382997STomas Winkler 508688a9cceSTomas Winkler /** 509688a9cceSTomas Winkler * mei_cl_device_find - find matching entry in the driver id table 510688a9cceSTomas Winkler * 511688a9cceSTomas Winkler * @cldev: me client device 512688a9cceSTomas Winkler * @cldrv: me client driver 513688a9cceSTomas Winkler * 514688a9cceSTomas Winkler * Return: id on success; NULL if no id is matching 515688a9cceSTomas Winkler */ 516688a9cceSTomas Winkler static const 517688a9cceSTomas Winkler struct mei_cl_device_id *mei_cl_device_find(struct mei_cl_device *cldev, 518688a9cceSTomas Winkler struct mei_cl_driver *cldrv) 519e5354107SSamuel Ortiz { 520e5354107SSamuel Ortiz const struct mei_cl_device_id *id; 521c93b76b3STomas Winkler const uuid_le *uuid; 522b26864caSTomas Winkler u8 version; 523b26864caSTomas Winkler bool match; 524e5354107SSamuel Ortiz 525b37719c3STomas Winkler uuid = mei_me_cl_uuid(cldev->me_cl); 526b26864caSTomas Winkler version = mei_me_cl_ver(cldev->me_cl); 527e5354107SSamuel Ortiz 528b37719c3STomas Winkler id = cldrv->id_table; 529b144ce2dSGreg Kroah-Hartman while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) { 530b144ce2dSGreg Kroah-Hartman if (!uuid_le_cmp(*uuid, id->uuid)) { 531b26864caSTomas Winkler match = true; 532688a9cceSTomas Winkler 533b26864caSTomas Winkler if (cldev->name[0]) 534b26864caSTomas Winkler if (strncmp(cldev->name, id->name, 535b26864caSTomas Winkler sizeof(id->name))) 536b26864caSTomas Winkler match = false; 537688a9cceSTomas Winkler 538b26864caSTomas Winkler if (id->version != MEI_CL_VERSION_ANY) 539b26864caSTomas Winkler if (id->version != version) 540b26864caSTomas Winkler match = false; 541b26864caSTomas Winkler if (match) 542688a9cceSTomas Winkler return id; 543c93b76b3STomas Winkler } 544e5354107SSamuel Ortiz 545e5354107SSamuel Ortiz id++; 546e5354107SSamuel Ortiz } 547e5354107SSamuel Ortiz 548688a9cceSTomas Winkler return NULL; 549688a9cceSTomas Winkler } 550688a9cceSTomas Winkler 551688a9cceSTomas Winkler /** 552688a9cceSTomas Winkler * mei_cl_device_match - device match function 553688a9cceSTomas Winkler * 554688a9cceSTomas Winkler * @dev: device 555688a9cceSTomas Winkler * @drv: driver 556688a9cceSTomas Winkler * 557688a9cceSTomas Winkler * Return: 1 if matching device was found 0 otherwise 558688a9cceSTomas Winkler */ 559688a9cceSTomas Winkler static int mei_cl_device_match(struct device *dev, struct device_driver *drv) 560688a9cceSTomas Winkler { 561688a9cceSTomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 562688a9cceSTomas Winkler struct mei_cl_driver *cldrv = to_mei_cl_driver(drv); 563688a9cceSTomas Winkler const struct mei_cl_device_id *found_id; 564688a9cceSTomas Winkler 565688a9cceSTomas Winkler if (!cldev) 566688a9cceSTomas Winkler return 0; 567688a9cceSTomas Winkler 56871ce7891STomas Winkler if (!cldev->do_match) 56971ce7891STomas Winkler return 0; 57071ce7891STomas Winkler 571688a9cceSTomas Winkler if (!cldrv || !cldrv->id_table) 572688a9cceSTomas Winkler return 0; 573688a9cceSTomas Winkler 574688a9cceSTomas Winkler found_id = mei_cl_device_find(cldev, cldrv); 575688a9cceSTomas Winkler if (found_id) 576688a9cceSTomas Winkler return 1; 577688a9cceSTomas Winkler 578e5354107SSamuel Ortiz return 0; 579e5354107SSamuel Ortiz } 580e5354107SSamuel Ortiz 581feb8cd0fSTomas Winkler /** 582feb8cd0fSTomas Winkler * mei_cl_device_probe - bus probe function 583feb8cd0fSTomas Winkler * 584feb8cd0fSTomas Winkler * @dev: device 585feb8cd0fSTomas Winkler * 586feb8cd0fSTomas Winkler * Return: 0 on success; < 0 otherwise 587feb8cd0fSTomas Winkler */ 588e5354107SSamuel Ortiz static int mei_cl_device_probe(struct device *dev) 589e5354107SSamuel Ortiz { 590feb8cd0fSTomas Winkler struct mei_cl_device *cldev; 591b37719c3STomas Winkler struct mei_cl_driver *cldrv; 592feb8cd0fSTomas Winkler const struct mei_cl_device_id *id; 593b9c79543SAlexey Khoroshilov int ret; 594feb8cd0fSTomas Winkler 595feb8cd0fSTomas Winkler cldev = to_mei_cl_device(dev); 596feb8cd0fSTomas Winkler cldrv = to_mei_cl_driver(dev->driver); 597e5354107SSamuel Ortiz 598b37719c3STomas Winkler if (!cldev) 599e5354107SSamuel Ortiz return 0; 600e5354107SSamuel Ortiz 601b37719c3STomas Winkler if (!cldrv || !cldrv->probe) 602e5354107SSamuel Ortiz return -ENODEV; 603e5354107SSamuel Ortiz 604feb8cd0fSTomas Winkler id = mei_cl_device_find(cldev, cldrv); 605feb8cd0fSTomas Winkler if (!id) 606feb8cd0fSTomas Winkler return -ENODEV; 607e5354107SSamuel Ortiz 608b9c79543SAlexey Khoroshilov ret = cldrv->probe(cldev, id); 609b9c79543SAlexey Khoroshilov if (ret) 610b9c79543SAlexey Khoroshilov return ret; 611e5354107SSamuel Ortiz 612b9c79543SAlexey Khoroshilov __module_get(THIS_MODULE); 613b9c79543SAlexey Khoroshilov return 0; 614e5354107SSamuel Ortiz } 615e5354107SSamuel Ortiz 616feb8cd0fSTomas Winkler /** 617feb8cd0fSTomas Winkler * mei_cl_device_remove - remove device from the bus 618feb8cd0fSTomas Winkler * 619feb8cd0fSTomas Winkler * @dev: device 620feb8cd0fSTomas Winkler * 621feb8cd0fSTomas Winkler * Return: 0 on success; < 0 otherwise 622feb8cd0fSTomas Winkler */ 623e5354107SSamuel Ortiz static int mei_cl_device_remove(struct device *dev) 624e5354107SSamuel Ortiz { 625b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 626b37719c3STomas Winkler struct mei_cl_driver *cldrv; 627feb8cd0fSTomas Winkler int ret = 0; 628e5354107SSamuel Ortiz 629b37719c3STomas Winkler if (!cldev || !dev->driver) 630e5354107SSamuel Ortiz return 0; 631e5354107SSamuel Ortiz 632b37719c3STomas Winkler if (cldev->event_cb) { 633b37719c3STomas Winkler cldev->event_cb = NULL; 634b37719c3STomas Winkler cancel_work_sync(&cldev->event_work); 6353e833295SSamuel Ortiz } 6363e833295SSamuel Ortiz 637b37719c3STomas Winkler cldrv = to_mei_cl_driver(dev->driver); 638feb8cd0fSTomas Winkler if (cldrv->remove) 639feb8cd0fSTomas Winkler ret = cldrv->remove(cldev); 640feb8cd0fSTomas Winkler 641feb8cd0fSTomas Winkler module_put(THIS_MODULE); 642e5354107SSamuel Ortiz dev->driver = NULL; 643feb8cd0fSTomas Winkler return ret; 644e5354107SSamuel Ortiz 645e5354107SSamuel Ortiz } 646e5354107SSamuel Ortiz 647007d64ebSTomas Winkler static ssize_t name_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); 651007d64ebSTomas Winkler 652423de92fSRasmus Villemoes return scnprintf(buf, PAGE_SIZE, "%s", cldev->name); 653007d64ebSTomas Winkler } 654007d64ebSTomas Winkler static DEVICE_ATTR_RO(name); 655007d64ebSTomas Winkler 656007d64ebSTomas Winkler static ssize_t uuid_show(struct device *dev, struct device_attribute *a, 657007d64ebSTomas Winkler char *buf) 658007d64ebSTomas Winkler { 659b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 660b37719c3STomas Winkler const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); 661007d64ebSTomas Winkler 662423de92fSRasmus Villemoes return scnprintf(buf, PAGE_SIZE, "%pUl", uuid); 663007d64ebSTomas Winkler } 664007d64ebSTomas Winkler static DEVICE_ATTR_RO(uuid); 665007d64ebSTomas Winkler 66640b7320eSTomas Winkler static ssize_t version_show(struct device *dev, struct device_attribute *a, 66740b7320eSTomas Winkler char *buf) 66840b7320eSTomas Winkler { 66940b7320eSTomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 67040b7320eSTomas Winkler u8 version = mei_me_cl_ver(cldev->me_cl); 67140b7320eSTomas Winkler 672423de92fSRasmus Villemoes return scnprintf(buf, PAGE_SIZE, "%02X", version); 67340b7320eSTomas Winkler } 67440b7320eSTomas Winkler static DEVICE_ATTR_RO(version); 67540b7320eSTomas Winkler 676e5354107SSamuel Ortiz static ssize_t modalias_show(struct device *dev, struct device_attribute *a, 677e5354107SSamuel Ortiz char *buf) 678e5354107SSamuel Ortiz { 679b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 680b37719c3STomas Winkler const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); 681e5354107SSamuel Ortiz 682423de92fSRasmus Villemoes return scnprintf(buf, PAGE_SIZE, "mei:%s:%pUl:", cldev->name, uuid); 683e5354107SSamuel Ortiz } 68432f389ecSGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias); 685e5354107SSamuel Ortiz 686d49dc5e7STomas Winkler static struct attribute *mei_cldev_attrs[] = { 687007d64ebSTomas Winkler &dev_attr_name.attr, 688007d64ebSTomas Winkler &dev_attr_uuid.attr, 68940b7320eSTomas Winkler &dev_attr_version.attr, 69032f389ecSGreg Kroah-Hartman &dev_attr_modalias.attr, 69132f389ecSGreg Kroah-Hartman NULL, 692e5354107SSamuel Ortiz }; 693d49dc5e7STomas Winkler ATTRIBUTE_GROUPS(mei_cldev); 694e5354107SSamuel Ortiz 69538d3c00dSTomas Winkler /** 69638d3c00dSTomas Winkler * mei_cl_device_uevent - me client bus uevent handler 69738d3c00dSTomas Winkler * 69838d3c00dSTomas Winkler * @dev: device 69938d3c00dSTomas Winkler * @env: uevent kobject 70038d3c00dSTomas Winkler * 70138d3c00dSTomas Winkler * Return: 0 on success -ENOMEM on when add_uevent_var fails 70238d3c00dSTomas Winkler */ 70338d3c00dSTomas Winkler static int mei_cl_device_uevent(struct device *dev, struct kobj_uevent_env *env) 704e5354107SSamuel Ortiz { 705b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 706b37719c3STomas Winkler const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); 70740b7320eSTomas Winkler u8 version = mei_me_cl_ver(cldev->me_cl); 70840b7320eSTomas Winkler 70940b7320eSTomas Winkler if (add_uevent_var(env, "MEI_CL_VERSION=%d", version)) 71040b7320eSTomas Winkler return -ENOMEM; 711c93b76b3STomas Winkler 712007d64ebSTomas Winkler if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid)) 713007d64ebSTomas Winkler return -ENOMEM; 714007d64ebSTomas Winkler 715b37719c3STomas Winkler if (add_uevent_var(env, "MEI_CL_NAME=%s", cldev->name)) 716007d64ebSTomas Winkler return -ENOMEM; 717007d64ebSTomas Winkler 718b26864caSTomas Winkler if (add_uevent_var(env, "MODALIAS=mei:%s:%pUl:%02X:", 719b26864caSTomas Winkler cldev->name, uuid, version)) 720e5354107SSamuel Ortiz return -ENOMEM; 721e5354107SSamuel Ortiz 722e5354107SSamuel Ortiz return 0; 723e5354107SSamuel Ortiz } 724e5354107SSamuel Ortiz 725e5354107SSamuel Ortiz static struct bus_type mei_cl_bus_type = { 726e5354107SSamuel Ortiz .name = "mei", 727d49dc5e7STomas Winkler .dev_groups = mei_cldev_groups, 728e5354107SSamuel Ortiz .match = mei_cl_device_match, 729e5354107SSamuel Ortiz .probe = mei_cl_device_probe, 730e5354107SSamuel Ortiz .remove = mei_cl_device_remove, 73138d3c00dSTomas Winkler .uevent = mei_cl_device_uevent, 732e5354107SSamuel Ortiz }; 733e5354107SSamuel Ortiz 734512f64d9STomas Winkler static struct mei_device *mei_dev_bus_get(struct mei_device *bus) 735512f64d9STomas Winkler { 736512f64d9STomas Winkler if (bus) 737512f64d9STomas Winkler get_device(bus->dev); 738512f64d9STomas Winkler 739512f64d9STomas Winkler return bus; 740512f64d9STomas Winkler } 741512f64d9STomas Winkler 742512f64d9STomas Winkler static void mei_dev_bus_put(struct mei_device *bus) 743512f64d9STomas Winkler { 744512f64d9STomas Winkler if (bus) 745512f64d9STomas Winkler put_device(bus->dev); 746512f64d9STomas Winkler } 747512f64d9STomas Winkler 748ae48d74dSTomas Winkler static void mei_cl_bus_dev_release(struct device *dev) 749e5354107SSamuel Ortiz { 750b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 751d49ed64aSAlexander Usyskin 752b37719c3STomas Winkler if (!cldev) 753d49ed64aSAlexander Usyskin return; 754d49ed64aSAlexander Usyskin 755b37719c3STomas Winkler mei_me_cl_put(cldev->me_cl); 756512f64d9STomas Winkler mei_dev_bus_put(cldev->bus); 757b37719c3STomas Winkler kfree(cldev); 758e5354107SSamuel Ortiz } 759e5354107SSamuel Ortiz 760e5354107SSamuel Ortiz static struct device_type mei_cl_device_type = { 761ae48d74dSTomas Winkler .release = mei_cl_bus_dev_release, 762e5354107SSamuel Ortiz }; 763e5354107SSamuel Ortiz 76471ce7891STomas Winkler /** 765213dd193STomas Winkler * mei_cl_bus_set_name - set device name for me client device 766213dd193STomas Winkler * 767213dd193STomas Winkler * @cldev: me client device 768213dd193STomas Winkler */ 769213dd193STomas Winkler static inline void mei_cl_bus_set_name(struct mei_cl_device *cldev) 770213dd193STomas Winkler { 771213dd193STomas Winkler dev_set_name(&cldev->dev, "mei:%s:%pUl:%02X", 772213dd193STomas Winkler cldev->name, 773213dd193STomas Winkler mei_me_cl_uuid(cldev->me_cl), 774213dd193STomas Winkler mei_me_cl_ver(cldev->me_cl)); 775213dd193STomas Winkler } 776213dd193STomas Winkler 777213dd193STomas Winkler /** 778ae48d74dSTomas Winkler * mei_cl_bus_dev_alloc - initialize and allocate mei client device 77971ce7891STomas Winkler * 78071ce7891STomas Winkler * @bus: mei device 78171ce7891STomas Winkler * @me_cl: me client 78271ce7891STomas Winkler * 78371ce7891STomas Winkler * Return: allocated device structur or NULL on allocation failure 78471ce7891STomas Winkler */ 785ae48d74dSTomas Winkler static struct mei_cl_device *mei_cl_bus_dev_alloc(struct mei_device *bus, 78671ce7891STomas Winkler struct mei_me_client *me_cl) 78771ce7891STomas Winkler { 78871ce7891STomas Winkler struct mei_cl_device *cldev; 78971ce7891STomas Winkler 79071ce7891STomas Winkler cldev = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); 79171ce7891STomas Winkler if (!cldev) 79271ce7891STomas Winkler return NULL; 79371ce7891STomas Winkler 79471ce7891STomas Winkler device_initialize(&cldev->dev); 79571ce7891STomas Winkler cldev->dev.parent = bus->dev; 79671ce7891STomas Winkler cldev->dev.bus = &mei_cl_bus_type; 79771ce7891STomas Winkler cldev->dev.type = &mei_cl_device_type; 79871ce7891STomas Winkler cldev->bus = mei_dev_bus_get(bus); 79971ce7891STomas Winkler cldev->me_cl = mei_me_cl_get(me_cl); 800213dd193STomas Winkler mei_cl_bus_set_name(cldev); 80171ce7891STomas Winkler cldev->is_added = 0; 80271ce7891STomas Winkler INIT_LIST_HEAD(&cldev->bus_list); 80371ce7891STomas Winkler 80471ce7891STomas Winkler return cldev; 80571ce7891STomas Winkler } 80671ce7891STomas Winkler 80771ce7891STomas Winkler /** 80871ce7891STomas Winkler * mei_cl_dev_setup - setup me client device 80971ce7891STomas Winkler * run fix up routines and set the device name 81071ce7891STomas Winkler * 81171ce7891STomas Winkler * @bus: mei device 81271ce7891STomas Winkler * @cldev: me client device 81371ce7891STomas Winkler * 81471ce7891STomas Winkler * Return: true if the device is eligible for enumeration 81571ce7891STomas Winkler */ 816ae48d74dSTomas Winkler static bool mei_cl_bus_dev_setup(struct mei_device *bus, 81771ce7891STomas Winkler struct mei_cl_device *cldev) 81871ce7891STomas Winkler { 81971ce7891STomas Winkler cldev->do_match = 1; 820ae48d74dSTomas Winkler mei_cl_bus_dev_fixup(cldev); 82171ce7891STomas Winkler 822213dd193STomas Winkler /* the device name can change during fix up */ 82371ce7891STomas Winkler if (cldev->do_match) 824213dd193STomas Winkler mei_cl_bus_set_name(cldev); 82571ce7891STomas Winkler 82671ce7891STomas Winkler return cldev->do_match == 1; 82771ce7891STomas Winkler } 82871ce7891STomas Winkler 82971ce7891STomas Winkler /** 83071ce7891STomas Winkler * mei_cl_bus_dev_add - add me client devices 83171ce7891STomas Winkler * 83271ce7891STomas Winkler * @cldev: me client device 83371ce7891STomas Winkler * 83471ce7891STomas Winkler * Return: 0 on success; < 0 on failre 83571ce7891STomas Winkler */ 83671ce7891STomas Winkler static int mei_cl_bus_dev_add(struct mei_cl_device *cldev) 83771ce7891STomas Winkler { 83871ce7891STomas Winkler int ret; 83971ce7891STomas Winkler 840b26864caSTomas Winkler dev_dbg(cldev->bus->dev, "adding %pUL:%02X\n", 841b26864caSTomas Winkler mei_me_cl_uuid(cldev->me_cl), 842b26864caSTomas Winkler mei_me_cl_ver(cldev->me_cl)); 84371ce7891STomas Winkler ret = device_add(&cldev->dev); 84471ce7891STomas Winkler if (!ret) 84571ce7891STomas Winkler cldev->is_added = 1; 84671ce7891STomas Winkler 84771ce7891STomas Winkler return ret; 84871ce7891STomas Winkler } 84971ce7891STomas Winkler 8506009595aSTomas Winkler /** 8516009595aSTomas Winkler * mei_cl_bus_dev_stop - stop the driver 8526009595aSTomas Winkler * 8536009595aSTomas Winkler * @cldev: me client device 8546009595aSTomas Winkler */ 8556009595aSTomas Winkler static void mei_cl_bus_dev_stop(struct mei_cl_device *cldev) 8566009595aSTomas Winkler { 8576009595aSTomas Winkler if (cldev->is_added) 8586009595aSTomas Winkler device_release_driver(&cldev->dev); 8596009595aSTomas Winkler } 8606009595aSTomas Winkler 8616009595aSTomas Winkler /** 8626009595aSTomas Winkler * mei_cl_bus_dev_destroy - destroy me client devices object 8636009595aSTomas Winkler * 8646009595aSTomas Winkler * @cldev: me client device 8652da55cfdSTomas Winkler * 8662da55cfdSTomas Winkler * Locking: called under "dev->cl_bus_lock" lock 8676009595aSTomas Winkler */ 8686009595aSTomas Winkler static void mei_cl_bus_dev_destroy(struct mei_cl_device *cldev) 8696009595aSTomas Winkler { 8702da55cfdSTomas Winkler 8712da55cfdSTomas Winkler WARN_ON(!mutex_is_locked(&cldev->bus->cl_bus_lock)); 8722da55cfdSTomas Winkler 8736009595aSTomas Winkler if (!cldev->is_added) 8746009595aSTomas Winkler return; 8756009595aSTomas Winkler 8766009595aSTomas Winkler device_del(&cldev->dev); 8776009595aSTomas Winkler 8786009595aSTomas Winkler list_del_init(&cldev->bus_list); 8796009595aSTomas Winkler 8806009595aSTomas Winkler cldev->is_added = 0; 8816009595aSTomas Winkler put_device(&cldev->dev); 8826009595aSTomas Winkler } 8836009595aSTomas Winkler 8846009595aSTomas Winkler /** 8856009595aSTomas Winkler * mei_cl_bus_remove_device - remove a devices form the bus 8866009595aSTomas Winkler * 8876009595aSTomas Winkler * @cldev: me client device 8886009595aSTomas Winkler */ 8896009595aSTomas Winkler static void mei_cl_bus_remove_device(struct mei_cl_device *cldev) 8906009595aSTomas Winkler { 8916009595aSTomas Winkler mei_cl_bus_dev_stop(cldev); 8926009595aSTomas Winkler mei_cl_bus_dev_destroy(cldev); 8936009595aSTomas Winkler } 8946009595aSTomas Winkler 8956009595aSTomas Winkler /** 8966009595aSTomas Winkler * mei_cl_bus_remove_devices - remove all devices form the bus 8976009595aSTomas Winkler * 8986009595aSTomas Winkler * @bus: mei device 8996009595aSTomas Winkler */ 9006009595aSTomas Winkler void mei_cl_bus_remove_devices(struct mei_device *bus) 9016009595aSTomas Winkler { 9026009595aSTomas Winkler struct mei_cl_device *cldev, *next; 9036009595aSTomas Winkler 9042da55cfdSTomas Winkler mutex_lock(&bus->cl_bus_lock); 9056009595aSTomas Winkler list_for_each_entry_safe(cldev, next, &bus->device_list, bus_list) 9066009595aSTomas Winkler mei_cl_bus_remove_device(cldev); 9072da55cfdSTomas Winkler mutex_unlock(&bus->cl_bus_lock); 9086009595aSTomas Winkler } 9096009595aSTomas Winkler 9106009595aSTomas Winkler 9116009595aSTomas Winkler /** 912ae48d74dSTomas Winkler * mei_cl_bus_dev_init - allocate and initializes an mei client devices 9136009595aSTomas Winkler * based on me client 9146009595aSTomas Winkler * 9156009595aSTomas Winkler * @bus: mei device 9166009595aSTomas Winkler * @me_cl: me client 9172da55cfdSTomas Winkler * 9182da55cfdSTomas Winkler * Locking: called under "dev->cl_bus_lock" lock 9196009595aSTomas Winkler */ 920ae48d74dSTomas Winkler static void mei_cl_bus_dev_init(struct mei_device *bus, 921ae48d74dSTomas Winkler struct mei_me_client *me_cl) 922e5354107SSamuel Ortiz { 923b37719c3STomas Winkler struct mei_cl_device *cldev; 9246009595aSTomas Winkler 9252da55cfdSTomas Winkler WARN_ON(!mutex_is_locked(&bus->cl_bus_lock)); 9262da55cfdSTomas Winkler 9276009595aSTomas Winkler dev_dbg(bus->dev, "initializing %pUl", mei_me_cl_uuid(me_cl)); 9286009595aSTomas Winkler 9296009595aSTomas Winkler if (me_cl->bus_added) 9306009595aSTomas Winkler return; 931e5354107SSamuel Ortiz 932ae48d74dSTomas Winkler cldev = mei_cl_bus_dev_alloc(bus, me_cl); 933b37719c3STomas Winkler if (!cldev) 9346009595aSTomas Winkler return; 935e5354107SSamuel Ortiz 9366009595aSTomas Winkler me_cl->bus_added = true; 9376009595aSTomas Winkler list_add_tail(&cldev->bus_list, &bus->device_list); 938c93b76b3STomas Winkler 939a7b71bc0SSamuel Ortiz } 940a7b71bc0SSamuel Ortiz 9416009595aSTomas Winkler /** 9426009595aSTomas Winkler * mei_cl_bus_rescan - scan me clients list and add create 9436009595aSTomas Winkler * devices for eligible clients 9446009595aSTomas Winkler * 9456009595aSTomas Winkler * @bus: mei device 9466009595aSTomas Winkler */ 9476009595aSTomas Winkler void mei_cl_bus_rescan(struct mei_device *bus) 948e5354107SSamuel Ortiz { 9496009595aSTomas Winkler struct mei_cl_device *cldev, *n; 9506009595aSTomas Winkler struct mei_me_client *me_cl; 9516009595aSTomas Winkler 9522da55cfdSTomas Winkler mutex_lock(&bus->cl_bus_lock); 9532da55cfdSTomas Winkler 9546009595aSTomas Winkler down_read(&bus->me_clients_rwsem); 9556009595aSTomas Winkler list_for_each_entry(me_cl, &bus->me_clients, list) 956ae48d74dSTomas Winkler mei_cl_bus_dev_init(bus, me_cl); 9576009595aSTomas Winkler up_read(&bus->me_clients_rwsem); 9586009595aSTomas Winkler 9596009595aSTomas Winkler list_for_each_entry_safe(cldev, n, &bus->device_list, bus_list) { 9606009595aSTomas Winkler 9616009595aSTomas Winkler if (!mei_me_cl_is_active(cldev->me_cl)) { 9626009595aSTomas Winkler mei_cl_bus_remove_device(cldev); 9636009595aSTomas Winkler continue; 964e5354107SSamuel Ortiz } 9656009595aSTomas Winkler 9666009595aSTomas Winkler if (cldev->is_added) 9676009595aSTomas Winkler continue; 9686009595aSTomas Winkler 969ae48d74dSTomas Winkler if (mei_cl_bus_dev_setup(bus, cldev)) 9706009595aSTomas Winkler mei_cl_bus_dev_add(cldev); 9716009595aSTomas Winkler else { 9726009595aSTomas Winkler list_del_init(&cldev->bus_list); 9736009595aSTomas Winkler put_device(&cldev->dev); 9746009595aSTomas Winkler } 9756009595aSTomas Winkler } 9766009595aSTomas Winkler mutex_unlock(&bus->cl_bus_lock); 9776009595aSTomas Winkler 9786009595aSTomas Winkler dev_dbg(bus->dev, "rescan end"); 9796009595aSTomas Winkler } 980333e4ee0SSamuel Ortiz 981a816a00eSAlexander Usyskin void mei_cl_bus_rescan_work(struct work_struct *work) 982a816a00eSAlexander Usyskin { 983a816a00eSAlexander Usyskin struct mei_device *bus = 984a816a00eSAlexander Usyskin container_of(work, struct mei_device, bus_rescan_work); 985025fb792SAlexander Usyskin struct mei_me_client *me_cl; 986025fb792SAlexander Usyskin 987025fb792SAlexander Usyskin mutex_lock(&bus->device_lock); 988025fb792SAlexander Usyskin me_cl = mei_me_cl_by_uuid(bus, &mei_amthif_guid); 989025fb792SAlexander Usyskin if (me_cl) 990025fb792SAlexander Usyskin mei_amthif_host_init(bus, me_cl); 991025fb792SAlexander Usyskin mei_me_cl_put(me_cl); 992025fb792SAlexander Usyskin mutex_unlock(&bus->device_lock); 993a816a00eSAlexander Usyskin 994a816a00eSAlexander Usyskin mei_cl_bus_rescan(bus); 995a816a00eSAlexander Usyskin } 996a816a00eSAlexander Usyskin 997d49dc5e7STomas Winkler int __mei_cldev_driver_register(struct mei_cl_driver *cldrv, 998d49dc5e7STomas Winkler struct module *owner) 999333e4ee0SSamuel Ortiz { 1000333e4ee0SSamuel Ortiz int err; 1001333e4ee0SSamuel Ortiz 1002b37719c3STomas Winkler cldrv->driver.name = cldrv->name; 1003b37719c3STomas Winkler cldrv->driver.owner = owner; 1004b37719c3STomas Winkler cldrv->driver.bus = &mei_cl_bus_type; 1005333e4ee0SSamuel Ortiz 1006b37719c3STomas Winkler err = driver_register(&cldrv->driver); 1007333e4ee0SSamuel Ortiz if (err) 1008333e4ee0SSamuel Ortiz return err; 1009333e4ee0SSamuel Ortiz 1010b37719c3STomas Winkler pr_debug("mei: driver [%s] registered\n", cldrv->driver.name); 1011333e4ee0SSamuel Ortiz 1012333e4ee0SSamuel Ortiz return 0; 1013333e4ee0SSamuel Ortiz } 1014d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(__mei_cldev_driver_register); 1015333e4ee0SSamuel Ortiz 1016d49dc5e7STomas Winkler void mei_cldev_driver_unregister(struct mei_cl_driver *cldrv) 1017333e4ee0SSamuel Ortiz { 1018b37719c3STomas Winkler driver_unregister(&cldrv->driver); 1019333e4ee0SSamuel Ortiz 1020b37719c3STomas Winkler pr_debug("mei: driver [%s] unregistered\n", cldrv->driver.name); 1021333e4ee0SSamuel Ortiz } 1022d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_driver_unregister); 10233e833295SSamuel Ortiz 10246009595aSTomas Winkler 1025cf3baefbSSamuel Ortiz int __init mei_cl_bus_init(void) 1026cf3baefbSSamuel Ortiz { 1027cf3baefbSSamuel Ortiz return bus_register(&mei_cl_bus_type); 1028cf3baefbSSamuel Ortiz } 1029cf3baefbSSamuel Ortiz 1030cf3baefbSSamuel Ortiz void __exit mei_cl_bus_exit(void) 1031cf3baefbSSamuel Ortiz { 1032cf3baefbSSamuel Ortiz bus_unregister(&mei_cl_bus_type); 1033cf3baefbSSamuel Ortiz } 1034