xref: /titanic_41/usr/src/uts/intel/io/heci/heci_intr.c (revision bdb9230ac765cb7af3fc1f4119caf2c5720dceb3)
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/cmn_err.h>
48 #include <sys/conf.h>
49 #include <sys/ddi.h>
50 #include <sys/ddi_impldefs.h>
51 #include <sys/devops.h>
52 #include <sys/instance.h>
53 #include <sys/modctl.h>
54 #include <sys/open.h>
55 #include <sys/stat.h>
56 #include <sys/sunddi.h>
57 #include <sys/sunndi.h>
58 #include <sys/systm.h>
59 #include <sys/mkdev.h>
60 #include <sys/list.h>
61 #include <sys/note.h>
62 #include "heci_data_structures.h"
63 
64 #include "heci.h"
65 #include "heci_interface.h"
66 
67 /*
68  *  interrupt function prototypes
69  */
70 static void heci_bh_handler(void *data);
71 static int heci_bh_read_handler(struct io_heci_list *complete_list,
72 		struct iamt_heci_device *dev,
73 		int32_t *slots);
74 static int heci_bh_write_handler(struct io_heci_list *complete_list,
75 		struct iamt_heci_device *dev,
76 		int32_t *slots);
77 static void heci_bh_read_bus_message(struct iamt_heci_device *dev,
78 		struct heci_msg_hdr *heci_hdr);
79 static int heci_bh_read_pthi_message(struct io_heci_list *complete_list,
80 		struct iamt_heci_device *dev,
81 		struct heci_msg_hdr *heci_hdr);
82 static int heci_bh_read_client_message(struct io_heci_list *complete_list,
83 		struct iamt_heci_device *dev,
84 		struct heci_msg_hdr *heci_hdr);
85 static void heci_client_connect_response(struct iamt_heci_device *dev,
86 		struct hbm_client_connect_response *connect_res);
87 static void heci_client_disconnect_response(struct iamt_heci_device *dev,
88 		struct hbm_client_connect_response *disconnect_res);
89 static void heci_client_flow_control_response(struct iamt_heci_device *dev,
90 		struct hbm_flow_control *flow_control);
91 static void heci_client_disconnect_request(struct iamt_heci_device *dev,
92 		struct hbm_client_disconnect_request *disconnect_req);
93 
94 static int heci_bh_process_device(struct iamt_heci_device *dev);
95 
96 /*
97  * heci_isr_interrupt - The ISR of the HECI device
98  *
99  * @irq: The irq number
100  * @dev_id: pointer to the device structure
101  *
102  * @return irqreturn_t
103  */
104 uint_t
105 heci_isr_interrupt(caddr_t arg1)
106 {
107 	struct iamt_heci_device *device =
108 	    (struct iamt_heci_device *)(void *)arg1;
109 
110 	mutex_enter(&device->device_lock);
111 
112 	if (device->heci_state == HECI_POWER_DOWN) {
113 		mutex_exit(&device->device_lock);
114 		return (DDI_INTR_UNCLAIMED);
115 	}
116 	device->host_hw_state = read_heci_register(device, H_CSR);
117 
118 	if ((device->host_hw_state & H_IS) != H_IS) {
119 		mutex_exit(&device->device_lock);
120 		return (DDI_INTR_UNCLAIMED);
121 	}
122 
123 	/* disable interrupts */
124 	heci_csr_disable_interrupts(device);
125 
126 	mutex_exit(&device->device_lock);
127 
128 	/*
129 	 * Our device interrupted, schedule work the heci_bh_handler
130 	 * to handle the interrupt processing. This needs to be a
131 	 * taskq
132 	 */
133 	DBG("schedule work the heci_bh_handler.\n");
134 	if (ddi_taskq_dispatch(device->work, heci_bh_handler,
135 	    (void*)arg1, DDI_NOSLEEP) == DDI_FAILURE)
136 		cmn_err(CE_WARN, "taskq_dispatch failed for heci_bh_handler");
137 
138 	return (DDI_INTR_CLAIMED);
139 }
140 
141 /*
142  * _heci_cmpl: process completed operation.
143  *
144  * @file_ext: private data of the file object.
145  * @priv_cb_pos: callback block.
146  */
147 static void _heci_cmpl(struct heci_file_private *file_ext,
148 				struct heci_cb_private *priv_cb_pos)
149 {
150 	if (priv_cb_pos->major_file_operations == HECI_WRITE) {
151 		heci_free_cb_private(priv_cb_pos);
152 		DBG("completing write call back.\n");
153 		file_ext->writing_state = HECI_WRITE_COMPLETE;
154 		pollwakeup(&file_ext->tx_pollwait, POLL_IN|POLLRDNORM);
155 
156 	} else if (priv_cb_pos->major_file_operations == HECI_READ &&
157 	    HECI_READING == file_ext->reading_state) {
158 		DBG("completing read call back information= %lu\n",
159 		    priv_cb_pos->information);
160 		file_ext->reading_state = HECI_READ_COMPLETE;
161 		cv_broadcast(&file_ext->rx_wait);
162 
163 	}
164 }
165 
166 /*
167  * _heci_cmpl_iamthif: process completed iamthif operation.
168  *
169  * @dev: Device object for our driver.
170  * @priv_cb_pos: callback block.
171  */
172 static void _heci_cmpl_iamthif(struct iamt_heci_device *dev,
173 				struct heci_cb_private *priv_cb_pos)
174 {
175 	if (dev->iamthif_canceled != 1) {
176 		dev->iamthif_state = HECI_IAMTHIF_READ_COMPLETE;
177 		dev->iamthif_stall_timer = 0;
178 		(void) memcpy(priv_cb_pos->response_buffer.data,
179 		    dev->iamthif_msg_buf,
180 		    dev->iamthif_msg_buf_index);
181 		list_add_tail(&priv_cb_pos->cb_list,
182 		    &dev->pthi_read_complete_list.heci_cb.cb_list);
183 		DBG("pthi read completed.\n");
184 	} else {
185 		run_next_iamthif_cmd(dev);
186 	}
187 	if (&dev->iamthif_file_ext.pollwait) {
188 		DBG("completing pthi call back.\n");
189 		pollwakeup(&dev->iamthif_file_ext.pollwait, POLL_IN|POLLRDNORM);
190 	}
191 }
192 /*
193  * heci_bh_handler - function called after ISR to handle the interrupt
194  * processing.
195  *
196  * @work: pointer to the work structure
197  *
198  * NOTE: This function is called by schedule work
199  */
200 static void
201 heci_bh_handler(void *data)
202 {
203 	struct iamt_heci_device *dev = (struct iamt_heci_device *)data;
204 
205 #ifdef SUNOS
206 	while (heci_bh_process_device(dev))
207 		;
208 #elif defined(LINUX)
209 	if (heci_bh_process_device(dev)) {
210 		PEPARE_WORK(&dev->work, heci_bh_handler);
211 		DBG("schedule work the heci_bh_handler.\n");
212 		rets = schedule_work(&dev->work);
213 		if (!rets) {
214 			printk(KERN_ERR "heci: schedule the heci_bh_handler"
215 			" failed error=%x\n", rets);
216 		}
217 	}
218 #else
219 
220 #error "Unknown platform!"
221 
222 #endif
223 }
224 
225 static int
226 heci_bh_process_device(struct iamt_heci_device *dev)
227 {
228 	struct io_heci_list complete_list;
229 	int32_t slots;
230 	int rets, isr_pending = 0;
231 	struct heci_cb_private *cb_pos = NULL, *cb_next = NULL;
232 	struct heci_file_private *file_ext;
233 	int bus_message_received = 0;
234 
235 	DBG("function called after ISR to handle the interrupt processing.\n");
236 	/* initialize our complete list */
237 	mutex_enter(&dev->device_lock);
238 	heci_initialize_list(&complete_list, dev);
239 	dev->host_hw_state = read_heci_register(dev, H_CSR);
240 	dev->me_hw_state = read_heci_register(dev, ME_CSR_HA);
241 
242 	/* check if ME wants a reset */
243 	if (((dev->me_hw_state & ME_RDY_HRA) == 0) &&
244 	    (dev->heci_state != HECI_RESETING) &&
245 	    (dev->heci_state != HECI_INITIALIZING)) {
246 		DBG("FW not ready.\n");
247 		heci_reset(dev, 1);
248 		mutex_exit(&dev->device_lock);
249 		return (0);
250 	}
251 
252 	/*  check if we need to start the dev */
253 	if ((dev->host_hw_state & H_RDY) == 0) {
254 		if ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA) {
255 			DBG("we need to start the dev.\n");
256 			dev->host_hw_state |= (H_IE | H_IG | H_RDY);
257 			heci_set_csr_register(dev);
258 			if (dev->heci_state == HECI_INITIALIZING) {
259 				dev->recvd_msg = 1;
260 				cv_broadcast(&dev->wait_recvd_msg);
261 				mutex_exit(&dev->device_lock);
262 
263 				return (0);
264 
265 			} else {
266 				mutex_exit(&dev->device_lock);
267 				if (dev->reinit_tsk &&
268 				    ddi_taskq_dispatch(dev->reinit_tsk,
269 				    heci_task_initialize_clients,
270 				    dev, DDI_SLEEP) == DDI_FAILURE) {
271 
272 					cmn_err(CE_WARN, "taskq_dispatch "
273 					    "failed for reinit_tsk");
274 				}
275 				return (0);
276 			}
277 		} else {
278 			DBG("enable interrupt FW not ready.\n");
279 			heci_csr_enable_interrupts(dev);
280 			mutex_exit(&dev->device_lock);
281 			return (0);
282 		}
283 	}
284 	/* check slots avalable for reading */
285 	slots = count_full_read_slots(dev);
286 	DBG("slots =%08x  extra_write_index =%08x.\n",
287 	    slots, dev->extra_write_index);
288 	while ((slots > 0) && (!dev->extra_write_index)) {
289 		DBG("slots =%08x  extra_write_index =%08x.\n", slots,
290 		    dev->extra_write_index);
291 		DBG("call heci_bh_read_handler.\n");
292 		rets = heci_bh_read_handler(&complete_list, dev, &slots);
293 		if (rets != 0)
294 			goto end;
295 	}
296 	rets = heci_bh_write_handler(&complete_list, dev, &slots);
297 end:
298 	DBG("end of bottom half function.\n");
299 	dev->host_hw_state = read_heci_register(dev, H_CSR);
300 	dev->host_buffer_is_empty = host_buffer_is_empty(dev);
301 
302 	if ((dev->host_hw_state & H_IS) == H_IS) {
303 		/* acknowledge interrupt and disable interrupts */
304 		heci_csr_disable_interrupts(dev);
305 
306 		DBG("schedule work the heci_bh_handler.\n");
307 		isr_pending = 1;
308 
309 
310 	} else {
311 		heci_csr_enable_interrupts(dev);
312 	}
313 
314 	if (dev->recvd_msg) {
315 		DBG("received waiting bus message\n");
316 		bus_message_received = 1;
317 	}
318 
319 	if (bus_message_received) {
320 		DBG("wake up dev->wait_recvd_msg\n");
321 		cv_broadcast(&dev->wait_recvd_msg);
322 		bus_message_received = 0;
323 	}
324 	if ((complete_list.status != 0) ||
325 	    list_empty(&complete_list.heci_cb.cb_list)) {
326 		mutex_exit(&dev->device_lock);
327 		return (isr_pending);
328 	}
329 
330 	mutex_exit(&dev->device_lock);
331 
332 	list_for_each_entry_safe(cb_pos, cb_next,
333 	    &complete_list.heci_cb.cb_list, cb_list, struct heci_cb_private) {
334 		file_ext = (struct heci_file_private *)cb_pos->file_private;
335 		list_del(&cb_pos->cb_list);
336 		if (file_ext != NULL) {
337 			if (file_ext != &dev->iamthif_file_ext) {
338 				DBG("completing call back.\n");
339 				_heci_cmpl(file_ext, cb_pos);
340 				cb_pos = NULL;
341 			} else if (file_ext == &dev->iamthif_file_ext) {
342 				_heci_cmpl_iamthif(dev, cb_pos);
343 			}
344 		}
345 	}
346 	return (isr_pending);
347 }
348 
349 
350 /*
351  * heci_bh_read_handler - bottom half read routine after ISR to
352  * handle the read processing.
353  *
354  * @cmpl_list: An instance of our list structure
355  * @dev: Device object for our driver
356  * @slots: slots to read.
357  *
358  * @return 0 on success, <0 on failure.
359  */
360 static int
361 heci_bh_read_handler(struct io_heci_list *cmpl_list,
362 		struct iamt_heci_device *dev,
363 		int32_t *slots)
364 {
365 	struct heci_msg_hdr *heci_hdr;
366 	int ret = 0;
367 	struct heci_file_private *file_pos = NULL;
368 	struct heci_file_private *file_next = NULL;
369 
370 	if (!dev->rd_msg_hdr) {
371 		dev->rd_msg_hdr = read_heci_register(dev, ME_CB_RW);
372 		DBG("slots=%08x.\n", *slots);
373 		(*slots)--;
374 		DBG("slots=%08x.\n", *slots);
375 	}
376 	heci_hdr = (struct heci_msg_hdr *)&dev->rd_msg_hdr;
377 	DBG("heci_hdr->length =%d\n", heci_hdr->length);
378 
379 	if ((heci_hdr->reserved) || !(dev->rd_msg_hdr)) {
380 		DBG("corrupted message header.\n");
381 		ret = -ECORRUPTED_MESSAGE_HEADER;
382 		goto end;
383 	}
384 
385 	if ((heci_hdr->host_addr) || (heci_hdr->me_addr)) {
386 		list_for_each_entry_safe(file_pos, file_next,
387 		&dev->file_list, link, struct heci_file_private) {
388 			DBG("list_for_each_entry_safe read host"
389 					" client = %d, ME client = %d\n",
390 					file_pos->host_client_id,
391 					file_pos->me_client_id);
392 			if ((file_pos->host_client_id == heci_hdr->host_addr) &&
393 			    (file_pos->me_client_id == heci_hdr->me_addr))
394 				break;
395 		}
396 
397 		if (&file_pos->link == &dev->file_list) {
398 			DBG("corrupted message header\n");
399 			ret = -ECORRUPTED_MESSAGE_HEADER;
400 			goto end;
401 		}
402 	}
403 	if (((*slots) * sizeof (uint32_t)) < heci_hdr->length) {
404 		DBG("we can't read the message slots=%08x.\n", *slots);
405 		/* we can't read the message */
406 		ret = -ERANGE;
407 		goto end;
408 	}
409 
410 	/* decide where to read the message too */
411 	if (!heci_hdr->host_addr) {
412 		DBG("call heci_bh_read_bus_message.\n");
413 		heci_bh_read_bus_message(dev, heci_hdr);
414 		DBG("end heci_bh_read_bus_message.\n");
415 	} else if ((heci_hdr->host_addr ==
416 	    dev->iamthif_file_ext.host_client_id) &&
417 	    (HECI_FILE_CONNECTED == dev->iamthif_file_ext.state) &&
418 	    (dev->iamthif_state == HECI_IAMTHIF_READING)) {
419 
420 		DBG("call heci_bh_read_iamthif_message.\n");
421 		DBG("heci_hdr->length =%d\n", heci_hdr->length);
422 		ret = heci_bh_read_pthi_message(cmpl_list, dev, heci_hdr);
423 		if (ret != 0)
424 			goto end;
425 
426 	} else {
427 		DBG("call heci_bh_read_client_message.\n");
428 		ret = heci_bh_read_client_message(cmpl_list, dev, heci_hdr);
429 		if (ret != 0)
430 			goto end;
431 
432 	}
433 
434 	/* reset the number of slots and header */
435 	*slots = count_full_read_slots(dev);
436 	dev->rd_msg_hdr = 0;
437 
438 	if (*slots == -ESLOTS_OVERFLOW) {
439 		/* overflow - reset */
440 		DBG("reseting due to slots overflow.\n");
441 		/* set the event since message has been read */
442 		ret = -ERANGE;
443 		goto end;
444 	}
445 end:
446 	return (ret);
447 }
448 
449 
450 /*
451  * heci_bh_read_bus_message - bottom half read routine after ISR to
452  * handle the read bus message cmd  processing.
453  *
454  * @dev: Device object for our driver
455  * @heci_hdr: header of bus message
456  */
457 static void heci_bh_read_bus_message(struct iamt_heci_device *dev,
458 		struct heci_msg_hdr *heci_hdr)
459 {
460 	struct heci_bus_message *heci_msg;
461 	struct hbm_host_version_response *version_res;
462 	struct hbm_client_connect_response *connect_res;
463 	struct hbm_client_connect_response *disconnect_res;
464 	struct hbm_flow_control *flow_control;
465 	struct hbm_props_response *props_res;
466 	struct hbm_host_enum_response *enum_res;
467 	struct hbm_client_disconnect_request *disconnect_req;
468 	struct hbm_host_stop_request *h_stop_req;
469 	int i;
470 	unsigned char *buffer;
471 
472 	/*  read the message to our buffer */
473 	buffer = (unsigned char *)dev->rd_msg_buf;
474 	ASSERT(heci_hdr->length < sizeof (dev->rd_msg_buf));
475 	heci_read_slots(dev, buffer, heci_hdr->length);
476 	heci_msg = (struct heci_bus_message *)buffer;
477 
478 	switch (*(uint8_t *)heci_msg) {
479 	case HOST_START_RES_CMD:
480 		version_res = (struct hbm_host_version_response *)heci_msg;
481 		if (version_res->host_version_supported) {
482 			dev->version.major_version = HBM_MAJOR_VERSION;
483 			dev->version.minor_version = HBM_MINOR_VERSION;
484 		} else {
485 			dev->version = version_res->me_max_version;
486 		}
487 		dev->recvd_msg = 1;
488 		DBG("host start response message received.\n");
489 		break;
490 
491 	case CLIENT_CONNECT_RES_CMD:
492 		connect_res =
493 		    (struct hbm_client_connect_response *)heci_msg;
494 		heci_client_connect_response(dev, connect_res);
495 		DBG("client connect response message received.\n");
496 		cv_broadcast(&dev->wait_recvd_msg);
497 		break;
498 
499 	case CLIENT_DISCONNECT_RES_CMD:
500 		disconnect_res =
501 		    (struct hbm_client_connect_response *)heci_msg;
502 		heci_client_disconnect_response(dev,	 disconnect_res);
503 		DBG("client disconnect response message received.\n");
504 		cv_broadcast(&dev->wait_recvd_msg);
505 		break;
506 
507 	case HECI_FLOW_CONTROL_CMD:
508 		flow_control = (struct hbm_flow_control *)heci_msg;
509 		heci_client_flow_control_response(dev, flow_control);
510 		DBG("client flow control response message received.\n");
511 		break;
512 
513 	case HOST_CLIENT_PROPERTEIS_RES_CMD:
514 		props_res = (struct hbm_props_response *)heci_msg;
515 		if (props_res->status != 0) {
516 			ASSERT(0);
517 			break;
518 		}
519 		for (i = 0; i < dev->num_heci_me_clients; i++) {
520 			if (dev->me_clients[i].client_id ==
521 			    props_res->address) {
522 				dev->me_clients[i].props =
523 				    props_res->client_properties;
524 				break;
525 			}
526 
527 		}
528 		dev->recvd_msg = 1;
529 		break;
530 
531 	case HOST_ENUM_RES_CMD:
532 		enum_res = (struct hbm_host_enum_response *)heci_msg;
533 		(void) memcpy(dev->heci_me_clients,
534 		    enum_res->valid_addresses, 32);
535 		dev->recvd_msg = 1;
536 		break;
537 
538 	case HOST_STOP_RES_CMD:
539 		dev->heci_state = HECI_DISABLED;
540 		DBG("reseting because of FW stop response.\n");
541 		heci_reset(dev, 1);
542 		break;
543 
544 	case CLIENT_DISCONNECT_REQ_CMD:
545 		/* search for client */
546 		disconnect_req =
547 		    (struct hbm_client_disconnect_request *)heci_msg;
548 		heci_client_disconnect_request(dev, disconnect_req);
549 		break;
550 
551 	case ME_STOP_REQ_CMD:
552 		/* prepare stop request */
553 		heci_hdr = (struct heci_msg_hdr *)&dev->ext_msg_buf[0];
554 		heci_hdr->host_addr = 0;
555 		heci_hdr->me_addr = 0;
556 		heci_hdr->length = sizeof (struct hbm_host_stop_request);
557 		heci_hdr->msg_complete = 1;
558 		heci_hdr->reserved = 0;
559 		h_stop_req =
560 		    (struct hbm_host_stop_request *)&dev->ext_msg_buf[1];
561 		(void) memset(h_stop_req, 0,
562 		    sizeof (struct hbm_host_stop_request));
563 		h_stop_req->cmd.cmd = HOST_STOP_REQ_CMD;
564 		h_stop_req->reason = DRIVER_STOP_REQUEST;
565 		h_stop_req->reserved[0] = 0;
566 		h_stop_req->reserved[1] = 0;
567 		dev->extra_write_index = 2;
568 		break;
569 
570 	default:
571 		ASSERT(0);
572 		break;
573 
574 	}
575 }
576 
577 /*
578  * heci_bh_read_pthi_message - bottom half read routine after ISR to
579  * handle the read pthi message data processing.
580  *
581  * @complete_list: An instance of our list structure
582  * @dev: Device object for our driver
583  * @heci_hdr: header of pthi message
584  *
585  * @return 0 on success, <0 on failure.
586  */
587 static int
588 heci_bh_read_pthi_message(struct io_heci_list *complete_list,
589 		struct iamt_heci_device *dev,
590 		struct heci_msg_hdr *heci_hdr)
591 {
592 	struct heci_file_private *file_ext;
593 	struct heci_cb_private *priv_cb;
594 	unsigned char *buffer;
595 
596 	ASSERT(heci_hdr->me_addr == dev->iamthif_file_ext.me_client_id);
597 	ASSERT(dev->iamthif_state == HECI_IAMTHIF_READING);
598 
599 	buffer = (unsigned char *)(dev->iamthif_msg_buf +
600 	    dev->iamthif_msg_buf_index);
601 	ASSERT(sizeof (dev->iamthif_msg_buf) >=
602 	    (dev->iamthif_msg_buf_index + heci_hdr->length));
603 
604 	heci_read_slots(dev, buffer, heci_hdr->length);
605 
606 	dev->iamthif_msg_buf_index += heci_hdr->length;
607 
608 	if (!(heci_hdr->msg_complete))
609 		return (0);
610 
611 	DBG("pthi_message_buffer_index=%d\n", heci_hdr->length);
612 	DBG("completed pthi read.\n ");
613 	if (!dev->iamthif_current_cb)
614 		return (-ENODEV);
615 
616 	priv_cb = dev->iamthif_current_cb;
617 	dev->iamthif_current_cb = NULL;
618 
619 	file_ext = (struct heci_file_private *)priv_cb->file_private;
620 	if (!file_ext)
621 		return (-ENODEV);
622 
623 	dev->iamthif_stall_timer = 0;
624 	priv_cb->information =	dev->iamthif_msg_buf_index;
625 	priv_cb->read_time = ddi_get_time();
626 	if ((dev->iamthif_ioctl) && (file_ext == &dev->iamthif_file_ext)) {
627 		/* found the iamthif cb */
628 		DBG("complete the pthi read cb.\n ");
629 		if (&dev->iamthif_file_ext) {
630 			DBG("add the pthi read cb to complete.\n ");
631 			list_add_tail(&priv_cb->cb_list,
632 			    &complete_list->heci_cb.cb_list);
633 		}
634 	}
635 	return (0);
636 }
637 
638 /*
639  * _heci_bh_state_ok - check if heci header matches file private data
640  *
641  * @file_ext: private data of the file object
642  * @heci_hdr: header of heci client message
643  *
644  * @return  !=0 if matches, 0 if no match.
645  */
646 static int
647 _heci_bh_state_ok(struct heci_file_private *file_ext,
648 					struct heci_msg_hdr *heci_hdr)
649 {
650 	return ((file_ext->host_client_id == heci_hdr->host_addr) &&
651 	    (file_ext->me_client_id == heci_hdr->me_addr) &&
652 	    (file_ext->state == HECI_FILE_CONNECTED) &&
653 	    (HECI_READ_COMPLETE != file_ext->reading_state));
654 }
655 
656 /*
657  * heci_bh_read_client_message - bottom half read routine after ISR to
658  * handle the read heci client message data  processing.
659  *
660  * @complete_list: An instance of our list structure
661  * @dev: Device object for our driver
662  * @heci_hdr: header of heci client message
663  *
664  * @return  0 on success, <0 on failure.
665  */
666 static int
667 heci_bh_read_client_message(struct io_heci_list *complete_list,
668 	struct iamt_heci_device *dev,
669 	struct heci_msg_hdr *heci_hdr)
670 {
671 	struct heci_file_private *file_ext;
672 	struct heci_cb_private *priv_cb_pos = NULL, *priv_cb_next = NULL;
673 	unsigned char *buffer = NULL;
674 
675 	DBG("start client msg\n");
676 	if (!((dev->read_list.status == 0) &&
677 	    !list_empty(&dev->read_list.heci_cb.cb_list)))
678 		goto quit;
679 
680 	list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
681 	    &dev->read_list.heci_cb.cb_list, cb_list, struct heci_cb_private) {
682 		file_ext = (struct heci_file_private *)
683 				priv_cb_pos->file_private;
684 		if ((file_ext != NULL) &&
685 		    (_heci_bh_state_ok(file_ext, heci_hdr))) {
686 			mutex_enter(&file_ext->read_io_lock);
687 			file_ext->reading_state = HECI_READING;
688 			buffer = (unsigned char *)
689 				(priv_cb_pos->response_buffer.data +
690 				priv_cb_pos->information);
691 			ASSERT(priv_cb_pos->response_buffer.size >=
692 					heci_hdr->length +
693 					priv_cb_pos->information);
694 
695 			if (priv_cb_pos->response_buffer.size <
696 					heci_hdr->length +
697 					priv_cb_pos->information) {
698 				DBG("message overflow.\n");
699 				list_del(&priv_cb_pos->cb_list);
700 				mutex_exit(&file_ext->read_io_lock);
701 				return (-ENOMEM);
702 			}
703 			if (buffer) {
704 				heci_read_slots(dev, buffer,
705 						heci_hdr->length);
706 			}
707 			priv_cb_pos->information += heci_hdr->length;
708 			if (heci_hdr->msg_complete) {
709 				file_ext->status = 0;
710 				list_del(&priv_cb_pos->cb_list);
711 				mutex_exit(&file_ext->read_io_lock);
712 				DBG("completed read host client = %d,"
713 					"ME client = %d, "
714 					"data length = %lu\n",
715 					file_ext->host_client_id,
716 					file_ext->me_client_id,
717 					priv_cb_pos->information);
718 
719 				DBG("priv_cb_pos->res_buffer - %s\n",
720 					priv_cb_pos->response_buffer.data);
721 				list_add_tail(&priv_cb_pos->cb_list,
722 					&complete_list->heci_cb.cb_list);
723 			} else {
724 				mutex_exit(&file_ext->read_io_lock);
725 			}
726 
727 			break;
728 		}
729 
730 	}
731 
732 quit:
733 	DBG("message read\n");
734 	if (!buffer) {
735 		heci_read_slots(dev, (unsigned char *)dev->rd_msg_buf,
736 		    heci_hdr->length);
737 		DBG("discarding message, header=%08x.\n",
738 		    *(uint32_t *)dev->rd_msg_buf);
739 	}
740 
741 	return (0);
742 }
743 
744 /*
745  * _heci_bh_iamthif_read: prepare to read iamthif data.
746  *
747  * @dev: Device object for our driver.
748  * @slots: free slots.
749  *
750  * @return  0, OK; otherwise, error.
751  */
752 static int
753 _heci_bh_iamthif_read(struct iamt_heci_device *dev, int32_t *slots)
754 {
755 
756 	if (((*slots) * sizeof (uint32_t)) >= (sizeof (struct heci_msg_hdr)
757 			+ sizeof (struct hbm_flow_control))) {
758 		*slots -= (sizeof (struct heci_msg_hdr) +
759 				sizeof (struct hbm_flow_control) + 3) / 4;
760 		if (!heci_send_flow_control(dev, &dev->iamthif_file_ext)) {
761 			DBG("iamthif flow control failed\n");
762 		} else {
763 			DBG("iamthif flow control success\n");
764 			dev->iamthif_state = HECI_IAMTHIF_READING;
765 			dev->iamthif_flow_control_pending = 0;
766 			dev->iamthif_msg_buf_index = 0;
767 			dev->iamthif_msg_buf_size = 0;
768 			dev->iamthif_stall_timer = IAMTHIF_STALL_TIMER;
769 			dev->host_buffer_is_empty = host_buffer_is_empty(dev);
770 		}
771 		return (0);
772 	} else {
773 		return (-ECOMPLETE_MESSAGE);
774 	}
775 }
776 
777 /*
778  * _heci_bh_close: process close related operation.
779  *
780  * @dev: Device object for our driver.
781  * @slots: free slots.
782  * @priv_cb_pos: callback block.
783  * @file_ext: private data of the file object.
784  * @cmpl_list: complete list.
785  *
786  * @return  0, OK; otherwise, error.
787  */
788 static int
789 _heci_bh_close(struct iamt_heci_device *dev, int32_t *slots,
790 	struct heci_cb_private *priv_cb_pos,
791 	struct heci_file_private *file_ext,
792 	struct io_heci_list *cmpl_list)
793 {
794 	if ((*slots * sizeof (uint32_t)) >= (sizeof (struct heci_msg_hdr) +
795 	    sizeof (struct hbm_client_disconnect_request))) {
796 		*slots -= (sizeof (struct heci_msg_hdr) +
797 			sizeof (struct hbm_client_disconnect_request) + 3) / 4;
798 
799 		if (!heci_disconnect(dev, file_ext)) {
800 			file_ext->status = 0;
801 			priv_cb_pos->information = 0;
802 			list_relink_node(&priv_cb_pos->cb_list,
803 					&cmpl_list->heci_cb.cb_list);
804 			return (-ECOMPLETE_MESSAGE);
805 		} else {
806 			file_ext->state = HECI_FILE_DISCONNECTING;
807 			file_ext->status = 0;
808 			priv_cb_pos->information = 0;
809 			list_relink_node(&priv_cb_pos->cb_list,
810 					&dev->ctrl_rd_list.heci_cb.cb_list);
811 			file_ext->timer_count = HECI_CONNECT_TIMEOUT;
812 		}
813 	} else {
814 		/* return the cancel routine */
815 		return (-ECORRUPTED_MESSAGE_HEADER);
816 	}
817 
818 	return (0);
819 }
820 
821 /*
822  * _heci_hb_close: process read related operation.
823  *
824  * @dev: Device object for our driver.
825  * @slots: free slots.
826  * @priv_cb_pos: callback block.
827  * @file_ext: private data of the file object.
828  * @cmpl_list: complete list.
829  *
830  * @return 0, OK; otherwise, error.
831  */
832 static int
833 _heci_bh_read(struct iamt_heci_device *dev,	int32_t *slots,
834 	struct heci_cb_private *priv_cb_pos,
835 	struct heci_file_private *file_ext,
836 	struct io_heci_list *cmpl_list)
837 {
838 	if ((*slots * sizeof (uint32_t)) >= (sizeof (struct heci_msg_hdr) +
839 	    sizeof (struct hbm_flow_control))) {
840 		*slots -= (sizeof (struct heci_msg_hdr) +
841 		    sizeof (struct hbm_flow_control) + 3) / 4;
842 		if (!heci_send_flow_control(dev, file_ext)) {
843 			file_ext->status = -ENODEV;
844 			priv_cb_pos->information = 0;
845 			list_relink_node(&priv_cb_pos->cb_list,
846 					&cmpl_list->heci_cb.cb_list);
847 			return (-ENODEV);
848 		} else {
849 			list_relink_node(&priv_cb_pos->cb_list,
850 					&dev->read_list.heci_cb.cb_list);
851 		}
852 	} else {
853 		/* return the cancel routine */
854 		list_del(&priv_cb_pos->cb_list);
855 		return (-ECORRUPTED_MESSAGE_HEADER);
856 	}
857 
858 	return (0);
859 }
860 
861 
862 /*
863  * _heci_bh_ioctl: process ioctl related operation.
864  *
865  * @dev: Device object for our driver.
866  * @slots: free slots.
867  * @priv_cb_pos: callback block.
868  * @file_ext: private data of the file object.
869  * @cmpl_list: complete list.
870  *
871  * @return  0, OK; otherwise, error.
872  */
873 static int
874 _heci_bh_ioctl(struct iamt_heci_device *dev,	int32_t *slots,
875 	struct heci_cb_private *priv_cb_pos,
876 	struct heci_file_private *file_ext,
877 	struct io_heci_list *cmpl_list)
878 {
879 	_NOTE(ARGUNUSED(cmpl_list));
880 
881 	if ((*slots * sizeof (uint32_t)) >= (sizeof (struct heci_msg_hdr) +
882 	    sizeof (struct hbm_client_connect_request))) {
883 		file_ext->state = HECI_FILE_CONNECTING;
884 		*slots -= (sizeof (struct heci_msg_hdr) +
885 			sizeof (struct hbm_client_connect_request) + 3) / 4;
886 		if (!heci_connect(dev, file_ext)) {
887 			file_ext->status = -ENODEV;
888 			priv_cb_pos->information = 0;
889 			list_del(&priv_cb_pos->cb_list);
890 			return (-ENODEV);
891 		} else {
892 			list_relink_node(&priv_cb_pos->cb_list,
893 				&dev->ctrl_rd_list.heci_cb.cb_list);
894 			file_ext->timer_count = HECI_CONNECT_TIMEOUT;
895 		}
896 	} else {
897 		/* return the cancel routine */
898 		list_del(&priv_cb_pos->cb_list);
899 		return (-ECORRUPTED_MESSAGE_HEADER);
900 	}
901 
902 	return (0);
903 }
904 
905 /*
906  * _heci_bh_cmpl: process completed and no-iamthif operation.
907  *
908  * @dev: Device object for our driver.
909  * @slots: free slots.
910  * @priv_cb_pos: callback block.
911  * @file_ext: private data of the file object.
912  * @cmpl_list: complete list.
913  *
914  * @return  0, OK; otherwise, error.
915  */
916 static int
917 _heci_bh_cmpl(struct iamt_heci_device *dev,	int32_t *slots,
918 			struct heci_cb_private *priv_cb_pos,
919 			struct heci_file_private *file_ext,
920 			struct io_heci_list *cmpl_list)
921 {
922 	struct heci_msg_hdr *heci_hdr;
923 
924 	if ((*slots * sizeof (uint32_t)) >= (sizeof (struct heci_msg_hdr) +
925 	    (priv_cb_pos->request_buffer.size -
926 	    priv_cb_pos->information))) {
927 		heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
928 		heci_hdr->host_addr = file_ext->host_client_id;
929 		heci_hdr->me_addr = file_ext->me_client_id;
930 		heci_hdr->length = ((priv_cb_pos->request_buffer.size) -
931 		    (priv_cb_pos->information));
932 		heci_hdr->msg_complete = 1;
933 		heci_hdr->reserved = 0;
934 		DBG("priv_cb_pos->request_buffer.size =%d"
935 		    "heci_hdr->msg_complete= %d\n",
936 		    priv_cb_pos->request_buffer.size,
937 		    heci_hdr->msg_complete);
938 		DBG("priv_cb_pos->information  =%lu\n",
939 		    priv_cb_pos->information);
940 		DBG("heci_hdr->length  =%d\n",
941 		    heci_hdr->length);
942 		*slots -= (sizeof (struct heci_msg_hdr) +
943 		    heci_hdr->length + 3) / 4;
944 		if (!heci_write_message(dev, heci_hdr,
945 		    (unsigned char *)(priv_cb_pos->request_buffer.data +
946 		    priv_cb_pos->information),
947 		    heci_hdr->length)) {
948 			file_ext->status = -ENODEV;
949 			list_relink_node(&priv_cb_pos->cb_list,
950 			    &cmpl_list->heci_cb.cb_list);
951 			return (-ENODEV);
952 		} else {
953 			flow_ctrl_reduce(dev, file_ext);
954 			file_ext->status = 0;
955 			priv_cb_pos->information += heci_hdr->length;
956 			list_relink_node(&priv_cb_pos->cb_list,
957 			    &dev->write_waiting_list.heci_cb.cb_list);
958 		}
959 	} else if (*slots == ((dev->host_hw_state & H_CBD) >> 24)) {
960 		/* buffer is still empty */
961 		heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
962 		heci_hdr->host_addr = file_ext->host_client_id;
963 		heci_hdr->me_addr = file_ext->me_client_id;
964 		heci_hdr->length =
965 		    (*slots * sizeof (uint32_t)) - sizeof (struct heci_msg_hdr);
966 		heci_hdr->msg_complete = 0;
967 		heci_hdr->reserved = 0;
968 
969 		(*slots) -= (sizeof (struct heci_msg_hdr) +
970 		    heci_hdr->length + 3) / 4;
971 		if (!heci_write_message(dev, heci_hdr,
972 		    (unsigned char *)
973 		    (priv_cb_pos->request_buffer.data +
974 		    priv_cb_pos->information),
975 		    heci_hdr->length)) {
976 			file_ext->status = -ENODEV;
977 			list_relink_node(&priv_cb_pos->cb_list,
978 			    &cmpl_list->heci_cb.cb_list);
979 			return (-ENODEV);
980 		} else {
981 			priv_cb_pos->information += heci_hdr->length;
982 			DBG("priv_cb_pos->request_buffer.size =%d"
983 			    " heci_hdr->msg_complete= %d\n",
984 			    priv_cb_pos->request_buffer.size,
985 			    heci_hdr->msg_complete);
986 			DBG("priv_cb_pos->information  =%lu\n",
987 			    priv_cb_pos->information);
988 			DBG("heci_hdr->length  =%d\n", heci_hdr->length);
989 		}
990 		return (-ECOMPLETE_MESSAGE);
991 	} else {
992 		return (-ECORRUPTED_MESSAGE_HEADER);
993 	}
994 
995 	return (0);
996 }
997 
998 /*
999  * _heci_bh_cmpl_iamthif: process completed iamthif operation.
1000  *
1001  * @dev: Device object for our driver.
1002  * @slots: free slots.
1003  * @priv_cb_pos: callback block.
1004  * @file_ext: private data of the file object.
1005  * @cmpl_list: complete list.
1006  *
1007  * @return  0, OK; otherwise, error.
1008  */
1009 static int
1010 _heci_bh_cmpl_iamthif(struct iamt_heci_device *dev, int32_t *slots,
1011 	struct heci_cb_private *priv_cb_pos,
1012 	struct heci_file_private *file_ext,
1013 	struct io_heci_list *cmpl_list)
1014 {
1015 	struct heci_msg_hdr *heci_hdr;
1016 
1017 	_NOTE(ARGUNUSED(cmpl_list));
1018 
1019 	if ((*slots * sizeof (uint32_t)) >= (sizeof (struct heci_msg_hdr) +
1020 	    dev->iamthif_msg_buf_size -
1021 	    dev->iamthif_msg_buf_index)) {
1022 		heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
1023 		heci_hdr->host_addr = file_ext->host_client_id;
1024 		heci_hdr->me_addr = file_ext->me_client_id;
1025 		heci_hdr->length = dev->iamthif_msg_buf_size -
1026 		    dev->iamthif_msg_buf_index;
1027 		heci_hdr->msg_complete = 1;
1028 		heci_hdr->reserved = 0;
1029 
1030 		*slots -= (sizeof (struct heci_msg_hdr) +
1031 		    heci_hdr->length + 3) / 4;
1032 
1033 		if (!heci_write_message(dev, heci_hdr,
1034 		    (dev->iamthif_msg_buf +
1035 		    dev->iamthif_msg_buf_index),
1036 		    heci_hdr->length)) {
1037 			dev->iamthif_state = HECI_IAMTHIF_IDLE;
1038 			file_ext->status = -ENODEV;
1039 			list_del(&priv_cb_pos->cb_list);
1040 			return (-ENODEV);
1041 		} else {
1042 			flow_ctrl_reduce(dev, file_ext);
1043 			dev->iamthif_msg_buf_index += heci_hdr->length;
1044 			priv_cb_pos->information = dev->iamthif_msg_buf_index;
1045 			file_ext->status = 0;
1046 			dev->iamthif_state = HECI_IAMTHIF_FLOW_CONTROL;
1047 			dev->iamthif_flow_control_pending = 1;
1048 			/* save iamthif cb sent to pthi client */
1049 			dev->iamthif_current_cb = priv_cb_pos;
1050 			list_relink_node(&priv_cb_pos->cb_list,
1051 			    &dev->write_waiting_list.heci_cb.cb_list);
1052 
1053 		}
1054 	} else if (*slots == ((dev->host_hw_state & H_CBD) >> 24)) {
1055 			/* buffer is still empty */
1056 		heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
1057 		heci_hdr->host_addr = file_ext->host_client_id;
1058 		heci_hdr->me_addr = file_ext->me_client_id;
1059 		heci_hdr->length =
1060 		    (*slots * sizeof (uint32_t)) - sizeof (struct heci_msg_hdr);
1061 		heci_hdr->msg_complete = 0;
1062 		heci_hdr->reserved = 0;
1063 
1064 		*slots -= (sizeof (struct heci_msg_hdr) +
1065 		    heci_hdr->length + 3) / 4;
1066 
1067 		if (!heci_write_message(dev, heci_hdr,
1068 		    (dev->iamthif_msg_buf +
1069 		    dev->iamthif_msg_buf_index),
1070 		    heci_hdr->length)) {
1071 			file_ext->status = -ENODEV;
1072 			list_del(&priv_cb_pos->cb_list);
1073 		} else {
1074 			dev->iamthif_msg_buf_index += heci_hdr->length;
1075 		}
1076 		return (-ECOMPLETE_MESSAGE);
1077 	} else {
1078 		return (-ECORRUPTED_MESSAGE_HEADER);
1079 	}
1080 
1081 	return (0);
1082 }
1083 
1084 /*
1085  * heci_bh_write_handler - bottom half write routine after
1086  * ISR to handle the write processing.
1087  *
1088  * @cmpl_list: An instance of our list structure
1089  * @dev: Device object for our driver
1090  * @slots: slots to write.
1091  *
1092  * @return 0 on success, <0 on failure.
1093  */
1094 static int
1095 heci_bh_write_handler(struct io_heci_list *cmpl_list,
1096 	struct iamt_heci_device *dev,
1097 	int32_t *slots)
1098 {
1099 
1100 	struct heci_file_private *file_ext;
1101 	struct heci_cb_private *priv_cb_pos = NULL, *priv_cb_next = NULL;
1102 	struct io_heci_list *list;
1103 	int ret;
1104 
1105 	if (!host_buffer_is_empty(dev)) {
1106 		DBG("host buffer is not empty.\n");
1107 		return (0);
1108 	}
1109 	dev->write_hang = (uint8_t)-1;
1110 	*slots = count_empty_write_slots(dev);
1111 	/* complete all waiting for write CB */
1112 	DBG("complete all waiting for write cb.\n");
1113 
1114 	list = &dev->write_waiting_list;
1115 	if ((list->status == 0) &&
1116 	    !list_empty(&list->heci_cb.cb_list)) {
1117 		list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
1118 		    &list->heci_cb.cb_list, cb_list, struct heci_cb_private) {
1119 			file_ext = (struct heci_file_private *)
1120 					priv_cb_pos->file_private;
1121 			if (file_ext != NULL) {
1122 				file_ext->status = 0;
1123 				list_del(&priv_cb_pos->cb_list);
1124 				if ((HECI_WRITING == file_ext->writing_state) &&
1125 					(priv_cb_pos->major_file_operations ==
1126 						HECI_WRITING) &&
1127 					(file_ext != &dev->iamthif_file_ext)) {
1128 					DBG("HECI WRITE COMPLETE\n");
1129 					file_ext->writing_state =
1130 						HECI_WRITE_COMPLETE;
1131 					list_add_tail(&priv_cb_pos->cb_list,
1132 						&cmpl_list->heci_cb.cb_list);
1133 				}
1134 				if (file_ext == &dev->iamthif_file_ext) {
1135 					DBG("check iamthif flow control.\n");
1136 					if (dev->iamthif_flow_control_pending) {
1137 						ret = _heci_bh_iamthif_read(dev,
1138 									slots);
1139 						if (ret != 0)
1140 							return (ret);
1141 					}
1142 				}
1143 			}
1144 
1145 		}
1146 	}
1147 
1148 	if ((dev->stop) && (!dev->wd_pending)) {
1149 		dev->wd_stoped = 1;
1150 		cv_broadcast(&dev->wait_stop_wd);
1151 		return (0);
1152 	}
1153 
1154 	if (dev->extra_write_index != 0) {
1155 		DBG("extra_write_index =%d.\n",	dev->extra_write_index);
1156 		if (!heci_write_message(dev,
1157 		    (struct heci_msg_hdr *)&dev->ext_msg_buf[0],
1158 		    (unsigned char *)&dev->ext_msg_buf[1],
1159 		    (dev->extra_write_index - 1) * sizeof (uint32_t))) {
1160 			DBG("heci_bh_handler: writing msg failed\n");
1161 		}
1162 		*slots -= dev->extra_write_index;
1163 		dev->extra_write_index = 0;
1164 	}
1165 	if (dev->heci_state == HECI_ENABLED) {
1166 		if (dev->wd_pending &&
1167 		    flow_ctrl_creds(dev, &dev->wd_file_ext)) {
1168 			if (!heci_send_wd(dev)) {
1169 				DBG("wd send failed.\n");
1170 			} else
1171 				flow_ctrl_reduce(dev, &dev->wd_file_ext);
1172 
1173 			dev->wd_pending = 0;
1174 
1175 			if (dev->wd_timeout != 0) {
1176 				*slots -= (sizeof (struct heci_msg_hdr) +
1177 				    HECI_START_WD_DATA_SIZE + 3) / 4;
1178 				dev->wd_due_counter = 2;
1179 			} else {
1180 				*slots -= (sizeof (struct heci_msg_hdr) +
1181 				    HECI_WD_PARAMS_SIZE + 3) / 4;
1182 				dev->wd_due_counter = 0;
1183 			}
1184 
1185 		}
1186 	}
1187 	if (dev->stop)
1188 		return (~ENODEV);
1189 
1190 	/* complete control write list CB */
1191 	if (dev->ctrl_wr_list.status == 0) {
1192 		/* complete control write list CB */
1193 		DBG("complete control write list cb.\n");
1194 		list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
1195 		    &dev->ctrl_wr_list.heci_cb.cb_list,
1196 		    cb_list, struct heci_cb_private) {
1197 			file_ext = (struct heci_file_private *)
1198 			    priv_cb_pos->file_private;
1199 			if (file_ext == NULL) {
1200 				list_del(&priv_cb_pos->cb_list);
1201 				return (-ENODEV);
1202 			}
1203 			switch (priv_cb_pos->major_file_operations) {
1204 			case HECI_CLOSE:
1205 				/* send disconnect message */
1206 				ret = _heci_bh_close(dev, slots,
1207 				    priv_cb_pos,
1208 				    file_ext, cmpl_list);
1209 				if (ret != 0)
1210 					return (ret);
1211 
1212 				break;
1213 			case HECI_READ:
1214 				/* send flow control message */
1215 				ret = _heci_bh_read(dev, slots,
1216 				    priv_cb_pos,
1217 				    file_ext, cmpl_list);
1218 				if (ret != 0)
1219 					return (ret);
1220 
1221 				break;
1222 			case HECI_IOCTL:
1223 				/* connect message */
1224 				if (!other_client_is_connecting(dev, file_ext))
1225 					continue;
1226 				ret = _heci_bh_ioctl(dev, slots,
1227 				    priv_cb_pos,
1228 				    file_ext, cmpl_list);
1229 				if (ret != 0)
1230 					return (ret);
1231 
1232 				break;
1233 
1234 			default:
1235 				ASSERT(0);
1236 			}
1237 
1238 		}
1239 	}
1240 	/* complete  write list CB */
1241 	if ((dev->write_list.status == 0) &&
1242 	    !list_empty(&dev->write_list.heci_cb.cb_list)) {
1243 
1244 		DBG("complete write list cb.\n");
1245 		list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
1246 		    &dev->write_list.heci_cb.cb_list,
1247 		    cb_list, struct heci_cb_private) {
1248 
1249 			file_ext = (struct heci_file_private *)
1250 			    priv_cb_pos->file_private;
1251 			if (file_ext != NULL) {
1252 				if (file_ext != &dev->iamthif_file_ext) {
1253 					if (!flow_ctrl_creds(dev, file_ext)) {
1254 						DBG("No flow control"
1255 						    " credentials for client"
1256 						    " %d, not sending.\n",
1257 						    file_ext->host_client_id);
1258 						continue;
1259 					}
1260 					ret = _heci_bh_cmpl(dev, slots,
1261 					    priv_cb_pos, file_ext, cmpl_list);
1262 					if (ret != 0)
1263 						return (ret);
1264 
1265 				} else if (file_ext == &dev->iamthif_file_ext) {
1266 					/* IAMTHIF IOCTL */
1267 					DBG("complete pthi write cb.\n");
1268 					if (!flow_ctrl_creds(dev, file_ext)) {
1269 						DBG("No flow control"
1270 						    " credentials for pthi"
1271 						    " client %d.\n",
1272 						    file_ext->host_client_id);
1273 						continue;
1274 					}
1275 					ret = _heci_bh_cmpl_iamthif(dev, slots,
1276 					    priv_cb_pos, file_ext, cmpl_list);
1277 					if (ret != 0)
1278 						return (ret);
1279 
1280 				}
1281 			}
1282 
1283 		}
1284 	}
1285 	return (0);
1286 }
1287 
1288 
1289 /*
1290  * is_treat_specially_client  - check if the message belong
1291  * to the file private data.
1292  *
1293  * @file_ext: private data of the file object
1294  * @rs: connect response bus message
1295  * @dev: Device object for our driver
1296  *
1297  * @return 0 on success, <0 on failure.
1298  */
1299 static int
1300 is_treat_specially_client(struct heci_file_private *file_ext,
1301 	struct hbm_client_connect_response *rs)
1302 {
1303 	int ret = 0;
1304 
1305 	if ((file_ext->host_client_id == rs->host_addr) &&
1306 	    (file_ext->me_client_id == rs->me_addr)) {
1307 		if (rs->status == 0) {
1308 			DBG("client connect status = 0x%08x.\n", rs->status);
1309 			file_ext->state = HECI_FILE_CONNECTED;
1310 			file_ext->status = 0;
1311 		} else {
1312 			DBG("client connect status = 0x%08x.\n", rs->status);
1313 			file_ext->state = HECI_FILE_DISCONNECTED;
1314 			file_ext->status = -ENODEV;
1315 		}
1316 		ret = 1;
1317 	}
1318 	DBG("client state = %d.\n", file_ext->state);
1319 	return (ret);
1320 }
1321 
1322 /*
1323  * heci_client_connect_response  - connect response bh routine
1324  *
1325  * @dev: Device object for our driver
1326  * @rs: connect response bus message
1327  */
1328 static void
1329 heci_client_connect_response(struct iamt_heci_device *dev,
1330 	struct hbm_client_connect_response *rs)
1331 {
1332 
1333 	struct heci_file_private *file_ext;
1334 	struct heci_cb_private *priv_cb_pos = NULL, *priv_cb_next = NULL;
1335 
1336 	/* if WD or iamthif client treat specially */
1337 
1338 	if ((is_treat_specially_client(&(dev->wd_file_ext), rs)) ||
1339 	    (is_treat_specially_client(&(dev->iamthif_file_ext), rs)))
1340 		return;
1341 
1342 	if (dev->ctrl_rd_list.status == 0 &&
1343 	    !list_empty(&dev->ctrl_rd_list.heci_cb.cb_list)) {
1344 
1345 		list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
1346 		    &dev->ctrl_rd_list.heci_cb.cb_list,
1347 		    cb_list, struct heci_cb_private) {
1348 
1349 			file_ext = (struct heci_file_private *)
1350 			    priv_cb_pos->file_private;
1351 			if (file_ext == NULL) {
1352 				list_del(&priv_cb_pos->cb_list);
1353 				return;
1354 			}
1355 			if (HECI_IOCTL == priv_cb_pos->major_file_operations) {
1356 				if (is_treat_specially_client(file_ext, rs)) {
1357 					list_del(&priv_cb_pos->cb_list);
1358 					file_ext->status = 0;
1359 					file_ext->timer_count = 0;
1360 					break;
1361 				}
1362 			}
1363 		}
1364 	}
1365 }
1366 
1367 /*
1368  * heci_client_disconnect_response  - disconnect response bh routine
1369  *
1370  * @dev: Device object for our driver
1371  * @rs: disconnect response bus message
1372  */
1373 static void heci_client_disconnect_response(struct iamt_heci_device *dev,
1374 					struct hbm_client_connect_response *rs)
1375 {
1376 	struct heci_file_private *file_ext;
1377 	struct heci_cb_private *priv_cb_pos = NULL, *priv_cb_next = NULL;
1378 
1379 	if (dev->ctrl_rd_list.status == 0 &&
1380 	    !list_empty(&dev->ctrl_rd_list.heci_cb.cb_list)) {
1381 
1382 		list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
1383 		    &dev->ctrl_rd_list.heci_cb.cb_list,
1384 		    cb_list, struct heci_cb_private) {
1385 
1386 			file_ext = (struct heci_file_private *)
1387 				priv_cb_pos->file_private;
1388 
1389 			if (file_ext == NULL) {
1390 				list_del(&priv_cb_pos->cb_list);
1391 				return;
1392 			}
1393 
1394 			DBG("list_for_each_entry_safe in ctrl_rd_list.\n");
1395 			if ((file_ext->host_client_id == rs->host_addr) &&
1396 				(file_ext->me_client_id == rs->me_addr)) {
1397 
1398 				list_del(&priv_cb_pos->cb_list);
1399 				if (rs->status == 0) {
1400 					file_ext->state =
1401 					    HECI_FILE_DISCONNECTED;
1402 				}
1403 
1404 				file_ext->status = 0;
1405 				file_ext->timer_count = 0;
1406 				break;
1407 			}
1408 		}
1409 	}
1410 }
1411 
1412 /*
1413  * same_flow_addr - tell they have same address.
1414  *
1415  * @file: private data of the file object.
1416  * @flow: flow control.
1417  *
1418  * @return  !=0, same; 0,not.
1419  */
1420 static int
1421 same_flow_addr(struct heci_file_private *file,
1422 	struct hbm_flow_control *flow)
1423 {
1424 	return ((file->host_client_id == flow->host_addr) &&
1425 	    (file->me_client_id == flow->me_addr));
1426 }
1427 
1428 /*
1429  * add_single_flow_creds - add single buffer credentials.
1430  *
1431  * @file: private data ot the file object.
1432  * @flow: flow control.
1433  */
1434 static void
1435 add_single_flow_creds(struct iamt_heci_device *dev,
1436 	struct hbm_flow_control *flow)
1437 {
1438 	struct heci_me_client *client;
1439 	int i;
1440 
1441 	for (i = 0; i < dev->num_heci_me_clients; i++) {
1442 		client = &dev->me_clients[i];
1443 		if ((client != NULL) &&
1444 		    (flow->me_addr == client->client_id)) {
1445 			if (client->props.single_recv_buf != 0) {
1446 				client->flow_ctrl_creds++;
1447 				DBG("recv flow ctrl msg ME %d (single).\n",
1448 				    flow->me_addr);
1449 				DBG("flow control credentials=%d.\n",
1450 				    client->flow_ctrl_creds);
1451 			} else {
1452 				ASSERT(0);	/* error in flow control */
1453 			}
1454 		}
1455 	}
1456 }
1457 /*
1458  * heci_client_flow_control_response  - flow control response bh routine
1459  *
1460  * @dev: Device object for our driver
1461  * @flow_control: flow control response bus message
1462  */
1463 static void
1464 heci_client_flow_control_response(struct iamt_heci_device *dev,
1465 	struct hbm_flow_control *flow_control)
1466 {
1467 	struct heci_file_private *file_pos = NULL;
1468 	struct heci_file_private *file_next = NULL;
1469 
1470 	if (flow_control->host_addr == 0) {
1471 		/* single receive buffer */
1472 		add_single_flow_creds(dev, flow_control);
1473 	} else {
1474 		/* normal connection */
1475 		list_for_each_entry_safe(file_pos, file_next,
1476 		    &dev->file_list, link, struct heci_file_private) {
1477 			DBG("list_for_each_entry_safe in file_list\n");
1478 
1479 			DBG("file_ext of host client %d ME client %d.\n",
1480 			    file_pos->host_client_id,
1481 			    file_pos->me_client_id);
1482 			DBG("flow ctrl msg for host %d ME %d.\n",
1483 			    flow_control->host_addr,
1484 			    flow_control->me_addr);
1485 			if (same_flow_addr(file_pos, flow_control)) {
1486 				DBG("recv ctrl msg for host  %d ME %d.\n",
1487 				    flow_control->host_addr,
1488 				    flow_control->me_addr);
1489 				file_pos->flow_ctrl_creds++;
1490 				DBG("flow control credentials=%d.\n",
1491 				    file_pos->flow_ctrl_creds);
1492 				break;
1493 			}
1494 		}
1495 	}
1496 }
1497 
1498 /*
1499  * same_disconn_addr - tell they have same address
1500  *
1501  * @file: private data of the file object.
1502  * @disconn: disconnection request.
1503  *
1504  * @return !=0, same; 0,not.
1505  */
1506 static int
1507 same_disconn_addr(struct heci_file_private *file,
1508 	struct hbm_client_disconnect_request *disconn)
1509 {
1510 	return ((file->host_client_id == disconn->host_addr) &&
1511 	    (file->me_client_id == disconn->me_addr));
1512 }
1513 
1514 /*
1515  * heci_client_disconnect_request  - disconnect request bh routine
1516  *
1517  * @dev: Device object for our driver.
1518  * @disconnect_req: disconnect request bus message.
1519  */
1520 static void
1521 heci_client_disconnect_request(struct iamt_heci_device *dev,
1522 		struct hbm_client_disconnect_request *disconnect_req)
1523 {
1524 	struct heci_msg_hdr *heci_hdr;
1525 	struct hbm_client_connect_response *disconnect_res;
1526 	struct heci_file_private *file_pos = NULL;
1527 	struct heci_file_private *file_next = NULL;
1528 
1529 	list_for_each_entry_safe(file_pos, file_next, &dev->file_list,
1530 	    link, struct heci_file_private) {
1531 
1532 		if (same_disconn_addr(file_pos, disconnect_req)) {
1533 			DBG("disconnect request host client %d ME client %d.\n",
1534 			    disconnect_req->host_addr,
1535 			    disconnect_req->me_addr);
1536 			file_pos->state = HECI_FILE_DISCONNECTED;
1537 			file_pos->timer_count = 0;
1538 			if (file_pos == &dev->wd_file_ext) {
1539 				dev->wd_due_counter = 0;
1540 				dev->wd_pending = 0;
1541 			} else if (file_pos == &dev->iamthif_file_ext)
1542 				dev->iamthif_timer = 0;
1543 
1544 			/* prepare disconnect response */
1545 			heci_hdr =
1546 			    (struct heci_msg_hdr *)&dev->ext_msg_buf[0];
1547 			heci_hdr->host_addr = 0;
1548 			heci_hdr->me_addr = 0;
1549 			heci_hdr->length =
1550 			    sizeof (struct hbm_client_connect_response);
1551 			heci_hdr->msg_complete = 1;
1552 			heci_hdr->reserved = 0;
1553 
1554 			disconnect_res =
1555 			    (struct hbm_client_connect_response *)
1556 			    &dev->ext_msg_buf[1];
1557 			disconnect_res->host_addr = file_pos->host_client_id;
1558 			disconnect_res->me_addr = file_pos->me_client_id;
1559 			*(uint8_t *)(&disconnect_res->cmd) =
1560 			    CLIENT_DISCONNECT_RES_CMD;
1561 			disconnect_res->status = 0;
1562 			dev->extra_write_index = 2;
1563 			break;
1564 		}
1565 	}
1566 }
1567 
1568 /*
1569  * heci_timer - timer function.
1570  *
1571  * @data: pointer to the device structure
1572  *
1573  * NOTE: This function is called by timer interrupt work
1574  */
1575 void
1576 heci_wd_timer(void *data)
1577 {
1578 	struct iamt_heci_device *dev = (struct iamt_heci_device *)data;
1579 
1580 	DBG("send watchdog.\n");
1581 	mutex_enter(&dev->device_lock);
1582 	if (dev->heci_state != HECI_ENABLED) {
1583 		dev->wd_timer = timeout(heci_wd_timer, data, 2 * HZ);
1584 		mutex_exit(&dev->device_lock);
1585 		return;
1586 	}
1587 	if (dev->wd_file_ext.state != HECI_FILE_CONNECTED) {
1588 		dev->wd_timer = timeout(heci_wd_timer, data, 2 * HZ);
1589 		mutex_exit(&dev->device_lock);
1590 		return;
1591 	}
1592 	/* Watchdog */
1593 	if ((dev->wd_due_counter != 0) && (dev->wd_bypass == 0)) {
1594 		if (--dev->wd_due_counter == 0) {
1595 			if (dev->host_buffer_is_empty &&
1596 			    flow_ctrl_creds(dev, &dev->wd_file_ext)) {
1597 				dev->host_buffer_is_empty = 0;
1598 				if (!heci_send_wd(dev)) {
1599 					DBG("wd send failed.\n");
1600 				} else {
1601 					flow_ctrl_reduce(dev,
1602 					    &dev->wd_file_ext);
1603 				}
1604 
1605 				if (dev->wd_timeout != 0)
1606 					dev->wd_due_counter = 2;
1607 				else
1608 					dev->wd_due_counter = 0;
1609 
1610 			} else
1611 				dev->wd_pending = 1;
1612 
1613 		}
1614 	}
1615 	if (dev->iamthif_stall_timer != 0) {
1616 		if (--dev->iamthif_stall_timer == 0) {
1617 			DBG("reseting because of hang to PTHI.\n");
1618 			heci_reset(dev, 1);
1619 			dev->iamthif_msg_buf_size = 0;
1620 			dev->iamthif_msg_buf_index = 0;
1621 			dev->iamthif_canceled = 0;
1622 			dev->iamthif_ioctl = 1;
1623 			dev->iamthif_state = HECI_IAMTHIF_IDLE;
1624 			dev->iamthif_timer = 0;
1625 			mutex_exit(&dev->device_lock);
1626 
1627 			if (dev->iamthif_current_cb)
1628 				heci_free_cb_private(dev->iamthif_current_cb);
1629 
1630 			mutex_enter(&dev->device_lock);
1631 			dev->iamthif_file_object = NULL;
1632 			dev->iamthif_current_cb = NULL;
1633 			run_next_iamthif_cmd(dev);
1634 		}
1635 	}
1636 	dev->wd_timer = timeout(heci_wd_timer, data, 2 * HZ);
1637 	mutex_exit(&dev->device_lock);
1638 }
1639