blob: f8821015f3f6bfffd05b9e1465d2ae831b62d10c [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
24#include "mei_dev.h"
Tomas Winkler4f3afe12012-05-09 16:38:59 +030025#include <linux/mei.h>
Oren Weilfb7d8792011-05-15 13:43:42 +030026#include "hw.h"
27#include "interface.h"
28
29
30/**
31 * mei_interrupt_quick_handler - The ISR of the MEI device
32 *
33 * @irq: The irq number
34 * @dev_id: pointer to the device structure
35 *
36 * returns irqreturn_t
37 */
38irqreturn_t mei_interrupt_quick_handler(int irq, void *dev_id)
39{
40 struct mei_device *dev = (struct mei_device *) dev_id;
41 u32 csr_reg = mei_hcsr_read(dev);
42
43 if ((csr_reg & H_IS) != H_IS)
44 return IRQ_NONE;
45
46 /* clear H_IS bit in H_CSR */
47 mei_reg_write(dev, H_CSR, csr_reg);
48
49 return IRQ_WAKE_THREAD;
50}
51
52/**
53 * _mei_cmpl - processes completed operation.
54 *
55 * @cl: private data of the file object.
56 * @cb_pos: callback block.
57 */
58static void _mei_cmpl(struct mei_cl *cl, struct mei_cl_cb *cb_pos)
59{
Tomas Winkler4b8960b2012-11-11 17:38:00 +020060 if (cb_pos->fop_type == MEI_FOP_WRITE) {
Tomas Winkler601a1ef2012-10-09 16:50:20 +020061 mei_io_cb_free(cb_pos);
Oren Weilfb7d8792011-05-15 13:43:42 +030062 cb_pos = NULL;
63 cl->writing_state = MEI_WRITE_COMPLETE;
64 if (waitqueue_active(&cl->tx_wait))
65 wake_up_interruptible(&cl->tx_wait);
66
Tomas Winkler4b8960b2012-11-11 17:38:00 +020067 } else if (cb_pos->fop_type == MEI_FOP_READ &&
Oren Weilfb7d8792011-05-15 13:43:42 +030068 MEI_READING == cl->reading_state) {
69 cl->reading_state = MEI_READ_COMPLETE;
70 if (waitqueue_active(&cl->rx_wait))
71 wake_up_interruptible(&cl->rx_wait);
72
73 }
74}
75
76/**
Oren Weilfb7d8792011-05-15 13:43:42 +030077 * _mei_irq_thread_state_ok - checks if mei header matches file private data
78 *
79 * @cl: private data of the file object
80 * @mei_hdr: header of mei client message
81 *
82 * returns !=0 if matches, 0 if no match.
83 */
84static int _mei_irq_thread_state_ok(struct mei_cl *cl,
85 struct mei_msg_hdr *mei_hdr)
86{
87 return (cl->host_client_id == mei_hdr->host_addr &&
88 cl->me_client_id == mei_hdr->me_addr &&
89 cl->state == MEI_FILE_CONNECTED &&
90 MEI_READ_COMPLETE != cl->reading_state);
91}
92
93/**
94 * mei_irq_thread_read_client_message - bottom half read routine after ISR to
95 * handle the read mei client message data processing.
96 *
97 * @complete_list: An instance of our list structure
98 * @dev: the device structure
99 * @mei_hdr: header of mei client message
100 *
101 * returns 0 on success, <0 on failure.
102 */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200103static int mei_irq_thread_read_client_message(struct mei_cl_cb *complete_list,
Oren Weilfb7d8792011-05-15 13:43:42 +0300104 struct mei_device *dev,
105 struct mei_msg_hdr *mei_hdr)
106{
107 struct mei_cl *cl;
108 struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
Tomas Winkler479bc592011-06-16 00:46:03 +0300109 unsigned char *buffer = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300110
111 dev_dbg(&dev->pdev->dev, "start client msg\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200112 if (list_empty(&dev->read_list.list))
Oren Weilfb7d8792011-05-15 13:43:42 +0300113 goto quit;
114
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200115 list_for_each_entry_safe(cb_pos, cb_next, &dev->read_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200116 cl = cb_pos->cl;
Oren Weilfb7d8792011-05-15 13:43:42 +0300117 if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) {
118 cl->reading_state = MEI_READING;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200119 buffer = cb_pos->response_buffer.data + cb_pos->buf_idx;
Oren Weilfb7d8792011-05-15 13:43:42 +0300120
121 if (cb_pos->response_buffer.size <
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200122 mei_hdr->length + cb_pos->buf_idx) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300123 dev_dbg(&dev->pdev->dev, "message overflow.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200124 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300125 return -ENOMEM;
126 }
127 if (buffer)
128 mei_read_slots(dev, buffer, mei_hdr->length);
129
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200130 cb_pos->buf_idx += mei_hdr->length;
Oren Weilfb7d8792011-05-15 13:43:42 +0300131 if (mei_hdr->msg_complete) {
132 cl->status = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200133 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300134 dev_dbg(&dev->pdev->dev,
Tomas Winklera4136b42012-09-11 00:43:22 +0300135 "completed read H cl = %d, ME cl = %d, length = %lu\n",
Oren Weilfb7d8792011-05-15 13:43:42 +0300136 cl->host_client_id,
137 cl->me_client_id,
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200138 cb_pos->buf_idx);
139
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200140 list_add_tail(&cb_pos->list,
141 &complete_list->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300142 }
143
144 break;
145 }
146
147 }
148
149quit:
150 dev_dbg(&dev->pdev->dev, "message read\n");
151 if (!buffer) {
Tomas Winkleredf1eed2012-02-09 19:25:54 +0200152 mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length);
Oren Weilfb7d8792011-05-15 13:43:42 +0300153 dev_dbg(&dev->pdev->dev, "discarding message, header =%08x.\n",
154 *(u32 *) dev->rd_msg_buf);
155 }
156
157 return 0;
158}
159
160/**
Oren Weilfb7d8792011-05-15 13:43:42 +0300161 * _mei_irq_thread_close - processes close related operation.
162 *
163 * @dev: the device structure.
164 * @slots: free slots.
165 * @cb_pos: callback block.
166 * @cl: private data of the file object.
167 * @cmpl_list: complete list.
168 *
169 * returns 0, OK; otherwise, error.
170 */
171static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots,
172 struct mei_cl_cb *cb_pos,
173 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200174 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300175{
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300176 if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200177 sizeof(struct hbm_client_connect_request)))
Oren Weilfb7d8792011-05-15 13:43:42 +0300178 return -EBADMSG;
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300179
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200180 *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request));
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300181
182 if (mei_disconnect(dev, cl)) {
183 cl->status = 0;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200184 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200185 list_move_tail(&cb_pos->list, &cmpl_list->list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300186 return -EMSGSIZE;
187 } else {
188 cl->state = MEI_FILE_DISCONNECTING;
189 cl->status = 0;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200190 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200191 list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300192 cl->timer_count = MEI_CONNECT_TIMEOUT;
Oren Weilfb7d8792011-05-15 13:43:42 +0300193 }
194
195 return 0;
196}
197
198/**
199 * is_treat_specially_client - checks if the message belongs
200 * to the file private data.
201 *
202 * @cl: private data of the file object
203 * @rs: connect response bus message
204 *
205 */
206static bool is_treat_specially_client(struct mei_cl *cl,
207 struct hbm_client_connect_response *rs)
208{
209
210 if (cl->host_client_id == rs->host_addr &&
211 cl->me_client_id == rs->me_addr) {
212 if (!rs->status) {
213 cl->state = MEI_FILE_CONNECTED;
214 cl->status = 0;
215
216 } else {
217 cl->state = MEI_FILE_DISCONNECTED;
218 cl->status = -ENODEV;
219 }
220 cl->timer_count = 0;
221
222 return true;
223 }
224 return false;
225}
226
227/**
228 * mei_client_connect_response - connects to response irq routine
229 *
230 * @dev: the device structure
231 * @rs: connect response bus message
232 */
233static void mei_client_connect_response(struct mei_device *dev,
234 struct hbm_client_connect_response *rs)
235{
236
237 struct mei_cl *cl;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200238 struct mei_cl_cb *pos = NULL, *next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300239
240 dev_dbg(&dev->pdev->dev,
241 "connect_response:\n"
242 "ME Client = %d\n"
243 "Host Client = %d\n"
244 "Status = %d\n",
245 rs->me_addr,
246 rs->host_addr,
247 rs->status);
248
249 /* if WD or iamthif client treat specially */
250
251 if (is_treat_specially_client(&(dev->wd_cl), rs)) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300252 dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n");
Tomas Winkler70cd5332011-12-22 18:50:50 +0200253 mei_watchdog_register(dev);
Oren Weil9ce178e2011-09-07 09:03:09 +0300254
Tomas Winkler70cd5332011-12-22 18:50:50 +0200255 /* next step in the state maching */
Tomas Winkler19838fb2012-11-01 21:17:15 +0200256 mei_amthif_host_init(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300257 return;
258 }
259
260 if (is_treat_specially_client(&(dev->iamthif_cl), rs)) {
261 dev->iamthif_state = MEI_IAMTHIF_IDLE;
262 return;
263 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200264 list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200265
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200266 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200267 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200268 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200269 return;
270 }
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200271 if (pos->fop_type == MEI_FOP_IOCTL) {
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200272 if (is_treat_specially_client(cl, rs)) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200273 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200274 cl->status = 0;
275 cl->timer_count = 0;
276 break;
Oren Weilfb7d8792011-05-15 13:43:42 +0300277 }
278 }
279 }
280}
281
282/**
283 * mei_client_disconnect_response - disconnects from response irq routine
284 *
285 * @dev: the device structure
286 * @rs: disconnect response bus message
287 */
288static void mei_client_disconnect_response(struct mei_device *dev,
289 struct hbm_client_connect_response *rs)
290{
291 struct mei_cl *cl;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200292 struct mei_cl_cb *pos = NULL, *next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300293
294 dev_dbg(&dev->pdev->dev,
295 "disconnect_response:\n"
296 "ME Client = %d\n"
297 "Host Client = %d\n"
298 "Status = %d\n",
299 rs->me_addr,
300 rs->host_addr,
301 rs->status);
302
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200303 list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200304 cl = pos->cl;
Oren Weilfb7d8792011-05-15 13:43:42 +0300305
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200306 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200307 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200308 return;
309 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300310
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200311 dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n");
312 if (cl->host_client_id == rs->host_addr &&
313 cl->me_client_id == rs->me_addr) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300314
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200315 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200316 if (!rs->status)
317 cl->state = MEI_FILE_DISCONNECTED;
Oren Weilfb7d8792011-05-15 13:43:42 +0300318
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200319 cl->status = 0;
320 cl->timer_count = 0;
321 break;
Oren Weilfb7d8792011-05-15 13:43:42 +0300322 }
323 }
324}
325
326/**
327 * same_flow_addr - tells if they have the same address.
328 *
329 * @file: private data of the file object.
330 * @flow: flow control.
331 *
332 * returns !=0, same; 0,not.
333 */
334static int same_flow_addr(struct mei_cl *cl, struct hbm_flow_control *flow)
335{
336 return (cl->host_client_id == flow->host_addr &&
337 cl->me_client_id == flow->me_addr);
338}
339
340/**
341 * add_single_flow_creds - adds single buffer credentials.
342 *
343 * @file: private data ot the file object.
344 * @flow: flow control.
345 */
346static void add_single_flow_creds(struct mei_device *dev,
347 struct hbm_flow_control *flow)
348{
349 struct mei_me_client *client;
350 int i;
351
Tomas Winklercf9673d2011-06-06 10:44:33 +0300352 for (i = 0; i < dev->me_clients_num; i++) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300353 client = &dev->me_clients[i];
354 if (client && flow->me_addr == client->client_id) {
355 if (client->props.single_recv_buf) {
356 client->mei_flow_ctrl_creds++;
357 dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n",
358 flow->me_addr);
359 dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n",
360 client->mei_flow_ctrl_creds);
361 } else {
362 BUG(); /* error in flow control */
363 }
364 }
365 }
366}
367
368/**
369 * mei_client_flow_control_response - flow control response irq routine
370 *
371 * @dev: the device structure
372 * @flow_control: flow control response bus message
373 */
374static void mei_client_flow_control_response(struct mei_device *dev,
375 struct hbm_flow_control *flow_control)
376{
377 struct mei_cl *cl_pos = NULL;
378 struct mei_cl *cl_next = NULL;
379
380 if (!flow_control->host_addr) {
381 /* single receive buffer */
382 add_single_flow_creds(dev, flow_control);
383 } else {
384 /* normal connection */
385 list_for_each_entry_safe(cl_pos, cl_next,
386 &dev->file_list, link) {
387 dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in file_list\n");
388
389 dev_dbg(&dev->pdev->dev, "cl of host client %d ME client %d.\n",
390 cl_pos->host_client_id,
391 cl_pos->me_client_id);
392 dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n",
393 flow_control->host_addr,
394 flow_control->me_addr);
395 if (same_flow_addr(cl_pos, flow_control)) {
396 dev_dbg(&dev->pdev->dev, "recv ctrl msg for host %d ME %d.\n",
397 flow_control->host_addr,
398 flow_control->me_addr);
399 cl_pos->mei_flow_ctrl_creds++;
400 dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n",
401 cl_pos->mei_flow_ctrl_creds);
402 break;
403 }
404 }
405 }
406}
407
408/**
409 * same_disconn_addr - tells if they have the same address
410 *
411 * @file: private data of the file object.
412 * @disconn: disconnection request.
413 *
414 * returns !=0, same; 0,not.
415 */
416static int same_disconn_addr(struct mei_cl *cl,
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200417 struct hbm_client_connect_request *req)
Oren Weilfb7d8792011-05-15 13:43:42 +0300418{
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200419 return (cl->host_client_id == req->host_addr &&
420 cl->me_client_id == req->me_addr);
Oren Weilfb7d8792011-05-15 13:43:42 +0300421}
422
423/**
424 * mei_client_disconnect_request - disconnects from request irq routine
425 *
426 * @dev: the device structure.
427 * @disconnect_req: disconnect request bus message.
428 */
429static void mei_client_disconnect_request(struct mei_device *dev,
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200430 struct hbm_client_connect_request *disconnect_req)
Oren Weilfb7d8792011-05-15 13:43:42 +0300431{
432 struct mei_msg_hdr *mei_hdr;
433 struct hbm_client_connect_response *disconnect_res;
434 struct mei_cl *cl_pos = NULL;
435 struct mei_cl *cl_next = NULL;
436
437 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
438 if (same_disconn_addr(cl_pos, disconnect_req)) {
439 dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
440 disconnect_req->host_addr,
441 disconnect_req->me_addr);
442 cl_pos->state = MEI_FILE_DISCONNECTED;
443 cl_pos->timer_count = 0;
Tomas Winklerd242a0a2012-07-04 19:24:50 +0300444 if (cl_pos == &dev->wd_cl)
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300445 dev->wd_pending = false;
Tomas Winklerd242a0a2012-07-04 19:24:50 +0300446 else if (cl_pos == &dev->iamthif_cl)
Oren Weilfb7d8792011-05-15 13:43:42 +0300447 dev->iamthif_timer = 0;
448
449 /* prepare disconnect response */
450 mei_hdr =
451 (struct mei_msg_hdr *) &dev->ext_msg_buf[0];
452 mei_hdr->host_addr = 0;
453 mei_hdr->me_addr = 0;
454 mei_hdr->length =
455 sizeof(struct hbm_client_connect_response);
456 mei_hdr->msg_complete = 1;
457 mei_hdr->reserved = 0;
458
459 disconnect_res =
460 (struct hbm_client_connect_response *)
461 &dev->ext_msg_buf[1];
462 disconnect_res->host_addr = cl_pos->host_client_id;
463 disconnect_res->me_addr = cl_pos->me_client_id;
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200464 disconnect_res->hbm_cmd = CLIENT_DISCONNECT_RES_CMD;
Oren Weilfb7d8792011-05-15 13:43:42 +0300465 disconnect_res->status = 0;
466 dev->extra_write_index = 2;
467 break;
468 }
469 }
470}
471
472
473/**
474 * mei_irq_thread_read_bus_message - bottom half read routine after ISR to
475 * handle the read bus message cmd processing.
476 *
477 * @dev: the device structure
478 * @mei_hdr: header of bus message
479 */
480static void mei_irq_thread_read_bus_message(struct mei_device *dev,
481 struct mei_msg_hdr *mei_hdr)
482{
483 struct mei_bus_message *mei_msg;
484 struct hbm_host_version_response *version_res;
485 struct hbm_client_connect_response *connect_res;
486 struct hbm_client_connect_response *disconnect_res;
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200487 struct hbm_client_connect_request *disconnect_req;
Oren Weilfb7d8792011-05-15 13:43:42 +0300488 struct hbm_flow_control *flow_control;
489 struct hbm_props_response *props_res;
490 struct hbm_host_enum_response *enum_res;
Oren Weilfb7d8792011-05-15 13:43:42 +0300491 struct hbm_host_stop_request *host_stop_req;
Oren Weilabc51b62011-09-21 16:45:30 +0300492 int res;
Oren Weilfb7d8792011-05-15 13:43:42 +0300493
Oren Weilfb7d8792011-05-15 13:43:42 +0300494
495 /* read the message to our buffer */
Oren Weilfb7d8792011-05-15 13:43:42 +0300496 BUG_ON(mei_hdr->length >= sizeof(dev->rd_msg_buf));
Tomas Winkleredf1eed2012-02-09 19:25:54 +0200497 mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length);
498 mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;
Oren Weilfb7d8792011-05-15 13:43:42 +0300499
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200500 switch (mei_msg->hbm_cmd) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300501 case HOST_START_RES_CMD:
502 version_res = (struct hbm_host_version_response *) mei_msg;
503 if (version_res->host_version_supported) {
504 dev->version.major_version = HBM_MAJOR_VERSION;
505 dev->version.minor_version = HBM_MINOR_VERSION;
Tomas Winklerb210d752012-08-07 00:03:56 +0300506 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
Oren Weilfb7d8792011-05-15 13:43:42 +0300507 dev->init_clients_state == MEI_START_MESSAGE) {
508 dev->init_clients_timer = 0;
Tomas Winklerc95efb72011-05-25 17:28:21 +0300509 mei_host_enum_clients_message(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300510 } else {
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300511 dev->recvd_msg = false;
Oren Weilfb7d8792011-05-15 13:43:42 +0300512 dev_dbg(&dev->pdev->dev, "IMEI reset due to received host start response bus message.\n");
513 mei_reset(dev, 1);
514 return;
515 }
516 } else {
517 dev->version = version_res->me_max_version;
518 /* send stop message */
Tomas Winkler97d5cb02012-03-06 22:34:32 +0200519 mei_hdr = (struct mei_msg_hdr *)&dev->wr_msg_buf[0];
Oren Weilfb7d8792011-05-15 13:43:42 +0300520 mei_hdr->host_addr = 0;
521 mei_hdr->me_addr = 0;
522 mei_hdr->length = sizeof(struct hbm_host_stop_request);
523 mei_hdr->msg_complete = 1;
524 mei_hdr->reserved = 0;
525
526 host_stop_req = (struct hbm_host_stop_request *)
527 &dev->wr_msg_buf[1];
528
529 memset(host_stop_req,
530 0,
531 sizeof(struct hbm_host_stop_request));
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200532 host_stop_req->hbm_cmd = HOST_STOP_REQ_CMD;
Oren Weilfb7d8792011-05-15 13:43:42 +0300533 host_stop_req->reason = DRIVER_STOP_REQUEST;
534 mei_write_message(dev, mei_hdr,
535 (unsigned char *) (host_stop_req),
536 mei_hdr->length);
537 dev_dbg(&dev->pdev->dev, "version mismatch.\n");
538 return;
539 }
540
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300541 dev->recvd_msg = true;
Oren Weilfb7d8792011-05-15 13:43:42 +0300542 dev_dbg(&dev->pdev->dev, "host start response message received.\n");
543 break;
544
545 case CLIENT_CONNECT_RES_CMD:
546 connect_res =
547 (struct hbm_client_connect_response *) mei_msg;
548 mei_client_connect_response(dev, connect_res);
549 dev_dbg(&dev->pdev->dev, "client connect response message received.\n");
550 wake_up(&dev->wait_recvd_msg);
551 break;
552
553 case CLIENT_DISCONNECT_RES_CMD:
554 disconnect_res =
555 (struct hbm_client_connect_response *) mei_msg;
Tomas Winkler441ab502011-12-13 23:39:34 +0200556 mei_client_disconnect_response(dev, disconnect_res);
Oren Weilfb7d8792011-05-15 13:43:42 +0300557 dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n");
558 wake_up(&dev->wait_recvd_msg);
559 break;
560
561 case MEI_FLOW_CONTROL_CMD:
562 flow_control = (struct hbm_flow_control *) mei_msg;
563 mei_client_flow_control_response(dev, flow_control);
564 dev_dbg(&dev->pdev->dev, "client flow control response message received.\n");
565 break;
566
567 case HOST_CLIENT_PROPERTIES_RES_CMD:
568 props_res = (struct hbm_props_response *)mei_msg;
569 if (props_res->status || !dev->me_clients) {
570 dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n");
571 mei_reset(dev, 1);
572 return;
573 }
Tomas Winkler441ab502011-12-13 23:39:34 +0200574 if (dev->me_clients[dev->me_client_presentation_num]
Oren Weilfb7d8792011-05-15 13:43:42 +0300575 .client_id == props_res->address) {
576
577 dev->me_clients[dev->me_client_presentation_num].props
578 = props_res->client_properties;
579
Tomas Winklerb210d752012-08-07 00:03:56 +0300580 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
Oren Weilfb7d8792011-05-15 13:43:42 +0300581 dev->init_clients_state ==
582 MEI_CLIENT_PROPERTIES_MESSAGE) {
583 dev->me_client_index++;
584 dev->me_client_presentation_num++;
Oren Weilabc51b62011-09-21 16:45:30 +0300585
Justin P. Mattock5f9092f32012-03-12 07:18:09 -0700586 /** Send Client Properties request **/
Oren Weilabc51b62011-09-21 16:45:30 +0300587 res = mei_host_client_properties(dev);
588 if (res < 0) {
589 dev_dbg(&dev->pdev->dev, "mei_host_client_properties() failed");
590 return;
591 } else if (!res) {
592 /*
593 * No more clients to send to.
594 * Clear Map for indicating now ME clients
595 * with associated host client
596 */
597 bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
598 dev->open_handle_count = 0;
599
600 /*
601 * Reserving the first three client IDs
602 * Client Id 0 - Reserved for MEI Bus Message communications
603 * Client Id 1 - Reserved for Watchdog
604 * Client ID 2 - Reserved for AMTHI
605 */
606 bitmap_set(dev->host_clients_map, 0, 3);
Tomas Winklerb210d752012-08-07 00:03:56 +0300607 dev->dev_state = MEI_DEV_ENABLED;
Oren Weilabc51b62011-09-21 16:45:30 +0300608
609 /* if wd initialization fails, initialization the AMTHI client,
610 * otherwise the AMTHI client will be initialized after the WD client connect response
611 * will be received
612 */
613 if (mei_wd_host_init(dev))
Tomas Winkler19838fb2012-11-01 21:17:15 +0200614 mei_amthif_host_init(dev);
Oren Weilabc51b62011-09-21 16:45:30 +0300615 }
616
Oren Weilfb7d8792011-05-15 13:43:42 +0300617 } else {
618 dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message");
619 mei_reset(dev, 1);
620 return;
621 }
622 } else {
623 dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message for wrong client ID\n");
624 mei_reset(dev, 1);
625 return;
626 }
627 break;
628
629 case HOST_ENUM_RES_CMD:
630 enum_res = (struct hbm_host_enum_response *) mei_msg;
631 memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
Tomas Winklerb210d752012-08-07 00:03:56 +0300632 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
Oren Weilfb7d8792011-05-15 13:43:42 +0300633 dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) {
634 dev->init_clients_timer = 0;
635 dev->me_client_presentation_num = 0;
636 dev->me_client_index = 0;
Tomas Winklerc95efb72011-05-25 17:28:21 +0300637 mei_allocate_me_clients_storage(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300638 dev->init_clients_state =
639 MEI_CLIENT_PROPERTIES_MESSAGE;
Tomas Winklerc95efb72011-05-25 17:28:21 +0300640 mei_host_client_properties(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300641 } else {
642 dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n");
643 mei_reset(dev, 1);
644 return;
645 }
646 break;
647
648 case HOST_STOP_RES_CMD:
Tomas Winklerb210d752012-08-07 00:03:56 +0300649 dev->dev_state = MEI_DEV_DISABLED;
Oren Weilfb7d8792011-05-15 13:43:42 +0300650 dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n");
651 mei_reset(dev, 1);
652 break;
653
654 case CLIENT_DISCONNECT_REQ_CMD:
655 /* search for client */
Tomas Winkleraeba4a02012-11-11 17:38:04 +0200656 disconnect_req = (struct hbm_client_connect_request *)mei_msg;
Oren Weilfb7d8792011-05-15 13:43:42 +0300657 mei_client_disconnect_request(dev, disconnect_req);
658 break;
659
660 case ME_STOP_REQ_CMD:
661 /* prepare stop request */
662 mei_hdr = (struct mei_msg_hdr *) &dev->ext_msg_buf[0];
663 mei_hdr->host_addr = 0;
664 mei_hdr->me_addr = 0;
665 mei_hdr->length = sizeof(struct hbm_host_stop_request);
666 mei_hdr->msg_complete = 1;
667 mei_hdr->reserved = 0;
668 host_stop_req =
669 (struct hbm_host_stop_request *) &dev->ext_msg_buf[1];
670 memset(host_stop_req, 0, sizeof(struct hbm_host_stop_request));
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200671 host_stop_req->hbm_cmd = HOST_STOP_REQ_CMD;
Oren Weilfb7d8792011-05-15 13:43:42 +0300672 host_stop_req->reason = DRIVER_STOP_REQUEST;
673 host_stop_req->reserved[0] = 0;
674 host_stop_req->reserved[1] = 0;
675 dev->extra_write_index = 2;
676 break;
677
678 default:
679 BUG();
680 break;
681
682 }
683}
684
685
686/**
687 * _mei_hb_read - processes read related operation.
688 *
689 * @dev: the device structure.
690 * @slots: free slots.
691 * @cb_pos: callback block.
692 * @cl: private data of the file object.
693 * @cmpl_list: complete list.
694 *
695 * returns 0, OK; otherwise, error.
696 */
697static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots,
698 struct mei_cl_cb *cb_pos,
699 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200700 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300701{
Tomas Winkler1e69d642012-05-29 16:39:12 +0300702 if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
Oren Weilfb7d8792011-05-15 13:43:42 +0300703 sizeof(struct hbm_flow_control))) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300704 /* return the cancel routine */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200705 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300706 return -EBADMSG;
707 }
708
Tomas Winkler7bdf72d2012-07-04 19:24:52 +0300709 *slots -= mei_data2slots(sizeof(struct hbm_flow_control));
710
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200711 if (mei_send_flow_control(dev, cl)) {
712 cl->status = -ENODEV;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200713 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200714 list_move_tail(&cb_pos->list, &cmpl_list->list);
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200715 return -ENODEV;
716 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200717 list_move_tail(&cb_pos->list, &dev->read_list.list);
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200718
Oren Weilfb7d8792011-05-15 13:43:42 +0300719 return 0;
720}
721
722
723/**
724 * _mei_irq_thread_ioctl - processes ioctl related operation.
725 *
726 * @dev: the device structure.
727 * @slots: free slots.
728 * @cb_pos: callback block.
729 * @cl: private data of the file object.
730 * @cmpl_list: complete list.
731 *
732 * returns 0, OK; otherwise, error.
733 */
734static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots,
735 struct mei_cl_cb *cb_pos,
736 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200737 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300738{
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300739 if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
Oren Weilfb7d8792011-05-15 13:43:42 +0300740 sizeof(struct hbm_client_connect_request))) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300741 /* return the cancel routine */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200742 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300743 return -EBADMSG;
744 }
745
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300746 cl->state = MEI_FILE_CONNECTING;
747 *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request));
748 if (mei_connect(dev, cl)) {
749 cl->status = -ENODEV;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200750 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200751 list_del(&cb_pos->list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300752 return -ENODEV;
753 } else {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200754 list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300755 cl->timer_count = MEI_CONNECT_TIMEOUT;
756 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300757 return 0;
758}
759
760/**
761 * _mei_irq_thread_cmpl - processes completed and no-iamthif operation.
762 *
763 * @dev: the device structure.
764 * @slots: free slots.
765 * @cb_pos: callback block.
766 * @cl: private data of the file object.
767 * @cmpl_list: complete list.
768 *
769 * returns 0, OK; otherwise, error.
770 */
771static int _mei_irq_thread_cmpl(struct mei_device *dev, s32 *slots,
772 struct mei_cl_cb *cb_pos,
773 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200774 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300775{
776 struct mei_msg_hdr *mei_hdr;
777
778 if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) +
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200779 (cb_pos->request_buffer.size - cb_pos->buf_idx))) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300780 mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
781 mei_hdr->host_addr = cl->host_client_id;
782 mei_hdr->me_addr = cl->me_client_id;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200783 mei_hdr->length = cb_pos->request_buffer.size - cb_pos->buf_idx;
Oren Weilfb7d8792011-05-15 13:43:42 +0300784 mei_hdr->msg_complete = 1;
785 mei_hdr->reserved = 0;
786 dev_dbg(&dev->pdev->dev, "cb_pos->request_buffer.size =%d"
787 "mei_hdr->msg_complete = %d\n",
788 cb_pos->request_buffer.size,
789 mei_hdr->msg_complete);
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200790 dev_dbg(&dev->pdev->dev, "cb_pos->buf_idx =%lu\n",
791 cb_pos->buf_idx);
Oren Weilfb7d8792011-05-15 13:43:42 +0300792 dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n",
793 mei_hdr->length);
Tomas Winkler7bdf72d2012-07-04 19:24:52 +0300794 *slots -= mei_data2slots(mei_hdr->length);
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200795 if (mei_write_message(dev, mei_hdr,
Oren Weilfb7d8792011-05-15 13:43:42 +0300796 (unsigned char *)
797 (cb_pos->request_buffer.data +
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200798 cb_pos->buf_idx),
Oren Weilfb7d8792011-05-15 13:43:42 +0300799 mei_hdr->length)) {
800 cl->status = -ENODEV;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200801 list_move_tail(&cb_pos->list, &cmpl_list->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300802 return -ENODEV;
803 } else {
804 if (mei_flow_ctrl_reduce(dev, cl))
805 return -ENODEV;
806 cl->status = 0;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200807 cb_pos->buf_idx += mei_hdr->length;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200808 list_move_tail(&cb_pos->list, &dev->write_waiting_list.list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300809 }
Tomas Winkler24aadc82012-06-25 23:46:27 +0300810 } else if (*slots == dev->hbuf_depth) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300811 /* buffer is still empty */
812 mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
813 mei_hdr->host_addr = cl->host_client_id;
814 mei_hdr->me_addr = cl->me_client_id;
815 mei_hdr->length =
816 (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
817 mei_hdr->msg_complete = 0;
818 mei_hdr->reserved = 0;
Tomas Winkler7bdf72d2012-07-04 19:24:52 +0300819 *slots -= mei_data2slots(mei_hdr->length);
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200820 if (mei_write_message(dev, mei_hdr,
Oren Weilfb7d8792011-05-15 13:43:42 +0300821 (unsigned char *)
822 (cb_pos->request_buffer.data +
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200823 cb_pos->buf_idx),
Oren Weilfb7d8792011-05-15 13:43:42 +0300824 mei_hdr->length)) {
825 cl->status = -ENODEV;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200826 list_move_tail(&cb_pos->list, &cmpl_list->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300827 return -ENODEV;
828 } else {
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200829 cb_pos->buf_idx += mei_hdr->length;
Oren Weilfb7d8792011-05-15 13:43:42 +0300830 dev_dbg(&dev->pdev->dev,
831 "cb_pos->request_buffer.size =%d"
832 " mei_hdr->msg_complete = %d\n",
833 cb_pos->request_buffer.size,
834 mei_hdr->msg_complete);
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200835 dev_dbg(&dev->pdev->dev, "cb_pos->buf_idx =%lu\n",
836 cb_pos->buf_idx);
Oren Weilfb7d8792011-05-15 13:43:42 +0300837 dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n",
838 mei_hdr->length);
839 }
840 return -EMSGSIZE;
841 } else {
842 return -EBADMSG;
843 }
844
845 return 0;
846}
847
848/**
Oren Weilfb7d8792011-05-15 13:43:42 +0300849 * mei_irq_thread_read_handler - bottom half read routine after ISR to
850 * handle the read processing.
851 *
852 * @cmpl_list: An instance of our list structure
853 * @dev: the device structure
854 * @slots: slots to read.
855 *
856 * returns 0 on success, <0 on failure.
857 */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200858static int mei_irq_thread_read_handler(struct mei_cl_cb *cmpl_list,
Oren Weilfb7d8792011-05-15 13:43:42 +0300859 struct mei_device *dev,
860 s32 *slots)
861{
862 struct mei_msg_hdr *mei_hdr;
863 struct mei_cl *cl_pos = NULL;
864 struct mei_cl *cl_next = NULL;
865 int ret = 0;
866
867 if (!dev->rd_msg_hdr) {
868 dev->rd_msg_hdr = mei_mecbrw_read(dev);
869 dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
870 (*slots)--;
871 dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
872 }
873 mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr;
874 dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n", mei_hdr->length);
875
876 if (mei_hdr->reserved || !dev->rd_msg_hdr) {
877 dev_dbg(&dev->pdev->dev, "corrupted message header.\n");
878 ret = -EBADMSG;
879 goto end;
880 }
881
882 if (mei_hdr->host_addr || mei_hdr->me_addr) {
883 list_for_each_entry_safe(cl_pos, cl_next,
884 &dev->file_list, link) {
885 dev_dbg(&dev->pdev->dev,
886 "list_for_each_entry_safe read host"
887 " client = %d, ME client = %d\n",
888 cl_pos->host_client_id,
889 cl_pos->me_client_id);
890 if (cl_pos->host_client_id == mei_hdr->host_addr &&
891 cl_pos->me_client_id == mei_hdr->me_addr)
892 break;
893 }
894
895 if (&cl_pos->link == &dev->file_list) {
896 dev_dbg(&dev->pdev->dev, "corrupted message header\n");
897 ret = -EBADMSG;
898 goto end;
899 }
900 }
901 if (((*slots) * sizeof(u32)) < mei_hdr->length) {
902 dev_dbg(&dev->pdev->dev,
903 "we can't read the message slots =%08x.\n",
904 *slots);
905 /* we can't read the message */
906 ret = -ERANGE;
907 goto end;
908 }
909
910 /* decide where to read the message too */
911 if (!mei_hdr->host_addr) {
912 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n");
913 mei_irq_thread_read_bus_message(dev, mei_hdr);
914 dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n");
915 } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id &&
916 (MEI_FILE_CONNECTED == dev->iamthif_cl.state) &&
917 (dev->iamthif_state == MEI_IAMTHIF_READING)) {
918 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n");
919 dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n",
920 mei_hdr->length);
Tomas Winkler19838fb2012-11-01 21:17:15 +0200921
922 ret = mei_amthif_irq_read_message(cmpl_list, dev, mei_hdr);
Oren Weilfb7d8792011-05-15 13:43:42 +0300923 if (ret)
924 goto end;
925
926 } else {
927 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n");
928 ret = mei_irq_thread_read_client_message(cmpl_list,
929 dev, mei_hdr);
930 if (ret)
931 goto end;
932
933 }
934
935 /* reset the number of slots and header */
936 *slots = mei_count_full_read_slots(dev);
937 dev->rd_msg_hdr = 0;
938
939 if (*slots == -EOVERFLOW) {
940 /* overflow - reset */
941 dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n");
942 /* set the event since message has been read */
943 ret = -ERANGE;
944 goto end;
945 }
946end:
947 return ret;
948}
949
950
951/**
952 * mei_irq_thread_write_handler - bottom half write routine after
953 * ISR to handle the write processing.
954 *
955 * @cmpl_list: An instance of our list structure
956 * @dev: the device structure
957 * @slots: slots to write.
958 *
959 * returns 0 on success, <0 on failure.
960 */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200961static int mei_irq_thread_write_handler(struct mei_cl_cb *cmpl_list,
962 struct mei_device *dev, s32 *slots)
Oren Weilfb7d8792011-05-15 13:43:42 +0300963{
964
965 struct mei_cl *cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200966 struct mei_cl_cb *pos = NULL, *next = NULL;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200967 struct mei_cl_cb *list;
Oren Weilfb7d8792011-05-15 13:43:42 +0300968 int ret;
969
Tomas Winkler726917f2012-06-25 23:46:28 +0300970 if (!mei_hbuf_is_empty(dev)) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300971 dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n");
972 return 0;
973 }
Tomas Winkler726917f2012-06-25 23:46:28 +0300974 *slots = mei_hbuf_empty_slots(dev);
Tomas Winkler7d5e0e52012-06-19 09:13:36 +0300975 if (*slots <= 0)
976 return -EMSGSIZE;
977
Oren Weilfb7d8792011-05-15 13:43:42 +0300978 /* complete all waiting for write CB */
979 dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n");
980
981 list = &dev->write_waiting_list;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200982 list_for_each_entry_safe(pos, next, &list->list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200983 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200984 if (cl == NULL)
985 continue;
Oren Weilfb7d8792011-05-15 13:43:42 +0300986
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200987 cl->status = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200988 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200989 if (MEI_WRITING == cl->writing_state &&
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200990 pos->fop_type == MEI_FOP_WRITE &&
991 cl != &dev->iamthif_cl) {
Tomas Winkler483136e2012-07-04 19:24:54 +0300992 dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n");
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200993 cl->writing_state = MEI_WRITE_COMPLETE;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200994 list_add_tail(&pos->list, &cmpl_list->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200995 }
996 if (cl == &dev->iamthif_cl) {
997 dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n");
998 if (dev->iamthif_flow_control_pending) {
Tomas Winkler19838fb2012-11-01 21:17:15 +0200999 ret = mei_amthif_irq_read(dev, slots);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001000 if (ret)
1001 return ret;
1002 }
Oren Weilfb7d8792011-05-15 13:43:42 +03001003 }
1004 }
1005
Tomas Winklerc216fde2012-08-16 19:39:43 +03001006 if (dev->wd_state == MEI_WD_STOPPING) {
1007 dev->wd_state = MEI_WD_IDLE;
Oren Weilfb7d8792011-05-15 13:43:42 +03001008 wake_up_interruptible(&dev->wait_stop_wd);
Oren Weilfb7d8792011-05-15 13:43:42 +03001009 }
1010
1011 if (dev->extra_write_index) {
1012 dev_dbg(&dev->pdev->dev, "extra_write_index =%d.\n",
1013 dev->extra_write_index);
1014 mei_write_message(dev,
1015 (struct mei_msg_hdr *) &dev->ext_msg_buf[0],
1016 (unsigned char *) &dev->ext_msg_buf[1],
1017 (dev->extra_write_index - 1) * sizeof(u32));
1018 *slots -= dev->extra_write_index;
1019 dev->extra_write_index = 0;
1020 }
Tomas Winklerb210d752012-08-07 00:03:56 +03001021 if (dev->dev_state == MEI_DEV_ENABLED) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001022 if (dev->wd_pending &&
Tomas Winkler483136e2012-07-04 19:24:54 +03001023 mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001024 if (mei_wd_send(dev))
1025 dev_dbg(&dev->pdev->dev, "wd send failed.\n");
Tomas Winkler483136e2012-07-04 19:24:54 +03001026 else if (mei_flow_ctrl_reduce(dev, &dev->wd_cl))
1027 return -ENODEV;
Oren Weilfb7d8792011-05-15 13:43:42 +03001028
Tomas Winklereb9af0a2011-05-25 17:28:22 +03001029 dev->wd_pending = false;
Oren Weilfb7d8792011-05-15 13:43:42 +03001030
Tomas Winklerc216fde2012-08-16 19:39:43 +03001031 if (dev->wd_state == MEI_WD_RUNNING)
Tomas Winkler248ffdf2012-08-16 19:39:42 +03001032 *slots -= mei_data2slots(MEI_WD_START_MSG_SIZE);
Tomas Winklerd242a0a2012-07-04 19:24:50 +03001033 else
Tomas Winkler248ffdf2012-08-16 19:39:42 +03001034 *slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE);
Oren Weilfb7d8792011-05-15 13:43:42 +03001035 }
1036 }
Oren Weilfb7d8792011-05-15 13:43:42 +03001037
1038 /* complete control write list CB */
Tomas Winklerc8372092011-11-27 21:43:33 +02001039 dev_dbg(&dev->pdev->dev, "complete control write list cb.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001040 list_for_each_entry_safe(pos, next, &dev->ctrl_wr_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +02001041 cl = pos->cl;
Tomas Winklerc8372092011-11-27 21:43:33 +02001042 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001043 list_del(&pos->list);
Tomas Winklerc8372092011-11-27 21:43:33 +02001044 return -ENODEV;
Oren Weilfb7d8792011-05-15 13:43:42 +03001045 }
Tomas Winkler4b8960b2012-11-11 17:38:00 +02001046 switch (pos->fop_type) {
1047 case MEI_FOP_CLOSE:
Tomas Winklerc8372092011-11-27 21:43:33 +02001048 /* send disconnect message */
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001049 ret = _mei_irq_thread_close(dev, slots, pos, cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +02001050 if (ret)
1051 return ret;
1052
1053 break;
Tomas Winkler4b8960b2012-11-11 17:38:00 +02001054 case MEI_FOP_READ:
Tomas Winklerc8372092011-11-27 21:43:33 +02001055 /* send flow control message */
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001056 ret = _mei_irq_thread_read(dev, slots, pos, cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +02001057 if (ret)
1058 return ret;
1059
1060 break;
Tomas Winkler4b8960b2012-11-11 17:38:00 +02001061 case MEI_FOP_IOCTL:
Tomas Winklerc8372092011-11-27 21:43:33 +02001062 /* connect message */
Natalia Ovsyanikove8cd29d2011-12-05 00:16:54 +02001063 if (mei_other_client_is_connecting(dev, cl))
Tomas Winklerc8372092011-11-27 21:43:33 +02001064 continue;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001065 ret = _mei_irq_thread_ioctl(dev, slots, pos, cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +02001066 if (ret)
1067 return ret;
1068
1069 break;
1070
1071 default:
1072 BUG();
1073 }
1074
Oren Weilfb7d8792011-05-15 13:43:42 +03001075 }
1076 /* complete write list CB */
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001077 dev_dbg(&dev->pdev->dev, "complete write list cb.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001078 list_for_each_entry_safe(pos, next, &dev->write_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +02001079 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001080 if (cl == NULL)
1081 continue;
Oren Weilfb7d8792011-05-15 13:43:42 +03001082
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001083 if (cl != &dev->iamthif_cl) {
Tomas Winklerd2041152012-06-19 09:13:34 +03001084 if (mei_flow_ctrl_creds(dev, cl) <= 0) {
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001085 dev_dbg(&dev->pdev->dev,
Tomas Winkler483136e2012-07-04 19:24:54 +03001086 "No flow control credentials for client %d, not sending.\n",
1087 cl->host_client_id);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001088 continue;
Oren Weilfb7d8792011-05-15 13:43:42 +03001089 }
Tomas Winkler483136e2012-07-04 19:24:54 +03001090 ret = _mei_irq_thread_cmpl(dev, slots, pos,
1091 cl, cmpl_list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001092 if (ret)
1093 return ret;
1094
1095 } else if (cl == &dev->iamthif_cl) {
1096 /* IAMTHIF IOCTL */
1097 dev_dbg(&dev->pdev->dev, "complete amthi write cb.\n");
Tomas Winklerd2041152012-06-19 09:13:34 +03001098 if (mei_flow_ctrl_creds(dev, cl) <= 0) {
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001099 dev_dbg(&dev->pdev->dev,
Tomas Winkler483136e2012-07-04 19:24:54 +03001100 "No flow control credentials for amthi client %d.\n",
1101 cl->host_client_id);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001102 continue;
1103 }
Tomas Winkler19838fb2012-11-01 21:17:15 +02001104 ret = mei_amthif_irq_process_completed(dev, slots, pos,
1105 cl, cmpl_list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001106 if (ret)
1107 return ret;
Oren Weilfb7d8792011-05-15 13:43:42 +03001108
1109 }
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001110
Oren Weilfb7d8792011-05-15 13:43:42 +03001111 }
1112 return 0;
1113}
1114
1115
1116
1117/**
1118 * mei_timer - timer function.
1119 *
1120 * @work: pointer to the work_struct structure
1121 *
1122 * NOTE: This function is called by timer interrupt work
1123 */
Oren Weila61c6532011-09-07 09:03:13 +03001124void mei_timer(struct work_struct *work)
Oren Weilfb7d8792011-05-15 13:43:42 +03001125{
1126 unsigned long timeout;
1127 struct mei_cl *cl_pos = NULL;
1128 struct mei_cl *cl_next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001129 struct mei_cl_cb *cb_pos = NULL;
1130 struct mei_cl_cb *cb_next = NULL;
1131
1132 struct mei_device *dev = container_of(work,
Oren Weila61c6532011-09-07 09:03:13 +03001133 struct mei_device, timer_work.work);
Oren Weilfb7d8792011-05-15 13:43:42 +03001134
1135
1136 mutex_lock(&dev->device_lock);
Tomas Winklerb210d752012-08-07 00:03:56 +03001137 if (dev->dev_state != MEI_DEV_ENABLED) {
1138 if (dev->dev_state == MEI_DEV_INIT_CLIENTS) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001139 if (dev->init_clients_timer) {
1140 if (--dev->init_clients_timer == 0) {
1141 dev_dbg(&dev->pdev->dev, "IMEI reset due to init clients timeout ,init clients state = %d.\n",
1142 dev->init_clients_state);
1143 mei_reset(dev, 1);
1144 }
1145 }
1146 }
1147 goto out;
1148 }
1149 /*** connect/disconnect timeouts ***/
1150 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
1151 if (cl_pos->timer_count) {
1152 if (--cl_pos->timer_count == 0) {
1153 dev_dbg(&dev->pdev->dev, "HECI reset due to connect/disconnect timeout.\n");
1154 mei_reset(dev, 1);
1155 goto out;
1156 }
1157 }
1158 }
1159
Oren Weilfb7d8792011-05-15 13:43:42 +03001160 if (dev->iamthif_stall_timer) {
1161 if (--dev->iamthif_stall_timer == 0) {
Masanari Iida32de21f2012-01-25 23:14:56 +09001162 dev_dbg(&dev->pdev->dev, "resetting because of hang to amthi.\n");
Oren Weilfb7d8792011-05-15 13:43:42 +03001163 mei_reset(dev, 1);
1164 dev->iamthif_msg_buf_size = 0;
1165 dev->iamthif_msg_buf_index = 0;
Tomas Winklereb9af0a2011-05-25 17:28:22 +03001166 dev->iamthif_canceled = false;
1167 dev->iamthif_ioctl = true;
Oren Weilfb7d8792011-05-15 13:43:42 +03001168 dev->iamthif_state = MEI_IAMTHIF_IDLE;
1169 dev->iamthif_timer = 0;
1170
Tomas Winkler601a1ef2012-10-09 16:50:20 +02001171 mei_io_cb_free(dev->iamthif_current_cb);
1172 dev->iamthif_current_cb = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001173
1174 dev->iamthif_file_object = NULL;
Tomas Winkler19838fb2012-11-01 21:17:15 +02001175 mei_amthif_run_next_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001176 }
1177 }
1178
1179 if (dev->iamthif_timer) {
1180
1181 timeout = dev->iamthif_timer +
Tomas Winkler3870c322012-11-01 21:17:14 +02001182 mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
Oren Weilfb7d8792011-05-15 13:43:42 +03001183
1184 dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n",
1185 dev->iamthif_timer);
1186 dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout);
1187 dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies);
1188 if (time_after(jiffies, timeout)) {
1189 /*
1190 * User didn't read the AMTHI data on time (15sec)
1191 * freeing AMTHI for other requests
1192 */
1193
1194 dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n");
1195
Tomas Winklere773efc2012-11-11 17:37:58 +02001196 list_for_each_entry_safe(cb_pos, cb_next,
1197 &dev->amthif_rd_complete_list.list, list) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001198
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001199 cl_pos = cb_pos->file_object->private_data;
Oren Weilfb7d8792011-05-15 13:43:42 +03001200
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001201 /* Finding the AMTHI entry. */
1202 if (cl_pos == &dev->iamthif_cl)
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001203 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +03001204 }
Tomas Winkler601a1ef2012-10-09 16:50:20 +02001205 mei_io_cb_free(dev->iamthif_current_cb);
1206 dev->iamthif_current_cb = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001207
1208 dev->iamthif_file_object->private_data = NULL;
1209 dev->iamthif_file_object = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001210 dev->iamthif_timer = 0;
Tomas Winkler19838fb2012-11-01 21:17:15 +02001211 mei_amthif_run_next_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001212
1213 }
1214 }
1215out:
Tomas Winkler441ab502011-12-13 23:39:34 +02001216 schedule_delayed_work(&dev->timer_work, 2 * HZ);
1217 mutex_unlock(&dev->device_lock);
Oren Weilfb7d8792011-05-15 13:43:42 +03001218}
1219
1220/**
1221 * mei_interrupt_thread_handler - function called after ISR to handle the interrupt
1222 * processing.
1223 *
1224 * @irq: The irq number
1225 * @dev_id: pointer to the device structure
1226 *
1227 * returns irqreturn_t
1228 *
1229 */
1230irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id)
1231{
1232 struct mei_device *dev = (struct mei_device *) dev_id;
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001233 struct mei_cl_cb complete_list;
Oren Weilfb7d8792011-05-15 13:43:42 +03001234 struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
1235 struct mei_cl *cl;
1236 s32 slots;
1237 int rets;
1238 bool bus_message_received;
1239
1240
1241 dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n");
1242 /* initialize our complete list */
1243 mutex_lock(&dev->device_lock);
Tomas Winkler0288c7c2011-06-06 10:44:34 +03001244 mei_io_list_init(&complete_list);
Oren Weilfb7d8792011-05-15 13:43:42 +03001245 dev->host_hw_state = mei_hcsr_read(dev);
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001246
1247 /* Ack the interrupt here
Justin P. Mattock5f9092f32012-03-12 07:18:09 -07001248 * In case of MSI we don't go through the quick handler */
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001249 if (pci_dev_msi_enabled(dev->pdev))
1250 mei_reg_write(dev, H_CSR, dev->host_hw_state);
1251
Oren Weilfb7d8792011-05-15 13:43:42 +03001252 dev->me_hw_state = mei_mecsr_read(dev);
1253
1254 /* check if ME wants a reset */
1255 if ((dev->me_hw_state & ME_RDY_HRA) == 0 &&
Tomas Winklerb210d752012-08-07 00:03:56 +03001256 dev->dev_state != MEI_DEV_RESETING &&
1257 dev->dev_state != MEI_DEV_INITIALIZING) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001258 dev_dbg(&dev->pdev->dev, "FW not ready.\n");
1259 mei_reset(dev, 1);
1260 mutex_unlock(&dev->device_lock);
1261 return IRQ_HANDLED;
1262 }
1263
1264 /* check if we need to start the dev */
1265 if ((dev->host_hw_state & H_RDY) == 0) {
1266 if ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA) {
1267 dev_dbg(&dev->pdev->dev, "we need to start the dev.\n");
1268 dev->host_hw_state |= (H_IE | H_IG | H_RDY);
1269 mei_hcsr_set(dev);
Tomas Winklerb210d752012-08-07 00:03:56 +03001270 dev->dev_state = MEI_DEV_INIT_CLIENTS;
Oren Weilfb7d8792011-05-15 13:43:42 +03001271 dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
1272 /* link is established
1273 * start sending messages.
1274 */
Tomas Winklerc95efb72011-05-25 17:28:21 +03001275 mei_host_start_message(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001276 mutex_unlock(&dev->device_lock);
1277 return IRQ_HANDLED;
1278 } else {
1279 dev_dbg(&dev->pdev->dev, "FW not ready.\n");
1280 mutex_unlock(&dev->device_lock);
1281 return IRQ_HANDLED;
1282 }
1283 }
Justin P. Mattock5f9092f32012-03-12 07:18:09 -07001284 /* check slots available for reading */
Oren Weilfb7d8792011-05-15 13:43:42 +03001285 slots = mei_count_full_read_slots(dev);
1286 dev_dbg(&dev->pdev->dev, "slots =%08x extra_write_index =%08x.\n",
1287 slots, dev->extra_write_index);
1288 while (slots > 0 && !dev->extra_write_index) {
1289 dev_dbg(&dev->pdev->dev, "slots =%08x extra_write_index =%08x.\n",
1290 slots, dev->extra_write_index);
1291 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_handler.\n");
1292 rets = mei_irq_thread_read_handler(&complete_list, dev, &slots);
1293 if (rets)
1294 goto end;
1295 }
1296 rets = mei_irq_thread_write_handler(&complete_list, dev, &slots);
1297end:
1298 dev_dbg(&dev->pdev->dev, "end of bottom half function.\n");
1299 dev->host_hw_state = mei_hcsr_read(dev);
Tomas Winkler726917f2012-06-25 23:46:28 +03001300 dev->mei_host_buffer_is_empty = mei_hbuf_is_empty(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001301
1302 bus_message_received = false;
1303 if (dev->recvd_msg && waitqueue_active(&dev->wait_recvd_msg)) {
1304 dev_dbg(&dev->pdev->dev, "received waiting bus message\n");
1305 bus_message_received = true;
1306 }
1307 mutex_unlock(&dev->device_lock);
1308 if (bus_message_received) {
1309 dev_dbg(&dev->pdev->dev, "wake up dev->wait_recvd_msg\n");
1310 wake_up_interruptible(&dev->wait_recvd_msg);
1311 bus_message_received = false;
1312 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001313 if (list_empty(&complete_list.list))
Oren Weilfb7d8792011-05-15 13:43:42 +03001314 return IRQ_HANDLED;
1315
1316
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001317 list_for_each_entry_safe(cb_pos, cb_next, &complete_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +02001318 cl = cb_pos->cl;
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001319 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +03001320 if (cl) {
1321 if (cl != &dev->iamthif_cl) {
1322 dev_dbg(&dev->pdev->dev, "completing call back.\n");
1323 _mei_cmpl(cl, cb_pos);
1324 cb_pos = NULL;
1325 } else if (cl == &dev->iamthif_cl) {
Tomas Winkler19838fb2012-11-01 21:17:15 +02001326 mei_amthif_complete(dev, cb_pos);
Oren Weilfb7d8792011-05-15 13:43:42 +03001327 }
1328 }
1329 }
1330 return IRQ_HANDLED;
1331}