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#include "heci_interface.h"
64#include "heci.h"
65
66
67const uint8_t watch_dog_data[] = {
68	1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
69};
70const uint8_t start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 };
71const uint8_t stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 };
72
73const uint8_t heci_wd_state_independence_msg[3][4] = {
74	{0x05, 0x02, 0x51, 0x10},
75	{0x05, 0x02, 0x52, 0x10},
76	{0x07, 0x02, 0x01, 0x10}
77};
78
79const struct guid heci_asf_guid = {
80	0x75B30CD6, 0xA29E, 0x4AF7,
81	{0xA7, 0x12, 0xE6, 0x17, 0x43, 0x93, 0xC8, 0xA6}
82};
83const struct guid heci_wd_guid = {
84	0x05B79A6F, 0x4628, 0x4D7F,
85	{0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB}
86};
87const struct guid heci_pthi_guid = {
88	0x12f80028, 0xb4b7, 0x4b2d,
89	{0xac, 0xa8, 0x46, 0xe0, 0xff, 0x65, 0x81, 0x4c}
90};
91
92
93/*
94 *  heci init function prototypes
95 */
96static void heci_check_asf_mode(struct iamt_heci_device *dev);
97static int host_start_message(struct iamt_heci_device *dev);
98static int host_enum_clients_message(struct iamt_heci_device *dev);
99static int allocate_me_clients_storage(struct iamt_heci_device *dev);
100static void host_init_wd(struct iamt_heci_device *dev);
101static void host_init_iamthif(struct iamt_heci_device *dev);
102static inline int heci_fe_same_id(struct heci_file_private *fe1,
103		struct heci_file_private *fe2);
104
105
106
107/*
108 * heci_initialize_list - Sets up a  queue  list.
109 *
110 * @list: An instance of our list structure
111 * @dev: Device object for our driver
112 */
113void
114heci_initialize_list(struct io_heci_list *list,
115	struct iamt_heci_device *dev)
116{
117	/* initialize our queue list */
118	LIST_INIT_HEAD(&list->heci_cb.cb_list);
119	list->status = 0;
120	list->device_extension = dev;
121}
122
123/*
124 * heci_flush_queues - flush our queues list belong to file_ext.
125 *
126 * @dev: Device object for our driver
127 * @file_ext: private data of the file object
128 *
129 */
130void
131heci_flush_queues(struct iamt_heci_device *dev,
132	struct heci_file_private *file_ext)
133{
134	int i;
135
136	if (!dev || !file_ext)
137		return;
138
139	/* flush our queue list belong to file_ext */
140	for (i = 0; i < HECI_IO_LISTS_NUMBER; i++) {
141		DBG("remove list entry belong to file_ext\n");
142		heci_flush_list(dev->io_list_array[i], file_ext);
143	}
144}
145
146
147/*
148 * heci_flush_list - remove list entry belong to file_ext.
149 *
150 * @list:  An instance of our list structure
151 * @file_ext: private data of the file object
152 */
153void
154heci_flush_list(struct io_heci_list *list,
155	struct heci_file_private *file_ext)
156{
157	struct heci_file_private *file_ext_tmp;
158	struct heci_cb_private *priv_cb_pos = NULL;
159	struct heci_cb_private *priv_cb_next = NULL;
160
161	if (!list || !file_ext)
162		return;
163
164	if (list->status != 0)
165		return;
166
167	if (list_empty(&list->heci_cb.cb_list))
168		return;
169
170	list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
171		&list->heci_cb.cb_list, cb_list, struct heci_cb_private) {
172		if (priv_cb_pos) {
173			file_ext_tmp = (struct heci_file_private *)
174				priv_cb_pos->file_private;
175			if (file_ext_tmp) {
176				if (heci_fe_same_id(file_ext, file_ext_tmp))
177					list_del(&priv_cb_pos->cb_list);
178			}
179		}
180	}
181}
182
183/*
184 * heci_reset_iamthif_params - initializes heci device iamthif
185 * @dev: The heci device structure
186 */
187static void heci_reset_iamthif_params(struct iamt_heci_device *dev)
188{
189	/* reset iamthif parameters. */
190	dev->iamthif_current_cb = NULL;
191	dev->iamthif_msg_buf_size = 0;
192	dev->iamthif_msg_buf_index = 0;
193	dev->iamthif_canceled = 0;
194	dev->iamthif_file_ext.file = NULL;
195	dev->iamthif_ioctl = 0;
196	dev->iamthif_state = HECI_IAMTHIF_IDLE;
197	dev->iamthif_timer = 0;
198}
199
200/*
201 * fini_heci_device - release resources allocated in init_heci_device()
202 */
203void
204fini_heci_device(struct iamt_heci_device *device)
205{
206	mutex_destroy(&device->device_lock);
207	cv_destroy(&device->wait_recvd_msg);
208	cv_destroy(&device->wait_stop_wd);
209	if (device->work)
210		ddi_taskq_destroy(device->work);
211	if (device->reinit_tsk)
212		ddi_taskq_destroy(device->reinit_tsk);
213
214}
215
216/*
217 * init_heci_device - initializes the heci device structure
218 *
219 */
220void
221init_heci_device(dev_info_t *dip,
222	struct iamt_heci_device *device)
223{
224	int i;
225
226	if (!device)
227		return;
228
229	/* setup our list array */
230	device->io_list_array[0] = &device->read_list;
231	device->io_list_array[1] = &device->write_list;
232	device->io_list_array[2] = &device->write_waiting_list;
233	device->io_list_array[3] = &device->ctrl_wr_list;
234	device->io_list_array[4] = &device->ctrl_rd_list;
235	device->io_list_array[5] = &device->pthi_cmd_list;
236	device->io_list_array[6] = &device->pthi_read_complete_list;
237	LIST_INIT_HEAD(&device->file_list);
238	LIST_INIT_HEAD(&device->wd_file_ext.link);
239	LIST_INIT_HEAD(&device->iamthif_file_ext.link);
240	mutex_init(&device->device_lock, NULL, MUTEX_DRIVER, NULL);
241	cv_init(&device->wait_recvd_msg, NULL, CV_DRIVER, NULL);
242	cv_init(&device->wait_stop_wd, NULL, CV_DRIVER, NULL);
243	device->open_handle_count = 0;
244	device->num_heci_me_clients = 0;
245	device->extra_write_index = 0;
246	device->rd_msg_hdr = 0;
247	device->mem_addr = NULL;
248	device->asf_mode = B_FALSE;
249	device->need_reset = B_FALSE;
250	device->recvd_msg = B_FALSE;
251	device->heci_state = HECI_INITIALIZING;
252	device->iamthif_state = HECI_IAMTHIF_IDLE;
253
254	device->work = ddi_taskq_create(dip, "heci_bh_handler", 1,
255	    TASKQ_DEFAULTPRI, 0);
256	if (device->work == NULL)
257		cmn_err(CE_WARN, "taskq_create failed for heci_bh_handler");
258	device->reinit_tsk = ddi_taskq_create(dip, "heci_reinit_tsk", 1,
259	    TASKQ_DEFAULTPRI, 0);
260	if (device->reinit_tsk == NULL)
261		cmn_err(CE_WARN, "taskq_create failed for reinit_tsk");
262
263	device->wd_pending = B_FALSE;
264	device->wd_stoped = B_FALSE;
265
266	device->me_clients = NULL;
267	for (i = 0; i < HECI_IO_LISTS_NUMBER; i++)
268		heci_initialize_list(device->io_list_array[i], device);
269	device->dip = dip;
270}
271
272/*
273 * heci_hw_init  - init host and fw to start work.
274 *
275 * @dev: Device object for our driver
276 *
277 * @return 0 on success, <0 on failure.
278 */
279int
280heci_hw_init(struct iamt_heci_device *dev)
281{
282	int err = 0;
283
284	mutex_enter(&dev->device_lock);
285	dev->host_hw_state = read_heci_register(dev, H_CSR);
286	dev->me_hw_state = read_heci_register(dev, ME_CSR_HA);
287	DBG("host_hw_state = 0x%08x, mestate = 0x%08x.\n",
288	    dev->host_hw_state, dev->me_hw_state);
289
290	if ((dev->host_hw_state & H_IS) == H_IS) {
291		/* acknowledge interrupt and stop interupts */
292		heci_set_csr_register(dev);
293	}
294	dev->recvd_msg = 0;
295	DBG("reset in start the heci device.\n");
296
297	heci_reset(dev, 1);
298
299	DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
300	    dev->host_hw_state, dev->me_hw_state);
301
302	/* wait for ME to turn on ME_RDY */
303	err = 0;
304	while (!dev->recvd_msg && err != -1) {
305		err = cv_reltimedwait(&dev->wait_recvd_msg,
306		    &dev->device_lock, HECI_INTEROP_TIMEOUT, TR_CLOCK_TICK);
307	}
308
309	if (err == -1 && !dev->recvd_msg) {
310		dev->heci_state = HECI_DISABLED;
311		DBG("wait_event_interruptible_timeout failed"
312		    "on wait for ME to turn on ME_RDY.\n");
313		mutex_exit(&dev->device_lock);
314		return (-ENODEV);
315	} else {
316		if (!(((dev->host_hw_state & H_RDY) == H_RDY) &&
317		    ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA))) {
318			dev->heci_state = HECI_DISABLED;
319			DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
320			    dev->host_hw_state,
321			    dev->me_hw_state);
322
323			if (!(dev->host_hw_state & H_RDY) != H_RDY)
324				DBG("host turn off H_RDY.\n");
325
326			if (!(dev->me_hw_state & ME_RDY_HRA) != ME_RDY_HRA)
327				DBG("ME turn off ME_RDY.\n");
328
329			cmn_err(CE_WARN,
330			    "heci: link layer initialization failed.\n");
331			mutex_exit(&dev->device_lock);
332			return (-ENODEV);
333		}
334	}
335	dev->recvd_msg = 0;
336	DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
337	    dev->host_hw_state, dev->me_hw_state);
338	DBG("ME turn on ME_RDY and host turn on H_RDY.\n");
339	DBG("heci: link layer has been established.\n");
340	mutex_exit(&dev->device_lock);
341	return (0);
342}
343
344/*
345 * heci_hw_reset  - reset fw via heci csr register.
346 *
347 * @dev: Device object for our driver
348 * @interrupts: if interrupt should be enable after reset.
349 */
350static void heci_hw_reset(struct iamt_heci_device *dev, int interrupts)
351{
352	dev->host_hw_state |= (H_RST | H_IG);
353
354	if (interrupts)
355		heci_csr_enable_interrupts(dev);
356	else
357		heci_csr_disable_interrupts(dev);
358
359}
360
361/*
362 * heci_reset  - reset host and fw.
363 *
364 * @dev: Device object for our driver
365 * @interrupts: if interrupt should be enable after reset.
366 */
367void
368heci_reset(struct iamt_heci_device *dev, int interrupts)
369{
370	struct heci_file_private *file_pos = NULL;
371	struct heci_file_private *file_next = NULL;
372	struct heci_cb_private *priv_cb_pos = NULL;
373	struct heci_cb_private *priv_cb_next = NULL;
374	int unexpected = 0;
375
376	if (dev->heci_state == HECI_RECOVERING_FROM_RESET) {
377		dev->need_reset = 1;
378		return;
379	}
380
381	if (dev->heci_state != HECI_INITIALIZING &&
382	    dev->heci_state != HECI_DISABLED &&
383	    dev->heci_state != HECI_POWER_DOWN &&
384	    dev->heci_state != HECI_POWER_UP)
385		unexpected = 1;
386
387	if (dev->reinit_tsk != NULL) {
388		mutex_exit(&dev->device_lock);
389		(void) ddi_taskq_wait(dev->reinit_tsk);
390		mutex_enter(&dev->device_lock);
391	}
392
393	dev->host_hw_state = read_heci_register(dev, H_CSR);
394
395	DBG("before reset host_hw_state = 0x%08x.\n",
396	    dev->host_hw_state);
397
398	heci_hw_reset(dev, interrupts);
399
400	dev->host_hw_state &= ~H_RST;
401	dev->host_hw_state |= H_IG;
402
403	write_heci_register(dev, H_CSR, dev->host_hw_state);
404
405	DBG("currently saved host_hw_state = 0x%08x.\n",
406	    dev->host_hw_state);
407
408	dev->need_reset = 0;
409
410	if (dev->heci_state != HECI_INITIALIZING) {
411		if ((dev->heci_state != HECI_DISABLED) &&
412		    (dev->heci_state != HECI_POWER_DOWN))
413			dev->heci_state = HECI_RESETING;
414
415		list_for_each_entry_safe(file_pos,
416		    file_next, &dev->file_list, link,
417		    struct heci_file_private) {
418			file_pos->state = HECI_FILE_DISCONNECTED;
419			file_pos->flow_ctrl_creds = 0;
420			file_pos->read_cb = NULL;
421			file_pos->timer_count = 0;
422		}
423		/* remove entry if already in list */
424		DBG("list del iamthif and wd file list.\n");
425		heci_remove_client_from_file_list(dev,
426		    dev->wd_file_ext.host_client_id);
427
428		heci_remove_client_from_file_list(dev,
429		    dev->iamthif_file_ext.host_client_id);
430
431		heci_reset_iamthif_params(dev);
432		dev->wd_due_counter = 0;
433		dev->extra_write_index = 0;
434	}
435
436	dev->num_heci_me_clients = 0;
437	dev->rd_msg_hdr = 0;
438	dev->stop = 0;
439	dev->wd_pending = 0;
440
441	/* update the state of the registers after reset */
442	dev->host_hw_state =  read_heci_register(dev, H_CSR);
443	dev->me_hw_state =  read_heci_register(dev, ME_CSR_HA);
444
445	DBG("after reset host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
446	    dev->host_hw_state, dev->me_hw_state);
447
448	if (unexpected)
449		cmn_err(CE_WARN, "unexpected heci reset.\n");
450
451	/* Wake up all readings so they can be interrupted */
452	list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link,
453	    struct heci_file_private) {
454		cmn_err(CE_NOTE, "heci: Waking up client!\n");
455		cv_broadcast(&file_pos->rx_wait);
456	}
457	/* remove all waiting requests */
458	if (dev->write_list.status == 0 &&
459	    !list_empty(&dev->write_list.heci_cb.cb_list)) {
460		list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
461		    &dev->write_list.heci_cb.cb_list, cb_list,
462		    struct heci_cb_private) {
463			if (priv_cb_pos) {
464				list_del(&priv_cb_pos->cb_list);
465				heci_free_cb_private(priv_cb_pos);
466			}
467		}
468	}
469}
470
471/*
472 * heci_initialize_clients  -  routine.
473 *
474 * @dev: Device object for our driver
475 *
476 */
477int
478heci_initialize_clients(struct iamt_heci_device *dev)
479{
480	int status;
481
482	/* msleep(100) FW needs time to be ready to talk with us */
483	delay(drv_usectohz(100000));
484	DBG("link is established start sending messages.\n");
485	/* link is established start sending messages. */
486	status = host_start_message(dev);
487	if (status != 0) {
488		mutex_enter(&dev->device_lock);
489		dev->heci_state = HECI_DISABLED;
490		mutex_exit(&dev->device_lock);
491		DBG("start sending messages failed.\n");
492		return (status);
493	}
494	/* enumerate clients */
495
496	status = host_enum_clients_message(dev);
497	if (status != 0) {
498		mutex_enter(&dev->device_lock);
499		dev->heci_state = HECI_DISABLED;
500		mutex_exit(&dev->device_lock);
501		DBG("enum clients failed.\n");
502		return (status);
503	}
504	/* allocate storage for ME clients representation */
505	status = allocate_me_clients_storage(dev);
506	if (status != 0) {
507		mutex_enter(&dev->device_lock);
508		dev->num_heci_me_clients = 0;
509		dev->heci_state = HECI_DISABLED;
510		mutex_exit(&dev->device_lock);
511		DBG("allocate clients failed.\n");
512		return (status);
513	}
514
515	heci_check_asf_mode(dev);
516	/* heci initialization wd */
517	host_init_wd(dev);
518	/* heci initialization iamthif client */
519	host_init_iamthif(dev);
520
521	mutex_enter(&dev->device_lock);
522	if (dev->need_reset) {
523		dev->need_reset = 0;
524		dev->heci_state = HECI_DISABLED;
525		mutex_exit(&dev->device_lock);
526		return (-ENODEV);
527	}
528
529	(void) memset(dev->heci_host_clients, 0,
530	    sizeof (dev->heci_host_clients));
531	dev->open_handle_count = 0;
532	dev->heci_host_clients[0] |= 7;
533	dev->current_host_client_id = 3;
534	dev->heci_state = HECI_ENABLED;
535	mutex_exit(&dev->device_lock);
536	DBG("initialization heci clients successful.\n");
537	return (0);
538}
539
540/*
541 * heci_task_initialize_clients  -  routine.
542 *
543 * @data: Device object for our driver
544 *
545 */
546void
547heci_task_initialize_clients(void *data)
548{
549	int ret;
550	struct iamt_heci_device *dev = (struct iamt_heci_device *)data;
551
552	ret = heci_initialize_clients(dev);
553	if (ret)
554		cmn_err(CE_WARN, "heci_initialize_clients() failed\n");
555}
556
557/*
558 * host_start_message - heci host send start message.
559 *
560 * @dev: Device object for our driver
561 *
562 * @return 0 on success, <0 on failure.
563 */
564static int
565host_start_message(struct iamt_heci_device *dev)
566{
567	long timeout = 60;	/* 60 second */
568	struct heci_msg_hdr *heci_hdr;
569	struct hbm_host_version_request *host_start_req;
570	struct hbm_host_stop_request *host_stop_req;
571	int err = 0;
572	clock_t delta = (clock_t)(timeout * HZ);
573
574	/* host start message */
575	mutex_enter(&dev->device_lock);
576	heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
577	heci_hdr->host_addr = 0;
578	heci_hdr->me_addr = 0;
579	heci_hdr->length = sizeof (struct hbm_host_version_request);
580	heci_hdr->msg_complete = 1;
581	heci_hdr->reserved = 0;
582
583	host_start_req =
584	    (struct hbm_host_version_request *)&dev->wr_msg_buf[1];
585	(void) memset(host_start_req, 0,
586	    sizeof (struct hbm_host_version_request));
587	host_start_req->cmd.cmd = HOST_START_REQ_CMD;
588	host_start_req->host_version.major_version = HBM_MAJOR_VERSION;
589	host_start_req->host_version.minor_version = HBM_MINOR_VERSION;
590	dev->recvd_msg = 0;
591	if (!heci_write_message(dev, heci_hdr,
592	    (unsigned char *)(host_start_req),
593	    heci_hdr->length)) {
594		DBG("send version to fw fail.\n");
595		mutex_exit(&dev->device_lock);
596		return (-ENODEV);
597	}
598	DBG("call wait_event_interruptible_timeout for response message.\n");
599	err = 0;
600	while (err != -1 && !dev->recvd_msg) {
601		err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
602		    delta, TR_CLOCK_TICK);
603	}
604	if (err == -1 && !dev->recvd_msg) {
605		DBG("wait_timeout failed on host start response message.\n");
606		mutex_exit(&dev->device_lock);
607		return (-ENODEV);
608	}
609	dev->recvd_msg = 0;
610	DBG("wait_timeout successful on host start response message.\n");
611	if ((dev->version.major_version != HBM_MAJOR_VERSION) ||
612	    (dev->version.minor_version != HBM_MINOR_VERSION)) {
613		/* send stop message */
614		heci_hdr->host_addr = 0;
615		heci_hdr->me_addr = 0;
616		heci_hdr->length = sizeof (struct hbm_host_stop_request);
617		heci_hdr->msg_complete = 1;
618		heci_hdr->reserved = 0;
619
620		host_stop_req =
621		    (struct hbm_host_stop_request *)&dev->wr_msg_buf[1];
622
623		(void) memset(host_stop_req, 0,
624		    sizeof (struct hbm_host_stop_request));
625		host_stop_req->cmd.cmd = HOST_STOP_REQ_CMD;
626		host_stop_req->reason = DRIVER_STOP_REQUEST;
627		if (!heci_write_message(dev, heci_hdr,
628		    (unsigned char *)(host_stop_req),
629		    heci_hdr->length)) {
630			DBG("sending stop msg to fw failed.\n");
631		}
632		DBG("version mismatch.\n");
633		mutex_exit(&dev->device_lock);
634		return (-ENODEV);
635	}
636	mutex_exit(&dev->device_lock);
637	return (0);
638}
639
640/*
641 * host_enum_clients_message - host send enumeration client request message.
642 *
643 * @dev: Device object for our driver
644 * @return 0 on success, <0 on failure.
645 */
646static int
647host_enum_clients_message(struct iamt_heci_device *dev)
648{
649	long timeout = 5;	/* 5 second */
650	struct heci_msg_hdr *heci_hdr;
651	struct hbm_host_enum_request *host_enum_req;
652	int err = 0;
653	uint8_t i, j;
654	clock_t delta = (clock_t)(timeout * HZ);
655
656	mutex_enter(&dev->device_lock);
657
658	heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
659	/* enumerate clients */
660	heci_hdr->host_addr = 0;
661	heci_hdr->me_addr = 0;
662	heci_hdr->length = sizeof (struct hbm_host_enum_request);
663	heci_hdr->msg_complete = 1;
664	heci_hdr->reserved = 0;
665
666	host_enum_req = (struct hbm_host_enum_request *)&dev->wr_msg_buf[1];
667	(void) memset(host_enum_req, 0, sizeof (struct hbm_host_enum_request));
668	host_enum_req->cmd.cmd = HOST_ENUM_REQ_CMD;
669	if (!heci_write_message(dev, heci_hdr,
670	    (unsigned char *)(host_enum_req),
671	    heci_hdr->length)) {
672		DBG("send enumeration request failed.\n");
673		mutex_exit(&dev->device_lock);
674		return (-ENODEV);
675	}
676	/* wait for response */
677	dev->recvd_msg = 0;
678	err = 0;
679	while (!dev->recvd_msg && err != -1) {
680		err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
681		    delta, TR_CLOCK_TICK);
682	}
683	if (err == -1 && !dev->recvd_msg) {
684		DBG("wait_event_interruptible_timeout failed "
685		"on enumeration clients response message.\n");
686		mutex_exit(&dev->device_lock);
687		return (-ENODEV);
688	}
689	dev->recvd_msg = 0;
690
691	/* count how many ME clients we have */
692	for (i = 0; i < sizeof (dev->heci_me_clients); i++) {
693		for (j = 0; j < 8; j++) {
694			if ((dev->heci_me_clients[i] & (1 << j)) != 0)
695				dev->num_heci_me_clients++;
696
697		}
698	}
699	mutex_exit(&dev->device_lock);
700
701	return (0);
702}
703
704/*
705 * host_client_properties - reads properties for client
706 *
707 * @dev: Device object for our driver
708 * @idx: client index in me client array
709 * @client_id: id of the client
710 *
711 * @return 0 on success, <0 on failure.
712 */
713static int
714host_client_properties(struct iamt_heci_device *dev,
715	struct heci_me_client *client)
716{
717	struct heci_msg_hdr *heci_hdr;
718	struct hbm_props_request *host_cli_req;
719	int err;
720	clock_t delta = 10 * HZ;
721
722	mutex_enter(&dev->device_lock);
723	heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
724	heci_hdr->host_addr = 0;
725	heci_hdr->me_addr = 0;
726	heci_hdr->length = sizeof (struct hbm_props_request);
727	heci_hdr->msg_complete = 1;
728	heci_hdr->reserved = 0;
729
730	host_cli_req = (struct hbm_props_request *)&dev->wr_msg_buf[1];
731	(void) memset(host_cli_req, 0, sizeof (struct hbm_props_request));
732	host_cli_req->cmd.cmd = HOST_CLIENT_PROPERTEIS_REQ_CMD;
733	host_cli_req->address = client->client_id;
734	if (!heci_write_message(dev, heci_hdr,
735	    (unsigned char *)(host_cli_req), heci_hdr->length)) {
736		DBG("send props request failed.\n");
737		mutex_exit(&dev->device_lock);
738		return (-ENODEV);
739	}
740	/* wait for response */
741	dev->recvd_msg = 0;
742
743	err = 0;
744	while (!dev->recvd_msg && err != -1) {
745		err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
746		    delta, TR_CLOCK_TICK);
747	}
748	if (err == -1 && !dev->recvd_msg) {
749		DBG("wait failed on props resp msg.\n");
750		mutex_exit(&dev->device_lock);
751		return (-ENODEV);
752	}
753	dev->recvd_msg = 0;
754	mutex_exit(&dev->device_lock);
755	return (0);
756}
757
758/*
759 * allocate_me_clients_storage - allocate storage for me clients
760 *
761 * @dev: Device object for our driver
762 *
763 * @return 0 on success, <0 on failure.
764 */
765static int
766allocate_me_clients_storage(struct iamt_heci_device *dev)
767{
768	struct heci_me_client *clients;
769	struct heci_me_client *client;
770	uint8_t num, i, j;
771	int err;
772
773	if (dev->num_heci_me_clients == 0)
774		return (0);
775
776	mutex_enter(&dev->device_lock);
777	if (dev->me_clients) {
778		kmem_free(dev->me_clients, dev->num_heci_me_clients*
779		    sizeof (struct heci_me_client));
780		dev->me_clients = NULL;
781	}
782	mutex_exit(&dev->device_lock);
783
784	/* allocate storage for ME clients representation */
785	clients = kmem_zalloc(dev->num_heci_me_clients*
786	    sizeof (struct heci_me_client), KM_SLEEP);
787	if (!clients) {
788		DBG("memory allocation for ME clients failed.\n");
789		return (-ENOMEM);
790	}
791
792	mutex_enter(&dev->device_lock);
793	dev->me_clients = clients;
794	mutex_exit(&dev->device_lock);
795
796	num = 0;
797	for (i = 0; i < sizeof (dev->heci_me_clients); i++) {
798		for (j = 0; j < 8; j++) {
799			if ((dev->heci_me_clients[i] & (1 << j)) != 0) {
800				client = &dev->me_clients[num];
801				client->client_id = (i * 8) + j;
802				client->flow_ctrl_creds = 0;
803				err = host_client_properties(dev, client);
804				if (err != 0) {
805					mutex_enter(&dev->device_lock);
806					kmem_free(dev->me_clients,
807					    dev->num_heci_me_clients*
808					    sizeof (struct heci_me_client));
809					dev->me_clients = NULL;
810					mutex_exit(&dev->device_lock);
811					return (err);
812				}
813				num++;
814			}
815		}
816	}
817
818	return (0);
819}
820
821/*
822 * heci_init_file_private - initializes private file structure.
823 *
824 * @priv: private file structure to be initialized
825 * @file: the file structure
826 *
827 */
828static void
829heci_init_file_private(struct heci_file_private *priv,
830	struct heci_file *file)
831{
832	_NOTE(ARGUNUSED(file));
833
834	(void) memset(priv, 0, sizeof (struct heci_file_private));
835	mutex_init(&priv->file_lock, NULL, MUTEX_DRIVER, NULL);
836	mutex_init(&priv->read_io_lock, NULL, MUTEX_DRIVER, NULL);
837	mutex_init(&priv->write_io_lock, NULL, MUTEX_DRIVER, NULL);
838	cv_init(&priv->rx_wait, NULL, CV_DRIVER, NULL);
839	DBG("priv->rx_wait =%p\n", (void *)&priv->rx_wait);
840	LIST_INIT_HEAD(&priv->link);
841	priv->reading_state = HECI_IDLE;
842	priv->writing_state = HECI_IDLE;
843}
844
845/*
846 * heci_find_me_client - search for ME client guid
847 *                       sets client_id in heci_file_private if found
848 * @dev: Device object for our driver
849 * @priv: private file structure to set client_id in
850 * @cguid: searched guid of ME client
851 * @client_id: id of host client to be set in file private structure
852 *
853 * @return ME client index
854 */
855static uint8_t
856heci_find_me_client(struct iamt_heci_device *dev,
857				struct heci_file_private *priv,
858				const struct guid *cguid, uint8_t client_id)
859{
860	uint8_t i;
861
862	if ((dev == NULL) || (priv == NULL) || (cguid == NULL))
863		return (0);
864
865	for (i = 0; i < dev->num_heci_me_clients; i++) {
866		if (memcmp(cguid,
867		    &dev->me_clients[i].props.protocol_name,
868		    sizeof (struct guid)) == 0) {
869			priv->me_client_id = dev->me_clients[i].client_id;
870			priv->state = HECI_FILE_CONNECTING;
871			priv->host_client_id = client_id;
872
873			list_add_tail(&priv->link, &dev->file_list);
874			return (i);
875		}
876	}
877	return (0);
878}
879
880/*
881 * heci_check_asf_mode - check for ASF client
882 *
883 * @dev: Device object for our driver
884 *
885 */
886static void
887heci_check_asf_mode(struct iamt_heci_device *dev)
888{
889	uint8_t i;
890
891	mutex_enter(&dev->device_lock);
892	dev->asf_mode = 0;
893	/* find ME ASF client - otherwise assume AMT mode */
894	DBG("find ME ASF client - otherwise assume AMT mode.\n");
895	for (i = 0; i < dev->num_heci_me_clients; i++) {
896		if (memcmp(&heci_asf_guid,
897		    &dev->me_clients[i].props.protocol_name,
898		    sizeof (struct guid)) == 0) {
899			dev->asf_mode = 1;
900			mutex_exit(&dev->device_lock);
901			DBG("found ME ASF client.\n");
902			return;
903		}
904	}
905	mutex_exit(&dev->device_lock);
906	DBG("assume AMT mode.\n");
907}
908
909/*
910 * heci_connect_me_client - connect ME client
911 * @dev: Device object for our driver
912 * @priv: private file structure
913 * @timeout: connect timeout in seconds
914 *
915 * @return 1 - if connected, 0 - if not
916 */
917static uint8_t
918heci_connect_me_client(struct iamt_heci_device *dev,
919	struct heci_file_private *priv,
920	long timeout)
921{
922	int err = 0;
923	clock_t delta = (clock_t)(timeout * HZ);
924
925	if ((dev == NULL) || (priv == NULL))
926		return (0);
927
928	if (!heci_connect(dev, priv)) {
929		DBG("failed to call heci_connect for client_id=%d.\n",
930		    priv->host_client_id);
931		heci_remove_client_from_file_list(dev, priv->host_client_id);
932		priv->state = HECI_FILE_DISCONNECTED;
933		return (0);
934	}
935	err = 0;
936	while (!(HECI_FILE_CONNECTED == priv->state ||
937	    HECI_FILE_DISCONNECTED == priv->state) &&
938	    err != -1) {
939		err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
940		    delta, TR_CLOCK_TICK);
941	}
942	if (HECI_FILE_CONNECTED != priv->state) {
943		heci_remove_client_from_file_list(dev, priv->host_client_id);
944		DBG("failed to connect client_id=%d state=%d.\n",
945		    priv->host_client_id, priv->state);
946		if (err)
947			DBG("failed connect err=%08x\n", err);
948		priv->state = HECI_FILE_DISCONNECTED;
949		return (0);
950	}
951	DBG("successfully connected client_id=%d.\n",
952	    priv->host_client_id);
953	return (1);
954}
955
956/*
957 * host_init_wd - heci initialization wd.
958 *
959 * @dev: Device object for our driver
960 *
961 */
962static void host_init_wd(struct iamt_heci_device *dev)
963{
964
965	mutex_enter(&dev->device_lock);
966
967	heci_init_file_private(&dev->wd_file_ext, NULL);
968
969	/* look for WD client and connect to it */
970	dev->wd_file_ext.state = HECI_FILE_DISCONNECTED;
971	dev->wd_timeout = 0;
972
973	if (dev->asf_mode) {
974		(void) memcpy(dev->wd_data, stop_wd_params,
975		    HECI_WD_PARAMS_SIZE);
976	} else {
977		/* AMT mode */
978		dev->wd_timeout = AMT_WD_VALUE;
979		DBG("dev->wd_timeout=%d.\n", dev->wd_timeout);
980		(void) memcpy(dev->wd_data, start_wd_params,
981		    HECI_WD_PARAMS_SIZE);
982		(void) memcpy(dev->wd_data + HECI_WD_PARAMS_SIZE,
983		    &dev->wd_timeout, sizeof (uint16_t));
984	}
985
986	/* find ME WD client */
987	(void) heci_find_me_client(dev, &dev->wd_file_ext,
988	    &heci_wd_guid, HECI_WD_HOST_CLIENT_ID);
989
990	DBG("check wd_file_ext\n");
991	if (HECI_FILE_CONNECTING == dev->wd_file_ext.state) {
992		if (heci_connect_me_client(dev, &dev->wd_file_ext, 15) == 1) {
993			DBG("dev->wd_timeout=%d.\n", dev->wd_timeout);
994			if (dev->wd_timeout != 0)
995				dev->wd_due_counter = 1;
996			else
997				dev->wd_due_counter = 0;
998			DBG("successfully connected to WD client.\n");
999		}
1000	} else
1001		DBG("failed to find WD client.\n");
1002
1003
1004	mutex_exit(&dev->device_lock);
1005}
1006
1007
1008/*
1009 * host_init_iamthif - heci initialization iamthif client.
1010 *
1011 * @dev: Device object for our driver
1012 *
1013 */
1014static void
1015host_init_iamthif(struct iamt_heci_device *dev)
1016{
1017	uint8_t i;
1018
1019	mutex_enter(&dev->device_lock);
1020
1021	heci_init_file_private(&dev->iamthif_file_ext, NULL);
1022	dev->iamthif_file_ext.state = HECI_FILE_DISCONNECTED;
1023
1024	/* find ME PTHI client */
1025	i = heci_find_me_client(dev, &dev->iamthif_file_ext,
1026	    &heci_pthi_guid, HECI_IAMTHIF_HOST_CLIENT_ID);
1027	if (dev->iamthif_file_ext.state != HECI_FILE_CONNECTING) {
1028		DBG("failed to find iamthif client.\n");
1029		mutex_exit(&dev->device_lock);
1030		return;
1031	}
1032
1033	ASSERT(dev->me_clients[i].props.max_msg_length == IAMTHIF_MTU);
1034
1035	if (heci_connect_me_client(dev, &dev->iamthif_file_ext, 15) == 1) {
1036		DBG("connected to iamthif client.\n");
1037		dev->iamthif_state = HECI_IAMTHIF_IDLE;
1038	}
1039	mutex_exit(&dev->device_lock);
1040}
1041
1042/*
1043 * heci_alloc_file_private - allocates a private file structure and set it up.
1044 * @file: the file structure
1045 *
1046 * @return  The allocated file or NULL on failure
1047 */
1048struct heci_file_private *
1049heci_alloc_file_private(struct heci_file *file)
1050{
1051	struct heci_file_private *priv;
1052
1053	priv = kmem_zalloc(sizeof (struct heci_file_private), KM_SLEEP);
1054	if (!priv)
1055		return (NULL);
1056
1057	heci_init_file_private(priv, file);
1058
1059	return (priv);
1060}
1061
1062/*
1063 * heci_free_file_private - free a private file structure that were previously
1064 * allocated by heci_alloc_file_private
1065 */
1066void
1067heci_free_file_private(struct heci_file_private *priv)
1068{
1069	mutex_destroy(&priv->file_lock);
1070	mutex_destroy(&priv->read_io_lock);
1071	mutex_destroy(&priv->write_io_lock);
1072	cv_destroy(&priv->rx_wait);
1073	kmem_free(priv, sizeof (struct heci_file_private));
1074
1075}
1076
1077/*
1078 * heci_disconnect_host_client  - send disconnect message  to fw from host
1079 * client.
1080 *
1081 * @dev: Device object for our driver
1082 * @file_ext: private data of the file object
1083 *
1084 * @return 0 on success, <0 on failure.
1085 */
1086int
1087heci_disconnect_host_client(struct iamt_heci_device *dev,
1088		struct heci_file_private *file_ext)
1089{
1090	int rets, err;
1091	long timeout = 15;	/* 15 seconds */
1092	struct heci_cb_private *priv_cb;
1093	clock_t delta = (clock_t)(timeout * HZ);
1094
1095	if ((!dev) || (!file_ext))
1096		return (-ENODEV);
1097
1098	if (file_ext->state != HECI_FILE_DISCONNECTING)
1099		return (0);
1100
1101	priv_cb = kmem_zalloc(sizeof (struct heci_cb_private), KM_SLEEP);
1102	if (!priv_cb)
1103		return (-ENOMEM);
1104
1105	LIST_INIT_HEAD(&priv_cb->cb_list);
1106	priv_cb->file_private = file_ext;
1107	priv_cb->major_file_operations = HECI_CLOSE;
1108	mutex_enter(&dev->device_lock);
1109	if (dev->host_buffer_is_empty) {
1110		dev->host_buffer_is_empty = 0;
1111		if (heci_disconnect(dev, file_ext)) {
1112			list_add_tail(&priv_cb->cb_list,
1113			    &dev->ctrl_rd_list.heci_cb.cb_list);
1114		} else {
1115			mutex_exit(&dev->device_lock);
1116			rets = -ENODEV;
1117			DBG("failed to call heci_disconnect.\n");
1118			goto free;
1119		}
1120	} else {
1121		DBG("add disconnect cb to control write list\n");
1122		list_add_tail(&priv_cb->cb_list,
1123		    &dev->ctrl_wr_list.heci_cb.cb_list);
1124	}
1125
1126	err = 0;
1127	while (err != -1 &&
1128	    (HECI_FILE_DISCONNECTED != file_ext->state)) {
1129
1130		err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
1131		    delta, TR_CLOCK_TICK);
1132	}
1133	mutex_exit(&dev->device_lock);
1134
1135	if (HECI_FILE_DISCONNECTED == file_ext->state) {
1136		rets = 0;
1137		DBG("successfully disconnected from fw client."
1138		    " me_client_id:%d, host_client_id:%d\n",
1139		    file_ext->me_client_id,
1140		    file_ext->host_client_id);
1141	} else {
1142		rets = -ENODEV;
1143		if (HECI_FILE_DISCONNECTED != file_ext->state)
1144			DBG("wrong status client disconnect.\n");
1145
1146		if (err)
1147			DBG("wait failed disconnect err=%08x\n", err);
1148
1149		DBG("failed to disconnect from fw client.\n"
1150		    " me_client_id:%d, host_client_id:%d\n",
1151		    file_ext->me_client_id,
1152		    file_ext->host_client_id);
1153	}
1154
1155	mutex_enter(&dev->device_lock);
1156	heci_flush_list(&dev->ctrl_rd_list, file_ext);
1157	heci_flush_list(&dev->ctrl_wr_list, file_ext);
1158	mutex_exit(&dev->device_lock);
1159free:
1160	heci_free_cb_private(priv_cb);
1161	return (rets);
1162}
1163
1164/*
1165 * heci_remove_client_from_file_list  -
1166 *	remove file private data from device file list
1167 *
1168 * @dev: Device object for our driver
1169 * @host_client_id: host client id to be removed
1170 *
1171 */
1172void
1173heci_remove_client_from_file_list(struct iamt_heci_device *dev,
1174	uint8_t host_client_id)
1175{
1176	struct heci_file_private *file_pos = NULL;
1177	struct heci_file_private *file_next = NULL;
1178	list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link,
1179		struct heci_file_private) {
1180		if (host_client_id == file_pos->host_client_id) {
1181			DBG("remove host client = %d, ME client = %d\n",
1182					file_pos->host_client_id,
1183					file_pos->me_client_id);
1184			list_del_init(&file_pos->link);
1185			break;
1186		}
1187	}
1188}
1189
1190/*
1191 * heci_fe_same_id - tell if file private data have same id
1192 *
1193 * @fe1: private data of 1. file object
1194 * @fe2: private data of 2. file object
1195 *
1196 * @return  !=0 - if ids are the same, 0 - if differ.
1197 */
1198static inline int heci_fe_same_id(struct heci_file_private *fe1,
1199		struct heci_file_private *fe2)
1200{
1201	return ((fe1->host_client_id == fe2->host_client_id) &&
1202	    (fe1->me_client_id == fe2->me_client_id));
1203}
1204