| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 1 | /* | 
 | 2 |  * | 
 | 3 |  * Intel Management Engine Interface (Intel MEI) Linux driver | 
| Tomas Winkler | 733ba91 | 2012-02-09 19:25:53 +0200 | [diff] [blame] | 4 |  * Copyright (c) 2003-2012, Intel Corporation. | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 5 |  * | 
 | 6 |  * This program is free software; you can redistribute it and/or modify it | 
 | 7 |  * under the terms and conditions of the GNU General Public License, | 
 | 8 |  * version 2, as published by the Free Software Foundation. | 
 | 9 |  * | 
 | 10 |  * This program is distributed in the hope it will be useful, but WITHOUT | 
 | 11 |  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
 | 12 |  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | 
 | 13 |  * more details. | 
 | 14 |  * | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | #include <linux/pci.h> | 
 | 18 | #include <linux/sched.h> | 
 | 19 | #include <linux/wait.h> | 
 | 20 | #include <linux/delay.h> | 
 | 21 |  | 
 | 22 | #include "mei_dev.h" | 
 | 23 | #include "hw.h" | 
 | 24 | #include "interface.h" | 
| Tomas Winkler | 4f3afe1 | 2012-05-09 16:38:59 +0300 | [diff] [blame] | 25 | #include <linux/mei.h> | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 26 |  | 
 | 27 | const uuid_le mei_amthi_guid  = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, 0xac, | 
 | 28 | 						0xa8, 0x46, 0xe0, 0xff, 0x65, | 
 | 29 | 						0x81, 0x4c); | 
 | 30 |  | 
 | 31 | /** | 
| Tomas Winkler | 0288c7c | 2011-06-06 10:44:34 +0300 | [diff] [blame] | 32 |  * mei_io_list_init - Sets up a queue list. | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 33 |  * | 
| Tomas Winkler | 0288c7c | 2011-06-06 10:44:34 +0300 | [diff] [blame] | 34 |  * @list: An instance io list structure | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 35 |  * @dev: the device structure | 
 | 36 |  */ | 
| Tomas Winkler | 0288c7c | 2011-06-06 10:44:34 +0300 | [diff] [blame] | 37 | void mei_io_list_init(struct mei_io_list *list) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 38 | { | 
 | 39 | 	/* initialize our queue list */ | 
 | 40 | 	INIT_LIST_HEAD(&list->mei_cb.cb_list); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 41 | } | 
 | 42 |  | 
 | 43 | /** | 
| Tomas Winkler | 0288c7c | 2011-06-06 10:44:34 +0300 | [diff] [blame] | 44 |  * mei_io_list_flush - removes list entry belonging to cl. | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 45 |  * | 
 | 46 |  * @list:  An instance of our list structure | 
 | 47 |  * @cl: private data of the file object | 
 | 48 |  */ | 
| Tomas Winkler | 0288c7c | 2011-06-06 10:44:34 +0300 | [diff] [blame] | 49 | void mei_io_list_flush(struct mei_io_list *list, struct mei_cl *cl) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 50 | { | 
| Tomas Winkler | 3a5352f | 2011-12-04 16:16:27 +0200 | [diff] [blame] | 51 | 	struct mei_cl_cb *pos; | 
 | 52 | 	struct mei_cl_cb *next; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 53 |  | 
| Tomas Winkler | b7cd2d9 | 2011-11-27 21:43:34 +0200 | [diff] [blame] | 54 | 	list_for_each_entry_safe(pos, next, &list->mei_cb.cb_list, cb_list) { | 
| Tomas Winkler | 3a5352f | 2011-12-04 16:16:27 +0200 | [diff] [blame] | 55 | 		if (pos->file_private) { | 
| Tomas Winkler | 0288c7c | 2011-06-06 10:44:34 +0300 | [diff] [blame] | 56 | 			struct mei_cl *cl_tmp; | 
| Tomas Winkler | b7cd2d9 | 2011-11-27 21:43:34 +0200 | [diff] [blame] | 57 | 			cl_tmp = (struct mei_cl *)pos->file_private; | 
| Tomas Winkler | 0288c7c | 2011-06-06 10:44:34 +0300 | [diff] [blame] | 58 | 			if (mei_cl_cmp_id(cl, cl_tmp)) | 
| Tomas Winkler | b7cd2d9 | 2011-11-27 21:43:34 +0200 | [diff] [blame] | 59 | 				list_del(&pos->cb_list); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 60 | 		} | 
 | 61 | 	} | 
 | 62 | } | 
