hbm.c 12.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 *
 * Intel Management Engine Interface (Intel MEI) Linux driver
 * Copyright (c) 2003-2012, Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 */

#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/mei.h>

#include "mei_dev.h"
#include "interface.h"

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
/**
 * mei_hbm_cl_hdr - construct client hbm header
 * @cl: - client
 * @hbm_cmd: host bus message command
 * @buf: buffer for cl header
 * @len: buffer length
 */
static inline
void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
{
	struct mei_hbm_cl_cmd *cmd = buf;

	memset(cmd, 0, len);

	cmd->hbm_cmd = hbm_cmd;
	cmd->host_addr = cl->host_client_id;
	cmd->me_addr = cl->me_client_id;
}

/**
 * same_disconn_addr - tells if they have the same address
 *
 * @file: private data of the file object.
 * @disconn: disconnection request.
 *
 * returns true if addres are same
 */
static inline
bool mei_hbm_cl_addr_equal(struct mei_cl *cl, void *buf)
{
	struct mei_hbm_cl_cmd *cmd = buf;
	return cl->host_client_id == cmd->host_addr &&
		cl->me_client_id == cmd->me_addr;
}


61
/**
62
 * mei_hbm_start_req - sends start request message.
63 64 65
 *
 * @dev: the device structure
 */
66
void mei_hbm_start_req(struct mei_device *dev)
67
{
68
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
69 70 71
	struct hbm_host_version_request *start_req;
	const size_t len = sizeof(struct hbm_host_version_request);

72
	mei_hbm_hdr(mei_hdr, len);
73 74

	/* host start message */
75
	start_req = (struct hbm_host_version_request *)dev->wr_msg.data;
76 77 78 79 80 81
	memset(start_req, 0, len);
	start_req->hbm_cmd = HOST_START_REQ_CMD;
	start_req->host_version.major_version = HBM_MAJOR_VERSION;
	start_req->host_version.minor_version = HBM_MINOR_VERSION;

	dev->recvd_msg = false;
82
	if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
83 84 85 86 87 88 89 90 91 92
		dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n");
		dev->dev_state = MEI_DEV_RESETING;
		mei_reset(dev, 1);
	}
	dev->init_clients_state = MEI_START_MESSAGE;
	dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
	return ;
}

/**
93
 * mei_hbm_enum_clients_req - sends enumeration client request message.
94 95 96 97 98
 *
 * @dev: the device structure
 *
 * returns none.
 */
99
static void mei_hbm_enum_clients_req(struct mei_device *dev)
100
{
101
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
102 103 104
	struct hbm_host_enum_request *enum_req;
	const size_t len = sizeof(struct hbm_host_enum_request);
	/* enumerate clients */
105
	mei_hbm_hdr(mei_hdr, len);
106

107 108
	enum_req = (struct hbm_host_enum_request *)dev->wr_msg.data;
	memset(enum_req, 0, len);
109 110
	enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;

111
	if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
112 113 114 115 116 117 118 119 120
		dev->dev_state = MEI_DEV_RESETING;
		dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
		mei_reset(dev, 1);
	}
	dev->init_clients_state = MEI_ENUM_CLIENTS_MESSAGE;
	dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
	return;
}

121 122 123 124 125 126 127
/**
 * mei_hbm_prop_requsest - request property for a single client
 *
 * @dev: the device structure
 *
 * returns none.
 */
128

129
static int mei_hbm_prop_req(struct mei_device *dev)
130 131
{

132
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
	struct hbm_props_request *prop_req;
	const size_t len = sizeof(struct hbm_props_request);
	unsigned long next_client_index;
	u8 client_num;


	client_num = dev->me_client_presentation_num;

	next_client_index = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX,
					  dev->me_client_index);

	/* We got all client properties */
	if (next_client_index == MEI_CLIENTS_MAX) {
		schedule_work(&dev->init_work);

		return 0;
	}

	dev->me_clients[client_num].client_id = next_client_index;
	dev->me_clients[client_num].mei_flow_ctrl_creds = 0;

154 155
	mei_hbm_hdr(mei_hdr, len);
	prop_req = (struct hbm_props_request *)dev->wr_msg.data;
156 157 158 159 160 161 162

	memset(prop_req, 0, sizeof(struct hbm_props_request));


	prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
	prop_req->address = next_client_index;

163
	if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
164 165 166 167 168 169 170 171 172 173 174 175 176
		dev->dev_state = MEI_DEV_RESETING;
		dev_err(&dev->pdev->dev, "Properties request command failed\n");
		mei_reset(dev, 1);

		return -EIO;
	}

	dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
	dev->me_client_index = next_client_index;

	return 0;
}

