diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-22 19:34:21 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-22 19:34:21 -0400 |
commit | fb09bafda67041b74a668dc9d77735e36bd33d3b (patch) | |
tree | 2dd32b65062a95045468fdcab366ecdb8e4fcac6 /drivers/misc/mei/init.c | |
parent | 94b5aff4c6f72fee6b0f49d49e4fa8b204e8ded9 (diff) | |
parent | c3c6cc91b0ae7b3d598488ad0b593bafba4a0817 (diff) |
Merge tag 'staging-3.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging tree changes from Greg Kroah-Hartman:
"Here is the big staging tree pull request for the 3.5-rc1 merge
window.
Loads of changes here, and we just narrowly added more lines than we
added:
622 files changed, 28356 insertions(+), 26059 deletions(-)
But, good news is that there is a number of subsystems that moved out
of the staging tree, to their respective "real" portions of the
kernel.
Code that moved out was:
- iio core code
- mei driver
- vme core and bridge drivers
There was one broken network driver that moved into staging as a step
before it is removed from the tree (pc300), and there was a few new
drivers added to the tree:
- new iio drivers
- gdm72xx wimax USB driver
- ipack subsystem and 2 drivers
All of the movements around have acks from the various subsystem
maintainers, and all of this has been in the linux-next tree for a
while.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>"
Fixed up various trivial conflicts, along with a non-trivial one found
in -next and pointed out by Olof Johanssen: a clean - but incorrect -
merge of the arch/arm/boot/dts/at91sam9g20.dtsi file. Fix up manually
as per Stephen Rothwell.
* tag 'staging-3.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (536 commits)
Staging: bcm: Remove two unused variables from Adapter.h
Staging: bcm: Removes the volatile type definition from Adapter.h
Staging: bcm: Rename all "INT" to "int" in Adapter.h
Staging: bcm: Fix warning: __packed vs. __attribute__((packed)) in Adapter.h
Staging: bcm: Correctly format all comments in Adapter.h
Staging: bcm: Fix all whitespace issues in Adapter.h
Staging: bcm: Properly format braces in Adapter.h
Staging: ipack/bridges/tpci200: remove unneeded casts
Staging: ipack/bridges/tpci200: remove TPCI200_SHORTNAME constant
Staging: ipack: remove board_name and bus_name fields from struct ipack_device
Staging: ipack: improve the register of a bus and a device in the bus.
staging: comedi: cleanup all the comedi_driver 'detach' functions
staging: comedi: remove all 'default N' in Kconfig
staging: line6/config.h: Delete unused header
staging: gdm72xx depends on NET
staging: gdm72xx: Set up parent link in sysfs for gdm72xx devices
staging: drm/omap: initial dmabuf/prime import support
staging: drm/omap: dmabuf/prime mmap support
pstore/ram: Add ECC support
pstore/ram: Switch to persistent_ram routines
...
Diffstat (limited to 'drivers/misc/mei/init.c')
-rw-r--r-- | drivers/misc/mei/init.c | 735 |
1 files changed, 735 insertions, 0 deletions
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c new file mode 100644 index 000000000000..a7d0bb0880ec --- /dev/null +++ b/drivers/misc/mei/init.c | |||
@@ -0,0 +1,735 @@ | |||
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/pci.h> | ||
18 | #include <linux/sched.h> | ||
19 | #include <linux/wait.h> | ||
20 | #include <linux/delay.h> | ||
21 | |||
22 | #include "mei_dev.h" | ||
23 | #include "hw.h" | ||
24 | #include "interface.h" | ||
25 | #include <linux/mei.h> | ||
26 | |||
27 | const uuid_le mei_amthi_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, 0xac, | ||
28 | 0xa8, 0x46, 0xe0, 0xff, 0x65, | ||
29 | 0x81, 0x4c); | ||
30 | |||
31 | /** | ||
32 | * mei_io_list_init - Sets up a queue list. | ||
33 | * | ||
34 | * @list: An instance io list structure | ||
35 | * @dev: the device structure | ||
36 | */ | ||
37 | void mei_io_list_init(struct mei_io_list *list) | ||
38 | { | ||
39 | /* initialize our queue list */ | ||
40 | INIT_LIST_HEAD(&list->mei_cb.cb_list); | ||
41 | } | ||
42 | |||
43 | /** | ||
44 | * mei_io_list_flush - removes list entry belonging to cl. | ||
45 | * | ||
46 | * @list: An instance of our list structure | ||
47 | * @cl: private data of the file object | ||
48 | */ | ||
49 | void mei_io_list_flush(struct mei_io_list *list, struct mei_cl *cl) | ||
50 | { | ||
51 | struct mei_cl_cb *pos; | ||
52 | struct mei_cl_cb *next; | ||
53 | |||
54 | list_for_each_entry_safe(pos, next, &list->mei_cb.cb_list, cb_list) { | ||
55 | if (pos->file_private) { | ||
56 | struct mei_cl *cl_tmp; | ||
57 | cl_tmp = (struct mei_cl *)pos->file_private; | ||
58 | if (mei_cl_cmp_id(cl, cl_tmp)) | ||
59 | list_del(&pos->cb_list); | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | /** | ||
64 | * mei_cl_flush_queues - flushes queue lists belonging to cl. | ||
65 | * | ||
66 | * @dev: the device structure | ||
67 | * @cl: private data of the file object | ||
68 | */ | ||
69 | int mei_cl_flush_queues(struct mei_cl *cl) | ||
70 | { | ||
71 | if (!cl || !cl->dev) | ||
72 | return -EINVAL; | ||
73 | |||
74 | dev_dbg(&cl->dev->pdev->dev, "remove list entry belonging to cl\n"); | ||
75 | mei_io_list_flush(&cl->dev->read_list, cl); | ||
76 | mei_io_list_flush(&cl->dev->write_list, cl); | ||
77 | mei_io_list_flush(&cl->dev->write_waiting_list, cl); | ||
78 | mei_io_list_flush(&cl->dev->ctrl_wr_list, cl); | ||
79 | mei_io_list_flush(&cl->dev->ctrl_rd_list, cl); | ||
80 | mei_io_list_flush(&cl->dev->amthi_cmd_list, cl); | ||
81 | mei_io_list_flush(&cl->dev->amthi_read_complete_list, cl); | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | |||
86 | |||
87 | /** | ||
88 | * mei_reset_iamthif_params - initializes mei device iamthif | ||
89 | * | ||
90 | * @dev: the device structure | ||
91 | */ | ||
92 | static void mei_reset_iamthif_params(struct mei_device *dev) | ||
93 | { | ||
94 | /* reset iamthif parameters. */ | ||
95 | dev->iamthif_current_cb = NULL; | ||
96 | dev->iamthif_msg_buf_size = 0; | ||
97 | dev->iamthif_msg_buf_index = 0; | ||
98 | dev->iamthif_canceled = false; | ||
99 | dev->iamthif_ioctl = false; | ||
100 | dev->iamthif_state = MEI_IAMTHIF_IDLE; | ||
101 | dev->iamthif_timer = 0; | ||
102 | } | ||
103 | |||
104 | /** | ||
105 | * init_mei_device - allocates and initializes the mei device structure | ||
106 | * | ||
107 | * @pdev: The pci device structure | ||
108 | * | ||
109 | * returns The mei_device_device pointer on success, NULL on failure. | ||
110 | */ | ||
111 | struct mei_device *mei_device_init(struct pci_dev *pdev) | ||
112 | { | ||
113 | struct mei_device *dev; | ||
114 | |||
115 | dev = kzalloc(sizeof(struct mei_device), GFP_KERNEL); | ||
116 | if (!dev) | ||
117 | return NULL; | ||
118 | |||
119 | /* setup our list array */ | ||
120 | INIT_LIST_HEAD(&dev->file_list); | ||
121 | INIT_LIST_HEAD(&dev->wd_cl.link); | ||
122 | INIT_LIST_HEAD(&dev->iamthif_cl.link); | ||
123 | mutex_init(&dev->device_lock); | ||
124 | init_waitqueue_head(&dev->wait_recvd_msg); | ||
125 | init_waitqueue_head(&dev->wait_stop_wd); | ||
126 | dev->mei_state = MEI_INITIALIZING; | ||
127 | dev->iamthif_state = MEI_IAMTHIF_IDLE; | ||
128 | dev->wd_interface_reg = false; | ||
129 | |||
130 | |||
131 | mei_io_list_init(&dev->read_list); | ||
132 | mei_io_list_init(&dev->write_list); | ||
133 | mei_io_list_init(&dev->write_waiting_list); | ||
134 | mei_io_list_init(&dev->ctrl_wr_list); | ||
135 | mei_io_list_init(&dev->ctrl_rd_list); | ||
136 | mei_io_list_init(&dev->amthi_cmd_list); | ||
137 | mei_io_list_init(&dev->amthi_read_complete_list); | ||
138 | dev->pdev = pdev; | ||
139 | return dev; | ||
140 | } | ||
141 | |||
142 | /** | ||
143 | * mei_hw_init - initializes host and fw to start work. | ||
144 | * | ||
145 | * @dev: the device structure | ||
146 | * | ||
147 | * returns 0 on success, <0 on failure. | ||
148 | */ | ||
149 | int mei_hw_init(struct mei_device *dev) | ||
150 | { | ||
151 | int err = 0; | ||
152 | int ret; | ||
153 | |||
154 | mutex_lock(&dev->device_lock); | ||
155 | |||
156 | dev->host_hw_state = mei_hcsr_read(dev); | ||
157 | dev->me_hw_state = mei_mecsr_read(dev); | ||
158 | dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, mestate = 0x%08x.\n", | ||
159 | dev->host_hw_state, dev->me_hw_state); | ||
160 | |||
161 | /* acknowledge interrupt and stop interupts */ | ||
162 | if ((dev->host_hw_state & H_IS) == H_IS) | ||
163 | mei_reg_write(dev, H_CSR, dev->host_hw_state); | ||
164 | |||
165 | dev->recvd_msg = false; | ||
166 | dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n"); | ||
167 | |||
168 | mei_reset(dev, 1); | ||
169 | |||
170 | dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n", | ||
171 | dev->host_hw_state, dev->me_hw_state); | ||
172 | |||
173 | /* wait for ME to turn on ME_RDY */ | ||
174 | if (!dev->recvd_msg) { | ||
175 | mutex_unlock(&dev->device_lock); | ||
176 | err = wait_event_interruptible_timeout(dev->wait_recvd_msg, | ||
177 | dev->recvd_msg, MEI_INTEROP_TIMEOUT); | ||
178 | mutex_lock(&dev->device_lock); | ||
179 | } | ||
180 | |||
181 | if (err <= 0 && !dev->recvd_msg) { | ||
182 | dev->mei_state = MEI_DISABLED; | ||
183 | dev_dbg(&dev->pdev->dev, | ||
184 | "wait_event_interruptible_timeout failed" | ||
185 | "on wait for ME to turn on ME_RDY.\n"); | ||
186 | ret = -ENODEV; | ||
187 | goto out; | ||
188 | } | ||
189 | |||
190 | if (!(((dev->host_hw_state & H_RDY) == H_RDY) && | ||
191 | ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA))) { | ||
192 | dev->mei_state = MEI_DISABLED; | ||
193 | dev_dbg(&dev->pdev->dev, | ||
194 | "host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n", | ||
195 | dev->host_hw_state, dev->me_hw_state); | ||
196 | |||
197 | if (!(dev->host_hw_state & H_RDY)) | ||
198 | dev_dbg(&dev->pdev->dev, "host turn off H_RDY.\n"); | ||
199 | |||
200 | if (!(dev->me_hw_state & ME_RDY_HRA)) | ||
201 | dev_dbg(&dev->pdev->dev, "ME turn off ME_RDY.\n"); | ||
202 | |||
203 | dev_err(&dev->pdev->dev, "link layer initialization failed.\n"); | ||
204 | ret = -ENODEV; | ||
205 | goto out; | ||
206 | } | ||
207 | |||
208 | if (dev->version.major_version != HBM_MAJOR_VERSION || | ||
209 | dev->version.minor_version != HBM_MINOR_VERSION) { | ||
210 | dev_dbg(&dev->pdev->dev, "MEI start failed.\n"); | ||
211 | ret = -ENODEV; | ||
212 | goto out; | ||
213 | } | ||
214 | |||
215 | dev->recvd_msg = false; | ||
216 | dev_dbg(&dev->pdev->dev, "host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n", | ||
217 | dev->host_hw_state, dev->me_hw_state); | ||
218 | dev_dbg(&dev->pdev->dev, "ME turn on ME_RDY and host turn on H_RDY.\n"); | ||
219 | dev_dbg(&dev->pdev->dev, "link layer has been established.\n"); | ||
220 | dev_dbg(&dev->pdev->dev, "MEI start success.\n"); | ||
221 | ret = 0; | ||
222 | |||
223 | out: | ||
224 | mutex_unlock(&dev->device_lock); | ||
225 | return ret; | ||
226 | } | ||
227 | |||
228 | /** | ||
229 | * mei_hw_reset - resets fw via mei csr register. | ||
230 | * | ||
231 | * @dev: the device structure | ||
232 | * @interrupts_enabled: if interrupt should be enabled after reset. | ||
233 | */ | ||
234 | static void mei_hw_reset(struct mei_device *dev, int interrupts_enabled) | ||
235 | { | ||
236 | dev->host_hw_state |= (H_RST | H_IG); | ||
237 | |||
238 | if (interrupts_enabled) | ||
239 | mei_enable_interrupts(dev); | ||
240 | else | ||
241 | mei_disable_interrupts(dev); | ||
242 | } | ||
243 | |||
244 | /** | ||
245 | * mei_reset - resets host and fw. | ||
246 | * | ||
247 | * @dev: the device structure | ||
248 | * @interrupts_enabled: if interrupt should be enabled after reset. | ||
249 | */ | ||
250 | void mei_reset(struct mei_device *dev, int interrupts_enabled) | ||
251 | { | ||
252 | struct mei_cl *cl_pos = NULL; | ||
253 | struct mei_cl *cl_next = NULL; | ||
254 | struct mei_cl_cb *cb_pos = NULL; | ||
255 | struct mei_cl_cb *cb_next = NULL; | ||
256 | bool unexpected; | ||
257 | |||
258 | if (dev->mei_state == MEI_RECOVERING_FROM_RESET) { | ||
259 | dev->need_reset = true; | ||
260 | return; | ||
261 | } | ||
262 | |||
263 | unexpected = (dev->mei_state != MEI_INITIALIZING && | ||
264 | dev->mei_state != MEI_DISABLED && | ||
265 | dev->mei_state != MEI_POWER_DOWN && | ||
266 | dev->mei_state != MEI_POWER_UP); | ||
267 | |||
268 | dev->host_hw_state = mei_hcsr_read(dev); | ||
269 | |||
270 | dev_dbg(&dev->pdev->dev, "before reset host_hw_state = 0x%08x.\n", | ||
271 | dev->host_hw_state); | ||
272 | |||
273 | mei_hw_reset(dev, interrupts_enabled); | ||
274 | |||
275 | dev->host_hw_state &= ~H_RST; | ||
276 | dev->host_hw_state |= H_IG; | ||
277 | |||
278 | mei_hcsr_set(dev); | ||
279 | |||
280 | dev_dbg(&dev->pdev->dev, "currently saved host_hw_state = 0x%08x.\n", | ||
281 | dev->host_hw_state); | ||
282 | |||
283 | dev->need_reset = false; | ||
284 | |||
285 | if (dev->mei_state != MEI_INITIALIZING) { | ||
286 | if (dev->mei_state != MEI_DISABLED && | ||
287 | dev->mei_state != MEI_POWER_DOWN) | ||
288 | dev->mei_state = MEI_RESETING; | ||
289 | |||
290 | list_for_each_entry_safe(cl_pos, | ||
291 | cl_next, &dev->file_list, link) { | ||
292 | cl_pos->state = MEI_FILE_DISCONNECTED; | ||
293 | cl_pos->mei_flow_ctrl_creds = 0; | ||
294 | cl_pos->read_cb = NULL; | ||
295 | cl_pos->timer_count = 0; | ||
296 | } | ||
297 | /* remove entry if already in list */ | ||
298 | dev_dbg(&dev->pdev->dev, "list del iamthif and wd file list.\n"); | ||
299 | mei_remove_client_from_file_list(dev, | ||
300 | dev->wd_cl.host_client_id); | ||
301 | |||
302 | mei_remove_client_from_file_list(dev, | ||
303 | dev->iamthif_cl.host_client_id); | ||
304 | |||
305 | mei_reset_iamthif_params(dev); | ||
306 | dev->wd_due_counter = 0; | ||
307 | dev->extra_write_index = 0; | ||
308 | } | ||
309 | |||
310 | dev->me_clients_num = 0; | ||
311 | dev->rd_msg_hdr = 0; | ||
312 | dev->stop = false; | ||
313 | dev->wd_pending = false; | ||
314 | |||
315 | /* update the state of the registers after reset */ | ||
316 | dev->host_hw_state = mei_hcsr_read(dev); | ||
317 | dev->me_hw_state = mei_mecsr_read(dev); | ||
318 | |||
319 | dev_dbg(&dev->pdev->dev, "after reset host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n", | ||
320 | dev->host_hw_state, dev->me_hw_state); | ||
321 | |||
322 | if (unexpected) | ||
323 | dev_warn(&dev->pdev->dev, "unexpected reset.\n"); | ||
324 | |||
325 | /* Wake up all readings so they can be interrupted */ | ||
326 | list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) { | ||
327 | if (waitqueue_active(&cl_pos->rx_wait)) { | ||
328 | dev_dbg(&dev->pdev->dev, "Waking up client!\n"); | ||
329 | wake_up_interruptible(&cl_pos->rx_wait); | ||
330 | } | ||
331 | } | ||
332 | /* remove all waiting requests */ | ||
333 | list_for_each_entry_safe(cb_pos, cb_next, | ||
334 | &dev->write_list.mei_cb.cb_list, cb_list) { | ||
335 | list_del(&cb_pos->cb_list); | ||
336 | mei_free_cb_private(cb_pos); | ||
337 | } | ||
338 | } | ||
339 | |||
340 | |||
341 | |||
342 | /** | ||
343 | * host_start_message - mei host sends start message. | ||
344 | * | ||
345 | * @dev: the device structure | ||
346 | * | ||
347 | * returns none. | ||
348 | */ | ||
349 | void mei_host_start_message(struct mei_device *dev) | ||
350 | { | ||
351 | struct mei_msg_hdr *mei_hdr; | ||
352 | struct hbm_host_version_request *host_start_req; | ||
353 | |||
354 | /* host start message */ | ||
355 | mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0]; | ||
356 | mei_hdr->host_addr = 0; | ||
357 | mei_hdr->me_addr = 0; | ||
358 | mei_hdr->length = sizeof(struct hbm_host_version_request); | ||
359 | mei_hdr->msg_complete = 1; | ||
360 | mei_hdr->reserved = 0; | ||
361 | |||
362 | host_start_req = | ||
363 | (struct hbm_host_version_request *) &dev->wr_msg_buf[1]; | ||
364 | memset(host_start_req, 0, sizeof(struct hbm_host_version_request)); | ||
365 | host_start_req->hbm_cmd = HOST_START_REQ_CMD; | ||
366 | host_start_req->host_version.major_version = HBM_MAJOR_VERSION; | ||
367 | host_start_req->host_version.minor_version = HBM_MINOR_VERSION; | ||
368 | dev->recvd_msg = false; | ||
369 | if (mei_write_message(dev, mei_hdr, (unsigned char *)host_start_req, | ||
370 | mei_hdr->length)) { | ||
371 | dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n"); | ||
372 | dev->mei_state = MEI_RESETING; | ||
373 | mei_reset(dev, 1); | ||
374 | } | ||
375 | dev->init_clients_state = MEI_START_MESSAGE; | ||
376 | dev->init_clients_timer = INIT_CLIENTS_TIMEOUT; | ||
377 | return ; | ||
378 | } | ||
379 | |||
380 | /** | ||
381 | * host_enum_clients_message - host sends enumeration client request message. | ||
382 | * | ||
383 | * @dev: the device structure | ||
384 | * | ||
385 | * returns none. | ||
386 | */ | ||
387 | void mei_host_enum_clients_message(struct mei_device *dev) | ||
388 | { | ||
389 | struct mei_msg_hdr *mei_hdr; | ||
390 | struct hbm_host_enum_request *host_enum_req; | ||
391 | mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0]; | ||
392 | /* enumerate clients */ | ||
393 | mei_hdr->host_addr = 0; | ||
394 | mei_hdr->me_addr = 0; | ||
395 | mei_hdr->length = sizeof(struct hbm_host_enum_request); | ||
396 | mei_hdr->msg_complete = 1; | ||
397 | mei_hdr->reserved = 0; | ||
398 | |||
399 | host_enum_req = (struct hbm_host_enum_request *) &dev->wr_msg_buf[1]; | ||
400 | memset(host_enum_req, 0, sizeof(struct hbm_host_enum_request)); | ||
401 | host_enum_req->hbm_cmd = HOST_ENUM_REQ_CMD; | ||
402 | if (mei_write_message(dev, mei_hdr, (unsigned char *)host_enum_req, | ||
403 | mei_hdr->length)) { | ||
404 | dev->mei_state = MEI_RESETING; | ||
405 | dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n"); | ||
406 | mei_reset(dev, 1); | ||
407 | } | ||
408 | dev->init_clients_state = MEI_ENUM_CLIENTS_MESSAGE; | ||
409 | dev->init_clients_timer = INIT_CLIENTS_TIMEOUT; | ||
410 | return; | ||
411 | } | ||
412 | |||
413 | |||
414 | /** | ||
415 | * allocate_me_clients_storage - allocates storage for me clients | ||
416 | * | ||
417 | * @dev: the device structure | ||
418 | * | ||
419 | * returns none. | ||
420 | */ | ||
421 | void mei_allocate_me_clients_storage(struct mei_device *dev) | ||
422 | { | ||
423 | struct mei_me_client *clients; | ||
424 | int b; | ||
425 | |||
426 | /* count how many ME clients we have */ | ||
427 | for_each_set_bit(b, dev->me_clients_map, MEI_CLIENTS_MAX) | ||
428 | dev->me_clients_num++; | ||
429 | |||
430 | if (dev->me_clients_num <= 0) | ||
431 | return ; | ||
432 | |||
433 | |||
434 | if (dev->me_clients != NULL) { | ||
435 | kfree(dev->me_clients); | ||
436 | dev->me_clients = NULL; | ||
437 | } | ||
438 | dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%zd.\n", | ||
439 | dev->me_clients_num * sizeof(struct mei_me_client)); | ||
440 | /* allocate storage for ME clients representation */ | ||
441 | clients = kcalloc(dev->me_clients_num, | ||
442 | sizeof(struct mei_me_client), GFP_KERNEL); | ||
443 | if (!clients) { | ||
444 | dev_dbg(&dev->pdev->dev, "memory allocation for ME clients failed.\n"); | ||
445 | dev->mei_state = MEI_RESETING; | ||
446 | mei_reset(dev, 1); | ||
447 | return ; | ||
448 | } | ||
449 | dev->me_clients = clients; | ||
450 | return ; | ||
451 | } | ||
452 | /** | ||
453 | * host_client_properties - reads properties for client | ||
454 | * | ||
455 | * @dev: the device structure | ||
456 | * | ||
457 | * returns: | ||
458 | * < 0 - Error. | ||
459 | * = 0 - no more clients. | ||
460 | * = 1 - still have clients to send properties request. | ||
461 | */ | ||
462 | int mei_host_client_properties(struct mei_device *dev) | ||
463 | { | ||
464 | struct mei_msg_hdr *mei_header; | ||
465 | struct hbm_props_request *host_cli_req; | ||
466 | int b; | ||
467 | u8 client_num = dev->me_client_presentation_num; | ||
468 | |||
469 | b = dev->me_client_index; | ||
470 | b = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX, b); | ||
471 | if (b < MEI_CLIENTS_MAX) { | ||
472 | dev->me_clients[client_num].client_id = b; | ||
473 | dev->me_clients[client_num].mei_flow_ctrl_creds = 0; | ||
474 | mei_header = (struct mei_msg_hdr *)&dev->wr_msg_buf[0]; | ||
475 | mei_header->host_addr = 0; | ||
476 | mei_header->me_addr = 0; | ||
477 | mei_header->length = sizeof(struct hbm_props_request); | ||
478 | mei_header->msg_complete = 1; | ||
479 | mei_header->reserved = 0; | ||
480 | |||
481 | host_cli_req = (struct hbm_props_request *)&dev->wr_msg_buf[1]; | ||
482 | |||
483 | memset(host_cli_req, 0, sizeof(struct hbm_props_request)); | ||
484 | |||
485 | host_cli_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD; | ||
486 | host_cli_req->address = b; | ||
487 | |||
488 | if (mei_write_message(dev, mei_header, | ||
489 | (unsigned char *)host_cli_req, | ||
490 | mei_header->length)) { | ||
491 | dev->mei_state = MEI_RESETING; | ||
492 | dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n"); | ||
493 | mei_reset(dev, 1); | ||
494 | return -EIO; | ||
495 | } | ||
496 | |||
497 | dev->init_clients_timer = INIT_CLIENTS_TIMEOUT; | ||
498 | dev->me_client_index = b; | ||
499 | return 1; | ||
500 | } | ||
501 | |||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | /** | ||
506 | * mei_init_file_private - initializes private file structure. | ||
507 | * | ||
508 | * @priv: private file structure to be initialized | ||
509 | * @file: the file structure | ||
510 | */ | ||
511 | void mei_cl_init(struct mei_cl *priv, struct mei_device *dev) | ||
512 | { | ||
513 | memset(priv, 0, sizeof(struct mei_cl)); | ||
514 | init_waitqueue_head(&priv->wait); | ||
515 | init_waitqueue_head(&priv->rx_wait); | ||
516 | init_waitqueue_head(&priv->tx_wait); | ||
517 | INIT_LIST_HEAD(&priv->link); | ||
518 | priv->reading_state = MEI_IDLE; | ||
519 | priv->writing_state = MEI_IDLE; | ||
520 | priv->dev = dev; | ||
521 | } | ||
522 | |||
523 | int mei_find_me_client_index(const struct mei_device *dev, uuid_le cuuid) | ||
524 | { | ||
525 | int i, res = -1; | ||
526 | |||
527 | for (i = 0; i < dev->me_clients_num; ++i) | ||
528 | if (uuid_le_cmp(cuuid, | ||
529 | dev->me_clients[i].props.protocol_name) == 0) { | ||
530 | res = i; | ||
531 | break; | ||
532 | } | ||
533 | |||
534 | return res; | ||
535 | } | ||
536 | |||
537 | |||
538 | /** | ||
539 | * mei_find_me_client_update_filext - searches for ME client guid | ||
540 | * sets client_id in mei_file_private if found | ||
541 | * @dev: the device structure | ||
542 | * @priv: private file structure to set client_id in | ||
543 | * @cguid: searched guid of ME client | ||
544 | * @client_id: id of host client to be set in file private structure | ||
545 | * | ||
546 | * returns ME client index | ||
547 | */ | ||
548 | u8 mei_find_me_client_update_filext(struct mei_device *dev, struct mei_cl *priv, | ||
549 | const uuid_le *cguid, u8 client_id) | ||
550 | { | ||
551 | int i; | ||
552 | |||
553 | if (!dev || !priv || !cguid) | ||
554 | return 0; | ||
555 | |||
556 | /* check for valid client id */ | ||
557 | i = mei_find_me_client_index(dev, *cguid); | ||
558 | if (i >= 0) { | ||
559 | priv->me_client_id = dev->me_clients[i].client_id; | ||
560 | priv->state = MEI_FILE_CONNECTING; | ||
561 | priv->host_client_id = client_id; | ||
562 | |||
563 | list_add_tail(&priv->link, &dev->file_list); | ||
564 | return (u8)i; | ||
565 | } | ||
566 | |||
567 | return 0; | ||
568 | } | ||
569 | |||
570 | /** | ||
571 | * host_init_iamthif - mei initialization iamthif client. | ||
572 | * | ||
573 | * @dev: the device structure | ||
574 | * | ||
575 | */ | ||
576 | void mei_host_init_iamthif(struct mei_device *dev) | ||
577 | { | ||
578 | u8 i; | ||
579 | unsigned char *msg_buf; | ||
580 | |||
581 | mei_cl_init(&dev->iamthif_cl, dev); | ||
582 | dev->iamthif_cl.state = MEI_FILE_DISCONNECTED; | ||
583 | |||
584 | /* find ME amthi client */ | ||
585 | i = mei_find_me_client_update_filext(dev, &dev->iamthif_cl, | ||
586 | &mei_amthi_guid, MEI_IAMTHIF_HOST_CLIENT_ID); | ||
587 | if (dev->iamthif_cl.state != MEI_FILE_CONNECTING) { | ||
588 | dev_dbg(&dev->pdev->dev, "failed to find iamthif client.\n"); | ||
589 | return; | ||
590 | } | ||
591 | |||
592 | /* Assign iamthif_mtu to the value received from ME */ | ||
593 | |||
594 | dev->iamthif_mtu = dev->me_clients[i].props.max_msg_length; | ||
595 | dev_dbg(&dev->pdev->dev, "IAMTHIF_MTU = %d\n", | ||
596 | dev->me_clients[i].props.max_msg_length); | ||
597 | |||
598 | kfree(dev->iamthif_msg_buf); | ||
599 | dev->iamthif_msg_buf = NULL; | ||
600 | |||
601 | /* allocate storage for ME message buffer */ | ||
602 | msg_buf = kcalloc(dev->iamthif_mtu, | ||
603 | sizeof(unsigned char), GFP_KERNEL); | ||
604 | if (!msg_buf) { | ||
605 | dev_dbg(&dev->pdev->dev, "memory allocation for ME message buffer failed.\n"); | ||
606 | return; | ||
607 | } | ||
608 | |||
609 | dev->iamthif_msg_buf = msg_buf; | ||
610 | |||
611 | if (mei_connect(dev, &dev->iamthif_cl)) { | ||
612 | dev_dbg(&dev->pdev->dev, "Failed to connect to AMTHI client\n"); | ||
613 | dev->iamthif_cl.state = MEI_FILE_DISCONNECTED; | ||
614 | dev->iamthif_cl.host_client_id = 0; | ||
615 | } else { | ||
616 | dev->iamthif_cl.timer_count = CONNECT_TIMEOUT; | ||
617 | } | ||
618 | } | ||
619 | |||
620 | /** | ||
621 | * mei_alloc_file_private - allocates a private file structure and sets it up. | ||
622 | * @file: the file structure | ||
623 | * | ||
624 | * returns The allocated file or NULL on failure | ||
625 | */ | ||
626 | struct mei_cl *mei_cl_allocate(struct mei_device *dev) | ||
627 | { | ||
628 | struct mei_cl *cl; | ||
629 | |||
630 | cl = kmalloc(sizeof(struct mei_cl), GFP_KERNEL); | ||
631 | if (!cl) | ||
632 | return NULL; | ||
633 | |||
634 | mei_cl_init(cl, dev); | ||
635 | |||
636 | return cl; | ||
637 | } | ||
638 | |||
639 | |||
640 | |||
641 | /** | ||
642 | * mei_disconnect_host_client - sends disconnect message to fw from host client. | ||
643 | * | ||
644 | * @dev: the device structure | ||
645 | * @cl: private data of the file object | ||
646 | * | ||
647 | * Locking: called under "dev->device_lock" lock | ||
648 | * | ||
649 | * returns 0 on success, <0 on failure. | ||
650 | */ | ||
651 | int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *cl) | ||
652 | { | ||
653 | int rets, err; | ||
654 | long timeout = 15; /* 15 seconds */ | ||
655 | struct mei_cl_cb *cb; | ||
656 | |||
657 | if (!dev || !cl) | ||
658 | return -ENODEV; | ||
659 | |||
660 | if (cl->state != MEI_FILE_DISCONNECTING) | ||
661 | return 0; | ||
662 | |||
663 | cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); | ||
664 | if (!cb) | ||
665 | return -ENOMEM; | ||
666 | |||
667 | INIT_LIST_HEAD(&cb->cb_list); | ||
668 | cb->file_private = cl; | ||
669 | cb->major_file_operations = MEI_CLOSE; | ||
670 | if (dev->mei_host_buffer_is_empty) { | ||
671 | dev->mei_host_buffer_is_empty = false; | ||
672 | if (mei_disconnect(dev, cl)) { | ||
673 | rets = -ENODEV; | ||
674 | dev_dbg(&dev->pdev->dev, "failed to call mei_disconnect.\n"); | ||
675 | goto free; | ||
676 | } | ||
677 | mdelay(10); /* Wait for hardware disconnection ready */ | ||
678 | list_add_tail(&cb->cb_list, &dev->ctrl_rd_list.mei_cb.cb_list); | ||
679 | } else { | ||
680 | dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n"); | ||
681 | list_add_tail(&cb->cb_list, | ||
682 | &dev->ctrl_wr_list.mei_cb.cb_list); | ||
683 | } | ||
684 | mutex_unlock(&dev->device_lock); | ||
685 | |||
686 | err = wait_event_timeout(dev->wait_recvd_msg, | ||
687 | (MEI_FILE_DISCONNECTED == cl->state), | ||
688 | timeout * HZ); | ||
689 | |||
690 | mutex_lock(&dev->device_lock); | ||
691 | if (MEI_FILE_DISCONNECTED == cl->state) { | ||
692 | rets = 0; | ||
693 | dev_dbg(&dev->pdev->dev, "successfully disconnected from FW client.\n"); | ||
694 | } else { | ||
695 | rets = -ENODEV; | ||
696 | if (MEI_FILE_DISCONNECTED != cl->state) | ||
697 | dev_dbg(&dev->pdev->dev, "wrong status client disconnect.\n"); | ||
698 | |||
699 | if (err) | ||
700 | dev_dbg(&dev->pdev->dev, | ||
701 | "wait failed disconnect err=%08x\n", | ||
702 | err); | ||
703 | |||
704 | dev_dbg(&dev->pdev->dev, "failed to disconnect from FW client.\n"); | ||
705 | } | ||
706 | |||
707 | mei_io_list_flush(&dev->ctrl_rd_list, cl); | ||
708 | mei_io_list_flush(&dev->ctrl_wr_list, cl); | ||
709 | free: | ||
710 | mei_free_cb_private(cb); | ||
711 | return rets; | ||
712 | } | ||
713 | |||
714 | /** | ||
715 | * mei_remove_client_from_file_list - | ||
716 | * removes file private data from device file list | ||
717 | * | ||
718 | * @dev: the device structure | ||
719 | * @host_client_id: host client id to be removed | ||
720 | */ | ||
721 | void mei_remove_client_from_file_list(struct mei_device *dev, | ||
722 | u8 host_client_id) | ||
723 | { | ||
724 | struct mei_cl *cl_pos = NULL; | ||
725 | struct mei_cl *cl_next = NULL; | ||
726 | list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) { | ||
727 | if (host_client_id == cl_pos->host_client_id) { | ||
728 | dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n", | ||
729 | cl_pos->host_client_id, | ||
730 | cl_pos->me_client_id); | ||
731 | list_del_init(&cl_pos->link); | ||
732 | break; | ||
733 | } | ||
734 | } | ||
735 | } | ||