]>
Commit | Line | Data |
---|---|---|
ab841160 OW |
1 | /* |
2 | * | |
3 | * Intel Management Engine Interface (Intel MEI) Linux driver | |
733ba91c | 4 | * Copyright (c) 2003-2012, Intel Corporation. |
ab841160 OW |
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 | ||
ab841160 | 17 | #include <linux/pci.h> |
ab841160 | 18 | #include <linux/sched.h> |
9ca9050b TW |
19 | #include <linux/wait.h> |
20 | #include <linux/delay.h> | |
ab841160 | 21 | |
4f3afe1d | 22 | #include <linux/mei.h> |
47a73801 TW |
23 | |
24 | #include "mei_dev.h" | |
0edb23fc | 25 | #include "hbm.h" |
90e0b5f1 TW |
26 | #include "client.h" |
27 | ||
28 | /** | |
29 | * mei_me_cl_by_uuid - locate index of me client | |
30 | * | |
31 | * @dev: mei device | |
32 | * returns me client index or -ENOENT if not found | |
33 | */ | |
34 | int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid) | |
35 | { | |
36 | int i, res = -ENOENT; | |
37 | ||
38 | for (i = 0; i < dev->me_clients_num; ++i) | |
39 | if (uuid_le_cmp(*uuid, | |
40 | dev->me_clients[i].props.protocol_name) == 0) { | |
41 | res = i; | |
42 | break; | |
43 | } | |
44 | ||
45 | return res; | |
46 | } | |
47 | ||
48 | ||
49 | /** | |
50 | * mei_me_cl_by_id return index to me_clients for client_id | |
51 | * | |
52 | * @dev: the device structure | |
53 | * @client_id: me client id | |
54 | * | |
55 | * Locking: called under "dev->device_lock" lock | |
56 | * | |
57 | * returns index on success, -ENOENT on failure. | |
58 | */ | |
59 | ||
60 | int mei_me_cl_by_id(struct mei_device *dev, u8 client_id) | |
61 | { | |
62 | int i; | |
63 | for (i = 0; i < dev->me_clients_num; i++) | |
64 | if (dev->me_clients[i].client_id == client_id) | |
65 | break; | |
66 | if (WARN_ON(dev->me_clients[i].client_id != client_id)) | |
67 | return -ENOENT; | |
68 | ||
69 | if (i == dev->me_clients_num) | |
70 | return -ENOENT; | |
71 | ||
72 | return i; | |
73 | } | |
ab841160 | 74 | |
9ca9050b TW |
75 | |
76 | /** | |
77 | * mei_io_list_flush - removes list entry belonging to cl. | |
78 | * | |
79 | * @list: An instance of our list structure | |
80 | * @cl: host client | |
81 | */ | |
82 | void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) | |
83 | { | |
84 | struct mei_cl_cb *cb; | |
85 | struct mei_cl_cb *next; | |
86 | ||
87 | list_for_each_entry_safe(cb, next, &list->list, list) { | |
88 | if (cb->cl && mei_cl_cmp_id(cl, cb->cl)) | |
89 | list_del(&cb->list); | |
90 | } | |
91 | } | |
92 | ||
601a1efa TW |
93 | /** |
94 | * mei_io_cb_free - free mei_cb_private related memory | |
95 | * | |
96 | * @cb: mei callback struct | |
97 | */ | |
98 | void mei_io_cb_free(struct mei_cl_cb *cb) | |
99 | { | |
100 | if (cb == NULL) | |
101 | return; | |
102 | ||
103 | kfree(cb->request_buffer.data); | |
104 | kfree(cb->response_buffer.data); | |
105 | kfree(cb); | |
106 | } | |
9ca9050b | 107 | |
664df38b TW |
108 | /** |
109 | * mei_io_cb_init - allocate and initialize io callback | |
110 | * | |
111 | * @cl - mei client | |
393b148f | 112 | * @fp: pointer to file structure |
664df38b TW |
113 | * |
114 | * returns mei_cl_cb pointer or NULL; | |
115 | */ | |
116 | struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp) | |
117 | { | |
118 | struct mei_cl_cb *cb; | |
119 | ||
120 | cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); | |
121 | if (!cb) | |
122 | return NULL; | |
123 | ||
124 | mei_io_list_init(cb); | |
125 | ||
126 | cb->file_object = fp; | |
db3ed431 | 127 | cb->cl = cl; |
664df38b TW |
128 | cb->buf_idx = 0; |
129 | return cb; | |
130 | } | |
131 | ||
664df38b TW |
132 | /** |
133 | * mei_io_cb_alloc_req_buf - allocate request buffer | |
134 | * | |
393b148f MI |
135 | * @cb: io callback structure |
136 | * @length: size of the buffer | |
664df38b TW |
137 | * |
138 | * returns 0 on success | |
139 | * -EINVAL if cb is NULL | |
140 | * -ENOMEM if allocation failed | |
141 | */ | |
142 | int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length) | |
143 | { | |
144 | if (!cb) | |
145 | return -EINVAL; | |
146 | ||
147 | if (length == 0) | |
148 | return 0; | |
149 | ||
150 | cb->request_buffer.data = kmalloc(length, GFP_KERNEL); | |
151 | if (!cb->request_buffer.data) | |
152 | return -ENOMEM; | |
153 | cb->request_buffer.size = length; | |
154 | return 0; | |
155 | } | |
156 | /** | |
83ce0741 | 157 | * mei_io_cb_alloc_resp_buf - allocate response buffer |
664df38b | 158 | * |
393b148f MI |
159 | * @cb: io callback structure |
160 | * @length: size of the buffer | |
664df38b TW |
161 | * |
162 | * returns 0 on success | |
163 | * -EINVAL if cb is NULL | |
164 | * -ENOMEM if allocation failed | |
165 | */ | |
166 | int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length) | |
167 | { | |
168 | if (!cb) | |
169 | return -EINVAL; | |
170 | ||
171 | if (length == 0) | |
172 | return 0; | |
173 | ||
174 | cb->response_buffer.data = kmalloc(length, GFP_KERNEL); | |
175 | if (!cb->response_buffer.data) | |
176 | return -ENOMEM; | |
177 | cb->response_buffer.size = length; | |
178 | return 0; | |
179 | } | |
180 | ||
601a1efa | 181 | |
9ca9050b TW |
182 | |
183 | /** | |
184 | * mei_cl_flush_queues - flushes queue lists belonging to cl. | |
185 | * | |
9ca9050b TW |
186 | * @cl: host client |
187 | */ | |
188 | int mei_cl_flush_queues(struct mei_cl *cl) | |
189 | { | |
c0abffbd AU |
190 | struct mei_device *dev; |
191 | ||
90e0b5f1 | 192 | if (WARN_ON(!cl || !cl->dev)) |
9ca9050b TW |
193 | return -EINVAL; |
194 | ||
c0abffbd AU |
195 | dev = cl->dev; |
196 | ||
197 | cl_dbg(dev, cl, "remove list entry belonging to cl\n"); | |
9ca9050b TW |
198 | mei_io_list_flush(&cl->dev->read_list, cl); |
199 | mei_io_list_flush(&cl->dev->write_list, cl); | |
200 | mei_io_list_flush(&cl->dev->write_waiting_list, cl); | |
201 | mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); | |
202 | mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); | |
203 | mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); | |
204 | mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl); | |
205 | return 0; | |
206 | } | |
207 | ||
ab841160 | 208 | |
9ca9050b | 209 | /** |
83ce0741 | 210 | * mei_cl_init - initializes cl. |
9ca9050b TW |
211 | * |
212 | * @cl: host client to be initialized | |
213 | * @dev: mei device | |
214 | */ | |
215 | void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) | |
216 | { | |
217 | memset(cl, 0, sizeof(struct mei_cl)); | |
218 | init_waitqueue_head(&cl->wait); | |
219 | init_waitqueue_head(&cl->rx_wait); | |
220 | init_waitqueue_head(&cl->tx_wait); | |
221 | INIT_LIST_HEAD(&cl->link); | |
a7b71bc0 | 222 | INIT_LIST_HEAD(&cl->device_link); |
9ca9050b TW |
223 | cl->reading_state = MEI_IDLE; |
224 | cl->writing_state = MEI_IDLE; | |
225 | cl->dev = dev; | |
226 | } | |
227 | ||
228 | /** | |
229 | * mei_cl_allocate - allocates cl structure and sets it up. | |
230 | * | |
231 | * @dev: mei device | |
232 | * returns The allocated file or NULL on failure | |
233 | */ | |
234 | struct mei_cl *mei_cl_allocate(struct mei_device *dev) | |
235 | { | |
236 | struct mei_cl *cl; | |
237 | ||
238 | cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL); | |
239 | if (!cl) | |
240 | return NULL; | |
241 | ||
242 | mei_cl_init(cl, dev); | |
243 | ||
244 | return cl; | |
245 | } | |
246 | ||
90e0b5f1 TW |
247 | /** |
248 | * mei_cl_find_read_cb - find this cl's callback in the read list | |
249 | * | |
393b148f MI |
250 | * @cl: host client |
251 | * | |
90e0b5f1 TW |
252 | * returns cb on success, NULL on error |
253 | */ | |
254 | struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl) | |
255 | { | |
256 | struct mei_device *dev = cl->dev; | |
257 | struct mei_cl_cb *cb = NULL; | |
258 | struct mei_cl_cb *next = NULL; | |
259 | ||
260 | list_for_each_entry_safe(cb, next, &dev->read_list.list, list) | |
261 | if (mei_cl_cmp_id(cl, cb->cl)) | |
262 | return cb; | |
263 | return NULL; | |
264 | } | |
265 | ||
83ce0741 | 266 | /** mei_cl_link: allocate host id in the host map |
9ca9050b | 267 | * |
781d0d89 | 268 | * @cl - host client |
83ce0741 | 269 | * @id - fixed host id or -1 for generic one |
393b148f | 270 | * |
781d0d89 | 271 | * returns 0 on success |
9ca9050b TW |
272 | * -EINVAL on incorrect values |
273 | * -ENONET if client not found | |
274 | */ | |
781d0d89 | 275 | int mei_cl_link(struct mei_cl *cl, int id) |
9ca9050b | 276 | { |
90e0b5f1 | 277 | struct mei_device *dev; |
22f96a0e | 278 | long open_handle_count; |
9ca9050b | 279 | |
781d0d89 | 280 | if (WARN_ON(!cl || !cl->dev)) |
9ca9050b TW |
281 | return -EINVAL; |
282 | ||
90e0b5f1 TW |
283 | dev = cl->dev; |
284 | ||
83ce0741 | 285 | /* If Id is not assigned get one*/ |
781d0d89 TW |
286 | if (id == MEI_HOST_CLIENT_ID_ANY) |
287 | id = find_first_zero_bit(dev->host_clients_map, | |
288 | MEI_CLIENTS_MAX); | |
9ca9050b | 289 | |
781d0d89 | 290 | if (id >= MEI_CLIENTS_MAX) { |
83ce0741 | 291 | dev_err(&dev->pdev->dev, "id exceeded %d", MEI_CLIENTS_MAX); |
e036cc57 TW |
292 | return -EMFILE; |
293 | } | |
294 | ||
22f96a0e TW |
295 | open_handle_count = dev->open_handle_count + dev->iamthif_open_count; |
296 | if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) { | |
83ce0741 | 297 | dev_err(&dev->pdev->dev, "open_handle_count exceeded %d", |
e036cc57 TW |
298 | MEI_MAX_OPEN_HANDLE_COUNT); |
299 | return -EMFILE; | |
9ca9050b TW |
300 | } |
301 | ||
781d0d89 TW |
302 | dev->open_handle_count++; |
303 | ||
304 | cl->host_client_id = id; | |
305 | list_add_tail(&cl->link, &dev->file_list); | |
306 | ||
307 | set_bit(id, dev->host_clients_map); | |
308 | ||
309 | cl->state = MEI_FILE_INITIALIZING; | |
310 | ||
c0abffbd | 311 | cl_dbg(dev, cl, "link cl\n"); |
781d0d89 | 312 | return 0; |
9ca9050b | 313 | } |
781d0d89 | 314 | |
9ca9050b | 315 | /** |
90e0b5f1 | 316 | * mei_cl_unlink - remove me_cl from the list |
9ca9050b | 317 | * |
393b148f | 318 | * @cl: host client |
9ca9050b | 319 | */ |
90e0b5f1 | 320 | int mei_cl_unlink(struct mei_cl *cl) |
9ca9050b | 321 | { |
90e0b5f1 | 322 | struct mei_device *dev; |
90e0b5f1 | 323 | |
781d0d89 TW |
324 | /* don't shout on error exit path */ |
325 | if (!cl) | |
326 | return 0; | |
327 | ||
8e9a4a9a TW |
328 | /* wd and amthif might not be initialized */ |
329 | if (!cl->dev) | |
330 | return 0; | |
90e0b5f1 TW |
331 | |
332 | dev = cl->dev; | |
333 | ||
a14c44d8 TW |
334 | cl_dbg(dev, cl, "unlink client"); |
335 | ||
22f96a0e TW |
336 | if (dev->open_handle_count > 0) |
337 | dev->open_handle_count--; | |
338 | ||
339 | /* never clear the 0 bit */ | |
340 | if (cl->host_client_id) | |
341 | clear_bit(cl->host_client_id, dev->host_clients_map); | |
342 | ||
343 | list_del_init(&cl->link); | |
344 | ||
345 | cl->state = MEI_FILE_INITIALIZING; | |
346 | ||
90e0b5f1 | 347 | return 0; |
9ca9050b TW |
348 | } |
349 | ||
350 | ||
351 | void mei_host_client_init(struct work_struct *work) | |
352 | { | |
353 | struct mei_device *dev = container_of(work, | |
354 | struct mei_device, init_work); | |
355 | struct mei_client_properties *client_props; | |
356 | int i; | |
357 | ||
358 | mutex_lock(&dev->device_lock); | |
359 | ||
9ca9050b TW |
360 | for (i = 0; i < dev->me_clients_num; i++) { |
361 | client_props = &dev->me_clients[i].props; | |
362 | ||
1a1aca42 | 363 | if (!uuid_le_cmp(client_props->protocol_name, mei_amthif_guid)) |
9ca9050b TW |
364 | mei_amthif_host_init(dev); |
365 | else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid)) | |
366 | mei_wd_host_init(dev); | |
59fcd7c6 SO |
367 | else if (!uuid_le_cmp(client_props->protocol_name, mei_nfc_guid)) |
368 | mei_nfc_host_init(dev); | |
369 | ||
9ca9050b TW |
370 | } |
371 | ||
372 | dev->dev_state = MEI_DEV_ENABLED; | |
6adb8efb | 373 | dev->reset_count = 0; |
9ca9050b TW |
374 | |
375 | mutex_unlock(&dev->device_lock); | |
376 | } | |
377 | ||
378 | ||
379 | /** | |
83ce0741 | 380 | * mei_cl_disconnect - disconnect host client from the me one |
9ca9050b | 381 | * |
90e0b5f1 | 382 | * @cl: host client |
9ca9050b TW |
383 | * |
384 | * Locking: called under "dev->device_lock" lock | |
385 | * | |
386 | * returns 0 on success, <0 on failure. | |
387 | */ | |
90e0b5f1 | 388 | int mei_cl_disconnect(struct mei_cl *cl) |
9ca9050b | 389 | { |
90e0b5f1 | 390 | struct mei_device *dev; |
9ca9050b TW |
391 | struct mei_cl_cb *cb; |
392 | int rets, err; | |
393 | ||
90e0b5f1 | 394 | if (WARN_ON(!cl || !cl->dev)) |
9ca9050b TW |
395 | return -ENODEV; |
396 | ||
90e0b5f1 TW |
397 | dev = cl->dev; |
398 | ||
c0abffbd AU |
399 | cl_dbg(dev, cl, "disconnecting"); |
400 | ||
9ca9050b TW |
401 | if (cl->state != MEI_FILE_DISCONNECTING) |
402 | return 0; | |
403 | ||
404 | cb = mei_io_cb_init(cl, NULL); | |
405 | if (!cb) | |
406 | return -ENOMEM; | |
407 | ||
408 | cb->fop_type = MEI_FOP_CLOSE; | |
330dd7da TW |
409 | if (dev->hbuf_is_ready) { |
410 | dev->hbuf_is_ready = false; | |
9ca9050b TW |
411 | if (mei_hbm_cl_disconnect_req(dev, cl)) { |
412 | rets = -ENODEV; | |
c0abffbd | 413 | cl_err(dev, cl, "failed to disconnect.\n"); |
9ca9050b TW |
414 | goto free; |
415 | } | |
416 | mdelay(10); /* Wait for hardware disconnection ready */ | |
417 | list_add_tail(&cb->list, &dev->ctrl_rd_list.list); | |
418 | } else { | |
c0abffbd | 419 | cl_dbg(dev, cl, "add disconnect cb to control write list\n"); |
9ca9050b TW |
420 | list_add_tail(&cb->list, &dev->ctrl_wr_list.list); |
421 | ||
422 | } | |
423 | mutex_unlock(&dev->device_lock); | |
424 | ||
425 | err = wait_event_timeout(dev->wait_recvd_msg, | |
426 | MEI_FILE_DISCONNECTED == cl->state, | |
427 | mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); | |
428 | ||
429 | mutex_lock(&dev->device_lock); | |
430 | if (MEI_FILE_DISCONNECTED == cl->state) { | |
431 | rets = 0; | |
c0abffbd | 432 | cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); |
9ca9050b TW |
433 | } else { |
434 | rets = -ENODEV; | |
435 | if (MEI_FILE_DISCONNECTED != cl->state) | |
c0abffbd | 436 | cl_err(dev, cl, "wrong status client disconnect.\n"); |
9ca9050b TW |
437 | |
438 | if (err) | |
c0abffbd | 439 | cl_dbg(dev, cl, "wait failed disconnect err=%08x\n", |
9ca9050b TW |
440 | err); |
441 | ||
c0abffbd | 442 | cl_err(dev, cl, "failed to disconnect from FW client.\n"); |
9ca9050b TW |
443 | } |
444 | ||
445 | mei_io_list_flush(&dev->ctrl_rd_list, cl); | |
446 | mei_io_list_flush(&dev->ctrl_wr_list, cl); | |
447 | free: | |
448 | mei_io_cb_free(cb); | |
449 | return rets; | |
450 | } | |
451 | ||
452 | ||
453 | /** | |
90e0b5f1 TW |
454 | * mei_cl_is_other_connecting - checks if other |
455 | * client with the same me client id is connecting | |
9ca9050b | 456 | * |
9ca9050b TW |
457 | * @cl: private data of the file object |
458 | * | |
83ce0741 | 459 | * returns true if other client is connected, false - otherwise. |
9ca9050b | 460 | */ |
90e0b5f1 | 461 | bool mei_cl_is_other_connecting(struct mei_cl *cl) |
9ca9050b | 462 | { |
90e0b5f1 TW |
463 | struct mei_device *dev; |
464 | struct mei_cl *pos; | |
465 | struct mei_cl *next; | |
9ca9050b | 466 | |
90e0b5f1 TW |
467 | if (WARN_ON(!cl || !cl->dev)) |
468 | return false; | |
469 | ||
470 | dev = cl->dev; | |
471 | ||
472 | list_for_each_entry_safe(pos, next, &dev->file_list, link) { | |
473 | if ((pos->state == MEI_FILE_CONNECTING) && | |
474 | (pos != cl) && cl->me_client_id == pos->me_client_id) | |
475 | return true; | |
9ca9050b TW |
476 | |
477 | } | |
90e0b5f1 TW |
478 | |
479 | return false; | |
9ca9050b TW |
480 | } |
481 | ||
9f81abda | 482 | /** |
83ce0741 | 483 | * mei_cl_connect - connect host client to the me one |
9f81abda TW |
484 | * |
485 | * @cl: host client | |
486 | * | |
487 | * Locking: called under "dev->device_lock" lock | |
488 | * | |
489 | * returns 0 on success, <0 on failure. | |
490 | */ | |
491 | int mei_cl_connect(struct mei_cl *cl, struct file *file) | |
492 | { | |
493 | struct mei_device *dev; | |
494 | struct mei_cl_cb *cb; | |
9f81abda TW |
495 | int rets; |
496 | ||
497 | if (WARN_ON(!cl || !cl->dev)) | |
498 | return -ENODEV; | |
499 | ||
500 | dev = cl->dev; | |
501 | ||
502 | cb = mei_io_cb_init(cl, file); | |
503 | if (!cb) { | |
504 | rets = -ENOMEM; | |
505 | goto out; | |
506 | } | |
507 | ||
02a7eecc | 508 | cb->fop_type = MEI_FOP_CONNECT; |
9f81abda | 509 | |
330dd7da TW |
510 | if (dev->hbuf_is_ready && !mei_cl_is_other_connecting(cl)) { |
511 | dev->hbuf_is_ready = false; | |
9f81abda TW |
512 | |
513 | if (mei_hbm_cl_connect_req(dev, cl)) { | |
514 | rets = -ENODEV; | |
515 | goto out; | |
516 | } | |
517 | cl->timer_count = MEI_CONNECT_TIMEOUT; | |
518 | list_add_tail(&cb->list, &dev->ctrl_rd_list.list); | |
519 | } else { | |
520 | list_add_tail(&cb->list, &dev->ctrl_wr_list.list); | |
521 | } | |
522 | ||
523 | mutex_unlock(&dev->device_lock); | |
524 | rets = wait_event_timeout(dev->wait_recvd_msg, | |
525 | (cl->state == MEI_FILE_CONNECTED || | |
526 | cl->state == MEI_FILE_DISCONNECTED), | |
206ecfc2 | 527 | mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); |
9f81abda TW |
528 | mutex_lock(&dev->device_lock); |
529 | ||
530 | if (cl->state != MEI_FILE_CONNECTED) { | |
531 | rets = -EFAULT; | |
532 | ||
533 | mei_io_list_flush(&dev->ctrl_rd_list, cl); | |
534 | mei_io_list_flush(&dev->ctrl_wr_list, cl); | |
535 | goto out; | |
536 | } | |
537 | ||
538 | rets = cl->status; | |
539 | ||
540 | out: | |
541 | mei_io_cb_free(cb); | |
542 | return rets; | |
543 | } | |
544 | ||
9ca9050b | 545 | /** |
90e0b5f1 | 546 | * mei_cl_flow_ctrl_creds - checks flow_control credits for cl. |
9ca9050b | 547 | * |
9ca9050b TW |
548 | * @cl: private data of the file object |
549 | * | |
550 | * returns 1 if mei_flow_ctrl_creds >0, 0 - otherwise. | |
551 | * -ENOENT if mei_cl is not present | |
552 | * -EINVAL if single_recv_buf == 0 | |
553 | */ | |
90e0b5f1 | 554 | int mei_cl_flow_ctrl_creds(struct mei_cl *cl) |
9ca9050b | 555 | { |
90e0b5f1 | 556 | struct mei_device *dev; |
9ca9050b TW |
557 | int i; |
558 | ||
90e0b5f1 TW |
559 | if (WARN_ON(!cl || !cl->dev)) |
560 | return -EINVAL; | |
561 | ||
562 | dev = cl->dev; | |
563 | ||
9ca9050b TW |
564 | if (!dev->me_clients_num) |
565 | return 0; | |
566 | ||
567 | if (cl->mei_flow_ctrl_creds > 0) | |
568 | return 1; | |
569 | ||
570 | for (i = 0; i < dev->me_clients_num; i++) { | |
571 | struct mei_me_client *me_cl = &dev->me_clients[i]; | |
572 | if (me_cl->client_id == cl->me_client_id) { | |
573 | if (me_cl->mei_flow_ctrl_creds) { | |
574 | if (WARN_ON(me_cl->props.single_recv_buf == 0)) | |
575 | return -EINVAL; | |
576 | return 1; | |
577 | } else { | |
578 | return 0; | |
579 | } | |
580 | } | |
581 | } | |
582 | return -ENOENT; | |
583 | } | |
584 | ||
585 | /** | |
90e0b5f1 | 586 | * mei_cl_flow_ctrl_reduce - reduces flow_control. |
9ca9050b | 587 | * |
9ca9050b | 588 | * @cl: private data of the file object |
393b148f | 589 | * |
9ca9050b TW |
590 | * @returns |
591 | * 0 on success | |
592 | * -ENOENT when me client is not found | |
593 | * -EINVAL when ctrl credits are <= 0 | |
594 | */ | |
90e0b5f1 | 595 | int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) |
9ca9050b | 596 | { |
90e0b5f1 | 597 | struct mei_device *dev; |
9ca9050b TW |
598 | int i; |
599 | ||
90e0b5f1 TW |
600 | if (WARN_ON(!cl || !cl->dev)) |
601 | return -EINVAL; | |
602 | ||
603 | dev = cl->dev; | |
604 | ||
9ca9050b TW |
605 | if (!dev->me_clients_num) |
606 | return -ENOENT; | |
607 | ||
608 | for (i = 0; i < dev->me_clients_num; i++) { | |
609 | struct mei_me_client *me_cl = &dev->me_clients[i]; | |
610 | if (me_cl->client_id == cl->me_client_id) { | |
611 | if (me_cl->props.single_recv_buf != 0) { | |
612 | if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) | |
613 | return -EINVAL; | |
614 | dev->me_clients[i].mei_flow_ctrl_creds--; | |
615 | } else { | |
616 | if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) | |
617 | return -EINVAL; | |
618 | cl->mei_flow_ctrl_creds--; | |
619 | } | |
620 | return 0; | |
621 | } | |
622 | } | |
623 | return -ENOENT; | |
624 | } | |
625 | ||
ab841160 | 626 | /** |
393b148f | 627 | * mei_cl_read_start - the start read client message function. |
ab841160 | 628 | * |
90e0b5f1 | 629 | * @cl: host client |
ab841160 OW |
630 | * |
631 | * returns 0 on success, <0 on failure. | |
632 | */ | |
fcb136e1 | 633 | int mei_cl_read_start(struct mei_cl *cl, size_t length) |
ab841160 | 634 | { |
90e0b5f1 | 635 | struct mei_device *dev; |
ab841160 | 636 | struct mei_cl_cb *cb; |
664df38b | 637 | int rets; |
ab841160 OW |
638 | int i; |
639 | ||
90e0b5f1 TW |
640 | if (WARN_ON(!cl || !cl->dev)) |
641 | return -ENODEV; | |
642 | ||
643 | dev = cl->dev; | |
644 | ||
b950ac1d | 645 | if (!mei_cl_is_connected(cl)) |
ab841160 OW |
646 | return -ENODEV; |
647 | ||
d91aaed3 | 648 | if (cl->read_cb) { |
c0abffbd | 649 | cl_dbg(dev, cl, "read is pending.\n"); |
ab841160 OW |
650 | return -EBUSY; |
651 | } | |
664df38b TW |
652 | i = mei_me_cl_by_id(dev, cl->me_client_id); |
653 | if (i < 0) { | |
c0abffbd | 654 | cl_err(dev, cl, "no such me client %d\n", cl->me_client_id); |
664df38b TW |
655 | return -ENODEV; |
656 | } | |
ab841160 | 657 | |
664df38b | 658 | cb = mei_io_cb_init(cl, NULL); |
ab841160 OW |
659 | if (!cb) |
660 | return -ENOMEM; | |
661 | ||
fcb136e1 TW |
662 | /* always allocate at least client max message */ |
663 | length = max_t(size_t, length, dev->me_clients[i].props.max_msg_length); | |
664 | rets = mei_io_cb_alloc_resp_buf(cb, length); | |
664df38b TW |
665 | if (rets) |
666 | goto err; | |
ab841160 | 667 | |
4b8960b4 | 668 | cb->fop_type = MEI_FOP_READ; |
ab841160 | 669 | cl->read_cb = cb; |
330dd7da TW |
670 | if (dev->hbuf_is_ready) { |
671 | dev->hbuf_is_ready = false; | |
8120e720 | 672 | if (mei_hbm_cl_flow_control_req(dev, cl)) { |
c0abffbd | 673 | cl_err(dev, cl, "flow control send failed\n"); |
ab841160 | 674 | rets = -ENODEV; |
664df38b | 675 | goto err; |
ab841160 | 676 | } |
fb601adb | 677 | list_add_tail(&cb->list, &dev->read_list.list); |
ab841160 | 678 | } else { |
fb601adb | 679 | list_add_tail(&cb->list, &dev->ctrl_wr_list.list); |
ab841160 OW |
680 | } |
681 | return rets; | |
664df38b | 682 | err: |
601a1efa | 683 | mei_io_cb_free(cb); |
ab841160 OW |
684 | return rets; |
685 | } | |
686 | ||
21767546 TW |
687 | /** |
688 | * mei_cl_irq_write_complete - write a message to device | |
689 | * from the interrupt thread context | |
690 | * | |
691 | * @cl: client | |
692 | * @cb: callback block. | |
693 | * @slots: free slots. | |
694 | * @cmpl_list: complete list. | |
695 | * | |
696 | * returns 0, OK; otherwise error. | |
697 | */ | |
698 | int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb, | |
699 | s32 *slots, struct mei_cl_cb *cmpl_list) | |
700 | { | |
136698e5 TW |
701 | struct mei_device *dev; |
702 | struct mei_msg_data *buf; | |
21767546 | 703 | struct mei_msg_hdr mei_hdr; |
136698e5 TW |
704 | size_t len; |
705 | u32 msg_slots; | |
2ebf8c94 | 706 | int rets; |
21767546 | 707 | |
136698e5 TW |
708 | |
709 | if (WARN_ON(!cl || !cl->dev)) | |
710 | return -ENODEV; | |
711 | ||
712 | dev = cl->dev; | |
713 | ||
714 | buf = &cb->request_buffer; | |
715 | ||
716 | rets = mei_cl_flow_ctrl_creds(cl); | |
717 | if (rets < 0) | |
718 | return rets; | |
719 | ||
720 | if (rets == 0) { | |
721 | cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); | |
722 | return 0; | |
723 | } | |
724 | ||
725 | len = buf->size - cb->buf_idx; | |
726 | msg_slots = mei_data2slots(len); | |
727 | ||
21767546 TW |
728 | mei_hdr.host_addr = cl->host_client_id; |
729 | mei_hdr.me_addr = cl->me_client_id; | |
730 | mei_hdr.reserved = 0; | |
479327fc | 731 | mei_hdr.internal = cb->internal; |
21767546 TW |
732 | |
733 | if (*slots >= msg_slots) { | |
734 | mei_hdr.length = len; | |
735 | mei_hdr.msg_complete = 1; | |
736 | /* Split the message only if we can write the whole host buffer */ | |
737 | } else if (*slots == dev->hbuf_depth) { | |
738 | msg_slots = *slots; | |
739 | len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); | |
740 | mei_hdr.length = len; | |
741 | mei_hdr.msg_complete = 0; | |
742 | } else { | |
743 | /* wait for next time the host buffer is empty */ | |
744 | return 0; | |
745 | } | |
746 | ||
c0abffbd | 747 | cl_dbg(dev, cl, "buf: size = %d idx = %lu\n", |
21767546 | 748 | cb->request_buffer.size, cb->buf_idx); |
21767546 TW |
749 | |
750 | *slots -= msg_slots; | |
136698e5 | 751 | rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx); |
2ebf8c94 TW |
752 | if (rets) { |
753 | cl->status = rets; | |
21767546 | 754 | list_move_tail(&cb->list, &cmpl_list->list); |
2ebf8c94 | 755 | return rets; |
21767546 TW |
756 | } |
757 | ||
758 | cl->status = 0; | |
4dfaa9f7 | 759 | cl->writing_state = MEI_WRITING; |
21767546 | 760 | cb->buf_idx += mei_hdr.length; |
4dfaa9f7 | 761 | |
21767546 TW |
762 | if (mei_hdr.msg_complete) { |
763 | if (mei_cl_flow_ctrl_reduce(cl)) | |
2ebf8c94 | 764 | return -EIO; |
21767546 TW |
765 | list_move_tail(&cb->list, &dev->write_waiting_list.list); |
766 | } | |
767 | ||
768 | return 0; | |
769 | } | |
770 | ||
4234a6de TW |
771 | /** |
772 | * mei_cl_write - submit a write cb to mei device | |
773 | assumes device_lock is locked | |
774 | * | |
775 | * @cl: host client | |
776 | * @cl: write callback with filled data | |
777 | * | |
83ce0741 | 778 | * returns number of bytes sent on success, <0 on failure. |
4234a6de TW |
779 | */ |
780 | int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) | |
781 | { | |
782 | struct mei_device *dev; | |
783 | struct mei_msg_data *buf; | |
784 | struct mei_msg_hdr mei_hdr; | |
785 | int rets; | |
786 | ||
787 | ||
788 | if (WARN_ON(!cl || !cl->dev)) | |
789 | return -ENODEV; | |
790 | ||
791 | if (WARN_ON(!cb)) | |
792 | return -EINVAL; | |
793 | ||
794 | dev = cl->dev; | |
795 | ||
796 | ||
797 | buf = &cb->request_buffer; | |
798 | ||
c0abffbd | 799 | cl_dbg(dev, cl, "mei_cl_write %d\n", buf->size); |
4234a6de TW |
800 | |
801 | ||
802 | cb->fop_type = MEI_FOP_WRITE; | |
803 | ||
804 | rets = mei_cl_flow_ctrl_creds(cl); | |
805 | if (rets < 0) | |
806 | goto err; | |
807 | ||
808 | /* Host buffer is not ready, we queue the request */ | |
809 | if (rets == 0 || !dev->hbuf_is_ready) { | |
810 | cb->buf_idx = 0; | |
811 | /* unseting complete will enqueue the cb for write */ | |
812 | mei_hdr.msg_complete = 0; | |
4234a6de TW |
813 | rets = buf->size; |
814 | goto out; | |
815 | } | |
816 | ||
817 | dev->hbuf_is_ready = false; | |
818 | ||
819 | /* Check for a maximum length */ | |
820 | if (buf->size > mei_hbuf_max_len(dev)) { | |
821 | mei_hdr.length = mei_hbuf_max_len(dev); | |
822 | mei_hdr.msg_complete = 0; | |
823 | } else { | |
824 | mei_hdr.length = buf->size; | |
825 | mei_hdr.msg_complete = 1; | |
826 | } | |
827 | ||
828 | mei_hdr.host_addr = cl->host_client_id; | |
829 | mei_hdr.me_addr = cl->me_client_id; | |
830 | mei_hdr.reserved = 0; | |
479327fc | 831 | mei_hdr.internal = cb->internal; |
4234a6de | 832 | |
4234a6de | 833 | |
2ebf8c94 TW |
834 | rets = mei_write_message(dev, &mei_hdr, buf->data); |
835 | if (rets) | |
4234a6de | 836 | goto err; |
4234a6de TW |
837 | |
838 | cl->writing_state = MEI_WRITING; | |
839 | cb->buf_idx = mei_hdr.length; | |
840 | ||
841 | rets = buf->size; | |
842 | out: | |
843 | if (mei_hdr.msg_complete) { | |
844 | if (mei_cl_flow_ctrl_reduce(cl)) { | |
845 | rets = -ENODEV; | |
846 | goto err; | |
847 | } | |
848 | list_add_tail(&cb->list, &dev->write_waiting_list.list); | |
849 | } else { | |
850 | list_add_tail(&cb->list, &dev->write_list.list); | |
851 | } | |
852 | ||
853 | ||
854 | if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { | |
855 | ||
856 | mutex_unlock(&dev->device_lock); | |
857 | if (wait_event_interruptible(cl->tx_wait, | |
858 | cl->writing_state == MEI_WRITE_COMPLETE)) { | |
859 | if (signal_pending(current)) | |
860 | rets = -EINTR; | |
861 | else | |
862 | rets = -ERESTARTSYS; | |
863 | } | |
864 | mutex_lock(&dev->device_lock); | |
865 | } | |
866 | err: | |
867 | return rets; | |
868 | } | |
869 | ||
870 | ||
db086fa9 TW |
871 | /** |
872 | * mei_cl_complete - processes completed operation for a client | |
873 | * | |
874 | * @cl: private data of the file object. | |
875 | * @cb: callback block. | |
876 | */ | |
877 | void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) | |
878 | { | |
879 | if (cb->fop_type == MEI_FOP_WRITE) { | |
880 | mei_io_cb_free(cb); | |
881 | cb = NULL; | |
882 | cl->writing_state = MEI_WRITE_COMPLETE; | |
883 | if (waitqueue_active(&cl->tx_wait)) | |
884 | wake_up_interruptible(&cl->tx_wait); | |
885 | ||
886 | } else if (cb->fop_type == MEI_FOP_READ && | |
887 | MEI_READING == cl->reading_state) { | |
888 | cl->reading_state = MEI_READ_COMPLETE; | |
889 | if (waitqueue_active(&cl->rx_wait)) | |
890 | wake_up_interruptible(&cl->rx_wait); | |
891 | else | |
892 | mei_cl_bus_rx_event(cl); | |
893 | ||
894 | } | |
895 | } | |
896 | ||
4234a6de | 897 | |
074b4c01 TW |
898 | /** |
899 | * mei_cl_all_disconnect - disconnect forcefully all connected clients | |
900 | * | |
901 | * @dev - mei device | |
902 | */ | |
903 | ||
904 | void mei_cl_all_disconnect(struct mei_device *dev) | |
905 | { | |
906 | struct mei_cl *cl, *next; | |
907 | ||
908 | list_for_each_entry_safe(cl, next, &dev->file_list, link) { | |
909 | cl->state = MEI_FILE_DISCONNECTED; | |
910 | cl->mei_flow_ctrl_creds = 0; | |
074b4c01 TW |
911 | cl->timer_count = 0; |
912 | } | |
913 | } | |
914 | ||
915 | ||
916 | /** | |
5290801c | 917 | * mei_cl_all_wakeup - wake up all readers and writers they can be interrupted |
074b4c01 TW |
918 | * |
919 | * @dev - mei device | |
920 | */ | |
5290801c | 921 | void mei_cl_all_wakeup(struct mei_device *dev) |
074b4c01 TW |
922 | { |
923 | struct mei_cl *cl, *next; | |
924 | list_for_each_entry_safe(cl, next, &dev->file_list, link) { | |
925 | if (waitqueue_active(&cl->rx_wait)) { | |
c0abffbd | 926 | cl_dbg(dev, cl, "Waking up reading client!\n"); |
074b4c01 TW |
927 | wake_up_interruptible(&cl->rx_wait); |
928 | } | |
5290801c | 929 | if (waitqueue_active(&cl->tx_wait)) { |
c0abffbd | 930 | cl_dbg(dev, cl, "Waking up writing client!\n"); |
5290801c TW |
931 | wake_up_interruptible(&cl->tx_wait); |
932 | } | |
074b4c01 TW |
933 | } |
934 | } | |
935 | ||
936 | /** | |
937 | * mei_cl_all_write_clear - clear all pending writes | |
938 | ||
939 | * @dev - mei device | |
940 | */ | |
941 | void mei_cl_all_write_clear(struct mei_device *dev) | |
942 | { | |
943 | struct mei_cl_cb *cb, *next; | |
30c54df7 | 944 | struct list_head *list; |
074b4c01 | 945 | |
30c54df7 AU |
946 | list = &dev->write_list.list; |
947 | list_for_each_entry_safe(cb, next, list, list) { | |
948 | list_del(&cb->list); | |
949 | mei_io_cb_free(cb); | |
950 | } | |
951 | ||
952 | list = &dev->write_waiting_list.list; | |
953 | list_for_each_entry_safe(cb, next, list, list) { | |
074b4c01 TW |
954 | list_del(&cb->list); |
955 | mei_io_cb_free(cb); | |
956 | } | |
957 | } | |
958 | ||
959 |