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