blob: 4de80d9b7c4574efc6be0fd2f8c17628a094e8c0 [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
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300126int mei_hbm_start_wait(struct mei_device *dev)
127{
128 int ret;
129 if (dev->hbm_state > MEI_HBM_START)
130 return 0;
131
132 mutex_unlock(&dev->device_lock);
133 ret = wait_event_interruptible_timeout(dev->wait_recvd_msg,
134 dev->hbm_state == MEI_HBM_IDLE ||
135 dev->hbm_state > MEI_HBM_START,
136 mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT));
137 mutex_lock(&dev->device_lock);
138
139 if (ret <= 0 && (dev->hbm_state <= MEI_HBM_START)) {
140 dev->hbm_state = MEI_HBM_IDLE;
141 dev_err(&dev->pdev->dev, "wating for mei start failed\n");
142 return -ETIMEDOUT;
143 }
144 return 0;
145}
146
Tomas Winkler6bbda152012-12-25 19:06:12 +0200147/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200148 * mei_hbm_start_req - sends start request message.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200149 *
150 * @dev: the device structure
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200151 */
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300152int mei_hbm_start_req(struct mei_device *dev)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200153{
Tomas Winklere46f1872012-12-25 19:06:10 +0200154 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200155 struct hbm_host_version_request *start_req;
156 const size_t len = sizeof(struct hbm_host_version_request);
157
Tomas Winklere46f1872012-12-25 19:06:10 +0200158 mei_hbm_hdr(mei_hdr, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200159
160 /* host start message */
Tomas Winklere46f1872012-12-25 19:06:10 +0200161 start_req = (struct hbm_host_version_request *)dev->wr_msg.data;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200162 memset(start_req, 0, len);
163 start_req->hbm_cmd = HOST_START_REQ_CMD;
164 start_req->host_version.major_version = HBM_MAJOR_VERSION;
165 start_req->host_version.minor_version = HBM_MINOR_VERSION;
166
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300167 dev->hbm_state = MEI_HBM_IDLE;
Tomas Winklere46f1872012-12-25 19:06:10 +0200168 if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
Tomas Winklerd6c36a42013-04-08 21:56:38 +0300169 dev_err(&dev->pdev->dev, "version message writet failed\n");
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200170 dev->dev_state = MEI_DEV_RESETING;
171 mei_reset(dev, 1);
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300172 return -ENODEV;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200173 }
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300174 dev->hbm_state = MEI_HBM_START;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200175 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300176 return 0;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200177}
178
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300179/*
Tomas Winkler8120e722012-12-25 19:06:11 +0200180 * mei_hbm_enum_clients_req - sends enumeration client request message.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200181 *
182 * @dev: the device structure
183 *
184 * returns none.
185 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200186static void mei_hbm_enum_clients_req(struct mei_device *dev)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200187{
Tomas Winklere46f1872012-12-25 19:06:10 +0200188 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200189 struct hbm_host_enum_request *enum_req;
190 const size_t len = sizeof(struct hbm_host_enum_request);
191 /* enumerate clients */
Tomas Winklere46f1872012-12-25 19:06:10 +0200192 mei_hbm_hdr(mei_hdr, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200193
Tomas Winklere46f1872012-12-25 19:06:10 +0200194 enum_req = (struct hbm_host_enum_request *)dev->wr_msg.data;
195 memset(enum_req, 0, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200196 enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
197
Tomas Winklere46f1872012-12-25 19:06:10 +0200198 if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200199 dev->dev_state = MEI_DEV_RESETING;
Tomas Winklerd6c36a42013-04-08 21:56:38 +0300200 dev_err(&dev->pdev->dev, "enumeration request write failed.\n");
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200201 mei_reset(dev, 1);
202 }
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300203 dev->hbm_state = MEI_HBM_ENUM_CLIENTS;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200204 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
205 return;
206}
207
Tomas Winkler8120e722012-12-25 19:06:11 +0200208/**
209 * mei_hbm_prop_requsest - request property for a single client
210 *
211 * @dev: the device structure
212 *
213 * returns none.
214 */
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200215
Tomas Winkler8120e722012-12-25 19:06:11 +0200216static int mei_hbm_prop_req(struct mei_device *dev)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200217{
218
Tomas Winklere46f1872012-12-25 19:06:10 +0200219 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200220 struct hbm_props_request *prop_req;
221 const size_t len = sizeof(struct hbm_props_request);
222 unsigned long next_client_index;
223 u8 client_num;
224
225
226 client_num = dev->me_client_presentation_num;
227
228 next_client_index = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX,
229 dev->me_client_index);
230
231 /* We got all client properties */
232 if (next_client_index == MEI_CLIENTS_MAX) {
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300233 dev->hbm_state = MEI_HBM_STARTED;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200234 schedule_work(&dev->init_work);
235
236 return 0;
237 }
238
239 dev->me_clients[client_num].client_id = next_client_index;
240 dev->me_clients[client_num].mei_flow_ctrl_creds = 0;
241
Tomas Winklere46f1872012-12-25 19:06:10 +0200242 mei_hbm_hdr(mei_hdr, len);
243 prop_req = (struct hbm_props_request *)dev->wr_msg.data;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200244
245 memset(prop_req, 0, sizeof(struct hbm_props_request));
246
247
248 prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
249 prop_req->address = next_client_index;
250
Tomas Winklere46f1872012-12-25 19:06:10 +0200251 if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200252 dev->dev_state = MEI_DEV_RESETING;
Tomas Winklerd6c36a42013-04-08 21:56:38 +0300253 dev_err(&dev->pdev->dev, "properties request write failed\n");
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200254 mei_reset(dev, 1);
255
256 return -EIO;
257 }
258
259 dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
260 dev->me_client_index = next_client_index;
261
262 return 0;
263}
264
265/**
Tomas Winklere46f1872012-12-25 19:06:10 +0200266 * mei_hbm_stop_req_prepare - perpare stop request message
267 *
268 * @dev - mei device
269 * @mei_hdr - mei message header
270 * @data - hbm message body buffer
271 */
272static void mei_hbm_stop_req_prepare(struct mei_device *dev,
273 struct mei_msg_hdr *mei_hdr, unsigned char *data)
274{
275 struct hbm_host_stop_request *req =
276 (struct hbm_host_stop_request *)data;
277 const size_t len = sizeof(struct hbm_host_stop_request);
278
279 mei_hbm_hdr(mei_hdr, len);
280
281 memset(req, 0, len);
282 req->hbm_cmd = HOST_STOP_REQ_CMD;
283 req->reason = DRIVER_STOP_REQUEST;
284}
285
286/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200287 * mei_hbm_cl_flow_control_req - sends flow control requst.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200288 *
289 * @dev: the device structure
Tomas Winkler8120e722012-12-25 19:06:11 +0200290 * @cl: client info
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200291 *
292 * This function returns -EIO on write failure
293 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200294int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200295{
Tomas Winklere46f1872012-12-25 19:06:10 +0200296 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200297 const size_t len = sizeof(struct hbm_flow_control);
298
Tomas Winklere46f1872012-12-25 19:06:10 +0200299 mei_hbm_hdr(mei_hdr, len);
300 mei_hbm_cl_hdr(cl, MEI_FLOW_CONTROL_CMD, dev->wr_msg.data, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200301
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200302 dev_dbg(&dev->pdev->dev, "sending flow control host client = %d, ME client = %d\n",
303 cl->host_client_id, cl->me_client_id);
304
Tomas Winklere46f1872012-12-25 19:06:10 +0200305 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200306}
307
308/**
Tomas Winkler6bbda152012-12-25 19:06:12 +0200309 * add_single_flow_creds - adds single buffer credentials.
310 *
311 * @file: private data ot the file object.
312 * @flow: flow control.
313 */
314static void mei_hbm_add_single_flow_creds(struct mei_device *dev,
315 struct hbm_flow_control *flow)
316{
317 struct mei_me_client *client;
318 int i;
319
320 for (i = 0; i < dev->me_clients_num; i++) {
321 client = &dev->me_clients[i];
322 if (client && flow->me_addr == client->client_id) {
323 if (client->props.single_recv_buf) {
324 client->mei_flow_ctrl_creds++;
325 dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n",
326 flow->me_addr);
327 dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n",
328 client->mei_flow_ctrl_creds);
329 } else {
330 BUG(); /* error in flow control */
331 }
332 }
333 }
334}
335
336/**
337 * mei_hbm_cl_flow_control_res - flow control response from me
338 *
339 * @dev: the device structure
340 * @flow_control: flow control response bus message
341 */
342static void mei_hbm_cl_flow_control_res(struct mei_device *dev,
343 struct hbm_flow_control *flow_control)
344{
345 struct mei_cl *cl = NULL;
346 struct mei_cl *next = NULL;
347
348 if (!flow_control->host_addr) {
349 /* single receive buffer */
350 mei_hbm_add_single_flow_creds(dev, flow_control);
351 return;
352 }
353
354 /* normal connection */
355 list_for_each_entry_safe(cl, next, &dev->file_list, link) {
356 if (mei_hbm_cl_addr_equal(cl, flow_control)) {
357 cl->mei_flow_ctrl_creds++;
358 dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n",
359 flow_control->host_addr, flow_control->me_addr);
360 dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n",
361 cl->mei_flow_ctrl_creds);
362 break;
363 }
364 }
365}
366
367
368/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200369 * mei_hbm_cl_disconnect_req - sends disconnect message to fw.
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200370 *
371 * @dev: the device structure
Tomas Winkler8120e722012-12-25 19:06:11 +0200372 * @cl: a client to disconnect from
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200373 *
374 * This function returns -EIO on write failure
375 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200376int mei_hbm_cl_disconnect_req(struct mei_device *dev, struct mei_cl *cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200377{
Tomas Winklere46f1872012-12-25 19:06:10 +0200378 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200379 const size_t len = sizeof(struct hbm_client_connect_request);
380
Tomas Winklere46f1872012-12-25 19:06:10 +0200381 mei_hbm_hdr(mei_hdr, len);
382 mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_REQ_CMD, dev->wr_msg.data, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200383
Tomas Winklere46f1872012-12-25 19:06:10 +0200384 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200385}
386
387/**
Tomas Winkler6bbda152012-12-25 19:06:12 +0200388 * mei_hbm_cl_disconnect_res - disconnect response from ME
389 *
390 * @dev: the device structure
391 * @rs: disconnect response bus message
392 */
393static void mei_hbm_cl_disconnect_res(struct mei_device *dev,
394 struct hbm_client_connect_response *rs)
395{
396 struct mei_cl *cl;
397 struct mei_cl_cb *pos = NULL, *next = NULL;
398
399 dev_dbg(&dev->pdev->dev,
400 "disconnect_response:\n"
401 "ME Client = %d\n"
402 "Host Client = %d\n"
403 "Status = %d\n",
404 rs->me_addr,
405 rs->host_addr,
406 rs->status);
407
408 list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
409 cl = pos->cl;
410
411 if (!cl) {
412 list_del(&pos->list);
413 return;
414 }
415
416 dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n");
417 if (mei_hbm_cl_addr_equal(cl, rs)) {
418 list_del(&pos->list);
419 if (!rs->status)
420 cl->state = MEI_FILE_DISCONNECTED;
421
422 cl->status = 0;
423 cl->timer_count = 0;
424 break;
425 }
426 }
427}
428
429/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200430 * mei_hbm_cl_connect_req - send connection request to specific me client
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200431 *
432 * @dev: the device structure
Tomas Winkler8120e722012-12-25 19:06:11 +0200433 * @cl: a client to connect to
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200434 *
Tomas Winkler8120e722012-12-25 19:06:11 +0200435 * returns -EIO on write failure
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200436 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200437int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200438{
Tomas Winklere46f1872012-12-25 19:06:10 +0200439 struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200440 const size_t len = sizeof(struct hbm_client_connect_request);
441
Tomas Winklere46f1872012-12-25 19:06:10 +0200442 mei_hbm_hdr(mei_hdr, len);
443 mei_hbm_cl_hdr(cl, CLIENT_CONNECT_REQ_CMD, dev->wr_msg.data, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200444
Tomas Winklere46f1872012-12-25 19:06:10 +0200445 return mei_write_message(dev, mei_hdr, dev->wr_msg.data);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200446}
447
448/**
Tomas Winkler6bbda152012-12-25 19:06:12 +0200449 * mei_hbm_cl_connect_res - connect resposne from the ME
450 *
451 * @dev: the device structure
452 * @rs: connect response bus message
453 */
454static void mei_hbm_cl_connect_res(struct mei_device *dev,
455 struct hbm_client_connect_response *rs)
456{
457
458 struct mei_cl *cl;
459 struct mei_cl_cb *pos = NULL, *next = NULL;
460
461 dev_dbg(&dev->pdev->dev,
462 "connect_response:\n"
463 "ME Client = %d\n"
464 "Host Client = %d\n"
465 "Status = %d\n",
466 rs->me_addr,
467 rs->host_addr,
468 rs->status);
469
470 /* if WD or iamthif client treat specially */
471
472 if (is_treat_specially_client(&dev->wd_cl, rs)) {
473 dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n");
474 mei_watchdog_register(dev);
475
476 return;
477 }
478
479 if (is_treat_specially_client(&dev->iamthif_cl, rs)) {
480 dev->iamthif_state = MEI_IAMTHIF_IDLE;
481 return;
482 }
483 list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
484
485 cl = pos->cl;
486 if (!cl) {
487 list_del(&pos->list);
488 return;
489 }
490 if (pos->fop_type == MEI_FOP_IOCTL) {
491 if (is_treat_specially_client(cl, rs)) {
492 list_del(&pos->list);
493 cl->status = 0;
494 cl->timer_count = 0;
495 break;
496 }
497 }
498 }
499}
500
501
502/**
Tomas Winkler8120e722012-12-25 19:06:11 +0200503 * mei_client_disconnect_request - disconnect request initiated by me
504 * host sends disoconnect response
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200505 *
506 * @dev: the device structure.
Tomas Winkler8120e722012-12-25 19:06:11 +0200507 * @disconnect_req: disconnect request bus message from the me
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200508 */
Tomas Winkler8120e722012-12-25 19:06:11 +0200509static void mei_hbm_fw_disconnect_req(struct mei_device *dev,
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200510 struct hbm_client_connect_request *disconnect_req)
511{
Tomas Winklercd51ed62012-12-25 19:06:09 +0200512 struct mei_cl *cl, *next;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200513 const size_t len = sizeof(struct hbm_client_connect_response);
514
Tomas Winklercd51ed62012-12-25 19:06:09 +0200515 list_for_each_entry_safe(cl, next, &dev->file_list, link) {
516 if (mei_hbm_cl_addr_equal(cl, disconnect_req)) {
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200517 dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
518 disconnect_req->host_addr,
519 disconnect_req->me_addr);
Tomas Winklercd51ed62012-12-25 19:06:09 +0200520 cl->state = MEI_FILE_DISCONNECTED;
521 cl->timer_count = 0;
522 if (cl == &dev->wd_cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200523 dev->wd_pending = false;
Tomas Winklercd51ed62012-12-25 19:06:09 +0200524 else if (cl == &dev->iamthif_cl)
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200525 dev->iamthif_timer = 0;
526
527 /* prepare disconnect response */
Tomas Winklere46f1872012-12-25 19:06:10 +0200528 mei_hbm_hdr(&dev->wr_ext_msg.hdr, len);
Tomas Winklercd51ed62012-12-25 19:06:09 +0200529 mei_hbm_cl_hdr(cl, CLIENT_DISCONNECT_RES_CMD,
Tomas Winklere46f1872012-12-25 19:06:10 +0200530 dev->wr_ext_msg.data, len);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200531 break;
532 }
533 }
534}
535
536
537/**
538 * mei_hbm_dispatch - bottom half read routine after ISR to
539 * handle the read bus message cmd processing.
540 *
541 * @dev: the device structure
542 * @mei_hdr: header of bus message
543 */
544void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
545{
546 struct mei_bus_message *mei_msg;
547 struct mei_me_client *me_client;
548 struct hbm_host_version_response *version_res;
549 struct hbm_client_connect_response *connect_res;
550 struct hbm_client_connect_response *disconnect_res;
551 struct hbm_client_connect_request *disconnect_req;
552 struct hbm_flow_control *flow_control;
553 struct hbm_props_response *props_res;
554 struct hbm_host_enum_response *enum_res;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200555
556 /* read the message to our buffer */
557 BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf));
558 mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
559 mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;
560
561 switch (mei_msg->hbm_cmd) {
562 case HOST_START_RES_CMD:
563 version_res = (struct hbm_host_version_response *)mei_msg;
Tomas Winklere46f1872012-12-25 19:06:10 +0200564 if (!version_res->host_version_supported) {
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200565 dev->version = version_res->me_max_version;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200566 dev_dbg(&dev->pdev->dev, "version mismatch.\n");
Tomas Winklere46f1872012-12-25 19:06:10 +0200567
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300568 dev->hbm_state = MEI_HBM_STOP;
Tomas Winklere46f1872012-12-25 19:06:10 +0200569 mei_hbm_stop_req_prepare(dev, &dev->wr_msg.hdr,
570 dev->wr_msg.data);
571 mei_write_message(dev, &dev->wr_msg.hdr,
572 dev->wr_msg.data);
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300573
Tomas Winklere46f1872012-12-25 19:06:10 +0200574 return;
575 }
576
577 dev->version.major_version = HBM_MAJOR_VERSION;
578 dev->version.minor_version = HBM_MINOR_VERSION;
579 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300580 dev->hbm_state == MEI_HBM_START) {
Tomas Winklere46f1872012-12-25 19:06:10 +0200581 dev->init_clients_timer = 0;
Tomas Winkler8120e722012-12-25 19:06:11 +0200582 mei_hbm_enum_clients_req(dev);
Tomas Winklere46f1872012-12-25 19:06:10 +0200583 } else {
Tomas Winklerd6c36a42013-04-08 21:56:38 +0300584 dev_err(&dev->pdev->dev, "reset: wrong host start response\n");
Tomas Winklere46f1872012-12-25 19:06:10 +0200585 mei_reset(dev, 1);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200586 return;
587 }
588
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300589 wake_up_interruptible(&dev->wait_recvd_msg);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200590 dev_dbg(&dev->pdev->dev, "host start response message received.\n");
591 break;
592
593 case CLIENT_CONNECT_RES_CMD:
594 connect_res = (struct hbm_client_connect_response *) mei_msg;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200595 mei_hbm_cl_connect_res(dev, connect_res);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200596 dev_dbg(&dev->pdev->dev, "client connect response message received.\n");
597 wake_up(&dev->wait_recvd_msg);
598 break;
599
600 case CLIENT_DISCONNECT_RES_CMD:
601 disconnect_res = (struct hbm_client_connect_response *) mei_msg;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200602 mei_hbm_cl_disconnect_res(dev, disconnect_res);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200603 dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n");
604 wake_up(&dev->wait_recvd_msg);
605 break;
606
607 case MEI_FLOW_CONTROL_CMD:
608 flow_control = (struct hbm_flow_control *) mei_msg;
Tomas Winkler6bbda152012-12-25 19:06:12 +0200609 mei_hbm_cl_flow_control_res(dev, flow_control);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200610 dev_dbg(&dev->pdev->dev, "client flow control response message received.\n");
611 break;
612
613 case HOST_CLIENT_PROPERTIES_RES_CMD:
614 props_res = (struct hbm_props_response *)mei_msg;
615 me_client = &dev->me_clients[dev->me_client_presentation_num];
616
617 if (props_res->status || !dev->me_clients) {
Tomas Winklerd6c36a42013-04-08 21:56:38 +0300618 dev_err(&dev->pdev->dev, "reset: properties response hbm wrong status.\n");
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200619 mei_reset(dev, 1);
620 return;
621 }
622
623 if (me_client->client_id != props_res->address) {
Tomas Winklerd6c36a42013-04-08 21:56:38 +0300624 dev_err(&dev->pdev->dev, "reset: host properties response address mismatch\n");
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200625 mei_reset(dev, 1);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200626 return;
627 }
628
629 if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300630 dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) {
Tomas Winklerd6c36a42013-04-08 21:56:38 +0300631 dev_err(&dev->pdev->dev, "reset: unexpected properties response\n");
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200632 mei_reset(dev, 1);
633
634 return;
635 }
636
637 me_client->props = props_res->client_properties;
638 dev->me_client_index++;
639 dev->me_client_presentation_num++;
640
Tomas Winkler8120e722012-12-25 19:06:11 +0200641 /* request property for the next client */
642 mei_hbm_prop_req(dev);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200643
644 break;
645
646 case HOST_ENUM_RES_CMD:
647 enum_res = (struct hbm_host_enum_response *) mei_msg;
648 memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
649 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300650 dev->hbm_state == MEI_HBM_ENUM_CLIENTS) {
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200651 dev->init_clients_timer = 0;
652 dev->me_client_presentation_num = 0;
653 dev->me_client_index = 0;
Tomas Winklera40b2602013-01-08 23:07:19 +0200654 mei_hbm_me_cl_allocate(dev);
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300655 dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200656
Tomas Winkler8120e722012-12-25 19:06:11 +0200657 /* first property reqeust */
658 mei_hbm_prop_req(dev);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200659 } else {
Tomas Winklerd6c36a42013-04-08 21:56:38 +0300660 dev_err(&dev->pdev->dev, "reset: unexpected enumeration response hbm.\n");
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200661 mei_reset(dev, 1);
662 return;
663 }
664 break;
665
666 case HOST_STOP_RES_CMD:
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300667
668 if (dev->hbm_state != MEI_HBM_STOP)
669 dev_err(&dev->pdev->dev, "unexpected stop response hbm.\n");
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200670 dev->dev_state = MEI_DEV_DISABLED;
Tomas Winklerd6c36a42013-04-08 21:56:38 +0300671 dev_info(&dev->pdev->dev, "reset: FW stop response.\n");
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200672 mei_reset(dev, 1);
673 break;
674
675 case CLIENT_DISCONNECT_REQ_CMD:
676 /* search for client */
677 disconnect_req = (struct hbm_client_connect_request *)mei_msg;
Tomas Winkler8120e722012-12-25 19:06:11 +0200678 mei_hbm_fw_disconnect_req(dev, disconnect_req);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200679 break;
680
681 case ME_STOP_REQ_CMD:
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200682
Tomas Winkler9b0d5ef2013-04-18 23:03:48 +0300683 dev->hbm_state = MEI_HBM_STOP;
Tomas Winklere46f1872012-12-25 19:06:10 +0200684 mei_hbm_stop_req_prepare(dev, &dev->wr_ext_msg.hdr,
685 dev->wr_ext_msg.data);
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200686 break;
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200687 default:
688 BUG();
689 break;
690
691 }
692}
693