19fff0425STomas Winkler // SPDX-License-Identifier: GPL-2.0 2e5354107SSamuel Ortiz /* 31e55b609STomas Winkler * Copyright (c) 2012-2019, Intel Corporation. All rights reserved. 4e5354107SSamuel Ortiz * Intel Management Engine Interface (Intel MEI) Linux driver 5e5354107SSamuel Ortiz */ 6e5354107SSamuel Ortiz 7e5354107SSamuel Ortiz #include <linux/module.h> 8e5354107SSamuel Ortiz #include <linux/device.h> 9e5354107SSamuel Ortiz #include <linux/kernel.h> 10174cd4b1SIngo Molnar #include <linux/sched/signal.h> 11e5354107SSamuel Ortiz #include <linux/init.h> 12e5354107SSamuel Ortiz #include <linux/errno.h> 13e5354107SSamuel Ortiz #include <linux/slab.h> 14e5354107SSamuel Ortiz #include <linux/mutex.h> 15e5354107SSamuel Ortiz #include <linux/interrupt.h> 16e5354107SSamuel Ortiz #include <linux/mei_cl_bus.h> 17e5354107SSamuel Ortiz 18e5354107SSamuel Ortiz #include "mei_dev.h" 193e833295SSamuel Ortiz #include "client.h" 20e5354107SSamuel Ortiz 21e5354107SSamuel Ortiz #define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver) 22e5354107SSamuel Ortiz 2362382997STomas Winkler /** 2462382997STomas Winkler * __mei_cl_send - internal client send (write) 2562382997STomas Winkler * 2662382997STomas Winkler * @cl: host client 2762382997STomas Winkler * @buf: buffer to send 2862382997STomas Winkler * @length: buffer length 2985261c1fSAlexander Usyskin * @vtag: virtual tag 30e0cb6b2fSAlexander Usyskin * @mode: sending mode 3162382997STomas Winkler * 3262382997STomas Winkler * Return: written size bytes or < 0 on error 3362382997STomas Winkler */ 340912ef48SKrzysztof Kozlowski ssize_t __mei_cl_send(struct mei_cl *cl, const u8 *buf, size_t length, u8 vtag, 35e0cb6b2fSAlexander Usyskin unsigned int mode) 3662382997STomas Winkler { 3762382997STomas Winkler struct mei_device *bus; 386cbb097fSAlexander Usyskin struct mei_cl_cb *cb; 3962382997STomas Winkler ssize_t rets; 4062382997STomas Winkler 4162382997STomas Winkler if (WARN_ON(!cl || !cl->dev)) 4262382997STomas Winkler return -ENODEV; 4362382997STomas Winkler 4462382997STomas Winkler bus = cl->dev; 4562382997STomas Winkler 4662382997STomas Winkler mutex_lock(&bus->device_lock); 4736edb140SAlexander Usyskin if (bus->dev_state != MEI_DEV_ENABLED && 4836edb140SAlexander Usyskin bus->dev_state != MEI_DEV_POWERING_DOWN) { 4915c13dfcSAlexander Usyskin rets = -ENODEV; 5015c13dfcSAlexander Usyskin goto out; 5115c13dfcSAlexander Usyskin } 5215c13dfcSAlexander Usyskin 5362382997STomas Winkler if (!mei_cl_is_connected(cl)) { 5462382997STomas Winkler rets = -ENODEV; 5562382997STomas Winkler goto out; 5662382997STomas Winkler } 5762382997STomas Winkler 5862382997STomas Winkler /* Check if we have an ME client device */ 5962382997STomas Winkler if (!mei_me_cl_is_active(cl->me_cl)) { 6062382997STomas Winkler rets = -ENOTTY; 6162382997STomas Winkler goto out; 6262382997STomas Winkler } 6362382997STomas Winkler 64b398d53cSAlexander Usyskin if (vtag) { 65b398d53cSAlexander Usyskin /* Check if vtag is supported by client */ 66b398d53cSAlexander Usyskin rets = mei_cl_vt_support_check(cl); 67b398d53cSAlexander Usyskin if (rets) 68b398d53cSAlexander Usyskin goto out; 69b398d53cSAlexander Usyskin } 70b398d53cSAlexander Usyskin 7162382997STomas Winkler if (length > mei_cl_mtu(cl)) { 7262382997STomas Winkler rets = -EFBIG; 7362382997STomas Winkler goto out; 7462382997STomas Winkler } 7562382997STomas Winkler 76af336cabSAlexander Usyskin while (cl->tx_cb_queued >= bus->tx_queue_limit) { 77af336cabSAlexander Usyskin mutex_unlock(&bus->device_lock); 78af336cabSAlexander Usyskin rets = wait_event_interruptible(cl->tx_wait, 79af336cabSAlexander Usyskin cl->writing_state == MEI_WRITE_COMPLETE || 80af336cabSAlexander Usyskin (!mei_cl_is_connected(cl))); 81af336cabSAlexander Usyskin mutex_lock(&bus->device_lock); 82af336cabSAlexander Usyskin if (rets) { 83af336cabSAlexander Usyskin if (signal_pending(current)) 84af336cabSAlexander Usyskin rets = -EINTR; 85af336cabSAlexander Usyskin goto out; 86af336cabSAlexander Usyskin } 87af336cabSAlexander Usyskin if (!mei_cl_is_connected(cl)) { 88af336cabSAlexander Usyskin rets = -ENODEV; 89af336cabSAlexander Usyskin goto out; 90af336cabSAlexander Usyskin } 91af336cabSAlexander Usyskin } 92af336cabSAlexander Usyskin 9362382997STomas Winkler cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL); 9462382997STomas Winkler if (!cb) { 9562382997STomas Winkler rets = -ENOMEM; 9662382997STomas Winkler goto out; 9762382997STomas Winkler } 9885261c1fSAlexander Usyskin cb->vtag = vtag; 9962382997STomas Winkler 100e0cb6b2fSAlexander Usyskin cb->internal = !!(mode & MEI_CL_IO_TX_INTERNAL); 101e0cb6b2fSAlexander Usyskin cb->blocking = !!(mode & MEI_CL_IO_TX_BLOCKING); 10262382997STomas Winkler memcpy(cb->buf.data, buf, length); 103*5d5bc189STomas Winkler /* hack we point data to header */ 104*5d5bc189STomas Winkler if (mode & MEI_CL_IO_SGL) { 105*5d5bc189STomas Winkler cb->ext_hdr = (struct mei_ext_hdr *)cb->buf.data; 106*5d5bc189STomas Winkler cb->buf.data = NULL; 107*5d5bc189STomas Winkler cb->buf.size = 0; 108*5d5bc189STomas Winkler } 10962382997STomas Winkler 110e0cb6b2fSAlexander Usyskin rets = mei_cl_write(cl, cb); 11162382997STomas Winkler 112*5d5bc189STomas Winkler if (mode & MEI_CL_IO_SGL && rets == 0) 113*5d5bc189STomas Winkler rets = length; 114*5d5bc189STomas Winkler 11562382997STomas Winkler out: 11662382997STomas Winkler mutex_unlock(&bus->device_lock); 11762382997STomas Winkler 11862382997STomas Winkler return rets; 11962382997STomas Winkler } 12062382997STomas Winkler 12162382997STomas Winkler /** 12262382997STomas Winkler * __mei_cl_recv - internal client receive (read) 12362382997STomas Winkler * 12462382997STomas Winkler * @cl: host client 125df7f5447STomas Winkler * @buf: buffer to receive 12662382997STomas Winkler * @length: buffer length 127076802d0SAlexander Usyskin * @mode: io mode 12885261c1fSAlexander Usyskin * @vtag: virtual tag 1299a7c0b69SAlexander Usyskin * @timeout: recv timeout, 0 for infinite timeout 13062382997STomas Winkler * 13162382997STomas Winkler * Return: read size in bytes of < 0 on error 13262382997STomas Winkler */ 13385261c1fSAlexander Usyskin ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length, u8 *vtag, 1349a7c0b69SAlexander Usyskin unsigned int mode, unsigned long timeout) 13562382997STomas Winkler { 13662382997STomas Winkler struct mei_device *bus; 13762382997STomas Winkler struct mei_cl_cb *cb; 13862382997STomas Winkler size_t r_length; 13962382997STomas Winkler ssize_t rets; 140076802d0SAlexander Usyskin bool nonblock = !!(mode & MEI_CL_IO_RX_NONBLOCK); 14162382997STomas Winkler 14262382997STomas Winkler if (WARN_ON(!cl || !cl->dev)) 14362382997STomas Winkler return -ENODEV; 14462382997STomas Winkler 14562382997STomas Winkler bus = cl->dev; 14662382997STomas Winkler 14762382997STomas Winkler mutex_lock(&bus->device_lock); 14836edb140SAlexander Usyskin if (bus->dev_state != MEI_DEV_ENABLED && 14936edb140SAlexander Usyskin bus->dev_state != MEI_DEV_POWERING_DOWN) { 15015c13dfcSAlexander Usyskin rets = -ENODEV; 15115c13dfcSAlexander Usyskin goto out; 15215c13dfcSAlexander Usyskin } 15362382997STomas Winkler 15462382997STomas Winkler cb = mei_cl_read_cb(cl, NULL); 15562382997STomas Winkler if (cb) 15662382997STomas Winkler goto copy; 15762382997STomas Winkler 15862382997STomas Winkler rets = mei_cl_read_start(cl, length, NULL); 15962382997STomas Winkler if (rets && rets != -EBUSY) 16062382997STomas Winkler goto out; 16162382997STomas Winkler 162076802d0SAlexander Usyskin if (nonblock) { 163076802d0SAlexander Usyskin rets = -EAGAIN; 164076802d0SAlexander Usyskin goto out; 165076802d0SAlexander Usyskin } 166076802d0SAlexander Usyskin 16762382997STomas Winkler /* wait on event only if there is no other waiter */ 1681eb5bd4dSAlexander Usyskin /* synchronized under device mutex */ 1691eb5bd4dSAlexander Usyskin if (!waitqueue_active(&cl->rx_wait)) { 17062382997STomas Winkler 17162382997STomas Winkler mutex_unlock(&bus->device_lock); 17262382997STomas Winkler 1739a7c0b69SAlexander Usyskin if (timeout) { 1749a7c0b69SAlexander Usyskin rets = wait_event_interruptible_timeout 1759a7c0b69SAlexander Usyskin (cl->rx_wait, 176d1376f3dSAlexander Usyskin mei_cl_read_cb(cl, NULL) || 1779a7c0b69SAlexander Usyskin (!mei_cl_is_connected(cl)), 1789a7c0b69SAlexander Usyskin msecs_to_jiffies(timeout)); 1799a7c0b69SAlexander Usyskin if (rets == 0) 1809a7c0b69SAlexander Usyskin return -ETIME; 1819a7c0b69SAlexander Usyskin if (rets < 0) { 18262382997STomas Winkler if (signal_pending(current)) 18362382997STomas Winkler return -EINTR; 18462382997STomas Winkler return -ERESTARTSYS; 18562382997STomas Winkler } 1869a7c0b69SAlexander Usyskin } else { 1879a7c0b69SAlexander Usyskin if (wait_event_interruptible 1889a7c0b69SAlexander Usyskin (cl->rx_wait, 189d1376f3dSAlexander Usyskin mei_cl_read_cb(cl, NULL) || 1909a7c0b69SAlexander Usyskin (!mei_cl_is_connected(cl)))) { 1919a7c0b69SAlexander Usyskin if (signal_pending(current)) 1929a7c0b69SAlexander Usyskin return -EINTR; 1939a7c0b69SAlexander Usyskin return -ERESTARTSYS; 1949a7c0b69SAlexander Usyskin } 1959a7c0b69SAlexander Usyskin } 19662382997STomas Winkler 19762382997STomas Winkler mutex_lock(&bus->device_lock); 19862382997STomas Winkler 19962382997STomas Winkler if (!mei_cl_is_connected(cl)) { 2002d4d5481STomas Winkler rets = -ENODEV; 20162382997STomas Winkler goto out; 20262382997STomas Winkler } 20362382997STomas Winkler } 20462382997STomas Winkler 20562382997STomas Winkler cb = mei_cl_read_cb(cl, NULL); 20662382997STomas Winkler if (!cb) { 20762382997STomas Winkler rets = 0; 20862382997STomas Winkler goto out; 20962382997STomas Winkler } 21062382997STomas Winkler 21162382997STomas Winkler copy: 21262382997STomas Winkler if (cb->status) { 21362382997STomas Winkler rets = cb->status; 21462382997STomas Winkler goto free; 21562382997STomas Winkler } 21662382997STomas Winkler 217*5d5bc189STomas Winkler /* for the GSC type - copy the extended header to the buffer */ 218*5d5bc189STomas Winkler if (cb->ext_hdr && cb->ext_hdr->type == MEI_EXT_HDR_GSC) { 219*5d5bc189STomas Winkler r_length = min_t(size_t, length, cb->ext_hdr->length * sizeof(u32)); 220*5d5bc189STomas Winkler memcpy(buf, cb->ext_hdr, r_length); 221*5d5bc189STomas Winkler } else { 22262382997STomas Winkler r_length = min_t(size_t, length, cb->buf_idx); 22362382997STomas Winkler memcpy(buf, cb->buf.data, r_length); 224*5d5bc189STomas Winkler } 22562382997STomas Winkler rets = r_length; 226*5d5bc189STomas Winkler 22785261c1fSAlexander Usyskin if (vtag) 22885261c1fSAlexander Usyskin *vtag = cb->vtag; 22962382997STomas Winkler 23062382997STomas Winkler free: 231d1376f3dSAlexander Usyskin mei_cl_del_rd_completed(cl, cb); 23262382997STomas Winkler out: 23362382997STomas Winkler mutex_unlock(&bus->device_lock); 23462382997STomas Winkler 23562382997STomas Winkler return rets; 23662382997STomas Winkler } 23762382997STomas Winkler 23862382997STomas Winkler /** 23985261c1fSAlexander Usyskin * mei_cldev_send_vtag - me device send with vtag (write) 24085261c1fSAlexander Usyskin * 24185261c1fSAlexander Usyskin * @cldev: me client device 24285261c1fSAlexander Usyskin * @buf: buffer to send 24385261c1fSAlexander Usyskin * @length: buffer length 24485261c1fSAlexander Usyskin * @vtag: virtual tag 24585261c1fSAlexander Usyskin * 24685261c1fSAlexander Usyskin * Return: 24785261c1fSAlexander Usyskin * * written size in bytes 24885261c1fSAlexander Usyskin * * < 0 on error 24985261c1fSAlexander Usyskin */ 25085261c1fSAlexander Usyskin 2510912ef48SKrzysztof Kozlowski ssize_t mei_cldev_send_vtag(struct mei_cl_device *cldev, const u8 *buf, 2520912ef48SKrzysztof Kozlowski size_t length, u8 vtag) 25385261c1fSAlexander Usyskin { 25485261c1fSAlexander Usyskin struct mei_cl *cl = cldev->cl; 25585261c1fSAlexander Usyskin 25685261c1fSAlexander Usyskin return __mei_cl_send(cl, buf, length, vtag, MEI_CL_IO_TX_BLOCKING); 25785261c1fSAlexander Usyskin } 25885261c1fSAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_send_vtag); 25985261c1fSAlexander Usyskin 26085261c1fSAlexander Usyskin /** 26185261c1fSAlexander Usyskin * mei_cldev_recv_vtag - client receive with vtag (read) 26285261c1fSAlexander Usyskin * 26385261c1fSAlexander Usyskin * @cldev: me client device 26485261c1fSAlexander Usyskin * @buf: buffer to receive 26585261c1fSAlexander Usyskin * @length: buffer length 26685261c1fSAlexander Usyskin * @vtag: virtual tag 26785261c1fSAlexander Usyskin * 26885261c1fSAlexander Usyskin * Return: 26985261c1fSAlexander Usyskin * * read size in bytes 27085261c1fSAlexander Usyskin * * < 0 on error 27185261c1fSAlexander Usyskin */ 27285261c1fSAlexander Usyskin 27385261c1fSAlexander Usyskin ssize_t mei_cldev_recv_vtag(struct mei_cl_device *cldev, u8 *buf, size_t length, 27485261c1fSAlexander Usyskin u8 *vtag) 27585261c1fSAlexander Usyskin { 27685261c1fSAlexander Usyskin struct mei_cl *cl = cldev->cl; 27785261c1fSAlexander Usyskin 27885261c1fSAlexander Usyskin return __mei_cl_recv(cl, buf, length, vtag, 0, 0); 27985261c1fSAlexander Usyskin } 28085261c1fSAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_recv_vtag); 28185261c1fSAlexander Usyskin 28285261c1fSAlexander Usyskin /** 28385261c1fSAlexander Usyskin * mei_cldev_recv_nonblock_vtag - non block client receive with vtag (read) 28485261c1fSAlexander Usyskin * 28585261c1fSAlexander Usyskin * @cldev: me client device 28685261c1fSAlexander Usyskin * @buf: buffer to receive 28785261c1fSAlexander Usyskin * @length: buffer length 28885261c1fSAlexander Usyskin * @vtag: virtual tag 28985261c1fSAlexander Usyskin * 29085261c1fSAlexander Usyskin * Return: 29185261c1fSAlexander Usyskin * * read size in bytes 29285261c1fSAlexander Usyskin * * -EAGAIN if function will block. 29385261c1fSAlexander Usyskin * * < 0 on other error 29485261c1fSAlexander Usyskin */ 29585261c1fSAlexander Usyskin ssize_t mei_cldev_recv_nonblock_vtag(struct mei_cl_device *cldev, u8 *buf, 29685261c1fSAlexander Usyskin size_t length, u8 *vtag) 29785261c1fSAlexander Usyskin { 29885261c1fSAlexander Usyskin struct mei_cl *cl = cldev->cl; 29985261c1fSAlexander Usyskin 30085261c1fSAlexander Usyskin return __mei_cl_recv(cl, buf, length, vtag, MEI_CL_IO_RX_NONBLOCK, 0); 30185261c1fSAlexander Usyskin } 30285261c1fSAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_recv_nonblock_vtag); 30385261c1fSAlexander Usyskin 30485261c1fSAlexander Usyskin /** 305d49dc5e7STomas Winkler * mei_cldev_send - me device send (write) 30662382997STomas Winkler * 30762382997STomas Winkler * @cldev: me client device 30862382997STomas Winkler * @buf: buffer to send 30962382997STomas Winkler * @length: buffer length 31062382997STomas Winkler * 31185261c1fSAlexander Usyskin * Return: 31285261c1fSAlexander Usyskin * * written size in bytes 31385261c1fSAlexander Usyskin * * < 0 on error 31462382997STomas Winkler */ 3150912ef48SKrzysztof Kozlowski ssize_t mei_cldev_send(struct mei_cl_device *cldev, const u8 *buf, size_t length) 31662382997STomas Winkler { 31785261c1fSAlexander Usyskin return mei_cldev_send_vtag(cldev, buf, length, 0); 31862382997STomas Winkler } 319d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_send); 32062382997STomas Winkler 32162382997STomas Winkler /** 32285261c1fSAlexander Usyskin * mei_cldev_recv - client receive (read) 32385261c1fSAlexander Usyskin * 32485261c1fSAlexander Usyskin * @cldev: me client device 32585261c1fSAlexander Usyskin * @buf: buffer to receive 32685261c1fSAlexander Usyskin * @length: buffer length 32785261c1fSAlexander Usyskin * 32885261c1fSAlexander Usyskin * Return: read size in bytes of < 0 on error 32985261c1fSAlexander Usyskin */ 33085261c1fSAlexander Usyskin ssize_t mei_cldev_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) 33185261c1fSAlexander Usyskin { 33285261c1fSAlexander Usyskin return mei_cldev_recv_vtag(cldev, buf, length, NULL); 33385261c1fSAlexander Usyskin } 33485261c1fSAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_recv); 33585261c1fSAlexander Usyskin 33685261c1fSAlexander Usyskin /** 337076802d0SAlexander Usyskin * mei_cldev_recv_nonblock - non block client receive (read) 338076802d0SAlexander Usyskin * 339076802d0SAlexander Usyskin * @cldev: me client device 340076802d0SAlexander Usyskin * @buf: buffer to receive 341076802d0SAlexander Usyskin * @length: buffer length 342076802d0SAlexander Usyskin * 343076802d0SAlexander Usyskin * Return: read size in bytes of < 0 on error 344076802d0SAlexander Usyskin * -EAGAIN if function will block. 345076802d0SAlexander Usyskin */ 346076802d0SAlexander Usyskin ssize_t mei_cldev_recv_nonblock(struct mei_cl_device *cldev, u8 *buf, 347076802d0SAlexander Usyskin size_t length) 348076802d0SAlexander Usyskin { 34985261c1fSAlexander Usyskin return mei_cldev_recv_nonblock_vtag(cldev, buf, length, NULL); 350076802d0SAlexander Usyskin } 351076802d0SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_recv_nonblock); 352076802d0SAlexander Usyskin 353076802d0SAlexander Usyskin /** 3547c7a6077SAlexander Usyskin * mei_cl_bus_rx_work - dispatch rx event for a bus device 35562382997STomas Winkler * 35662382997STomas Winkler * @work: work 35762382997STomas Winkler */ 3587c7a6077SAlexander Usyskin static void mei_cl_bus_rx_work(struct work_struct *work) 35962382997STomas Winkler { 36062382997STomas Winkler struct mei_cl_device *cldev; 361bc46b45aSAlexander Usyskin struct mei_device *bus; 36262382997STomas Winkler 3637c7a6077SAlexander Usyskin cldev = container_of(work, struct mei_cl_device, rx_work); 36462382997STomas Winkler 365bc46b45aSAlexander Usyskin bus = cldev->bus; 366bc46b45aSAlexander Usyskin 3677c7a6077SAlexander Usyskin if (cldev->rx_cb) 3687c7a6077SAlexander Usyskin cldev->rx_cb(cldev); 36962382997STomas Winkler 370bc46b45aSAlexander Usyskin mutex_lock(&bus->device_lock); 371c7a6252bSAlexander Usyskin if (mei_cl_is_connected(cldev->cl)) 3723030dc05STomas Winkler mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL); 373bc46b45aSAlexander Usyskin mutex_unlock(&bus->device_lock); 374bc46b45aSAlexander Usyskin } 3757c7a6077SAlexander Usyskin 3767c7a6077SAlexander Usyskin /** 3777c7a6077SAlexander Usyskin * mei_cl_bus_notif_work - dispatch FW notif event for a bus device 3787c7a6077SAlexander Usyskin * 3797c7a6077SAlexander Usyskin * @work: work 3807c7a6077SAlexander Usyskin */ 3817c7a6077SAlexander Usyskin static void mei_cl_bus_notif_work(struct work_struct *work) 3827c7a6077SAlexander Usyskin { 3837c7a6077SAlexander Usyskin struct mei_cl_device *cldev; 3847c7a6077SAlexander Usyskin 3857c7a6077SAlexander Usyskin cldev = container_of(work, struct mei_cl_device, notif_work); 3867c7a6077SAlexander Usyskin 3877c7a6077SAlexander Usyskin if (cldev->notif_cb) 3887c7a6077SAlexander Usyskin cldev->notif_cb(cldev); 38962382997STomas Winkler } 39062382997STomas Winkler 39162382997STomas Winkler /** 392bb2ef9c3SAlexander Usyskin * mei_cl_bus_notify_event - schedule notify cb on bus client 393bb2ef9c3SAlexander Usyskin * 394bb2ef9c3SAlexander Usyskin * @cl: host client 395850f8940STomas Winkler * 396850f8940STomas Winkler * Return: true if event was scheduled 397850f8940STomas Winkler * false if the client is not waiting for event 398bb2ef9c3SAlexander Usyskin */ 399850f8940STomas Winkler bool mei_cl_bus_notify_event(struct mei_cl *cl) 400bb2ef9c3SAlexander Usyskin { 401bb2ef9c3SAlexander Usyskin struct mei_cl_device *cldev = cl->cldev; 402bb2ef9c3SAlexander Usyskin 4037c7a6077SAlexander Usyskin if (!cldev || !cldev->notif_cb) 404850f8940STomas Winkler return false; 405bb2ef9c3SAlexander Usyskin 406bb2ef9c3SAlexander Usyskin if (!cl->notify_ev) 407850f8940STomas Winkler return false; 408bb2ef9c3SAlexander Usyskin 4097c7a6077SAlexander Usyskin schedule_work(&cldev->notif_work); 410bb2ef9c3SAlexander Usyskin 411bb2ef9c3SAlexander Usyskin cl->notify_ev = false; 412850f8940STomas Winkler 413850f8940STomas Winkler return true; 414bb2ef9c3SAlexander Usyskin } 415bb2ef9c3SAlexander Usyskin 416bb2ef9c3SAlexander Usyskin /** 417a1f9ae2bSTomas Winkler * mei_cl_bus_rx_event - schedule rx event 41862382997STomas Winkler * 41962382997STomas Winkler * @cl: host client 420a1f9ae2bSTomas Winkler * 421a1f9ae2bSTomas Winkler * Return: true if event was scheduled 422a1f9ae2bSTomas Winkler * false if the client is not waiting for event 42362382997STomas Winkler */ 424a1f9ae2bSTomas Winkler bool mei_cl_bus_rx_event(struct mei_cl *cl) 42562382997STomas Winkler { 42662382997STomas Winkler struct mei_cl_device *cldev = cl->cldev; 42762382997STomas Winkler 4287c7a6077SAlexander Usyskin if (!cldev || !cldev->rx_cb) 429a1f9ae2bSTomas Winkler return false; 43062382997STomas Winkler 4317c7a6077SAlexander Usyskin schedule_work(&cldev->rx_work); 432a1f9ae2bSTomas Winkler 433a1f9ae2bSTomas Winkler return true; 43462382997STomas Winkler } 43562382997STomas Winkler 43662382997STomas Winkler /** 4377c7a6077SAlexander Usyskin * mei_cldev_register_rx_cb - register Rx event callback 43862382997STomas Winkler * 43962382997STomas Winkler * @cldev: me client devices 4407c7a6077SAlexander Usyskin * @rx_cb: callback function 44162382997STomas Winkler * 44262382997STomas Winkler * Return: 0 on success 44362382997STomas Winkler * -EALREADY if an callback is already registered 44462382997STomas Winkler * <0 on other errors 44562382997STomas Winkler */ 4467c7a6077SAlexander Usyskin int mei_cldev_register_rx_cb(struct mei_cl_device *cldev, mei_cldev_cb_t rx_cb) 44762382997STomas Winkler { 448bc46b45aSAlexander Usyskin struct mei_device *bus = cldev->bus; 44948168f45STomas Winkler int ret; 45048168f45STomas Winkler 4517c7a6077SAlexander Usyskin if (!rx_cb) 4527c7a6077SAlexander Usyskin return -EINVAL; 4537c7a6077SAlexander Usyskin if (cldev->rx_cb) 45462382997STomas Winkler return -EALREADY; 45562382997STomas Winkler 4567c7a6077SAlexander Usyskin cldev->rx_cb = rx_cb; 4577c7a6077SAlexander Usyskin INIT_WORK(&cldev->rx_work, mei_cl_bus_rx_work); 45862382997STomas Winkler 459bc46b45aSAlexander Usyskin mutex_lock(&bus->device_lock); 460c7a6252bSAlexander Usyskin if (mei_cl_is_connected(cldev->cl)) 4613030dc05STomas Winkler ret = mei_cl_read_start(cldev->cl, mei_cl_mtu(cldev->cl), NULL); 462c7a6252bSAlexander Usyskin else 463c7a6252bSAlexander Usyskin ret = -ENODEV; 464bc46b45aSAlexander Usyskin mutex_unlock(&bus->device_lock); 465c2192bbcSAlexander Usyskin if (ret && ret != -EBUSY) { 466c2192bbcSAlexander Usyskin cancel_work_sync(&cldev->rx_work); 467c2192bbcSAlexander Usyskin cldev->rx_cb = NULL; 46848168f45STomas Winkler return ret; 469c2192bbcSAlexander Usyskin } 47062382997STomas Winkler 47162382997STomas Winkler return 0; 47262382997STomas Winkler } 4737c7a6077SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_register_rx_cb); 4747c7a6077SAlexander Usyskin 4757c7a6077SAlexander Usyskin /** 4767c7a6077SAlexander Usyskin * mei_cldev_register_notif_cb - register FW notification event callback 4777c7a6077SAlexander Usyskin * 4787c7a6077SAlexander Usyskin * @cldev: me client devices 4797c7a6077SAlexander Usyskin * @notif_cb: callback function 4807c7a6077SAlexander Usyskin * 4817c7a6077SAlexander Usyskin * Return: 0 on success 4827c7a6077SAlexander Usyskin * -EALREADY if an callback is already registered 4837c7a6077SAlexander Usyskin * <0 on other errors 4847c7a6077SAlexander Usyskin */ 4857c7a6077SAlexander Usyskin int mei_cldev_register_notif_cb(struct mei_cl_device *cldev, 4867c7a6077SAlexander Usyskin mei_cldev_cb_t notif_cb) 4877c7a6077SAlexander Usyskin { 4887c7a6077SAlexander Usyskin struct mei_device *bus = cldev->bus; 4897c7a6077SAlexander Usyskin int ret; 4907c7a6077SAlexander Usyskin 4917c7a6077SAlexander Usyskin if (!notif_cb) 4927c7a6077SAlexander Usyskin return -EINVAL; 4937c7a6077SAlexander Usyskin 4947c7a6077SAlexander Usyskin if (cldev->notif_cb) 4957c7a6077SAlexander Usyskin return -EALREADY; 4967c7a6077SAlexander Usyskin 4977c7a6077SAlexander Usyskin cldev->notif_cb = notif_cb; 4987c7a6077SAlexander Usyskin INIT_WORK(&cldev->notif_work, mei_cl_bus_notif_work); 4997c7a6077SAlexander Usyskin 5007c7a6077SAlexander Usyskin mutex_lock(&bus->device_lock); 5017c7a6077SAlexander Usyskin ret = mei_cl_notify_request(cldev->cl, NULL, 1); 5027c7a6077SAlexander Usyskin mutex_unlock(&bus->device_lock); 503c2192bbcSAlexander Usyskin if (ret) { 504c2192bbcSAlexander Usyskin cancel_work_sync(&cldev->notif_work); 505c2192bbcSAlexander Usyskin cldev->notif_cb = NULL; 5067c7a6077SAlexander Usyskin return ret; 507c2192bbcSAlexander Usyskin } 5087c7a6077SAlexander Usyskin 5097c7a6077SAlexander Usyskin return 0; 5107c7a6077SAlexander Usyskin } 5117c7a6077SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_register_notif_cb); 51262382997STomas Winkler 51362382997STomas Winkler /** 514d49dc5e7STomas Winkler * mei_cldev_get_drvdata - driver data getter 51562382997STomas Winkler * 51662382997STomas Winkler * @cldev: mei client device 51762382997STomas Winkler * 51862382997STomas Winkler * Return: driver private data 51962382997STomas Winkler */ 520d49dc5e7STomas Winkler void *mei_cldev_get_drvdata(const struct mei_cl_device *cldev) 52162382997STomas Winkler { 52262382997STomas Winkler return dev_get_drvdata(&cldev->dev); 52362382997STomas Winkler } 524d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_get_drvdata); 52562382997STomas Winkler 52662382997STomas Winkler /** 527d49dc5e7STomas Winkler * mei_cldev_set_drvdata - driver data setter 52862382997STomas Winkler * 52962382997STomas Winkler * @cldev: mei client device 53062382997STomas Winkler * @data: data to store 53162382997STomas Winkler */ 532d49dc5e7STomas Winkler void mei_cldev_set_drvdata(struct mei_cl_device *cldev, void *data) 53362382997STomas Winkler { 53462382997STomas Winkler dev_set_drvdata(&cldev->dev, data); 53562382997STomas Winkler } 536d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_set_drvdata); 53762382997STomas Winkler 53862382997STomas Winkler /** 539baeacd03STomas Winkler * mei_cldev_uuid - return uuid of the underlying me client 540baeacd03STomas Winkler * 541baeacd03STomas Winkler * @cldev: mei client device 542baeacd03STomas Winkler * 543baeacd03STomas Winkler * Return: me client uuid 544baeacd03STomas Winkler */ 545baeacd03STomas Winkler const uuid_le *mei_cldev_uuid(const struct mei_cl_device *cldev) 546baeacd03STomas Winkler { 547baeacd03STomas Winkler return mei_me_cl_uuid(cldev->me_cl); 548baeacd03STomas Winkler } 549baeacd03STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_uuid); 550baeacd03STomas Winkler 551baeacd03STomas Winkler /** 552baeacd03STomas Winkler * mei_cldev_ver - return protocol version of the underlying me client 553baeacd03STomas Winkler * 554baeacd03STomas Winkler * @cldev: mei client device 555baeacd03STomas Winkler * 556baeacd03STomas Winkler * Return: me client protocol version 557baeacd03STomas Winkler */ 558baeacd03STomas Winkler u8 mei_cldev_ver(const struct mei_cl_device *cldev) 559baeacd03STomas Winkler { 560baeacd03STomas Winkler return mei_me_cl_ver(cldev->me_cl); 561baeacd03STomas Winkler } 562baeacd03STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_ver); 563baeacd03STomas Winkler 564baeacd03STomas Winkler /** 56501a14edeSTomas Winkler * mei_cldev_enabled - check whether the device is enabled 56601a14edeSTomas Winkler * 56701a14edeSTomas Winkler * @cldev: mei client device 56801a14edeSTomas Winkler * 56901a14edeSTomas Winkler * Return: true if me client is initialized and connected 57001a14edeSTomas Winkler */ 5710912ef48SKrzysztof Kozlowski bool mei_cldev_enabled(const struct mei_cl_device *cldev) 57201a14edeSTomas Winkler { 573c110cdb1SAlexander Usyskin return mei_cl_is_connected(cldev->cl); 57401a14edeSTomas Winkler } 57501a14edeSTomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_enabled); 57601a14edeSTomas Winkler 57701a14edeSTomas Winkler /** 578257355a4STomas Winkler * mei_cl_bus_module_get - acquire module of the underlying 579257355a4STomas Winkler * hw driver. 580257355a4STomas Winkler * 581257355a4STomas Winkler * @cldev: mei client device 582257355a4STomas Winkler * 583257355a4STomas Winkler * Return: true on success; false if the module was removed. 584257355a4STomas Winkler */ 585257355a4STomas Winkler static bool mei_cl_bus_module_get(struct mei_cl_device *cldev) 586257355a4STomas Winkler { 587257355a4STomas Winkler return try_module_get(cldev->bus->dev->driver->owner); 588257355a4STomas Winkler } 589257355a4STomas Winkler 590257355a4STomas Winkler /** 591257355a4STomas Winkler * mei_cl_bus_module_put - release the underlying hw module. 592257355a4STomas Winkler * 593257355a4STomas Winkler * @cldev: mei client device 594257355a4STomas Winkler */ 595257355a4STomas Winkler static void mei_cl_bus_module_put(struct mei_cl_device *cldev) 596257355a4STomas Winkler { 597257355a4STomas Winkler module_put(cldev->bus->dev->driver->owner); 598257355a4STomas Winkler } 599257355a4STomas Winkler 600257355a4STomas Winkler /** 601e5617d2bSAlexander Usyskin * mei_cl_bus_vtag - get bus vtag entry wrapper 602e5617d2bSAlexander Usyskin * The tag for bus client is always first. 603e5617d2bSAlexander Usyskin * 604e5617d2bSAlexander Usyskin * @cl: host client 605e5617d2bSAlexander Usyskin * 606e5617d2bSAlexander Usyskin * Return: bus vtag or NULL 607e5617d2bSAlexander Usyskin */ 608e5617d2bSAlexander Usyskin static inline struct mei_cl_vtag *mei_cl_bus_vtag(struct mei_cl *cl) 609e5617d2bSAlexander Usyskin { 610e5617d2bSAlexander Usyskin return list_first_entry_or_null(&cl->vtag_map, 611e5617d2bSAlexander Usyskin struct mei_cl_vtag, list); 612e5617d2bSAlexander Usyskin } 613e5617d2bSAlexander Usyskin 614e5617d2bSAlexander Usyskin /** 615e5617d2bSAlexander Usyskin * mei_cl_bus_vtag_alloc - add bus client entry to vtag map 616e5617d2bSAlexander Usyskin * 617e5617d2bSAlexander Usyskin * @cldev: me client device 618e5617d2bSAlexander Usyskin * 619e5617d2bSAlexander Usyskin * Return: 620e5617d2bSAlexander Usyskin * * 0 on success 621e5617d2bSAlexander Usyskin * * -ENOMEM if memory allocation failed 622e5617d2bSAlexander Usyskin */ 623e5617d2bSAlexander Usyskin static int mei_cl_bus_vtag_alloc(struct mei_cl_device *cldev) 624e5617d2bSAlexander Usyskin { 625e5617d2bSAlexander Usyskin struct mei_cl *cl = cldev->cl; 626e5617d2bSAlexander Usyskin struct mei_cl_vtag *cl_vtag; 627e5617d2bSAlexander Usyskin 628e5617d2bSAlexander Usyskin /* 629e5617d2bSAlexander Usyskin * Bail out if the client does not supports vtags 630e5617d2bSAlexander Usyskin * or has already allocated one 631e5617d2bSAlexander Usyskin */ 632e5617d2bSAlexander Usyskin if (mei_cl_vt_support_check(cl) || mei_cl_bus_vtag(cl)) 633e5617d2bSAlexander Usyskin return 0; 634e5617d2bSAlexander Usyskin 635e5617d2bSAlexander Usyskin cl_vtag = mei_cl_vtag_alloc(NULL, 0); 636e5617d2bSAlexander Usyskin if (IS_ERR(cl_vtag)) 637e5617d2bSAlexander Usyskin return -ENOMEM; 638e5617d2bSAlexander Usyskin 639e5617d2bSAlexander Usyskin list_add_tail(&cl_vtag->list, &cl->vtag_map); 640e5617d2bSAlexander Usyskin 641e5617d2bSAlexander Usyskin return 0; 642e5617d2bSAlexander Usyskin } 643e5617d2bSAlexander Usyskin 644e5617d2bSAlexander Usyskin /** 645e5617d2bSAlexander Usyskin * mei_cl_bus_vtag_free - remove the bus entry from vtag map 646e5617d2bSAlexander Usyskin * 647e5617d2bSAlexander Usyskin * @cldev: me client device 648e5617d2bSAlexander Usyskin */ 649e5617d2bSAlexander Usyskin static void mei_cl_bus_vtag_free(struct mei_cl_device *cldev) 650e5617d2bSAlexander Usyskin { 651e5617d2bSAlexander Usyskin struct mei_cl *cl = cldev->cl; 652e5617d2bSAlexander Usyskin struct mei_cl_vtag *cl_vtag; 653e5617d2bSAlexander Usyskin 654e5617d2bSAlexander Usyskin cl_vtag = mei_cl_bus_vtag(cl); 655e5617d2bSAlexander Usyskin if (!cl_vtag) 656e5617d2bSAlexander Usyskin return; 657e5617d2bSAlexander Usyskin 658e5617d2bSAlexander Usyskin list_del(&cl_vtag->list); 659e5617d2bSAlexander Usyskin kfree(cl_vtag); 660e5617d2bSAlexander Usyskin } 661e5617d2bSAlexander Usyskin 6622cca3465SAlexander Usyskin void *mei_cldev_dma_map(struct mei_cl_device *cldev, u8 buffer_id, size_t size) 6632cca3465SAlexander Usyskin { 6642cca3465SAlexander Usyskin struct mei_device *bus; 6652cca3465SAlexander Usyskin struct mei_cl *cl; 6662cca3465SAlexander Usyskin int ret; 6672cca3465SAlexander Usyskin 6682cca3465SAlexander Usyskin if (!cldev || !buffer_id || !size) 6692cca3465SAlexander Usyskin return ERR_PTR(-EINVAL); 6702cca3465SAlexander Usyskin 6712cca3465SAlexander Usyskin if (!IS_ALIGNED(size, MEI_FW_PAGE_SIZE)) { 6722cca3465SAlexander Usyskin dev_err(&cldev->dev, "Map size should be aligned to %lu\n", 6732cca3465SAlexander Usyskin MEI_FW_PAGE_SIZE); 6742cca3465SAlexander Usyskin return ERR_PTR(-EINVAL); 6752cca3465SAlexander Usyskin } 6762cca3465SAlexander Usyskin 6772cca3465SAlexander Usyskin cl = cldev->cl; 6782cca3465SAlexander Usyskin bus = cldev->bus; 6792cca3465SAlexander Usyskin 6802cca3465SAlexander Usyskin mutex_lock(&bus->device_lock); 6812cca3465SAlexander Usyskin if (cl->state == MEI_FILE_UNINITIALIZED) { 6822cca3465SAlexander Usyskin ret = mei_cl_link(cl); 6832cca3465SAlexander Usyskin if (ret) 6842cca3465SAlexander Usyskin goto out; 6852cca3465SAlexander Usyskin /* update pointers */ 6862cca3465SAlexander Usyskin cl->cldev = cldev; 6872cca3465SAlexander Usyskin } 6882cca3465SAlexander Usyskin 6892cca3465SAlexander Usyskin ret = mei_cl_dma_alloc_and_map(cl, NULL, buffer_id, size); 6902cca3465SAlexander Usyskin out: 6912cca3465SAlexander Usyskin mutex_unlock(&bus->device_lock); 6922cca3465SAlexander Usyskin if (ret) 6932cca3465SAlexander Usyskin return ERR_PTR(ret); 6942cca3465SAlexander Usyskin return cl->dma.vaddr; 6952cca3465SAlexander Usyskin } 6962cca3465SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_dma_map); 6972cca3465SAlexander Usyskin 6982cca3465SAlexander Usyskin int mei_cldev_dma_unmap(struct mei_cl_device *cldev) 6992cca3465SAlexander Usyskin { 7002cca3465SAlexander Usyskin struct mei_device *bus; 7012cca3465SAlexander Usyskin struct mei_cl *cl; 7022cca3465SAlexander Usyskin int ret; 7032cca3465SAlexander Usyskin 7042cca3465SAlexander Usyskin if (!cldev) 7052cca3465SAlexander Usyskin return -EINVAL; 7062cca3465SAlexander Usyskin 7072cca3465SAlexander Usyskin cl = cldev->cl; 7082cca3465SAlexander Usyskin bus = cldev->bus; 7092cca3465SAlexander Usyskin 7102cca3465SAlexander Usyskin mutex_lock(&bus->device_lock); 7112cca3465SAlexander Usyskin ret = mei_cl_dma_unmap(cl, NULL); 7122cca3465SAlexander Usyskin 7132cca3465SAlexander Usyskin mei_cl_flush_queues(cl, NULL); 7142cca3465SAlexander Usyskin mei_cl_unlink(cl); 7152cca3465SAlexander Usyskin mutex_unlock(&bus->device_lock); 7162cca3465SAlexander Usyskin return ret; 7172cca3465SAlexander Usyskin } 7182cca3465SAlexander Usyskin EXPORT_SYMBOL_GPL(mei_cldev_dma_unmap); 7192cca3465SAlexander Usyskin 720e5617d2bSAlexander Usyskin /** 7215026c9cbSAlexander Usyskin * mei_cldev_enable - enable me client device 72262382997STomas Winkler * create connection with me client 72362382997STomas Winkler * 72462382997STomas Winkler * @cldev: me client device 72562382997STomas Winkler * 72662382997STomas Winkler * Return: 0 on success and < 0 on error 72762382997STomas Winkler */ 728d49dc5e7STomas Winkler int mei_cldev_enable(struct mei_cl_device *cldev) 72962382997STomas Winkler { 7306009595aSTomas Winkler struct mei_device *bus = cldev->bus; 7316009595aSTomas Winkler struct mei_cl *cl; 7326009595aSTomas Winkler int ret; 73362382997STomas Winkler 7346009595aSTomas Winkler cl = cldev->cl; 73562382997STomas Winkler 7366009595aSTomas Winkler mutex_lock(&bus->device_lock); 73734f1166aSTomas Winkler if (cl->state == MEI_FILE_UNINITIALIZED) { 738c110cdb1SAlexander Usyskin ret = mei_cl_link(cl); 739c110cdb1SAlexander Usyskin if (ret) 74034f1166aSTomas Winkler goto out; 7416009595aSTomas Winkler /* update pointers */ 7426009595aSTomas Winkler cl->cldev = cldev; 7436009595aSTomas Winkler } 74462382997STomas Winkler 74562382997STomas Winkler if (mei_cl_is_connected(cl)) { 7466009595aSTomas Winkler ret = 0; 7476009595aSTomas Winkler goto out; 74862382997STomas Winkler } 74962382997STomas Winkler 7506009595aSTomas Winkler if (!mei_me_cl_is_active(cldev->me_cl)) { 7516009595aSTomas Winkler dev_err(&cldev->dev, "me client is not active\n"); 7526009595aSTomas Winkler ret = -ENOTTY; 7536009595aSTomas Winkler goto out; 75462382997STomas Winkler } 75562382997STomas Winkler 756e5617d2bSAlexander Usyskin ret = mei_cl_bus_vtag_alloc(cldev); 757e5617d2bSAlexander Usyskin if (ret) 758e5617d2bSAlexander Usyskin goto out; 759e5617d2bSAlexander Usyskin 7606009595aSTomas Winkler ret = mei_cl_connect(cl, cldev->me_cl, NULL); 761e5617d2bSAlexander Usyskin if (ret < 0) { 7626009595aSTomas Winkler dev_err(&cldev->dev, "cannot connect\n"); 763e5617d2bSAlexander Usyskin mei_cl_bus_vtag_free(cldev); 764e5617d2bSAlexander Usyskin } 7656009595aSTomas Winkler 7666009595aSTomas Winkler out: 76762382997STomas Winkler mutex_unlock(&bus->device_lock); 76862382997STomas Winkler 7696009595aSTomas Winkler return ret; 77062382997STomas Winkler } 771d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_enable); 77262382997STomas Winkler 77362382997STomas Winkler /** 77457080e88SAlexander Usyskin * mei_cldev_unregister_callbacks - internal wrapper for unregistering 77557080e88SAlexander Usyskin * callbacks. 77657080e88SAlexander Usyskin * 77757080e88SAlexander Usyskin * @cldev: client device 77857080e88SAlexander Usyskin */ 77957080e88SAlexander Usyskin static void mei_cldev_unregister_callbacks(struct mei_cl_device *cldev) 78057080e88SAlexander Usyskin { 78157080e88SAlexander Usyskin if (cldev->rx_cb) { 78257080e88SAlexander Usyskin cancel_work_sync(&cldev->rx_work); 78357080e88SAlexander Usyskin cldev->rx_cb = NULL; 78457080e88SAlexander Usyskin } 78557080e88SAlexander Usyskin 78657080e88SAlexander Usyskin if (cldev->notif_cb) { 78757080e88SAlexander Usyskin cancel_work_sync(&cldev->notif_work); 78857080e88SAlexander Usyskin cldev->notif_cb = NULL; 78957080e88SAlexander Usyskin } 79057080e88SAlexander Usyskin } 79157080e88SAlexander Usyskin 79257080e88SAlexander Usyskin /** 793d49dc5e7STomas Winkler * mei_cldev_disable - disable me client device 79462382997STomas Winkler * disconnect form the me client 79562382997STomas Winkler * 79662382997STomas Winkler * @cldev: me client device 79762382997STomas Winkler * 79862382997STomas Winkler * Return: 0 on success and < 0 on error 79962382997STomas Winkler */ 800d49dc5e7STomas Winkler int mei_cldev_disable(struct mei_cl_device *cldev) 80162382997STomas Winkler { 80262382997STomas Winkler struct mei_device *bus; 8036009595aSTomas Winkler struct mei_cl *cl; 8046009595aSTomas Winkler int err; 80562382997STomas Winkler 806c110cdb1SAlexander Usyskin if (!cldev) 80762382997STomas Winkler return -ENODEV; 80862382997STomas Winkler 8096009595aSTomas Winkler cl = cldev->cl; 8106009595aSTomas Winkler 8116009595aSTomas Winkler bus = cldev->bus; 81262382997STomas Winkler 81357080e88SAlexander Usyskin mei_cldev_unregister_callbacks(cldev); 81457080e88SAlexander Usyskin 81562382997STomas Winkler mutex_lock(&bus->device_lock); 81662382997STomas Winkler 817e5617d2bSAlexander Usyskin mei_cl_bus_vtag_free(cldev); 818e5617d2bSAlexander Usyskin 81962382997STomas Winkler if (!mei_cl_is_connected(cl)) { 8208d52af67STomas Winkler dev_dbg(bus->dev, "Already disconnected\n"); 8218d52af67STomas Winkler err = 0; 8228d52af67STomas Winkler goto out; 8238d52af67STomas Winkler } 8248d52af67STomas Winkler 82562382997STomas Winkler err = mei_cl_disconnect(cl); 8266009595aSTomas Winkler if (err < 0) 8278d52af67STomas Winkler dev_err(bus->dev, "Could not disconnect from the ME client\n"); 82862382997STomas Winkler 82969bf5313STomas Winkler out: 8302cca3465SAlexander Usyskin /* Flush queues and remove any pending read unless we have mapped DMA */ 8312cca3465SAlexander Usyskin if (!cl->dma_mapped) { 8326009595aSTomas Winkler mei_cl_flush_queues(cl, NULL); 8336009595aSTomas Winkler mei_cl_unlink(cl); 8342cca3465SAlexander Usyskin } 8356009595aSTomas Winkler 83662382997STomas Winkler mutex_unlock(&bus->device_lock); 83762382997STomas Winkler return err; 83862382997STomas Winkler } 839d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_disable); 84062382997STomas Winkler 841688a9cceSTomas Winkler /** 842688a9cceSTomas Winkler * mei_cl_device_find - find matching entry in the driver id table 843688a9cceSTomas Winkler * 844688a9cceSTomas Winkler * @cldev: me client device 845688a9cceSTomas Winkler * @cldrv: me client driver 846688a9cceSTomas Winkler * 847688a9cceSTomas Winkler * Return: id on success; NULL if no id is matching 848688a9cceSTomas Winkler */ 849688a9cceSTomas Winkler static const 8500912ef48SKrzysztof Kozlowski struct mei_cl_device_id *mei_cl_device_find(const struct mei_cl_device *cldev, 8510912ef48SKrzysztof Kozlowski const struct mei_cl_driver *cldrv) 852e5354107SSamuel Ortiz { 853e5354107SSamuel Ortiz const struct mei_cl_device_id *id; 854c93b76b3STomas Winkler const uuid_le *uuid; 855b26864caSTomas Winkler u8 version; 856b26864caSTomas Winkler bool match; 857e5354107SSamuel Ortiz 858b37719c3STomas Winkler uuid = mei_me_cl_uuid(cldev->me_cl); 859b26864caSTomas Winkler version = mei_me_cl_ver(cldev->me_cl); 860e5354107SSamuel Ortiz 861b37719c3STomas Winkler id = cldrv->id_table; 862b144ce2dSGreg Kroah-Hartman while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) { 863b144ce2dSGreg Kroah-Hartman if (!uuid_le_cmp(*uuid, id->uuid)) { 864b26864caSTomas Winkler match = true; 865688a9cceSTomas Winkler 866b26864caSTomas Winkler if (cldev->name[0]) 867b26864caSTomas Winkler if (strncmp(cldev->name, id->name, 868b26864caSTomas Winkler sizeof(id->name))) 869b26864caSTomas Winkler match = false; 870688a9cceSTomas Winkler 871b26864caSTomas Winkler if (id->version != MEI_CL_VERSION_ANY) 872b26864caSTomas Winkler if (id->version != version) 873b26864caSTomas Winkler match = false; 874b26864caSTomas Winkler if (match) 875688a9cceSTomas Winkler return id; 876c93b76b3STomas Winkler } 877e5354107SSamuel Ortiz 878e5354107SSamuel Ortiz id++; 879e5354107SSamuel Ortiz } 880e5354107SSamuel Ortiz 881688a9cceSTomas Winkler return NULL; 882688a9cceSTomas Winkler } 883688a9cceSTomas Winkler 884688a9cceSTomas Winkler /** 885688a9cceSTomas Winkler * mei_cl_device_match - device match function 886688a9cceSTomas Winkler * 887688a9cceSTomas Winkler * @dev: device 888688a9cceSTomas Winkler * @drv: driver 889688a9cceSTomas Winkler * 890688a9cceSTomas Winkler * Return: 1 if matching device was found 0 otherwise 891688a9cceSTomas Winkler */ 892688a9cceSTomas Winkler static int mei_cl_device_match(struct device *dev, struct device_driver *drv) 893688a9cceSTomas Winkler { 8940912ef48SKrzysztof Kozlowski const struct mei_cl_device *cldev = to_mei_cl_device(dev); 8950912ef48SKrzysztof Kozlowski const struct mei_cl_driver *cldrv = to_mei_cl_driver(drv); 896688a9cceSTomas Winkler const struct mei_cl_device_id *found_id; 897688a9cceSTomas Winkler 898688a9cceSTomas Winkler if (!cldev) 899688a9cceSTomas Winkler return 0; 900688a9cceSTomas Winkler 90171ce7891STomas Winkler if (!cldev->do_match) 90271ce7891STomas Winkler return 0; 90371ce7891STomas Winkler 904688a9cceSTomas Winkler if (!cldrv || !cldrv->id_table) 905688a9cceSTomas Winkler return 0; 906688a9cceSTomas Winkler 907688a9cceSTomas Winkler found_id = mei_cl_device_find(cldev, cldrv); 908688a9cceSTomas Winkler if (found_id) 909688a9cceSTomas Winkler return 1; 910688a9cceSTomas Winkler 911e5354107SSamuel Ortiz return 0; 912e5354107SSamuel Ortiz } 913e5354107SSamuel Ortiz 914feb8cd0fSTomas Winkler /** 915feb8cd0fSTomas Winkler * mei_cl_device_probe - bus probe function 916feb8cd0fSTomas Winkler * 917feb8cd0fSTomas Winkler * @dev: device 918feb8cd0fSTomas Winkler * 919feb8cd0fSTomas Winkler * Return: 0 on success; < 0 otherwise 920feb8cd0fSTomas Winkler */ 921e5354107SSamuel Ortiz static int mei_cl_device_probe(struct device *dev) 922e5354107SSamuel Ortiz { 923feb8cd0fSTomas Winkler struct mei_cl_device *cldev; 924b37719c3STomas Winkler struct mei_cl_driver *cldrv; 925feb8cd0fSTomas Winkler const struct mei_cl_device_id *id; 926b9c79543SAlexey Khoroshilov int ret; 927feb8cd0fSTomas Winkler 928feb8cd0fSTomas Winkler cldev = to_mei_cl_device(dev); 929feb8cd0fSTomas Winkler cldrv = to_mei_cl_driver(dev->driver); 930e5354107SSamuel Ortiz 931b37719c3STomas Winkler if (!cldev) 932e5354107SSamuel Ortiz return 0; 933e5354107SSamuel Ortiz 934b37719c3STomas Winkler if (!cldrv || !cldrv->probe) 935e5354107SSamuel Ortiz return -ENODEV; 936e5354107SSamuel Ortiz 937feb8cd0fSTomas Winkler id = mei_cl_device_find(cldev, cldrv); 938feb8cd0fSTomas Winkler if (!id) 939feb8cd0fSTomas Winkler return -ENODEV; 940e5354107SSamuel Ortiz 941b5958faaSAlexander Usyskin if (!mei_cl_bus_module_get(cldev)) { 942b5958faaSAlexander Usyskin dev_err(&cldev->dev, "get hw module failed"); 943b5958faaSAlexander Usyskin return -ENODEV; 944b5958faaSAlexander Usyskin } 945b5958faaSAlexander Usyskin 946b9c79543SAlexey Khoroshilov ret = cldrv->probe(cldev, id); 947b5958faaSAlexander Usyskin if (ret) { 948b5958faaSAlexander Usyskin mei_cl_bus_module_put(cldev); 949b9c79543SAlexey Khoroshilov return ret; 950b5958faaSAlexander Usyskin } 951e5354107SSamuel Ortiz 952b9c79543SAlexey Khoroshilov __module_get(THIS_MODULE); 953b9c79543SAlexey Khoroshilov return 0; 954e5354107SSamuel Ortiz } 955e5354107SSamuel Ortiz 956feb8cd0fSTomas Winkler /** 957feb8cd0fSTomas Winkler * mei_cl_device_remove - remove device from the bus 958feb8cd0fSTomas Winkler * 959feb8cd0fSTomas Winkler * @dev: device 960feb8cd0fSTomas Winkler * 961feb8cd0fSTomas Winkler * Return: 0 on success; < 0 otherwise 962feb8cd0fSTomas Winkler */ 963fc7a6209SUwe Kleine-König static void mei_cl_device_remove(struct device *dev) 964e5354107SSamuel Ortiz { 965b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 966f320ff03SUwe Kleine-König struct mei_cl_driver *cldrv = to_mei_cl_driver(dev->driver); 967e5354107SSamuel Ortiz 9689ecb839fSAlexander Usyskin if (cldrv->remove) 969bf5c9cc8SUwe Kleine-König cldrv->remove(cldev); 9709ecb839fSAlexander Usyskin 97157080e88SAlexander Usyskin mei_cldev_unregister_callbacks(cldev); 9723e833295SSamuel Ortiz 973b5958faaSAlexander Usyskin mei_cl_bus_module_put(cldev); 974feb8cd0fSTomas Winkler module_put(THIS_MODULE); 975e5354107SSamuel Ortiz } 976e5354107SSamuel Ortiz 977007d64ebSTomas Winkler static ssize_t name_show(struct device *dev, struct device_attribute *a, 978007d64ebSTomas Winkler char *buf) 979007d64ebSTomas Winkler { 980b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 981007d64ebSTomas Winkler 982423de92fSRasmus Villemoes return scnprintf(buf, PAGE_SIZE, "%s", cldev->name); 983007d64ebSTomas Winkler } 984007d64ebSTomas Winkler static DEVICE_ATTR_RO(name); 985007d64ebSTomas Winkler 986007d64ebSTomas Winkler static ssize_t uuid_show(struct device *dev, struct device_attribute *a, 987007d64ebSTomas Winkler char *buf) 988007d64ebSTomas Winkler { 989b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 990b37719c3STomas Winkler const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); 991007d64ebSTomas Winkler 99249ef431dSTomas Winkler return sprintf(buf, "%pUl", uuid); 993007d64ebSTomas Winkler } 994007d64ebSTomas Winkler static DEVICE_ATTR_RO(uuid); 995007d64ebSTomas Winkler 99640b7320eSTomas Winkler static ssize_t version_show(struct device *dev, struct device_attribute *a, 99740b7320eSTomas Winkler char *buf) 99840b7320eSTomas Winkler { 99940b7320eSTomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 100040b7320eSTomas Winkler u8 version = mei_me_cl_ver(cldev->me_cl); 100140b7320eSTomas Winkler 100249ef431dSTomas Winkler return sprintf(buf, "%02X", version); 100340b7320eSTomas Winkler } 100440b7320eSTomas Winkler static DEVICE_ATTR_RO(version); 100540b7320eSTomas Winkler 1006e5354107SSamuel Ortiz static ssize_t modalias_show(struct device *dev, struct device_attribute *a, 1007e5354107SSamuel Ortiz char *buf) 1008e5354107SSamuel Ortiz { 1009b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 1010b37719c3STomas Winkler const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); 10116f9193ecSPratyush Anand u8 version = mei_me_cl_ver(cldev->me_cl); 1012e5354107SSamuel Ortiz 10136f9193ecSPratyush Anand return scnprintf(buf, PAGE_SIZE, "mei:%s:%pUl:%02X:", 10146f9193ecSPratyush Anand cldev->name, uuid, version); 1015e5354107SSamuel Ortiz } 101632f389ecSGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias); 1017e5354107SSamuel Ortiz 101864498695SAlexander Usyskin static ssize_t max_conn_show(struct device *dev, struct device_attribute *a, 101964498695SAlexander Usyskin char *buf) 102064498695SAlexander Usyskin { 102164498695SAlexander Usyskin struct mei_cl_device *cldev = to_mei_cl_device(dev); 102264498695SAlexander Usyskin u8 maxconn = mei_me_cl_max_conn(cldev->me_cl); 102364498695SAlexander Usyskin 102449ef431dSTomas Winkler return sprintf(buf, "%d", maxconn); 102564498695SAlexander Usyskin } 102664498695SAlexander Usyskin static DEVICE_ATTR_RO(max_conn); 102764498695SAlexander Usyskin 102864498695SAlexander Usyskin static ssize_t fixed_show(struct device *dev, struct device_attribute *a, 102964498695SAlexander Usyskin char *buf) 103064498695SAlexander Usyskin { 103164498695SAlexander Usyskin struct mei_cl_device *cldev = to_mei_cl_device(dev); 103264498695SAlexander Usyskin u8 fixed = mei_me_cl_fixed(cldev->me_cl); 103364498695SAlexander Usyskin 103449ef431dSTomas Winkler return sprintf(buf, "%d", fixed); 103564498695SAlexander Usyskin } 103664498695SAlexander Usyskin static DEVICE_ATTR_RO(fixed); 103764498695SAlexander Usyskin 10382dd1e5aeSAlexander Usyskin static ssize_t vtag_show(struct device *dev, struct device_attribute *a, 10392dd1e5aeSAlexander Usyskin char *buf) 10402dd1e5aeSAlexander Usyskin { 10412dd1e5aeSAlexander Usyskin struct mei_cl_device *cldev = to_mei_cl_device(dev); 10422dd1e5aeSAlexander Usyskin bool vt = mei_me_cl_vt(cldev->me_cl); 10432dd1e5aeSAlexander Usyskin 10442dd1e5aeSAlexander Usyskin return sprintf(buf, "%d", vt); 10452dd1e5aeSAlexander Usyskin } 10462dd1e5aeSAlexander Usyskin static DEVICE_ATTR_RO(vtag); 10472dd1e5aeSAlexander Usyskin 104864498695SAlexander Usyskin static ssize_t max_len_show(struct device *dev, struct device_attribute *a, 104964498695SAlexander Usyskin char *buf) 105064498695SAlexander Usyskin { 105164498695SAlexander Usyskin struct mei_cl_device *cldev = to_mei_cl_device(dev); 105264498695SAlexander Usyskin u32 maxlen = mei_me_cl_max_len(cldev->me_cl); 105364498695SAlexander Usyskin 105449ef431dSTomas Winkler return sprintf(buf, "%u", maxlen); 105564498695SAlexander Usyskin } 105664498695SAlexander Usyskin static DEVICE_ATTR_RO(max_len); 105764498695SAlexander Usyskin 1058d49dc5e7STomas Winkler static struct attribute *mei_cldev_attrs[] = { 1059007d64ebSTomas Winkler &dev_attr_name.attr, 1060007d64ebSTomas Winkler &dev_attr_uuid.attr, 106140b7320eSTomas Winkler &dev_attr_version.attr, 106232f389ecSGreg Kroah-Hartman &dev_attr_modalias.attr, 106364498695SAlexander Usyskin &dev_attr_max_conn.attr, 106464498695SAlexander Usyskin &dev_attr_fixed.attr, 10652dd1e5aeSAlexander Usyskin &dev_attr_vtag.attr, 106664498695SAlexander Usyskin &dev_attr_max_len.attr, 106732f389ecSGreg Kroah-Hartman NULL, 1068e5354107SSamuel Ortiz }; 1069d49dc5e7STomas Winkler ATTRIBUTE_GROUPS(mei_cldev); 1070e5354107SSamuel Ortiz 107138d3c00dSTomas Winkler /** 107238d3c00dSTomas Winkler * mei_cl_device_uevent - me client bus uevent handler 107338d3c00dSTomas Winkler * 107438d3c00dSTomas Winkler * @dev: device 107538d3c00dSTomas Winkler * @env: uevent kobject 107638d3c00dSTomas Winkler * 107738d3c00dSTomas Winkler * Return: 0 on success -ENOMEM on when add_uevent_var fails 107838d3c00dSTomas Winkler */ 107938d3c00dSTomas Winkler static int mei_cl_device_uevent(struct device *dev, struct kobj_uevent_env *env) 1080e5354107SSamuel Ortiz { 1081b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 1082b37719c3STomas Winkler const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); 108340b7320eSTomas Winkler u8 version = mei_me_cl_ver(cldev->me_cl); 108440b7320eSTomas Winkler 108540b7320eSTomas Winkler if (add_uevent_var(env, "MEI_CL_VERSION=%d", version)) 108640b7320eSTomas Winkler return -ENOMEM; 1087c93b76b3STomas Winkler 1088007d64ebSTomas Winkler if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid)) 1089007d64ebSTomas Winkler return -ENOMEM; 1090007d64ebSTomas Winkler 1091b37719c3STomas Winkler if (add_uevent_var(env, "MEI_CL_NAME=%s", cldev->name)) 1092007d64ebSTomas Winkler return -ENOMEM; 1093007d64ebSTomas Winkler 1094b26864caSTomas Winkler if (add_uevent_var(env, "MODALIAS=mei:%s:%pUl:%02X:", 1095b26864caSTomas Winkler cldev->name, uuid, version)) 1096e5354107SSamuel Ortiz return -ENOMEM; 1097e5354107SSamuel Ortiz 1098e5354107SSamuel Ortiz return 0; 1099e5354107SSamuel Ortiz } 1100e5354107SSamuel Ortiz 1101e5354107SSamuel Ortiz static struct bus_type mei_cl_bus_type = { 1102e5354107SSamuel Ortiz .name = "mei", 1103d49dc5e7STomas Winkler .dev_groups = mei_cldev_groups, 1104e5354107SSamuel Ortiz .match = mei_cl_device_match, 1105e5354107SSamuel Ortiz .probe = mei_cl_device_probe, 1106e5354107SSamuel Ortiz .remove = mei_cl_device_remove, 110738d3c00dSTomas Winkler .uevent = mei_cl_device_uevent, 1108e5354107SSamuel Ortiz }; 1109e5354107SSamuel Ortiz 1110512f64d9STomas Winkler static struct mei_device *mei_dev_bus_get(struct mei_device *bus) 1111512f64d9STomas Winkler { 1112512f64d9STomas Winkler if (bus) 1113512f64d9STomas Winkler get_device(bus->dev); 1114512f64d9STomas Winkler 1115512f64d9STomas Winkler return bus; 1116512f64d9STomas Winkler } 1117512f64d9STomas Winkler 1118512f64d9STomas Winkler static void mei_dev_bus_put(struct mei_device *bus) 1119512f64d9STomas Winkler { 1120512f64d9STomas Winkler if (bus) 1121512f64d9STomas Winkler put_device(bus->dev); 1122512f64d9STomas Winkler } 1123512f64d9STomas Winkler 1124ae48d74dSTomas Winkler static void mei_cl_bus_dev_release(struct device *dev) 1125e5354107SSamuel Ortiz { 1126b37719c3STomas Winkler struct mei_cl_device *cldev = to_mei_cl_device(dev); 1127d49ed64aSAlexander Usyskin 1128b37719c3STomas Winkler if (!cldev) 1129d49ed64aSAlexander Usyskin return; 1130d49ed64aSAlexander Usyskin 11312cca3465SAlexander Usyskin mei_cl_flush_queues(cldev->cl, NULL); 1132b37719c3STomas Winkler mei_me_cl_put(cldev->me_cl); 1133512f64d9STomas Winkler mei_dev_bus_put(cldev->bus); 113434f1166aSTomas Winkler mei_cl_unlink(cldev->cl); 1135c110cdb1SAlexander Usyskin kfree(cldev->cl); 1136b37719c3STomas Winkler kfree(cldev); 1137e5354107SSamuel Ortiz } 1138e5354107SSamuel Ortiz 1139b8d01b7fSBhumika Goyal static const struct device_type mei_cl_device_type = { 1140ae48d74dSTomas Winkler .release = mei_cl_bus_dev_release, 1141e5354107SSamuel Ortiz }; 1142e5354107SSamuel Ortiz 114371ce7891STomas Winkler /** 1144213dd193STomas Winkler * mei_cl_bus_set_name - set device name for me client device 11457a2b9e6eSAlexander Usyskin * <controller>-<client device> 11467a2b9e6eSAlexander Usyskin * Example: 0000:00:16.0-55213584-9a29-4916-badf-0fb7ed682aeb 1147213dd193STomas Winkler * 1148213dd193STomas Winkler * @cldev: me client device 1149213dd193STomas Winkler */ 1150213dd193STomas Winkler static inline void mei_cl_bus_set_name(struct mei_cl_device *cldev) 1151213dd193STomas Winkler { 11527a2b9e6eSAlexander Usyskin dev_set_name(&cldev->dev, "%s-%pUl", 11537a2b9e6eSAlexander Usyskin dev_name(cldev->bus->dev), 11547a2b9e6eSAlexander Usyskin mei_me_cl_uuid(cldev->me_cl)); 1155213dd193STomas Winkler } 1156213dd193STomas Winkler 1157213dd193STomas Winkler /** 1158ae48d74dSTomas Winkler * mei_cl_bus_dev_alloc - initialize and allocate mei client device 115971ce7891STomas Winkler * 116071ce7891STomas Winkler * @bus: mei device 116171ce7891STomas Winkler * @me_cl: me client 116271ce7891STomas Winkler * 116371ce7891STomas Winkler * Return: allocated device structur or NULL on allocation failure 116471ce7891STomas Winkler */ 1165ae48d74dSTomas Winkler static struct mei_cl_device *mei_cl_bus_dev_alloc(struct mei_device *bus, 116671ce7891STomas Winkler struct mei_me_client *me_cl) 116771ce7891STomas Winkler { 116871ce7891STomas Winkler struct mei_cl_device *cldev; 1169c110cdb1SAlexander Usyskin struct mei_cl *cl; 117071ce7891STomas Winkler 117171ae5255STomas Winkler cldev = kzalloc(sizeof(*cldev), GFP_KERNEL); 117271ce7891STomas Winkler if (!cldev) 117371ce7891STomas Winkler return NULL; 117471ce7891STomas Winkler 1175c110cdb1SAlexander Usyskin cl = mei_cl_allocate(bus); 1176c110cdb1SAlexander Usyskin if (!cl) { 1177c110cdb1SAlexander Usyskin kfree(cldev); 1178c110cdb1SAlexander Usyskin return NULL; 1179c110cdb1SAlexander Usyskin } 1180c110cdb1SAlexander Usyskin 118171ce7891STomas Winkler device_initialize(&cldev->dev); 118271ce7891STomas Winkler cldev->dev.parent = bus->dev; 118371ce7891STomas Winkler cldev->dev.bus = &mei_cl_bus_type; 118471ce7891STomas Winkler cldev->dev.type = &mei_cl_device_type; 118571ce7891STomas Winkler cldev->bus = mei_dev_bus_get(bus); 118671ce7891STomas Winkler cldev->me_cl = mei_me_cl_get(me_cl); 1187c110cdb1SAlexander Usyskin cldev->cl = cl; 1188213dd193STomas Winkler mei_cl_bus_set_name(cldev); 118971ce7891STomas Winkler cldev->is_added = 0; 119071ce7891STomas Winkler INIT_LIST_HEAD(&cldev->bus_list); 119171ce7891STomas Winkler 119271ce7891STomas Winkler return cldev; 119371ce7891STomas Winkler } 119471ce7891STomas Winkler 119571ce7891STomas Winkler /** 11967bbd2584SMauro Carvalho Chehab * mei_cl_bus_dev_setup - setup me client device 119771ce7891STomas Winkler * run fix up routines and set the device name 119871ce7891STomas Winkler * 119971ce7891STomas Winkler * @bus: mei device 120071ce7891STomas Winkler * @cldev: me client device 120171ce7891STomas Winkler * 120271ce7891STomas Winkler * Return: true if the device is eligible for enumeration 120371ce7891STomas Winkler */ 1204ae48d74dSTomas Winkler static bool mei_cl_bus_dev_setup(struct mei_device *bus, 120571ce7891STomas Winkler struct mei_cl_device *cldev) 120671ce7891STomas Winkler { 120771ce7891STomas Winkler cldev->do_match = 1; 1208ae48d74dSTomas Winkler mei_cl_bus_dev_fixup(cldev); 120971ce7891STomas Winkler 1210213dd193STomas Winkler /* the device name can change during fix up */ 121171ce7891STomas Winkler if (cldev->do_match) 1212213dd193STomas Winkler mei_cl_bus_set_name(cldev); 121371ce7891STomas Winkler 121471ce7891STomas Winkler return cldev->do_match == 1; 121571ce7891STomas Winkler } 121671ce7891STomas Winkler 121771ce7891STomas Winkler /** 121871ce7891STomas Winkler * mei_cl_bus_dev_add - add me client devices 121971ce7891STomas Winkler * 122071ce7891STomas Winkler * @cldev: me client device 122171ce7891STomas Winkler * 122271ce7891STomas Winkler * Return: 0 on success; < 0 on failre 122371ce7891STomas Winkler */ 122471ce7891STomas Winkler static int mei_cl_bus_dev_add(struct mei_cl_device *cldev) 122571ce7891STomas Winkler { 122671ce7891STomas Winkler int ret; 122771ce7891STomas Winkler 1228b26864caSTomas Winkler dev_dbg(cldev->bus->dev, "adding %pUL:%02X\n", 1229b26864caSTomas Winkler mei_me_cl_uuid(cldev->me_cl), 1230b26864caSTomas Winkler mei_me_cl_ver(cldev->me_cl)); 123171ce7891STomas Winkler ret = device_add(&cldev->dev); 123271ce7891STomas Winkler if (!ret) 123371ce7891STomas Winkler cldev->is_added = 1; 123471ce7891STomas Winkler 123571ce7891STomas Winkler return ret; 123671ce7891STomas Winkler } 123771ce7891STomas Winkler 12386009595aSTomas Winkler /** 12396009595aSTomas Winkler * mei_cl_bus_dev_stop - stop the driver 12406009595aSTomas Winkler * 12416009595aSTomas Winkler * @cldev: me client device 12426009595aSTomas Winkler */ 12436009595aSTomas Winkler static void mei_cl_bus_dev_stop(struct mei_cl_device *cldev) 12446009595aSTomas Winkler { 12456009595aSTomas Winkler if (cldev->is_added) 12466009595aSTomas Winkler device_release_driver(&cldev->dev); 12476009595aSTomas Winkler } 12486009595aSTomas Winkler 12496009595aSTomas Winkler /** 12506009595aSTomas Winkler * mei_cl_bus_dev_destroy - destroy me client devices object 12516009595aSTomas Winkler * 12526009595aSTomas Winkler * @cldev: me client device 12532da55cfdSTomas Winkler * 12542da55cfdSTomas Winkler * Locking: called under "dev->cl_bus_lock" lock 12556009595aSTomas Winkler */ 12566009595aSTomas Winkler static void mei_cl_bus_dev_destroy(struct mei_cl_device *cldev) 12576009595aSTomas Winkler { 12582da55cfdSTomas Winkler 12592da55cfdSTomas Winkler WARN_ON(!mutex_is_locked(&cldev->bus->cl_bus_lock)); 12602da55cfdSTomas Winkler 12616009595aSTomas Winkler if (!cldev->is_added) 12626009595aSTomas Winkler return; 12636009595aSTomas Winkler 12646009595aSTomas Winkler device_del(&cldev->dev); 12656009595aSTomas Winkler 12666009595aSTomas Winkler list_del_init(&cldev->bus_list); 12676009595aSTomas Winkler 12686009595aSTomas Winkler cldev->is_added = 0; 12696009595aSTomas Winkler put_device(&cldev->dev); 12706009595aSTomas Winkler } 12716009595aSTomas Winkler 12726009595aSTomas Winkler /** 12736009595aSTomas Winkler * mei_cl_bus_remove_device - remove a devices form the bus 12746009595aSTomas Winkler * 12756009595aSTomas Winkler * @cldev: me client device 12766009595aSTomas Winkler */ 12776009595aSTomas Winkler static void mei_cl_bus_remove_device(struct mei_cl_device *cldev) 12786009595aSTomas Winkler { 12796009595aSTomas Winkler mei_cl_bus_dev_stop(cldev); 12806009595aSTomas Winkler mei_cl_bus_dev_destroy(cldev); 12816009595aSTomas Winkler } 12826009595aSTomas Winkler 12836009595aSTomas Winkler /** 12846009595aSTomas Winkler * mei_cl_bus_remove_devices - remove all devices form the bus 12856009595aSTomas Winkler * 12866009595aSTomas Winkler * @bus: mei device 12876009595aSTomas Winkler */ 12886009595aSTomas Winkler void mei_cl_bus_remove_devices(struct mei_device *bus) 12896009595aSTomas Winkler { 12906009595aSTomas Winkler struct mei_cl_device *cldev, *next; 12916009595aSTomas Winkler 12922da55cfdSTomas Winkler mutex_lock(&bus->cl_bus_lock); 12936009595aSTomas Winkler list_for_each_entry_safe(cldev, next, &bus->device_list, bus_list) 12946009595aSTomas Winkler mei_cl_bus_remove_device(cldev); 12952da55cfdSTomas Winkler mutex_unlock(&bus->cl_bus_lock); 12966009595aSTomas Winkler } 12976009595aSTomas Winkler 12986009595aSTomas Winkler 12996009595aSTomas Winkler /** 1300ae48d74dSTomas Winkler * mei_cl_bus_dev_init - allocate and initializes an mei client devices 13016009595aSTomas Winkler * based on me client 13026009595aSTomas Winkler * 13036009595aSTomas Winkler * @bus: mei device 13046009595aSTomas Winkler * @me_cl: me client 13052da55cfdSTomas Winkler * 13062da55cfdSTomas Winkler * Locking: called under "dev->cl_bus_lock" lock 13076009595aSTomas Winkler */ 1308ae48d74dSTomas Winkler static void mei_cl_bus_dev_init(struct mei_device *bus, 1309ae48d74dSTomas Winkler struct mei_me_client *me_cl) 1310e5354107SSamuel Ortiz { 1311b37719c3STomas Winkler struct mei_cl_device *cldev; 13126009595aSTomas Winkler 13132da55cfdSTomas Winkler WARN_ON(!mutex_is_locked(&bus->cl_bus_lock)); 13142da55cfdSTomas Winkler 13156009595aSTomas Winkler dev_dbg(bus->dev, "initializing %pUl", mei_me_cl_uuid(me_cl)); 13166009595aSTomas Winkler 13176009595aSTomas Winkler if (me_cl->bus_added) 13186009595aSTomas Winkler return; 1319e5354107SSamuel Ortiz 1320ae48d74dSTomas Winkler cldev = mei_cl_bus_dev_alloc(bus, me_cl); 1321b37719c3STomas Winkler if (!cldev) 13226009595aSTomas Winkler return; 1323e5354107SSamuel Ortiz 13246009595aSTomas Winkler me_cl->bus_added = true; 13256009595aSTomas Winkler list_add_tail(&cldev->bus_list, &bus->device_list); 1326c93b76b3STomas Winkler 1327a7b71bc0SSamuel Ortiz } 1328a7b71bc0SSamuel Ortiz 13296009595aSTomas Winkler /** 13306009595aSTomas Winkler * mei_cl_bus_rescan - scan me clients list and add create 13316009595aSTomas Winkler * devices for eligible clients 13326009595aSTomas Winkler * 13336009595aSTomas Winkler * @bus: mei device 13346009595aSTomas Winkler */ 13358bb2d27fSAlexander Usyskin static void mei_cl_bus_rescan(struct mei_device *bus) 1336e5354107SSamuel Ortiz { 13376009595aSTomas Winkler struct mei_cl_device *cldev, *n; 13386009595aSTomas Winkler struct mei_me_client *me_cl; 13396009595aSTomas Winkler 13402da55cfdSTomas Winkler mutex_lock(&bus->cl_bus_lock); 13412da55cfdSTomas Winkler 13426009595aSTomas Winkler down_read(&bus->me_clients_rwsem); 13436009595aSTomas Winkler list_for_each_entry(me_cl, &bus->me_clients, list) 1344ae48d74dSTomas Winkler mei_cl_bus_dev_init(bus, me_cl); 13456009595aSTomas Winkler up_read(&bus->me_clients_rwsem); 13466009595aSTomas Winkler 13476009595aSTomas Winkler list_for_each_entry_safe(cldev, n, &bus->device_list, bus_list) { 13486009595aSTomas Winkler 13496009595aSTomas Winkler if (!mei_me_cl_is_active(cldev->me_cl)) { 13506009595aSTomas Winkler mei_cl_bus_remove_device(cldev); 13516009595aSTomas Winkler continue; 1352e5354107SSamuel Ortiz } 13536009595aSTomas Winkler 13546009595aSTomas Winkler if (cldev->is_added) 13556009595aSTomas Winkler continue; 13566009595aSTomas Winkler 1357ae48d74dSTomas Winkler if (mei_cl_bus_dev_setup(bus, cldev)) 13586009595aSTomas Winkler mei_cl_bus_dev_add(cldev); 13596009595aSTomas Winkler else { 13606009595aSTomas Winkler list_del_init(&cldev->bus_list); 13616009595aSTomas Winkler put_device(&cldev->dev); 13626009595aSTomas Winkler } 13636009595aSTomas Winkler } 13646009595aSTomas Winkler mutex_unlock(&bus->cl_bus_lock); 13656009595aSTomas Winkler 13666009595aSTomas Winkler dev_dbg(bus->dev, "rescan end"); 13676009595aSTomas Winkler } 1368333e4ee0SSamuel Ortiz 1369a816a00eSAlexander Usyskin void mei_cl_bus_rescan_work(struct work_struct *work) 1370a816a00eSAlexander Usyskin { 1371a816a00eSAlexander Usyskin struct mei_device *bus = 1372a816a00eSAlexander Usyskin container_of(work, struct mei_device, bus_rescan_work); 1373a816a00eSAlexander Usyskin 1374a816a00eSAlexander Usyskin mei_cl_bus_rescan(bus); 1375a816a00eSAlexander Usyskin } 1376a816a00eSAlexander Usyskin 1377d49dc5e7STomas Winkler int __mei_cldev_driver_register(struct mei_cl_driver *cldrv, 1378d49dc5e7STomas Winkler struct module *owner) 1379333e4ee0SSamuel Ortiz { 1380333e4ee0SSamuel Ortiz int err; 1381333e4ee0SSamuel Ortiz 1382b37719c3STomas Winkler cldrv->driver.name = cldrv->name; 1383b37719c3STomas Winkler cldrv->driver.owner = owner; 1384b37719c3STomas Winkler cldrv->driver.bus = &mei_cl_bus_type; 1385333e4ee0SSamuel Ortiz 1386b37719c3STomas Winkler err = driver_register(&cldrv->driver); 1387333e4ee0SSamuel Ortiz if (err) 1388333e4ee0SSamuel Ortiz return err; 1389333e4ee0SSamuel Ortiz 1390b37719c3STomas Winkler pr_debug("mei: driver [%s] registered\n", cldrv->driver.name); 1391333e4ee0SSamuel Ortiz 1392333e4ee0SSamuel Ortiz return 0; 1393333e4ee0SSamuel Ortiz } 1394d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(__mei_cldev_driver_register); 1395333e4ee0SSamuel Ortiz 1396d49dc5e7STomas Winkler void mei_cldev_driver_unregister(struct mei_cl_driver *cldrv) 1397333e4ee0SSamuel Ortiz { 1398b37719c3STomas Winkler driver_unregister(&cldrv->driver); 1399333e4ee0SSamuel Ortiz 1400b37719c3STomas Winkler pr_debug("mei: driver [%s] unregistered\n", cldrv->driver.name); 1401333e4ee0SSamuel Ortiz } 1402d49dc5e7STomas Winkler EXPORT_SYMBOL_GPL(mei_cldev_driver_unregister); 14033e833295SSamuel Ortiz 14046009595aSTomas Winkler 1405cf3baefbSSamuel Ortiz int __init mei_cl_bus_init(void) 1406cf3baefbSSamuel Ortiz { 1407cf3baefbSSamuel Ortiz return bus_register(&mei_cl_bus_type); 1408cf3baefbSSamuel Ortiz } 1409cf3baefbSSamuel Ortiz 1410cf3baefbSSamuel Ortiz void __exit mei_cl_bus_exit(void) 1411cf3baefbSSamuel Ortiz { 1412cf3baefbSSamuel Ortiz bus_unregister(&mei_cl_bus_type); 1413cf3baefbSSamuel Ortiz } 1414