| Tomas Winkler | 0288c7c | 2011-06-06 10:44:34 +0300 | [diff] [blame] | 63 | /** | 
 | 64 |  * mei_cl_flush_queues - flushes queue lists belonging to cl. | 
 | 65 |  * | 
 | 66 |  * @dev: the device structure | 
 | 67 |  * @cl: private data of the file object | 
 | 68 |  */ | 
 | 69 | int mei_cl_flush_queues(struct mei_cl *cl) | 
 | 70 | { | 
 | 71 | 	if (!cl || !cl->dev) | 
 | 72 | 		return -EINVAL; | 
 | 73 |  | 
 | 74 | 	dev_dbg(&cl->dev->pdev->dev, "remove list entry belonging to cl\n"); | 
 | 75 | 	mei_io_list_flush(&cl->dev->read_list, cl); | 
 | 76 | 	mei_io_list_flush(&cl->dev->write_list, cl); | 
 | 77 | 	mei_io_list_flush(&cl->dev->write_waiting_list, cl); | 
 | 78 | 	mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); | 
 | 79 | 	mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); | 
 | 80 | 	mei_io_list_flush(&cl->dev->amthi_cmd_list, cl); | 
 | 81 | 	mei_io_list_flush(&cl->dev->amthi_read_complete_list, cl); | 
 | 82 | 	return 0; | 
 | 83 | } | 
 | 84 |  | 
 | 85 |  | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 86 |  | 
 | 87 | /** | 
 | 88 |  * mei_reset_iamthif_params - initializes mei device iamthif | 
 | 89 |  * | 
 | 90 |  * @dev: the device structure | 
 | 91 |  */ | 
 | 92 | static void mei_reset_iamthif_params(struct mei_device *dev) | 
 | 93 | { | 
 | 94 | 	/* reset iamthif parameters. */ | 
 | 95 | 	dev->iamthif_current_cb = NULL; | 
 | 96 | 	dev->iamthif_msg_buf_size = 0; | 
 | 97 | 	dev->iamthif_msg_buf_index = 0; | 
| Tomas Winkler | eb9af0a | 2011-05-25 17:28:22 +0300 | [diff] [blame] | 98 | 	dev->iamthif_canceled = false; | 
 | 99 | 	dev->iamthif_ioctl = false; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 100 | 	dev->iamthif_state = MEI_IAMTHIF_IDLE; | 
 | 101 | 	dev->iamthif_timer = 0; | 
 | 102 | } | 
 | 103 |  | 
 | 104 | /** | 
 | 105 |  * init_mei_device - allocates and initializes the mei device structure | 
 | 106 |  * | 
 | 107 |  * @pdev: The pci device structure | 
 | 108 |  * | 
 | 109 |  * returns The mei_device_device pointer on success, NULL on failure. | 
 | 110 |  */ | 
| Tomas Winkler | c95efb7 | 2011-05-25 17:28:21 +0300 | [diff] [blame] | 111 | struct mei_device *mei_device_init(struct pci_dev *pdev) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 112 | { | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 113 | 	struct mei_device *dev; | 
 | 114 |  | 
 | 115 | 	dev = kzalloc(sizeof(struct mei_device), GFP_KERNEL); | 
 | 116 | 	if (!dev) | 
 | 117 | 		return NULL; | 
 | 118 |  | 
 | 119 | 	/* setup our list array */ | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 120 | 	INIT_LIST_HEAD(&dev->file_list); | 
 | 121 | 	INIT_LIST_HEAD(&dev->wd_cl.link); | 
 | 122 | 	INIT_LIST_HEAD(&dev->iamthif_cl.link); | 
 | 123 | 	mutex_init(&dev->device_lock); | 
 | 124 | 	init_waitqueue_head(&dev->wait_recvd_msg); | 
 | 125 | 	init_waitqueue_head(&dev->wait_stop_wd); | 
 | 126 | 	dev->mei_state = MEI_INITIALIZING; | 
 | 127 | 	dev->iamthif_state = MEI_IAMTHIF_IDLE; | 
| Oren Weil | 9ce178e | 2011-09-07 09:03:09 +0300 | [diff] [blame] | 128 | 	dev->wd_interface_reg = false; | 
| Tomas Winkler | 0288c7c | 2011-06-06 10:44:34 +0300 | [diff] [blame] | 129 |  | 
 | 130 |  | 
 | 131 | 	mei_io_list_init(&dev->read_list); | 
 | 132 | 	mei_io_list_init(&dev->write_list); | 
 | 133 | 	mei_io_list_init(&dev->write_waiting_list); | 
 | 134 | 	mei_io_list_init(&dev->ctrl_wr_list); | 
 | 135 | 	mei_io_list_init(&dev->ctrl_rd_list); | 
 | 136 | 	mei_io_list_init(&dev->amthi_cmd_list); | 
 | 137 | 	mei_io_list_init(&dev->amthi_read_complete_list); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 138 | 	dev->pdev = pdev; | 
 | 139 | 	return dev; | 
 | 140 | } | 
 | 141 |  | 
 | 142 | /** | 
 | 143 |  * mei_hw_init - initializes host and fw to start work. | 
 | 144 |  * | 
 | 145 |  * @dev: the device structure | 
 | 146 |  * | 
 | 147 |  * returns 0 on success, <0 on failure. | 
 | 148 |  */ | 
 | 149 | int mei_hw_init(struct mei_device *dev) | 
 | 150 | { | 
 | 151 | 	int err = 0; | 
 | 152 | 	int ret; | 
 | 153 |  | 
 | 154 | 	mutex_lock(&dev->device_lock); | 
 | 155 |  | 
 | 156 | 	dev->host_hw_state = mei_hcsr_read(dev); | 
 | 157 | 	dev->me_hw_state = mei_mecsr_read(dev); | 
 | 158 | 	dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, mestate = 0x%08x.\n", | 
 | 159 | 	    dev->host_hw_state, dev->me_hw_state); | 
 | 160 |  | 
 | 161 | 	/* acknowledge interrupt and stop interupts */ | 
 | 162 | 	if ((dev->host_hw_state & H_IS) == H_IS) | 
 | 163 | 		mei_reg_write(dev, H_CSR, dev->host_hw_state); | 
 | 164 |  | 
| Tomas Winkler | eb9af0a | 2011-05-25 17:28:22 +0300 | [diff] [blame] | 165 | 	dev->recvd_msg = false; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 166 | 	dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n"); | 
 | 167 |  | 
 | 168 | 	mei_reset(dev, 1); | 
 | 169 |  | 
 | 170 | 	dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n", | 
 | 171 | 	    dev->host_hw_state, dev->me_hw_state); | 
 | 172 |  | 
 | 173 | 	/* wait for ME to turn on ME_RDY */ | 
 | 174 | 	if (!dev->recvd_msg) { | 
 | 175 | 		mutex_unlock(&dev->device_lock); | 
 | 176 | 		err = wait_event_interruptible_timeout(dev->wait_recvd_msg, | 
 | 177 | 			dev->recvd_msg, MEI_INTEROP_TIMEOUT); | 
 | 178 | 		mutex_lock(&dev->device_lock); | 
 | 179 | 	} | 
 | 180 |  | 
