blob: fb9e63ba3bb1b2495897d0f139d308425d3d2ac9 [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"
Tomas Winkler0edb23f2013-01-08 23:07:12 +020023#include "hbm.h"
Tomas Winkler9dc64d62013-01-08 23:07:17 +020024#include "hw-me.h"
Tomas Winklerbb1b0132012-12-25 19:06:07 +020025
26/**
Tomas Winklera40b2602013-01-08 23:07:19 +020027 * mei_hbm_me_cl_allocate - allocates storage for me clients
28 *
29 * @dev: the device structure
30 *
31 * returns none.
32 */
33static void mei_hbm_me_cl_allocate(struct mei_device *dev)
34{
35 struct mei_me_client *clients;
36 int b;
37
38 /* count how many ME clients we have */
39 for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX)
40 dev->me_clients_num++;
41
42 if (dev->me_clients_num <= 0)
43 return;
44
45 kfree(dev->me_clients);
46 dev->me_clients = NULL;
47
48 dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%zd.\n",
49 dev->me_clients_num * sizeof(struct mei_me_client));
50 /* allocate storage for ME clients representation */
51 clients = kcalloc(dev->me_clients_num,
52 sizeof(struct mei_me_client), GFP_KERNEL);
53 if (!clients) {
54 dev_err(&dev->pdev->dev, "memory allocation for ME clients failed.\n");
55 dev->dev_state = MEI_DEV_RESETING;
56 mei_reset(dev, 1);
57 return;
58 }
59 dev->me_clients = clients;
60 return;
61}
62
63/**
Tomas Winklercd51ed62012-12-25 19:06:09 +020064 * mei_hbm_cl_hdr - construct client hbm header
65 * @cl: - client
66 * @hbm_cmd: host bus message command
67 * @buf: buffer for cl header
68 * @len: buffer length
69 */
70static inline
71void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
72{
73 struct mei_hbm_cl_cmd *cmd = buf;
74
75 memset(cmd, 0, len);
76
77 cmd->hbm_cmd = hbm_cmd;
78 cmd->host_addr = cl->host_client_id;
79 cmd->me_addr = cl->me_client_id;
80}
81
82/**
83 * same_disconn_addr - tells if they have the same address
84 *
85 * @file: private data of the file object.
86 * @disconn: disconnection request.
87 *
88 * returns true if addres are same
89 */
90static inline
91bool mei_hbm_cl_addr_equal(struct mei_cl *cl, void *buf)
92{
93 struct mei_hbm_cl_cmd *cmd = buf;
94 return cl->host_client_id == cmd->host_addr &&
95 cl->me_client_id == cmd->me_addr;
96}
97
98
99/**
Tomas Winkler6bbda152012-12-25 19:06:12 +0200100 * is_treat_specially_client - checks if the message belongs
101 * to the file private data.
102 *
103 * @cl: private data of the file object
104 * @rs: connect response bus message
105 *
106 */
107static bool is_treat_specially_client(struct mei_cl *cl,
108 struct hbm_client_connect_response *rs)
109{
110 if (mei_hbm_cl_addr_equal(cl, rs)) {
111 if (!rs->status) {
112 cl->state = MEI_FILE_CONNECTED;
113 cl->status = 0;
114
115 } else {
116 cl->state = MEI_FILE_DISCONNECTED;
117 cl->status = -ENODEV;
118 }
119 cl->timer_count = 0;
120
121 return true;
122 }
123 return false;
124}
125
126/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200127 * mei_hbm_start_req - sends start request message.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200128 *
129 * @dev: the device structure
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200130 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200131void mei_hbm_start_req(struct mei_device *dev)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200132{
Tomas Winklere46f1872012-12-25 19:06:10 +0200133 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200134 struct hbm_host_version_request *start_req;
135 const size_t len = sizeof(struct hbm_host_version_request);
136
Tomas Winklere46f1872012-12-25 19:06:10 +0200137 mei_hbm_hdr(mei_hdr, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200138
139 /* host start message */
Tomas Winklere46f1872012-12-25 19:06:10 +0200140 start_req = (struct hbm_host_version_request *)dev->wr_msg.data;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200141 memset(start_req, 0, len);
142 start_req->hbm_cmd = HOST_START_REQ_CMD;
143 start_req->host_version.major_version = HBM_MAJOR_VERSION;
144 start_req->host_version.minor_version = HBM_MINOR_VERSION;
145
146 dev->recvd_msg = false;
Tomas Winklere46f1872012-12-25 19:06:10 +0200147 if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200148 dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n");
149 dev->dev_state = MEI_DEV_RESETING;
150 mei_reset(dev, 1);
151 }
152 dev->init_clients_state = MEI_START_MESSAGE;
153 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
154 return ;
155}
156
157/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200158 * mei_hbm_enum_clients_req - sends enumeration client request message.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200159 *
160 * @dev: the device structure
161 *
162 * returns none.
163 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200164static void mei_hbm_enum_clients_req(struct mei_device *dev)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200165{
Tomas Winklere46f1872012-12-25 19:06:10 +0200166 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200167 struct hbm_host_enum_request *enum_req;
168 const size_t len = sizeof(struct hbm_host_enum_request);
169 /* enumerate clients */
Tomas Winklere46f1872012-12-25 19:06:10 +0200170 mei_hbm_hdr(mei_hdr, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200171
Tomas Winklere46f1872012-12-25 19:06:10 +0200172 enum_req = (struct hbm_host_enum_request *)dev->wr_msg.data;
173 memset(enum_req, 0, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200174 enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
175
Tomas Winklere46f1872012-12-25 19:06:10 +0200176 if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200177 dev->dev_state = MEI_DEV_RESETING;
178 dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
179 mei_reset(dev, 1);
180 }
181 dev->init_clients_state = MEI_ENUM_CLIENTS_MESSAGE;
182 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
183 return;
184}
185
Tomas Winkler8120e722012-12-25 19:06:11 +0200186/**
187 * mei_hbm_prop_requsest - request property for a single client
188 *
189 * @dev: the device structure
190 *
191 * returns none.
192 */
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200193
Tomas Winkler8120e722012-12-25 19:06:11 +0200194static int mei_hbm_prop_req(struct mei_device *dev)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200195{
196
Tomas Winklere46f1872012-12-25 19:06:10 +0200197 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200198 struct hbm_props_request *prop_req;
199 const size_t len = sizeof(struct hbm_props_request);
200 unsigned long next_client_index;
201 u8 client_num;
202
203
204 client_num = dev->me_client_presentation_num;
205
206 next_client_index = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX,
207 dev->me_client_index);
208
209 /* We got all client properties */
210 if (next_client_index == MEI_CLIENTS_MAX) {
211 schedule_work(&dev->init_work);
212
213 return 0;
214 }
215
216 dev->me_clients[client_num].client_id = next_client_index;
217 dev->me_clients[client_num].mei_flow_ctrl_creds = 0;
218
Tomas Winklere46f1872012-12-25 19:06:10 +0200219 mei_hbm_hdr(mei_hdr, len);
220 prop_req = (struct hbm_props_request *)dev->wr_msg.data;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200221
222 memset(prop_req, 0, sizeof(struct hbm_props_request));
223
224
225 prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
226 prop_req->address = next_client_index;
227
Tomas Winklere46f1872012-12-25 19:06:10 +0200228 if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200229 dev->dev_state = MEI_DEV_RESETING;
230 dev_err(&dev->pdev->dev, "Properties request command failed\n");
231 mei_reset(dev, 1);
232
233 return -EIO;
234 }
235
236 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
237 dev->me_client_index = next_client_index;
238
239 return 0;
240}
241
242/**
Tomas Winklere46f1872012-12-25 19:06:10 +0200243 * mei_hbm_stop_req_prepare - perpare stop request message
244 *
245 * @dev - mei device
246 * @mei_hdr - mei message header
247 * @data - hbm message body buffer
248 */
249static void mei_hbm_stop_req_prepare(struct mei_device *dev,
250 struct mei_msg_hdr *mei_hdr, unsigned char *data)
251{
252 struct hbm_host_stop_request *req =
253 (struct hbm_host_stop_request *)data;
254 const size_t len = sizeof(struct hbm_host_stop_request);
255
256 mei_hbm_hdr(mei_hdr, len);
257
258 memset(req, 0, len);
259 req->hbm_cmd = HOST_STOP_REQ_CMD;
260 req->reason = DRIVER_STOP_REQUEST;
261}
262
263/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200264 * mei_hbm_cl_flow_control_req - sends flow control requst.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200265 *
266 * @dev: the device structure
Tomas Winkler8120e722012-12-25 19:06:11 +0200267 * @cl: client info
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200268 *
269 * This function returns -EIO on write failure
270 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200271int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200272{
Tomas Winklere46f1872012-12-25 19:06:10 +0200273 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200274 const size_t len = sizeof(struct hbm_flow_control);
275
Tomas Winklere46f1872012-12-25 19:06:10 +0200276 mei_hbm_hdr(mei_hdr, len);
277 mei_hbm_cl_hdr(cl, MEI_FLOW_CONTROL_CMD, dev->wr_msg.data, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200278
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200279 dev_dbg(&dev->pdev->dev, "sending flow control host client = %d, ME client = %d\n",
280 cl->host_client_id, cl->me_client_id);
281
Tomas Winklere46f1872012-12-25 19:06:10 +0200282 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200283}
284
285/**
Tomas Winkler6bbda152012-12-25 19:06:12 +0200286 * add_single_flow_creds - adds single buffer credentials.
287 *
288 * @file: private data ot the file object.
289 * @flow: flow control.
290 */
291static void mei_hbm_add_single_flow_creds(struct mei_device *dev,
292 struct hbm_flow_control *flow)
293{
294 struct mei_me_client *client;
295 int i;
296
297 for (i = 0; i < dev->me_clients_num; i++) {
298 client = &dev->me_clients[i];
299 if (client && flow->me_addr == client->client_id) {
300 if (client->props.single_recv_buf) {
301 client->mei_flow_ctrl_creds++;
302 dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n",
303 flow->me_addr);
304 dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n",
305 client->mei_flow_ctrl_creds);
306 } else {
307 BUG(); /* error in flow control */
308 }
309 }
310 }
311}
312
313/**
314 * mei_hbm_cl_flow_control_res - flow control response from me
315 *
316 * @dev: the device structure
317 * @flow_control: flow control response bus message
318 */
319static void mei_hbm_cl_flow_control_res(struct mei_device *dev,
320 struct hbm_flow_control *flow_control)
321{
322 struct mei_cl *cl = NULL;
323 struct mei_cl *next = NULL;
324
325 if (!flow_control->host_addr) {
326 /* single receive buffer */
327 mei_hbm_add_single_flow_creds(dev, flow_control);
328 return;
329 }
330
331 /* normal connection */
332 list_for_each_entry_safe(cl, next, &dev->file_list, link) {
333 if (mei_hbm_cl_addr_equal(cl, flow_control)) {
334 cl->mei_flow_ctrl_creds++;
335 dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n",
336 flow_control->host_addr, flow_control->me_addr);
337 dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n",
338 cl->mei_flow_ctrl_creds);
339 break;
340 }
341 }
342}
343
344
345/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200346 * mei_hbm_cl_disconnect_req - sends disconnect message to fw.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200347 *
348 * @dev: the device structure
Tomas Winkler8120e722012-12-25 19:06:11 +0200349 * @cl: a client to disconnect from
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200350 *
351 * This function returns -EIO on write failure
352 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200353int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200354{
Tomas Winklere46f1872012-12-25 19:06:10 +0200355 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200356 const size_t len = sizeof(struct hbm_client_connect_request);
357
Tomas Winklere46f1872012-12-25 19:06:10 +0200358 mei_hbm_hdr(mei_hdr, len);
359 mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_REQ_CMD, dev->wr_msg.data, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200360
Tomas Winklere46f1872012-12-25 19:06:10 +0200361 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200362}
363
364/**
Tomas Winkler6bbda152012-12-25 19:06:12 +0200365 * mei_hbm_cl_disconnect_res - disconnect response from ME
366 *
367 * @dev: the device structure
368 * @rs: disconnect response bus message
369 */
370static void mei_hbm_cl_disconnect_res(struct mei_device *dev,
371 struct hbm_client_connect_response *rs)
372{
373 struct mei_cl *cl;
374 struct mei_cl_cb *pos = NULL, *next = NULL;
375
376 dev_dbg(&dev->pdev->dev,
377 "disconnect_response:\n"
378 "ME Client = %d\n"
379 "Host Client = %d\n"
380 "Status = %d\n",
381 rs->me_addr,
382 rs->host_addr,
383 rs->status);
384
385 list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
386 cl = pos->cl;
387
388 if (!cl) {
389 list_del(&pos->list);
390 return;
391 }
392
393 dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n");
394 if (mei_hbm_cl_addr_equal(cl, rs)) {
395 list_del(&pos->list);
396 if (!rs->status)
397 cl->state = MEI_FILE_DISCONNECTED;
398
399 cl->status = 0;
400 cl->timer_count = 0;
401 break;
402 }
403 }
404}
405
406/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200407 * mei_hbm_cl_connect_req - send connection request to specific me client
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200408 *
409 * @dev: the device structure
Tomas Winkler8120e722012-12-25 19:06:11 +0200410 * @cl: a client to connect to
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200411 *
Tomas Winkler8120e722012-12-25 19:06:11 +0200412 * returns -EIO on write failure
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200413 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200414int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200415{
Tomas Winklere46f1872012-12-25 19:06:10 +0200416 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200417 const size_t len = sizeof(struct hbm_client_connect_request);
418
Tomas Winklere46f1872012-12-25 19:06:10 +0200419 mei_hbm_hdr(mei_hdr, len);
420 mei_hbm_cl_hdr(cl, CLIENT_CONNECT_REQ_CMD, dev->wr_msg.data, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200421
Tomas Winklere46f1872012-12-25 19:06:10 +0200422 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200423}
424
425/**
Tomas Winkler6bbda152012-12-25 19:06:12 +0200426 * mei_hbm_cl_connect_res - connect resposne from the ME
427 *
428 * @dev: the device structure
429 * @rs: connect response bus message
430 */
431static void mei_hbm_cl_connect_res(struct mei_device *dev,
432 struct hbm_client_connect_response *rs)
433{
434
435 struct mei_cl *cl;
436 struct mei_cl_cb *pos = NULL, *next = NULL;
437
438 dev_dbg(&dev->pdev->dev,
439 "connect_response:\n"
440 "ME Client = %d\n"
441 "Host Client = %d\n"
442 "Status = %d\n",
443 rs->me_addr,
444 rs->host_addr,
445 rs->status);
446
447 /* if WD or iamthif client treat specially */
448
449 if (is_treat_specially_client(&dev->wd_cl, rs)) {
450 dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n");
451 mei_watchdog_register(dev);
452
453 return;
454 }
455
456 if (is_treat_specially_client(&dev->iamthif_cl, rs)) {
457 dev->iamthif_state = MEI_IAMTHIF_IDLE;
458 return;
459 }
460 list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
461
462 cl = pos->cl;
463 if (!cl) {
464 list_del(&pos->list);
465 return;
466 }
467 if (pos->fop_type == MEI_FOP_IOCTL) {
468 if (is_treat_specially_client(cl, rs)) {
469 list_del(&pos->list);
470 cl->status = 0;
471 cl->timer_count = 0;
472 break;
473 }
474 }
475 }
476}
477
478
479/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200480 * mei_client_disconnect_request - disconnect request initiated by me
481 * host sends disoconnect response
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200482 *
483 * @dev: the device structure.
Tomas Winkler8120e722012-12-25 19:06:11 +0200484 * @disconnect_req: disconnect request bus message from the me
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200485 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200486static void mei_hbm_fw_disconnect_req(struct mei_device *dev,
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200487 struct hbm_client_connect_request *disconnect_req)
488{
Tomas Winklercd51ed62012-12-25 19:06:09 +0200489 struct mei_cl *cl, *next;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200490 const size_t len = sizeof(struct hbm_client_connect_response);
491
Tomas Winklercd51ed62012-12-25 19:06:09 +0200492 list_for_each_entry_safe(cl, next, &dev->file_list, link) {
493 if (mei_hbm_cl_addr_equal(cl, disconnect_req)) {
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200494 dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
495 disconnect_req->host_addr,
496 disconnect_req->me_addr);
Tomas Winklercd51ed62012-12-25 19:06:09 +0200497 cl->state = MEI_FILE_DISCONNECTED;
498 cl->timer_count = 0;
499 if (cl == &dev->wd_cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200500 dev->wd_pending = false;
Tomas Winklercd51ed62012-12-25 19:06:09 +0200501 else if (cl == &dev->iamthif_cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200502 dev->iamthif_timer = 0;
503
504 /* prepare disconnect response */
Tomas Winklere46f1872012-12-25 19:06:10 +0200505 mei_hbm_hdr(&dev->wr_ext_msg.hdr, len);
Tomas Winklercd51ed62012-12-25 19:06:09 +0200506 mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD,
Tomas Winklere46f1872012-12-25 19:06:10 +0200507 dev->wr_ext_msg.data, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200508 break;
509 }
510 }
511}
512
513
514/**
515 * mei_hbm_dispatch - bottom half read routine after ISR to
516 * handle the read bus message cmd processing.
517 *
518 * @dev: the device structure
519 * @mei_hdr: header of bus message
520 */
521void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
522{
523 struct mei_bus_message *mei_msg;
524 struct mei_me_client *me_client;
525 struct hbm_host_version_response *version_res;
526 struct hbm_client_connect_response *connect_res;
527 struct hbm_client_connect_response *disconnect_res;
528 struct hbm_client_connect_request *disconnect_req;
529 struct hbm_flow_control *flow_control;
530 struct hbm_props_response *props_res;
531 struct hbm_host_enum_response *enum_res;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200532
533 /* read the message to our buffer */
534 BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf));
535 mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
536 mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;
537
538 switch (mei_msg->hbm_cmd) {
539 case HOST_START_RES_CMD:
540 version_res = (struct hbm_host_version_response *)mei_msg;
Tomas Winklere46f1872012-12-25 19:06:10 +0200541 if (!version_res->host_version_supported) {
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200542 dev->version = version_res->me_max_version;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200543 dev_dbg(&dev->pdev->dev, "version mismatch.\n");
Tomas Winklere46f1872012-12-25 19:06:10 +0200544
545 mei_hbm_stop_req_prepare(dev, &dev->wr_msg.hdr,
546 dev->wr_msg.data);
547 mei_write_message(dev, &dev->wr_msg.hdr,
548 dev->wr_msg.data);
549 return;
550 }
551
552 dev->version.major_version = HBM_MAJOR_VERSION;
553 dev->version.minor_version = HBM_MINOR_VERSION;
554 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
555 dev->init_clients_state == MEI_START_MESSAGE) {
556 dev->init_clients_timer = 0;
Tomas Winkler8120e722012-12-25 19:06:11 +0200557 mei_hbm_enum_clients_req(dev);
Tomas Winklere46f1872012-12-25 19:06:10 +0200558 } else {
559 dev->recvd_msg = false;
560 dev_dbg(&dev->pdev->dev, "reset due to received hbm: host start\n");
561 mei_reset(dev, 1);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200562 return;
563 }
564
565 dev->recvd_msg = true;
566 dev_dbg(&dev->pdev->dev, "host start response message received.\n");
567 break;
568
569 case CLIENT_CONNECT_RES_CMD:
570 connect_res = (struct hbm_client_connect_response *) mei_msg;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200571 mei_hbm_cl_connect_res(dev, connect_res);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200572 dev_dbg(&dev->pdev->dev, "client connect response message received.\n");
573 wake_up(&dev->wait_recvd_msg);
574 break;
575
576 case CLIENT_DISCONNECT_RES_CMD:
577 disconnect_res = (struct hbm_client_connect_response *) mei_msg;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200578 mei_hbm_cl_disconnect_res(dev, disconnect_res);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200579 dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n");
580 wake_up(&dev->wait_recvd_msg);
581 break;
582
583 case MEI_FLOW_CONTROL_CMD:
584 flow_control = (struct hbm_flow_control *) mei_msg;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200585 mei_hbm_cl_flow_control_res(dev, flow_control);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200586 dev_dbg(&dev->pdev->dev, "client flow control response message received.\n");
587 break;
588
589 case HOST_CLIENT_PROPERTIES_RES_CMD:
590 props_res = (struct hbm_props_response *)mei_msg;
591 me_client = &dev->me_clients[dev->me_client_presentation_num];
592
593 if (props_res->status || !dev->me_clients) {
594 dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n");
595 mei_reset(dev, 1);
596 return;
597 }
598
599 if (me_client->client_id != props_res->address) {
600 dev_err(&dev->pdev->dev,
601 "Host client properties reply mismatch\n");
602 mei_reset(dev, 1);
603
604 return;
605 }
606
607 if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
608 dev->init_clients_state != MEI_CLIENT_PROPERTIES_MESSAGE) {
609 dev_err(&dev->pdev->dev,
610 "Unexpected client properties reply\n");
611 mei_reset(dev, 1);
612
613 return;
614 }
615
616 me_client->props = props_res->client_properties;
617 dev->me_client_index++;
618 dev->me_client_presentation_num++;
619
Tomas Winkler8120e722012-12-25 19:06:11 +0200620 /* request property for the next client */
621 mei_hbm_prop_req(dev);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200622
623 break;
624
625 case HOST_ENUM_RES_CMD:
626 enum_res = (struct hbm_host_enum_response *) mei_msg;
627 memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
628 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
629 dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) {
630 dev->init_clients_timer = 0;
631 dev->me_client_presentation_num = 0;
632 dev->me_client_index = 0;
Tomas Winklera40b2602013-01-08 23:07:19 +0200633 mei_hbm_me_cl_allocate(dev);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200634 dev->init_clients_state =
635 MEI_CLIENT_PROPERTIES_MESSAGE;
636
Tomas Winkler8120e722012-12-25 19:06:11 +0200637 /* first property reqeust */
638 mei_hbm_prop_req(dev);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200639 } else {
640 dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n");
641 mei_reset(dev, 1);
642 return;
643 }
644 break;
645
646 case HOST_STOP_RES_CMD:
647 dev->dev_state = MEI_DEV_DISABLED;
648 dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n");
649 mei_reset(dev, 1);
650 break;
651
652 case CLIENT_DISCONNECT_REQ_CMD:
653 /* search for client */
654 disconnect_req = (struct hbm_client_connect_request *)mei_msg;
Tomas Winkler8120e722012-12-25 19:06:11 +0200655 mei_hbm_fw_disconnect_req(dev, disconnect_req);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200656 break;
657
658 case ME_STOP_REQ_CMD:
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200659
Tomas Winklere46f1872012-12-25 19:06:10 +0200660 mei_hbm_stop_req_prepare(dev, &dev->wr_ext_msg.hdr,
661 dev->wr_ext_msg.data);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200662 break;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200663 default:
664 BUG();
665 break;
666
667 }
668}
669