blob: cc3e76c60417d69dcc4a3a56eb6e1dcc455de114 [file] [log] [blame]
Tomas Winkler9ca90502013-01-08 23:07:13 +02001/*
2 *
3 * Intel Management Engine Interface (Intel MEI) Linux driver
4 * Copyright (c) 2003-2012, Intel Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 */
16
17#include <linux/pci.h>
18#include <linux/sched.h>
19#include <linux/wait.h>
20#include <linux/delay.h>
21
22#include <linux/mei.h>
23
24#include "mei_dev.h"
25#include "hbm.h"
26#include "interface.h"
Tomas Winkler90e0b5f2013-01-08 23:07:14 +020027#include "client.h"
28
29/**
30 * mei_me_cl_by_uuid - locate index of me client
31 *
32 * @dev: mei device
33 * returns me client index or -ENOENT if not found
34 */
35int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid)
36{
37 int i, res = -ENOENT;
38
39 for (i = 0; i < dev->me_clients_num; ++i)
40 if (uuid_le_cmp(*uuid,
41 dev->me_clients[i].props.protocol_name) == 0) {
42 res = i;
43 break;
44 }
45
46 return res;
47}
48
49
50/**
51 * mei_me_cl_by_id return index to me_clients for client_id
52 *
53 * @dev: the device structure
54 * @client_id: me client id
55 *
56 * Locking: called under "dev->device_lock" lock
57 *
58 * returns index on success, -ENOENT on failure.
59 */
60
61int mei_me_cl_by_id(struct mei_device *dev, u8 client_id)
62{
63 int i;
64 for (i = 0; i < dev->me_clients_num; i++)
65 if (dev->me_clients[i].client_id == client_id)
66 break;
67 if (WARN_ON(dev->me_clients[i].client_id != client_id))
68 return -ENOENT;
69
70 if (i == dev->me_clients_num)
71 return -ENOENT;
72
73 return i;
74}
Tomas Winkler9ca90502013-01-08 23:07:13 +020075
76
77/**
78 * mei_io_list_flush - removes list entry belonging to cl.
79 *
80 * @list: An instance of our list structure
81 * @cl: host client
82 */
83void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl)
84{
85 struct mei_cl_cb *cb;
86 struct mei_cl_cb *next;
87
88 list_for_each_entry_safe(cb, next, &list->list, list) {
89 if (cb->cl && mei_cl_cmp_id(cl, cb->cl))
90 list_del(&cb->list);
91 }
92}
93
94/**
95 * mei_io_cb_free - free mei_cb_private related memory
96 *
97 * @cb: mei callback struct
98 */
99void mei_io_cb_free(struct mei_cl_cb *cb)
100{
101 if (cb == NULL)
102 return;
103
104 kfree(cb->request_buffer.data);
105 kfree(cb->response_buffer.data);
106 kfree(cb);
107}
108
109/**
110 * mei_io_cb_init - allocate and initialize io callback
111 *
112 * @cl - mei client
113 * @file: pointer to file structure
114 *
115 * returns mei_cl_cb pointer or NULL;
116 */
117struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp)
118{
119 struct mei_cl_cb *cb;
120
121 cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
122 if (!cb)
123 return NULL;
124
125 mei_io_list_init(cb);
126
127 cb->file_object = fp;
128 cb->cl = cl;
129 cb->buf_idx = 0;
130 return cb;
131}
132
133/**
134 * mei_io_cb_alloc_req_buf - allocate request buffer
135 *
136 * @cb - io callback structure
137 * @size: size of the buffer
138 *
139 * returns 0 on success
140 * -EINVAL if cb is NULL
141 * -ENOMEM if allocation failed
142 */
143int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length)
144{
145 if (!cb)
146 return -EINVAL;
147
148 if (length == 0)
149 return 0;
150
151 cb->request_buffer.data = kmalloc(length, GFP_KERNEL);
152 if (!cb->request_buffer.data)
153 return -ENOMEM;
154 cb->request_buffer.size = length;
155 return 0;
156}
157/**
158 * mei_io_cb_alloc_req_buf - allocate respose buffer
159 *
160 * @cb - io callback structure
161 * @size: size of the buffer
162 *
163 * returns 0 on success
164 * -EINVAL if cb is NULL
165 * -ENOMEM if allocation failed
166 */
167int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length)
168{
169 if (!cb)
170 return -EINVAL;
171
172 if (length == 0)
173 return 0;
174
175 cb->response_buffer.data = kmalloc(length, GFP_KERNEL);
176 if (!cb->response_buffer.data)
177 return -ENOMEM;
178 cb->response_buffer.size = length;
179 return 0;
180}
181
182
183
184/**
185 * mei_cl_flush_queues - flushes queue lists belonging to cl.
186 *
187 * @dev: the device structure
188 * @cl: host client
189 */
190int mei_cl_flush_queues(struct mei_cl *cl)
191{
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200192 if (WARN_ON(!cl || !cl->dev))
Tomas Winkler9ca90502013-01-08 23:07:13 +0200193 return -EINVAL;
194
195 dev_dbg(&cl->dev->pdev->dev, "remove list entry belonging to cl\n");
196 mei_io_list_flush(&cl->dev->read_list, cl);
197 mei_io_list_flush(&cl->dev->write_list, cl);
198 mei_io_list_flush(&cl->dev->write_waiting_list, cl);
199 mei_io_list_flush(&cl->dev->ctrl_wr_list, cl);
200 mei_io_list_flush(&cl->dev->ctrl_rd_list, cl);
201 mei_io_list_flush(&cl->dev->amthif_cmd_list, cl);
202 mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl);
203 return 0;
204}
205
Tomas Winkler9ca90502013-01-08 23:07:13 +0200206
207/**
208 * mei_cl_init - initializes intialize cl.
209 *
210 * @cl: host client to be initialized
211 * @dev: mei device
212 */
213void mei_cl_init(struct mei_cl *cl, struct mei_device *dev)
214{
215 memset(cl, 0, sizeof(struct mei_cl));
216 init_waitqueue_head(&cl->wait);
217 init_waitqueue_head(&cl->rx_wait);
218 init_waitqueue_head(&cl->tx_wait);
219 INIT_LIST_HEAD(&cl->link);
220 cl->reading_state = MEI_IDLE;
221 cl->writing_state = MEI_IDLE;
222 cl->dev = dev;
223}
224
225/**
226 * mei_cl_allocate - allocates cl structure and sets it up.
227 *
228 * @dev: mei device
229 * returns The allocated file or NULL on failure
230 */
231struct mei_cl *mei_cl_allocate(struct mei_device *dev)
232{
233 struct mei_cl *cl;
234
235 cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL);
236 if (!cl)
237 return NULL;
238
239 mei_cl_init(cl, dev);
240
241 return cl;
242}
243
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200244/**
245 * mei_cl_find_read_cb - find this cl's callback in the read list
246 *
247 * @dev: device structure
248 * returns cb on success, NULL on error
249 */
250struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl)
251{
252 struct mei_device *dev = cl->dev;
253 struct mei_cl_cb *cb = NULL;
254 struct mei_cl_cb *next = NULL;
255
256 list_for_each_entry_safe(cb, next, &dev->read_list.list, list)
257 if (mei_cl_cmp_id(cl, cb->cl))
258 return cb;
259 return NULL;
260}
261
Tomas Winkler9ca90502013-01-08 23:07:13 +0200262
263/**
264 * mei_me_cl_link - create link between host and me clinet and add
265 * me_cl to the list
266 *
Tomas Winkler9ca90502013-01-08 23:07:13 +0200267 * @cl: link between me and host client assocated with opened file descriptor
268 * @uuid: uuid of ME client
269 * @client_id: id of the host client
270 *
271 * returns ME client index if ME client
272 * -EINVAL on incorrect values
273 * -ENONET if client not found
274 */
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200275int mei_cl_link_me(struct mei_cl *cl, const uuid_le *uuid, u8 host_cl_id)
Tomas Winkler9ca90502013-01-08 23:07:13 +0200276{
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200277 struct mei_device *dev;
Tomas Winkler9ca90502013-01-08 23:07:13 +0200278 int i;
279
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200280 if (WARN_ON(!cl || !cl->dev || !uuid))
Tomas Winkler9ca90502013-01-08 23:07:13 +0200281 return -EINVAL;
282
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200283 dev = cl->dev;
284
Tomas Winkler9ca90502013-01-08 23:07:13 +0200285 /* check for valid client id */
286 i = mei_me_cl_by_uuid(dev, uuid);
287 if (i >= 0) {
288 cl->me_client_id = dev->me_clients[i].client_id;
289 cl->state = MEI_FILE_CONNECTING;
290 cl->host_client_id = host_cl_id;
291
292 list_add_tail(&cl->link, &dev->file_list);
293 return (u8)i;
294 }
295
296 return -ENOENT;
297}
298/**
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200299 * mei_cl_unlink - remove me_cl from the list
Tomas Winkler9ca90502013-01-08 23:07:13 +0200300 *
301 * @dev: the device structure
302 * @host_client_id: host client id to be removed
303 */
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200304int mei_cl_unlink(struct mei_cl *cl)
Tomas Winkler9ca90502013-01-08 23:07:13 +0200305{
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200306 struct mei_device *dev;
Tomas Winkler9ca90502013-01-08 23:07:13 +0200307 struct mei_cl *pos, *next;
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200308
309 if (WARN_ON(!cl || !cl->dev))
310 return -EINVAL;
311
312 dev = cl->dev;
313
Tomas Winkler9ca90502013-01-08 23:07:13 +0200314 list_for_each_entry_safe(pos, next, &dev->file_list, link) {
315 if (cl->host_client_id == pos->host_client_id) {
316 dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n",
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200317 pos->host_client_id, pos->me_client_id);
Tomas Winkler9ca90502013-01-08 23:07:13 +0200318 list_del_init(&pos->link);
319 break;
320 }
321 }
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200322 return 0;
Tomas Winkler9ca90502013-01-08 23:07:13 +0200323}
324
325
326void mei_host_client_init(struct work_struct *work)
327{
328 struct mei_device *dev = container_of(work,
329 struct mei_device, init_work);
330 struct mei_client_properties *client_props;
331 int i;
332
333 mutex_lock(&dev->device_lock);
334
335 bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
336 dev->open_handle_count = 0;
337
338 /*
339 * Reserving the first three client IDs
340 * 0: Reserved for MEI Bus Message communications
341 * 1: Reserved for Watchdog
342 * 2: Reserved for AMTHI
343 */
344 bitmap_set(dev->host_clients_map, 0, 3);
345
346 for (i = 0; i < dev->me_clients_num; i++) {
347 client_props = &dev->me_clients[i].props;
348
349 if (!uuid_le_cmp(client_props->protocol_name, mei_amthi_guid))
350 mei_amthif_host_init(dev);
351 else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid))
352 mei_wd_host_init(dev);
353 }
354
355 dev->dev_state = MEI_DEV_ENABLED;
356
357 mutex_unlock(&dev->device_lock);
358}
359
360
361/**
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200362 * mei_cl_disconnect - disconnect host clinet form the me one
Tomas Winkler9ca90502013-01-08 23:07:13 +0200363 *
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200364 * @cl: host client
Tomas Winkler9ca90502013-01-08 23:07:13 +0200365 *
366 * Locking: called under "dev->device_lock" lock
367 *
368 * returns 0 on success, <0 on failure.
369 */
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200370int mei_cl_disconnect(struct mei_cl *cl)
Tomas Winkler9ca90502013-01-08 23:07:13 +0200371{
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200372 struct mei_device *dev;
Tomas Winkler9ca90502013-01-08 23:07:13 +0200373 struct mei_cl_cb *cb;
374 int rets, err;
375
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200376 if (WARN_ON(!cl || !cl->dev))
Tomas Winkler9ca90502013-01-08 23:07:13 +0200377 return -ENODEV;
378
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200379 dev = cl->dev;
380
Tomas Winkler9ca90502013-01-08 23:07:13 +0200381 if (cl->state != MEI_FILE_DISCONNECTING)
382 return 0;
383
384 cb = mei_io_cb_init(cl, NULL);
385 if (!cb)
386 return -ENOMEM;
387
388 cb->fop_type = MEI_FOP_CLOSE;
389 if (dev->mei_host_buffer_is_empty) {
390 dev->mei_host_buffer_is_empty = false;
391 if (mei_hbm_cl_disconnect_req(dev, cl)) {
392 rets = -ENODEV;
393 dev_err(&dev->pdev->dev, "failed to disconnect.\n");
394 goto free;
395 }
396 mdelay(10); /* Wait for hardware disconnection ready */
397 list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
398 } else {
399 dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n");
400 list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
401
402 }
403 mutex_unlock(&dev->device_lock);
404
405 err = wait_event_timeout(dev->wait_recvd_msg,
406 MEI_FILE_DISCONNECTED == cl->state,
407 mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
408
409 mutex_lock(&dev->device_lock);
410 if (MEI_FILE_DISCONNECTED == cl->state) {
411 rets = 0;
412 dev_dbg(&dev->pdev->dev, "successfully disconnected from FW client.\n");
413 } else {
414 rets = -ENODEV;
415 if (MEI_FILE_DISCONNECTED != cl->state)
416 dev_dbg(&dev->pdev->dev, "wrong status client disconnect.\n");
417
418 if (err)
419 dev_dbg(&dev->pdev->dev,
420 "wait failed disconnect err=%08x\n",
421 err);
422
423 dev_dbg(&dev->pdev->dev, "failed to disconnect from FW client.\n");
424 }
425
426 mei_io_list_flush(&dev->ctrl_rd_list, cl);
427 mei_io_list_flush(&dev->ctrl_wr_list, cl);
428free:
429 mei_io_cb_free(cb);
430 return rets;
431}
432
433
434/**
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200435 * mei_cl_is_other_connecting - checks if other
436 * client with the same me client id is connecting
Tomas Winkler9ca90502013-01-08 23:07:13 +0200437 *
Tomas Winkler9ca90502013-01-08 23:07:13 +0200438 * @cl: private data of the file object
439 *
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200440 * returns ture if other client is connected, 0 - otherwise.
Tomas Winkler9ca90502013-01-08 23:07:13 +0200441 */
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200442bool mei_cl_is_other_connecting(struct mei_cl *cl)
Tomas Winkler9ca90502013-01-08 23:07:13 +0200443{
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200444 struct mei_device *dev;
445 struct mei_cl *pos;
446 struct mei_cl *next;
Tomas Winkler9ca90502013-01-08 23:07:13 +0200447
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200448 if (WARN_ON(!cl || !cl->dev))
449 return false;
450
451 dev = cl->dev;
452
453 list_for_each_entry_safe(pos, next, &dev->file_list, link) {
454 if ((pos->state == MEI_FILE_CONNECTING) &&
455 (pos != cl) && cl->me_client_id == pos->me_client_id)
456 return true;
Tomas Winkler9ca90502013-01-08 23:07:13 +0200457
458 }
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200459
460 return false;
Tomas Winkler9ca90502013-01-08 23:07:13 +0200461}
462
463/**
Tomas Winkler9f81abda2013-01-08 23:07:15 +0200464 * mei_cl_connect - connect host clinet to the me one
465 *
466 * @cl: host client
467 *
468 * Locking: called under "dev->device_lock" lock
469 *
470 * returns 0 on success, <0 on failure.
471 */
472int mei_cl_connect(struct mei_cl *cl, struct file *file)
473{
474 struct mei_device *dev;
475 struct mei_cl_cb *cb;
476 long timeout = mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT);
477 int rets;
478
479 if (WARN_ON(!cl || !cl->dev))
480 return -ENODEV;
481
482 dev = cl->dev;
483
484 cb = mei_io_cb_init(cl, file);
485 if (!cb) {
486 rets = -ENOMEM;
487 goto out;
488 }
489
490 cb->fop_type = MEI_FOP_IOCTL;
491
492 if (dev->mei_host_buffer_is_empty &&
493 !mei_cl_is_other_connecting(cl)) {
494 dev->mei_host_buffer_is_empty = false;
495
496 if (mei_hbm_cl_connect_req(dev, cl)) {
497 rets = -ENODEV;
498 goto out;
499 }
500 cl->timer_count = MEI_CONNECT_TIMEOUT;
501 list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
502 } else {
503 list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
504 }
505
506 mutex_unlock(&dev->device_lock);
507 rets = wait_event_timeout(dev->wait_recvd_msg,
508 (cl->state == MEI_FILE_CONNECTED ||
509 cl->state == MEI_FILE_DISCONNECTED),
510 timeout * HZ);
511 mutex_lock(&dev->device_lock);
512
513 if (cl->state != MEI_FILE_CONNECTED) {
514 rets = -EFAULT;
515
516 mei_io_list_flush(&dev->ctrl_rd_list, cl);
517 mei_io_list_flush(&dev->ctrl_wr_list, cl);
518 goto out;
519 }
520
521 rets = cl->status;
522
523out:
524 mei_io_cb_free(cb);
525 return rets;
526}
527
528/**
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200529 * mei_cl_flow_ctrl_creds - checks flow_control credits for cl.
Tomas Winkler9ca90502013-01-08 23:07:13 +0200530 *
531 * @dev: the device structure
532 * @cl: private data of the file object
533 *
534 * returns 1 if mei_flow_ctrl_creds >0, 0 - otherwise.
535 * -ENOENT if mei_cl is not present
536 * -EINVAL if single_recv_buf == 0
537 */
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200538int mei_cl_flow_ctrl_creds(struct mei_cl *cl)
Tomas Winkler9ca90502013-01-08 23:07:13 +0200539{
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200540 struct mei_device *dev;
Tomas Winkler9ca90502013-01-08 23:07:13 +0200541 int i;
542
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200543 if (WARN_ON(!cl || !cl->dev))
544 return -EINVAL;
545
546 dev = cl->dev;
547
Tomas Winkler9ca90502013-01-08 23:07:13 +0200548 if (!dev->me_clients_num)
549 return 0;
550
551 if (cl->mei_flow_ctrl_creds > 0)
552 return 1;
553
554 for (i = 0; i < dev->me_clients_num; i++) {
555 struct mei_me_client *me_cl = &dev->me_clients[i];
556 if (me_cl->client_id == cl->me_client_id) {
557 if (me_cl->mei_flow_ctrl_creds) {
558 if (WARN_ON(me_cl->props.single_recv_buf == 0))
559 return -EINVAL;
560 return 1;
561 } else {
562 return 0;
563 }
564 }
565 }
566 return -ENOENT;
567}
568
569/**
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200570 * mei_cl_flow_ctrl_reduce - reduces flow_control.
Tomas Winkler9ca90502013-01-08 23:07:13 +0200571 *
572 * @dev: the device structure
573 * @cl: private data of the file object
574 * @returns
575 * 0 on success
576 * -ENOENT when me client is not found
577 * -EINVAL when ctrl credits are <= 0
578 */
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200579int mei_cl_flow_ctrl_reduce(struct mei_cl *cl)
Tomas Winkler9ca90502013-01-08 23:07:13 +0200580{
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200581 struct mei_device *dev;
Tomas Winkler9ca90502013-01-08 23:07:13 +0200582 int i;
583
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200584 if (WARN_ON(!cl || !cl->dev))
585 return -EINVAL;
586
587 dev = cl->dev;
588
Tomas Winkler9ca90502013-01-08 23:07:13 +0200589 if (!dev->me_clients_num)
590 return -ENOENT;
591
592 for (i = 0; i < dev->me_clients_num; i++) {
593 struct mei_me_client *me_cl = &dev->me_clients[i];
594 if (me_cl->client_id == cl->me_client_id) {
595 if (me_cl->props.single_recv_buf != 0) {
596 if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0))
597 return -EINVAL;
598 dev->me_clients[i].mei_flow_ctrl_creds--;
599 } else {
600 if (WARN_ON(cl->mei_flow_ctrl_creds <= 0))
601 return -EINVAL;
602 cl->mei_flow_ctrl_creds--;
603 }
604 return 0;
605 }
606 }
607 return -ENOENT;
608}
609
Tomas Winkler9ca90502013-01-08 23:07:13 +0200610/**
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200611 * mei_cl_start_read - the start read client message function.
Tomas Winkler9ca90502013-01-08 23:07:13 +0200612 *
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200613 * @cl: host client
Tomas Winkler9ca90502013-01-08 23:07:13 +0200614 *
615 * returns 0 on success, <0 on failure.
616 */
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200617int mei_cl_read_start(struct mei_cl *cl)
Tomas Winkler9ca90502013-01-08 23:07:13 +0200618{
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200619 struct mei_device *dev;
Tomas Winkler9ca90502013-01-08 23:07:13 +0200620 struct mei_cl_cb *cb;
621 int rets;
622 int i;
623
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200624 if (WARN_ON(!cl || !cl->dev))
625 return -ENODEV;
626
627 dev = cl->dev;
628
Tomas Winkler9ca90502013-01-08 23:07:13 +0200629 if (cl->state != MEI_FILE_CONNECTED)
630 return -ENODEV;
631
632 if (dev->dev_state != MEI_DEV_ENABLED)
633 return -ENODEV;
634
635 if (cl->read_pending || cl->read_cb) {
636 dev_dbg(&dev->pdev->dev, "read is pending.\n");
637 return -EBUSY;
638 }
639 i = mei_me_cl_by_id(dev, cl->me_client_id);
640 if (i < 0) {
641 dev_err(&dev->pdev->dev, "no such me client %d\n",
642 cl->me_client_id);
643 return -ENODEV;
644 }
645
646 cb = mei_io_cb_init(cl, NULL);
647 if (!cb)
648 return -ENOMEM;
649
650 rets = mei_io_cb_alloc_resp_buf(cb,
651 dev->me_clients[i].props.max_msg_length);
652 if (rets)
653 goto err;
654
655 cb->fop_type = MEI_FOP_READ;
656 cl->read_cb = cb;
657 if (dev->mei_host_buffer_is_empty) {
658 dev->mei_host_buffer_is_empty = false;
659 if (mei_hbm_cl_flow_control_req(dev, cl)) {
660 rets = -ENODEV;
661 goto err;
662 }
663 list_add_tail(&cb->list, &dev->read_list.list);
664 } else {
665 list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
666 }
667 return rets;
668err:
669 mei_io_cb_free(cb);
670 return rets;
671}
672