177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
/**
 * mei_hbm_stop_req_prepare - perpare stop request message
 *
 * @dev - mei device
 * @mei_hdr - mei message header
 * @data - hbm message body buffer
 */
static void mei_hbm_stop_req_prepare(struct mei_device *dev,
		struct mei_msg_hdr *mei_hdr, unsigned char *data)
{
	struct hbm_host_stop_request *req =
			(struct hbm_host_stop_request *)data;
	const size_t len = sizeof(struct hbm_host_stop_request);

	mei_hbm_hdr(mei_hdr, len);

	memset(req, 0, len);
	req->hbm_cmd = HOST_STOP_REQ_CMD;
	req->reason = DRIVER_STOP_REQUEST;
}

198
/**
199
 * mei_hbm_cl_flow_control_req - sends flow control requst.
200 201
 *
 * @dev: the device structure
202
 * @cl: client info
203 204 205
 *
 * This function returns -EIO on write failure
 */
206
int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
207
{
208
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
209 210
	const size_t len = sizeof(struct hbm_flow_control);

211 212
	mei_hbm_hdr(mei_hdr, len);
	mei_hbm_cl_hdr(cl, MEI_FLOW_CONTROL_CMD, dev->wr_msg.data, len);
213 214 215 216

	dev_dbg(&dev->pdev->dev, "sending flow control host client = %d, ME client = %d\n",
		cl->host_client_id, cl->me_client_id);

217
	return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
218 219 220
}

/**
221
 * mei_hbm_cl_disconnect_req - sends disconnect message to fw.
222 223
 *
 * @dev: the device structure
224
 * @cl: a client to disconnect from
225 226 227
 *
 * This function returns -EIO on write failure
 */
228
int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
229
{
230
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
231 232
	const size_t len = sizeof(struct hbm_client_connect_request);

233 234
	mei_hbm_hdr(mei_hdr, len);
	mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_REQ_CMD, dev->wr_msg.data, len);
235

236
	return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
237 238 239
}

/**
240
 * mei_hbm_cl_connect_req - send connection request to specific me client
241 242
 *
 * @dev: the device structure
243
 * @cl: a client to connect to
244
 *
245
 * returns -EIO on write failure
246
 */
247
int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl)
248
{
249
	struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
250 251
	const size_t len = sizeof(struct hbm_client_connect_request);

252 253
	mei_hbm_hdr(mei_hdr, len);
	mei_hbm_cl_hdr(cl, CLIENT_CONNECT_REQ_CMD, dev->wr_msg.data, len);
254

255
	return mei_write_message(dev, mei_hdr,  dev->wr_msg.data);
256 257 258
}

/**
259 260
 * mei_client_disconnect_request - disconnect request initiated by me
 *  host sends disoconnect response
261 262
 *
 * @dev: the device structure.
263
 * @disconnect_req: disconnect request bus message from the me
264
 */
265
static void mei_hbm_fw_disconnect_req(struct mei_device *dev,
266 267
		struct hbm_client_connect_request *disconnect_req)
{
268
	struct mei_cl *cl, *next;
269 270
	const size_t len = sizeof(struct hbm_client_connect_response);

271 272
	list_for_each_entry_safe(cl, next, &dev->file_list, link) {
		if (mei_hbm_cl_addr_equal(cl, disconnect_req)) {
273 274 275
			dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
					disconnect_req->host_addr,
					disconnect_req->me_addr);
276 277 278
			cl->state = MEI_FILE_DISCONNECTED;
			cl->timer_count = 0;
			if (cl == &dev->wd_cl)
279
				dev->wd_pending = false;
280
			else if (cl == &dev->iamthif_cl)
281 282 283
				dev->iamthif_timer = 0;

			/* prepare disconnect response */
284
			mei_hbm_hdr(&dev->wr_ext_msg.hdr, len);
285
			mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD,
286
					 dev->wr_ext_msg.data, len);
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
			break;
		}
	}
}


/**
 * mei_hbm_dispatch - bottom half read routine after ISR to
 * handle the read bus message cmd processing.
 *
 * @dev: the device structure
 * @mei_hdr: header of bus message
 */
