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