io_heci.c revision 9203:3ebffd0a1b10
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
68static 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 */
81int
82heci_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	}
128end:
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 */
144int
145heci_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		}
195fail:
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		clock_t tm;
340		tm = ddi_get_lbolt();
341		err = cv_timedwait(&dev->wait_recvd_msg,
342		    &dev->device_lock, tm + timeout * HZ);
343	}
344	mutex_exit(&dev->device_lock);
345
346	if (HECI_FILE_CONNECTED == file_ext->state) {
347		DBG("successfully connected to FW client."
348		    " me_client_id:%d, host_client_id:%d\n",
349		    file_ext->me_client_id,
350		    file_ext->host_client_id);
351		rets = file_ext->status;
352		/* now copy the data to user space */
353		if (ddi_copyout(res_msg.data, k_msg.data, res_msg.size, mode)) {
354			rets = -EFAULT;
355			goto end;
356		}
357		if (ddi_copyout(&res_msg.size, &u_msg->size,
358		    sizeof (uint32_t), mode)) {
359			rets = -EFAULT;
360			goto end;
361		}
362		goto end;
363	} else {
364		DBG("failed to connect to FW client.file_ext->state = %d,"
365		    " me_client_id:%d, host_client_id:%d\n",
366		    file_ext->state, file_ext->me_client_id,
367		    file_ext->host_client_id);
368		if (!err) {
369			DBG("wait_event_interruptible_timeout failed on client"
370			    " connect message fw response message.\n");
371		}
372		rets = -EFAULT;
373		goto remove_list;
374	}
375
376remove_list:
377	if (priv_cb) {
378		mutex_enter(&dev->device_lock);
379		heci_flush_list(&dev->ctrl_rd_list, file_ext);
380		heci_flush_list(&dev->ctrl_wr_list, file_ext);
381		mutex_exit(&dev->device_lock);
382	}
383end:
384	DBG("free connect cb memory.");
385	kmem_free(req_msg.data, sizeof (struct guid));
386	req_msg.data = NULL;
387	kmem_free(res_msg.data, sizeof (struct heci_client));
388	res_msg.data = NULL;
389	if (priv_cb) {
390		kmem_free(priv_cb, sizeof (struct heci_cb_private));
391		priv_cb = NULL;
392	}
393	return (rets);
394}
395
396/*
397 * heci_ioctl_wd  - the wd IOCTL function
398 *
399 * @dev: Device object for our driver
400 * @if_num:  minor number
401 * @k_msg: data in kernel on the stack
402 * @file_ext: private data of the file object
403 *
404 * @return 0 on success, <0 on failure.
405 */
406int
407heci_ioctl_wd(struct iamt_heci_device *dev, int if_num,
408	struct heci_message_data k_msg,
409	struct heci_file_private *file_ext, int mode)
410{
411	int rets = 0;
412	struct heci_message_data req_msg;	/* in kernel on the stack */
413
414	if (if_num < HECI_MINOR_NUMBER)
415		return (-ENODEV);
416
417	mutex_enter(&file_ext->file_lock);
418	if (k_msg.size != HECI_WATCHDOG_DATA_SIZE) {
419		DBG("user buffer has invalid size.\n");
420		mutex_exit(&file_ext->file_lock);
421		return (-EMSGSIZE);
422	}
423	mutex_exit(&file_ext->file_lock);
424
425	req_msg.data = kmem_zalloc(HECI_WATCHDOG_DATA_SIZE, KM_SLEEP);
426	if (!req_msg.data) {
427		DBG("failed allocation request buffer size = %d.\n",
428		    HECI_WATCHDOG_DATA_SIZE);
429		return (-ENOMEM);
430	}
431	req_msg.size = HECI_WATCHDOG_DATA_SIZE;
432
433	/*
434	 * copy the message to kernel space - use a pointer already
435	 * copied into kernel space
436	 */
437	if (ddi_copyin(k_msg.data, req_msg.data, k_msg.size, mode)) {
438		rets = -EFAULT;
439		goto end;
440	}
441	mutex_enter(&dev->device_lock);
442	if (dev->heci_state != HECI_ENABLED) {
443		rets = -ENODEV;
444		mutex_exit(&dev->device_lock);
445		goto end;
446	}
447
448	if (dev->wd_file_ext.state != HECI_FILE_CONNECTED) {
449		rets = -ENODEV;
450		mutex_exit(&dev->device_lock);
451		goto end;
452	}
453	if (!dev->asf_mode) {
454		rets = -EIO;
455		mutex_exit(&dev->device_lock);
456		goto end;
457	}
458
459	(void) memcpy(&dev->wd_data[HECI_WD_PARAMS_SIZE], req_msg.data,
460	    HECI_WATCHDOG_DATA_SIZE);
461
462	dev->wd_timeout = (req_msg.data[1] << 8) + req_msg.data[0];
463	dev->wd_pending = 0;
464	dev->wd_due_counter = 1;	/* next timer */
465	if (dev->wd_timeout == 0) {
466		(void) memcpy(dev->wd_data, &stop_wd_params,
467		    HECI_WD_PARAMS_SIZE);
468	} else {
469		(void) memcpy(dev->wd_data, &start_wd_params,
470		    HECI_WD_PARAMS_SIZE);
471		dev->wd_timer = timeout(heci_wd_timer, dev, 1);
472	}
473	mutex_exit(&dev->device_lock);
474end:
475	kmem_free(req_msg.data, HECI_WATCHDOG_DATA_SIZE);
476	return (rets);
477}
478
479
480/*
481 * heci_ioctl_bypass_wd  - the bypass_wd IOCTL function
482 *
483 * @dev: Device object for our driver
484 * @if_num:  minor number
485 * @k_msg: data in kernel on the stack
486 * @file_ext: private data of the file object
487 *
488 * @return 0 on success, <0 on failure.
489 */
490int
491heci_ioctl_bypass_wd(struct iamt_heci_device *dev, int if_num,
492	struct heci_message_data k_msg,
493	struct heci_file_private *file_ext, int mode)
494{
495	uint8_t flag = 0;
496	int rets = 0;
497
498	if (if_num < HECI_MINOR_NUMBER)
499		return (-ENODEV);
500
501	mutex_enter(&file_ext->file_lock);
502	if (k_msg.size < 1) {
503		DBG("user buffer less than HECI_WATCHDOG_DATA_SIZE.\n");
504		mutex_exit(&file_ext->file_lock);
505		return (-EMSGSIZE);
506	}
507	mutex_exit(&file_ext->file_lock);
508	if (ddi_copyin(k_msg.data, &flag, 1, mode)) {
509		rets = -EFAULT;
510		goto end;
511	}
512
513	mutex_enter(&dev->device_lock);
514	flag = flag ? (1) : (0);
515	dev->wd_bypass = flag;
516	mutex_exit(&dev->device_lock);
517end:
518	return (rets);
519}
520
521/*
522 * find_pthi_read_list_entry - finds a PTHIlist entry for current file
523 *
524 * @dev: Device object for our driver
525 * @file: pointer to file object
526 *
527 * @return   returned a list entry on success, NULL on failure.
528 */
529struct heci_cb_private *
530find_pthi_read_list_entry(
531		struct iamt_heci_device *dev,
532		struct heci_file *file)
533{
534	struct heci_file_private *file_ext_temp;
535	struct heci_cb_private *priv_cb_pos = NULL;
536	struct heci_cb_private *priv_cb_next = NULL;
537
538	if ((dev->pthi_read_complete_list.status == 0) &&
539	    !list_empty(&dev->pthi_read_complete_list.heci_cb.cb_list)) {
540
541		list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
542		    &dev->pthi_read_complete_list.heci_cb.cb_list, cb_list,
543		    struct heci_cb_private) {
544
545			file_ext_temp = (struct heci_file_private *)
546					priv_cb_pos->file_private;
547			if ((file_ext_temp != NULL) &&
548			    (file_ext_temp == &dev->iamthif_file_ext) &&
549			    (priv_cb_pos->file_object == file))
550				return (priv_cb_pos);
551		}
552	}
553	return (NULL);
554}
555
556/*
557 * pthi_read - read data from pthi client
558 *
559 * @dev: Device object for our driver
560 * @if_num:  minor number
561 * @file: pointer to file object
562 * @*ubuf: pointer to user data in user space
563 * @length: data length to read
564 * @offset: data read offset
565 *
566 * @return
567 *  returned data length on success,
568 *  zero if no data to read,
569 *  negative on failure.
570 */
571int
572pthi_read(struct iamt_heci_device *dev, int if_num, struct heci_file *file,
573	struct uio *uio_p)
574{
575
576	int rets = 0;
577	struct heci_cb_private *priv_cb = NULL;
578	struct heci_file_private *file_ext = file->private_data;
579	uint8_t i;
580	unsigned long currtime = ddi_get_time();
581
582	if ((if_num < HECI_MINOR_NUMBER) || (!dev))
583		return (-ENODEV);
584
585	if ((file_ext == NULL) || (file_ext != &dev->iamthif_file_ext))
586		return (-ENODEV);
587
588	mutex_enter(&dev->device_lock);
589	for (i = 0; i < dev->num_heci_me_clients; i++) {
590		if (dev->me_clients[i].client_id ==
591		    dev->iamthif_file_ext.me_client_id)
592			break;
593	}
594	ASSERT(dev->me_clients[i].client_id == file_ext->me_client_id);
595	if ((i == dev->num_heci_me_clients) ||
596	    (dev->me_clients[i].client_id !=
597	    dev->iamthif_file_ext.me_client_id)) {
598		DBG("PTHI client not found.\n");
599		mutex_exit(&dev->device_lock);
600		return (-ENODEV);
601	}
602	priv_cb = find_pthi_read_list_entry(dev, file);
603	if (!priv_cb) {
604		mutex_exit(&dev->device_lock);
605		return (0); /* No more data to read */
606	} else {
607		if (priv_cb &&
608		    (currtime - priv_cb->read_time > IAMTHIF_READ_TIMER)) {
609			/* 15 sec for the message has expired */
610			list_del(&priv_cb->cb_list);
611			mutex_exit(&dev->device_lock);
612			rets = -ETIMEDOUT;
613			goto free;
614		}
615		/* if the whole message will fit remove it from the list */
616		if ((priv_cb->information >= UIO_OFFSET(uio_p)) &&
617		    (UIO_LENGTH(uio_p) >=
618		    (priv_cb->information - UIO_OFFSET(uio_p)))) {
619
620			list_del(&priv_cb->cb_list);
621
622		} else if ((priv_cb->information > 0) &&
623		    (priv_cb->information <= UIO_OFFSET(uio_p))) {
624
625			/* end of the message has been reached */
626			list_del(&priv_cb->cb_list);
627			rets = 0;
628			mutex_exit(&dev->device_lock);
629			goto free;
630		}
631		/*
632		 * else means that not full buffer will be read and do not
633		 * remove message from deletion list
634		 */
635	}
636	DBG("pthi priv_cb->response_buffer size - %d\n",
637	    priv_cb->response_buffer.size);
638	DBG("pthi priv_cb->information - %lu\n",
639	    priv_cb->information);
640	mutex_exit(&dev->device_lock);
641
642	rets = uiomove(priv_cb->response_buffer.data,
643	    min(uio_p->uio_resid, priv_cb->information),
644	    UIO_READ, uio_p);
645free:
646	DBG("free pthi cb memory.\n");
647	kmem_free(priv_cb->request_buffer.data, priv_cb->request_buffer.size);
648	kmem_free(priv_cb->response_buffer.data, priv_cb->response_buffer.size);
649	kmem_free(priv_cb, sizeof (struct heci_cb_private));
650	return (rets);
651}
652
653/*
654 * heci_start_read  - the start read client message function.
655 *
656 * @dev: Device object for our driver
657 * @if_num:  minor number
658 * @file_ext: private data of the file object
659 *
660 * @return 0 on success, <0 on failure.
661 */
662int
663heci_start_read(struct iamt_heci_device *dev, int if_num,
664		    struct heci_file_private *file_ext)
665{
666	int rets = 0;
667	uint8_t i;
668	struct heci_cb_private *priv_cb = NULL;
669
670	if ((if_num < HECI_MINOR_NUMBER) || (!dev) || (!file_ext)) {
671		DBG("received wrong function input param.\n");
672		return (-ENODEV);
673	}
674	if (file_ext->state != HECI_FILE_CONNECTED)
675		return (-ENODEV);
676
677	mutex_enter(&dev->device_lock);
678	if (dev->heci_state != HECI_ENABLED) {
679		mutex_exit(&dev->device_lock);
680		return (-ENODEV);
681	}
682	mutex_exit(&dev->device_lock);
683	DBG("check if read is pending.\n");
684	if ((file_ext->read_pending) || (file_ext->read_cb != NULL)) {
685		DBG("read is pending.\n");
686		return (-EBUSY);
687	}
688	priv_cb = kmem_zalloc(sizeof (struct heci_cb_private), KM_SLEEP);
689	if (!priv_cb)
690		return (-ENOMEM);
691
692	DBG("allocation call back success\n"
693	    "host client = %d, ME client = %d\n",
694	    file_ext->host_client_id, file_ext->me_client_id);
695	mutex_enter(&dev->device_lock);
696	for (i = 0; i < dev->num_heci_me_clients; i++) {
697		if (dev->me_clients[i].client_id == file_ext->me_client_id)
698			break;
699
700	}
701
702	ASSERT(dev->me_clients[i].client_id == file_ext->me_client_id);
703	if (i == dev->num_heci_me_clients) {
704		rets = -ENODEV;
705		goto unlock;
706	}
707
708	priv_cb->response_buffer.size = dev->me_clients[i].props.max_msg_length;
709	mutex_exit(&dev->device_lock);
710	priv_cb->response_buffer.data =
711	    kmem_zalloc(priv_cb->response_buffer.size, KM_SLEEP);
712	if (!priv_cb->response_buffer.data) {
713		rets = -ENOMEM;
714		goto fail;
715	}
716	DBG("allocation call back data success.\n");
717	priv_cb->major_file_operations = HECI_READ;
718	/* make sure information is zero before we start */
719	priv_cb->information = 0;
720	priv_cb->file_private = (void *)file_ext;
721	file_ext->read_cb = priv_cb;
722	mutex_enter(&dev->device_lock);
723	if (dev->host_buffer_is_empty) {
724		dev->host_buffer_is_empty = 0;
725		if (!heci_send_flow_control(dev, file_ext)) {
726			rets = -ENODEV;
727			goto unlock;
728		} else {
729			list_add_tail(&priv_cb->cb_list,
730			    &dev->read_list.heci_cb.cb_list);
731		}
732	} else {
733		list_add_tail(&priv_cb->cb_list,
734		    &dev->ctrl_wr_list.heci_cb.cb_list);
735	}
736	mutex_exit(&dev->device_lock);
737	return (rets);
738unlock:
739	mutex_exit(&dev->device_lock);
740fail:
741	heci_free_cb_private(priv_cb);
742	return (rets);
743}
744
745/*
746 * pthi_write: write iamthif data to pthi client
747 *
748 * @dev: Device object for our driver
749 * @priv_cb: heci call back struct
750 *
751 * @return 0 on success, <0 on failure.
752 */
753int
754pthi_write(struct iamt_heci_device *dev,
755	struct heci_cb_private *priv_cb)
756{
757	int rets = 0;
758	struct heci_msg_hdr heci_hdr;
759
760	if ((!dev) || (!priv_cb))
761		return (-ENODEV);
762
763	DBG("write data to pthi client.\n");
764
765	dev->iamthif_state = HECI_IAMTHIF_WRITING;
766	dev->iamthif_current_cb = priv_cb;
767	dev->iamthif_file_object = priv_cb->file_object;
768	dev->iamthif_canceled = 0;
769	dev->iamthif_ioctl = 1;
770	dev->iamthif_msg_buf_size = priv_cb->request_buffer.size;
771	(void) memcpy(dev->iamthif_msg_buf, priv_cb->request_buffer.data,
772	    priv_cb->request_buffer.size);
773
774	if (flow_ctrl_creds(dev, &dev->iamthif_file_ext) &&
775	    dev->host_buffer_is_empty) {
776		dev->host_buffer_is_empty = 0;
777		if (priv_cb->request_buffer.size >
778		    (((dev->host_hw_state & H_CBD) >> 24) *
779		    sizeof (uint32_t)) - sizeof (struct heci_msg_hdr)) {
780			heci_hdr.length =
781			    (((dev->host_hw_state & H_CBD) >> 24) *
782			    sizeof (uint32_t)) - sizeof (struct heci_msg_hdr);
783			heci_hdr.msg_complete = 0;
784		} else {
785			heci_hdr.length = priv_cb->request_buffer.size;
786			heci_hdr.msg_complete = 1;
787		}
788
789		heci_hdr.host_addr = dev->iamthif_file_ext.host_client_id;
790		heci_hdr.me_addr = dev->iamthif_file_ext.me_client_id;
791		heci_hdr.reserved = 0;
792		dev->iamthif_msg_buf_index += heci_hdr.length;
793		if (!heci_write_message(dev, &heci_hdr,
794		    (unsigned char *)(dev->iamthif_msg_buf),
795		    heci_hdr.length))
796			return (-ENODEV);
797
798		if (heci_hdr.msg_complete) {
799			flow_ctrl_reduce(dev, &dev->iamthif_file_ext);
800			dev->iamthif_flow_control_pending = 1;
801			dev->iamthif_state = HECI_IAMTHIF_FLOW_CONTROL;
802			DBG("add pthi cb to write waiting list\n");
803			dev->iamthif_current_cb = priv_cb;
804			dev->iamthif_file_object = priv_cb->file_object;
805			list_add_tail(&priv_cb->cb_list,
806			    &dev->write_waiting_list.heci_cb.cb_list);
807		} else {
808			DBG("message does not complete, "
809			    "so add pthi cb to write list.\n");
810			list_add_tail(&priv_cb->cb_list,
811			    &dev->write_list.heci_cb.cb_list);
812		}
813	} else {
814		if (!(dev->host_buffer_is_empty))
815			DBG("host buffer is not empty");
816
817		DBG("No flow control credentials, "
818		    "so add iamthif cb to write list.\n");
819		list_add_tail(&priv_cb->cb_list,
820		    &dev->write_list.heci_cb.cb_list);
821	}
822	return (rets);
823}
824
825/*
826 * iamthif_ioctl_send_msg - send cmd data to pthi client
827 *
828 * @dev: Device object for our driver
829 *
830 * @return 0 on success, <0 on failure.
831 */
832void
833run_next_iamthif_cmd(struct iamt_heci_device *dev)
834{
835	struct heci_file_private *file_ext_tmp;
836	struct heci_cb_private *priv_cb_pos = NULL;
837	struct heci_cb_private *priv_cb_next = NULL;
838	int status = 0;
839
840	if (!dev)
841		return;
842
843	dev->iamthif_msg_buf_size = 0;
844	dev->iamthif_msg_buf_index = 0;
845	dev->iamthif_canceled = 0;
846	dev->iamthif_ioctl = 1;
847	dev->iamthif_state = HECI_IAMTHIF_IDLE;
848	dev->iamthif_timer = 0;
849	dev->iamthif_file_object = NULL;
850
851	if (dev->pthi_cmd_list.status == 0 &&
852	    !list_empty(&dev->pthi_cmd_list.heci_cb.cb_list)) {
853		DBG("complete pthi cmd_list cb.\n");
854
855		list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
856		    &dev->pthi_cmd_list.heci_cb.cb_list, cb_list,
857		    struct heci_cb_private) {
858
859			list_del(&priv_cb_pos->cb_list);
860			file_ext_tmp = (struct heci_file_private *)
861					priv_cb_pos->file_private;
862
863			if ((file_ext_tmp != NULL) &&
864			    (file_ext_tmp == &dev->iamthif_file_ext)) {
865				status = pthi_write(dev, priv_cb_pos);
866				if (status != 0) {
867					DBG("pthi write failed status = %d\n",
868							status);
869					return;
870				}
871				break;
872			}
873		}
874	}
875}
876
877/*
878 * heci_free_cb_private - free heci_cb_private related memory
879 *
880 * @priv_cb: heci callback struct
881 */
882void
883heci_free_cb_private(struct heci_cb_private *priv_cb)
884{
885	if (priv_cb == NULL)
886		return;
887
888	kmem_free(priv_cb->request_buffer.data, priv_cb->request_buffer.size);
889	kmem_free(priv_cb->response_buffer.data, priv_cb->response_buffer.size);
890	kmem_free(priv_cb, sizeof (struct heci_cb_private));
891}
892
893/*
894 * heci_fe_same_id - tell if file private data have same id
895 *
896 * @fe1: private data of 1. file object
897 * @fe2: private data of 2. file object
898 *
899 * @return  !=0 - if ids are the same, 0 - if differ.
900 */
901static inline int heci_fe_same_id(struct heci_file_private *fe1,
902		struct heci_file_private *fe2)
903{
904	return ((fe1->host_client_id == fe2->host_client_id) &&
905	    (fe1->me_client_id == fe2->me_client_id));
906}
907