]>
Commit | Line | Data |
---|---|---|
ab841160 OW |
1 | /* |
2 | * | |
3 | * Intel Management Engine Interface (Intel MEI) Linux driver | |
733ba91c | 4 | * Copyright (c) 2003-2012, Intel Corporation. |
ab841160 OW |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms and conditions of the GNU General Public License, | |
8 | * version 2, as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | */ | |
16 | ||
ab841160 | 17 | #include <linux/pci.h> |
ab841160 | 18 | #include <linux/sched.h> |
9ca9050b TW |
19 | #include <linux/wait.h> |
20 | #include <linux/delay.h> | |
ab841160 | 21 | |
4f3afe1d | 22 | #include <linux/mei.h> |
47a73801 TW |
23 | |
24 | #include "mei_dev.h" | |
0edb23fc | 25 | #include "hbm.h" |
ab841160 | 26 | #include "interface.h" |
ab841160 | 27 | |
9ca9050b TW |
28 | |
29 | /** | |
30 | * mei_io_list_flush - removes list entry belonging to cl. | |
31 | * | |
32 | * @list: An instance of our list structure | |
33 | * @cl: host client | |
34 | */ | |
35 | void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl) | |
36 | { | |
37 | struct mei_cl_cb *cb; | |
38 | struct mei_cl_cb *next; | |
39 | ||
40 | list_for_each_entry_safe(cb, next, &list->list, list) { | |
41 | if (cb->cl && mei_cl_cmp_id(cl, cb->cl)) | |
42 | list_del(&cb->list); | |
43 | } | |
44 | } | |
45 | ||
601a1efa TW |
46 | /** |
47 | * mei_io_cb_free - free mei_cb_private related memory | |
48 | * | |
49 | * @cb: mei callback struct | |
50 | */ | |
51 | void mei_io_cb_free(struct mei_cl_cb *cb) | |
52 | { | |
53 | if (cb == NULL) | |
54 | return; | |
55 | ||
56 | kfree(cb->request_buffer.data); | |
57 | kfree(cb->response_buffer.data); | |
58 | kfree(cb); | |
59 | } | |
9ca9050b | 60 | |
664df38b TW |
61 | /** |
62 | * mei_io_cb_init - allocate and initialize io callback | |
63 | * | |
64 | * @cl - mei client | |
65 | * @file: pointer to file structure | |
66 | * | |
67 | * returns mei_cl_cb pointer or NULL; | |
68 | */ | |
69 | struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp) | |
70 | { | |
71 | struct mei_cl_cb *cb; | |
72 | ||
73 | cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); | |
74 | if (!cb) | |
75 | return NULL; | |
76 | ||
77 | mei_io_list_init(cb); | |
78 | ||
79 | cb->file_object = fp; | |
db3ed431 | 80 | cb->cl = cl; |
664df38b TW |
81 | cb->buf_idx = 0; |
82 | return cb; | |
83 | } | |
84 | ||
664df38b TW |
85 | /** |
86 | * mei_io_cb_alloc_req_buf - allocate request buffer | |
87 | * | |
88 | * @cb - io callback structure | |
89 | * @size: size of the buffer | |
90 | * | |
91 | * returns 0 on success | |
92 | * -EINVAL if cb is NULL | |
93 | * -ENOMEM if allocation failed | |
94 | */ | |
95 | int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length) | |
96 | { | |
97 | if (!cb) | |
98 | return -EINVAL; | |
99 | ||
100 | if (length == 0) | |
101 | return 0; | |
102 | ||
103 | cb->request_buffer.data = kmalloc(length, GFP_KERNEL); | |
104 | if (!cb->request_buffer.data) | |
105 | return -ENOMEM; | |
106 | cb->request_buffer.size = length; | |
107 | return 0; | |
108 | } | |
109 | /** | |
110 | * mei_io_cb_alloc_req_buf - allocate respose buffer | |
111 | * | |
112 | * @cb - io callback structure | |
113 | * @size: size of the buffer | |
114 | * | |
115 | * returns 0 on success | |
116 | * -EINVAL if cb is NULL | |
117 | * -ENOMEM if allocation failed | |
118 | */ | |
119 | int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length) | |
120 | { | |
121 | if (!cb) | |
122 | return -EINVAL; | |
123 | ||
124 | if (length == 0) | |
125 | return 0; | |
126 | ||
127 | cb->response_buffer.data = kmalloc(length, GFP_KERNEL); | |
128 | if (!cb->response_buffer.data) | |
129 | return -ENOMEM; | |
130 | cb->response_buffer.size = length; | |
131 | return 0; | |
132 | } | |
133 | ||
601a1efa | 134 | |
9ca9050b TW |
135 | |
136 | /** | |
137 | * mei_cl_flush_queues - flushes queue lists belonging to cl. | |
138 | * | |
139 | * @dev: the device structure | |
140 | * @cl: host client | |
141 | */ | |
142 | int mei_cl_flush_queues(struct mei_cl *cl) | |
143 | { | |
144 | if (!cl || !cl->dev) | |
145 | return -EINVAL; | |
146 | ||
147 | dev_dbg(&cl->dev->pdev->dev, "remove list entry belonging to cl\n"); | |
148 | mei_io_list_flush(&cl->dev->read_list, cl); | |
149 | mei_io_list_flush(&cl->dev->write_list, cl); | |
150 | mei_io_list_flush(&cl->dev->write_waiting_list, cl); | |
151 | mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); | |
152 | mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); | |
153 | mei_io_list_flush(&cl->dev->amthif_cmd_list, cl); | |
154 | mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl); | |
155 | return 0; | |
156 | } | |
157 | ||
158 | /** | |
159 | * mei_me_cl_by_uuid - locate index of me client | |
160 | * | |
161 | * @dev: mei device | |
162 | * returns me client index or -ENOENT if not found | |
163 | */ | |
164 | int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid) | |
165 | { | |
166 | int i, res = -ENOENT; | |
167 | ||
168 | for (i = 0; i < dev->me_clients_num; ++i) | |
169 | if (uuid_le_cmp(*uuid, | |
170 | dev->me_clients[i].props.protocol_name) == 0) { | |
171 | res = i; | |
172 | break; | |
173 | } | |
174 | ||
175 | return res; | |
176 | } | |
177 | ||
178 | ||
07b509b7 TW |
179 | /** |
180 | * mei_me_cl_by_id return index to me_clients for client_id | |
181 | * | |
182 | * @dev: the device structure | |
183 | * @client_id: me client id | |
184 | * | |
185 | * Locking: called under "dev->device_lock" lock | |
186 | * | |
187 | * returns index on success, -ENOENT on failure. | |
188 | */ | |
ab841160 | 189 | |
07b509b7 TW |
190 | int mei_me_cl_by_id(struct mei_device *dev, u8 client_id) |
191 | { | |
192 | int i; | |
193 | for (i = 0; i < dev->me_clients_num; i++) | |
194 | if (dev->me_clients[i].client_id == client_id) | |
195 | break; | |
196 | if (WARN_ON(dev->me_clients[i].client_id != client_id)) | |
197 | return -ENOENT; | |
198 | ||
199 | if (i == dev->me_clients_num) | |
200 | return -ENOENT; | |
201 | ||
202 | return i; | |
203 | } | |
ab841160 | 204 | |
9ca9050b TW |
205 | /** |
206 | * mei_cl_init - initializes intialize cl. | |
207 | * | |
208 | * @cl: host client to be initialized | |
209 | * @dev: mei device | |
210 | */ | |
211 | void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) | |
212 | { | |
213 | memset(cl, 0, sizeof(struct mei_cl)); | |
214 | init_waitqueue_head(&cl->wait); | |
215 | init_waitqueue_head(&cl->rx_wait); | |
216 | init_waitqueue_head(&cl->tx_wait); | |
217 | INIT_LIST_HEAD(&cl->link); | |
218 | cl->reading_state = MEI_IDLE; | |
219 | cl->writing_state = MEI_IDLE; | |
220 | cl->dev = dev; | |
221 | } | |
222 | ||
223 | /** | |
224 | * mei_cl_allocate - allocates cl structure and sets it up. | |
225 | * | |
226 | * @dev: mei device | |
227 | * returns The allocated file or NULL on failure | |
228 | */ | |
229 | struct mei_cl *mei_cl_allocate(struct mei_device *dev) | |
230 | { | |
231 | struct mei_cl *cl; | |
232 | ||
233 | cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL); | |
234 | if (!cl) | |
235 | return NULL; | |
236 | ||
237 | mei_cl_init(cl, dev); | |
238 | ||
239 | return cl; | |
240 | } | |
241 | ||
242 | ||
243 | /** | |
244 | * mei_me_cl_link - create link between host and me clinet and add | |
245 | * me_cl to the list | |
246 | * | |
247 | * @dev: the device structure | |
248 | * @cl: link between me and host client assocated with opened file descriptor | |
249 | * @uuid: uuid of ME client | |
250 | * @client_id: id of the host client | |
251 | * | |
252 | * returns ME client index if ME client | |
253 | * -EINVAL on incorrect values | |
254 | * -ENONET if client not found | |
255 | */ | |
256 | int mei_me_cl_link(struct mei_device *dev, struct mei_cl *cl, | |
257 | const uuid_le *uuid, u8 host_cl_id) | |
258 | { | |
259 | int i; | |
260 | ||
261 | if (!dev || !cl || !uuid) | |
262 | return -EINVAL; | |
263 | ||
264 | /* check for valid client id */ | |
265 | i = mei_me_cl_by_uuid(dev, uuid); | |
266 | if (i >= 0) { | |
267 | cl->me_client_id = dev->me_clients[i].client_id; | |
268 | cl->state = MEI_FILE_CONNECTING; | |
269 | cl->host_client_id = host_cl_id; | |
270 | ||
271 | list_add_tail(&cl->link, &dev->file_list); | |
272 | return (u8)i; | |
273 | } | |
274 | ||
275 | return -ENOENT; | |
276 | } | |
277 | /** | |
278 | * mei_me_cl_unlink - remove me_cl from the list | |
279 | * | |
280 | * @dev: the device structure | |
281 | * @host_client_id: host client id to be removed | |
282 | */ | |
283 | void mei_me_cl_unlink(struct mei_device *dev, struct mei_cl *cl) | |
284 | { | |
285 | struct mei_cl *pos, *next; | |
286 | list_for_each_entry_safe(pos, next, &dev->file_list, link) { | |
287 | if (cl->host_client_id == pos->host_client_id) { | |
288 | dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n", | |
289 | pos->host_client_id, pos->me_client_id); | |
290 | list_del_init(&pos->link); | |
291 | break; | |
292 | } | |
293 | } | |
294 | } | |
295 | ||
296 | ||
297 | void mei_host_client_init(struct work_struct *work) | |
298 | { | |
299 | struct mei_device *dev = container_of(work, | |
300 | struct mei_device, init_work); | |
301 | struct mei_client_properties *client_props; | |
302 | int i; | |
303 | ||
304 | mutex_lock(&dev->device_lock); | |
305 | ||
306 | bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX); | |
307 | dev->open_handle_count = 0; | |
308 | ||
309 | /* | |
310 | * Reserving the first three client IDs | |
311 | * 0: Reserved for MEI Bus Message communications | |
312 | * 1: Reserved for Watchdog | |
313 | * 2: Reserved for AMTHI | |
314 | */ | |
315 | bitmap_set(dev->host_clients_map, 0, 3); | |
316 | ||
317 | for (i = 0; i < dev->me_clients_num; i++) { | |
318 | client_props = &dev->me_clients[i].props; | |
319 | ||
320 | if (!uuid_le_cmp(client_props->protocol_name, mei_amthi_guid)) | |
321 | mei_amthif_host_init(dev); | |
322 | else if (!uuid_le_cmp(client_props->protocol_name, mei_wd_guid)) | |
323 | mei_wd_host_init(dev); | |
324 | } | |
325 | ||
326 | dev->dev_state = MEI_DEV_ENABLED; | |
327 | ||
328 | mutex_unlock(&dev->device_lock); | |
329 | } | |
330 | ||
331 | ||
332 | /** | |
333 | * mei_disconnect_host_client - sends disconnect message to fw from host client. | |
334 | * | |
335 | * @dev: the device structure | |
336 | * @cl: private data of the file object | |
337 | * | |
338 | * Locking: called under "dev->device_lock" lock | |
339 | * | |
340 | * returns 0 on success, <0 on failure. | |
341 | */ | |
342 | int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl) | |
343 | { | |
344 | struct mei_cl_cb *cb; | |
345 | int rets, err; | |
346 | ||
347 | if (!dev || !cl) | |
348 | return -ENODEV; | |
349 | ||
350 | if (cl->state != MEI_FILE_DISCONNECTING) | |
351 | return 0; | |
352 | ||
353 | cb = mei_io_cb_init(cl, NULL); | |
354 | if (!cb) | |
355 | return -ENOMEM; | |
356 | ||
357 | cb->fop_type = MEI_FOP_CLOSE; | |
358 | if (dev->mei_host_buffer_is_empty) { | |
359 | dev->mei_host_buffer_is_empty = false; | |
360 | if (mei_hbm_cl_disconnect_req(dev, cl)) { | |
361 | rets = -ENODEV; | |
362 | dev_err(&dev->pdev->dev, "failed to disconnect.\n"); | |
363 | goto free; | |
364 | } | |
365 | mdelay(10); /* Wait for hardware disconnection ready */ | |
366 | list_add_tail(&cb->list, &dev->ctrl_rd_list.list); | |
367 | } else { | |
368 | dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n"); | |
369 | list_add_tail(&cb->list, &dev->ctrl_wr_list.list); | |
370 | ||
371 | } | |
372 | mutex_unlock(&dev->device_lock); | |
373 | ||
374 | err = wait_event_timeout(dev->wait_recvd_msg, | |
375 | MEI_FILE_DISCONNECTED == cl->state, | |
376 | mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); | |
377 | ||
378 | mutex_lock(&dev->device_lock); | |
379 | if (MEI_FILE_DISCONNECTED == cl->state) { | |
380 | rets = 0; | |
381 | dev_dbg(&dev->pdev->dev, "successfully disconnected from FW client.\n"); | |
382 | } else { | |
383 | rets = -ENODEV; | |
384 | if (MEI_FILE_DISCONNECTED != cl->state) | |
385 | dev_dbg(&dev->pdev->dev, "wrong status client disconnect.\n"); | |
386 | ||
387 | if (err) | |
388 | dev_dbg(&dev->pdev->dev, | |
389 | "wait failed disconnect err=%08x\n", | |
390 | err); | |
391 | ||
392 | dev_dbg(&dev->pdev->dev, "failed to disconnect from FW client.\n"); | |
393 | } | |
394 | ||
395 | mei_io_list_flush(&dev->ctrl_rd_list, cl); | |
396 | mei_io_list_flush(&dev->ctrl_wr_list, cl); | |
397 | free: | |
398 | mei_io_cb_free(cb); | |
399 | return rets; | |
400 | } | |
401 | ||
402 | ||
403 | /** | |
404 | * mei_other_client_is_connecting - checks if other | |
405 | * client with the same client id is connected. | |
406 | * | |
407 | * @dev: the device structure | |
408 | * @cl: private data of the file object | |
409 | * | |
410 | * returns 1 if other client is connected, 0 - otherwise. | |
411 | */ | |
412 | int mei_other_client_is_connecting(struct mei_device *dev, | |
413 | struct mei_cl *cl) | |
414 | { | |
415 | struct mei_cl *cl_pos = NULL; | |
416 | struct mei_cl *cl_next = NULL; | |
417 | ||
418 | list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) { | |
419 | if ((cl_pos->state == MEI_FILE_CONNECTING) && | |
420 | (cl_pos != cl) && | |
421 | cl->me_client_id == cl_pos->me_client_id) | |
422 | return 1; | |
423 | ||
424 | } | |
425 | return 0; | |
426 | } | |
427 | ||
428 | /** | |
429 | * mei_flow_ctrl_creds - checks flow_control credentials. | |
430 | * | |
431 | * @dev: the device structure | |
432 | * @cl: private data of the file object | |
433 | * | |
434 | * returns 1 if mei_flow_ctrl_creds >0, 0 - otherwise. | |
435 | * -ENOENT if mei_cl is not present | |
436 | * -EINVAL if single_recv_buf == 0 | |
437 | */ | |
438 | int mei_flow_ctrl_creds(struct mei_device *dev, struct mei_cl *cl) | |
439 | { | |
440 | int i; | |
441 | ||
442 | if (!dev->me_clients_num) | |
443 | return 0; | |
444 | ||
445 | if (cl->mei_flow_ctrl_creds > 0) | |
446 | return 1; | |
447 | ||
448 | for (i = 0; i < dev->me_clients_num; i++) { | |
449 | struct mei_me_client *me_cl = &dev->me_clients[i]; | |
450 | if (me_cl->client_id == cl->me_client_id) { | |
451 | if (me_cl->mei_flow_ctrl_creds) { | |
452 | if (WARN_ON(me_cl->props.single_recv_buf == 0)) | |
453 | return -EINVAL; | |
454 | return 1; | |
455 | } else { | |
456 | return 0; | |
457 | } | |
458 | } | |
459 | } | |
460 | return -ENOENT; | |
461 | } | |
462 | ||
463 | /** | |
464 | * mei_flow_ctrl_reduce - reduces flow_control. | |
465 | * | |
466 | * @dev: the device structure | |
467 | * @cl: private data of the file object | |
468 | * @returns | |
469 | * 0 on success | |
470 | * -ENOENT when me client is not found | |
471 | * -EINVAL when ctrl credits are <= 0 | |
472 | */ | |
473 | int mei_flow_ctrl_reduce(struct mei_device *dev, struct mei_cl *cl) | |
474 | { | |
475 | int i; | |
476 | ||
477 | if (!dev->me_clients_num) | |
478 | return -ENOENT; | |
479 | ||
480 | for (i = 0; i < dev->me_clients_num; i++) { | |
481 | struct mei_me_client *me_cl = &dev->me_clients[i]; | |
482 | if (me_cl->client_id == cl->me_client_id) { | |
483 | if (me_cl->props.single_recv_buf != 0) { | |
484 | if (WARN_ON(me_cl->mei_flow_ctrl_creds <= 0)) | |
485 | return -EINVAL; | |
486 | dev->me_clients[i].mei_flow_ctrl_creds--; | |
487 | } else { | |
488 | if (WARN_ON(cl->mei_flow_ctrl_creds <= 0)) | |
489 | return -EINVAL; | |
490 | cl->mei_flow_ctrl_creds--; | |
491 | } | |
492 | return 0; | |
493 | } | |
494 | } | |
495 | return -ENOENT; | |
496 | } | |
497 | ||
498 | ||
499 | ||
ab841160 OW |
500 | /** |
501 | * mei_ioctl_connect_client - the connect to fw client IOCTL function | |
502 | * | |
503 | * @dev: the device structure | |
504 | * @data: IOCTL connect data, input and output parameters | |
505 | * @file: private data of the file object | |
506 | * | |
507 | * Locking: called under "dev->device_lock" lock | |
508 | * | |
509 | * returns 0 on success, <0 on failure. | |
510 | */ | |
511 | int mei_ioctl_connect_client(struct file *file, | |
512 | struct mei_connect_client_data *data) | |
513 | { | |
514 | struct mei_device *dev; | |
515 | struct mei_cl_cb *cb; | |
516 | struct mei_client *client; | |
517 | struct mei_cl *cl; | |
3870c320 | 518 | long timeout = mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT); |
ab841160 OW |
519 | int i; |
520 | int err; | |
521 | int rets; | |
522 | ||
523 | cl = file->private_data; | |
524 | if (WARN_ON(!cl || !cl->dev)) | |
525 | return -ENODEV; | |
526 | ||
527 | dev = cl->dev; | |
528 | ||
529 | dev_dbg(&dev->pdev->dev, "mei_ioctl_connect_client() Entry\n"); | |
530 | ||
ab841160 | 531 | /* buffered ioctl cb */ |
664df38b | 532 | cb = mei_io_cb_init(cl, file); |
ab841160 OW |
533 | if (!cb) { |
534 | rets = -ENOMEM; | |
535 | goto end; | |
536 | } | |
ab841160 | 537 | |
4b8960b4 | 538 | cb->fop_type = MEI_FOP_IOCTL; |
ab841160 | 539 | |
b210d750 | 540 | if (dev->dev_state != MEI_DEV_ENABLED) { |
ab841160 OW |
541 | rets = -ENODEV; |
542 | goto end; | |
543 | } | |
544 | if (cl->state != MEI_FILE_INITIALIZING && | |
545 | cl->state != MEI_FILE_DISCONNECTED) { | |
546 | rets = -EBUSY; | |
547 | goto end; | |
548 | } | |
549 | ||
550 | /* find ME client we're trying to connect to */ | |
07b509b7 | 551 | i = mei_me_cl_by_uuid(dev, &data->in_client_uuid); |
ab841160 OW |
552 | if (i >= 0 && !dev->me_clients[i].props.fixed_address) { |
553 | cl->me_client_id = dev->me_clients[i].client_id; | |
554 | cl->state = MEI_FILE_CONNECTING; | |
555 | } | |
556 | ||
557 | dev_dbg(&dev->pdev->dev, "Connect to FW Client ID = %d\n", | |
558 | cl->me_client_id); | |
559 | dev_dbg(&dev->pdev->dev, "FW Client - Protocol Version = %d\n", | |
560 | dev->me_clients[i].props.protocol_version); | |
561 | dev_dbg(&dev->pdev->dev, "FW Client - Max Msg Len = %d\n", | |
562 | dev->me_clients[i].props.max_msg_length); | |
563 | ||
5f9092f3 JM |
564 | /* if we're connecting to amthi client then we will use the |
565 | * existing connection | |
ab841160 OW |
566 | */ |
567 | if (uuid_le_cmp(data->in_client_uuid, mei_amthi_guid) == 0) { | |
568 | dev_dbg(&dev->pdev->dev, "FW Client is amthi\n"); | |
569 | if (dev->iamthif_cl.state != MEI_FILE_CONNECTED) { | |
570 | rets = -ENODEV; | |
571 | goto end; | |
572 | } | |
573 | clear_bit(cl->host_client_id, dev->host_clients_map); | |
ff8b2f4e | 574 | mei_me_cl_unlink(dev, cl); |
ab841160 | 575 | |
ab841160 | 576 | kfree(cl); |
ab841160 OW |
577 | cl = NULL; |
578 | file->private_data = &dev->iamthif_cl; | |
579 | ||
580 | client = &data->out_client_properties; | |
581 | client->max_msg_length = | |
582 | dev->me_clients[i].props.max_msg_length; | |
583 | client->protocol_version = | |
584 | dev->me_clients[i].props.protocol_version; | |
585 | rets = dev->iamthif_cl.status; | |
586 | ||
587 | goto end; | |
588 | } | |
589 | ||
590 | if (cl->state != MEI_FILE_CONNECTING) { | |
591 | rets = -ENODEV; | |
592 | goto end; | |
593 | } | |
594 | ||
595 | ||
596 | /* prepare the output buffer */ | |
597 | client = &data->out_client_properties; | |
598 | client->max_msg_length = dev->me_clients[i].props.max_msg_length; | |
599 | client->protocol_version = dev->me_clients[i].props.protocol_version; | |
600 | dev_dbg(&dev->pdev->dev, "Can connect?\n"); | |
601 | if (dev->mei_host_buffer_is_empty | |
602 | && !mei_other_client_is_connecting(dev, cl)) { | |
603 | dev_dbg(&dev->pdev->dev, "Sending Connect Message\n"); | |
eb9af0ac | 604 | dev->mei_host_buffer_is_empty = false; |
8120e720 | 605 | if (mei_hbm_cl_connect_req(dev, cl)) { |
ab841160 OW |
606 | dev_dbg(&dev->pdev->dev, "Sending connect message - failed\n"); |
607 | rets = -ENODEV; | |
608 | goto end; | |
609 | } else { | |
610 | dev_dbg(&dev->pdev->dev, "Sending connect message - succeeded\n"); | |
611 | cl->timer_count = MEI_CONNECT_TIMEOUT; | |
fb601adb | 612 | list_add_tail(&cb->list, &dev->ctrl_rd_list.list); |
ab841160 OW |
613 | } |
614 | ||
615 | ||
616 | } else { | |
617 | dev_dbg(&dev->pdev->dev, "Queuing the connect request due to device busy\n"); | |
ab841160 | 618 | dev_dbg(&dev->pdev->dev, "add connect cb to control write list.\n"); |
fb601adb | 619 | list_add_tail(&cb->list, &dev->ctrl_wr_list.list); |
ab841160 OW |
620 | } |
621 | mutex_unlock(&dev->device_lock); | |
622 | err = wait_event_timeout(dev->wait_recvd_msg, | |
623 | (MEI_FILE_CONNECTED == cl->state || | |
3870c320 | 624 | MEI_FILE_DISCONNECTED == cl->state), timeout); |
ab841160 OW |
625 | |
626 | mutex_lock(&dev->device_lock); | |
627 | if (MEI_FILE_CONNECTED == cl->state) { | |
628 | dev_dbg(&dev->pdev->dev, "successfully connected to FW client.\n"); | |
629 | rets = cl->status; | |
630 | goto end; | |
631 | } else { | |
632 | dev_dbg(&dev->pdev->dev, "failed to connect to FW client.cl->state = %d.\n", | |
633 | cl->state); | |
634 | if (!err) { | |
635 | dev_dbg(&dev->pdev->dev, | |
636 | "wait_event_interruptible_timeout failed on client" | |
637 | " connect message fw response message.\n"); | |
638 | } | |
639 | rets = -EFAULT; | |
640 | ||
0288c7c9 TW |
641 | mei_io_list_flush(&dev->ctrl_rd_list, cl); |
642 | mei_io_list_flush(&dev->ctrl_wr_list, cl); | |
ab841160 OW |
643 | goto end; |
644 | } | |
645 | rets = 0; | |
646 | end: | |
647 | dev_dbg(&dev->pdev->dev, "free connect cb memory."); | |
601a1efa | 648 | mei_io_cb_free(cb); |
ab841160 OW |
649 | return rets; |
650 | } | |
651 | ||
ab841160 OW |
652 | /** |
653 | * mei_start_read - the start read client message function. | |
654 | * | |
655 | * @dev: the device structure | |
656 | * @if_num: minor number | |
657 | * @cl: private data of the file object | |
658 | * | |
659 | * returns 0 on success, <0 on failure. | |
660 | */ | |
661 | int mei_start_read(struct mei_device *dev, struct mei_cl *cl) | |
662 | { | |
663 | struct mei_cl_cb *cb; | |
664df38b | 664 | int rets; |
ab841160 OW |
665 | int i; |
666 | ||
667 | if (cl->state != MEI_FILE_CONNECTED) | |
668 | return -ENODEV; | |
669 | ||
b210d750 | 670 | if (dev->dev_state != MEI_DEV_ENABLED) |
ab841160 OW |
671 | return -ENODEV; |
672 | ||
ab841160 OW |
673 | if (cl->read_pending || cl->read_cb) { |
674 | dev_dbg(&dev->pdev->dev, "read is pending.\n"); | |
675 | return -EBUSY; | |
676 | } | |
664df38b TW |
677 | i = mei_me_cl_by_id(dev, cl->me_client_id); |
678 | if (i < 0) { | |
679 | dev_err(&dev->pdev->dev, "no such me client %d\n", | |
680 | cl->me_client_id); | |
681 | return -ENODEV; | |
682 | } | |
ab841160 | 683 | |
664df38b | 684 | cb = mei_io_cb_init(cl, NULL); |
ab841160 OW |
685 | if (!cb) |
686 | return -ENOMEM; | |
687 | ||
664df38b TW |
688 | rets = mei_io_cb_alloc_resp_buf(cb, |
689 | dev->me_clients[i].props.max_msg_length); | |
690 | if (rets) | |
691 | goto err; | |
ab841160 | 692 | |
4b8960b4 | 693 | cb->fop_type = MEI_FOP_READ; |
ab841160 OW |
694 | cl->read_cb = cb; |
695 | if (dev->mei_host_buffer_is_empty) { | |
eb9af0ac | 696 | dev->mei_host_buffer_is_empty = false; |
8120e720 | 697 | if (mei_hbm_cl_flow_control_req(dev, cl)) { |
ab841160 | 698 | rets = -ENODEV; |
664df38b | 699 | goto err; |
ab841160 | 700 | } |
fb601adb | 701 | list_add_tail(&cb->list, &dev->read_list.list); |
ab841160 | 702 | } else { |
fb601adb | 703 | list_add_tail(&cb->list, &dev->ctrl_wr_list.list); |
ab841160 OW |
704 | } |
705 | return rets; | |
664df38b | 706 | err: |
601a1efa | 707 | mei_io_cb_free(cb); |
ab841160 OW |
708 | return rets; |
709 | } | |
710 |