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> 19*174cd4b1SIngo Molnar #include <linux/sched/signal.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 39e0cb6b2fSAlexander Usyskin * @mode: sending mode 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, 44e0cb6b2fSAlexander Usyskin unsigned int mode) 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 83e0cb6b2fSAlexander Usyskin cb->internal = !!(mode & MEI_CL_IO_TX_INTERNAL); 84e0cb6b2fSAlexander Usyskin cb->blocking = !!(mode & MEI_CL_IO_TX_BLOCKING); 8562382997STomas Winkler memcpy(cb->buf.data, buf, length); 8662382997STomas Winkler 87e0cb6b2fSAlexander Usyskin rets = mei_cl_write(cl, cb); 8862382997STomas Winkler 8962382997STomas Winkler out: 9062382997STomas Winkler mutex_unlock(&bus->device_lock); 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 101076802d0SAlexander Usyskin * @mode: io mode 10262382997STomas Winkler * 10362382997STomas Winkler * Return: read size in bytes of < 0 on error 10462382997STomas Winkler */ 105076802d0SAlexander Usyskin ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length, 106076802d0SAlexander Usyskin unsigned int mode) 10762382997STomas Winkler { 10862382997STomas Winkler struct mei_device *bus; 10962382997STomas Winkler struct mei_cl_cb *cb; 11062382997STomas Winkler size_t r_length; 11162382997STomas Winkler ssize_t rets; 112076802d0SAlexander Usyskin bool nonblock = !!(mode & MEI_CL_IO_RX_NONBLOCK); 11362382997STomas Winkler 11462382997STomas Winkler if (WARN_ON(!cl || !cl->dev)) 11562382997STomas Winkler return -ENODEV; 11662382997STomas Winkler 11762382997STomas Winkler bus = cl->dev; 11862382997STomas Winkler 11962382997STomas Winkler mutex_lock(&bus->device_lock); 12015c13dfcSAlexander Usyskin if (bus->dev_state != MEI_DEV_ENABLED) { 12115c13dfcSAlexander Usyskin rets = -ENODEV; 12215c13dfcSAlexander Usyskin goto out; 12315c13dfcSAlexander Usyskin } 12462382997STomas Winkler 12562382997STomas Winkler cb = mei_cl_read_cb(cl, NULL); 12662382997STomas Winkler if (cb) 12762382997STomas Winkler goto copy; 12862382997STomas Winkler 12962382997STomas Winkler rets = mei_cl_read_start(cl, length, NULL); 13062382997STomas Winkler if (rets && rets != -EBUSY) 13162382997STomas Winkler goto out; 13262382997STomas Winkler 133076802d0SAlexander Usyskin if (nonblock) { 134076802d0SAlexander Usyskin rets = -EAGAIN; 135076802d0SAlexander Usyskin goto out; 136076802d0SAlexander Usyskin } 137076802d0SAlexander Usyskin 13862382997STomas Winkler /* wait on event only if there is no other waiter */ 1391eb5bd4dSAlexander Usyskin /* synchronized under device mutex */ 1401eb5bd4dSAlexander Usyskin if (!waitqueue_active(&cl->rx_wait)) { 14162382997STomas Winkler 14262382997STomas Winkler mutex_unlock(&bus->device_lock); 14362382997STomas Winkler 14462382997STomas Winkler if (wait_event_interruptible(cl->rx_wait, 14562382997STomas Winkler (!list_empty(&cl->rd_completed)) || 14662382997STomas Winkler (!mei_cl_is_connected(cl)))) { 14762382997STomas Winkler 14862382997STomas Winkler if (signal_pending(current)) 14962382997STomas Winkler return -EINTR; 15062382997STomas Winkler return -ERESTARTSYS; 15162382997STomas Winkler } 15262382997STomas Winkler 15362382997STomas Winkler mutex_lock(&bus->device_lock); 15462382997STomas Winkler 15562382997STomas Winkler if (!mei_cl_is_connected(cl)) { 1562d4d5481STomas Winkler rets = -ENODEV; 15762382997STomas Winkler goto out; 15862382997STomas Winkler } 15962382997STomas Winkler } 16062382997STomas Winkler 16162382997STomas Winkler cb = mei_cl_read_cb(cl, NULL); 16262382997STomas Winkler if (!cb) { 16362382997STomas Winkler rets = 0; 16462382997STomas Winkler goto out; 16562382997STomas Winkler } 16662382997STomas Winkler 16762382997STomas Winkler copy: 16862382997STomas Winkler if (cb->status) { 16962382997STomas Winkler rets = cb->status; 17062382997STomas Winkler goto free; 17162382997STomas Winkler } 17262382997STomas Winkler 17362382997STomas Winkler r_length = min_t(size_t, length, cb->buf_idx); 17462382997STomas Winkler memcpy(buf, cb->buf.data, r_length); 17562382997STomas Winkler rets = r_length; 17662382997STomas Winkler 17762382997STomas Winkler free: 17862382997STomas Winkler mei_io_cb_free(cb); 17962382997STomas Winkler out: 18062382997STomas Winkler mutex_unlock(&bus->device_lock); 18162382997STomas Winkler 18262382997STomas Winkler return rets; 18362382997STomas Winkler } 18462382997STomas Winkler 18562382997STomas Winkler /** 186d49dc5e7STomas Winkler * mei_cldev_send - me device send (write) 18762382997STomas Winkler * 18862382997STomas Winkler * @cldev: me client device 18962382997STomas Winkler * @buf: buffer to send 19062382997STomas Winkler * @length: buffer length 19162382997STomas Winkler * 19262382997STomas Winkler * Return: written size in bytes or < 0 on error 19362382997STomas Winkler */ 194d49dc5e7STomas Winkler ssize_t mei_cldev_send(struct mei_cl_device *cldev, u8 *buf, size_t length) 19562382997STomas Winkler { 19662382997STomas Winkler struct mei_cl *cl = cldev->cl; 19762382997STomas Winkler 198e0cb6b2fSAlexander Usyskin return __mei_cl_send(cl, buf, length, MEI_CL_IO_TX_BLOCKING); 19962382997STomas Winkler } 200d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_send); 20162382997STomas Winkler 20262382997STomas Winkler /** 203076802d0SAlexander Usyskin * mei_cldev_recv_nonblock - non block client receive (read) 204076802d0SAlexander Usyskin * 205076802d0SAlexander Usyskin * @cldev: me client device 206076802d0SAlexander Usyskin * @buf: buffer to receive 207076802d0SAlexander Usyskin * @length: buffer length 208076802d0SAlexander Usyskin * 209076802d0SAlexander Usyskin * Return: read size in bytes of < 0 on error 210076802d0SAlexander Usyskin * -EAGAIN if function will block. 211076802d0SAlexander Usyskin */ 212076802d0SAlexander Usyskin ssize_t mei_cldev_recv_nonblock(struct mei_cl_device *cldev, u8 *buf, 213076802d0SAlexander Usyskin size_t length) 214076802d0SAlexander Usyskin { 215076802d0SAlexander Usyskin struct mei_cl *cl = cldev->cl; 216076802d0SAlexander Usyskin 217076802d0SAlexander Usyskin return __mei_cl_recv(cl, buf, length, MEI_CL_IO_RX_NONBLOCK); 218076802d0SAlexander Usyskin } 219076802d0SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_recv_nonblock); 220076802d0SAlexander Usyskin 221076802d0SAlexander Usyskin /** 222d49dc5e7STomas Winkler * mei_cldev_recv - client receive (read) 22362382997STomas Winkler * 22462382997STomas Winkler * @cldev: me client device 225df7f5447STomas Winkler * @buf: buffer to receive 22662382997STomas Winkler * @length: buffer length 22762382997STomas Winkler * 22862382997STomas Winkler * Return: read size in bytes of < 0 on error 22962382997STomas Winkler */ 230d49dc5e7STomas Winkler ssize_t mei_cldev_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) 23162382997STomas Winkler { 23262382997STomas Winkler struct mei_cl *cl = cldev->cl; 23362382997STomas Winkler 234076802d0SAlexander Usyskin return __mei_cl_recv(cl, buf, length, 0); 23562382997STomas Winkler } 236d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_recv); 23762382997STomas Winkler 23862382997STomas Winkler /** 2397c7a6077SAlexander Usyskin * mei_cl_bus_rx_work - dispatch rx event for a bus device 24062382997STomas Winkler * 24162382997STomas Winkler * @work: work 24262382997STomas Winkler */ 2437c7a6077SAlexander Usyskin static void mei_cl_bus_rx_work(struct work_struct *work) 24462382997STomas Winkler { 24562382997STomas Winkler struct mei_cl_device *cldev; 246bc46b45aSAlexander Usyskin struct mei_device *bus; 24762382997STomas Winkler 2487c7a6077SAlexander Usyskin cldev = container_of(work, struct mei_cl_device, rx_work); 24962382997STomas Winkler 250bc46b45aSAlexander Usyskin bus = cldev->bus; 251bc46b45aSAlexander Usyskin 2527c7a6077SAlexander Usyskin if (cldev->rx_cb) 2537c7a6077SAlexander Usyskin cldev->rx_cb(cldev); 25462382997STomas Winkler 255bc46b45aSAlexander Usyskin mutex_lock(&bus->device_lock); 2563030dc05STomas Winkler mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL); 257bc46b45aSAlexander Usyskin mutex_unlock(&bus->device_lock); 258bc46b45aSAlexander Usyskin } 2597c7a6077SAlexander Usyskin 2607c7a6077SAlexander Usyskin /** 2617c7a6077SAlexander Usyskin * mei_cl_bus_notif_work - dispatch FW notif event for a bus device 2627c7a6077SAlexander Usyskin * 2637c7a6077SAlexander Usyskin * @work: work 2647c7a6077SAlexander Usyskin */ 2657c7a6077SAlexander Usyskin static void mei_cl_bus_notif_work(struct work_struct *work) 2667c7a6077SAlexander Usyskin { 2677c7a6077SAlexander Usyskin struct mei_cl_device *cldev; 2687c7a6077SAlexander Usyskin 2697c7a6077SAlexander Usyskin cldev = container_of(work, struct mei_cl_device, notif_work); 2707c7a6077SAlexander Usyskin 2717c7a6077SAlexander Usyskin if (cldev->notif_cb) 2727c7a6077SAlexander Usyskin cldev->notif_cb(cldev); 27362382997STomas Winkler } 27462382997STomas Winkler 27562382997STomas Winkler /** 276bb2ef9c3SAlexander Usyskin * mei_cl_bus_notify_event - schedule notify cb on bus client 277bb2ef9c3SAlexander Usyskin * 278bb2ef9c3SAlexander Usyskin * @cl: host client 279850f8940STomas Winkler * 280850f8940STomas Winkler * Return: true if event was scheduled 281850f8940STomas Winkler * false if the client is not waiting for event 282bb2ef9c3SAlexander Usyskin */ 283850f8940STomas Winkler bool mei_cl_bus_notify_event(struct mei_cl *cl) 284bb2ef9c3SAlexander Usyskin { 285bb2ef9c3SAlexander Usyskin struct mei_cl_device *cldev = cl->cldev; 286bb2ef9c3SAlexander Usyskin 2877c7a6077SAlexander Usyskin if (!cldev || !cldev->notif_cb) 288850f8940STomas Winkler return false; 289bb2ef9c3SAlexander Usyskin 290bb2ef9c3SAlexander Usyskin if (!cl->notify_ev) 291850f8940STomas Winkler return false; 292bb2ef9c3SAlexander Usyskin 2937c7a6077SAlexander Usyskin schedule_work(&cldev->notif_work); 294bb2ef9c3SAlexander Usyskin 295bb2ef9c3SAlexander Usyskin cl->notify_ev = false; 296850f8940STomas Winkler 297850f8940STomas Winkler return true; 298bb2ef9c3SAlexander Usyskin } 299bb2ef9c3SAlexander Usyskin 300bb2ef9c3SAlexander Usyskin /** 301a1f9ae2bSTomas Winkler * mei_cl_bus_rx_event - schedule rx event 30262382997STomas Winkler * 30362382997STomas Winkler * @cl: host client 304a1f9ae2bSTomas Winkler * 305a1f9ae2bSTomas Winkler * Return: true if event was scheduled 306a1f9ae2bSTomas Winkler * false if the client is not waiting for event 30762382997STomas Winkler */ 308a1f9ae2bSTomas Winkler bool mei_cl_bus_rx_event(struct mei_cl *cl) 30962382997STomas Winkler { 31062382997STomas Winkler struct mei_cl_device *cldev = cl->cldev; 31162382997STomas Winkler 3127c7a6077SAlexander Usyskin if (!cldev || !cldev->rx_cb) 313a1f9ae2bSTomas Winkler return false; 31462382997STomas Winkler 3157c7a6077SAlexander Usyskin schedule_work(&cldev->rx_work); 316a1f9ae2bSTomas Winkler 317a1f9ae2bSTomas Winkler return true; 31862382997STomas Winkler } 31962382997STomas Winkler 32062382997STomas Winkler /** 3217c7a6077SAlexander Usyskin * mei_cldev_register_rx_cb - register Rx event callback 32262382997STomas Winkler * 32362382997STomas Winkler * @cldev: me client devices 3247c7a6077SAlexander Usyskin * @rx_cb: callback function 32562382997STomas Winkler * 32662382997STomas Winkler * Return: 0 on success 32762382997STomas Winkler * -EALREADY if an callback is already registered 32862382997STomas Winkler * <0 on other errors 32962382997STomas Winkler */ 3307c7a6077SAlexander Usyskin int mei_cldev_register_rx_cb(struct mei_cl_device *cldev, mei_cldev_cb_t rx_cb) 33162382997STomas Winkler { 332bc46b45aSAlexander Usyskin struct mei_device *bus = cldev->bus; 33348168f45STomas Winkler int ret; 33448168f45STomas Winkler 3357c7a6077SAlexander Usyskin if (!rx_cb) 3367c7a6077SAlexander Usyskin return -EINVAL; 3377c7a6077SAlexander Usyskin if (cldev->rx_cb) 33862382997STomas Winkler return -EALREADY; 33962382997STomas Winkler 3407c7a6077SAlexander Usyskin cldev->rx_cb = rx_cb; 3417c7a6077SAlexander Usyskin INIT_WORK(&cldev->rx_work, mei_cl_bus_rx_work); 34262382997STomas Winkler 343bc46b45aSAlexander Usyskin mutex_lock(&bus->device_lock); 3443030dc05STomas Winkler ret = mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL); 345bc46b45aSAlexander Usyskin mutex_unlock(&bus->device_lock); 34648168f45STomas Winkler if (ret && ret != -EBUSY) 34748168f45STomas Winkler return ret; 34862382997STomas Winkler 34962382997STomas Winkler return 0; 35062382997STomas Winkler } 3517c7a6077SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_register_rx_cb); 3527c7a6077SAlexander Usyskin 3537c7a6077SAlexander Usyskin /** 3547c7a6077SAlexander Usyskin * mei_cldev_register_notif_cb - register FW notification event callback 3557c7a6077SAlexander Usyskin * 3567c7a6077SAlexander Usyskin * @cldev: me client devices 3577c7a6077SAlexander Usyskin * @notif_cb: callback function 3587c7a6077SAlexander Usyskin * 3597c7a6077SAlexander Usyskin * Return: 0 on success 3607c7a6077SAlexander Usyskin * -EALREADY if an callback is already registered 3617c7a6077SAlexander Usyskin * <0 on other errors 3627c7a6077SAlexander Usyskin */ 3637c7a6077SAlexander Usyskin int mei_cldev_register_notif_cb(struct mei_cl_device *cldev, 3647c7a6077SAlexander Usyskin mei_cldev_cb_t notif_cb) 3657c7a6077SAlexander Usyskin { 3667c7a6077SAlexander Usyskin struct mei_device *bus = cldev->bus; 3677c7a6077SAlexander Usyskin int ret; 3687c7a6077SAlexander Usyskin 3697c7a6077SAlexander Usyskin if (!notif_cb) 3707c7a6077SAlexander Usyskin return -EINVAL; 3717c7a6077SAlexander Usyskin 3727c7a6077SAlexander Usyskin if (cldev->notif_cb) 3737c7a6077SAlexander Usyskin return -EALREADY; 3747c7a6077SAlexander Usyskin 3757c7a6077SAlexander Usyskin cldev->notif_cb = notif_cb; 3767c7a6077SAlexander Usyskin INIT_WORK(&cldev->notif_work, mei_cl_bus_notif_work); 3777c7a6077SAlexander Usyskin 3787c7a6077SAlexander Usyskin mutex_lock(&bus->device_lock); 3797c7a6077SAlexander Usyskin ret = mei_cl_notify_request(cldev->cl, NULL, 1); 3807c7a6077SAlexander Usyskin mutex_unlock(&bus->device_lock); 3817c7a6077SAlexander Usyskin if (ret) 3827c7a6077SAlexander Usyskin return ret; 3837c7a6077SAlexander Usyskin 3847c7a6077SAlexander Usyskin return 0; 3857c7a6077SAlexander Usyskin } 3867c7a6077SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_register_notif_cb); 38762382997STomas Winkler 38862382997STomas Winkler /** 389d49dc5e7STomas Winkler * mei_cldev_get_drvdata - driver data getter 39062382997STomas Winkler * 39162382997STomas Winkler * @cldev: mei client device 39262382997STomas Winkler * 39362382997STomas Winkler * Return: driver private data 39462382997STomas Winkler */ 395d49dc5e7STomas Winkler void *mei_cldev_get_drvdata(const struct mei_cl_device *cldev) 39662382997STomas Winkler { 39762382997STomas Winkler return dev_get_drvdata(&cldev->dev); 39862382997STomas Winkler } 399d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_get_drvdata); 40062382997STomas Winkler 40162382997STomas Winkler /** 402d49dc5e7STomas Winkler * mei_cldev_set_drvdata - driver data setter 40362382997STomas Winkler * 40462382997STomas Winkler * @cldev: mei client device 40562382997STomas Winkler * @data: data to store 40662382997STomas Winkler */ 407d49dc5e7STomas Winkler void mei_cldev_set_drvdata(struct mei_cl_device *cldev, void *data) 40862382997STomas Winkler { 40962382997STomas Winkler dev_set_drvdata(&cldev->dev, data); 41062382997STomas Winkler } 411d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_set_drvdata); 41262382997STomas Winkler 41362382997STomas Winkler /** 414baeacd03STomas Winkler * mei_cldev_uuid - return uuid of the underlying me client 415baeacd03STomas Winkler * 416baeacd03STomas Winkler * @cldev: mei client device 417baeacd03STomas Winkler * 418baeacd03STomas Winkler * Return: me client uuid 419baeacd03STomas Winkler */ 420baeacd03STomas Winkler const uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev) 421baeacd03STomas Winkler { 422baeacd03STomas Winkler return mei_me_cl_uuid(cldev->me_cl); 423baeacd03STomas Winkler } 424baeacd03STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_uuid); 425baeacd03STomas Winkler 426baeacd03STomas Winkler /** 427baeacd03STomas Winkler * mei_cldev_ver - return protocol version of the underlying me client 428baeacd03STomas Winkler * 429baeacd03STomas Winkler * @cldev: mei client device 430baeacd03STomas Winkler * 431baeacd03STomas Winkler * Return: me client protocol version 432baeacd03STomas Winkler */ 433baeacd03STomas Winkler u8 mei_cldev_ver(const struct mei_cl_device *cldev) 434baeacd03STomas Winkler { 435baeacd03STomas Winkler return mei_me_cl_ver(cldev->me_cl); 436baeacd03STomas Winkler } 437baeacd03STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_ver); 438baeacd03STomas Winkler 439baeacd03STomas Winkler /** 44001a14edeSTomas Winkler * mei_cldev_enabled - check whether the device is enabled 44101a14edeSTomas Winkler * 44201a14edeSTomas Winkler * @cldev: mei client device 44301a14edeSTomas Winkler * 44401a14edeSTomas Winkler * Return: true if me client is initialized and connected 44501a14edeSTomas Winkler */ 44601a14edeSTomas Winkler bool mei_cldev_enabled(struct mei_cl_device *cldev) 44701a14edeSTomas Winkler { 448c110cdb1SAlexander Usyskin return mei_cl_is_connected(cldev->cl); 44901a14edeSTomas Winkler } 45001a14edeSTomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_enabled); 45101a14edeSTomas Winkler 45201a14edeSTomas Winkler /** 4535026c9cbSAlexander Usyskin * mei_cldev_enable - enable me client device 45462382997STomas Winkler * create connection with 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_enable(struct mei_cl_device *cldev) 46162382997STomas Winkler { 4626009595aSTomas Winkler struct mei_device *bus = cldev->bus; 4636009595aSTomas Winkler struct mei_cl *cl; 4646009595aSTomas Winkler int ret; 46562382997STomas Winkler 4666009595aSTomas Winkler cl = cldev->cl; 46762382997STomas Winkler 468c110cdb1SAlexander Usyskin if (cl->state == MEI_FILE_UNINITIALIZED) { 4696009595aSTomas Winkler mutex_lock(&bus->device_lock); 470c110cdb1SAlexander Usyskin ret = mei_cl_link(cl); 4716009595aSTomas Winkler mutex_unlock(&bus->device_lock); 472c110cdb1SAlexander Usyskin if (ret) 473c110cdb1SAlexander Usyskin return ret; 4746009595aSTomas Winkler /* update pointers */ 4756009595aSTomas Winkler cl->cldev = cldev; 4766009595aSTomas Winkler } 47762382997STomas Winkler 47862382997STomas Winkler mutex_lock(&bus->device_lock); 47962382997STomas Winkler if (mei_cl_is_connected(cl)) { 4806009595aSTomas Winkler ret = 0; 4816009595aSTomas Winkler goto out; 48262382997STomas Winkler } 48362382997STomas Winkler 4846009595aSTomas Winkler if (!mei_me_cl_is_active(cldev->me_cl)) { 4856009595aSTomas Winkler dev_err(&cldev->dev, "me client is not active\n"); 4866009595aSTomas Winkler ret = -ENOTTY; 4876009595aSTomas Winkler goto out; 48862382997STomas Winkler } 48962382997STomas Winkler 4906009595aSTomas Winkler ret = mei_cl_connect(cl, cldev->me_cl, NULL); 4916009595aSTomas Winkler if (ret < 0) 4926009595aSTomas Winkler dev_err(&cldev->dev, "cannot connect\n"); 4936009595aSTomas Winkler 4946009595aSTomas Winkler out: 49562382997STomas Winkler mutex_unlock(&bus->device_lock); 49662382997STomas Winkler 4976009595aSTomas Winkler return ret; 49862382997STomas Winkler } 499d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_enable); 50062382997STomas Winkler 50162382997STomas Winkler /** 50257080e88SAlexander Usyskin * mei_cldev_unregister_callbacks - internal wrapper for unregistering 50357080e88SAlexander Usyskin * callbacks. 50457080e88SAlexander Usyskin * 50557080e88SAlexander Usyskin * @cldev: client device 50657080e88SAlexander Usyskin */ 50757080e88SAlexander Usyskin static void mei_cldev_unregister_callbacks(struct mei_cl_device *cldev) 50857080e88SAlexander Usyskin { 50957080e88SAlexander Usyskin if (cldev->rx_cb) { 51057080e88SAlexander Usyskin cancel_work_sync(&cldev->rx_work); 51157080e88SAlexander Usyskin cldev->rx_cb = NULL; 51257080e88SAlexander Usyskin } 51357080e88SAlexander Usyskin 51457080e88SAlexander Usyskin if (cldev->notif_cb) { 51557080e88SAlexander Usyskin cancel_work_sync(&cldev->notif_work); 51657080e88SAlexander Usyskin cldev->notif_cb = NULL; 51757080e88SAlexander Usyskin } 51857080e88SAlexander Usyskin } 51957080e88SAlexander Usyskin 52057080e88SAlexander Usyskin /** 521d49dc5e7STomas Winkler * mei_cldev_disable - disable me client device 52262382997STomas Winkler * disconnect form the me client 52362382997STomas Winkler * 52462382997STomas Winkler * @cldev: me client device 52562382997STomas Winkler * 52662382997STomas Winkler * Return: 0 on success and < 0 on error 52762382997STomas Winkler */ 528d49dc5e7STomas Winkler int mei_cldev_disable(struct mei_cl_device *cldev) 52962382997STomas Winkler { 53062382997STomas Winkler struct mei_device *bus; 5316009595aSTomas Winkler struct mei_cl *cl; 5326009595aSTomas Winkler int err; 53362382997STomas Winkler 534c110cdb1SAlexander Usyskin if (!cldev) 53562382997STomas Winkler return -ENODEV; 53662382997STomas Winkler 5376009595aSTomas Winkler cl = cldev->cl; 5386009595aSTomas Winkler 5396009595aSTomas Winkler bus = cldev->bus; 54062382997STomas Winkler 54157080e88SAlexander Usyskin mei_cldev_unregister_callbacks(cldev); 54257080e88SAlexander Usyskin 54362382997STomas Winkler mutex_lock(&bus->device_lock); 54462382997STomas Winkler 54562382997STomas Winkler if (!mei_cl_is_connected(cl)) { 546d882039eSAlexander Usyskin dev_dbg(bus->dev, "Already disconnected"); 54762382997STomas Winkler err = 0; 54862382997STomas Winkler goto out; 54962382997STomas Winkler } 55062382997STomas Winkler 55162382997STomas Winkler err = mei_cl_disconnect(cl); 5526009595aSTomas Winkler if (err < 0) 55362382997STomas Winkler dev_err(bus->dev, "Could not disconnect from the ME client"); 55462382997STomas Winkler 55562382997STomas Winkler out: 5566009595aSTomas Winkler /* Flush queues and remove any pending read */ 5576009595aSTomas Winkler mei_cl_flush_queues(cl, NULL); 5586009595aSTomas Winkler mei_cl_unlink(cl); 5596009595aSTomas Winkler 56062382997STomas Winkler mutex_unlock(&bus->device_lock); 56162382997STomas Winkler return err; 56262382997STomas Winkler } 563d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_disable); 56462382997STomas Winkler 565688a9cceSTomas Winkler /** 5665d882460SAlexander Usyskin * mei_cl_bus_module_get - acquire module of the underlying 5675d882460SAlexander Usyskin * hw module. 5685d882460SAlexander Usyskin * 5695d882460SAlexander Usyskin * @cl: host client 5705d882460SAlexander Usyskin * 5715d882460SAlexander Usyskin * Return: true on success; false if the module was removed. 5725d882460SAlexander Usyskin */ 5735d882460SAlexander Usyskin bool mei_cl_bus_module_get(struct mei_cl *cl) 5745d882460SAlexander Usyskin { 5755d882460SAlexander Usyskin struct mei_cl_device *cldev = cl->cldev; 5765d882460SAlexander Usyskin 5775d882460SAlexander Usyskin if (!cldev) 5785d882460SAlexander Usyskin return true; 5795d882460SAlexander Usyskin 5805d882460SAlexander Usyskin return try_module_get(cldev->bus->dev->driver->owner); 5815d882460SAlexander Usyskin } 5825d882460SAlexander Usyskin 5835d882460SAlexander Usyskin /** 5845d882460SAlexander Usyskin * mei_cl_bus_module_put - release the underlying hw module. 5855d882460SAlexander Usyskin * 5865d882460SAlexander Usyskin * @cl: host client 5875d882460SAlexander Usyskin */ 5885d882460SAlexander Usyskin void mei_cl_bus_module_put(struct mei_cl *cl) 5895d882460SAlexander Usyskin { 5905d882460SAlexander Usyskin struct mei_cl_device *cldev = cl->cldev; 5915d882460SAlexander Usyskin 5925d882460SAlexander Usyskin if (cldev) 5935d882460SAlexander Usyskin module_put(cldev->bus->dev->driver->owner); 5945d882460SAlexander Usyskin } 5955d882460SAlexander Usyskin 5965d882460SAlexander Usyskin /** 597688a9cceSTomas Winkler * mei_cl_device_find - find matching entry in the driver id table 598688a9cceSTomas Winkler * 599688a9cceSTomas Winkler * @cldev: me client device 600688a9cceSTomas Winkler * @cldrv: me client driver 601688a9cceSTomas Winkler * 602688a9cceSTomas Winkler * Return: id on success; NULL if no id is matching 603688a9cceSTomas Winkler */ 604688a9cceSTomas Winkler static const 605688a9cceSTomas Winkler struct mei_cl_device_id *mei_cl_device_find(struct mei_cl_device *cldev, 606688a9cceSTomas Winkler struct mei_cl_driver *cldrv) 607e5354107SSamuel Ortiz { 608e5354107SSamuel Ortiz const struct mei_cl_device_id *id; 609c93b76b3STomas Winkler const uuid_le *uuid; 610b26864caSTomas Winkler u8 version; 611b26864caSTomas Winkler bool match; 612e5354107SSamuel Ortiz 613b37719c3STomas Winkler uuid = mei_me_cl_uuid(cldev->me_cl); 614b26864caSTomas Winkler version = mei_me_cl_ver(cldev->me_cl); 615e5354107SSamuel Ortiz 616b37719c3STomas Winkler id = cldrv->id_table; 617b144ce2dSGreg Kroah-Hartman while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) { 618b144ce2dSGreg Kroah-Hartman if (!uuid_le_cmp(*uuid, id->uuid)) { 619b26864caSTomas Winkler match = true; 620688a9cceSTomas Winkler 621b26864caSTomas Winkler if (cldev->name[0]) 622b26864caSTomas Winkler if (strncmp(cldev->name, id->name, 623b26864caSTomas Winkler sizeof(id->name))) 624b26864caSTomas Winkler match = false; 625688a9cceSTomas Winkler 626b26864caSTomas Winkler if (id->version != MEI_CL_VERSION_ANY) 627b26864caSTomas Winkler if (id->version != version) 628b26864caSTomas Winkler match = false; 629b26864caSTomas Winkler if (match) 630688a9cceSTomas Winkler return id; 631c93b76b3STomas Winkler } 632e5354107SSamuel Ortiz 633e5354107SSamuel Ortiz id++; 634e5354107SSamuel Ortiz } 635e5354107SSamuel Ortiz 636688a9cceSTomas Winkler return NULL; 637688a9cceSTomas Winkler } 638688a9cceSTomas Winkler 639688a9cceSTomas Winkler /** 640688a9cceSTomas Winkler * mei_cl_device_match - device match function 641688a9cceSTomas Winkler * 642688a9cceSTomas Winkler * @dev: device 643688a9cceSTomas Winkler * @drv: driver 644688a9cceSTomas Winkler * 645688a9cceSTomas Winkler * Return: 1 if matching device was found 0 otherwise 646688a9cceSTomas Winkler */ 647688a9cceSTomas Winkler static int mei_cl_device_match(struct device *dev, struct device_driver *drv) 648688a9cceSTomas Winkler { 649688a9cceSTomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 650688a9cceSTomas Winkler struct mei_cl_driver *cldrv = to_mei_cl_driver(drv); 651688a9cceSTomas Winkler const struct mei_cl_device_id *found_id; 652688a9cceSTomas Winkler 653688a9cceSTomas Winkler if (!cldev) 654688a9cceSTomas Winkler return 0; 655688a9cceSTomas Winkler 65671ce7891STomas Winkler if (!cldev->do_match) 65771ce7891STomas Winkler return 0; 65871ce7891STomas Winkler 659688a9cceSTomas Winkler if (!cldrv || !cldrv->id_table) 660688a9cceSTomas Winkler return 0; 661688a9cceSTomas Winkler 662688a9cceSTomas Winkler found_id = mei_cl_device_find(cldev, cldrv); 663688a9cceSTomas Winkler if (found_id) 664688a9cceSTomas Winkler return 1; 665688a9cceSTomas Winkler 666e5354107SSamuel Ortiz return 0; 667e5354107SSamuel Ortiz } 668e5354107SSamuel Ortiz 669feb8cd0fSTomas Winkler /** 670feb8cd0fSTomas Winkler * mei_cl_device_probe - bus probe function 671feb8cd0fSTomas Winkler * 672feb8cd0fSTomas Winkler * @dev: device 673feb8cd0fSTomas Winkler * 674feb8cd0fSTomas Winkler * Return: 0 on success; < 0 otherwise 675feb8cd0fSTomas Winkler */ 676e5354107SSamuel Ortiz static int mei_cl_device_probe(struct device *dev) 677e5354107SSamuel Ortiz { 678feb8cd0fSTomas Winkler struct mei_cl_device *cldev; 679b37719c3STomas Winkler struct mei_cl_driver *cldrv; 680feb8cd0fSTomas Winkler const struct mei_cl_device_id *id; 681b9c79543SAlexey Khoroshilov int ret; 682feb8cd0fSTomas Winkler 683feb8cd0fSTomas Winkler cldev = to_mei_cl_device(dev); 684feb8cd0fSTomas Winkler cldrv = to_mei_cl_driver(dev->driver); 685e5354107SSamuel Ortiz 686b37719c3STomas Winkler if (!cldev) 687e5354107SSamuel Ortiz return 0; 688e5354107SSamuel Ortiz 689b37719c3STomas Winkler if (!cldrv || !cldrv->probe) 690e5354107SSamuel Ortiz return -ENODEV; 691e5354107SSamuel Ortiz 692feb8cd0fSTomas Winkler id = mei_cl_device_find(cldev, cldrv); 693feb8cd0fSTomas Winkler if (!id) 694feb8cd0fSTomas Winkler return -ENODEV; 695e5354107SSamuel Ortiz 696b9c79543SAlexey Khoroshilov ret = cldrv->probe(cldev, id); 697b9c79543SAlexey Khoroshilov if (ret) 698b9c79543SAlexey Khoroshilov return ret; 699e5354107SSamuel Ortiz 700b9c79543SAlexey Khoroshilov __module_get(THIS_MODULE); 701b9c79543SAlexey Khoroshilov return 0; 702e5354107SSamuel Ortiz } 703e5354107SSamuel Ortiz 704feb8cd0fSTomas Winkler /** 705feb8cd0fSTomas Winkler * mei_cl_device_remove - remove device from the bus 706feb8cd0fSTomas Winkler * 707feb8cd0fSTomas Winkler * @dev: device 708feb8cd0fSTomas Winkler * 709feb8cd0fSTomas Winkler * Return: 0 on success; < 0 otherwise 710feb8cd0fSTomas Winkler */ 711e5354107SSamuel Ortiz static int mei_cl_device_remove(struct device *dev) 712e5354107SSamuel Ortiz { 713b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 714b37719c3STomas Winkler struct mei_cl_driver *cldrv; 715feb8cd0fSTomas Winkler int ret = 0; 716e5354107SSamuel Ortiz 717b37719c3STomas Winkler if (!cldev || !dev->driver) 718e5354107SSamuel Ortiz return 0; 719e5354107SSamuel Ortiz 7209ecb839fSAlexander Usyskin cldrv = to_mei_cl_driver(dev->driver); 7219ecb839fSAlexander Usyskin if (cldrv->remove) 7229ecb839fSAlexander Usyskin ret = cldrv->remove(cldev); 7239ecb839fSAlexander Usyskin 72457080e88SAlexander Usyskin mei_cldev_unregister_callbacks(cldev); 7253e833295SSamuel Ortiz 726feb8cd0fSTomas Winkler module_put(THIS_MODULE); 727e5354107SSamuel Ortiz dev->driver = NULL; 728feb8cd0fSTomas Winkler return ret; 729e5354107SSamuel Ortiz 730e5354107SSamuel Ortiz } 731e5354107SSamuel Ortiz 732007d64ebSTomas Winkler static ssize_t name_show(struct device *dev, struct device_attribute *a, 733007d64ebSTomas Winkler char *buf) 734007d64ebSTomas Winkler { 735b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 736007d64ebSTomas Winkler 737423de92fSRasmus Villemoes return scnprintf(buf, PAGE_SIZE, "%s", cldev->name); 738007d64ebSTomas Winkler } 739007d64ebSTomas Winkler static DEVICE_ATTR_RO(name); 740007d64ebSTomas Winkler 741007d64ebSTomas Winkler static ssize_t uuid_show(struct device *dev, struct device_attribute *a, 742007d64ebSTomas Winkler char *buf) 743007d64ebSTomas Winkler { 744b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 745b37719c3STomas Winkler const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); 746007d64ebSTomas Winkler 747423de92fSRasmus Villemoes return scnprintf(buf, PAGE_SIZE, "%pUl", uuid); 748007d64ebSTomas Winkler } 749007d64ebSTomas Winkler static DEVICE_ATTR_RO(uuid); 750007d64ebSTomas Winkler 75140b7320eSTomas Winkler static ssize_t version_show(struct device *dev, struct device_attribute *a, 75240b7320eSTomas Winkler char *buf) 75340b7320eSTomas Winkler { 75440b7320eSTomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 75540b7320eSTomas Winkler u8 version = mei_me_cl_ver(cldev->me_cl); 75640b7320eSTomas Winkler 757423de92fSRasmus Villemoes return scnprintf(buf, PAGE_SIZE, "%02X", version); 75840b7320eSTomas Winkler } 75940b7320eSTomas Winkler static DEVICE_ATTR_RO(version); 76040b7320eSTomas Winkler 761e5354107SSamuel Ortiz static ssize_t modalias_show(struct device *dev, struct device_attribute *a, 762e5354107SSamuel Ortiz char *buf) 763e5354107SSamuel Ortiz { 764b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 765b37719c3STomas Winkler const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); 766e5354107SSamuel Ortiz 767423de92fSRasmus Villemoes return scnprintf(buf, PAGE_SIZE, "mei:%s:%pUl:", cldev->name, uuid); 768e5354107SSamuel Ortiz } 76932f389ecSGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias); 770e5354107SSamuel Ortiz 771d49dc5e7STomas Winkler static struct attribute *mei_cldev_attrs[] = { 772007d64ebSTomas Winkler &dev_attr_name.attr, 773007d64ebSTomas Winkler &dev_attr_uuid.attr, 77440b7320eSTomas Winkler &dev_attr_version.attr, 77532f389ecSGreg Kroah-Hartman &dev_attr_modalias.attr, 77632f389ecSGreg Kroah-Hartman NULL, 777e5354107SSamuel Ortiz }; 778d49dc5e7STomas Winkler ATTRIBUTE_GROUPS(mei_cldev); 779e5354107SSamuel Ortiz 78038d3c00dSTomas Winkler /** 78138d3c00dSTomas Winkler * mei_cl_device_uevent - me client bus uevent handler 78238d3c00dSTomas Winkler * 78338d3c00dSTomas Winkler * @dev: device 78438d3c00dSTomas Winkler * @env: uevent kobject 78538d3c00dSTomas Winkler * 78638d3c00dSTomas Winkler * Return: 0 on success -ENOMEM on when add_uevent_var fails 78738d3c00dSTomas Winkler */ 78838d3c00dSTomas Winkler static int mei_cl_device_uevent(struct device *dev, struct kobj_uevent_env *env) 789e5354107SSamuel Ortiz { 790b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 791b37719c3STomas Winkler const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); 79240b7320eSTomas Winkler u8 version = mei_me_cl_ver(cldev->me_cl); 79340b7320eSTomas Winkler 79440b7320eSTomas Winkler if (add_uevent_var(env, "MEI_CL_VERSION=%d", version)) 79540b7320eSTomas Winkler return -ENOMEM; 796c93b76b3STomas Winkler 797007d64ebSTomas Winkler if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid)) 798007d64ebSTomas Winkler return -ENOMEM; 799007d64ebSTomas Winkler 800b37719c3STomas Winkler if (add_uevent_var(env, "MEI_CL_NAME=%s", cldev->name)) 801007d64ebSTomas Winkler return -ENOMEM; 802007d64ebSTomas Winkler 803b26864caSTomas Winkler if (add_uevent_var(env, "MODALIAS=mei:%s:%pUl:%02X:", 804b26864caSTomas Winkler cldev->name, uuid, version)) 805e5354107SSamuel Ortiz return -ENOMEM; 806e5354107SSamuel Ortiz 807e5354107SSamuel Ortiz return 0; 808e5354107SSamuel Ortiz } 809e5354107SSamuel Ortiz 810e5354107SSamuel Ortiz static struct bus_type mei_cl_bus_type = { 811e5354107SSamuel Ortiz .name = "mei", 812d49dc5e7STomas Winkler .dev_groups = mei_cldev_groups, 813e5354107SSamuel Ortiz .match = mei_cl_device_match, 814e5354107SSamuel Ortiz .probe = mei_cl_device_probe, 815e5354107SSamuel Ortiz .remove = mei_cl_device_remove, 81638d3c00dSTomas Winkler .uevent = mei_cl_device_uevent, 817e5354107SSamuel Ortiz }; 818e5354107SSamuel Ortiz 819512f64d9STomas Winkler static struct mei_device *mei_dev_bus_get(struct mei_device *bus) 820512f64d9STomas Winkler { 821512f64d9STomas Winkler if (bus) 822512f64d9STomas Winkler get_device(bus->dev); 823512f64d9STomas Winkler 824512f64d9STomas Winkler return bus; 825512f64d9STomas Winkler } 826512f64d9STomas Winkler 827512f64d9STomas Winkler static void mei_dev_bus_put(struct mei_device *bus) 828512f64d9STomas Winkler { 829512f64d9STomas Winkler if (bus) 830512f64d9STomas Winkler put_device(bus->dev); 831512f64d9STomas Winkler } 832512f64d9STomas Winkler 833ae48d74dSTomas Winkler static void mei_cl_bus_dev_release(struct device *dev) 834e5354107SSamuel Ortiz { 835b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 836d49ed64aSAlexander Usyskin 837b37719c3STomas Winkler if (!cldev) 838d49ed64aSAlexander Usyskin return; 839d49ed64aSAlexander Usyskin 840b37719c3STomas Winkler mei_me_cl_put(cldev->me_cl); 841512f64d9STomas Winkler mei_dev_bus_put(cldev->bus); 842c110cdb1SAlexander Usyskin kfree(cldev->cl); 843b37719c3STomas Winkler kfree(cldev); 844e5354107SSamuel Ortiz } 845e5354107SSamuel Ortiz 846e5354107SSamuel Ortiz static struct device_type mei_cl_device_type = { 847ae48d74dSTomas Winkler .release = mei_cl_bus_dev_release, 848e5354107SSamuel Ortiz }; 849e5354107SSamuel Ortiz 85071ce7891STomas Winkler /** 851213dd193STomas Winkler * mei_cl_bus_set_name - set device name for me client device 852213dd193STomas Winkler * 853213dd193STomas Winkler * @cldev: me client device 854213dd193STomas Winkler */ 855213dd193STomas Winkler static inline void mei_cl_bus_set_name(struct mei_cl_device *cldev) 856213dd193STomas Winkler { 857213dd193STomas Winkler dev_set_name(&cldev->dev, "mei:%s:%pUl:%02X", 858213dd193STomas Winkler cldev->name, 859213dd193STomas Winkler mei_me_cl_uuid(cldev->me_cl), 860213dd193STomas Winkler mei_me_cl_ver(cldev->me_cl)); 861213dd193STomas Winkler } 862213dd193STomas Winkler 863213dd193STomas Winkler /** 864ae48d74dSTomas Winkler * mei_cl_bus_dev_alloc - initialize and allocate mei client device 86571ce7891STomas Winkler * 86671ce7891STomas Winkler * @bus: mei device 86771ce7891STomas Winkler * @me_cl: me client 86871ce7891STomas Winkler * 86971ce7891STomas Winkler * Return: allocated device structur or NULL on allocation failure 87071ce7891STomas Winkler */ 871ae48d74dSTomas Winkler static struct mei_cl_device *mei_cl_bus_dev_alloc(struct mei_device *bus, 87271ce7891STomas Winkler struct mei_me_client *me_cl) 87371ce7891STomas Winkler { 87471ce7891STomas Winkler struct mei_cl_device *cldev; 875c110cdb1SAlexander Usyskin struct mei_cl *cl; 87671ce7891STomas Winkler 87771ce7891STomas Winkler cldev = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); 87871ce7891STomas Winkler if (!cldev) 87971ce7891STomas Winkler return NULL; 88071ce7891STomas Winkler 881c110cdb1SAlexander Usyskin cl = mei_cl_allocate(bus); 882c110cdb1SAlexander Usyskin if (!cl) { 883c110cdb1SAlexander Usyskin kfree(cldev); 884c110cdb1SAlexander Usyskin return NULL; 885c110cdb1SAlexander Usyskin } 886c110cdb1SAlexander Usyskin 88771ce7891STomas Winkler device_initialize(&cldev->dev); 88871ce7891STomas Winkler cldev->dev.parent = bus->dev; 88971ce7891STomas Winkler cldev->dev.bus = &mei_cl_bus_type; 89071ce7891STomas Winkler cldev->dev.type = &mei_cl_device_type; 89171ce7891STomas Winkler cldev->bus = mei_dev_bus_get(bus); 89271ce7891STomas Winkler cldev->me_cl = mei_me_cl_get(me_cl); 893c110cdb1SAlexander Usyskin cldev->cl = cl; 894213dd193STomas Winkler mei_cl_bus_set_name(cldev); 89571ce7891STomas Winkler cldev->is_added = 0; 89671ce7891STomas Winkler INIT_LIST_HEAD(&cldev->bus_list); 89771ce7891STomas Winkler 89871ce7891STomas Winkler return cldev; 89971ce7891STomas Winkler } 90071ce7891STomas Winkler 90171ce7891STomas Winkler /** 90271ce7891STomas Winkler * mei_cl_dev_setup - setup me client device 90371ce7891STomas Winkler * run fix up routines and set the device name 90471ce7891STomas Winkler * 90571ce7891STomas Winkler * @bus: mei device 90671ce7891STomas Winkler * @cldev: me client device 90771ce7891STomas Winkler * 90871ce7891STomas Winkler * Return: true if the device is eligible for enumeration 90971ce7891STomas Winkler */ 910ae48d74dSTomas Winkler static bool mei_cl_bus_dev_setup(struct mei_device *bus, 91171ce7891STomas Winkler struct mei_cl_device *cldev) 91271ce7891STomas Winkler { 91371ce7891STomas Winkler cldev->do_match = 1; 914ae48d74dSTomas Winkler mei_cl_bus_dev_fixup(cldev); 91571ce7891STomas Winkler 916213dd193STomas Winkler /* the device name can change during fix up */ 91771ce7891STomas Winkler if (cldev->do_match) 918213dd193STomas Winkler mei_cl_bus_set_name(cldev); 91971ce7891STomas Winkler 92071ce7891STomas Winkler return cldev->do_match == 1; 92171ce7891STomas Winkler } 92271ce7891STomas Winkler 92371ce7891STomas Winkler /** 92471ce7891STomas Winkler * mei_cl_bus_dev_add - add me client devices 92571ce7891STomas Winkler * 92671ce7891STomas Winkler * @cldev: me client device 92771ce7891STomas Winkler * 92871ce7891STomas Winkler * Return: 0 on success; < 0 on failre 92971ce7891STomas Winkler */ 93071ce7891STomas Winkler static int mei_cl_bus_dev_add(struct mei_cl_device *cldev) 93171ce7891STomas Winkler { 93271ce7891STomas Winkler int ret; 93371ce7891STomas Winkler 934b26864caSTomas Winkler dev_dbg(cldev->bus->dev, "adding %pUL:%02X\n", 935b26864caSTomas Winkler mei_me_cl_uuid(cldev->me_cl), 936b26864caSTomas Winkler mei_me_cl_ver(cldev->me_cl)); 93771ce7891STomas Winkler ret = device_add(&cldev->dev); 93871ce7891STomas Winkler if (!ret) 93971ce7891STomas Winkler cldev->is_added = 1; 94071ce7891STomas Winkler 94171ce7891STomas Winkler return ret; 94271ce7891STomas Winkler } 94371ce7891STomas Winkler 9446009595aSTomas Winkler /** 9456009595aSTomas Winkler * mei_cl_bus_dev_stop - stop the driver 9466009595aSTomas Winkler * 9476009595aSTomas Winkler * @cldev: me client device 9486009595aSTomas Winkler */ 9496009595aSTomas Winkler static void mei_cl_bus_dev_stop(struct mei_cl_device *cldev) 9506009595aSTomas Winkler { 9516009595aSTomas Winkler if (cldev->is_added) 9526009595aSTomas Winkler device_release_driver(&cldev->dev); 9536009595aSTomas Winkler } 9546009595aSTomas Winkler 9556009595aSTomas Winkler /** 9566009595aSTomas Winkler * mei_cl_bus_dev_destroy - destroy me client devices object 9576009595aSTomas Winkler * 9586009595aSTomas Winkler * @cldev: me client device 9592da55cfdSTomas Winkler * 9602da55cfdSTomas Winkler * Locking: called under "dev->cl_bus_lock" lock 9616009595aSTomas Winkler */ 9626009595aSTomas Winkler static void mei_cl_bus_dev_destroy(struct mei_cl_device *cldev) 9636009595aSTomas Winkler { 9642da55cfdSTomas Winkler 9652da55cfdSTomas Winkler WARN_ON(!mutex_is_locked(&cldev->bus->cl_bus_lock)); 9662da55cfdSTomas Winkler 9676009595aSTomas Winkler if (!cldev->is_added) 9686009595aSTomas Winkler return; 9696009595aSTomas Winkler 9706009595aSTomas Winkler device_del(&cldev->dev); 9716009595aSTomas Winkler 9726009595aSTomas Winkler list_del_init(&cldev->bus_list); 9736009595aSTomas Winkler 9746009595aSTomas Winkler cldev->is_added = 0; 9756009595aSTomas Winkler put_device(&cldev->dev); 9766009595aSTomas Winkler } 9776009595aSTomas Winkler 9786009595aSTomas Winkler /** 9796009595aSTomas Winkler * mei_cl_bus_remove_device - remove a devices form the bus 9806009595aSTomas Winkler * 9816009595aSTomas Winkler * @cldev: me client device 9826009595aSTomas Winkler */ 9836009595aSTomas Winkler static void mei_cl_bus_remove_device(struct mei_cl_device *cldev) 9846009595aSTomas Winkler { 9856009595aSTomas Winkler mei_cl_bus_dev_stop(cldev); 9866009595aSTomas Winkler mei_cl_bus_dev_destroy(cldev); 9876009595aSTomas Winkler } 9886009595aSTomas Winkler 9896009595aSTomas Winkler /** 9906009595aSTomas Winkler * mei_cl_bus_remove_devices - remove all devices form the bus 9916009595aSTomas Winkler * 9926009595aSTomas Winkler * @bus: mei device 9936009595aSTomas Winkler */ 9946009595aSTomas Winkler void mei_cl_bus_remove_devices(struct mei_device *bus) 9956009595aSTomas Winkler { 9966009595aSTomas Winkler struct mei_cl_device *cldev, *next; 9976009595aSTomas Winkler 9982da55cfdSTomas Winkler mutex_lock(&bus->cl_bus_lock); 9996009595aSTomas Winkler list_for_each_entry_safe(cldev, next, &bus->device_list, bus_list) 10006009595aSTomas Winkler mei_cl_bus_remove_device(cldev); 10012da55cfdSTomas Winkler mutex_unlock(&bus->cl_bus_lock); 10026009595aSTomas Winkler } 10036009595aSTomas Winkler 10046009595aSTomas Winkler 10056009595aSTomas Winkler /** 1006ae48d74dSTomas Winkler * mei_cl_bus_dev_init - allocate and initializes an mei client devices 10076009595aSTomas Winkler * based on me client 10086009595aSTomas Winkler * 10096009595aSTomas Winkler * @bus: mei device 10106009595aSTomas Winkler * @me_cl: me client 10112da55cfdSTomas Winkler * 10122da55cfdSTomas Winkler * Locking: called under "dev->cl_bus_lock" lock 10136009595aSTomas Winkler */ 1014ae48d74dSTomas Winkler static void mei_cl_bus_dev_init(struct mei_device *bus, 1015ae48d74dSTomas Winkler struct mei_me_client *me_cl) 1016e5354107SSamuel Ortiz { 1017b37719c3STomas Winkler struct mei_cl_device *cldev; 10186009595aSTomas Winkler 10192da55cfdSTomas Winkler WARN_ON(!mutex_is_locked(&bus->cl_bus_lock)); 10202da55cfdSTomas Winkler 10216009595aSTomas Winkler dev_dbg(bus->dev, "initializing %pUl", mei_me_cl_uuid(me_cl)); 10226009595aSTomas Winkler 10236009595aSTomas Winkler if (me_cl->bus_added) 10246009595aSTomas Winkler return; 1025e5354107SSamuel Ortiz 1026ae48d74dSTomas Winkler cldev = mei_cl_bus_dev_alloc(bus, me_cl); 1027b37719c3STomas Winkler if (!cldev) 10286009595aSTomas Winkler return; 1029e5354107SSamuel Ortiz 10306009595aSTomas Winkler me_cl->bus_added = true; 10316009595aSTomas Winkler list_add_tail(&cldev->bus_list, &bus->device_list); 1032c93b76b3STomas Winkler 1033a7b71bc0SSamuel Ortiz } 1034a7b71bc0SSamuel Ortiz 10356009595aSTomas Winkler /** 10366009595aSTomas Winkler * mei_cl_bus_rescan - scan me clients list and add create 10376009595aSTomas Winkler * devices for eligible clients 10386009595aSTomas Winkler * 10396009595aSTomas Winkler * @bus: mei device 10406009595aSTomas Winkler */ 10416009595aSTomas Winkler void mei_cl_bus_rescan(struct mei_device *bus) 1042e5354107SSamuel Ortiz { 10436009595aSTomas Winkler struct mei_cl_device *cldev, *n; 10446009595aSTomas Winkler struct mei_me_client *me_cl; 10456009595aSTomas Winkler 10462da55cfdSTomas Winkler mutex_lock(&bus->cl_bus_lock); 10472da55cfdSTomas Winkler 10486009595aSTomas Winkler down_read(&bus->me_clients_rwsem); 10496009595aSTomas Winkler list_for_each_entry(me_cl, &bus->me_clients, list) 1050ae48d74dSTomas Winkler mei_cl_bus_dev_init(bus, me_cl); 10516009595aSTomas Winkler up_read(&bus->me_clients_rwsem); 10526009595aSTomas Winkler 10536009595aSTomas Winkler list_for_each_entry_safe(cldev, n, &bus->device_list, bus_list) { 10546009595aSTomas Winkler 10556009595aSTomas Winkler if (!mei_me_cl_is_active(cldev->me_cl)) { 10566009595aSTomas Winkler mei_cl_bus_remove_device(cldev); 10576009595aSTomas Winkler continue; 1058e5354107SSamuel Ortiz } 10596009595aSTomas Winkler 10606009595aSTomas Winkler if (cldev->is_added) 10616009595aSTomas Winkler continue; 10626009595aSTomas Winkler 1063ae48d74dSTomas Winkler if (mei_cl_bus_dev_setup(bus, cldev)) 10646009595aSTomas Winkler mei_cl_bus_dev_add(cldev); 10656009595aSTomas Winkler else { 10666009595aSTomas Winkler list_del_init(&cldev->bus_list); 10676009595aSTomas Winkler put_device(&cldev->dev); 10686009595aSTomas Winkler } 10696009595aSTomas Winkler } 10706009595aSTomas Winkler mutex_unlock(&bus->cl_bus_lock); 10716009595aSTomas Winkler 10726009595aSTomas Winkler dev_dbg(bus->dev, "rescan end"); 10736009595aSTomas Winkler } 1074333e4ee0SSamuel Ortiz 1075a816a00eSAlexander Usyskin void mei_cl_bus_rescan_work(struct work_struct *work) 1076a816a00eSAlexander Usyskin { 1077a816a00eSAlexander Usyskin struct mei_device *bus = 1078a816a00eSAlexander Usyskin container_of(work, struct mei_device, bus_rescan_work); 1079025fb792SAlexander Usyskin struct mei_me_client *me_cl; 1080025fb792SAlexander Usyskin 1081025fb792SAlexander Usyskin me_cl = mei_me_cl_by_uuid(bus, &mei_amthif_guid); 1082025fb792SAlexander Usyskin if (me_cl) 1083025fb792SAlexander Usyskin mei_amthif_host_init(bus, me_cl); 1084025fb792SAlexander Usyskin mei_me_cl_put(me_cl); 1085a816a00eSAlexander Usyskin 1086a816a00eSAlexander Usyskin mei_cl_bus_rescan(bus); 1087a816a00eSAlexander Usyskin } 1088a816a00eSAlexander Usyskin 1089d49dc5e7STomas Winkler int __mei_cldev_driver_register(struct mei_cl_driver *cldrv, 1090d49dc5e7STomas Winkler struct module *owner) 1091333e4ee0SSamuel Ortiz { 1092333e4ee0SSamuel Ortiz int err; 1093333e4ee0SSamuel Ortiz 1094b37719c3STomas Winkler cldrv->driver.name = cldrv->name; 1095b37719c3STomas Winkler cldrv->driver.owner = owner; 1096b37719c3STomas Winkler cldrv->driver.bus = &mei_cl_bus_type; 1097333e4ee0SSamuel Ortiz 1098b37719c3STomas Winkler err = driver_register(&cldrv->driver); 1099333e4ee0SSamuel Ortiz if (err) 1100333e4ee0SSamuel Ortiz return err; 1101333e4ee0SSamuel Ortiz 1102b37719c3STomas Winkler pr_debug("mei: driver [%s] registered\n", cldrv->driver.name); 1103333e4ee0SSamuel Ortiz 1104333e4ee0SSamuel Ortiz return 0; 1105333e4ee0SSamuel Ortiz } 1106d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(__mei_cldev_driver_register); 1107333e4ee0SSamuel Ortiz 1108d49dc5e7STomas Winkler void mei_cldev_driver_unregister(struct mei_cl_driver *cldrv) 1109333e4ee0SSamuel Ortiz { 1110b37719c3STomas Winkler driver_unregister(&cldrv->driver); 1111333e4ee0SSamuel Ortiz 1112b37719c3STomas Winkler pr_debug("mei: driver [%s] unregistered\n", cldrv->driver.name); 1113333e4ee0SSamuel Ortiz } 1114d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_driver_unregister); 11153e833295SSamuel Ortiz 11166009595aSTomas Winkler 1117cf3baefbSSamuel Ortiz int __init mei_cl_bus_init(void) 1118cf3baefbSSamuel Ortiz { 1119cf3baefbSSamuel Ortiz return bus_register(&mei_cl_bus_type); 1120cf3baefbSSamuel Ortiz } 1121cf3baefbSSamuel Ortiz 1122cf3baefbSSamuel Ortiz void __exit mei_cl_bus_exit(void) 1123cf3baefbSSamuel Ortiz { 1124cf3baefbSSamuel Ortiz bus_unregister(&mei_cl_bus_type); 1125cf3baefbSSamuel Ortiz } 1126