1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2013-2023, Intel Corporation. All rights reserved. |
4 | * Intel Management Engine Interface (Intel MEI) Linux driver |
5 | */ |
6 | |
7 | #include <linux/kernel.h> |
8 | #include <linux/sched.h> |
9 | #include <linux/module.h> |
10 | #include <linux/device.h> |
11 | #include <linux/slab.h> |
12 | |
13 | #include <linux/mei.h> |
14 | #include <linux/mei_cl_bus.h> |
15 | |
16 | #include "mei_dev.h" |
17 | #include "client.h" |
18 | #include "mkhi.h" |
19 | |
20 | #define MEI_UUID_NFC_INFO UUID_LE(0xd2de1625, 0x382d, 0x417d, \ |
21 | 0x48, 0xa4, 0xef, 0xab, 0xba, 0x8a, 0x12, 0x06) |
22 | |
23 | static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO; |
24 | |
25 | #define MEI_UUID_NFC_HCI UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, \ |
26 | 0x94, 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c) |
27 | |
28 | #define MEI_UUID_WD UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, \ |
29 | 0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB) |
30 | |
31 | #define MEI_UUID_MKHIF_FIX UUID_LE(0x55213584, 0x9a29, 0x4916, \ |
32 | 0xba, 0xdf, 0xf, 0xb7, 0xed, 0x68, 0x2a, 0xeb) |
33 | |
34 | #define MEI_UUID_IGSC_MKHI UUID_LE(0xE2C2AFA2, 0x3817, 0x4D19, \ |
35 | 0x9D, 0x95, 0x06, 0xB1, 0x6B, 0x58, 0x8A, 0x5D) |
36 | |
37 | #define MEI_UUID_IGSC_MKHI_FIX UUID_LE(0x46E0C1FB, 0xA546, 0x414F, \ |
38 | 0x91, 0x70, 0xB7, 0xF4, 0x6D, 0x57, 0xB4, 0xAD) |
39 | |
40 | #define MEI_UUID_HDCP UUID_LE(0xB638AB7E, 0x94E2, 0x4EA2, \ |
41 | 0xA5, 0x52, 0xD1, 0xC5, 0x4B, 0x62, 0x7F, 0x04) |
42 | |
43 | #define MEI_UUID_PAVP UUID_LE(0xfbf6fcf1, 0x96cf, 0x4e2e, 0xA6, \ |
44 | 0xa6, 0x1b, 0xab, 0x8c, 0xbe, 0x36, 0xb1) |
45 | |
46 | #define MEI_UUID_ANY NULL_UUID_LE |
47 | |
48 | /** |
49 | * number_of_connections - determine whether an client be on the bus |
50 | * according number of connections |
51 | * We support only clients: |
52 | * 1. with single connection |
53 | * 2. and fixed clients (max_number_of_connections == 0) |
54 | * |
55 | * @cldev: me clients device |
56 | */ |
57 | static void number_of_connections(struct mei_cl_device *cldev) |
58 | { |
59 | if (cldev->me_cl->props.max_number_of_connections > 1) |
60 | cldev->do_match = 0; |
61 | } |
62 | |
63 | /** |
64 | * blacklist - blacklist a client from the bus |
65 | * |
66 | * @cldev: me clients device |
67 | */ |
68 | static void blacklist(struct mei_cl_device *cldev) |
69 | { |
70 | cldev->do_match = 0; |
71 | } |
72 | |
73 | /** |
74 | * whitelist - forcefully whitelist client |
75 | * |
76 | * @cldev: me clients device |
77 | */ |
78 | static void whitelist(struct mei_cl_device *cldev) |
79 | { |
80 | cldev->do_match = 1; |
81 | } |
82 | |
83 | #define OSTYPE_LINUX 2 |
84 | struct mei_os_ver { |
85 | __le16 build; |
86 | __le16 reserved1; |
87 | u8 os_type; |
88 | u8 major; |
89 | u8 minor; |
90 | u8 reserved2; |
91 | } __packed; |
92 | |
93 | struct mkhi_fw_ver_block { |
94 | u16 minor; |
95 | u8 major; |
96 | u8 platform; |
97 | u16 buildno; |
98 | u16 hotfix; |
99 | } __packed; |
100 | |
101 | struct mkhi_fw_ver { |
102 | struct mkhi_fw_ver_block ver[MEI_MAX_FW_VER_BLOCKS]; |
103 | } __packed; |
104 | |
105 | #define MKHI_OSVER_BUF_LEN (sizeof(struct mkhi_msg_hdr) + \ |
106 | sizeof(struct mkhi_fwcaps) + \ |
107 | sizeof(struct mei_os_ver)) |
108 | static int mei_osver(struct mei_cl_device *cldev) |
109 | { |
110 | const size_t size = MKHI_OSVER_BUF_LEN; |
111 | u8 buf[MKHI_OSVER_BUF_LEN]; |
112 | struct mkhi_msg *req; |
113 | struct mkhi_fwcaps *fwcaps; |
114 | struct mei_os_ver *os_ver; |
115 | unsigned int mode = MEI_CL_IO_TX_BLOCKING | MEI_CL_IO_TX_INTERNAL; |
116 | |
117 | memset(buf, 0, size); |
118 | |
119 | req = (struct mkhi_msg *)buf; |
120 | req->hdr.group_id = MKHI_FWCAPS_GROUP_ID; |
121 | req->hdr.command = MKHI_FWCAPS_SET_OS_VER_APP_RULE_CMD; |
122 | |
123 | fwcaps = (struct mkhi_fwcaps *)req->data; |
124 | |
125 | fwcaps->id.rule_type = 0x0; |
126 | fwcaps->id.feature_id = MKHI_FEATURE_PTT; |
127 | fwcaps->len = sizeof(*os_ver); |
128 | os_ver = (struct mei_os_ver *)fwcaps->data; |
129 | os_ver->os_type = OSTYPE_LINUX; |
130 | |
131 | return __mei_cl_send(cl: cldev->cl, buf, length: size, vtag: 0, mode); |
132 | } |
133 | |
134 | #define MKHI_FWVER_BUF_LEN (sizeof(struct mkhi_msg_hdr) + \ |
135 | sizeof(struct mkhi_fw_ver)) |
136 | #define MKHI_FWVER_LEN(__num) (sizeof(struct mkhi_msg_hdr) + \ |
137 | sizeof(struct mkhi_fw_ver_block) * (__num)) |
138 | static int mei_fwver(struct mei_cl_device *cldev) |
139 | { |
140 | u8 buf[MKHI_FWVER_BUF_LEN]; |
141 | struct mkhi_msg req; |
142 | struct mkhi_msg *rsp; |
143 | struct mkhi_fw_ver *fwver; |
144 | int bytes_recv, ret, i; |
145 | |
146 | memset(buf, 0, sizeof(buf)); |
147 | |
148 | req.hdr.group_id = MKHI_GEN_GROUP_ID; |
149 | req.hdr.command = MKHI_GEN_GET_FW_VERSION_CMD; |
150 | |
151 | ret = __mei_cl_send(cl: cldev->cl, buf: (u8 *)&req, length: sizeof(req), vtag: 0, |
152 | mode: MEI_CL_IO_TX_BLOCKING); |
153 | if (ret < 0) { |
154 | dev_info(&cldev->dev, "Could not send ReqFWVersion cmd ret = %d\n" , ret); |
155 | return ret; |
156 | } |
157 | |
158 | ret = 0; |
159 | bytes_recv = __mei_cl_recv(cl: cldev->cl, buf, length: sizeof(buf), NULL, mode: 0, |
160 | timeout: cldev->bus->timeouts.mkhi_recv); |
161 | if (bytes_recv < 0 || (size_t)bytes_recv < MKHI_FWVER_LEN(1)) { |
162 | /* |
163 | * Should be at least one version block, |
164 | * error out if nothing found |
165 | */ |
166 | dev_info(&cldev->dev, "Could not read FW version ret = %d\n" , bytes_recv); |
167 | return -EIO; |
168 | } |
169 | |
170 | rsp = (struct mkhi_msg *)buf; |
171 | fwver = (struct mkhi_fw_ver *)rsp->data; |
172 | memset(cldev->bus->fw_ver, 0, sizeof(cldev->bus->fw_ver)); |
173 | for (i = 0; i < MEI_MAX_FW_VER_BLOCKS; i++) { |
174 | if ((size_t)bytes_recv < MKHI_FWVER_LEN(i + 1)) |
175 | break; |
176 | dev_dbg(&cldev->dev, "FW version%d %d:%d.%d.%d.%d\n" , |
177 | i, fwver->ver[i].platform, |
178 | fwver->ver[i].major, fwver->ver[i].minor, |
179 | fwver->ver[i].hotfix, fwver->ver[i].buildno); |
180 | |
181 | cldev->bus->fw_ver[i].platform = fwver->ver[i].platform; |
182 | cldev->bus->fw_ver[i].major = fwver->ver[i].major; |
183 | cldev->bus->fw_ver[i].minor = fwver->ver[i].minor; |
184 | cldev->bus->fw_ver[i].hotfix = fwver->ver[i].hotfix; |
185 | cldev->bus->fw_ver[i].buildno = fwver->ver[i].buildno; |
186 | } |
187 | cldev->bus->fw_ver_received = 1; |
188 | |
189 | return ret; |
190 | } |
191 | |
192 | #define GFX_MEMORY_READY_TIMEOUT 200 /* timeout in milliseconds */ |
193 | |
194 | static int mei_gfx_memory_ready(struct mei_cl_device *cldev) |
195 | { |
196 | struct mkhi_gfx_mem_ready req = {0}; |
197 | unsigned int mode = MEI_CL_IO_TX_INTERNAL | MEI_CL_IO_TX_BLOCKING; |
198 | |
199 | req.hdr.group_id = MKHI_GROUP_ID_GFX; |
200 | req.hdr.command = MKHI_GFX_MEMORY_READY_CMD_REQ; |
201 | req.flags = MKHI_GFX_MEM_READY_PXP_ALLOWED; |
202 | |
203 | dev_dbg(&cldev->dev, "Sending memory ready command\n" ); |
204 | return __mei_cl_send_timeout(cl: cldev->cl, buf: (u8 *)&req, length: sizeof(req), vtag: 0, |
205 | mode, GFX_MEMORY_READY_TIMEOUT); |
206 | } |
207 | |
208 | static void mei_mkhi_fix(struct mei_cl_device *cldev) |
209 | { |
210 | int ret; |
211 | |
212 | /* No need to enable the client if nothing is needed from it */ |
213 | if (!cldev->bus->fw_f_fw_ver_supported && |
214 | !cldev->bus->hbm_f_os_supported) |
215 | return; |
216 | |
217 | ret = mei_cldev_enable(cldev); |
218 | if (ret) |
219 | return; |
220 | |
221 | if (cldev->bus->fw_f_fw_ver_supported) { |
222 | ret = mei_fwver(cldev); |
223 | if (ret < 0) |
224 | dev_info(&cldev->dev, "FW version command failed %d\n" , |
225 | ret); |
226 | } |
227 | |
228 | if (cldev->bus->hbm_f_os_supported) { |
229 | ret = mei_osver(cldev); |
230 | if (ret < 0) |
231 | dev_info(&cldev->dev, "OS version command failed %d\n" , |
232 | ret); |
233 | } |
234 | mei_cldev_disable(cldev); |
235 | } |
236 | |
237 | static void mei_gsc_mkhi_ver(struct mei_cl_device *cldev) |
238 | { |
239 | int ret; |
240 | |
241 | /* |
242 | * No need to enable the client if nothing is needed from it. |
243 | * No need to fill in version if it is already filled in by the fix address client. |
244 | */ |
245 | if (!cldev->bus->fw_f_fw_ver_supported || cldev->bus->fw_ver_received) |
246 | return; |
247 | |
248 | ret = mei_cldev_enable(cldev); |
249 | if (ret) |
250 | return; |
251 | |
252 | ret = mei_fwver(cldev); |
253 | if (ret < 0) |
254 | dev_info(&cldev->dev, "FW version command failed %d\n" , ret); |
255 | mei_cldev_disable(cldev); |
256 | } |
257 | |
258 | static void mei_gsc_mkhi_fix_ver(struct mei_cl_device *cldev) |
259 | { |
260 | int ret; |
261 | |
262 | /* No need to enable the client if nothing is needed from it */ |
263 | if (!cldev->bus->fw_f_fw_ver_supported && |
264 | cldev->bus->pxp_mode != MEI_DEV_PXP_INIT) |
265 | return; |
266 | |
267 | ret = mei_cldev_enable(cldev); |
268 | if (ret) |
269 | return; |
270 | |
271 | if (cldev->bus->pxp_mode == MEI_DEV_PXP_INIT) { |
272 | ret = mei_gfx_memory_ready(cldev); |
273 | if (ret < 0) { |
274 | dev_err(&cldev->dev, "memory ready command failed %d\n" , ret); |
275 | } else { |
276 | dev_dbg(&cldev->dev, "memory ready command sent\n" ); |
277 | cldev->bus->pxp_mode = MEI_DEV_PXP_SETUP; |
278 | } |
279 | /* we go to reset after that */ |
280 | goto out; |
281 | } |
282 | |
283 | ret = mei_fwver(cldev); |
284 | if (ret < 0) |
285 | dev_info(&cldev->dev, "FW version command failed %d\n" , |
286 | ret); |
287 | out: |
288 | mei_cldev_disable(cldev); |
289 | } |
290 | |
291 | /** |
292 | * mei_wd - wd client on the bus, change protocol version |
293 | * as the API has changed. |
294 | * |
295 | * @cldev: me clients device |
296 | */ |
297 | #if IS_ENABLED(CONFIG_INTEL_MEI_ME) |
298 | #include <linux/pci.h> |
299 | #include "hw-me-regs.h" |
300 | static void mei_wd(struct mei_cl_device *cldev) |
301 | { |
302 | struct pci_dev *pdev = to_pci_dev(cldev->dev.parent); |
303 | |
304 | if (pdev->device == MEI_DEV_ID_WPT_LP || |
305 | pdev->device == MEI_DEV_ID_SPT || |
306 | pdev->device == MEI_DEV_ID_SPT_H) |
307 | cldev->me_cl->props.protocol_version = 0x2; |
308 | |
309 | cldev->do_match = 1; |
310 | } |
311 | #else |
312 | static inline void mei_wd(struct mei_cl_device *cldev) {} |
313 | #endif /* CONFIG_INTEL_MEI_ME */ |
314 | |
315 | struct mei_nfc_cmd { |
316 | u8 command; |
317 | u8 status; |
318 | u16 req_id; |
319 | u32 reserved; |
320 | u16 data_size; |
321 | u8 sub_command; |
322 | u8 data[]; |
323 | } __packed; |
324 | |
325 | struct mei_nfc_reply { |
326 | u8 command; |
327 | u8 status; |
328 | u16 req_id; |
329 | u32 reserved; |
330 | u16 data_size; |
331 | u8 sub_command; |
332 | u8 reply_status; |
333 | u8 data[]; |
334 | } __packed; |
335 | |
336 | struct mei_nfc_if_version { |
337 | u8 radio_version_sw[3]; |
338 | u8 reserved[3]; |
339 | u8 radio_version_hw[3]; |
340 | u8 i2c_addr; |
341 | u8 fw_ivn; |
342 | u8 vendor_id; |
343 | u8 radio_type; |
344 | } __packed; |
345 | |
346 | |
347 | #define MEI_NFC_CMD_MAINTENANCE 0x00 |
348 | #define MEI_NFC_SUBCMD_IF_VERSION 0x01 |
349 | |
350 | /* Vendors */ |
351 | #define MEI_NFC_VENDOR_INSIDE 0x00 |
352 | #define MEI_NFC_VENDOR_NXP 0x01 |
353 | |
354 | /* Radio types */ |
355 | #define MEI_NFC_VENDOR_INSIDE_UREAD 0x00 |
356 | #define MEI_NFC_VENDOR_NXP_PN544 0x01 |
357 | |
358 | /** |
359 | * mei_nfc_if_version - get NFC interface version |
360 | * |
361 | * @cl: host client (nfc info) |
362 | * @ver: NFC interface version to be filled in |
363 | * |
364 | * Return: 0 on success; < 0 otherwise |
365 | */ |
366 | static int mei_nfc_if_version(struct mei_cl *cl, |
367 | struct mei_nfc_if_version *ver) |
368 | { |
369 | struct mei_device *bus; |
370 | struct mei_nfc_cmd cmd = { |
371 | .command = MEI_NFC_CMD_MAINTENANCE, |
372 | .data_size = 1, |
373 | .sub_command = MEI_NFC_SUBCMD_IF_VERSION, |
374 | }; |
375 | struct mei_nfc_reply *reply = NULL; |
376 | size_t if_version_length; |
377 | u8 vtag; |
378 | int bytes_recv, ret; |
379 | |
380 | bus = cl->dev; |
381 | |
382 | WARN_ON(mutex_is_locked(&bus->device_lock)); |
383 | |
384 | ret = __mei_cl_send(cl, buf: (u8 *)&cmd, length: sizeof(cmd), vtag: 0, |
385 | mode: MEI_CL_IO_TX_BLOCKING); |
386 | if (ret < 0) { |
387 | dev_err(bus->dev, "Could not send IF version cmd ret = %d\n" , ret); |
388 | return ret; |
389 | } |
390 | |
391 | /* to be sure on the stack we alloc memory */ |
392 | if_version_length = sizeof(*reply) + sizeof(*ver); |
393 | |
394 | reply = kzalloc(size: if_version_length, GFP_KERNEL); |
395 | if (!reply) |
396 | return -ENOMEM; |
397 | |
398 | ret = 0; |
399 | bytes_recv = __mei_cl_recv(cl, buf: (u8 *)reply, length: if_version_length, vtag: &vtag, |
400 | mode: 0, timeout: 0); |
401 | if (bytes_recv < 0 || (size_t)bytes_recv < if_version_length) { |
402 | dev_err(bus->dev, "Could not read IF version ret = %d\n" , bytes_recv); |
403 | ret = -EIO; |
404 | goto err; |
405 | } |
406 | |
407 | memcpy(ver, reply->data, sizeof(*ver)); |
408 | |
409 | dev_info(bus->dev, "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n" , |
410 | ver->fw_ivn, ver->vendor_id, ver->radio_type); |
411 | |
412 | err: |
413 | kfree(objp: reply); |
414 | return ret; |
415 | } |
416 | |
417 | /** |
418 | * mei_nfc_radio_name - derive nfc radio name from the interface version |
419 | * |
420 | * @ver: NFC radio version |
421 | * |
422 | * Return: radio name string |
423 | */ |
424 | static const char *mei_nfc_radio_name(struct mei_nfc_if_version *ver) |
425 | { |
426 | |
427 | if (ver->vendor_id == MEI_NFC_VENDOR_INSIDE) { |
428 | if (ver->radio_type == MEI_NFC_VENDOR_INSIDE_UREAD) |
429 | return "microread" ; |
430 | } |
431 | |
432 | if (ver->vendor_id == MEI_NFC_VENDOR_NXP) { |
433 | if (ver->radio_type == MEI_NFC_VENDOR_NXP_PN544) |
434 | return "pn544" ; |
435 | } |
436 | |
437 | return NULL; |
438 | } |
439 | |
440 | /** |
441 | * mei_nfc - The nfc fixup function. The function retrieves nfc radio |
442 | * name and set is as device attribute so we can load |
443 | * the proper device driver for it |
444 | * |
445 | * @cldev: me client device (nfc) |
446 | */ |
447 | static void mei_nfc(struct mei_cl_device *cldev) |
448 | { |
449 | struct mei_device *bus; |
450 | struct mei_cl *cl; |
451 | struct mei_me_client *me_cl = NULL; |
452 | struct mei_nfc_if_version ver; |
453 | const char *radio_name = NULL; |
454 | int ret; |
455 | |
456 | bus = cldev->bus; |
457 | |
458 | mutex_lock(&bus->device_lock); |
459 | /* we need to connect to INFO GUID */ |
460 | cl = mei_cl_alloc_linked(dev: bus); |
461 | if (IS_ERR(ptr: cl)) { |
462 | ret = PTR_ERR(ptr: cl); |
463 | cl = NULL; |
464 | dev_err(bus->dev, "nfc hook alloc failed %d\n" , ret); |
465 | goto out; |
466 | } |
467 | |
468 | me_cl = mei_me_cl_by_uuid(dev: bus, uuid: &mei_nfc_info_guid); |
469 | if (!me_cl) { |
470 | ret = -ENOTTY; |
471 | dev_err(bus->dev, "Cannot find nfc info %d\n" , ret); |
472 | goto out; |
473 | } |
474 | |
475 | ret = mei_cl_connect(cl, me_cl, NULL); |
476 | if (ret < 0) { |
477 | dev_err(&cldev->dev, "Can't connect to the NFC INFO ME ret = %d\n" , |
478 | ret); |
479 | goto out; |
480 | } |
481 | |
482 | mutex_unlock(lock: &bus->device_lock); |
483 | |
484 | ret = mei_nfc_if_version(cl, ver: &ver); |
485 | if (ret) |
486 | goto disconnect; |
487 | |
488 | radio_name = mei_nfc_radio_name(ver: &ver); |
489 | |
490 | if (!radio_name) { |
491 | ret = -ENOENT; |
492 | dev_err(&cldev->dev, "Can't get the NFC interface version ret = %d\n" , |
493 | ret); |
494 | goto disconnect; |
495 | } |
496 | |
497 | dev_dbg(bus->dev, "nfc radio %s\n" , radio_name); |
498 | strscpy(p: cldev->name, q: radio_name, size: sizeof(cldev->name)); |
499 | |
500 | disconnect: |
501 | mutex_lock(&bus->device_lock); |
502 | if (mei_cl_disconnect(cl) < 0) |
503 | dev_err(bus->dev, "Can't disconnect the NFC INFO ME\n" ); |
504 | |
505 | mei_cl_flush_queues(cl, NULL); |
506 | |
507 | out: |
508 | mei_cl_unlink(cl); |
509 | mutex_unlock(lock: &bus->device_lock); |
510 | mei_me_cl_put(me_cl); |
511 | kfree(objp: cl); |
512 | |
513 | if (ret) |
514 | cldev->do_match = 0; |
515 | |
516 | dev_dbg(bus->dev, "end of fixup match = %d\n" , cldev->do_match); |
517 | } |
518 | |
519 | /** |
520 | * vt_support - enable on bus clients with vtag support |
521 | * |
522 | * @cldev: me clients device |
523 | */ |
524 | static void vt_support(struct mei_cl_device *cldev) |
525 | { |
526 | if (cldev->me_cl->props.vt_supported == 1) |
527 | cldev->do_match = 1; |
528 | } |
529 | |
530 | /** |
531 | * pxp_is_ready - enable bus client if pxp is ready |
532 | * |
533 | * @cldev: me clients device |
534 | */ |
535 | static void pxp_is_ready(struct mei_cl_device *cldev) |
536 | { |
537 | struct mei_device *bus = cldev->bus; |
538 | |
539 | switch (bus->pxp_mode) { |
540 | case MEI_DEV_PXP_READY: |
541 | case MEI_DEV_PXP_DEFAULT: |
542 | cldev->do_match = 1; |
543 | break; |
544 | default: |
545 | cldev->do_match = 0; |
546 | break; |
547 | } |
548 | } |
549 | |
550 | #define MEI_FIXUP(_uuid, _hook) { _uuid, _hook } |
551 | |
552 | static struct mei_fixup { |
553 | |
554 | const uuid_le uuid; |
555 | void (*hook)(struct mei_cl_device *cldev); |
556 | } mei_fixups[] = { |
557 | MEI_FIXUP(MEI_UUID_ANY, number_of_connections), |
558 | MEI_FIXUP(MEI_UUID_NFC_INFO, blacklist), |
559 | MEI_FIXUP(MEI_UUID_NFC_HCI, mei_nfc), |
560 | MEI_FIXUP(MEI_UUID_WD, mei_wd), |
561 | MEI_FIXUP(MEI_UUID_MKHIF_FIX, mei_mkhi_fix), |
562 | MEI_FIXUP(MEI_UUID_IGSC_MKHI_FIX, mei_gsc_mkhi_fix_ver), |
563 | MEI_FIXUP(MEI_UUID_IGSC_MKHI, mei_gsc_mkhi_ver), |
564 | MEI_FIXUP(MEI_UUID_HDCP, whitelist), |
565 | MEI_FIXUP(MEI_UUID_ANY, vt_support), |
566 | MEI_FIXUP(MEI_UUID_PAVP, pxp_is_ready), |
567 | }; |
568 | |
569 | /** |
570 | * mei_cl_bus_dev_fixup - run fixup handlers |
571 | * |
572 | * @cldev: me client device |
573 | */ |
574 | void mei_cl_bus_dev_fixup(struct mei_cl_device *cldev) |
575 | { |
576 | struct mei_fixup *f; |
577 | const uuid_le *uuid = mei_me_cl_uuid(me_cl: cldev->me_cl); |
578 | size_t i; |
579 | |
580 | for (i = 0; i < ARRAY_SIZE(mei_fixups); i++) { |
581 | |
582 | f = &mei_fixups[i]; |
583 | if (uuid_le_cmp(u1: f->uuid, MEI_UUID_ANY) == 0 || |
584 | uuid_le_cmp(u1: f->uuid, u2: *uuid) == 0) |
585 | f->hook(cldev); |
586 | } |
587 | } |
588 | |
589 | |