1 /*
2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 /*
7 * Part of Intel(R) Manageability Engine Interface Linux driver
8 *
9 * Copyright (c) 2003 - 2008 Intel Corp.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions, and the following disclaimer,
17 * without modification.
18 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
19 * substantially similar to the "NO WARRANTY" disclaimer below
20 * ("Disclaimer") and any redistribution must be conditioned upon
21 * including a substantially similar Disclaimer requirement for further
22 * binary redistribution.
23 * 3. Neither the names of the above-listed copyright holders nor the names
24 * of any contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * Alternatively, this software may be distributed under the terms of the
28 * GNU General Public License ("GPL") version 2 as published by the Free
29 * Software Foundation.
30 *
31 * NO WARRANTY
32 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
33 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
34 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
35 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
36 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
41 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42 * POSSIBILITY OF SUCH DAMAGES.
43 *
44 */
45
46 #include <sys/types.h>
47 #include <sys/note.h>
48 #include <sys/cmn_err.h>
49 #include <sys/conf.h>
50 #include <sys/ddi.h>
51 #include <sys/ddi_impldefs.h>
52 #include <sys/devops.h>
53 #include <sys/instance.h>
54 #include <sys/modctl.h>
55 #include <sys/open.h>
56 #include <sys/stat.h>
57 #include <sys/sunddi.h>
58 #include <sys/sunndi.h>
59 #include <sys/systm.h>
60 #include <sys/mkdev.h>
61 #include <sys/list.h>
62 #include "heci_data_structures.h"
63 #include "heci.h"
64 #include "heci_interface.h"
65 #include "version.h"
66
67
68 static inline int heci_fe_same_id(struct heci_file_private *fe1,
69 struct heci_file_private *fe2);
70 /*
71 * heci_ioctl_get_version - the get driver version IOCTL function
72 *
73 * @dev: Device object for our driver
74 * @if_num: minor number
75 * @*u_msg: pointer to user data struct in user space
76 * @k_msg: data in kernel on the stack
77 * @file_ext: private data of the file object
78 *
79 * @return 0 on success, <0 on failure.
80 */
81 int
heci_ioctl_get_version(struct iamt_heci_device * dev,int if_num,struct heci_message_data * u_msg,struct heci_message_data k_msg,struct heci_file_private * file_ext,int mode)82 heci_ioctl_get_version(struct iamt_heci_device *dev, int if_num,
83 struct heci_message_data *u_msg,
84 struct heci_message_data k_msg,
85 struct heci_file_private *file_ext, int mode)
86 {
87
88 int rets = 0;
89 struct heci_driver_version *version;
90 struct heci_message_data res_msg;
91
92 if ((if_num < HECI_MINOR_NUMBER) || (!dev) ||
93 (!file_ext))
94 return (-ENODEV);
95
96 if (k_msg.size < (sizeof (struct heci_driver_version) - 2)) {
97 DBG("user buffer less than heci_driver_version.\n");
98 return (-EMSGSIZE);
99 }
100
101 res_msg.data = kmem_zalloc(sizeof (struct heci_driver_version),
102 KM_SLEEP);
103 if (!res_msg.data) {
104 DBG("failed allocation response buffer size = %d.\n",
105 (int)sizeof (struct heci_driver_version));
106 return (-ENOMEM);
107 }
108
109 version = (struct heci_driver_version *)res_msg.data;
110 version->major = MAJOR_VERSION;
111 version->minor = MINOR_VERSION;
112 version->hotfix = QUICK_FIX_NUMBER;
113 version->build = VER_BUILD;
114 res_msg.size = sizeof (struct heci_driver_version);
115 if (k_msg.size < sizeof (struct heci_driver_version))
116 res_msg.size -= 2;
117
118 rets = file_ext->status;
119 /* now copy the data to user space */
120 if (ddi_copyout(res_msg.data, k_msg.data, res_msg.size, mode)) {
121 rets = -EFAULT;
122 goto end;
123 }
124 if (ddi_copyout(&res_msg.size, &u_msg->size, sizeof (uint32_t), mode)) {
125 rets = -EFAULT;
126 goto end;
127 }
128 end:
129 kmem_free(res_msg.data, sizeof (struct heci_driver_version));
130 return (rets);
131 }
132
133 /*
134 * heci_ioctl_connect_client - the connect to fw client IOCTL function
135 *
136 * @dev: Device object for our driver
137 * @if_num: minor number
138 * @*u_msg: pointer to user data struct in user space
139 * @k_msg: data in kernel on the stack
140 * @file_ext: private data of the file object
141 *
142 * @return 0 on success, <0 on failure.
143 */
144 int
heci_ioctl_connect_client(struct iamt_heci_device * dev,int if_num,struct heci_message_data * u_msg,struct heci_message_data k_msg,struct heci_file * file,int mode)145 heci_ioctl_connect_client(struct iamt_heci_device *dev, int if_num,
146 struct heci_message_data *u_msg,
147 struct heci_message_data k_msg,
148 struct heci_file *file, int mode)
149 {
150
151 int rets = 0;
152 struct heci_message_data req_msg, res_msg;
153 struct heci_cb_private *priv_cb = NULL;
154 struct heci_client *client;
155 struct heci_file_private *file_ext;
156 struct heci_file_private *file_pos = NULL;
157 struct heci_file_private *file_next = NULL;
158 long timeout = 15; /* 15 second */
159 uint8_t i;
160 int err = 0;
161
162 if ((if_num < HECI_MINOR_NUMBER) || (!dev) || (!file))
163 return (-ENODEV);
164
165 file_ext = file->private_data;
166 if (!file_ext)
167 return (-ENODEV);
168
169 if (k_msg.size != sizeof (struct guid)) {
170 DBG("user buffer size is not equal to size of struct "
171 "guid(16).\n");
172 return (-EMSGSIZE);
173 }
174
175 if (!k_msg.data)
176 return (-EIO);
177
178 req_msg.data = kmem_zalloc(sizeof (struct guid), KM_SLEEP);
179 res_msg.data = kmem_zalloc(sizeof (struct heci_client), KM_SLEEP);
180
181 if (!res_msg.data) {
182 DBG("failed allocation response buffer size = %d.\n",
183 (int)sizeof (struct heci_client));
184 kmem_free(req_msg.data, sizeof (struct guid));
185 return (-ENOMEM);
186 }
187 if (!req_msg.data) {
188 DBG("failed allocation request buffer size = %d.\n",
189 (int)sizeof (struct guid));
190 if (res_msg.data) {
191 kmem_free(res_msg.data, sizeof (struct heci_client));
192 res_msg.data = NULL;
193 goto fail;
194 }
195 fail:
196 return (-ENOMEM);
197 }
198 req_msg.size = sizeof (struct guid);
199 res_msg.size = sizeof (struct heci_client);
200
201 /*
202 * copy the message to kernel space -
203 * use a pointer already copied into kernel space
204 */
205 if (ddi_copyin(k_msg.data, req_msg.data, k_msg.size, mode)) {
206 rets = -EFAULT;
207 goto end;
208 }
209 /* buffered ioctl cb */
210 priv_cb = kmem_zalloc(sizeof (struct heci_cb_private), KM_SLEEP);
211 if (!priv_cb) {
212 rets = -ENOMEM;
213 goto end;
214 }
215 LIST_INIT_HEAD(&priv_cb->cb_list);
216 priv_cb->response_buffer.data = res_msg.data;
217 priv_cb->response_buffer.size = res_msg.size;
218 priv_cb->request_buffer.data = req_msg.data;
219 priv_cb->request_buffer.size = req_msg.size;
220 priv_cb->major_file_operations = HECI_IOCTL;
221 mutex_enter(&dev->device_lock);
222 if (dev->heci_state != HECI_ENABLED) {
223 rets = -ENODEV;
224 mutex_exit(&dev->device_lock);
225 goto end;
226 }
227 if ((file_ext->state != HECI_FILE_INITIALIZING) &&
228 (file_ext->state != HECI_FILE_DISCONNECTED)) {
229 rets = -EBUSY;
230 mutex_exit(&dev->device_lock);
231 goto end;
232 }
233
234 DBG("req_msg.data:%x", *(uint32_t *)(void *)req_msg.data);
235 /* find ME client we're trying to connect to */
236 for (i = 0; i < dev->num_heci_me_clients; i++) {
237 DBG("guid:%x, me_client_id:%d\n",
238 dev->me_clients[i].props.protocol_name.data1,
239 dev->me_clients[i].client_id);
240 if (memcmp((struct guid *)req_msg.data,
241 &dev->me_clients[i].props.protocol_name,
242 sizeof (struct guid)) == 0) {
243 if (dev->me_clients[i].props.fixed_address == 0) {
244 file_ext->me_client_id =
245 dev->me_clients[i].client_id;
246 file_ext->state = HECI_FILE_CONNECTING;
247 }
248 break;
249 }
250 }
251 /*
252 * if we're connecting to PTHI client so we will use the exist
253 * connection
254 */
255 if (memcmp((struct guid *)req_msg.data, &heci_pthi_guid,
256 sizeof (struct guid)) == 0) {
257 if (dev->iamthif_file_ext.state != HECI_FILE_CONNECTED) {
258 rets = -ENODEV;
259 mutex_exit(&dev->device_lock);
260 goto end;
261 }
262 dev->heci_host_clients[file_ext->host_client_id / 8] &=
263 ~(1 << (file_ext->host_client_id % 8));
264 list_for_each_entry_safe(file_pos,
265 file_next, &dev->file_list, link,
266 struct heci_file_private) {
267 if (heci_fe_same_id(file_ext, file_pos)) {
268 DBG("remove file private data node host"
269 " client = %d, ME client = %d.\n",
270 file_pos->host_client_id,
271 file_pos->me_client_id);
272 list_del(&file_pos->link);
273 }
274
275 }
276 DBG("free file private data memory.\n");
277 kmem_free(file_ext, sizeof (struct heci_file_private));
278 file_ext = NULL;
279 file->private_data = &dev->iamthif_file_ext;
280 client = (struct heci_client *)res_msg.data;
281 client->max_message_length =
282 dev->me_clients[i].props.max_msg_length;
283 client->protocol_version =
284 dev->me_clients[i].props.protocol_version;
285 rets = dev->iamthif_file_ext.status;
286 mutex_exit(&dev->device_lock);
287
288 /* now copy the data to user space */
289 if (ddi_copyout(res_msg.data, k_msg.data, res_msg.size, mode)) {
290 cmn_err(CE_WARN, "ddi_copyout error on res_msg.data");
291 rets = -EFAULT;
292 goto end;
293 }
294 if (ddi_copyout(&res_msg.size, &u_msg->size,
295 sizeof (uint32_t), mode)) {
296 cmn_err(CE_WARN, "ddi_copyout error on res_msg.size");
297 rets = -EFAULT;
298 goto end;
299 }
300 goto end;
301 }
302 mutex_enter(&file_ext->file_lock);
303 if (file_ext->state != HECI_FILE_CONNECTING) {
304 rets = -ENODEV;
305 mutex_exit(&file_ext->file_lock);
306 mutex_exit(&dev->device_lock);
307 goto end;
308 }
309 mutex_exit(&file_ext->file_lock);
310 /* prepare the output buffer */
311 client = (struct heci_client *)res_msg.data;
312 client->max_message_length = dev->me_clients[i].props.max_msg_length;
313 client->protocol_version = dev->me_clients[i].props.protocol_version;
314 if (dev->host_buffer_is_empty &&
315 !other_client_is_connecting(dev, file_ext)) {
316 dev->host_buffer_is_empty = 0;
317 if (!heci_connect(dev, file_ext)) {
318 rets = -ENODEV;
319 mutex_exit(&dev->device_lock);
320 goto end;
321 } else {
322 file_ext->timer_count = HECI_CONNECT_TIMEOUT;
323 priv_cb->file_private = file_ext;
324 list_add_tail(&priv_cb->cb_list,
325 &dev->ctrl_rd_list.heci_cb.
326 cb_list);
327 }
328
329
330 } else {
331 priv_cb->file_private = file_ext;
332 DBG("add connect cb to control write list.\n");
333 list_add_tail(&priv_cb->cb_list,
334 &dev->ctrl_wr_list.heci_cb.cb_list);
335 }
336 err = 0;
337 while (err != -1 && HECI_FILE_CONNECTED != file_ext->state &&
338 HECI_FILE_DISCONNECTED != file_ext->state) {
339 err = cv_reltimedwait(&dev->wait_recvd_msg,
340 &dev->device_lock, timeout * HZ, TR_CLOCK_TICK);
341 }
342 mutex_exit(&dev->device_lock);
343
344 if (HECI_FILE_CONNECTED == file_ext->state) {
345 DBG("successfully connected to FW client."
346 " me_client_id:%d, host_client_id:%d\n",
347 file_ext->me_client_id,
348 file_ext->host_client_id);
349 rets = file_ext->status;
350 /* now copy the data to user space */
351 if (ddi_copyout(res_msg.data, k_msg.data, res_msg.size, mode)) {
352 rets = -EFAULT;
353 goto end;
354 }
355 if (ddi_copyout(&res_msg.size, &u_msg->size,
356 sizeof (uint32_t), mode)) {
357 rets = -EFAULT;
358 goto end;
359 }
360 goto end;
361 } else {
362 DBG("failed to connect to FW client.file_ext->state = %d,"
363 " me_client_id:%d, host_client_id:%d\n",
364 file_ext->state, file_ext->me_client_id,
365 file_ext->host_client_id);
366 if (!err) {
367 DBG("wait_event_interruptible_timeout failed on client"
368 " connect message fw response message.\n");
369 }
370 rets = -EFAULT;
371 goto remove_list;
372 }
373
374 remove_list:
375 if (priv_cb) {
376 mutex_enter(&dev->device_lock);
377 heci_flush_list(&dev->ctrl_rd_list, file_ext);
378 heci_flush_list(&dev->ctrl_wr_list, file_ext);
379 mutex_exit(&dev->device_lock);
380 }
381 end:
382 DBG("free connect cb memory.");
383 kmem_free(req_msg.data, sizeof (struct guid));
384 req_msg.data = NULL;
385 kmem_free(res_msg.data, sizeof (struct heci_client));
386 res_msg.data = NULL;
387 if (priv_cb) {
388 kmem_free(priv_cb, sizeof (struct heci_cb_private));
389 priv_cb = NULL;
390 }
391 return (rets);
392 }
393
394 /*
395 * heci_ioctl_wd - the wd IOCTL function
396 *
397 * @dev: Device object for our driver
398 * @if_num: minor number
399 * @k_msg: data in kernel on the stack
400 * @file_ext: private data of the file object
401 *
402 * @return 0 on success, <0 on failure.
403 */
404 int
heci_ioctl_wd(struct iamt_heci_device * dev,int if_num,struct heci_message_data k_msg,struct heci_file_private * file_ext,int mode)405 heci_ioctl_wd(struct iamt_heci_device *dev, int if_num,
406 struct heci_message_data k_msg,
407 struct heci_file_private *file_ext, int mode)
408 {
409 int rets = 0;
410 struct heci_message_data req_msg; /* in kernel on the stack */
411
412 if (if_num < HECI_MINOR_NUMBER)
413 return (-ENODEV);
414
415 mutex_enter(&file_ext->file_lock);
416 if (k_msg.size != HECI_WATCHDOG_DATA_SIZE) {
417 DBG("user buffer has invalid size.\n");
418 mutex_exit(&file_ext->file_lock);
419 return (-EMSGSIZE);
420 }
421 mutex_exit(&file_ext->file_lock);
422
423 req_msg.data = kmem_zalloc(HECI_WATCHDOG_DATA_SIZE, KM_SLEEP);
424 if (!req_msg.data) {
425 DBG("failed allocation request buffer size = %d.\n",
426 HECI_WATCHDOG_DATA_SIZE);
427 return (-ENOMEM);
428 }
429 req_msg.size = HECI_WATCHDOG_DATA_SIZE;
430
431 /*
432 * copy the message to kernel space - use a pointer already
433 * copied into kernel space
434 */
435 if (ddi_copyin(k_msg.data, req_msg.data, k_msg.size, mode)) {
436 rets = -EFAULT;
437 goto end;
438 }
439 mutex_enter(&dev->device_lock);
440 if (dev->heci_state != HECI_ENABLED) {
441 rets = -ENODEV;
442 mutex_exit(&dev->device_lock);
443 goto end;
444 }
445
446 if (dev->wd_file_ext.state != HECI_FILE_CONNECTED) {
447 rets = -ENODEV;
448 mutex_exit(&dev->device_lock);
449 goto end;
450 }
451 if (!dev->asf_mode) {
452 rets = -EIO;
453 mutex_exit(&dev->device_lock);
454 goto end;
455 }
456
457 (void) memcpy(&dev->wd_data[HECI_WD_PARAMS_SIZE], req_msg.data,
458 HECI_WATCHDOG_DATA_SIZE);
459
460 dev->wd_timeout = (req_msg.data[1] << 8) + req_msg.data[0];
461 dev->wd_pending = 0;
462 dev->wd_due_counter = 1; /* next timer */
463 if (dev->wd_timeout == 0) {
464 (void) memcpy(dev->wd_data, &stop_wd_params,
465 HECI_WD_PARAMS_SIZE);
466 } else {
467 (void) memcpy(dev->wd_data, &start_wd_params,
468 HECI_WD_PARAMS_SIZE);
469 dev->wd_timer = timeout(heci_wd_timer, dev, 1);
470 }
471 mutex_exit(&dev->device_lock);
472 end:
473 kmem_free(req_msg.data, HECI_WATCHDOG_DATA_SIZE);
474 return (rets);
475 }
476
477
478 /*
479 * heci_ioctl_bypass_wd - the bypass_wd IOCTL function
480 *
481 * @dev: Device object for our driver
482 * @if_num: minor number
483 * @k_msg: data in kernel on the stack
484 * @file_ext: private data of the file object
485 *
486 * @return 0 on success, <0 on failure.
487 */
488 int
heci_ioctl_bypass_wd(struct iamt_heci_device * dev,int if_num,struct heci_message_data k_msg,struct heci_file_private * file_ext,int mode)489 heci_ioctl_bypass_wd(struct iamt_heci_device *dev, int if_num,
490 struct heci_message_data k_msg,
491 struct heci_file_private *file_ext, int mode)
492 {
493 uint8_t flag = 0;
494 int rets = 0;
495
496 if (if_num < HECI_MINOR_NUMBER)
497 return (-ENODEV);
498
499 mutex_enter(&file_ext->file_lock);
500 if (k_msg.size < 1) {
501 DBG("user buffer less than HECI_WATCHDOG_DATA_SIZE.\n");
502 mutex_exit(&file_ext->file_lock);
503 return (-EMSGSIZE);
504 }
505 mutex_exit(&file_ext->file_lock);
506 if (ddi_copyin(k_msg.data, &flag, 1, mode)) {
507 rets = -EFAULT;
508 goto end;
509 }
510
511 mutex_enter(&dev->device_lock);
512 flag = flag ? (1) : (0);
513 dev->wd_bypass = flag;
514 mutex_exit(&dev->device_lock);
515 end:
516 return (rets);
517 }
518
519 /*
520 * find_pthi_read_list_entry - finds a PTHIlist entry for current file
521 *
522 * @dev: Device object for our driver
523 * @file: pointer to file object
524 *
525 * @return returned a list entry on success, NULL on failure.
526 */
527 struct heci_cb_private *
find_pthi_read_list_entry(struct iamt_heci_device * dev,struct heci_file * file)528 find_pthi_read_list_entry(
529 struct iamt_heci_device *dev,
530 struct heci_file *file)
531 {
532 struct heci_file_private *file_ext_temp;
533 struct heci_cb_private *priv_cb_pos = NULL;
534 struct heci_cb_private *priv_cb_next = NULL;
535
536 if ((dev->pthi_read_complete_list.status == 0) &&
537 !list_empty(&dev->pthi_read_complete_list.heci_cb.cb_list)) {
538
539 list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
540 &dev->pthi_read_complete_list.heci_cb.cb_list, cb_list,
541 struct heci_cb_private) {
542
543 file_ext_temp = (struct heci_file_private *)
544 priv_cb_pos->file_private;
545 if ((file_ext_temp != NULL) &&
546 (file_ext_temp == &dev->iamthif_file_ext) &&
547 (priv_cb_pos->file_object == file))
548 return (priv_cb_pos);
549 }
550 }
551 return (NULL);
552 }
553
554 /*
555 * pthi_read - read data from pthi client
556 *
557 * @dev: Device object for our driver
558 * @if_num: minor number
559 * @file: pointer to file object
560 * @*ubuf: pointer to user data in user space
561 * @length: data length to read
562 * @offset: data read offset
563 *
564 * @return
565 * returned data length on success,
566 * zero if no data to read,
567 * negative on failure.
568 */
569 int
pthi_read(struct iamt_heci_device * dev,int if_num,struct heci_file * file,struct uio * uio_p)570 pthi_read(struct iamt_heci_device *dev, int if_num, struct heci_file *file,
571 struct uio *uio_p)
572 {
573
574 int rets = 0;
575 struct heci_cb_private *priv_cb = NULL;
576 struct heci_file_private *file_ext = file->private_data;
577 uint8_t i;
578 unsigned long currtime = ddi_get_time();
579
580 if ((if_num < HECI_MINOR_NUMBER) || (!dev))
581 return (-ENODEV);
582
583 if ((file_ext == NULL) || (file_ext != &dev->iamthif_file_ext))
584 return (-ENODEV);
585
586 mutex_enter(&dev->device_lock);
587 for (i = 0; i < dev->num_heci_me_clients; i++) {
588 if (dev->me_clients[i].client_id ==
589 dev->iamthif_file_ext.me_client_id)
590 break;
591 }
592 ASSERT(dev->me_clients[i].client_id == file_ext->me_client_id);
593 if ((i == dev->num_heci_me_clients) ||
594 (dev->me_clients[i].client_id !=
595 dev->iamthif_file_ext.me_client_id)) {
596 DBG("PTHI client not found.\n");
597 mutex_exit(&dev->device_lock);
598 return (-ENODEV);
599 }
600 priv_cb = find_pthi_read_list_entry(dev, file);
601 if (!priv_cb) {
602 mutex_exit(&dev->device_lock);
603 return (0); /* No more data to read */
604 } else {
605 if (priv_cb &&
606 (currtime - priv_cb->read_time > IAMTHIF_READ_TIMER)) {
607 /* 15 sec for the message has expired */
608 list_del(&priv_cb->cb_list);
609 mutex_exit(&dev->device_lock);
610 rets = -ETIMEDOUT;
611 goto free;
612 }
613 /* if the whole message will fit remove it from the list */
614 if ((priv_cb->information >= UIO_OFFSET(uio_p)) &&
615 (UIO_LENGTH(uio_p) >=
616 (priv_cb->information - UIO_OFFSET(uio_p)))) {
617
618 list_del(&priv_cb->cb_list);
619
620 } else if ((priv_cb->information > 0) &&
621 (priv_cb->information <= UIO_OFFSET(uio_p))) {
622
623 /* end of the message has been reached */
624 list_del(&priv_cb->cb_list);
625 rets = 0;
626 mutex_exit(&dev->device_lock);
627 goto free;
628 }
629 /*
630 * else means that not full buffer will be read and do not
631 * remove message from deletion list
632 */
633 }
634 DBG("pthi priv_cb->response_buffer size - %d\n",
635 priv_cb->response_buffer.size);
636 DBG("pthi priv_cb->information - %lu\n",
637 priv_cb->information);
638 mutex_exit(&dev->device_lock);
639
640 rets = uiomove(priv_cb->response_buffer.data,
641 min(uio_p->uio_resid, priv_cb->information),
642 UIO_READ, uio_p);
643 free:
644 DBG("free pthi cb memory.\n");
645 kmem_free(priv_cb->request_buffer.data, priv_cb->request_buffer.size);
646 kmem_free(priv_cb->response_buffer.data, priv_cb->response_buffer.size);
647 kmem_free(priv_cb, sizeof (struct heci_cb_private));
648 return (rets);
649 }
650
651 /*
652 * heci_start_read - the start read client message function.
653 *
654 * @dev: Device object for our driver
655 * @if_num: minor number
656 * @file_ext: private data of the file object
657 *
658 * @return 0 on success, <0 on failure.
659 */
660 int
heci_start_read(struct iamt_heci_device * dev,int if_num,struct heci_file_private * file_ext)661 heci_start_read(struct iamt_heci_device *dev, int if_num,
662 struct heci_file_private *file_ext)
663 {
664 int rets = 0;
665 uint8_t i;
666 struct heci_cb_private *priv_cb = NULL;
667
668 if ((if_num < HECI_MINOR_NUMBER) || (!dev) || (!file_ext)) {
669 DBG("received wrong function input param.\n");
670 return (-ENODEV);
671 }
672 if (file_ext->state != HECI_FILE_CONNECTED)
673 return (-ENODEV);
674
675 mutex_enter(&dev->device_lock);
676 if (dev->heci_state != HECI_ENABLED) {
677 mutex_exit(&dev->device_lock);
678 return (-ENODEV);
679 }
680 mutex_exit(&dev->device_lock);
681 DBG("check if read is pending.\n");
682 if ((file_ext->read_pending) || (file_ext->read_cb != NULL)) {
683 DBG("read is pending.\n");
684 return (-EBUSY);
685 }
686 priv_cb = kmem_zalloc(sizeof (struct heci_cb_private), KM_SLEEP);
687 if (!priv_cb)
688 return (-ENOMEM);
689
690 DBG("allocation call back success\n"
691 "host client = %d, ME client = %d\n",
692 file_ext->host_client_id, file_ext->me_client_id);
693 mutex_enter(&dev->device_lock);
694 for (i = 0; i < dev->num_heci_me_clients; i++) {
695 if (dev->me_clients[i].client_id == file_ext->me_client_id)
696 break;
697
698 }
699
700 ASSERT(dev->me_clients[i].client_id == file_ext->me_client_id);
701 if (i == dev->num_heci_me_clients) {
702 rets = -ENODEV;
703 goto unlock;
704 }
705
706 priv_cb->response_buffer.size = dev->me_clients[i].props.max_msg_length;
707 mutex_exit(&dev->device_lock);
708 priv_cb->response_buffer.data =
709 kmem_zalloc(priv_cb->response_buffer.size, KM_SLEEP);
710 if (!priv_cb->response_buffer.data) {
711 rets = -ENOMEM;
712 goto fail;
713 }
714 DBG("allocation call back data success.\n");
715 priv_cb->major_file_operations = HECI_READ;
716 /* make sure information is zero before we start */
717 priv_cb->information = 0;
718 priv_cb->file_private = (void *)file_ext;
719 file_ext->read_cb = priv_cb;
720 mutex_enter(&dev->device_lock);
721 if (dev->host_buffer_is_empty) {
722 dev->host_buffer_is_empty = 0;
723 if (!heci_send_flow_control(dev, file_ext)) {
724 rets = -ENODEV;
725 goto unlock;
726 } else {
727 list_add_tail(&priv_cb->cb_list,
728 &dev->read_list.heci_cb.cb_list);
729 }
730 } else {
731 list_add_tail(&priv_cb->cb_list,
732 &dev->ctrl_wr_list.heci_cb.cb_list);
733 }
734 mutex_exit(&dev->device_lock);
735 return (rets);
736 unlock:
737 mutex_exit(&dev->device_lock);
738 fail:
739 heci_free_cb_private(priv_cb);
740 return (rets);
741 }
742
743 /*
744 * pthi_write: write iamthif data to pthi client
745 *
746 * @dev: Device object for our driver
747 * @priv_cb: heci call back struct
748 *
749 * @return 0 on success, <0 on failure.
750 */
751 int
pthi_write(struct iamt_heci_device * dev,struct heci_cb_private * priv_cb)752 pthi_write(struct iamt_heci_device *dev,
753 struct heci_cb_private *priv_cb)
754 {
755 int rets = 0;
756 struct heci_msg_hdr heci_hdr;
757
758 if ((!dev) || (!priv_cb))
759 return (-ENODEV);
760
761 DBG("write data to pthi client.\n");
762
763 dev->iamthif_state = HECI_IAMTHIF_WRITING;
764 dev->iamthif_current_cb = priv_cb;
765 dev->iamthif_file_object = priv_cb->file_object;
766 dev->iamthif_canceled = 0;
767 dev->iamthif_ioctl = 1;
768 dev->iamthif_msg_buf_size = priv_cb->request_buffer.size;
769 (void) memcpy(dev->iamthif_msg_buf, priv_cb->request_buffer.data,
770 priv_cb->request_buffer.size);
771
772 if (flow_ctrl_creds(dev, &dev->iamthif_file_ext) &&
773 dev->host_buffer_is_empty) {
774 dev->host_buffer_is_empty = 0;
775 if (priv_cb->request_buffer.size >
776 (((dev->host_hw_state & H_CBD) >> 24) *
777 sizeof (uint32_t)) - sizeof (struct heci_msg_hdr)) {
778 heci_hdr.length =
779 (((dev->host_hw_state & H_CBD) >> 24) *
780 sizeof (uint32_t)) - sizeof (struct heci_msg_hdr);
781 heci_hdr.msg_complete = 0;
782 } else {
783 heci_hdr.length = priv_cb->request_buffer.size;
784 heci_hdr.msg_complete = 1;
785 }
786
787 heci_hdr.host_addr = dev->iamthif_file_ext.host_client_id;
788 heci_hdr.me_addr = dev->iamthif_file_ext.me_client_id;
789 heci_hdr.reserved = 0;
790 dev->iamthif_msg_buf_index += heci_hdr.length;
791 if (!heci_write_message(dev, &heci_hdr,
792 (unsigned char *)(dev->iamthif_msg_buf),
793 heci_hdr.length))
794 return (-ENODEV);
795
796 if (heci_hdr.msg_complete) {
797 flow_ctrl_reduce(dev, &dev->iamthif_file_ext);
798 dev->iamthif_flow_control_pending = 1;
799 dev->iamthif_state = HECI_IAMTHIF_FLOW_CONTROL;
800 DBG("add pthi cb to write waiting list\n");
801 dev->iamthif_current_cb = priv_cb;
802 dev->iamthif_file_object = priv_cb->file_object;
803 list_add_tail(&priv_cb->cb_list,
804 &dev->write_waiting_list.heci_cb.cb_list);
805 } else {
806 DBG("message does not complete, "
807 "so add pthi cb to write list.\n");
808 list_add_tail(&priv_cb->cb_list,
809 &dev->write_list.heci_cb.cb_list);
810 }
811 } else {
812 if (!(dev->host_buffer_is_empty))
813 DBG("host buffer is not empty");
814
815 DBG("No flow control credentials, "
816 "so add iamthif cb to write list.\n");
817 list_add_tail(&priv_cb->cb_list,
818 &dev->write_list.heci_cb.cb_list);
819 }
820 return (rets);
821 }
822
823 /*
824 * iamthif_ioctl_send_msg - send cmd data to pthi client
825 *
826 * @dev: Device object for our driver
827 *
828 * @return 0 on success, <0 on failure.
829 */
830 void
run_next_iamthif_cmd(struct iamt_heci_device * dev)831 run_next_iamthif_cmd(struct iamt_heci_device *dev)
832 {
833 struct heci_file_private *file_ext_tmp;
834 struct heci_cb_private *priv_cb_pos = NULL;
835 struct heci_cb_private *priv_cb_next = NULL;
836 int status = 0;
837
838 if (!dev)
839 return;
840
841 dev->iamthif_msg_buf_size = 0;
842 dev->iamthif_msg_buf_index = 0;
843 dev->iamthif_canceled = 0;
844 dev->iamthif_ioctl = 1;
845 dev->iamthif_state = HECI_IAMTHIF_IDLE;
846 dev->iamthif_timer = 0;
847 dev->iamthif_file_object = NULL;
848
849 if (dev->pthi_cmd_list.status == 0 &&
850 !list_empty(&dev->pthi_cmd_list.heci_cb.cb_list)) {
851 DBG("complete pthi cmd_list cb.\n");
852
853 list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
854 &dev->pthi_cmd_list.heci_cb.cb_list, cb_list,
855 struct heci_cb_private) {
856
857 list_del(&priv_cb_pos->cb_list);
858 file_ext_tmp = (struct heci_file_private *)
859 priv_cb_pos->file_private;
860
861 if ((file_ext_tmp != NULL) &&
862 (file_ext_tmp == &dev->iamthif_file_ext)) {
863 status = pthi_write(dev, priv_cb_pos);
864 if (status != 0) {
865 DBG("pthi write failed status = %d\n",
866 status);
867 return;
868 }
869 break;
870 }
871 }
872 }
873 }
874
875 /*
876 * heci_free_cb_private - free heci_cb_private related memory
877 *
878 * @priv_cb: heci callback struct
879 */
880 void
heci_free_cb_private(struct heci_cb_private * priv_cb)881 heci_free_cb_private(struct heci_cb_private *priv_cb)
882 {
883 if (priv_cb == NULL)
884 return;
885
886 kmem_free(priv_cb->request_buffer.data, priv_cb->request_buffer.size);
887 kmem_free(priv_cb->response_buffer.data, priv_cb->response_buffer.size);
888 kmem_free(priv_cb, sizeof (struct heci_cb_private));
889 }
890
891 /*
892 * heci_fe_same_id - tell if file private data have same id
893 *
894 * @fe1: private data of 1. file object
895 * @fe2: private data of 2. file object
896 *
897 * @return !=0 - if ids are the same, 0 - if differ.
898 */
heci_fe_same_id(struct heci_file_private * fe1,struct heci_file_private * fe2)899 static inline int heci_fe_same_id(struct heci_file_private *fe1,
900 struct heci_file_private *fe2)
901 {
902 return ((fe1->host_client_id == fe2->host_client_id) &&
903 (fe1->me_client_id == fe2->me_client_id));
904 }
905