xref: /linux/drivers/misc/mei/bus.c (revision 5d5bc18971543e82f6adf0b31745cf6545bc47a7)
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