blob: 2c4c1bb6d36afd4ac78f13370344823f68d1f870 [file] [log] [blame]
Tomas Winklerbb1b0132012-12-25 19:06:07 +02001/*
2 *
3 * Intel Management Engine Interface (Intel MEI) Linux driver
4 * Copyright (c) 2003-2012, Intel Corporation.
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/mei.h>
21
22#include "mei_dev.h"
23#include "interface.h"
24
25/**
26 * host_start_message - mei host sends start message.
27 *
28 * @dev: the device structure
29 *
30 * returns none.
31 */
32void mei_host_start_message(struct mei_device *dev)
33{
34 struct mei_msg_hdr *mei_hdr;
35 struct hbm_host_version_request *start_req;
36 const size_t len = sizeof(struct hbm_host_version_request);
37
38 mei_hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len);
39
40 /* host start message */
41 start_req = (struct hbm_host_version_request *)&dev->wr_msg_buf[1];
42 memset(start_req, 0, len);
43 start_req->hbm_cmd = HOST_START_REQ_CMD;
44 start_req->host_version.major_version = HBM_MAJOR_VERSION;
45 start_req->host_version.minor_version = HBM_MINOR_VERSION;
46
47 dev->recvd_msg = false;
48 if (mei_write_message(dev, mei_hdr, (unsigned char *)start_req)) {
49 dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n");
50 dev->dev_state = MEI_DEV_RESETING;
51 mei_reset(dev, 1);
52 }
53 dev->init_clients_state = MEI_START_MESSAGE;
54 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
55 return ;
56}
57
58/**
59 * host_enum_clients_message - host sends enumeration client request message.
60 *
61 * @dev: the device structure
62 *
63 * returns none.
64 */
65void mei_host_enum_clients_message(struct mei_device *dev)
66{
67 struct mei_msg_hdr *mei_hdr;
68 struct hbm_host_enum_request *enum_req;
69 const size_t len = sizeof(struct hbm_host_enum_request);
70 /* enumerate clients */
71 mei_hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len);
72
73 enum_req = (struct hbm_host_enum_request *) &dev->wr_msg_buf[1];
74 memset(enum_req, 0, sizeof(struct hbm_host_enum_request));
75 enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
76
77 if (mei_write_message(dev, mei_hdr, (unsigned char *)enum_req)) {
78 dev->dev_state = MEI_DEV_RESETING;
79 dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
80 mei_reset(dev, 1);
81 }
82 dev->init_clients_state = MEI_ENUM_CLIENTS_MESSAGE;
83 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
84 return;
85}
86
87
88int mei_host_client_enumerate(struct mei_device *dev)
89{
90
91 struct mei_msg_hdr *mei_hdr;
92 struct hbm_props_request *prop_req;
93 const size_t len = sizeof(struct hbm_props_request);
94 unsigned long next_client_index;
95 u8 client_num;
96
97
98 client_num = dev->me_client_presentation_num;
99
100 next_client_index = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX,
101 dev->me_client_index);
102
103 /* We got all client properties */
104 if (next_client_index == MEI_CLIENTS_MAX) {
105 schedule_work(&dev->init_work);
106
107 return 0;
108 }
109
110 dev->me_clients[client_num].client_id = next_client_index;
111 dev->me_clients[client_num].mei_flow_ctrl_creds = 0;
112
113 mei_hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len);
114 prop_req = (struct hbm_props_request *)&dev->wr_msg_buf[1];
115
116 memset(prop_req, 0, sizeof(struct hbm_props_request));
117
118
119 prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
120 prop_req->address = next_client_index;
121
122 if (mei_write_message(dev, mei_hdr, (unsigned char *) prop_req)) {
123 dev->dev_state = MEI_DEV_RESETING;
124 dev_err(&dev->pdev->dev, "Properties request command failed\n");
125 mei_reset(dev, 1);
126
127 return -EIO;
128 }
129
130 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
131 dev->me_client_index = next_client_index;
132
133 return 0;
134}
135
136/**
137 * mei_send_flow_control - sends flow control to fw.
138 *
139 * @dev: the device structure
140 * @cl: private data of the file object
141 *
142 * This function returns -EIO on write failure
143 */
144int mei_send_flow_control(struct mei_device *dev, struct mei_cl *cl)
145{
146 struct mei_msg_hdr *mei_hdr;
147 struct hbm_flow_control *flow_ctrl;
148 const size_t len = sizeof(struct hbm_flow_control);
149
150 mei_hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len);
151
152 flow_ctrl = (struct hbm_flow_control *)&dev->wr_msg_buf[1];
153 memset(flow_ctrl, 0, len);
154 flow_ctrl->hbm_cmd = MEI_FLOW_CONTROL_CMD;
155 flow_ctrl->host_addr = cl->host_client_id;
156 flow_ctrl->me_addr = cl->me_client_id;
157 /* FIXME: reserved !? */
158 memset(flow_ctrl->reserved, 0, sizeof(flow_ctrl->reserved));
159 dev_dbg(&dev->pdev->dev, "sending flow control host client = %d, ME client = %d\n",
160 cl->host_client_id, cl->me_client_id);
161
162 return mei_write_message(dev, mei_hdr, (unsigned char *) flow_ctrl);
163}
164
165/**
166 * mei_disconnect - sends disconnect message to fw.
167 *
168 * @dev: the device structure
169 * @cl: private data of the file object
170 *
171 * This function returns -EIO on write failure
172 */
173int mei_disconnect(struct mei_device *dev, struct mei_cl *cl)
174{
175 struct mei_msg_hdr *hdr;
176 struct hbm_client_connect_request *req;
177 const size_t len = sizeof(struct hbm_client_connect_request);
178
179 hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len);
180
181 req = (struct hbm_client_connect_request *)&dev->wr_msg_buf[1];
182 memset(req, 0, len);
183 req->hbm_cmd = CLIENT_DISCONNECT_REQ_CMD;
184 req->host_addr = cl->host_client_id;
185 req->me_addr = cl->me_client_id;
186 req->reserved = 0;
187
188 return mei_write_message(dev, hdr, (unsigned char *)req);
189}
190
191/**
192 * mei_connect - sends connect message to fw.
193 *
194 * @dev: the device structure
195 * @cl: private data of the file object
196 *
197 * This function returns -EIO on write failure
198 */
199int mei_connect(struct mei_device *dev, struct mei_cl *cl)
200{
201 struct mei_msg_hdr *hdr;
202 struct hbm_client_connect_request *req;
203 const size_t len = sizeof(struct hbm_client_connect_request);
204
205 hdr = mei_hbm_hdr(&dev->wr_msg_buf[0], len);
206
207 req = (struct hbm_client_connect_request *) &dev->wr_msg_buf[1];
208 req->hbm_cmd = CLIENT_CONNECT_REQ_CMD;
209 req->host_addr = cl->host_client_id;
210 req->me_addr = cl->me_client_id;
211 req->reserved = 0;
212
213 return mei_write_message(dev, hdr, (unsigned char *) req);
214}
215
216/**
217 * same_disconn_addr - tells if they have the same address
218 *
219 * @file: private data of the file object.
220 * @disconn: disconnection request.
221 *
222 * returns !=0, same; 0,not.
223 */
224static int same_disconn_addr(struct mei_cl *cl,
225 struct hbm_client_connect_request *req)
226{
227 return (cl->host_client_id == req->host_addr &&
228 cl->me_client_id == req->me_addr);
229}
230
231/**
232 * mei_client_disconnect_request - disconnects from request irq routine
233 *
234 * @dev: the device structure.
235 * @disconnect_req: disconnect request bus message.
236 */
237static void mei_client_disconnect_request(struct mei_device *dev,
238 struct hbm_client_connect_request *disconnect_req)
239{
240 struct hbm_client_connect_response *disconnect_res;
241 struct mei_cl *pos, *next;
242 const size_t len = sizeof(struct hbm_client_connect_response);
243
244 list_for_each_entry_safe(pos, next, &dev->file_list, link) {
245 if (same_disconn_addr(pos, disconnect_req)) {
246 dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
247 disconnect_req->host_addr,
248 disconnect_req->me_addr);
249 pos->state = MEI_FILE_DISCONNECTED;
250 pos->timer_count = 0;
251 if (pos == &dev->wd_cl)
252 dev->wd_pending = false;
253 else if (pos == &dev->iamthif_cl)
254 dev->iamthif_timer = 0;
255
256 /* prepare disconnect response */
257 (void)mei_hbm_hdr((u32 *)&dev->wr_ext_msg.hdr, len);
258 disconnect_res =
259 (struct hbm_client_connect_response *)
260 &dev->wr_ext_msg.data;
261 disconnect_res->hbm_cmd = CLIENT_DISCONNECT_RES_CMD;
262 disconnect_res->host_addr = pos->host_client_id;
263 disconnect_res->me_addr = pos->me_client_id;
264 disconnect_res->status = 0;
265 break;
266 }
267 }
268}
269
270
271/**
272 * mei_hbm_dispatch - bottom half read routine after ISR to
273 * handle the read bus message cmd processing.
274 *
275 * @dev: the device structure
276 * @mei_hdr: header of bus message
277 */
278void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
279{
280 struct mei_bus_message *mei_msg;
281 struct mei_me_client *me_client;
282 struct hbm_host_version_response *version_res;
283 struct hbm_client_connect_response *connect_res;
284 struct hbm_client_connect_response *disconnect_res;
285 struct hbm_client_connect_request *disconnect_req;
286 struct hbm_flow_control *flow_control;
287 struct hbm_props_response *props_res;
288 struct hbm_host_enum_response *enum_res;
289 struct hbm_host_stop_request *stop_req;
290
291 /* read the message to our buffer */
292 BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf));
293 mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
294 mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;
295
296 switch (mei_msg->hbm_cmd) {
297 case HOST_START_RES_CMD:
298 version_res = (struct hbm_host_version_response *)mei_msg;
299 if (version_res->host_version_supported) {
300 dev->version.major_version = HBM_MAJOR_VERSION;
301 dev->version.minor_version = HBM_MINOR_VERSION;
302 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
303 dev->init_clients_state == MEI_START_MESSAGE) {
304 dev->init_clients_timer = 0;
305 mei_host_enum_clients_message(dev);
306 } else {
307 dev->recvd_msg = false;
308 dev_dbg(&dev->pdev->dev, "IMEI reset due to received host start response bus message.\n");
309 mei_reset(dev, 1);
310 return;
311 }
312 } else {
313 u32 *buf = dev->wr_msg_buf;
314 const size_t len = sizeof(struct hbm_host_stop_request);
315
316 dev->version = version_res->me_max_version;
317
318 /* send stop message */
319 hdr = mei_hbm_hdr(&buf[0], len);
320 stop_req = (struct hbm_host_stop_request *)&buf[1];
321 memset(stop_req, 0, len);
322 stop_req->hbm_cmd = HOST_STOP_REQ_CMD;
323 stop_req->reason = DRIVER_STOP_REQUEST;
324
325 mei_write_message(dev, hdr, (unsigned char *)stop_req);
326 dev_dbg(&dev->pdev->dev, "version mismatch.\n");
327 return;
328 }
329
330 dev->recvd_msg = true;
331 dev_dbg(&dev->pdev->dev, "host start response message received.\n");
332 break;
333
334 case CLIENT_CONNECT_RES_CMD:
335 connect_res = (struct hbm_client_connect_response *) mei_msg;
336 mei_client_connect_response(dev, connect_res);
337 dev_dbg(&dev->pdev->dev, "client connect response message received.\n");
338 wake_up(&dev->wait_recvd_msg);
339 break;
340
341 case CLIENT_DISCONNECT_RES_CMD:
342 disconnect_res = (struct hbm_client_connect_response *) mei_msg;
343 mei_client_disconnect_response(dev, disconnect_res);
344 dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n");
345 wake_up(&dev->wait_recvd_msg);
346 break;
347
348 case MEI_FLOW_CONTROL_CMD:
349 flow_control = (struct hbm_flow_control *) mei_msg;
350 mei_client_flow_control_response(dev, flow_control);
351 dev_dbg(&dev->pdev->dev, "client flow control response message received.\n");
352 break;
353
354 case HOST_CLIENT_PROPERTIES_RES_CMD:
355 props_res = (struct hbm_props_response *)mei_msg;
356 me_client = &dev->me_clients[dev->me_client_presentation_num];
357
358 if (props_res->status || !dev->me_clients) {
359 dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n");
360 mei_reset(dev, 1);
361 return;
362 }
363
364 if (me_client->client_id != props_res->address) {
365 dev_err(&dev->pdev->dev,
366 "Host client properties reply mismatch\n");
367 mei_reset(dev, 1);
368
369 return;
370 }
371
372 if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
373 dev->init_clients_state != MEI_CLIENT_PROPERTIES_MESSAGE) {
374 dev_err(&dev->pdev->dev,
375 "Unexpected client properties reply\n");
376 mei_reset(dev, 1);
377
378 return;
379 }
380
381 me_client->props = props_res->client_properties;
382 dev->me_client_index++;
383 dev->me_client_presentation_num++;
384
385 mei_host_client_enumerate(dev);
386
387 break;
388
389 case HOST_ENUM_RES_CMD:
390 enum_res = (struct hbm_host_enum_response *) mei_msg;
391 memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
392 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
393 dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) {
394 dev->init_clients_timer = 0;
395 dev->me_client_presentation_num = 0;
396 dev->me_client_index = 0;
397 mei_allocate_me_clients_storage(dev);
398 dev->init_clients_state =
399 MEI_CLIENT_PROPERTIES_MESSAGE;
400
401 mei_host_client_enumerate(dev);
402 } else {
403 dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n");
404 mei_reset(dev, 1);
405 return;
406 }
407 break;
408
409 case HOST_STOP_RES_CMD:
410 dev->dev_state = MEI_DEV_DISABLED;
411 dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n");
412 mei_reset(dev, 1);
413 break;
414
415 case CLIENT_DISCONNECT_REQ_CMD:
416 /* search for client */
417 disconnect_req = (struct hbm_client_connect_request *)mei_msg;
418 mei_client_disconnect_request(dev, disconnect_req);
419 break;
420
421 case ME_STOP_REQ_CMD:
422 {
423 /* prepare stop request: sent in next interrupt event */
424
425 const size_t len = sizeof(struct hbm_host_stop_request);
426
427 hdr = mei_hbm_hdr((u32 *)&dev->wr_ext_msg.hdr, len);
428 stop_req = (struct hbm_host_stop_request *)&dev->wr_ext_msg.data;
429 memset(stop_req, 0, len);
430 stop_req->hbm_cmd = HOST_STOP_REQ_CMD;
431 stop_req->reason = DRIVER_STOP_REQUEST;
432 break;
433 }
434 default:
435 BUG();
436 break;
437
438 }
439}
440