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