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