aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/mei/bus.c226
-rw-r--r--drivers/misc/mei/mei_dev.h30
2 files changed, 256 insertions, 0 deletions
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index d16b3c3e1b38..16c7fff50549 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -16,6 +16,7 @@
16#include <linux/module.h> 16#include <linux/module.h>
17#include <linux/device.h> 17#include <linux/device.h>
18#include <linux/kernel.h> 18#include <linux/kernel.h>
19#include <linux/sched.h>
19#include <linux/init.h> 20#include <linux/init.h>
20#include <linux/errno.h> 21#include <linux/errno.h>
21#include <linux/slab.h> 22#include <linux/slab.h>
@@ -25,6 +26,8 @@
25#include <linux/mei_cl_bus.h> 26#include <linux/mei_cl_bus.h>
26 27
27#include "mei_dev.h" 28#include "mei_dev.h"
29#include "hw-me.h"
30#include "client.h"
28 31
29#define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver) 32#define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver)
30#define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev) 33#define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev)
@@ -81,6 +84,11 @@ static int mei_cl_device_remove(struct device *dev)
81 if (!device || !dev->driver) 84 if (!device || !dev->driver)
82 return 0; 85 return 0;
83 86
87 if (device->event_cb) {
88 device->event_cb = NULL;
89 cancel_work_sync(&device->event_work);
90 }
91
84 driver = to_mei_cl_driver(dev->driver); 92 driver = to_mei_cl_driver(dev->driver);
85 if (!driver->remove) { 93 if (!driver->remove) {
86 dev->driver = NULL; 94 dev->driver = NULL;
@@ -196,3 +204,221 @@ void mei_cl_driver_unregister(struct mei_cl_driver *driver)
196 pr_debug("mei: driver [%s] unregistered\n", driver->driver.name); 204 pr_debug("mei: driver [%s] unregistered\n", driver->driver.name);
197} 205}
198EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); 206EXPORT_SYMBOL_GPL(mei_cl_driver_unregister);
207
208int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length)
209{
210 struct mei_device *dev;
211 struct mei_msg_hdr mei_hdr;
212 struct mei_cl_cb *cb;
213 int me_cl_id, err;
214
215 if (WARN_ON(!cl || !cl->dev))
216 return -ENODEV;
217
218 if (cl->state != MEI_FILE_CONNECTED)
219 return -ENODEV;
220
221 cb = mei_io_cb_init(cl, NULL);
222 if (!cb)
223 return -ENOMEM;
224
225 err = mei_io_cb_alloc_req_buf(cb, length);
226 if (err < 0) {
227 mei_io_cb_free(cb);
228 return err;
229 }
230
231 memcpy(cb->request_buffer.data, buf, length);
232 cb->fop_type = MEI_FOP_WRITE;
233
234 dev = cl->dev;
235
236 mutex_lock(&dev->device_lock);
237
238 /* Check if we have an ME client device */
239 me_cl_id = mei_me_cl_by_id(dev, cl->me_client_id);
240 if (me_cl_id == dev->me_clients_num) {
241 err = -ENODEV;
242 goto out_err;
243 }
244
245 if (length > dev->me_clients[me_cl_id].props.max_msg_length) {
246 err = -EINVAL;
247 goto out_err;
248 }
249
250 err = mei_cl_flow_ctrl_creds(cl);
251 if (err < 0)
252 goto out_err;
253
254 /* Host buffer is not ready, we queue the request */
255 if (err == 0 || !dev->hbuf_is_ready) {
256 cb->buf_idx = 0;
257 mei_hdr.msg_complete = 0;
258 cl->writing_state = MEI_WRITING;
259 list_add_tail(&cb->list, &dev->write_list.list);
260
261 mutex_unlock(&dev->device_lock);
262
263 return length;
264 }
265
266 dev->hbuf_is_ready = false;
267
268 /* Check for a maximum length */
269 if (length > mei_hbuf_max_len(dev)) {
270 mei_hdr.length = mei_hbuf_max_len(dev);
271 mei_hdr.msg_complete = 0;
272 } else {
273 mei_hdr.length = length;
274 mei_hdr.msg_complete = 1;
275 }
276
277 mei_hdr.host_addr = cl->host_client_id;
278 mei_hdr.me_addr = cl->me_client_id;
279 mei_hdr.reserved = 0;
280
281 if (mei_write_message(dev, &mei_hdr, buf)) {
282 err = -EIO;
283 goto out_err;
284 }
285
286 cl->writing_state = MEI_WRITING;
287 cb->buf_idx = mei_hdr.length;
288
289 if (!mei_hdr.msg_complete) {
290 list_add_tail(&cb->list, &dev->write_list.list);
291 } else {
292 if (mei_cl_flow_ctrl_reduce(cl)) {
293 err = -EIO;
294 goto out_err;
295 }
296
297 list_add_tail(&cb->list, &dev->write_waiting_list.list);
298 }
299
300 mutex_unlock(&dev->device_lock);
301
302 return mei_hdr.length;
303
304out_err:
305 mutex_unlock(&dev->device_lock);
306 mei_io_cb_free(cb);
307
308 return err;
309}
310
311int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
312{
313 struct mei_device *dev;
314 struct mei_cl_cb *cb;
315 size_t r_length;
316 int err;
317
318 if (WARN_ON(!cl || !cl->dev))
319 return -ENODEV;
320
321 dev = cl->dev;
322
323 mutex_lock(&dev->device_lock);
324
325 if (!cl->read_cb) {
326 err = mei_cl_read_start(cl);
327 if (err < 0) {
328 mutex_unlock(&dev->device_lock);
329 return err;
330 }
331 }
332
333 if (cl->reading_state != MEI_READ_COMPLETE &&
334 !waitqueue_active(&cl->rx_wait)) {
335 mutex_unlock(&dev->device_lock);
336
337 if (wait_event_interruptible(cl->rx_wait,
338 (MEI_READ_COMPLETE == cl->reading_state))) {
339 if (signal_pending(current))
340 return -EINTR;
341 return -ERESTARTSYS;
342 }
343
344 mutex_lock(&dev->device_lock);
345 }
346
347 cb = cl->read_cb;
348
349 if (cl->reading_state != MEI_READ_COMPLETE) {
350 r_length = 0;
351 goto out;
352 }
353
354 r_length = min_t(size_t, length, cb->buf_idx);
355
356 memcpy(buf, cb->response_buffer.data, r_length);
357
358 mei_io_cb_free(cb);
359 cl->reading_state = MEI_IDLE;
360 cl->read_cb = NULL;
361
362out:
363 mutex_unlock(&dev->device_lock);
364
365 return r_length;
366}
367
368int mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length)
369{
370 struct mei_cl *cl = NULL;
371
372 /* TODO: hook between mei_bus_client and mei_cl */
373
374 if (device->ops && device->ops->send)
375 return device->ops->send(device, buf, length);
376
377 return __mei_cl_send(cl, buf, length);
378}
379EXPORT_SYMBOL_GPL(mei_cl_send);
380
381int mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length)
382{
383 struct mei_cl *cl = NULL;
384
385 /* TODO: hook between mei_bus_client and mei_cl */
386
387 if (device->ops && device->ops->recv)
388 return device->ops->recv(device, buf, length);
389
390 return __mei_cl_recv(cl, buf, length);
391}
392EXPORT_SYMBOL_GPL(mei_cl_recv);
393
394static void mei_bus_event_work(struct work_struct *work)
395{
396 struct mei_cl_device *device;
397
398 device = container_of(work, struct mei_cl_device, event_work);
399
400 if (device->event_cb)
401 device->event_cb(device, device->events, device->event_context);
402
403 device->events = 0;
404
405 /* Prepare for the next read */
406 mei_cl_read_start(device->cl);
407}
408
409int mei_cl_register_event_cb(struct mei_cl_device *device,
410 mei_cl_event_cb_t event_cb, void *context)
411{
412 if (device->event_cb)
413 return -EALREADY;
414
415 device->events = 0;
416 device->event_cb = event_cb;
417 device->event_context = context;
418 INIT_WORK(&device->event_work, mei_bus_event_work);
419
420 mei_cl_read_start(device->cl);
421
422 return 0;
423}
424EXPORT_SYMBOL_GPL(mei_cl_register_event_cb);
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index 7abb705ddf3f..cde5687039f3 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -268,6 +268,25 @@ struct mei_cl_device *mei_cl_add_device(struct mei_device *dev,
268 uuid_le uuid, char *name); 268 uuid_le uuid, char *name);
269void mei_cl_remove_device(struct mei_cl_device *device); 269void mei_cl_remove_device(struct mei_cl_device *device);
270 270
271int __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length);
272int __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length);
273
274/**
275 * struct mei_cl_transport_ops - MEI CL device transport ops
276 * This structure allows ME host clients to implement technology
277 * specific transport layers.
278 *
279 * @send: Tx hook for the device. This allows ME host clients to trap
280 * the device driver buffers before actually physically
281 * pushing it to the ME.
282 * @recv: Rx hook for the device. This allows ME host clients to trap the
283 * ME buffers before forwarding them to the device driver.
284 */
285struct mei_cl_transport_ops {
286 int (*send)(struct mei_cl_device *device, u8 *buf, size_t length);
287 int (*recv)(struct mei_cl_device *device, u8 *buf, size_t length);
288};
289
271/** 290/**
272 * struct mei_cl_device - MEI device handle 291 * struct mei_cl_device - MEI device handle
273 * An mei_cl_device pointer is returned from mei_add_device() 292 * An mei_cl_device pointer is returned from mei_add_device()
@@ -278,6 +297,10 @@ void mei_cl_remove_device(struct mei_cl_device *device);
278 * @dev: linux driver model device pointer 297 * @dev: linux driver model device pointer
279 * @uuid: me client uuid 298 * @uuid: me client uuid
280 * @cl: mei client 299 * @cl: mei client
300 * @ops: ME transport ops
301 * @event_cb: Drivers register this callback to get asynchronous ME
302 * events (e.g. Rx buffer pending) notifications.
303 * @events: Events bitmask sent to the driver.
281 * @priv_data: client private data 304 * @priv_data: client private data
282 */ 305 */
283struct mei_cl_device { 306struct mei_cl_device {
@@ -285,6 +308,13 @@ struct mei_cl_device {
285 308
286 struct mei_cl *cl; 309 struct mei_cl *cl;
287 310
311 const struct mei_cl_transport_ops *ops;
312
313 struct work_struct event_work;
314 mei_cl_event_cb_t event_cb;
315 void *event_context;
316 unsigned long events;
317
288 void *priv_data; 318 void *priv_data;
289}; 319};
290 320