| Tomas Winkler | a534bb6 | 2011-06-13 16:39:31 +0300 | [diff] [blame] | 181 | 	if (err <= 0 && !dev->recvd_msg) { | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 182 | 		dev->mei_state = MEI_DISABLED; | 
 | 183 | 		dev_dbg(&dev->pdev->dev, | 
 | 184 | 			"wait_event_interruptible_timeout failed" | 
 | 185 | 			"on wait for ME to turn on ME_RDY.\n"); | 
 | 186 | 		ret = -ENODEV; | 
 | 187 | 		goto out; | 
 | 188 | 	} | 
 | 189 |  | 
 | 190 | 	if (!(((dev->host_hw_state & H_RDY) == H_RDY) && | 
 | 191 | 	      ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA))) { | 
 | 192 | 		dev->mei_state = MEI_DISABLED; | 
 | 193 | 		dev_dbg(&dev->pdev->dev, | 
 | 194 | 			"host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n", | 
 | 195 | 			dev->host_hw_state, dev->me_hw_state); | 
 | 196 |  | 
| Dan Carpenter | 8eb73c6 | 2011-06-09 10:18:23 +0300 | [diff] [blame] | 197 | 		if (!(dev->host_hw_state & H_RDY)) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 198 | 			dev_dbg(&dev->pdev->dev, "host turn off H_RDY.\n"); | 
 | 199 |  | 
| Dan Carpenter | 8eb73c6 | 2011-06-09 10:18:23 +0300 | [diff] [blame] | 200 | 		if (!(dev->me_hw_state & ME_RDY_HRA)) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 201 | 			dev_dbg(&dev->pdev->dev, "ME turn off ME_RDY.\n"); | 
 | 202 |  | 
| Tomas Winkler | d39a464 | 2012-03-19 17:58:42 +0200 | [diff] [blame] | 203 | 		dev_err(&dev->pdev->dev, "link layer initialization failed.\n"); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 204 | 		ret = -ENODEV; | 
 | 205 | 		goto out; | 
 | 206 | 	} | 
 | 207 |  | 
 | 208 | 	if (dev->version.major_version != HBM_MAJOR_VERSION || | 
 | 209 | 	    dev->version.minor_version != HBM_MINOR_VERSION) { | 
 | 210 | 		dev_dbg(&dev->pdev->dev, "MEI start failed.\n"); | 
 | 211 | 		ret = -ENODEV; | 
 | 212 | 		goto out; | 
 | 213 | 	} | 
 | 214 |  | 
