diff options
Diffstat (limited to 'drivers/misc/mei/bus.c')
-rw-r--r-- | drivers/misc/mei/bus.c | 226 |
1 files changed, 226 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 | } |
198 | EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); | 206 | EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); |
207 | |||
208 | int __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 | |||
304 | out_err: | ||
305 | mutex_unlock(&dev->device_lock); | ||
306 | mei_io_cb_free(cb); | ||
307 | |||
308 | return err; | ||
309 | } | ||
310 | |||
311 | int __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 | |||
362 | out: | ||
363 | mutex_unlock(&dev->device_lock); | ||
364 | |||
365 | return r_length; | ||
366 | } | ||
367 | |||
368 | int 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 | } | ||
379 | EXPORT_SYMBOL_GPL(mei_cl_send); | ||
380 | |||
381 | int 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 | } | ||
392 | EXPORT_SYMBOL_GPL(mei_cl_recv); | ||
393 | |||
394 | static 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 | |||
409 | int 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 | } | ||
424 | EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); | ||