void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
{
	struct mei_bus_message *mei_msg;
	struct mei_me_client *me_client;
	struct hbm_host_version_response *version_res;
	struct hbm_client_connect_response *connect_res;
	struct hbm_client_connect_response *disconnect_res;
	struct hbm_client_connect_request *disconnect_req;
	struct hbm_flow_control *flow_control;
	struct hbm_props_response *props_res;
	struct hbm_host_enum_response *enum_res;

	/* read the message to our buffer */
	BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf));
	mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
	mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;

	switch (mei_msg->hbm_cmd) {
	case HOST_START_RES_CMD:
		version_res = (struct hbm_host_version_response *)mei_msg;
320
		if (!version_res->host_version_supported) {
321
			dev->version = version_res->me_max_version;
322
			dev_dbg(&dev->pdev->dev, "version mismatch.\n");
323

324 325 326 327 328 329
			mei_hbm_stop_req_prepare(dev, &dev->wr_msg.hdr,
						dev->wr_msg.data);
			mei_write_message(dev, &dev->wr_msg.hdr,
					dev->wr_msg.data);
			return;
		}
330

331 332 333 334 335
		dev->version.major_version = HBM_MAJOR_VERSION;
		dev->version.minor_version = HBM_MINOR_VERSION;
		if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
		    dev->init_clients_state == MEI_START_MESSAGE) {
			dev->init_clients_timer = 0;
336
			mei_hbm_enum_clients_req(dev);
337 338 339 340
		} else {
			dev->recvd_msg = false;
			dev_dbg(&dev->pdev->dev, "reset due to received hbm: host start\n");
			mei_reset(dev, 1);
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
			return;
		}

		dev->recvd_msg = true;
		dev_dbg(&dev->pdev->dev, "host start response message received.\n");
		break;

	case CLIENT_CONNECT_RES_CMD:
		connect_res = (struct hbm_client_connect_response *) mei_msg;
		mei_client_connect_response(dev, connect_res);
		dev_dbg(&dev->pdev->dev, "client connect response message received.\n");
		wake_up(&dev->wait_recvd_msg);
		break;

	case CLIENT_DISCONNECT_RES_CMD:
		disconnect_res = (struct hbm_client_connect_response *) mei_msg;
		mei_client_disconnect_response(dev, disconnect_res);
		dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n");
		wake_up(&dev->wait_recvd_msg);
		break;

	case MEI_FLOW_CONTROL_CMD:
		flow_control = (struct hbm_flow_control *) mei_msg;
		mei_client_flow_control_response(dev, flow_control);
		dev_dbg(&dev->pdev->dev, "client flow control response message received.\n");
		break;

	case HOST_CLIENT_PROPERTIES_RES_CMD:
		props_res = (struct hbm_props_response *)mei_msg;
		me_client = &dev->me_clients[dev->me_client_presentation_num];

		if (props_res->status || !dev->me_clients) {
			dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n");
			mei_reset(dev, 1);
			return;
		}

		if (me_client->client_id != props_res->address) {
			dev_err(&dev->pdev->dev,
				"Host client properties reply mismatch\n");
			mei_reset(dev, 1);

			return;
		}

		if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
		    dev->init_clients_state != MEI_CLIENT_PROPERTIES_MESSAGE) {
			dev_err(&dev->pdev->dev,
				"Unexpected client properties reply\n");
			mei_reset(dev, 1);

			return;
		}

		me_client->props = props_res->client_properties;
		dev->me_client_index++;
		dev->me_client_presentation_num++;

399 400
		/* request property for the next client */
		mei_hbm_prop_req(dev);
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415

		break;

	case HOST_ENUM_RES_CMD:
		enum_res = (struct hbm_host_enum_response *) mei_msg;
		memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
		if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
		    dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) {
				dev->init_clients_timer = 0;
				dev->me_client_presentation_num = 0;
				dev->me_client_index = 0;
				mei_allocate_me_clients_storage(dev);
				dev->init_clients_state =
					MEI_CLIENT_PROPERTIES_MESSAGE;

416 417
				/* first property reqeust */
				mei_hbm_prop_req(dev);
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
		} else {
			dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n");
			mei_reset(dev, 1);
			return;
		}
		break;

	case HOST_STOP_RES_CMD:
		dev->dev_state = MEI_DEV_DISABLED;
		dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n");
		mei_reset(dev, 1);
		break;

	case CLIENT_DISCONNECT_REQ_CMD:
		/* search for client */
		disconnect_req = (struct hbm_client_connect_request *)mei_msg;
434
		mei_hbm_fw_disconnect_req(dev, disconnect_req);
435 436 437 438
		break;

	case ME_STOP_REQ_CMD:

439 440
		mei_hbm_stop_req_prepare(dev, &dev->wr_ext_msg.hdr,
					dev->wr_ext_msg.data);
441 442 443 444 445 446 447 448
		break;
	default:
		BUG();
		break;

	}
}