| Tomas Winkler | eb9af0a | 2011-05-25 17:28:22 +0300 | [diff] [blame] | 215 | 	dev->recvd_msg = false; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 216 | 	dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n", | 
 | 217 | 	    dev->host_hw_state, dev->me_hw_state); | 
 | 218 | 	dev_dbg(&dev->pdev->dev, "ME turn on ME_RDY and host turn on H_RDY.\n"); | 
 | 219 | 	dev_dbg(&dev->pdev->dev, "link layer has been established.\n"); | 
 | 220 | 	dev_dbg(&dev->pdev->dev, "MEI  start success.\n"); | 
 | 221 | 	ret = 0; | 
 | 222 |  | 
 | 223 | out: | 
 | 224 | 	mutex_unlock(&dev->device_lock); | 
 | 225 | 	return ret; | 
 | 226 | } | 
 | 227 |  | 
 | 228 | /** | 
 | 229 |  * mei_hw_reset - resets fw via mei csr register. | 
 | 230 |  * | 
 | 231 |  * @dev: the device structure | 
 | 232 |  * @interrupts_enabled: if interrupt should be enabled after reset. | 
 | 233 |  */ | 
 | 234 | static void mei_hw_reset(struct mei_device *dev, int interrupts_enabled) | 
 | 235 | { | 
 | 236 | 	dev->host_hw_state |= (H_RST | H_IG); | 
 | 237 |  | 
 | 238 | 	if (interrupts_enabled) | 
 | 239 | 		mei_enable_interrupts(dev); | 
 | 240 | 	else | 
 | 241 | 		mei_disable_interrupts(dev); | 
 | 242 | } | 
 | 243 |  | 
 | 244 | /** | 
 | 245 |  * mei_reset - resets host and fw. | 
 | 246 |  * | 
 | 247 |  * @dev: the device structure | 
 | 248 |  * @interrupts_enabled: if interrupt should be enabled after reset. | 
 | 249 |  */ | 
 | 250 | void mei_reset(struct mei_device *dev, int interrupts_enabled) | 
 | 251 | { | 
 | 252 | 	struct mei_cl *cl_pos = NULL; | 
 | 253 | 	struct mei_cl *cl_next = NULL; | 
 | 254 | 	struct mei_cl_cb *cb_pos = NULL; | 
 | 255 | 	struct mei_cl_cb *cb_next = NULL; | 
 | 256 | 	bool unexpected; | 
 | 257 |  | 
 | 258 | 	if (dev->mei_state == MEI_RECOVERING_FROM_RESET) { | 
| Tomas Winkler | eb9af0a | 2011-05-25 17:28:22 +0300 | [diff] [blame] | 259 | 		dev->need_reset = true; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 260 | 		return; | 
 | 261 | 	} | 
 | 262 |  | 
 | 263 | 	unexpected = (dev->mei_state != MEI_INITIALIZING && | 
 | 264 | 			dev->mei_state != MEI_DISABLED && | 
 | 265 | 			dev->mei_state != MEI_POWER_DOWN && | 
 | 266 | 			dev->mei_state != MEI_POWER_UP); | 
 | 267 |  | 
 | 268 | 	dev->host_hw_state = mei_hcsr_read(dev); | 
 | 269 |  | 
 | 270 | 	dev_dbg(&dev->pdev->dev, "before reset host_hw_state = 0x%08x.\n", | 
 | 271 | 	    dev->host_hw_state); | 
 | 272 |  | 
 | 273 | 	mei_hw_reset(dev, interrupts_enabled); | 
 | 274 |  | 
 | 275 | 	dev->host_hw_state &= ~H_RST; | 
 | 276 | 	dev->host_hw_state |= H_IG; | 
 | 277 |  | 
 | 278 | 	mei_hcsr_set(dev); | 
 | 279 |  | 
 | 280 | 	dev_dbg(&dev->pdev->dev, "currently saved host_hw_state = 0x%08x.\n", | 
 | 281 | 	    dev->host_hw_state); | 
 | 282 |  | 
| Tomas Winkler | eb9af0a | 2011-05-25 17:28:22 +0300 | [diff] [blame] | 283 | 	dev->need_reset = false; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 284 |  | 
 | 285 | 	if (dev->mei_state != MEI_INITIALIZING) { | 
 | 286 | 		if (dev->mei_state != MEI_DISABLED && | 
 | 287 | 		    dev->mei_state != MEI_POWER_DOWN) | 
 | 288 | 			dev->mei_state = MEI_RESETING; | 
 | 289 |  | 
 | 290 | 		list_for_each_entry_safe(cl_pos, | 
 | 291 | 				cl_next, &dev->file_list, link) { | 
 | 292 | 			cl_pos->state = MEI_FILE_DISCONNECTED; | 
 | 293 | 			cl_pos->mei_flow_ctrl_creds = 0; | 
 | 294 | 			cl_pos->read_cb = NULL; | 
 | 295 | 			cl_pos->timer_count = 0; | 
 | 296 | 		} | 
 | 297 | 		/* remove entry if already in list */ | 
 | 298 | 		dev_dbg(&dev->pdev->dev, "list del iamthif and wd file list.\n"); | 
 | 299 | 		mei_remove_client_from_file_list(dev, | 
 | 300 | 				dev->wd_cl.host_client_id); | 
 | 301 |  | 
 | 302 | 		mei_remove_client_from_file_list(dev, | 
 | 303 | 				dev->iamthif_cl.host_client_id); | 
 | 304 |  | 
 | 305 | 		mei_reset_iamthif_params(dev); | 
 | 306 | 		dev->wd_due_counter = 0; | 
 | 307 | 		dev->extra_write_index = 0; | 
 | 308 | 	} | 
 | 309 |  | 
| Tomas Winkler | cf9673d | 2011-06-06 10:44:33 +0300 | [diff] [blame] | 310 | 	dev->me_clients_num = 0; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 311 | 	dev->rd_msg_hdr = 0; | 
| Tomas Winkler | eb9af0a | 2011-05-25 17:28:22 +0300 | [diff] [blame] | 312 | 	dev->stop = false; | 
 | 313 | 	dev->wd_pending = false; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 314 |  | 
 | 315 | 	/* update the state of the registers after reset */ | 
 | 316 | 	dev->host_hw_state = mei_hcsr_read(dev); | 
 | 317 | 	dev->me_hw_state = mei_mecsr_read(dev); | 
 | 318 |  | 
 | 319 | 	dev_dbg(&dev->pdev->dev, "after reset host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n", | 
 | 320 | 	    dev->host_hw_state, dev->me_hw_state); | 
 | 321 |  | 
 | 322 | 	if (unexpected) | 
 | 323 | 		dev_warn(&dev->pdev->dev, "unexpected reset.\n"); | 
 | 324 |  | 
 | 325 | 	/* Wake up all readings so they can be interrupted */ | 
 | 326 | 	list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) { | 
 | 327 | 		if (waitqueue_active(&cl_pos->rx_wait)) { | 
 | 328 | 			dev_dbg(&dev->pdev->dev, "Waking up client!\n"); | 
 | 329 | 			wake_up_interruptible(&cl_pos->rx_wait); | 
 | 330 | 		} | 
 | 331 | 	} | 
 | 332 | 	/* remove all waiting requests */ | 
| Tomas Winkler | b7cd2d9 | 2011-11-27 21:43:34 +0200 | [diff] [blame] | 333 | 	list_for_each_entry_safe(cb_pos, cb_next, | 
 | 334 | 			&dev->write_list.mei_cb.cb_list, cb_list) { | 
| Tomas Winkler | 3a5352f | 2011-12-04 16:16:27 +0200 | [diff] [blame] | 335 | 		list_del(&cb_pos->cb_list); | 
 | 336 | 		mei_free_cb_private(cb_pos); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 337 | 	} | 
 | 338 | } | 
 | 339 |  | 
 | 340 |  | 
 | 341 |  | 
 | 342 | /** | 
 | 343 |  * host_start_message - mei host sends start message. | 
 | 344 |  * | 
 | 345 |  * @dev: the device structure | 
 | 346 |  * | 
 | 347 |  * returns none. | 
 | 348 |  */ | 
| Tomas Winkler | c95efb7 | 2011-05-25 17:28:21 +0300 | [diff] [blame] | 349 | void mei_host_start_message(struct mei_device *dev) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 350 | { | 
 | 351 | 	struct mei_msg_hdr *mei_hdr; | 
 | 352 | 	struct hbm_host_version_request *host_start_req; | 
 | 353 |  | 
 | 354 | 	/* host start message */ | 
 | 355 | 	mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0]; | 
 | 356 | 	mei_hdr->host_addr = 0; | 
 | 357 | 	mei_hdr->me_addr = 0; | 
 | 358 | 	mei_hdr->length = sizeof(struct hbm_host_version_request); | 
 | 359 | 	mei_hdr->msg_complete = 1; | 
 | 360 | 	mei_hdr->reserved = 0; | 
 | 361 |  | 
 | 362 | 	host_start_req = | 
 | 363 | 	    (struct hbm_host_version_request *) &dev->wr_msg_buf[1]; | 
 | 364 | 	memset(host_start_req, 0, sizeof(struct hbm_host_version_request)); | 
