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