blob: cd89b68fcf43f4e595eb0568af03e6eb08496e89 [file] [log] [blame]
Oren Weilfb7d8792011-05-15 13:43:42 +03001/*
2 *
3 * Intel Management Engine Interface (Intel MEI) Linux driver
Tomas Winkler733ba912012-02-09 19:25:53 +02004 * Copyright (c) 2003-2012, Intel Corporation.
Oren Weilfb7d8792011-05-15 13:43:42 +03005 *
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
18#include <linux/pci.h>
19#include <linux/kthread.h>
20#include <linux/interrupt.h>
21#include <linux/fs.h>
22#include <linux/jiffies.h>
23
Tomas Winkler4f3afe12012-05-09 16:38:59 +030024#include <linux/mei.h>
Tomas Winkler47a73802012-12-25 19:06:03 +020025
26#include "mei_dev.h"
Oren Weilfb7d8792011-05-15 13:43:42 +030027#include "interface.h"
28
29
30/**
Oren Weilfb7d8792011-05-15 13:43:42 +030031 * _mei_cmpl - processes completed operation.
32 *
33 * @cl: private data of the file object.
34 * @cb_pos: callback block.
35 */
36static void _mei_cmpl(struct mei_cl *cl, struct mei_cl_cb *cb_pos)
37{
Tomas Winkler4b8960b2012-11-11 17:38:00 +020038 if (cb_pos->fop_type == MEI_FOP_WRITE) {
Tomas Winkler601a1ef2012-10-09 16:50:20 +020039 mei_io_cb_free(cb_pos);
Oren Weilfb7d8792011-05-15 13:43:42 +030040 cb_pos = NULL;
41 cl->writing_state = MEI_WRITE_COMPLETE;
42 if (waitqueue_active(&cl->tx_wait))
43 wake_up_interruptible(&cl->tx_wait);
44
Tomas Winkler4b8960b2012-11-11 17:38:00 +020045 } else if (cb_pos->fop_type == MEI_FOP_READ &&
Oren Weilfb7d8792011-05-15 13:43:42 +030046 MEI_READING == cl->reading_state) {
47 cl->reading_state = MEI_READ_COMPLETE;
48 if (waitqueue_active(&cl->rx_wait))
49 wake_up_interruptible(&cl->rx_wait);
50
51 }
52}
53
54/**
Oren Weilfb7d8792011-05-15 13:43:42 +030055 * _mei_irq_thread_state_ok - checks if mei header matches file private data
56 *
57 * @cl: private data of the file object
58 * @mei_hdr: header of mei client message
59 *
60 * returns !=0 if matches, 0 if no match.
61 */
62static int _mei_irq_thread_state_ok(struct mei_cl *cl,
63 struct mei_msg_hdr *mei_hdr)
64{
65 return (cl->host_client_id == mei_hdr->host_addr &&
66 cl->me_client_id == mei_hdr->me_addr &&
67 cl->state == MEI_FILE_CONNECTED &&
68 MEI_READ_COMPLETE != cl->reading_state);
69}
70
71/**
72 * mei_irq_thread_read_client_message - bottom half read routine after ISR to
73 * handle the read mei client message data processing.
74 *
75 * @complete_list: An instance of our list structure
76 * @dev: the device structure
77 * @mei_hdr: header of mei client message
78 *
79 * returns 0 on success, <0 on failure.
80 */
Tomas Winklerfb601ad2012-10-15 12:06:48 +020081static int mei_irq_thread_read_client_message(struct mei_cl_cb *complete_list,
Oren Weilfb7d8792011-05-15 13:43:42 +030082 struct mei_device *dev,
83 struct mei_msg_hdr *mei_hdr)
84{
85 struct mei_cl *cl;
86 struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
Tomas Winkler479bc592011-06-16 00:46:03 +030087 unsigned char *buffer = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +030088
89 dev_dbg(&dev->pdev->dev, "start client msg\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +020090 if (list_empty(&dev->read_list.list))
Oren Weilfb7d8792011-05-15 13:43:42 +030091 goto quit;
92
Tomas Winklerfb601ad2012-10-15 12:06:48 +020093 list_for_each_entry_safe(cb_pos, cb_next, &dev->read_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +020094 cl = cb_pos->cl;
Oren Weilfb7d8792011-05-15 13:43:42 +030095 if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) {
96 cl->reading_state = MEI_READING;
Tomas Winklerebb108ef2012-10-09 16:50:16 +020097 buffer = cb_pos->response_buffer.data + cb_pos->buf_idx;
Oren Weilfb7d8792011-05-15 13:43:42 +030098
99 if (cb_pos->response_buffer.size <
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200100 mei_hdr->length + cb_pos->buf_idx) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300101 dev_dbg(&dev->pdev->dev, "message overflow.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200102 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300103 return -ENOMEM;
104 }
105 if (buffer)
106 mei_read_slots(dev, buffer, mei_hdr->length);
107
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200108 cb_pos->buf_idx += mei_hdr->length;
Oren Weilfb7d8792011-05-15 13:43:42 +0300109 if (mei_hdr->msg_complete) {
110 cl->status = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200111 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300112 dev_dbg(&dev->pdev->dev,
Tomas Winklera4136b42012-09-11 00:43:22 +0300113 "completed read H cl = %d, ME cl = %d, length = %lu\n",
Oren Weilfb7d8792011-05-15 13:43:42 +0300114 cl->host_client_id,
115 cl->me_client_id,
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200116 cb_pos->buf_idx);
117
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200118 list_add_tail(&cb_pos->list,
119 &complete_list->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300120 }
121
122 break;
123 }
124
125 }
126
127quit:
128 dev_dbg(&dev->pdev->dev, "message read\n");
129 if (!buffer) {
Tomas Winkleredf1eed2012-02-09 19:25:54 +0200130 mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length);
Tomas Winkler15d4acc2012-12-25 19:06:00 +0200131 dev_dbg(&dev->pdev->dev, "discarding message " MEI_HDR_FMT "\n",
132 MEI_HDR_PRM(mei_hdr));
Oren Weilfb7d8792011-05-15 13:43:42 +0300133 }
134
135 return 0;
136}
137
138/**
Oren Weilfb7d8792011-05-15 13:43:42 +0300139 * _mei_irq_thread_close - processes close related operation.
140 *
141 * @dev: the device structure.
142 * @slots: free slots.
143 * @cb_pos: callback block.
144 * @cl: private data of the file object.
145 * @cmpl_list: complete list.
146 *
147 * returns 0, OK; otherwise, error.
148 */
149static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots,
150 struct mei_cl_cb *cb_pos,
151 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200152 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300153{
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300154 if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200155 sizeof(struct hbm_client_connect_request)))
Oren Weilfb7d8792011-05-15 13:43:42 +0300156 return -EBADMSG;
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300157
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200158 *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request));
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300159
160 if (mei_disconnect(dev, cl)) {
161 cl->status = 0;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200162 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200163 list_move_tail(&cb_pos->list, &cmpl_list->list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300164 return -EMSGSIZE;
165 } else {
166 cl->state = MEI_FILE_DISCONNECTING;
167 cl->status = 0;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200168 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200169 list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300170 cl->timer_count = MEI_CONNECT_TIMEOUT;
Oren Weilfb7d8792011-05-15 13:43:42 +0300171 }
172
173 return 0;
174}
175
176/**
177 * is_treat_specially_client - checks if the message belongs
178 * to the file private data.
179 *
180 * @cl: private data of the file object
181 * @rs: connect response bus message
182 *
183 */
184static bool is_treat_specially_client(struct mei_cl *cl,
185 struct hbm_client_connect_response *rs)
186{
187
188 if (cl->host_client_id == rs->host_addr &&
189 cl->me_client_id == rs->me_addr) {
190 if (!rs->status) {
191 cl->state = MEI_FILE_CONNECTED;
192 cl->status = 0;
193
194 } else {
195 cl->state = MEI_FILE_DISCONNECTED;
196 cl->status = -ENODEV;
197 }
198 cl->timer_count = 0;
199
200 return true;
201 }
202 return false;
203}
204
205/**
206 * mei_client_connect_response - connects to response irq routine
207 *
208 * @dev: the device structure
209 * @rs: connect response bus message
210 */
211static void mei_client_connect_response(struct mei_device *dev,
212 struct hbm_client_connect_response *rs)
213{
214
215 struct mei_cl *cl;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200216 struct mei_cl_cb *pos = NULL, *next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300217
218 dev_dbg(&dev->pdev->dev,
219 "connect_response:\n"
220 "ME Client = %d\n"
221 "Host Client = %d\n"
222 "Status = %d\n",
223 rs->me_addr,
224 rs->host_addr,
225 rs->status);
226
227 /* if WD or iamthif client treat specially */
228
229 if (is_treat_specially_client(&(dev->wd_cl), rs)) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300230 dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n");
Tomas Winkler70cd5332011-12-22 18:50:50 +0200231 mei_watchdog_register(dev);
Oren Weil9ce178e2011-09-07 09:03:09 +0300232
Oren Weilfb7d8792011-05-15 13:43:42 +0300233 return;
234 }
235
236 if (is_treat_specially_client(&(dev->iamthif_cl), rs)) {
237 dev->iamthif_state = MEI_IAMTHIF_IDLE;
238 return;
239 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200240 list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200241
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200242 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200243 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200244 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200245 return;
246 }
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200247 if (pos->fop_type == MEI_FOP_IOCTL) {
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200248 if (is_treat_specially_client(cl, rs)) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200249 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200250 cl->status = 0;
251 cl->timer_count = 0;
252 break;
Oren Weilfb7d8792011-05-15 13:43:42 +0300253 }
254 }
255 }
256}
257
258/**
259 * mei_client_disconnect_response - disconnects from response irq routine
260 *
261 * @dev: the device structure
262 * @rs: disconnect response bus message
263 */
264static void mei_client_disconnect_response(struct mei_device *dev,
265 struct hbm_client_connect_response *rs)
266{
267 struct mei_cl *cl;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200268 struct mei_cl_cb *pos = NULL, *next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300269
270 dev_dbg(&dev->pdev->dev,
271 "disconnect_response:\n"
272 "ME Client = %d\n"
273 "Host Client = %d\n"
274 "Status = %d\n",
275 rs->me_addr,
276 rs->host_addr,
277 rs->status);
278
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200279 list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200280 cl = pos->cl;
Oren Weilfb7d8792011-05-15 13:43:42 +0300281
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200282 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200283 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200284 return;
285 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300286
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200287 dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n");
288 if (cl->host_client_id == rs->host_addr &&
289 cl->me_client_id == rs->me_addr) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300290
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200291 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200292 if (!rs->status)
293 cl->state = MEI_FILE_DISCONNECTED;
Oren Weilfb7d8792011-05-15 13:43:42 +0300294
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200295 cl->status = 0;
296 cl->timer_count = 0;
297 break;
Oren Weilfb7d8792011-05-15 13:43:42 +0300298 }
299 }
300}
301
302/**
303 * same_flow_addr - tells if they have the same address.
304 *
305 * @file: private data of the file object.
306 * @flow: flow control.
307 *
308 * returns !=0, same; 0,not.
309 */
310static int same_flow_addr(struct mei_cl *cl, struct hbm_flow_control *flow)
311{
312 return (cl->host_client_id == flow->host_addr &&
313 cl->me_client_id == flow->me_addr);
314}
315
316/**
317 * add_single_flow_creds - adds single buffer credentials.
318 *
319 * @file: private data ot the file object.
320 * @flow: flow control.
321 */
322static void add_single_flow_creds(struct mei_device *dev,
323 struct hbm_flow_control *flow)
324{
325 struct mei_me_client *client;
326 int i;
327
Tomas Winklercf9673d2011-06-06 10:44:33 +0300328 for (i = 0; i < dev->me_clients_num; i++) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300329 client = &dev->me_clients[i];
330 if (client && flow->me_addr == client->client_id) {
331 if (client->props.single_recv_buf) {
332 client->mei_flow_ctrl_creds++;
333 dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n",
334 flow->me_addr);
335 dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n",
336 client->mei_flow_ctrl_creds);
337 } else {
338 BUG(); /* error in flow control */
339 }
340 }
341 }
342}
343
344/**
345 * mei_client_flow_control_response - flow control response irq routine
346 *
347 * @dev: the device structure
348 * @flow_control: flow control response bus message
349 */
350static void mei_client_flow_control_response(struct mei_device *dev,
351 struct hbm_flow_control *flow_control)
352{
353 struct mei_cl *cl_pos = NULL;
354 struct mei_cl *cl_next = NULL;
355
356 if (!flow_control->host_addr) {
357 /* single receive buffer */
358 add_single_flow_creds(dev, flow_control);
359 } else {
360 /* normal connection */
361 list_for_each_entry_safe(cl_pos, cl_next,
362 &dev->file_list, link) {
363 dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in file_list\n");
364
365 dev_dbg(&dev->pdev->dev, "cl of host client %d ME client %d.\n",
366 cl_pos->host_client_id,
367 cl_pos->me_client_id);
368 dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n",
369 flow_control->host_addr,
370 flow_control->me_addr);
371 if (same_flow_addr(cl_pos, flow_control)) {
372 dev_dbg(&dev->pdev->dev, "recv ctrl msg for host %d ME %d.\n",
373 flow_control->host_addr,
374 flow_control->me_addr);
375 cl_pos->mei_flow_ctrl_creds++;
376 dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n",
377 cl_pos->mei_flow_ctrl_creds);
378 break;
379 }
380 }
381 }
382}
383
384/**
385 * same_disconn_addr - tells if they have the same address
386 *
387 * @file: private data of the file object.
388 * @disconn: disconnection request.
389 *
390 * returns !=0, same; 0,not.
391 */
392static int same_disconn_addr(struct mei_cl *cl,
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200393 struct hbm_client_connect_request *req)
Oren Weilfb7d8792011-05-15 13:43:42 +0300394{
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200395 return (cl->host_client_id == req->host_addr &&
396 cl->me_client_id == req->me_addr);
Oren Weilfb7d8792011-05-15 13:43:42 +0300397}
398
399/**
400 * mei_client_disconnect_request - disconnects from request irq routine
401 *
402 * @dev: the device structure.
403 * @disconnect_req: disconnect request bus message.
404 */
405static void mei_client_disconnect_request(struct mei_device *dev,
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200406 struct hbm_client_connect_request *disconnect_req)
Oren Weilfb7d8792011-05-15 13:43:42 +0300407{
Oren Weilfb7d8792011-05-15 13:43:42 +0300408 struct hbm_client_connect_response *disconnect_res;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200409 struct mei_cl *pos, *next;
410 const size_t len = sizeof(struct hbm_client_connect_response);
Oren Weilfb7d8792011-05-15 13:43:42 +0300411
Tomas Winkler5bd64712012-11-18 15:13:14 +0200412 list_for_each_entry_safe(pos, next, &dev->file_list, link) {
413 if (same_disconn_addr(pos, disconnect_req)) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300414 dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
415 disconnect_req->host_addr,
416 disconnect_req->me_addr);
Tomas Winkler5bd64712012-11-18 15:13:14 +0200417 pos->state = MEI_FILE_DISCONNECTED;
418 pos->timer_count = 0;
419 if (pos == &dev->wd_cl)
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300420 dev->wd_pending = false;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200421 else if (pos == &dev->iamthif_cl)
Oren Weilfb7d8792011-05-15 13:43:42 +0300422 dev->iamthif_timer = 0;
423
424 /* prepare disconnect response */
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200425 (void)mei_hbm_hdr((u32 *)&dev->wr_ext_msg.hdr, len);
Oren Weilfb7d8792011-05-15 13:43:42 +0300426 disconnect_res =
427 (struct hbm_client_connect_response *)
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200428 &dev->wr_ext_msg.data;
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200429 disconnect_res->hbm_cmd = CLIENT_DISCONNECT_RES_CMD;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200430 disconnect_res->host_addr = pos->host_client_id;
431 disconnect_res->me_addr = pos->me_client_id;
Oren Weilfb7d8792011-05-15 13:43:42 +0300432 disconnect_res->status = 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300433 break;
434 }
435 }
436}
437
Oren Weilfb7d8792011-05-15 13:43:42 +0300438/**
439 * mei_irq_thread_read_bus_message - bottom half read routine after ISR to
440 * handle the read bus message cmd processing.
441 *
442 * @dev: the device structure
443 * @mei_hdr: header of bus message
444 */
445static void mei_irq_thread_read_bus_message(struct mei_device *dev,
Tomas Winkler438763f2012-12-25 19:05:59 +0200446 struct mei_msg_hdr *hdr)
Oren Weilfb7d8792011-05-15 13:43:42 +0300447{
448 struct mei_bus_message *mei_msg;
Samuel Ortizc1174c02012-11-18 15:13:20 +0200449 struct mei_me_client *me_client;
Oren Weilfb7d8792011-05-15 13:43:42 +0300450 struct hbm_host_version_response *version_res;
451 struct hbm_client_connect_response *connect_res;
452 struct hbm_client_connect_response *disconnect_res;
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200453 struct hbm_client_connect_request *disconnect_req;
Oren Weilfb7d8792011-05-15 13:43:42 +0300454 struct hbm_flow_control *flow_control;
455 struct hbm_props_response *props_res;
456 struct hbm_host_enum_response *enum_res;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200457 struct hbm_host_stop_request *stop_req;
Oren Weilfb7d8792011-05-15 13:43:42 +0300458
459 /* read the message to our buffer */
Tomas Winkler438763f2012-12-25 19:05:59 +0200460 BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf));
461 mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
Tomas Winkleredf1eed2012-02-09 19:25:54 +0200462 mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;
Oren Weilfb7d8792011-05-15 13:43:42 +0300463
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200464 switch (mei_msg->hbm_cmd) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300465 case HOST_START_RES_CMD:
466 version_res = (struct hbm_host_version_response *) mei_msg;
467 if (version_res->host_version_supported) {
468 dev->version.major_version = HBM_MAJOR_VERSION;
469 dev->version.minor_version = HBM_MINOR_VERSION;
Tomas Winklerb210d752012-08-07 00:03:56 +0300470 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
Oren Weilfb7d8792011-05-15 13:43:42 +0300471 dev->init_clients_state == MEI_START_MESSAGE) {
472 dev->init_clients_timer = 0;
Tomas Winklerc95efb72011-05-25 17:28:21 +0300473 mei_host_enum_clients_message(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300474 } else {
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300475 dev->recvd_msg = false;
Oren Weilfb7d8792011-05-15 13:43:42 +0300476 dev_dbg(&dev->pdev->dev, "IMEI reset due to received host start response bus message.\n");
477 mei_reset(dev, 1);
478 return;
479 }
480 } else {
Tomas Winkler5bd64712012-11-18 15:13:14 +0200481 u32 *buf = dev->wr_msg_buf;
482 const size_t len = sizeof(struct hbm_host_stop_request);
483
Oren Weilfb7d8792011-05-15 13:43:42 +0300484 dev->version = version_res->me_max_version;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200485
Oren Weilfb7d8792011-05-15 13:43:42 +0300486 /* send stop message */
Tomas Winkler438763f2012-12-25 19:05:59 +0200487 hdr = mei_hbm_hdr(&buf[0], len);
Tomas Winkler5bd64712012-11-18 15:13:14 +0200488 stop_req = (struct hbm_host_stop_request *)&buf[1];
489 memset(stop_req, 0, len);
490 stop_req->hbm_cmd = HOST_STOP_REQ_CMD;
491 stop_req->reason = DRIVER_STOP_REQUEST;
Oren Weilfb7d8792011-05-15 13:43:42 +0300492
Tomas Winkler438763f2012-12-25 19:05:59 +0200493 mei_write_message(dev, hdr, (unsigned char *)stop_req);
Oren Weilfb7d8792011-05-15 13:43:42 +0300494 dev_dbg(&dev->pdev->dev, "version mismatch.\n");
495 return;
496 }
497
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300498 dev->recvd_msg = true;
Oren Weilfb7d8792011-05-15 13:43:42 +0300499 dev_dbg(&dev->pdev->dev, "host start response message received.\n");
500 break;
501
502 case CLIENT_CONNECT_RES_CMD:
Tomas Winkler5bd64712012-11-18 15:13:14 +0200503 connect_res = (struct hbm_client_connect_response *) mei_msg;
Oren Weilfb7d8792011-05-15 13:43:42 +0300504 mei_client_connect_response(dev, connect_res);
505 dev_dbg(&dev->pdev->dev, "client connect response message received.\n");
506 wake_up(&dev->wait_recvd_msg);
507 break;
508
509 case CLIENT_DISCONNECT_RES_CMD:
Tomas Winkler5bd64712012-11-18 15:13:14 +0200510 disconnect_res = (struct hbm_client_connect_response *) mei_msg;
Tomas Winkler441ab502011-12-13 23:39:34 +0200511 mei_client_disconnect_response(dev, disconnect_res);
Oren Weilfb7d8792011-05-15 13:43:42 +0300512 dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n");
513 wake_up(&dev->wait_recvd_msg);
514 break;
515
516 case MEI_FLOW_CONTROL_CMD:
517 flow_control = (struct hbm_flow_control *) mei_msg;
518 mei_client_flow_control_response(dev, flow_control);
519 dev_dbg(&dev->pdev->dev, "client flow control response message received.\n");
520 break;
521
522 case HOST_CLIENT_PROPERTIES_RES_CMD:
523 props_res = (struct hbm_props_response *)mei_msg;
Samuel Ortizc1174c02012-11-18 15:13:20 +0200524 me_client = &dev->me_clients[dev->me_client_presentation_num];
525
Oren Weilfb7d8792011-05-15 13:43:42 +0300526 if (props_res->status || !dev->me_clients) {
527 dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n");
528 mei_reset(dev, 1);
529 return;
530 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300531
Samuel Ortizc1174c02012-11-18 15:13:20 +0200532 if (me_client->client_id != props_res->address) {
533 dev_err(&dev->pdev->dev,
534 "Host client properties reply mismatch\n");
Oren Weilfb7d8792011-05-15 13:43:42 +0300535 mei_reset(dev, 1);
Samuel Ortizc1174c02012-11-18 15:13:20 +0200536
Oren Weilfb7d8792011-05-15 13:43:42 +0300537 return;
538 }
Samuel Ortizc1174c02012-11-18 15:13:20 +0200539
540 if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
541 dev->init_clients_state != MEI_CLIENT_PROPERTIES_MESSAGE) {
542 dev_err(&dev->pdev->dev,
543 "Unexpected client properties reply\n");
544 mei_reset(dev, 1);
545
546 return;
547 }
548
549 me_client->props = props_res->client_properties;
550 dev->me_client_index++;
551 dev->me_client_presentation_num++;
552
553 mei_host_client_enumerate(dev);
554
Oren Weilfb7d8792011-05-15 13:43:42 +0300555 break;
556
557 case HOST_ENUM_RES_CMD:
558 enum_res = (struct hbm_host_enum_response *) mei_msg;
559 memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
Tomas Winklerb210d752012-08-07 00:03:56 +0300560 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
Oren Weilfb7d8792011-05-15 13:43:42 +0300561 dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) {
562 dev->init_clients_timer = 0;
563 dev->me_client_presentation_num = 0;
564 dev->me_client_index = 0;
Tomas Winklerc95efb72011-05-25 17:28:21 +0300565 mei_allocate_me_clients_storage(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300566 dev->init_clients_state =
567 MEI_CLIENT_PROPERTIES_MESSAGE;
Samuel Ortizc1174c02012-11-18 15:13:20 +0200568
569 mei_host_client_enumerate(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300570 } else {
571 dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n");
572 mei_reset(dev, 1);
573 return;
574 }
575 break;
576
577 case HOST_STOP_RES_CMD:
Tomas Winklerb210d752012-08-07 00:03:56 +0300578 dev->dev_state = MEI_DEV_DISABLED;
Oren Weilfb7d8792011-05-15 13:43:42 +0300579 dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n");
580 mei_reset(dev, 1);
581 break;
582
583 case CLIENT_DISCONNECT_REQ_CMD:
584 /* search for client */
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200585 disconnect_req = (struct hbm_client_connect_request *)mei_msg;
Oren Weilfb7d8792011-05-15 13:43:42 +0300586 mei_client_disconnect_request(dev, disconnect_req);
587 break;
588
589 case ME_STOP_REQ_CMD:
Tomas Winkler5bd64712012-11-18 15:13:14 +0200590 {
591 /* prepare stop request: sent in next interrupt event */
592
Tomas Winkler5bd64712012-11-18 15:13:14 +0200593 const size_t len = sizeof(struct hbm_host_stop_request);
594
Tomas Winkler438763f2012-12-25 19:05:59 +0200595 hdr = mei_hbm_hdr((u32 *)&dev->wr_ext_msg.hdr, len);
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200596 stop_req = (struct hbm_host_stop_request *)&dev->wr_ext_msg.data;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200597 memset(stop_req, 0, len);
598 stop_req->hbm_cmd = HOST_STOP_REQ_CMD;
599 stop_req->reason = DRIVER_STOP_REQUEST;
Oren Weilfb7d8792011-05-15 13:43:42 +0300600 break;
Tomas Winkler5bd64712012-11-18 15:13:14 +0200601 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300602 default:
603 BUG();
604 break;
605
606 }
607}
608
609
610/**
611 * _mei_hb_read - processes read related operation.
612 *
613 * @dev: the device structure.
614 * @slots: free slots.
615 * @cb_pos: callback block.
616 * @cl: private data of the file object.
617 * @cmpl_list: complete list.
618 *
619 * returns 0, OK; otherwise, error.
620 */
621static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots,
622 struct mei_cl_cb *cb_pos,
623 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200624 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300625{
Tomas Winkler1e69d642012-05-29 16:39:12 +0300626 if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
Oren Weilfb7d8792011-05-15 13:43:42 +0300627 sizeof(struct hbm_flow_control))) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300628 /* return the cancel routine */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200629 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300630 return -EBADMSG;
631 }
632
Tomas Winkler7bdf72d2012-07-04 19:24:52 +0300633 *slots -= mei_data2slots(sizeof(struct hbm_flow_control));
634
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200635 if (mei_send_flow_control(dev, cl)) {
636 cl->status = -ENODEV;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200637 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200638 list_move_tail(&cb_pos->list, &cmpl_list->list);
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200639 return -ENODEV;
640 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200641 list_move_tail(&cb_pos->list, &dev->read_list.list);
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200642
Oren Weilfb7d8792011-05-15 13:43:42 +0300643 return 0;
644}
645
646
647/**
648 * _mei_irq_thread_ioctl - processes ioctl related operation.
649 *
650 * @dev: the device structure.
651 * @slots: free slots.
652 * @cb_pos: callback block.
653 * @cl: private data of the file object.
654 * @cmpl_list: complete list.
655 *
656 * returns 0, OK; otherwise, error.
657 */
658static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots,
659 struct mei_cl_cb *cb_pos,
660 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200661 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300662{
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300663 if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
Oren Weilfb7d8792011-05-15 13:43:42 +0300664 sizeof(struct hbm_client_connect_request))) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300665 /* return the cancel routine */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200666 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300667 return -EBADMSG;
668 }
669
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300670 cl->state = MEI_FILE_CONNECTING;
671 *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request));
672 if (mei_connect(dev, cl)) {
673 cl->status = -ENODEV;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200674 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200675 list_del(&cb_pos->list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300676 return -ENODEV;
677 } else {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200678 list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300679 cl->timer_count = MEI_CONNECT_TIMEOUT;
680 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300681 return 0;
682}
683
684/**
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200685 * mei_irq_thread_write_complete - write messages to device.
Oren Weilfb7d8792011-05-15 13:43:42 +0300686 *
687 * @dev: the device structure.
688 * @slots: free slots.
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200689 * @cb: callback block.
Oren Weilfb7d8792011-05-15 13:43:42 +0300690 * @cmpl_list: complete list.
691 *
692 * returns 0, OK; otherwise, error.
693 */
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200694static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots,
695 struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300696{
697 struct mei_msg_hdr *mei_hdr;
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200698 struct mei_cl *cl = cb->cl;
699 size_t len = cb->request_buffer.size - cb->buf_idx;
700 size_t msg_slots = mei_data2slots(len);
Oren Weilfb7d8792011-05-15 13:43:42 +0300701
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200702 mei_hdr = (struct mei_msg_hdr *)&dev->wr_msg_buf[0];
703 mei_hdr->host_addr = cl->host_client_id;
704 mei_hdr->me_addr = cl->me_client_id;
705 mei_hdr->reserved = 0;
706
707 if (*slots >= msg_slots) {
708 mei_hdr->length = len;
Oren Weilfb7d8792011-05-15 13:43:42 +0300709 mei_hdr->msg_complete = 1;
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200710 /* Split the message only if we can write the whole host buffer */
Tomas Winkler24aadc82012-06-25 23:46:27 +0300711 } else if (*slots == dev->hbuf_depth) {
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200712 msg_slots = *slots;
713 len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
714 mei_hdr->length = len;
Oren Weilfb7d8792011-05-15 13:43:42 +0300715 mei_hdr->msg_complete = 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300716 } else {
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200717 /* wait for next time the host buffer is empty */
718 return 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300719 }
720
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200721 dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n",
722 cb->request_buffer.size, cb->buf_idx);
Tomas Winkler15d4acc2012-12-25 19:06:00 +0200723 dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200724
725 *slots -= msg_slots;
726 if (mei_write_message(dev, mei_hdr,
Tomas Winkler438763f2012-12-25 19:05:59 +0200727 cb->request_buffer.data + cb->buf_idx)) {
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200728 cl->status = -ENODEV;
729 list_move_tail(&cb->list, &cmpl_list->list);
730 return -ENODEV;
731 }
732
733 if (mei_flow_ctrl_reduce(dev, cl))
734 return -ENODEV;
735
736 cl->status = 0;
737 cb->buf_idx += mei_hdr->length;
738 if (mei_hdr->msg_complete)
739 list_move_tail(&cb->list, &dev->write_waiting_list.list);
740
Oren Weilfb7d8792011-05-15 13:43:42 +0300741 return 0;
742}
743
744/**
Oren Weilfb7d8792011-05-15 13:43:42 +0300745 * mei_irq_thread_read_handler - bottom half read routine after ISR to
746 * handle the read processing.
747 *
748 * @cmpl_list: An instance of our list structure
749 * @dev: the device structure
750 * @slots: slots to read.
751 *
752 * returns 0 on success, <0 on failure.
753 */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200754static int mei_irq_thread_read_handler(struct mei_cl_cb *cmpl_list,
Oren Weilfb7d8792011-05-15 13:43:42 +0300755 struct mei_device *dev,
756 s32 *slots)
757{
758 struct mei_msg_hdr *mei_hdr;
759 struct mei_cl *cl_pos = NULL;
760 struct mei_cl *cl_next = NULL;
761 int ret = 0;
762
763 if (!dev->rd_msg_hdr) {
764 dev->rd_msg_hdr = mei_mecbrw_read(dev);
765 dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
766 (*slots)--;
767 dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
768 }
769 mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr;
Tomas Winkler15d4acc2012-12-25 19:06:00 +0200770 dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
Oren Weilfb7d8792011-05-15 13:43:42 +0300771
772 if (mei_hdr->reserved || !dev->rd_msg_hdr) {
773 dev_dbg(&dev->pdev->dev, "corrupted message header.\n");
774 ret = -EBADMSG;
775 goto end;
776 }
777
778 if (mei_hdr->host_addr || mei_hdr->me_addr) {
779 list_for_each_entry_safe(cl_pos, cl_next,
780 &dev->file_list, link) {
781 dev_dbg(&dev->pdev->dev,
782 "list_for_each_entry_safe read host"
783 " client = %d, ME client = %d\n",
784 cl_pos->host_client_id,
785 cl_pos->me_client_id);
786 if (cl_pos->host_client_id == mei_hdr->host_addr &&
787 cl_pos->me_client_id == mei_hdr->me_addr)
788 break;
789 }
790
791 if (&cl_pos->link == &dev->file_list) {
792 dev_dbg(&dev->pdev->dev, "corrupted message header\n");
793 ret = -EBADMSG;
794 goto end;
795 }
796 }
797 if (((*slots) * sizeof(u32)) < mei_hdr->length) {
798 dev_dbg(&dev->pdev->dev,
799 "we can't read the message slots =%08x.\n",
800 *slots);
801 /* we can't read the message */
802 ret = -ERANGE;
803 goto end;
804 }
805
806 /* decide where to read the message too */
807 if (!mei_hdr->host_addr) {
808 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n");
809 mei_irq_thread_read_bus_message(dev, mei_hdr);
810 dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n");
811 } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id &&
812 (MEI_FILE_CONNECTED == dev->iamthif_cl.state) &&
813 (dev->iamthif_state == MEI_IAMTHIF_READING)) {
814 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n");
Tomas Winkler15d4acc2012-12-25 19:06:00 +0200815
816 dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
Tomas Winkler19838fb2012-11-01 21:17:15 +0200817
818 ret = mei_amthif_irq_read_message(cmpl_list, dev, mei_hdr);
Oren Weilfb7d8792011-05-15 13:43:42 +0300819 if (ret)
820 goto end;
Oren Weilfb7d8792011-05-15 13:43:42 +0300821 } else {
822 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n");
823 ret = mei_irq_thread_read_client_message(cmpl_list,
824 dev, mei_hdr);
825 if (ret)
826 goto end;
827
828 }
829
830 /* reset the number of slots and header */
831 *slots = mei_count_full_read_slots(dev);
832 dev->rd_msg_hdr = 0;
833
834 if (*slots == -EOVERFLOW) {
835 /* overflow - reset */
836 dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n");
837 /* set the event since message has been read */
838 ret = -ERANGE;
839 goto end;
840 }
841end:
842 return ret;
843}
844
845
846/**
847 * mei_irq_thread_write_handler - bottom half write routine after
848 * ISR to handle the write processing.
849 *
Oren Weilfb7d8792011-05-15 13:43:42 +0300850 * @dev: the device structure
Tomas Winkler9a84d612012-11-18 15:13:18 +0200851 * @cmpl_list: An instance of our list structure
Oren Weilfb7d8792011-05-15 13:43:42 +0300852 *
853 * returns 0 on success, <0 on failure.
854 */
Tomas Winkler9a84d612012-11-18 15:13:18 +0200855static int mei_irq_thread_write_handler(struct mei_device *dev,
856 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300857{
858
859 struct mei_cl *cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200860 struct mei_cl_cb *pos = NULL, *next = NULL;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200861 struct mei_cl_cb *list;
Tomas Winkler9a84d612012-11-18 15:13:18 +0200862 s32 slots;
Oren Weilfb7d8792011-05-15 13:43:42 +0300863 int ret;
864
Tomas Winkler726917f2012-06-25 23:46:28 +0300865 if (!mei_hbuf_is_empty(dev)) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300866 dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n");
867 return 0;
868 }
Tomas Winkler9a84d612012-11-18 15:13:18 +0200869 slots = mei_hbuf_empty_slots(dev);
870 if (slots <= 0)
Tomas Winkler7d5e0e52012-06-19 09:13:36 +0300871 return -EMSGSIZE;
872
Oren Weilfb7d8792011-05-15 13:43:42 +0300873 /* complete all waiting for write CB */
874 dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n");
875
876 list = &dev->write_waiting_list;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200877 list_for_each_entry_safe(pos, next, &list->list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200878 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200879 if (cl == NULL)
880 continue;
Oren Weilfb7d8792011-05-15 13:43:42 +0300881
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200882 cl->status = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200883 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200884 if (MEI_WRITING == cl->writing_state &&
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200885 pos->fop_type == MEI_FOP_WRITE &&
886 cl != &dev->iamthif_cl) {
Tomas Winkler483136e2012-07-04 19:24:54 +0300887 dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n");
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200888 cl->writing_state = MEI_WRITE_COMPLETE;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200889 list_add_tail(&pos->list, &cmpl_list->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200890 }
891 if (cl == &dev->iamthif_cl) {
892 dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n");
893 if (dev->iamthif_flow_control_pending) {
Tomas Winkler9a84d612012-11-18 15:13:18 +0200894 ret = mei_amthif_irq_read(dev, &slots);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200895 if (ret)
896 return ret;
897 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300898 }
899 }
900
Tomas Winklerc216fde2012-08-16 19:39:43 +0300901 if (dev->wd_state == MEI_WD_STOPPING) {
902 dev->wd_state = MEI_WD_IDLE;
Oren Weilfb7d8792011-05-15 13:43:42 +0300903 wake_up_interruptible(&dev->wait_stop_wd);
Oren Weilfb7d8792011-05-15 13:43:42 +0300904 }
905
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200906 if (dev->wr_ext_msg.hdr.length) {
907 mei_write_message(dev, &dev->wr_ext_msg.hdr,
Tomas Winkler438763f2012-12-25 19:05:59 +0200908 dev->wr_ext_msg.data);
Tomas Winkler9a84d612012-11-18 15:13:18 +0200909 slots -= mei_data2slots(dev->wr_ext_msg.hdr.length);
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200910 dev->wr_ext_msg.hdr.length = 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300911 }
Tomas Winklerb210d752012-08-07 00:03:56 +0300912 if (dev->dev_state == MEI_DEV_ENABLED) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300913 if (dev->wd_pending &&
Tomas Winkler483136e2012-07-04 19:24:54 +0300914 mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300915 if (mei_wd_send(dev))
916 dev_dbg(&dev->pdev->dev, "wd send failed.\n");
Tomas Winkler483136e2012-07-04 19:24:54 +0300917 else if (mei_flow_ctrl_reduce(dev, &dev->wd_cl))
918 return -ENODEV;
Oren Weilfb7d8792011-05-15 13:43:42 +0300919
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300920 dev->wd_pending = false;
Oren Weilfb7d8792011-05-15 13:43:42 +0300921
Tomas Winklerc216fde2012-08-16 19:39:43 +0300922 if (dev->wd_state == MEI_WD_RUNNING)
Tomas Winkler9a84d612012-11-18 15:13:18 +0200923 slots -= mei_data2slots(MEI_WD_START_MSG_SIZE);
Tomas Winklerd242a0a2012-07-04 19:24:50 +0300924 else
Tomas Winkler9a84d612012-11-18 15:13:18 +0200925 slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE);
Oren Weilfb7d8792011-05-15 13:43:42 +0300926 }
927 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300928
929 /* complete control write list CB */
Tomas Winklerc8372092011-11-27 21:43:33 +0200930 dev_dbg(&dev->pdev->dev, "complete control write list cb.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200931 list_for_each_entry_safe(pos, next, &dev->ctrl_wr_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200932 cl = pos->cl;
Tomas Winklerc8372092011-11-27 21:43:33 +0200933 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200934 list_del(&pos->list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200935 return -ENODEV;
Oren Weilfb7d8792011-05-15 13:43:42 +0300936 }
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200937 switch (pos->fop_type) {
938 case MEI_FOP_CLOSE:
Tomas Winklerc8372092011-11-27 21:43:33 +0200939 /* send disconnect message */
Tomas Winkler9a84d612012-11-18 15:13:18 +0200940 ret = _mei_irq_thread_close(dev, &slots, pos,
941 cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200942 if (ret)
943 return ret;
944
945 break;
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200946 case MEI_FOP_READ:
Tomas Winklerc8372092011-11-27 21:43:33 +0200947 /* send flow control message */
Tomas Winkler9a84d612012-11-18 15:13:18 +0200948 ret = _mei_irq_thread_read(dev, &slots, pos,
949 cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200950 if (ret)
951 return ret;
952
953 break;
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200954 case MEI_FOP_IOCTL:
Tomas Winklerc8372092011-11-27 21:43:33 +0200955 /* connect message */
Natalia Ovsyanikove8cd29d2011-12-05 00:16:54 +0200956 if (mei_other_client_is_connecting(dev, cl))
Tomas Winklerc8372092011-11-27 21:43:33 +0200957 continue;
Tomas Winkler9a84d612012-11-18 15:13:18 +0200958 ret = _mei_irq_thread_ioctl(dev, &slots, pos,
959 cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200960 if (ret)
961 return ret;
962
963 break;
964
965 default:
966 BUG();
967 }
968
Oren Weilfb7d8792011-05-15 13:43:42 +0300969 }
970 /* complete write list CB */
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200971 dev_dbg(&dev->pdev->dev, "complete write list cb.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200972 list_for_each_entry_safe(pos, next, &dev->write_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200973 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200974 if (cl == NULL)
975 continue;
Tomas Winklerbe9d87a2012-11-18 15:13:19 +0200976 if (mei_flow_ctrl_creds(dev, cl) <= 0) {
977 dev_dbg(&dev->pdev->dev,
978 "No flow control credentials for client %d, not sending.\n",
979 cl->host_client_id);
980 continue;
981 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300982
Tomas Winklerbe9d87a2012-11-18 15:13:19 +0200983 if (cl == &dev->iamthif_cl)
Tomas Winkler9a84d612012-11-18 15:13:18 +0200984 ret = mei_amthif_irq_write_complete(dev, &slots,
Tomas Winkler24c656e2012-11-18 15:13:17 +0200985 pos, cmpl_list);
Tomas Winklerbe9d87a2012-11-18 15:13:19 +0200986 else
987 ret = mei_irq_thread_write_complete(dev, &slots, pos,
988 cmpl_list);
989 if (ret)
990 return ret;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200991
Oren Weilfb7d8792011-05-15 13:43:42 +0300992 }
993 return 0;
994}
995
996
997
998/**
999 * mei_timer - timer function.
1000 *
1001 * @work: pointer to the work_struct structure
1002 *
1003 * NOTE: This function is called by timer interrupt work
1004 */
Oren Weila61c6532011-09-07 09:03:13 +03001005void mei_timer(struct work_struct *work)
Oren Weilfb7d8792011-05-15 13:43:42 +03001006{
1007 unsigned long timeout;
1008 struct mei_cl *cl_pos = NULL;
1009 struct mei_cl *cl_next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001010 struct mei_cl_cb *cb_pos = NULL;
1011 struct mei_cl_cb *cb_next = NULL;
1012
1013 struct mei_device *dev = container_of(work,
Oren Weila61c6532011-09-07 09:03:13 +03001014 struct mei_device, timer_work.work);
Oren Weilfb7d8792011-05-15 13:43:42 +03001015
1016
1017 mutex_lock(&dev->device_lock);
Tomas Winklerb210d752012-08-07 00:03:56 +03001018 if (dev->dev_state != MEI_DEV_ENABLED) {
1019 if (dev->dev_state == MEI_DEV_INIT_CLIENTS) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001020 if (dev->init_clients_timer) {
1021 if (--dev->init_clients_timer == 0) {
1022 dev_dbg(&dev->pdev->dev, "IMEI reset due to init clients timeout ,init clients state = %d.\n",
1023 dev->init_clients_state);
1024 mei_reset(dev, 1);
1025 }
1026 }
1027 }
1028 goto out;
1029 }
1030 /*** connect/disconnect timeouts ***/
1031 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
1032 if (cl_pos->timer_count) {
1033 if (--cl_pos->timer_count == 0) {
1034 dev_dbg(&dev->pdev->dev, "HECI reset due to connect/disconnect timeout.\n");
1035 mei_reset(dev, 1);
1036 goto out;
1037 }
1038 }
1039 }
1040
Oren Weilfb7d8792011-05-15 13:43:42 +03001041 if (dev->iamthif_stall_timer) {
1042 if (--dev->iamthif_stall_timer == 0) {
Masanari Iida32de21f2012-01-25 23:14:56 +09001043 dev_dbg(&dev->pdev->dev, "resetting because of hang to amthi.\n");
Oren Weilfb7d8792011-05-15 13:43:42 +03001044 mei_reset(dev, 1);
1045 dev->iamthif_msg_buf_size = 0;
1046 dev->iamthif_msg_buf_index = 0;
Tomas Winklereb9af0a2011-05-25 17:28:22 +03001047 dev->iamthif_canceled = false;
1048 dev->iamthif_ioctl = true;
Oren Weilfb7d8792011-05-15 13:43:42 +03001049 dev->iamthif_state = MEI_IAMTHIF_IDLE;
1050 dev->iamthif_timer = 0;
1051
Tomas Winkler601a1ef2012-10-09 16:50:20 +02001052 mei_io_cb_free(dev->iamthif_current_cb);
1053 dev->iamthif_current_cb = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001054
1055 dev->iamthif_file_object = NULL;
Tomas Winkler19838fb2012-11-01 21:17:15 +02001056 mei_amthif_run_next_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001057 }
1058 }
1059
1060 if (dev->iamthif_timer) {
1061
1062 timeout = dev->iamthif_timer +
Tomas Winkler3870c322012-11-01 21:17:14 +02001063 mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
Oren Weilfb7d8792011-05-15 13:43:42 +03001064
1065 dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n",
1066 dev->iamthif_timer);
1067 dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout);
1068 dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies);
1069 if (time_after(jiffies, timeout)) {
1070 /*
1071 * User didn't read the AMTHI data on time (15sec)
1072 * freeing AMTHI for other requests
1073 */
1074
1075 dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n");
1076
Tomas Winklere773efc2012-11-11 17:37:58 +02001077 list_for_each_entry_safe(cb_pos, cb_next,
1078 &dev->amthif_rd_complete_list.list, list) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001079
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001080 cl_pos = cb_pos->file_object->private_data;
Oren Weilfb7d8792011-05-15 13:43:42 +03001081
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001082 /* Finding the AMTHI entry. */
1083 if (cl_pos == &dev->iamthif_cl)
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001084 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +03001085 }
Tomas Winkler601a1ef2012-10-09 16:50:20 +02001086 mei_io_cb_free(dev->iamthif_current_cb);
1087 dev->iamthif_current_cb = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001088
1089 dev->iamthif_file_object->private_data = NULL;
1090 dev->iamthif_file_object = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001091 dev->iamthif_timer = 0;
Tomas Winkler19838fb2012-11-01 21:17:15 +02001092 mei_amthif_run_next_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001093
1094 }
1095 }
1096out:
Tomas Winkler441ab502011-12-13 23:39:34 +02001097 schedule_delayed_work(&dev->timer_work, 2 * HZ);
1098 mutex_unlock(&dev->device_lock);
Oren Weilfb7d8792011-05-15 13:43:42 +03001099}
1100
1101/**
1102 * mei_interrupt_thread_handler - function called after ISR to handle the interrupt
1103 * processing.
1104 *
1105 * @irq: The irq number
1106 * @dev_id: pointer to the device structure
1107 *
1108 * returns irqreturn_t
1109 *
1110 */
1111irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id)
1112{
1113 struct mei_device *dev = (struct mei_device *) dev_id;
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001114 struct mei_cl_cb complete_list;
Oren Weilfb7d8792011-05-15 13:43:42 +03001115 struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
1116 struct mei_cl *cl;
1117 s32 slots;
1118 int rets;
1119 bool bus_message_received;
1120
1121
1122 dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n");
1123 /* initialize our complete list */
1124 mutex_lock(&dev->device_lock);
Tomas Winkler0288c7c2011-06-06 10:44:34 +03001125 mei_io_list_init(&complete_list);
Oren Weilfb7d8792011-05-15 13:43:42 +03001126 dev->host_hw_state = mei_hcsr_read(dev);
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001127
1128 /* Ack the interrupt here
Justin P. Mattock5f9092f2012-03-12 07:18:09 -07001129 * In case of MSI we don't go through the quick handler */
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001130 if (pci_dev_msi_enabled(dev->pdev))
Tomas Winkler3a65dd42012-12-25 19:06:06 +02001131 mei_clear_interrupts(dev);
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001132
Oren Weilfb7d8792011-05-15 13:43:42 +03001133 dev->me_hw_state = mei_mecsr_read(dev);
1134
1135 /* check if ME wants a reset */
1136 if ((dev->me_hw_state & ME_RDY_HRA) == 0 &&
Tomas Winklerb210d752012-08-07 00:03:56 +03001137 dev->dev_state != MEI_DEV_RESETING &&
1138 dev->dev_state != MEI_DEV_INITIALIZING) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001139 dev_dbg(&dev->pdev->dev, "FW not ready.\n");
1140 mei_reset(dev, 1);
1141 mutex_unlock(&dev->device_lock);
1142 return IRQ_HANDLED;
1143 }
1144
1145 /* check if we need to start the dev */
1146 if ((dev->host_hw_state & H_RDY) == 0) {
1147 if ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA) {
1148 dev_dbg(&dev->pdev->dev, "we need to start the dev.\n");
1149 dev->host_hw_state |= (H_IE | H_IG | H_RDY);
1150 mei_hcsr_set(dev);
Tomas Winklerb210d752012-08-07 00:03:56 +03001151 dev->dev_state = MEI_DEV_INIT_CLIENTS;
Oren Weilfb7d8792011-05-15 13:43:42 +03001152 dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
1153 /* link is established
1154 * start sending messages.
1155 */
Tomas Winklerc95efb72011-05-25 17:28:21 +03001156 mei_host_start_message(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001157 mutex_unlock(&dev->device_lock);
1158 return IRQ_HANDLED;
1159 } else {
1160 dev_dbg(&dev->pdev->dev, "FW not ready.\n");
1161 mutex_unlock(&dev->device_lock);
1162 return IRQ_HANDLED;
1163 }
1164 }
Justin P. Mattock5f9092f2012-03-12 07:18:09 -07001165 /* check slots available for reading */
Oren Weilfb7d8792011-05-15 13:43:42 +03001166 slots = mei_count_full_read_slots(dev);
Tomas Winkler5fb54fb2012-11-18 15:13:15 +02001167 while (slots > 0) {
1168 /* we have urgent data to send so break the read */
1169 if (dev->wr_ext_msg.hdr.length)
1170 break;
1171 dev_dbg(&dev->pdev->dev, "slots =%08x\n", slots);
Oren Weilfb7d8792011-05-15 13:43:42 +03001172 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_handler.\n");
1173 rets = mei_irq_thread_read_handler(&complete_list, dev, &slots);
1174 if (rets)
1175 goto end;
1176 }
Tomas Winkler9a84d612012-11-18 15:13:18 +02001177 rets = mei_irq_thread_write_handler(dev, &complete_list);
Oren Weilfb7d8792011-05-15 13:43:42 +03001178end:
1179 dev_dbg(&dev->pdev->dev, "end of bottom half function.\n");
1180 dev->host_hw_state = mei_hcsr_read(dev);
Tomas Winkler726917f2012-06-25 23:46:28 +03001181 dev->mei_host_buffer_is_empty = mei_hbuf_is_empty(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001182
1183 bus_message_received = false;
1184 if (dev->recvd_msg && waitqueue_active(&dev->wait_recvd_msg)) {
1185 dev_dbg(&dev->pdev->dev, "received waiting bus message\n");
1186 bus_message_received = true;
1187 }
1188 mutex_unlock(&dev->device_lock);
1189 if (bus_message_received) {
1190 dev_dbg(&dev->pdev->dev, "wake up dev->wait_recvd_msg\n");
1191 wake_up_interruptible(&dev->wait_recvd_msg);
1192 bus_message_received = false;
1193 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001194 if (list_empty(&complete_list.list))
Oren Weilfb7d8792011-05-15 13:43:42 +03001195 return IRQ_HANDLED;
1196
1197
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001198 list_for_each_entry_safe(cb_pos, cb_next, &complete_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +02001199 cl = cb_pos->cl;
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001200 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +03001201 if (cl) {
1202 if (cl != &dev->iamthif_cl) {
1203 dev_dbg(&dev->pdev->dev, "completing call back.\n");
1204 _mei_cmpl(cl, cb_pos);
1205 cb_pos = NULL;
1206 } else if (cl == &dev->iamthif_cl) {
Tomas Winkler19838fb2012-11-01 21:17:15 +02001207 mei_amthif_complete(dev, cb_pos);
Oren Weilfb7d8792011-05-15 13:43:42 +03001208 }
1209 }
1210 }
1211 return IRQ_HANDLED;
1212}