| Tomas Winkler | 1ca7e78 | 2012-02-26 23:18:57 +0200 | [diff] [blame] | 365 | 	host_start_req->hbm_cmd = HOST_START_REQ_CMD; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 366 | 	host_start_req->host_version.major_version = HBM_MAJOR_VERSION; | 
 | 367 | 	host_start_req->host_version.minor_version = HBM_MINOR_VERSION; | 
| Tomas Winkler | eb9af0a | 2011-05-25 17:28:22 +0300 | [diff] [blame] | 368 | 	dev->recvd_msg = false; | 
| Tomas Winkler | 1ccb7b6 | 2012-03-14 14:39:42 +0200 | [diff] [blame] | 369 | 	if (mei_write_message(dev, mei_hdr, (unsigned char *)host_start_req, | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 370 | 				       mei_hdr->length)) { | 
 | 371 | 		dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n"); | 
 | 372 | 		dev->mei_state = MEI_RESETING; | 
 | 373 | 		mei_reset(dev, 1); | 
 | 374 | 	} | 
 | 375 | 	dev->init_clients_state = MEI_START_MESSAGE; | 
 | 376 | 	dev->init_clients_timer = INIT_CLIENTS_TIMEOUT; | 
 | 377 | 	return ; | 
 | 378 | } | 
 | 379 |  | 
 | 380 | /** | 
 | 381 |  * host_enum_clients_message - host sends enumeration client request message. | 
 | 382 |  * | 
 | 383 |  * @dev: the device structure | 
 | 384 |  * | 
 | 385 |  * returns none. | 
 | 386 |  */ | 
| Tomas Winkler | c95efb7 | 2011-05-25 17:28:21 +0300 | [diff] [blame] | 387 | void mei_host_enum_clients_message(struct mei_device *dev) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 388 | { | 
 | 389 | 	struct mei_msg_hdr *mei_hdr; | 
 | 390 | 	struct hbm_host_enum_request *host_enum_req; | 
 | 391 | 	mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0]; | 
 | 392 | 	/* enumerate clients */ | 
 | 393 | 	mei_hdr->host_addr = 0; | 
 | 394 | 	mei_hdr->me_addr = 0; | 
 | 395 | 	mei_hdr->length = sizeof(struct hbm_host_enum_request); | 
 | 396 | 	mei_hdr->msg_complete = 1; | 
 | 397 | 	mei_hdr->reserved = 0; | 
 | 398 |  | 
 | 399 | 	host_enum_req = (struct hbm_host_enum_request *) &dev->wr_msg_buf[1]; | 
 | 400 | 	memset(host_enum_req, 0, sizeof(struct hbm_host_enum_request)); | 
| Tomas Winkler | 1ca7e78 | 2012-02-26 23:18:57 +0200 | [diff] [blame] | 401 | 	host_enum_req->hbm_cmd = HOST_ENUM_REQ_CMD; | 
| Tomas Winkler | 1ccb7b6 | 2012-03-14 14:39:42 +0200 | [diff] [blame] | 402 | 	if (mei_write_message(dev, mei_hdr, (unsigned char *)host_enum_req, | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 403 | 				mei_hdr->length)) { | 
 | 404 | 		dev->mei_state = MEI_RESETING; | 
 | 405 | 		dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n"); | 
 | 406 | 		mei_reset(dev, 1); | 
 | 407 | 	} | 
 | 408 | 	dev->init_clients_state = MEI_ENUM_CLIENTS_MESSAGE; | 
 | 409 | 	dev->init_clients_timer = INIT_CLIENTS_TIMEOUT; | 
| Tomas Winkler | 1ccb7b6 | 2012-03-14 14:39:42 +0200 | [diff] [blame] | 410 | 	return; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 411 | } | 
 | 412 |  | 
 | 413 |  | 
 | 414 | /** | 
 | 415 |  * allocate_me_clients_storage - allocates storage for me clients | 
 | 416 |  * | 
 | 417 |  * @dev: the device structure | 
 | 418 |  * | 
 | 419 |  * returns none. | 
 | 420 |  */ | 
| Tomas Winkler | c95efb7 | 2011-05-25 17:28:21 +0300 | [diff] [blame] | 421 | void mei_allocate_me_clients_storage(struct mei_device *dev) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 422 | { | 
 | 423 | 	struct mei_me_client *clients; | 
 | 424 | 	int b; | 
 | 425 |  | 
 | 426 | 	/* count how many ME clients we have */ | 
 | 427 | 	for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX) | 
| Tomas Winkler | cf9673d | 2011-06-06 10:44:33 +0300 | [diff] [blame] | 428 | 		dev->me_clients_num++; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 429 |  | 
| Tomas Winkler | cf9673d | 2011-06-06 10:44:33 +0300 | [diff] [blame] | 430 | 	if (dev->me_clients_num <= 0) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 431 | 		return ; | 
 | 432 |  | 
 | 433 |  | 
 | 434 | 	if (dev->me_clients != NULL) { | 
 | 435 | 		kfree(dev->me_clients); | 
 | 436 | 		dev->me_clients = NULL; | 
 | 437 | 	} | 
 | 438 | 	dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%zd.\n", | 
| Tomas Winkler | cf9673d | 2011-06-06 10:44:33 +0300 | [diff] [blame] | 439 | 		dev->me_clients_num * sizeof(struct mei_me_client)); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 440 | 	/* allocate storage for ME clients representation */ | 
| Tomas Winkler | cf9673d | 2011-06-06 10:44:33 +0300 | [diff] [blame] | 441 | 	clients = kcalloc(dev->me_clients_num, | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 442 | 			sizeof(struct mei_me_client), GFP_KERNEL); | 
 | 443 | 	if (!clients) { | 
 | 444 | 		dev_dbg(&dev->pdev->dev, "memory allocation for ME clients failed.\n"); | 
 | 445 | 		dev->mei_state = MEI_RESETING; | 
 | 446 | 		mei_reset(dev, 1); | 
 | 447 | 		return ; | 
 | 448 | 	} | 
 | 449 | 	dev->me_clients = clients; | 
 | 450 | 	return ; | 
 | 451 | } | 
 | 452 | /** | 
 | 453 |  * host_client_properties - reads properties for client | 
 | 454 |  * | 
 | 455 |  * @dev: the device structure | 
 | 456 |  * | 
| Oren Weil | abc51b6 | 2011-09-21 16:45:30 +0300 | [diff] [blame] | 457 |  * returns: | 
 | 458 |  * 	< 0 - Error. | 
 | 459 |  *  = 0 - no more clients. | 
 | 460 |  *  = 1 - still have clients to send properties request. | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 461 |  */ | 
