Staging: add heci driver
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / drivers / staging / heci / heci_init.c
blobc2d88c3009b102d6666d7bf4485e79d2a73750c3
1 /*
2 * Part of Intel(R) Manageability Engine Interface Linux driver
4 * Copyright (c) 2003 - 2008 Intel Corp.
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions, and the following disclaimer,
12 * without modification.
13 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
14 * substantially similar to the "NO WARRANTY" disclaimer below
15 * ("Disclaimer") and any redistribution must be conditioned upon
16 * including a substantially similar Disclaimer requirement for further
17 * binary redistribution.
18 * 3. Neither the names of the above-listed copyright holders nor the names
19 * of any contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
22 * Alternatively, this software may be distributed under the terms of the
23 * GNU General Public License ("GPL") version 2 as published by the Free
24 * Software Foundation.
26 * NO WARRANTY
27 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
30 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
36 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGES.
41 #include <linux/module.h>
42 #include <linux/pci.h>
43 #include <linux/reboot.h>
44 #include <linux/poll.h>
45 #include <linux/init.h>
46 #include <linux/kdev_t.h>
47 #include <linux/moduleparam.h>
48 #include <linux/wait.h>
49 #include <linux/delay.h>
50 #include <linux/kthread.h>
51 #include "kcompat.h"
53 #include "heci_data_structures.h"
54 #include "heci_interface.h"
55 #include "heci.h"
58 const __u8 heci_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 };
59 const __u8 heci_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 };
61 const __u8 heci_wd_state_independence_msg[3][4] = {
62 {0x05, 0x02, 0x51, 0x10},
63 {0x05, 0x02, 0x52, 0x10},
64 {0x07, 0x02, 0x01, 0x10}
67 const struct guid heci_asf_guid = {
68 0x75B30CD6, 0xA29E, 0x4AF7,
69 {0xA7, 0x12, 0xE6, 0x17, 0x43, 0x93, 0xC8, 0xA6}
71 const struct guid heci_wd_guid = {
72 0x05B79A6F, 0x4628, 0x4D7F,
73 {0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB}
75 const struct guid heci_pthi_guid = {
76 0x12f80028, 0xb4b7, 0x4b2d,
77 {0xac, 0xa8, 0x46, 0xe0, 0xff, 0x65, 0x81, 0x4c}
82 * heci init function prototypes
84 static void heci_check_asf_mode(struct iamt_heci_device *dev);
85 static int host_start_message(struct iamt_heci_device *dev);
86 static int host_enum_clients_message(struct iamt_heci_device *dev);
87 static int allocate_me_clients_storage(struct iamt_heci_device *dev);
88 static void host_init_wd(struct iamt_heci_device *dev);
89 static void host_init_iamthif(struct iamt_heci_device *dev);
90 static int heci_wait_event_int_timeout(struct iamt_heci_device *dev,
91 long timeout);
94 /**
95 * heci_initialize_list - Sets up a queue list.
97 * @list: An instance of our list structure
98 * @dev: Device object for our driver
100 void heci_initialize_list(struct io_heci_list *list,
101 struct iamt_heci_device *dev)
103 /* initialize our queue list */
104 INIT_LIST_HEAD(&list->heci_cb.cb_list);
105 list->status = 0;
106 list->device_extension = dev;
110 * heci_flush_queues - flush our queues list belong to file_ext.
112 * @dev: Device object for our driver
113 * @file_ext: private data of the file object
115 void heci_flush_queues(struct iamt_heci_device *dev,
116 struct heci_file_private *file_ext)
118 int i;
120 if (!dev || !file_ext)
121 return;
123 /* flush our queue list belong to file_ext */
124 for (i = 0; i < HECI_IO_LISTS_NUMBER; i++) {
125 DBG("remove list entry belong to file_ext\n");
126 heci_flush_list(dev->io_list_array[i], file_ext);
132 * heci_flush_list - remove list entry belong to file_ext.
134 * @list: An instance of our list structure
135 * @file_ext: private data of the file object
137 void heci_flush_list(struct io_heci_list *list,
138 struct heci_file_private *file_ext)
140 struct heci_file_private *file_ext_tmp;
141 struct heci_cb_private *priv_cb_pos = NULL;
142 struct heci_cb_private *priv_cb_next = NULL;
144 if (!list || !file_ext)
145 return;
147 if (list->status != 0)
148 return;
150 if (list_empty(&list->heci_cb.cb_list))
151 return;
153 list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
154 &list->heci_cb.cb_list, cb_list) {
155 if (priv_cb_pos) {
156 file_ext_tmp = (struct heci_file_private *)
157 priv_cb_pos->file_private;
158 if (file_ext_tmp) {
159 if (heci_fe_same_id(file_ext, file_ext_tmp))
160 list_del(&priv_cb_pos->cb_list);
167 * heci_reset_iamthif_params - initializes heci device iamthif
169 * @dev: The heci device structure
171 static void heci_reset_iamthif_params(struct iamt_heci_device *dev)
173 /* reset iamthif parameters. */
174 dev->iamthif_current_cb = NULL;
175 dev->iamthif_msg_buf_size = 0;
176 dev->iamthif_msg_buf_index = 0;
177 dev->iamthif_canceled = 0;
178 dev->iamthif_file_ext.file = NULL;
179 dev->iamthif_ioctl = 0;
180 dev->iamthif_state = HECI_IAMTHIF_IDLE;
181 dev->iamthif_timer = 0;
185 * init_heci_device - allocates and initializes the heci device structure
187 * @pdev: The pci device structure
189 * returns The heci_device_device pointer on success, NULL on failure.
191 struct iamt_heci_device *init_heci_device(struct pci_dev *pdev)
193 int i;
194 struct iamt_heci_device *dev;
196 dev = kzalloc(sizeof(struct iamt_heci_device), GFP_KERNEL);
197 if (!dev)
198 return NULL;
200 /* setup our list array */
201 dev->io_list_array[0] = &dev->read_list;
202 dev->io_list_array[1] = &dev->write_list;
203 dev->io_list_array[2] = &dev->write_waiting_list;
204 dev->io_list_array[3] = &dev->ctrl_wr_list;
205 dev->io_list_array[4] = &dev->ctrl_rd_list;
206 dev->io_list_array[5] = &dev->pthi_cmd_list;
207 dev->io_list_array[6] = &dev->pthi_read_complete_list;
208 INIT_LIST_HEAD(&dev->file_list);
209 INIT_LIST_HEAD(&dev->wd_file_ext.link);
210 INIT_LIST_HEAD(&dev->iamthif_file_ext.link);
211 spin_lock_init(&dev->device_lock);
212 init_waitqueue_head(&dev->wait_recvd_msg);
213 init_waitqueue_head(&dev->wait_stop_wd);
214 dev->heci_state = HECI_INITIALIZING;
215 dev->iamthif_state = HECI_IAMTHIF_IDLE;
217 /* init work for schedule work */
218 INIT_WORK(&dev->work, NULL);
219 for (i = 0; i < HECI_IO_LISTS_NUMBER; i++)
220 heci_initialize_list(dev->io_list_array[i], dev);
221 dev->pdev = pdev;
222 return dev;
228 static int heci_wait_event_int_timeout(struct iamt_heci_device *dev,
229 long timeout)
231 return wait_event_interruptible_timeout(dev->wait_recvd_msg,
232 (dev->recvd_msg), timeout);
236 * heci_hw_init - init host and fw to start work.
238 * @dev: Device object for our driver
240 * returns 0 on success, <0 on failure.
242 int heci_hw_init(struct iamt_heci_device *dev)
244 int err = 0;
246 dev->host_hw_state = read_heci_register(dev, H_CSR);
247 dev->me_hw_state = read_heci_register(dev, ME_CSR_HA);
248 DBG("host_hw_state = 0x%08x, mestate = 0x%08x.\n",
249 dev->host_hw_state, dev->me_hw_state);
251 if ((dev->host_hw_state & H_IS) == H_IS) {
252 /* acknowledge interrupt and stop interupts */
253 heci_set_csr_register(dev);
255 dev->recvd_msg = 0;
256 DBG("reset in start the heci device.\n");
258 heci_reset(dev, 1);
260 DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
261 dev->host_hw_state, dev->me_hw_state);
263 /* wait for ME to turn on ME_RDY */
264 if (!dev->recvd_msg)
265 err = heci_wait_event_int_timeout(dev, HECI_INTEROP_TIMEOUT);
267 if (!err && !dev->recvd_msg) {
268 dev->heci_state = HECI_DISABLED;
269 DBG("wait_event_interruptible_timeout failed"
270 "on wait for ME to turn on ME_RDY.\n");
271 return -ENODEV;
272 } else {
273 if (!(((dev->host_hw_state & H_RDY) == H_RDY)
274 && ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA))) {
275 dev->heci_state = HECI_DISABLED;
276 DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
277 dev->host_hw_state,
278 dev->me_hw_state);
280 if (!(dev->host_hw_state & H_RDY) != H_RDY)
281 DBG("host turn off H_RDY.\n");
283 if (!(dev->me_hw_state & ME_RDY_HRA) != ME_RDY_HRA)
284 DBG("ME turn off ME_RDY.\n");
286 printk(KERN_ERR
287 "heci: link layer initialization failed.\n");
288 return -ENODEV;
291 dev->recvd_msg = 0;
292 DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
293 dev->host_hw_state, dev->me_hw_state);
294 DBG("ME turn on ME_RDY and host turn on H_RDY.\n");
295 printk(KERN_INFO "heci: link layer has been established.\n");
296 return 0;
300 * heci_hw_reset - reset fw via heci csr register.
302 * @dev: Device object for our driver
303 * @interrupts: if interrupt should be enable after reset.
305 static void heci_hw_reset(struct iamt_heci_device *dev, int interrupts)
307 dev->host_hw_state |= (H_RST | H_IG);
309 if (interrupts)
310 heci_csr_enable_interrupts(dev);
311 else
312 heci_csr_disable_interrupts(dev);
314 BUG_ON((dev->host_hw_state & H_RST) != H_RST);
315 BUG_ON((dev->host_hw_state & H_RDY) != 0);
319 * heci_reset - reset host and fw.
321 * @dev: Device object for our driver
322 * @interrupts: if interrupt should be enable after reset.
324 void heci_reset(struct iamt_heci_device *dev, int interrupts)
326 struct heci_file_private *file_pos = NULL;
327 struct heci_file_private *file_next = NULL;
328 struct heci_cb_private *priv_cb_pos = NULL;
329 struct heci_cb_private *priv_cb_next = NULL;
330 int unexpected = 0;
332 if (dev->heci_state == HECI_RECOVERING_FROM_RESET) {
333 dev->need_reset = 1;
334 return;
337 if (dev->heci_state != HECI_INITIALIZING &&
338 dev->heci_state != HECI_DISABLED &&
339 dev->heci_state != HECI_POWER_DOWN &&
340 dev->heci_state != HECI_POWER_UP)
341 unexpected = 1;
343 if (dev->reinit_tsk != NULL) {
344 kthread_stop(dev->reinit_tsk);
345 dev->reinit_tsk = NULL;
348 dev->host_hw_state = read_heci_register(dev, H_CSR);
350 DBG("before reset host_hw_state = 0x%08x.\n",
351 dev->host_hw_state);
353 heci_hw_reset(dev, interrupts);
355 dev->host_hw_state &= ~H_RST;
356 dev->host_hw_state |= H_IG;
358 write_heci_register(dev, H_CSR, dev->host_hw_state);
360 DBG("currently saved host_hw_state = 0x%08x.\n",
361 dev->host_hw_state);
363 dev->need_reset = 0;
365 if (dev->heci_state != HECI_INITIALIZING) {
366 if ((dev->heci_state != HECI_DISABLED) &&
367 (dev->heci_state != HECI_POWER_DOWN))
368 dev->heci_state = HECI_RESETING;
370 list_for_each_entry_safe(file_pos,
371 file_next, &dev->file_list, link) {
372 file_pos->state = HECI_FILE_DISCONNECTED;
373 file_pos->flow_ctrl_creds = 0;
374 file_pos->read_cb = NULL;
375 file_pos->timer_count = 0;
377 /* remove entry if already in list */
378 DBG("list del iamthif and wd file list.\n");
379 heci_remove_client_from_file_list(dev,
380 dev->wd_file_ext.host_client_id);
382 heci_remove_client_from_file_list(dev,
383 dev->iamthif_file_ext.host_client_id);
385 heci_reset_iamthif_params(dev);
386 dev->wd_due_counter = 0;
387 dev->extra_write_index = 0;
390 dev->num_heci_me_clients = 0;
391 dev->rd_msg_hdr = 0;
392 dev->stop = 0;
393 dev->wd_pending = 0;
395 /* update the state of the registers after reset */
396 dev->host_hw_state = read_heci_register(dev, H_CSR);
397 dev->me_hw_state = read_heci_register(dev, ME_CSR_HA);
399 DBG("after reset host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
400 dev->host_hw_state, dev->me_hw_state);
402 if (unexpected)
403 printk(KERN_WARNING "heci: unexpected reset.\n");
405 /* Wake up all readings so they can be interrupted */
406 list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link) {
407 if (&file_pos->rx_wait &&
408 waitqueue_active(&file_pos->rx_wait)) {
409 printk(KERN_INFO "heci: Waking up client!\n");
410 wake_up_interruptible(&file_pos->rx_wait);
413 /* remove all waiting requests */
414 if (dev->write_list.status == 0 &&
415 !list_empty(&dev->write_list.heci_cb.cb_list)) {
416 list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
417 &dev->write_list.heci_cb.cb_list, cb_list) {
418 if (priv_cb_pos) {
419 list_del(&priv_cb_pos->cb_list);
420 heci_free_cb_private(priv_cb_pos);
427 * heci_initialize_clients - heci communication initialization.
429 * @dev: Device object for our driver
431 int heci_initialize_clients(struct iamt_heci_device *dev)
433 int status;
435 msleep(100); /* FW needs time to be ready to talk with us */
436 DBG("link is established start sending messages.\n");
437 /* link is established start sending messages. */
438 status = host_start_message(dev);
439 if (status != 0) {
440 spin_lock_bh(&dev->device_lock);
441 dev->heci_state = HECI_DISABLED;
442 spin_unlock_bh(&dev->device_lock);
443 DBG("start sending messages failed.\n");
444 return status;
447 /* enumerate clients */
448 status = host_enum_clients_message(dev);
449 if (status != 0) {
450 spin_lock_bh(&dev->device_lock);
451 dev->heci_state = HECI_DISABLED;
452 spin_unlock_bh(&dev->device_lock);
453 DBG("enum clients failed.\n");
454 return status;
456 /* allocate storage for ME clients representation */
457 status = allocate_me_clients_storage(dev);
458 if (status != 0) {
459 spin_lock_bh(&dev->device_lock);
460 dev->num_heci_me_clients = 0;
461 dev->heci_state = HECI_DISABLED;
462 spin_unlock_bh(&dev->device_lock);
463 DBG("allocate clients failed.\n");
464 return status;
467 heci_check_asf_mode(dev);
468 /*heci initialization wd */
469 host_init_wd(dev);
470 /*heci initialization iamthif client */
471 host_init_iamthif(dev);
473 spin_lock_bh(&dev->device_lock);
474 if (dev->need_reset) {
475 dev->need_reset = 0;
476 dev->heci_state = HECI_DISABLED;
477 spin_unlock_bh(&dev->device_lock);
478 return -ENODEV;
481 memset(dev->heci_host_clients, 0, sizeof(dev->heci_host_clients));
482 dev->open_handle_count = 0;
483 dev->heci_host_clients[0] |= 7;
484 dev->current_host_client_id = 3;
485 dev->heci_state = HECI_ENABLED;
486 spin_unlock_bh(&dev->device_lock);
487 DBG("initialization heci clients successful.\n");
488 return 0;
492 * heci_task_initialize_clients - heci reinitialization task
494 * @data: Device object for our driver
496 int heci_task_initialize_clients(void *data)
498 int ret;
499 struct iamt_heci_device *dev = (struct iamt_heci_device *) data;
501 spin_lock_bh(&dev->device_lock);
502 if (dev->reinit_tsk != NULL) {
503 spin_unlock_bh(&dev->device_lock);
504 DBG("reinit task already started.\n");
505 return 0;
507 dev->reinit_tsk = current;
508 current->flags |= PF_NOFREEZE;
509 spin_unlock_bh(&dev->device_lock);
511 ret = heci_initialize_clients(dev);
513 spin_lock_bh(&dev->device_lock);
514 dev->reinit_tsk = NULL;
515 spin_unlock_bh(&dev->device_lock);
517 return ret;
521 * host_start_message - heci host send start message.
523 * @dev: Device object for our driver
525 * returns 0 on success, <0 on failure.
527 static int host_start_message(struct iamt_heci_device *dev)
529 long timeout = 60; /* 60 second */
531 struct heci_msg_hdr *heci_hdr;
532 struct hbm_host_version_request *host_start_req;
533 struct hbm_host_stop_request *host_stop_req;
534 int err = 0;
536 /* host start message */
537 heci_hdr = (struct heci_msg_hdr *) &dev->wr_msg_buf[0];
538 heci_hdr->host_addr = 0;
539 heci_hdr->me_addr = 0;
540 heci_hdr->length = sizeof(struct hbm_host_version_request);
541 heci_hdr->msg_complete = 1;
542 heci_hdr->reserved = 0;
544 host_start_req =
545 (struct hbm_host_version_request *) &dev->wr_msg_buf[1];
546 memset(host_start_req, 0, sizeof(struct hbm_host_version_request));
547 host_start_req->cmd.cmd = HOST_START_REQ_CMD;
548 host_start_req->host_version.major_version = HBM_MAJOR_VERSION;
549 host_start_req->host_version.minor_version = HBM_MINOR_VERSION;
550 dev->recvd_msg = 0;
551 if (!heci_write_message(dev, heci_hdr,
552 (unsigned char *) (host_start_req),
553 heci_hdr->length)) {
554 DBG("send version to fw fail.\n");
555 return -ENODEV;
557 DBG("call wait_event_interruptible_timeout for response message.\n");
558 /* wait for response */
559 err = heci_wait_event_int_timeout(dev, timeout * HZ);
560 if (!err && !dev->recvd_msg) {
561 DBG("wait_timeout failed on host start response message.\n");
562 return -ENODEV;
564 dev->recvd_msg = 0;
565 DBG("wait_timeout successful on host start response message.\n");
566 if ((dev->version.major_version != HBM_MAJOR_VERSION) ||
567 (dev->version.minor_version != HBM_MINOR_VERSION)) {
568 /* send stop message */
569 heci_hdr->host_addr = 0;
570 heci_hdr->me_addr = 0;
571 heci_hdr->length = sizeof(struct hbm_host_stop_request);
572 heci_hdr->msg_complete = 1;
573 heci_hdr->reserved = 0;
575 host_stop_req =
576 (struct hbm_host_stop_request *) &dev->wr_msg_buf[1];
578 memset(host_stop_req, 0, sizeof(struct hbm_host_stop_request));
579 host_stop_req->cmd.cmd = HOST_STOP_REQ_CMD;
580 host_stop_req->reason = DRIVER_STOP_REQUEST;
581 heci_write_message(dev, heci_hdr,
582 (unsigned char *) (host_stop_req),
583 heci_hdr->length);
584 DBG("version mismatch.\n");
585 return -ENODEV;
588 return 0;
592 * host_enum_clients_message - host send enumeration client request message.
594 * @dev: Device object for our driver
596 * returns 0 on success, <0 on failure.
598 static int host_enum_clients_message(struct iamt_heci_device *dev)
600 long timeout = 5; /*5 second */
601 struct heci_msg_hdr *heci_hdr;
602 struct hbm_host_enum_request *host_enum_req;
603 int err = 0;
604 int i, j;
606 heci_hdr = (struct heci_msg_hdr *) &dev->wr_msg_buf[0];
607 /* enumerate clients */
608 heci_hdr->host_addr = 0;
609 heci_hdr->me_addr = 0;
610 heci_hdr->length = sizeof(struct hbm_host_enum_request);
611 heci_hdr->msg_complete = 1;
612 heci_hdr->reserved = 0;
614 host_enum_req = (struct hbm_host_enum_request *) &dev->wr_msg_buf[1];
615 memset(host_enum_req, 0, sizeof(struct hbm_host_enum_request));
616 host_enum_req->cmd.cmd = HOST_ENUM_REQ_CMD;
617 if (!heci_write_message(dev, heci_hdr,
618 (unsigned char *) (host_enum_req),
619 heci_hdr->length)) {
620 DBG("send enumeration request failed.\n");
621 return -ENODEV;
623 /* wait for response */
624 dev->recvd_msg = 0;
625 err = heci_wait_event_int_timeout(dev, timeout * HZ);
626 if (!err && !dev->recvd_msg) {
627 DBG("wait_event_interruptible_timeout failed "
628 "on enumeration clients response message.\n");
629 return -ENODEV;
631 dev->recvd_msg = 0;
633 spin_lock_bh(&dev->device_lock);
634 /* count how many ME clients we have */
635 for (i = 0; i < sizeof(dev->heci_me_clients); i++) {
636 for (j = 0; j < 8; j++) {
637 if ((dev->heci_me_clients[i] & (1 << j)) != 0)
638 dev->num_heci_me_clients++;
642 spin_unlock_bh(&dev->device_lock);
644 return 0;
648 * host_client_properties - reads properties for client
650 * @dev: Device object for our driver
651 * @idx: client index in me client array
652 * @client_id: id of the client
654 * returns 0 on success, <0 on failure.
656 static int host_client_properties(struct iamt_heci_device *dev,
657 struct heci_me_client *client)
659 struct heci_msg_hdr *heci_hdr;
660 struct hbm_props_request *host_cli_req;
661 int err;
663 heci_hdr = (struct heci_msg_hdr *) &dev->wr_msg_buf[0];
664 heci_hdr->host_addr = 0;
665 heci_hdr->me_addr = 0;
666 heci_hdr->length = sizeof(struct hbm_props_request);
667 heci_hdr->msg_complete = 1;
668 heci_hdr->reserved = 0;
670 host_cli_req = (struct hbm_props_request *) &dev->wr_msg_buf[1];
671 memset(host_cli_req, 0, sizeof(struct hbm_props_request));
672 host_cli_req->cmd.cmd = HOST_CLIENT_PROPERTEIS_REQ_CMD;
673 host_cli_req->address = client->client_id;
674 if (!heci_write_message(dev, heci_hdr,
675 (unsigned char *) (host_cli_req),
676 heci_hdr->length)) {
677 DBG("send props request failed.\n");
678 return -ENODEV;
680 /* wait for response */
681 dev->recvd_msg = 0;
682 err = heci_wait_event_int_timeout(dev, 10 * HZ);
683 if (!err && !dev->recvd_msg) {
684 DBG("wait failed on props resp msg.\n");
685 return -ENODEV;
687 dev->recvd_msg = 0;
688 return 0;
692 * allocate_me_clients_storage - allocate storage for me clients
694 * @dev: Device object for our driver
696 * returns 0 on success, <0 on failure.
698 static int allocate_me_clients_storage(struct iamt_heci_device *dev)
700 struct heci_me_client *clients;
701 struct heci_me_client *client;
702 __u8 num, i, j;
703 int err;
705 if (dev->num_heci_me_clients <= 0)
706 return 0;
708 spin_lock_bh(&dev->device_lock);
709 kfree(dev->me_clients);
710 dev->me_clients = NULL;
711 spin_unlock_bh(&dev->device_lock);
713 /* allocate storage for ME clients representation */
714 clients = kcalloc(dev->num_heci_me_clients,
715 sizeof(struct heci_me_client), GFP_KERNEL);
716 if (!clients) {
717 DBG("memory allocation for ME clients failed.\n");
718 return -ENOMEM;
721 spin_lock_bh(&dev->device_lock);
722 dev->me_clients = clients;
723 spin_unlock_bh(&dev->device_lock);
725 num = 0;
726 for (i = 0; i < sizeof(dev->heci_me_clients); i++) {
727 for (j = 0; j < 8; j++) {
728 if ((dev->heci_me_clients[i] & (1 << j)) != 0) {
729 client = &dev->me_clients[num];
730 client->client_id = (i * 8) + j;
731 client->flow_ctrl_creds = 0;
732 err = host_client_properties(dev, client);
733 if (err != 0) {
734 spin_lock_bh(&dev->device_lock);
735 kfree(dev->me_clients);
736 dev->me_clients = NULL;
737 spin_unlock_bh(&dev->device_lock);
738 return err;
740 num++;
745 return 0;
749 * heci_init_file_private - initializes private file structure.
751 * @priv: private file structure to be initialized
752 * @file: the file structure
754 static void heci_init_file_private(struct heci_file_private *priv,
755 struct file *file)
757 memset(priv, 0, sizeof(struct heci_file_private));
758 spin_lock_init(&priv->file_lock);
759 spin_lock_init(&priv->read_io_lock);
760 spin_lock_init(&priv->write_io_lock);
761 init_waitqueue_head(&priv->wait);
762 init_waitqueue_head(&priv->rx_wait);
763 DBG("priv->rx_wait =%p\n", &priv->rx_wait);
764 init_waitqueue_head(&priv->tx_wait);
765 INIT_LIST_HEAD(&priv->link);
766 priv->reading_state = HECI_IDLE;
767 priv->writing_state = HECI_IDLE;
771 * heci_find_me_client - search for ME client guid
772 * sets client_id in heci_file_private if found
773 * @dev: Device object for our driver
774 * @priv: private file structure to set client_id in
775 * @cguid: searched guid of ME client
776 * @client_id: id of host client to be set in file private structure
778 * returns ME client index
780 static __u8 heci_find_me_client(struct iamt_heci_device *dev,
781 struct heci_file_private *priv,
782 const struct guid *cguid, __u8 client_id)
784 __u8 i;
786 if ((dev == NULL) || (priv == NULL) || (cguid == NULL))
787 return 0;
789 for (i = 0; i < dev->num_heci_me_clients; i++) {
790 if (memcmp(cguid,
791 &dev->me_clients[i].props.protocol_name,
792 sizeof(struct guid)) == 0) {
793 priv->me_client_id = dev->me_clients[i].client_id;
794 priv->state = HECI_FILE_CONNECTING;
795 priv->host_client_id = client_id;
797 list_add_tail(&priv->link, &dev->file_list);
798 return i;
801 return 0;
805 * heci_check_asf_mode - check for ASF client
807 * @dev: Device object for our driver
809 static void heci_check_asf_mode(struct iamt_heci_device *dev)
811 __u8 i;
813 spin_lock_bh(&dev->device_lock);
814 dev->asf_mode = 0;
815 /* find ME ASF client - otherwise assume AMT mode */
816 DBG("find ME ASF client - otherwise assume AMT mode.\n");
817 for (i = 0; i < dev->num_heci_me_clients; i++) {
818 if (memcmp(&heci_asf_guid,
819 &dev->me_clients[i].props.protocol_name,
820 sizeof(struct guid)) == 0) {
821 dev->asf_mode = 1;
822 spin_unlock_bh(&dev->device_lock);
823 DBG("found ME ASF client.\n");
824 return;
827 spin_unlock_bh(&dev->device_lock);
828 DBG("assume AMT mode.\n");
832 * heci_connect_me_client - connect ME client
833 * @dev: Device object for our driver
834 * @priv: private file structure
835 * @timeout: connect timeout in seconds
837 * returns 1 - if connected, 0 - if not
839 static __u8 heci_connect_me_client(struct iamt_heci_device *dev,
840 struct heci_file_private *priv,
841 long timeout)
843 int err = 0;
845 if ((dev == NULL) || (priv == NULL))
846 return 0;
848 if (!heci_connect(dev, priv)) {
849 DBG("failed to call heci_connect for client_id=%d.\n",
850 priv->host_client_id);
851 spin_lock_bh(&dev->device_lock);
852 heci_remove_client_from_file_list(dev, priv->host_client_id);
853 priv->state = HECI_FILE_DISCONNECTED;
854 spin_unlock_bh(&dev->device_lock);
855 return 0;
858 err = wait_event_timeout(dev->wait_recvd_msg,
859 (HECI_FILE_CONNECTED == priv->state ||
860 HECI_FILE_DISCONNECTED == priv->state),
861 timeout * HZ);
862 if (HECI_FILE_CONNECTED != priv->state) {
863 spin_lock_bh(&dev->device_lock);
864 heci_remove_client_from_file_list(dev, priv->host_client_id);
865 DBG("failed to connect client_id=%d state=%d.\n",
866 priv->host_client_id, priv->state);
867 if (err)
868 DBG("failed connect err=%08x\n", err);
869 priv->state = HECI_FILE_DISCONNECTED;
870 spin_unlock_bh(&dev->device_lock);
871 return 0;
873 DBG("successfully connected client_id=%d.\n",
874 priv->host_client_id);
875 return 1;
879 * host_init_wd - heci initialization wd.
881 * @dev: Device object for our driver
883 static void host_init_wd(struct iamt_heci_device *dev)
885 spin_lock_bh(&dev->device_lock);
887 heci_init_file_private(&dev->wd_file_ext, NULL);
889 /* look for WD client and connect to it */
890 dev->wd_file_ext.state = HECI_FILE_DISCONNECTED;
891 dev->wd_timeout = 0;
893 if (dev->asf_mode) {
894 memcpy(dev->wd_data, heci_stop_wd_params, HECI_WD_PARAMS_SIZE);
895 } else {
896 /* AMT mode */
897 dev->wd_timeout = AMT_WD_VALUE;
898 DBG("dev->wd_timeout=%d.\n", dev->wd_timeout);
899 memcpy(dev->wd_data, heci_start_wd_params, HECI_WD_PARAMS_SIZE);
900 memcpy(dev->wd_data + HECI_WD_PARAMS_SIZE,
901 &dev->wd_timeout, sizeof(__u16));
904 /* find ME WD client */
905 heci_find_me_client(dev, &dev->wd_file_ext,
906 &heci_wd_guid, HECI_WD_HOST_CLIENT_ID);
907 spin_unlock_bh(&dev->device_lock);
909 DBG("check wd_file_ext\n");
910 if (HECI_FILE_CONNECTING == dev->wd_file_ext.state) {
911 if (heci_connect_me_client(dev, &dev->wd_file_ext, 15) == 1) {
912 DBG("dev->wd_timeout=%d.\n", dev->wd_timeout);
913 if (dev->wd_timeout != 0)
914 dev->wd_due_counter = 1;
915 else
916 dev->wd_due_counter = 0;
917 DBG("successfully connected to WD client.\n");
919 } else
920 DBG("failed to find WD client.\n");
923 spin_lock_bh(&dev->device_lock);
924 dev->wd_timer.function = &heci_wd_timer;
925 dev->wd_timer.data = (unsigned long) dev;
926 spin_unlock_bh(&dev->device_lock);
931 * host_init_iamthif - heci initialization iamthif client.
933 * @dev: Device object for our driver
936 static void host_init_iamthif(struct iamt_heci_device *dev)
938 __u8 i;
940 spin_lock_bh(&dev->device_lock);
942 heci_init_file_private(&dev->iamthif_file_ext, NULL);
943 dev->iamthif_file_ext.state = HECI_FILE_DISCONNECTED;
945 /* find ME PTHI client */
946 i = heci_find_me_client(dev, &dev->iamthif_file_ext,
947 &heci_pthi_guid, HECI_IAMTHIF_HOST_CLIENT_ID);
948 if (dev->iamthif_file_ext.state != HECI_FILE_CONNECTING) {
949 DBG("failed to find iamthif client.\n");
950 spin_unlock_bh(&dev->device_lock);
951 return;
954 BUG_ON(dev->me_clients[i].props.max_msg_length != IAMTHIF_MTU);
956 spin_unlock_bh(&dev->device_lock);
957 if (heci_connect_me_client(dev, &dev->iamthif_file_ext, 15) == 1) {
958 DBG("connected to iamthif client.\n");
959 dev->iamthif_state = HECI_IAMTHIF_IDLE;
964 * heci_alloc_file_private - allocates a private file structure and set it up.
965 * @file: the file structure
967 * returns The allocated file or NULL on failure
969 struct heci_file_private *heci_alloc_file_private(struct file *file)
971 struct heci_file_private *priv;
973 priv = kmalloc(sizeof(struct heci_file_private), GFP_KERNEL);
974 if (!priv)
975 return NULL;
977 heci_init_file_private(priv, file);
979 return priv;
985 * heci_disconnect_host_client - send disconnect message to fw from host client.
987 * @dev: Device object for our driver
988 * @file_ext: private data of the file object
990 * returns 0 on success, <0 on failure.
992 int heci_disconnect_host_client(struct iamt_heci_device *dev,
993 struct heci_file_private *file_ext)
995 int rets, err;
996 long timeout = 15; /* 15 seconds */
997 struct heci_cb_private *priv_cb;
999 if ((!dev) || (!file_ext))
1000 return -ENODEV;
1002 if (file_ext->state != HECI_FILE_DISCONNECTING)
1003 return 0;
1005 priv_cb = kzalloc(sizeof(struct heci_cb_private), GFP_KERNEL);
1006 if (!priv_cb)
1007 return -ENOMEM;
1009 INIT_LIST_HEAD(&priv_cb->cb_list);
1010 priv_cb->file_private = file_ext;
1011 priv_cb->major_file_operations = HECI_CLOSE;
1012 spin_lock_bh(&dev->device_lock);
1013 if (dev->host_buffer_is_empty) {
1014 dev->host_buffer_is_empty = 0;
1015 if (heci_disconnect(dev, file_ext)) {
1016 list_add_tail(&priv_cb->cb_list,
1017 &dev->ctrl_rd_list.heci_cb.cb_list);
1018 } else {
1019 spin_unlock_bh(&dev->device_lock);
1020 rets = -ENODEV;
1021 DBG("failed to call heci_disconnect.\n");
1022 goto free;
1024 } else {
1025 DBG("add disconnect cb to control write list\n");
1026 list_add_tail(&priv_cb->cb_list,
1027 &dev->ctrl_wr_list.heci_cb.cb_list);
1029 spin_unlock_bh(&dev->device_lock);
1031 err = wait_event_timeout(dev->wait_recvd_msg,
1032 (HECI_FILE_DISCONNECTED == file_ext->state),
1033 timeout * HZ);
1034 if (HECI_FILE_DISCONNECTED == file_ext->state) {
1035 rets = 0;
1036 DBG("successfully disconnected from fw client.\n");
1037 } else {
1038 rets = -ENODEV;
1039 if (HECI_FILE_DISCONNECTED != file_ext->state)
1040 DBG("wrong status client disconnect.\n");
1042 if (err)
1043 DBG("wait failed disconnect err=%08x\n", err);
1045 DBG("failed to disconnect from fw client.\n");
1048 spin_lock_bh(&dev->device_lock);
1049 heci_flush_list(&dev->ctrl_rd_list, file_ext);
1050 heci_flush_list(&dev->ctrl_wr_list, file_ext);
1051 spin_unlock_bh(&dev->device_lock);
1052 free:
1053 heci_free_cb_private(priv_cb);
1054 return rets;
1058 * heci_remove_client_from_file_list -
1059 * remove file private data from device file list
1061 * @dev: Device object for our driver
1062 * @host_client_id: host client id to be removed
1064 void heci_remove_client_from_file_list(struct iamt_heci_device *dev,
1065 __u8 host_client_id)
1067 struct heci_file_private *file_pos = NULL;
1068 struct heci_file_private *file_next = NULL;
1069 list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link) {
1070 if (host_client_id == file_pos->host_client_id) {
1071 DBG("remove host client = %d, ME client = %d\n",
1072 file_pos->host_client_id,
1073 file_pos->me_client_id);
1074 list_del_init(&file_pos->link);
1075 break;