Commit | Line | Data |
---|---|---|
19838fb8 TW |
1 | /* |
2 | * | |
3 | * Intel Management Engine Interface (Intel MEI) Linux driver | |
4 | * Copyright (c) 2003-2012, Intel Corporation. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms and conditions of the GNU General Public License, | |
8 | * version 2, as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | * more details. | |
14 | * | |
15 | */ | |
16 | ||
17 | #include <linux/kernel.h> | |
18 | #include <linux/fs.h> | |
19 | #include <linux/errno.h> | |
20 | #include <linux/types.h> | |
21 | #include <linux/fcntl.h> | |
19838fb8 TW |
22 | #include <linux/ioctl.h> |
23 | #include <linux/cdev.h> | |
24 | #include <linux/list.h> | |
25 | #include <linux/delay.h> | |
26 | #include <linux/sched.h> | |
27 | #include <linux/uuid.h> | |
28 | #include <linux/jiffies.h> | |
29 | #include <linux/uaccess.h> | |
1f180359 | 30 | #include <linux/slab.h> |
19838fb8 | 31 | |
47a73801 | 32 | #include <linux/mei.h> |
19838fb8 TW |
33 | |
34 | #include "mei_dev.h" | |
0edb23fc | 35 | #include "hbm.h" |
90e0b5f1 | 36 | #include "client.h" |
19838fb8 | 37 | |
1a1aca42 TW |
38 | const uuid_le mei_amthif_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, |
39 | 0xac, 0xa8, 0x46, 0xe0, | |
40 | 0xff, 0x65, 0x81, 0x4c); | |
19838fb8 TW |
41 | |
42 | /** | |
43 | * mei_amthif_reset_params - initializes mei device iamthif | |
44 | * | |
45 | * @dev: the device structure | |
46 | */ | |
47 | void mei_amthif_reset_params(struct mei_device *dev) | |
48 | { | |
49 | /* reset iamthif parameters. */ | |
19838fb8 | 50 | dev->iamthif_canceled = false; |
19838fb8 | 51 | dev->iamthif_state = MEI_IAMTHIF_IDLE; |
4a704575 | 52 | dev->iamthif_stall_timer = 0; |
22f96a0e | 53 | dev->iamthif_open_count = 0; |
19838fb8 TW |
54 | } |
55 | ||
56 | /** | |
393b148f | 57 | * mei_amthif_host_init - mei initialization amthif client. |
19838fb8 TW |
58 | * |
59 | * @dev: the device structure | |
d49ed64a | 60 | * @me_cl: me client |
19838fb8 | 61 | * |
ce23139c | 62 | * Return: 0 on success, <0 on failure. |
19838fb8 | 63 | */ |
d49ed64a | 64 | int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl) |
19838fb8 | 65 | { |
781d0d89 | 66 | struct mei_cl *cl = &dev->iamthif_cl; |
d320832f | 67 | int ret; |
19838fb8 | 68 | |
e728ae27 AU |
69 | mutex_lock(&dev->device_lock); |
70 | ||
71 | if (mei_cl_is_connected(cl)) { | |
72 | ret = 0; | |
73 | goto out; | |
74 | } | |
025fb792 | 75 | |
6222f7bf TW |
76 | dev->iamthif_state = MEI_IAMTHIF_IDLE; |
77 | ||
781d0d89 | 78 | mei_cl_init(cl, dev); |
19838fb8 | 79 | |
7851e008 | 80 | ret = mei_cl_link(cl); |
781d0d89 | 81 | if (ret < 0) { |
79563db9 | 82 | dev_err(dev->dev, "amthif: failed cl_link %d\n", ret); |
e728ae27 | 83 | goto out; |
781d0d89 TW |
84 | } |
85 | ||
d49ed64a | 86 | ret = mei_cl_connect(cl, me_cl, NULL); |
64092858 | 87 | |
e728ae27 AU |
88 | out: |
89 | mutex_unlock(&dev->device_lock); | |
64092858 | 90 | return ret; |
19838fb8 TW |
91 | } |
92 | ||
19838fb8 | 93 | /** |
c54bf3ab | 94 | * mei_amthif_read_start - queue message for sending read credential |
19838fb8 | 95 | * |
c54bf3ab | 96 | * @cl: host client |
3030dc05 | 97 | * @fp: file pointer of message recipient |
19838fb8 | 98 | * |
a8605ea2 | 99 | * Return: 0 on success, <0 on failure. |
19838fb8 | 100 | */ |
3030dc05 | 101 | static int mei_amthif_read_start(struct mei_cl *cl, const struct file *fp) |
19838fb8 | 102 | { |
c54bf3ab TW |
103 | struct mei_device *dev = cl->dev; |
104 | struct mei_cl_cb *cb; | |
19838fb8 | 105 | |
3030dc05 | 106 | cb = mei_cl_enqueue_ctrl_wr_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, fp); |
35eda92a AU |
107 | if (!cb) |
108 | return -ENOMEM; | |
19838fb8 | 109 | |
46978ada | 110 | cl->rx_flow_ctrl_creds++; |
19838fb8 | 111 | |
c54bf3ab | 112 | dev->iamthif_state = MEI_IAMTHIF_READING; |
97d549b4 | 113 | cl->fp = cb->fp; |
19838fb8 | 114 | |
19838fb8 TW |
115 | return 0; |
116 | } | |
117 | ||
118 | /** | |
ce23139c | 119 | * mei_amthif_run_next_cmd - send next amt command from queue |
19838fb8 TW |
120 | * |
121 | * @dev: the device structure | |
ab5c4a56 | 122 | * |
a8605ea2 | 123 | * Return: 0 on success, <0 on failure. |
19838fb8 | 124 | */ |
8660172e | 125 | int mei_amthif_run_next_cmd(struct mei_device *dev) |
19838fb8 | 126 | { |
8660172e | 127 | struct mei_cl *cl = &dev->iamthif_cl; |
05e314e2 | 128 | struct mei_cl_cb *cb; |
22393381 | 129 | int ret; |
19838fb8 | 130 | |
19838fb8 | 131 | dev->iamthif_canceled = false; |
19838fb8 | 132 | |
2bf94cab | 133 | dev_dbg(dev->dev, "complete amthif cmd_list cb.\n"); |
19838fb8 | 134 | |
140c7553 AU |
135 | cb = list_first_entry_or_null(&dev->amthif_cmd_list.list, |
136 | typeof(*cb), list); | |
22393381 AU |
137 | if (!cb) { |
138 | dev->iamthif_state = MEI_IAMTHIF_IDLE; | |
97d549b4 | 139 | cl->fp = NULL; |
8660172e | 140 | return 0; |
22393381 | 141 | } |
8660172e | 142 | |
c54bf3ab | 143 | list_del_init(&cb->list); |
22393381 | 144 | dev->iamthif_state = MEI_IAMTHIF_WRITING; |
97d549b4 | 145 | cl->fp = cb->fp; |
22393381 AU |
146 | |
147 | ret = mei_cl_write(cl, cb, false); | |
148 | if (ret < 0) | |
149 | return ret; | |
150 | ||
151 | if (cb->completed) | |
152 | cb->status = mei_amthif_read_start(cl, cb->fp); | |
153 | ||
154 | return 0; | |
19838fb8 TW |
155 | } |
156 | ||
8660172e TW |
157 | /** |
158 | * mei_amthif_write - write amthif data to amthif client | |
159 | * | |
160 | * @cl: host client | |
161 | * @cb: mei call back struct | |
162 | * | |
163 | * Return: 0 on success, <0 on failure. | |
164 | */ | |
165 | int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb) | |
166 | { | |
167 | ||
77b007b1 | 168 | struct mei_device *dev = cl->dev; |
8660172e | 169 | |
8660172e | 170 | list_add_tail(&cb->list, &dev->amthif_cmd_list.list); |
5cf8b2a9 AU |
171 | |
172 | /* | |
173 | * The previous request is still in processing, queue this one. | |
174 | */ | |
ca455faf | 175 | if (dev->iamthif_state != MEI_IAMTHIF_IDLE) |
5cf8b2a9 AU |
176 | return 0; |
177 | ||
8660172e TW |
178 | return mei_amthif_run_next_cmd(dev); |
179 | } | |
744f0f2f | 180 | |
1d9013f0 TW |
181 | /** |
182 | * mei_amthif_poll - the amthif poll function | |
183 | * | |
1d9013f0 TW |
184 | * @file: pointer to file structure |
185 | * @wait: pointer to poll_table structure | |
186 | * | |
187 | * Return: poll mask | |
188 | * | |
189 | * Locking: called under "dev->device_lock" lock | |
190 | */ | |
ca455faf | 191 | unsigned int mei_amthif_poll(struct file *file, poll_table *wait) |
744f0f2f | 192 | { |
ca455faf AU |
193 | struct mei_cl *cl = file->private_data; |
194 | struct mei_cl_cb *cb = mei_cl_read_cb(cl, file); | |
744f0f2f | 195 | unsigned int mask = 0; |
b950ac1d | 196 | |
ca455faf AU |
197 | poll_wait(file, &cl->rx_wait, wait); |
198 | if (cb) | |
1d9013f0 | 199 | mask |= POLLIN | POLLRDNORM; |
b950ac1d | 200 | |
744f0f2f TW |
201 | return mask; |
202 | } | |
203 | ||
19838fb8 | 204 | /** |
9d098192 | 205 | * mei_amthif_irq_write - write iamthif command in irq thread context. |
19838fb8 | 206 | * |
19838fb8 | 207 | * @cl: private data of the file object. |
a8605ea2 | 208 | * @cb: callback block. |
19838fb8 TW |
209 | * @cmpl_list: complete list. |
210 | * | |
a8605ea2 | 211 | * Return: 0, OK; otherwise, error. |
19838fb8 | 212 | */ |
9d098192 TW |
213 | int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, |
214 | struct mei_cl_cb *cmpl_list) | |
19838fb8 | 215 | { |
8660172e | 216 | int ret; |
24c656e5 | 217 | |
8660172e TW |
218 | ret = mei_cl_irq_write(cl, cb, cmpl_list); |
219 | if (ret) | |
220 | return ret; | |
19838fb8 | 221 | |
8660172e | 222 | if (cb->completed) |
62e8e6ad | 223 | cb->status = mei_amthif_read_start(cl, cb->fp); |
24c656e5 | 224 | |
19838fb8 TW |
225 | return 0; |
226 | } | |
227 | ||
228 | /** | |
ce23139c | 229 | * mei_amthif_irq_read_msg - read routine after ISR to |
1a1aca42 | 230 | * handle the read amthif message |
19838fb8 | 231 | * |
db4756fd | 232 | * @cl: mei client |
1a1aca42 | 233 | * @mei_hdr: header of amthif message |
331e4187 | 234 | * @cmpl_list: completed callbacks list |
19838fb8 | 235 | * |
331e4187 | 236 | * Return: -ENODEV if cb is NULL 0 otherwise; error message is in cb->status |
19838fb8 | 237 | */ |
db4756fd | 238 | int mei_amthif_irq_read_msg(struct mei_cl *cl, |
5ceb46e2 | 239 | struct mei_msg_hdr *mei_hdr, |
331e4187 | 240 | struct mei_cl_cb *cmpl_list) |
19838fb8 | 241 | { |
db4756fd | 242 | struct mei_device *dev; |
331e4187 | 243 | int ret; |
19838fb8 | 244 | |
db4756fd | 245 | dev = cl->dev; |
19838fb8 | 246 | |
9d04ee11 AU |
247 | if (dev->iamthif_state != MEI_IAMTHIF_READING) { |
248 | mei_irq_discard_msg(dev, mei_hdr); | |
331e4187 | 249 | return 0; |
9d04ee11 | 250 | } |
19838fb8 | 251 | |
331e4187 TW |
252 | ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list); |
253 | if (ret) | |
254 | return ret; | |
19838fb8 TW |
255 | |
256 | if (!mei_hdr->msg_complete) | |
257 | return 0; | |
258 | ||
2bf94cab | 259 | dev_dbg(dev->dev, "completed amthif read.\n "); |
19838fb8 | 260 | dev->iamthif_stall_timer = 0; |
19838fb8 | 261 | |
19838fb8 TW |
262 | return 0; |
263 | } | |
264 | ||
265 | /** | |
266 | * mei_amthif_complete - complete amthif callback. | |
267 | * | |
9abd8b31 | 268 | * @cl: host client |
a8605ea2 | 269 | * @cb: callback block. |
19838fb8 | 270 | */ |
9abd8b31 | 271 | void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb) |
19838fb8 | 272 | { |
9abd8b31 | 273 | struct mei_device *dev = cl->dev; |
c54bf3ab | 274 | |
32a1dc1d AU |
275 | dev_dbg(dev->dev, "completing amthif call back.\n"); |
276 | switch (cb->fop_type) { | |
277 | case MEI_FOP_WRITE: | |
c54bf3ab TW |
278 | if (!cb->status) { |
279 | dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER; | |
280 | mei_io_cb_free(cb); | |
281 | return; | |
282 | } | |
32a1dc1d | 283 | dev->iamthif_state = MEI_IAMTHIF_IDLE; |
97d549b4 | 284 | cl->fp = NULL; |
32a1dc1d AU |
285 | if (!dev->iamthif_canceled) { |
286 | /* | |
287 | * in case of error enqueue the write cb to complete | |
288 | * read list so it can be propagated to the reader | |
289 | */ | |
290 | list_add_tail(&cb->list, &cl->rd_completed); | |
291 | wake_up_interruptible(&cl->rx_wait); | |
292 | } else { | |
293 | mei_io_cb_free(cb); | |
294 | } | |
295 | break; | |
296 | case MEI_FOP_READ: | |
297 | if (!dev->iamthif_canceled) { | |
298 | list_add_tail(&cb->list, &cl->rd_completed); | |
299 | dev_dbg(dev->dev, "amthif read completed\n"); | |
300 | wake_up_interruptible(&cl->rx_wait); | |
301 | } else { | |
302 | mei_io_cb_free(cb); | |
303 | } | |
c54bf3ab | 304 | |
32a1dc1d AU |
305 | dev->iamthif_stall_timer = 0; |
306 | mei_amthif_run_next_cmd(dev); | |
307 | break; | |
308 | default: | |
309 | WARN_ON(1); | |
19838fb8 | 310 | } |
19838fb8 TW |
311 | } |
312 | ||
a562d5c2 TW |
313 | /** |
314 | * mei_clear_list - removes all callbacks associated with file | |
315 | * from mei_cb_list | |
316 | * | |
a562d5c2 TW |
317 | * @file: file structure |
318 | * @mei_cb_list: callbacks list | |
319 | * | |
320 | * mei_clear_list is called to clear resources associated with file | |
321 | * when application calls close function or Ctrl-C was pressed | |
a562d5c2 | 322 | */ |
c85dba9e AU |
323 | static void mei_clear_list(const struct file *file, |
324 | struct list_head *mei_cb_list) | |
a562d5c2 | 325 | { |
928fa666 | 326 | struct mei_cl_cb *cb, *next; |
32a1dc1d AU |
327 | |
328 | list_for_each_entry_safe(cb, next, mei_cb_list, list) | |
329 | if (file == cb->fp) | |
928fa666 | 330 | mei_io_cb_free(cb); |
a562d5c2 TW |
331 | } |
332 | ||
a562d5c2 TW |
333 | /** |
334 | * mei_amthif_release - the release function | |
335 | * | |
393b148f | 336 | * @dev: device structure |
a562d5c2 TW |
337 | * @file: pointer to file structure |
338 | * | |
a8605ea2 | 339 | * Return: 0 on success, <0 on error |
a562d5c2 TW |
340 | */ |
341 | int mei_amthif_release(struct mei_device *dev, struct file *file) | |
342 | { | |
97d549b4 AU |
343 | struct mei_cl *cl = file->private_data; |
344 | ||
22f96a0e TW |
345 | if (dev->iamthif_open_count > 0) |
346 | dev->iamthif_open_count--; | |
a562d5c2 | 347 | |
97d549b4 | 348 | if (cl->fp == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) { |
a562d5c2 | 349 | |
2bf94cab | 350 | dev_dbg(dev->dev, "amthif canceled iamthif state %d\n", |
a562d5c2 TW |
351 | dev->iamthif_state); |
352 | dev->iamthif_canceled = true; | |
a562d5c2 TW |
353 | } |
354 | ||
c85dba9e AU |
355 | mei_clear_list(file, &dev->amthif_cmd_list.list); |
356 | mei_clear_list(file, &cl->rd_completed); | |
357 | mei_clear_list(file, &dev->ctrl_rd_list.list); | |
a562d5c2 TW |
358 | |
359 | return 0; | |
360 | } |