| Oren Weil | abc51b6 | 2011-09-21 16:45:30 +0300 | [diff] [blame] | 462 | int mei_host_client_properties(struct mei_device *dev) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 463 | { | 
 | 464 | 	struct mei_msg_hdr *mei_header; | 
 | 465 | 	struct hbm_props_request *host_cli_req; | 
 | 466 | 	int b; | 
 | 467 | 	u8 client_num = dev->me_client_presentation_num; | 
 | 468 |  | 
 | 469 | 	b = dev->me_client_index; | 
 | 470 | 	b = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX, b); | 
 | 471 | 	if (b < MEI_CLIENTS_MAX) { | 
 | 472 | 		dev->me_clients[client_num].client_id = b; | 
 | 473 | 		dev->me_clients[client_num].mei_flow_ctrl_creds = 0; | 
 | 474 | 		mei_header = (struct mei_msg_hdr *)&dev->wr_msg_buf[0]; | 
 | 475 | 		mei_header->host_addr = 0; | 
 | 476 | 		mei_header->me_addr = 0; | 
 | 477 | 		mei_header->length = sizeof(struct hbm_props_request); | 
 | 478 | 		mei_header->msg_complete = 1; | 
 | 479 | 		mei_header->reserved = 0; | 
 | 480 |  | 
 | 481 | 		host_cli_req = (struct hbm_props_request *)&dev->wr_msg_buf[1]; | 
 | 482 |  | 
 | 483 | 		memset(host_cli_req, 0, sizeof(struct hbm_props_request)); | 
 | 484 |  | 
| Tomas Winkler | 1ca7e78 | 2012-02-26 23:18:57 +0200 | [diff] [blame] | 485 | 		host_cli_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 486 | 		host_cli_req->address = b; | 
 | 487 |  | 
| Tomas Winkler | 1ccb7b6 | 2012-03-14 14:39:42 +0200 | [diff] [blame] | 488 | 		if (mei_write_message(dev, mei_header, | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 489 | 				(unsigned char *)host_cli_req, | 
 | 490 | 				mei_header->length)) { | 
 | 491 | 			dev->mei_state = MEI_RESETING; | 
 | 492 | 			dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n"); | 
 | 493 | 			mei_reset(dev, 1); | 
| Oren Weil | abc51b6 | 2011-09-21 16:45:30 +0300 | [diff] [blame] | 494 | 			return -EIO; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 495 | 		} | 
 | 496 |  | 
 | 497 | 		dev->init_clients_timer = INIT_CLIENTS_TIMEOUT; | 
 | 498 | 		dev->me_client_index = b; | 
| Oren Weil | abc51b6 | 2011-09-21 16:45:30 +0300 | [diff] [blame] | 499 | 		return 1; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 500 | 	} | 
 | 501 |  | 
| Oren Weil | abc51b6 | 2011-09-21 16:45:30 +0300 | [diff] [blame] | 502 | 	return 0; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 503 | } | 
 | 504 |  | 
 | 505 | /** | 
 | 506 |  * mei_init_file_private - initializes private file structure. | 
 | 507 |  * | 
 | 508 |  * @priv: private file structure to be initialized | 
 | 509 |  * @file: the file structure | 
 | 510 |  */ | 
| Tomas Winkler | c95efb7 | 2011-05-25 17:28:21 +0300 | [diff] [blame] | 511 | void mei_cl_init(struct mei_cl *priv, struct mei_device *dev) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 512 | { | 
 | 513 | 	memset(priv, 0, sizeof(struct mei_cl)); | 
 | 514 | 	init_waitqueue_head(&priv->wait); | 
 | 515 | 	init_waitqueue_head(&priv->rx_wait); | 
 | 516 | 	init_waitqueue_head(&priv->tx_wait); | 
 | 517 | 	INIT_LIST_HEAD(&priv->link); | 
 | 518 | 	priv->reading_state = MEI_IDLE; | 
 | 519 | 	priv->writing_state = MEI_IDLE; | 
 | 520 | 	priv->dev = dev; | 
 | 521 | } | 
 | 522 |  | 
 | 523 | int mei_find_me_client_index(const struct mei_device *dev, uuid_le cuuid) | 
 | 524 | { | 
 | 525 | 	int i, res = -1; | 
 | 526 |  | 
| Tomas Winkler | cf9673d | 2011-06-06 10:44:33 +0300 | [diff] [blame] | 527 | 	for (i = 0; i < dev->me_clients_num; ++i) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 528 | 		if (uuid_le_cmp(cuuid, | 
 | 529 | 				dev->me_clients[i].props.protocol_name) == 0) { | 
 | 530 | 			res = i; | 
 | 531 | 			break; | 
 | 532 | 		} | 
 | 533 |  | 
 | 534 | 	return res; | 
 | 535 | } | 
 | 536 |  | 
 | 537 |  | 
 | 538 | /** | 
 | 539 |  * mei_find_me_client_update_filext - searches for ME client guid | 
 | 540 |  *                       sets client_id in mei_file_private if found | 
 | 541 |  * @dev: the device structure | 
 | 542 |  * @priv: private file structure to set client_id in | 
 | 543 |  * @cguid: searched guid of ME client | 
 | 544 |  * @client_id: id of host client to be set in file private structure | 
 | 545 |  * | 
 | 546 |  * returns ME client index | 
 | 547 |  */ | 
 | 548 | u8 mei_find_me_client_update_filext(struct mei_device *dev, struct mei_cl *priv, | 
 | 549 | 				const uuid_le *cguid, u8 client_id) | 
 | 550 | { | 
 | 551 | 	int i; | 
 | 552 |  | 
 | 553 | 	if (!dev || !priv || !cguid) | 
 | 554 | 		return 0; | 
 | 555 |  | 
 | 556 | 	/* check for valid client id */ | 
 | 557 | 	i = mei_find_me_client_index(dev, *cguid); | 
 | 558 | 	if (i >= 0) { | 
 | 559 | 		priv->me_client_id = dev->me_clients[i].client_id; | 
 | 560 | 		priv->state = MEI_FILE_CONNECTING; | 
 | 561 | 		priv->host_client_id = client_id; | 
 | 562 |  | 
 | 563 | 		list_add_tail(&priv->link, &dev->file_list); | 
 | 564 | 		return (u8)i; | 
 | 565 | 	} | 
 | 566 |  | 
 | 567 | 	return 0; | 
 | 568 | } | 
 | 569 |  | 
 | 570 | /** | 
 | 571 |  * host_init_iamthif - mei initialization iamthif client. | 
 | 572 |  * | 
 | 573 |  * @dev: the device structure | 
 | 574 |  * | 
 | 575 |  */ | 
