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