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