| Tomas Winkler | c95efb7 | 2011-05-25 17:28:21 +0300 | [diff] [blame] | 576 | void mei_host_init_iamthif(struct mei_device *dev) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 577 | { | 
 | 578 | 	u8 i; | 
 | 579 | 	unsigned char *msg_buf; | 
 | 580 |  | 
| Tomas Winkler | c95efb7 | 2011-05-25 17:28:21 +0300 | [diff] [blame] | 581 | 	mei_cl_init(&dev->iamthif_cl, dev); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 582 | 	dev->iamthif_cl.state = MEI_FILE_DISCONNECTED; | 
 | 583 |  | 
 | 584 | 	/* find ME amthi client */ | 
 | 585 | 	i = mei_find_me_client_update_filext(dev, &dev->iamthif_cl, | 
 | 586 | 			    &mei_amthi_guid, MEI_IAMTHIF_HOST_CLIENT_ID); | 
 | 587 | 	if (dev->iamthif_cl.state != MEI_FILE_CONNECTING) { | 
 | 588 | 		dev_dbg(&dev->pdev->dev, "failed to find iamthif client.\n"); | 
 | 589 | 		return; | 
 | 590 | 	} | 
 | 591 |  | 
| Tomas Winkler | c9667bf | 2011-12-13 23:39:33 +0200 | [diff] [blame] | 592 | 	/* Assign iamthif_mtu to the value received from ME  */ | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 593 |  | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 594 | 	dev->iamthif_mtu = dev->me_clients[i].props.max_msg_length; | 
| Tomas Winkler | c9667bf | 2011-12-13 23:39:33 +0200 | [diff] [blame] | 595 | 	dev_dbg(&dev->pdev->dev, "IAMTHIF_MTU = %d\n", | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 596 | 			dev->me_clients[i].props.max_msg_length); | 
 | 597 |  | 
 | 598 | 	kfree(dev->iamthif_msg_buf); | 
 | 599 | 	dev->iamthif_msg_buf = NULL; | 
 | 600 |  | 
 | 601 | 	/* allocate storage for ME message buffer */ | 
 | 602 | 	msg_buf = kcalloc(dev->iamthif_mtu, | 
 | 603 | 			sizeof(unsigned char), GFP_KERNEL); | 
 | 604 | 	if (!msg_buf) { | 
 | 605 | 		dev_dbg(&dev->pdev->dev, "memory allocation for ME message buffer failed.\n"); | 
 | 606 | 		return; | 
 | 607 | 	} | 
 | 608 |  | 
 | 609 | 	dev->iamthif_msg_buf = msg_buf; | 
 | 610 |  | 
| Tomas Winkler | 1ccb7b6 | 2012-03-14 14:39:42 +0200 | [diff] [blame] | 611 | 	if (mei_connect(dev, &dev->iamthif_cl)) { | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 612 | 		dev_dbg(&dev->pdev->dev, "Failed to connect to AMTHI client\n"); | 
 | 613 | 		dev->iamthif_cl.state = MEI_FILE_DISCONNECTED; | 
 | 614 | 		dev->iamthif_cl.host_client_id = 0; | 
 | 615 | 	} else { | 
 | 616 | 		dev->iamthif_cl.timer_count = CONNECT_TIMEOUT; | 
 | 617 | 	} | 
 | 618 | } | 
 | 619 |  | 
 | 620 | /** | 
 | 621 |  * mei_alloc_file_private - allocates a private file structure and sets it up. | 
 | 622 |  * @file: the file structure | 
 | 623 |  * | 
 | 624 |  * returns  The allocated file or NULL on failure | 
 | 625 |  */ | 
| Tomas Winkler | c95efb7 | 2011-05-25 17:28:21 +0300 | [diff] [blame] | 626 | struct mei_cl *mei_cl_allocate(struct mei_device *dev) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 627 | { | 
| Tomas Winkler | c95efb7 | 2011-05-25 17:28:21 +0300 | [diff] [blame] | 628 | 	struct mei_cl *cl; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 629 |  | 
| Tomas Winkler | c95efb7 | 2011-05-25 17:28:21 +0300 | [diff] [blame] | 630 | 	cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL); | 
 | 631 | 	if (!cl) | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 632 | 		return NULL; | 
 | 633 |  | 
| Tomas Winkler | c95efb7 | 2011-05-25 17:28:21 +0300 | [diff] [blame] | 634 | 	mei_cl_init(cl, dev); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 635 |  | 
| Tomas Winkler | c95efb7 | 2011-05-25 17:28:21 +0300 | [diff] [blame] | 636 | 	return cl; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 637 | } | 
 | 638 |  | 
 | 639 |  | 
 | 640 |  | 
 | 641 | /** | 
 | 642 |  * mei_disconnect_host_client - sends disconnect message to fw from host client. | 
 | 643 |  * | 
 | 644 |  * @dev: the device structure | 
 | 645 |  * @cl: private data of the file object | 
 | 646 |  * | 
 | 647 |  * Locking: called under "dev->device_lock" lock | 
 | 648 |  * | 
 | 649 |  * returns 0 on success, <0 on failure. | 
 | 650 |  */ | 
 | 651 | int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl) | 
 | 652 | { | 
 | 653 | 	int rets, err; | 
 | 654 | 	long timeout = 15;	/* 15 seconds */ | 
 | 655 | 	struct mei_cl_cb *cb; | 
 | 656 |  | 
 | 657 | 	if (!dev || !cl) | 
 | 658 | 		return -ENODEV; | 
 | 659 |  | 
 | 660 | 	if (cl->state != MEI_FILE_DISCONNECTING) | 
 | 661 | 		return 0; | 
 | 662 |  | 
 | 663 | 	cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); | 
 | 664 | 	if (!cb) | 
 | 665 | 		return -ENOMEM; | 
 | 666 |  | 
 | 667 | 	INIT_LIST_HEAD(&cb->cb_list); | 
 | 668 | 	cb->file_private = cl; | 
 | 669 | 	cb->major_file_operations = MEI_CLOSE; | 
 | 670 | 	if (dev->mei_host_buffer_is_empty) { | 
| Tomas Winkler | eb9af0a | 2011-05-25 17:28:22 +0300 | [diff] [blame] | 671 | 		dev->mei_host_buffer_is_empty = false; | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 672 | 		if (mei_disconnect(dev, cl)) { | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 673 | 			rets = -ENODEV; | 
 | 674 | 			dev_dbg(&dev->pdev->dev, "failed to call mei_disconnect.\n"); | 
 | 675 | 			goto free; | 
 | 676 | 		} | 
| Tomas Winkler | 1ccb7b6 | 2012-03-14 14:39:42 +0200 | [diff] [blame] | 677 | 		mdelay(10); /* Wait for hardware disconnection ready */ | 
 | 678 | 		list_add_tail(&cb->cb_list, &dev->ctrl_rd_list.mei_cb.cb_list); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 679 | 	} else { | 
 | 680 | 		dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n"); | 
 | 681 | 		list_add_tail(&cb->cb_list, | 
 | 682 | 				&dev->ctrl_wr_list.mei_cb.cb_list); | 
 | 683 | 	} | 
 | 684 | 	mutex_unlock(&dev->device_lock); | 
 | 685 |  | 
 | 686 | 	err = wait_event_timeout(dev->wait_recvd_msg, | 
 | 687 | 		 (MEI_FILE_DISCONNECTED == cl->state), | 
 | 688 | 		 timeout * HZ); | 
 | 689 |  | 
 | 690 | 	mutex_lock(&dev->device_lock); | 
 | 691 | 	if (MEI_FILE_DISCONNECTED == cl->state) { | 
 | 692 | 		rets = 0; | 
 | 693 | 		dev_dbg(&dev->pdev->dev, "successfully disconnected from FW client.\n"); | 
 | 694 | 	} else { | 
 | 695 | 		rets = -ENODEV; | 
 | 696 | 		if (MEI_FILE_DISCONNECTED != cl->state) | 
 | 697 | 			dev_dbg(&dev->pdev->dev, "wrong status client disconnect.\n"); | 
 | 698 |  | 
 | 699 | 		if (err) | 
 | 700 | 			dev_dbg(&dev->pdev->dev, | 
 | 701 | 					"wait failed disconnect err=%08x\n", | 
 | 702 | 					err); | 
 | 703 |  | 
 | 704 | 		dev_dbg(&dev->pdev->dev, "failed to disconnect from FW client.\n"); | 
 | 705 | 	} | 
 | 706 |  | 
| Tomas Winkler | 0288c7c | 2011-06-06 10:44:34 +0300 | [diff] [blame] | 707 | 	mei_io_list_flush(&dev->ctrl_rd_list, cl); | 
 | 708 | 	mei_io_list_flush(&dev->ctrl_wr_list, cl); | 
| Oren Weil | 91f01c6 | 2011-05-15 13:43:44 +0300 | [diff] [blame] | 709 | free: | 
 | 710 | 	mei_free_cb_private(cb); | 
 | 711 | 	return rets; | 
 | 712 | } | 
 | 713 |  | 
 | 714 | /** | 
 | 715 |  * mei_remove_client_from_file_list - | 
 | 716 |  *	removes file private data from device file list | 
 | 717 |  * | 
 | 718 |  * @dev: the device structure | 
 | 719 |  * @host_client_id: host client id to be removed | 
 | 720 |  */ | 
 | 721 | void mei_remove_client_from_file_list(struct mei_device *dev, | 
 | 722 | 				       u8 host_client_id) | 
 | 723 | { | 
 | 724 | 	struct mei_cl *cl_pos = NULL; | 
 | 725 | 	struct mei_cl *cl_next = NULL; | 
 | 726 | 	list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) { | 
 | 727 | 		if (host_client_id == cl_pos->host_client_id) { | 
 | 728 | 			dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n", | 
 | 729 | 					cl_pos->host_client_id, | 
 | 730 | 					cl_pos->me_client_id); | 
 | 731 | 			list_del_init(&cl_pos->link); | 
 | 732 | 			break; | 
 | 733 | 		} | 
 | 734 | 	} | 
 | 735 | } |