1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (c) 2018 Bootlin
4 * Author: Miquel Raynal <miquel.raynal@bootlin.com>
9 #include <tpm-common.h>
11 #include <linux/bitops.h>
12 #include "tpm-utils.h"
14 u32 tpm2_startup(struct udevice *dev, enum tpm2_startup_types mode)
16 const u8 command_v2[12] = {
17 tpm_u16(TPM2_ST_NO_SESSIONS),
19 tpm_u32(TPM2_CC_STARTUP),
25 * Note TPM2_Startup command will return RC_SUCCESS the first time,
26 * but will return RC_INITIALIZE otherwise.
28 ret = tpm_sendrecv_command(dev, command_v2, NULL, NULL);
29 if (ret && ret != TPM2_RC_INITIALIZE)
35 u32 tpm2_self_test(struct udevice *dev, enum tpm2_yes_no full_test)
37 const u8 command_v2[12] = {
38 tpm_u16(TPM2_ST_NO_SESSIONS),
40 tpm_u32(TPM2_CC_SELF_TEST),
44 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
47 u32 tpm2_auto_start(struct udevice *dev)
51 rc = tpm2_self_test(dev, TPMI_YES);
53 if (rc == TPM2_RC_INITIALIZE) {
54 rc = tpm2_startup(dev, TPM2_SU_CLEAR);
58 rc = tpm2_self_test(dev, TPMI_YES);
64 u32 tpm2_clear(struct udevice *dev, u32 handle, const char *pw,
67 /* Length of the message header, up to start of password */
69 u8 command_v2[COMMAND_BUFFER_SIZE] = {
70 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
71 tpm_u32(offset + pw_sz), /* Length */
72 tpm_u32(TPM2_CC_CLEAR), /* Command code */
75 tpm_u32(handle), /* TPM resource handle */
78 tpm_u32(9 + pw_sz), /* Authorization size */
79 tpm_u32(TPM2_RS_PW), /* Session handle */
80 tpm_u16(0), /* Size of <nonce> */
81 /* <nonce> (if any) */
82 0, /* Attributes: Cont/Excl/Rst */
83 tpm_u16(pw_sz), /* Size of <hmac/password> */
84 /* STRING(pw) <hmac/password> (if any) */
89 * Fill the command structure starting from the first buffer:
90 * - the password (if any)
92 ret = pack_byte_string(command_v2, sizeof(command_v2), "s",
98 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
101 u32 tpm2_nv_define_space(struct udevice *dev, u32 space_index,
102 size_t space_size, u32 nv_attributes,
103 const u8 *nv_policy, size_t nv_policy_size)
106 * Calculate the offset of the nv_policy piece by adding each of the
109 const int platform_len = sizeof(u32);
110 const int session_hdr_len = 13;
111 const int message_len = 14;
112 uint offset = TPM2_HDR_LEN + platform_len + session_hdr_len +
114 u8 command_v2[COMMAND_BUFFER_SIZE] = {
115 /* header 10 bytes */
116 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
117 tpm_u32(offset + nv_policy_size + 2),/* Length */
118 tpm_u32(TPM2_CC_NV_DEFINE_SPACE),/* Command code */
120 /* handles 4 bytes */
121 tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */
123 /* session header 13 bytes */
124 tpm_u32(9), /* Header size */
125 tpm_u32(TPM2_RS_PW), /* Password authorisation */
126 tpm_u16(0), /* nonce_size */
127 0, /* session_attrs */
128 tpm_u16(0), /* auth_size */
130 /* message 14 bytes + policy */
131 tpm_u16(message_len + nv_policy_size), /* size */
132 tpm_u32(space_index),
133 tpm_u16(TPM2_ALG_SHA256),
134 tpm_u32(nv_attributes),
135 tpm_u16(nv_policy_size),
144 * Fill the command structure starting from the first buffer:
145 * - the password (if any)
147 ret = pack_byte_string(command_v2, sizeof(command_v2), "sw",
148 offset, nv_policy, nv_policy_size,
149 offset + nv_policy_size, space_size);
151 return TPM_LIB_ERROR;
153 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
156 u32 tpm2_pcr_extend(struct udevice *dev, u32 index, u32 algorithm,
157 const u8 *digest, u32 digest_len)
159 /* Length of the message header, up to start of digest */
161 u8 command_v2[COMMAND_BUFFER_SIZE] = {
162 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
163 tpm_u32(offset + digest_len), /* Length */
164 tpm_u32(TPM2_CC_PCR_EXTEND), /* Command code */
167 tpm_u32(index), /* Handle (PCR Index) */
170 tpm_u32(9), /* Authorization size */
171 tpm_u32(TPM2_RS_PW), /* Session handle */
172 tpm_u16(0), /* Size of <nonce> */
173 /* <nonce> (if any) */
174 0, /* Attributes: Cont/Excl/Rst */
175 tpm_u16(0), /* Size of <hmac/password> */
176 /* <hmac/password> (if any) */
179 tpm_u32(1), /* Count (number of hashes) */
180 tpm_u16(algorithm), /* Algorithm of the hash */
181 /* STRING(digest) Digest */
188 * Fill the command structure starting from the first buffer:
191 ret = pack_byte_string(command_v2, sizeof(command_v2), "s",
192 offset, digest, digest_len);
194 return TPM_LIB_ERROR;
196 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
199 u32 tpm2_nv_read_value(struct udevice *dev, u32 index, void *data, u32 count)
201 u8 command_v2[COMMAND_BUFFER_SIZE] = {
202 /* header 10 bytes */
203 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
204 tpm_u32(10 + 8 + 4 + 9 + 4), /* Length */
205 tpm_u32(TPM2_CC_NV_READ), /* Command code */
207 /* handles 8 bytes */
208 tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */
209 tpm_u32(HR_NV_INDEX + index), /* Password authorisation */
212 tpm_u32(9), /* Authorization size */
213 tpm_u32(TPM2_RS_PW), /* Session handle */
214 tpm_u16(0), /* Size of <nonce> */
215 /* <nonce> (if any) */
216 0, /* Attributes: Cont/Excl/Rst */
217 tpm_u16(0), /* Size of <hmac/password> */
218 /* <hmac/password> (if any) */
220 tpm_u16(count), /* Number of bytes */
221 tpm_u16(0), /* Offset */
223 size_t response_len = COMMAND_BUFFER_SIZE;
224 u8 response[COMMAND_BUFFER_SIZE];
229 ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
231 return log_msg_ret("read", ret);
232 if (unpack_byte_string(response, response_len, "wdds",
233 0, &tag, 2, &size, 6, &code,
235 return TPM_LIB_ERROR;
240 u32 tpm2_nv_write_value(struct udevice *dev, u32 index, const void *data,
243 struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
244 uint offset = 10 + 8 + 4 + 9 + 2;
245 uint len = offset + count + 2;
246 /* Use empty password auth if platform hierarchy is disabled */
247 u32 auth = priv->plat_hier_disabled ? HR_NV_INDEX + index :
249 u8 command_v2[COMMAND_BUFFER_SIZE] = {
250 /* header 10 bytes */
251 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
252 tpm_u32(len), /* Length */
253 tpm_u32(TPM2_CC_NV_WRITE), /* Command code */
255 /* handles 8 bytes */
256 tpm_u32(auth), /* Primary platform seed */
257 tpm_u32(HR_NV_INDEX + index), /* Password authorisation */
260 tpm_u32(9), /* Authorization size */
261 tpm_u32(TPM2_RS_PW), /* Session handle */
262 tpm_u16(0), /* Size of <nonce> */
263 /* <nonce> (if any) */
264 0, /* Attributes: Cont/Excl/Rst */
265 tpm_u16(0), /* Size of <hmac/password> */
266 /* <hmac/password> (if any) */
270 size_t response_len = COMMAND_BUFFER_SIZE;
271 u8 response[COMMAND_BUFFER_SIZE];
274 ret = pack_byte_string(command_v2, sizeof(command_v2), "sw",
278 return TPM_LIB_ERROR;
280 return tpm_sendrecv_command(dev, command_v2, response, &response_len);
283 u32 tpm2_pcr_read(struct udevice *dev, u32 idx, unsigned int idx_min_sz,
284 u16 algorithm, void *data, u32 digest_len,
285 unsigned int *updates)
287 u8 idx_array_sz = max(idx_min_sz, DIV_ROUND_UP(idx, 8));
288 u8 command_v2[COMMAND_BUFFER_SIZE] = {
289 tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
290 tpm_u32(17 + idx_array_sz), /* Length */
291 tpm_u32(TPM2_CC_PCR_READ), /* Command code */
293 /* TPML_PCR_SELECTION */
294 tpm_u32(1), /* Number of selections */
295 tpm_u16(algorithm), /* Algorithm of the hash */
296 idx_array_sz, /* Array size for selection */
297 /* bitmap(idx) Selected PCR bitmap */
299 size_t response_len = COMMAND_BUFFER_SIZE;
300 u8 response[COMMAND_BUFFER_SIZE];
301 unsigned int pcr_sel_idx = idx / 8;
302 u8 pcr_sel_bit = BIT(idx % 8);
303 unsigned int counter = 0;
306 if (pack_byte_string(command_v2, COMMAND_BUFFER_SIZE, "b",
307 17 + pcr_sel_idx, pcr_sel_bit))
308 return TPM_LIB_ERROR;
310 ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
314 if (digest_len > response_len)
315 return TPM_LIB_ERROR;
317 if (unpack_byte_string(response, response_len, "ds",
319 response_len - digest_len, data,
321 return TPM_LIB_ERROR;
329 u32 tpm2_get_capability(struct udevice *dev, u32 capability, u32 property,
330 void *buf, size_t prop_count)
332 u8 command_v2[COMMAND_BUFFER_SIZE] = {
333 tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
334 tpm_u32(22), /* Length */
335 tpm_u32(TPM2_CC_GET_CAPABILITY), /* Command code */
337 tpm_u32(capability), /* Capability */
338 tpm_u32(property), /* Property */
339 tpm_u32(prop_count), /* Property count */
341 u8 response[COMMAND_BUFFER_SIZE];
342 size_t response_len = COMMAND_BUFFER_SIZE;
343 unsigned int properties_off;
346 ret = tpm_sendrecv_command(dev, command_v2, response, &response_len);
351 * In the response buffer, the properties are located after the:
352 * tag (u16), response size (u32), response code (u32),
353 * YES/NO flag (u8), TPM_CAP (u32).
355 properties_off = sizeof(u16) + sizeof(u32) + sizeof(u32) +
356 sizeof(u8) + sizeof(u32);
357 memcpy(buf, &response[properties_off], response_len - properties_off);
362 u32 tpm2_dam_reset(struct udevice *dev, const char *pw, const ssize_t pw_sz)
364 u8 command_v2[COMMAND_BUFFER_SIZE] = {
365 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
366 tpm_u32(27 + pw_sz), /* Length */
367 tpm_u32(TPM2_CC_DAM_RESET), /* Command code */
370 tpm_u32(TPM2_RH_LOCKOUT), /* TPM resource handle */
373 tpm_u32(9 + pw_sz), /* Authorization size */
374 tpm_u32(TPM2_RS_PW), /* Session handle */
375 tpm_u16(0), /* Size of <nonce> */
376 /* <nonce> (if any) */
377 0, /* Attributes: Cont/Excl/Rst */
378 tpm_u16(pw_sz), /* Size of <hmac/password> */
379 /* STRING(pw) <hmac/password> (if any) */
381 unsigned int offset = 27;
385 * Fill the command structure starting from the first buffer:
386 * - the password (if any)
388 ret = pack_byte_string(command_v2, sizeof(command_v2), "s",
392 return TPM_LIB_ERROR;
394 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
397 u32 tpm2_dam_parameters(struct udevice *dev, const char *pw,
398 const ssize_t pw_sz, unsigned int max_tries,
399 unsigned int recovery_time,
400 unsigned int lockout_recovery)
402 u8 command_v2[COMMAND_BUFFER_SIZE] = {
403 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
404 tpm_u32(27 + pw_sz + 12), /* Length */
405 tpm_u32(TPM2_CC_DAM_PARAMETERS), /* Command code */
408 tpm_u32(TPM2_RH_LOCKOUT), /* TPM resource handle */
411 tpm_u32(9 + pw_sz), /* Authorization size */
412 tpm_u32(TPM2_RS_PW), /* Session handle */
413 tpm_u16(0), /* Size of <nonce> */
414 /* <nonce> (if any) */
415 0, /* Attributes: Cont/Excl/Rst */
416 tpm_u16(pw_sz), /* Size of <hmac/password> */
417 /* STRING(pw) <hmac/password> (if any) */
419 /* LOCKOUT PARAMETERS */
420 /* tpm_u32(max_tries) Max tries (0, always lock) */
421 /* tpm_u32(recovery_time) Recovery time (0, no lock) */
422 /* tpm_u32(lockout_recovery) Lockout recovery */
424 unsigned int offset = 27;
428 * Fill the command structure starting from the first buffer:
429 * - the password (if any)
434 ret = pack_byte_string(command_v2, sizeof(command_v2), "sddd",
436 offset + pw_sz, max_tries,
437 offset + pw_sz + 4, recovery_time,
438 offset + pw_sz + 8, lockout_recovery);
439 offset += pw_sz + 12;
441 return TPM_LIB_ERROR;
443 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
446 int tpm2_change_auth(struct udevice *dev, u32 handle, const char *newpw,
447 const ssize_t newpw_sz, const char *oldpw,
448 const ssize_t oldpw_sz)
450 unsigned int offset = 27;
451 u8 command_v2[COMMAND_BUFFER_SIZE] = {
452 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
453 tpm_u32(offset + oldpw_sz + 2 + newpw_sz), /* Length */
454 tpm_u32(TPM2_CC_HIERCHANGEAUTH), /* Command code */
457 tpm_u32(handle), /* TPM resource handle */
460 tpm_u32(9 + oldpw_sz), /* Authorization size */
461 tpm_u32(TPM2_RS_PW), /* Session handle */
462 tpm_u16(0), /* Size of <nonce> */
463 /* <nonce> (if any) */
464 0, /* Attributes: Cont/Excl/Rst */
465 tpm_u16(oldpw_sz) /* Size of <hmac/password> */
466 /* STRING(oldpw) <hmac/password> (if any) */
468 /* TPM2B_AUTH (TPM2B_DIGEST) */
469 /* tpm_u16(newpw_sz) Digest size, new pw length */
470 /* STRING(newpw) Digest buffer, new pw */
475 * Fill the command structure starting from the first buffer:
476 * - the old password (if any)
477 * - size of the new password
480 ret = pack_byte_string(command_v2, sizeof(command_v2), "sws",
481 offset, oldpw, oldpw_sz,
482 offset + oldpw_sz, newpw_sz,
483 offset + oldpw_sz + 2, newpw, newpw_sz);
484 offset += oldpw_sz + 2 + newpw_sz;
486 return TPM_LIB_ERROR;
488 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
491 u32 tpm2_pcr_setauthpolicy(struct udevice *dev, const char *pw,
492 const ssize_t pw_sz, u32 index, const char *key)
494 u8 command_v2[COMMAND_BUFFER_SIZE] = {
495 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
496 tpm_u32(35 + pw_sz + TPM2_DIGEST_LEN), /* Length */
497 tpm_u32(TPM2_CC_PCR_SETAUTHPOL), /* Command code */
500 tpm_u32(TPM2_RH_PLATFORM), /* TPM resource handle */
503 tpm_u32(9 + pw_sz), /* Authorization size */
504 tpm_u32(TPM2_RS_PW), /* session handle */
505 tpm_u16(0), /* Size of <nonce> */
506 /* <nonce> (if any) */
507 0, /* Attributes: Cont/Excl/Rst */
508 tpm_u16(pw_sz) /* Size of <hmac/password> */
509 /* STRING(pw) <hmac/password> (if any) */
511 /* TPM2B_AUTH (TPM2B_DIGEST) */
512 /* tpm_u16(TPM2_DIGEST_LEN) Digest size length */
513 /* STRING(key) Digest buffer (PCR key) */
516 /* tpm_u16(TPM2_ALG_SHA256) Algorithm of the hash */
519 /* tpm_u32(index), PCR Index */
521 unsigned int offset = 27;
525 * Fill the command structure starting from the first buffer:
526 * - the password (if any)
527 * - the PCR key length
529 * - the hash algorithm
532 ret = pack_byte_string(command_v2, sizeof(command_v2), "swswd",
534 offset + pw_sz, TPM2_DIGEST_LEN,
535 offset + pw_sz + 2, key, TPM2_DIGEST_LEN,
536 offset + pw_sz + 2 + TPM2_DIGEST_LEN,
538 offset + pw_sz + 4 + TPM2_DIGEST_LEN, index);
539 offset += pw_sz + 2 + TPM2_DIGEST_LEN + 2 + 4;
541 return TPM_LIB_ERROR;
543 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
546 u32 tpm2_pcr_setauthvalue(struct udevice *dev, const char *pw,
547 const ssize_t pw_sz, u32 index, const char *key,
548 const ssize_t key_sz)
550 u8 command_v2[COMMAND_BUFFER_SIZE] = {
551 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
552 tpm_u32(33 + pw_sz + TPM2_DIGEST_LEN), /* Length */
553 tpm_u32(TPM2_CC_PCR_SETAUTHVAL), /* Command code */
556 tpm_u32(index), /* Handle (PCR Index) */
559 tpm_u32(9 + pw_sz), /* Authorization size */
560 tpm_u32(TPM2_RS_PW), /* session handle */
561 tpm_u16(0), /* Size of <nonce> */
562 /* <nonce> (if any) */
563 0, /* Attributes: Cont/Excl/Rst */
564 tpm_u16(pw_sz), /* Size of <hmac/password> */
565 /* STRING(pw) <hmac/password> (if any) */
568 /* tpm_u16(key_sz) Key length */
569 /* STRING(key) Key */
571 unsigned int offset = 27;
575 * Fill the command structure starting from the first buffer:
576 * - the password (if any)
577 * - the number of digests, 1 in our case
578 * - the algorithm, sha256 in our case
579 * - the digest (64 bytes)
581 ret = pack_byte_string(command_v2, sizeof(command_v2), "sws",
583 offset + pw_sz, key_sz,
584 offset + pw_sz + 2, key, key_sz);
585 offset += pw_sz + 2 + key_sz;
587 return TPM_LIB_ERROR;
589 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
592 u32 tpm2_get_random(struct udevice *dev, void *data, u32 count)
594 const u8 command_v2[10] = {
595 tpm_u16(TPM2_ST_NO_SESSIONS),
597 tpm_u32(TPM2_CC_GET_RANDOM),
599 u8 buf[COMMAND_BUFFER_SIZE], response[COMMAND_BUFFER_SIZE];
601 const size_t data_size_offset = 10;
602 const size_t data_offset = 12;
603 size_t response_length = sizeof(response);
608 u32 this_bytes = min((size_t)count,
609 sizeof(response) - data_offset);
612 if (pack_byte_string(buf, sizeof(buf), "sw",
613 0, command_v2, sizeof(command_v2),
614 sizeof(command_v2), this_bytes))
615 return TPM_LIB_ERROR;
616 err = tpm_sendrecv_command(dev, buf, response,
620 if (unpack_byte_string(response, response_length, "w",
621 data_size_offset, &data_size))
622 return TPM_LIB_ERROR;
623 if (data_size > this_bytes)
624 return TPM_LIB_ERROR;
625 if (unpack_byte_string(response, response_length, "s",
626 data_offset, out, data_size))
627 return TPM_LIB_ERROR;
636 u32 tpm2_write_lock(struct udevice *dev, u32 index)
638 u8 command_v2[COMMAND_BUFFER_SIZE] = {
639 /* header 10 bytes */
640 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
641 tpm_u32(10 + 8 + 13), /* Length */
642 tpm_u32(TPM2_CC_NV_WRITELOCK), /* Command code */
644 /* handles 8 bytes */
645 tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */
646 tpm_u32(HR_NV_INDEX + index), /* Password authorisation */
648 /* session header 9 bytes */
649 tpm_u32(9), /* Header size */
650 tpm_u32(TPM2_RS_PW), /* Password authorisation */
651 tpm_u16(0), /* nonce_size */
652 0, /* session_attrs */
653 tpm_u16(0), /* auth_size */
656 return tpm_sendrecv_command(dev, command_v2, NULL, NULL);
659 u32 tpm2_disable_platform_hierarchy(struct udevice *dev)
661 struct tpm_chip_priv *priv = dev_get_uclass_priv(dev);
662 u8 command_v2[COMMAND_BUFFER_SIZE] = {
663 /* header 10 bytes */
664 tpm_u16(TPM2_ST_SESSIONS), /* TAG */
665 tpm_u32(10 + 4 + 13 + 5), /* Length */
666 tpm_u32(TPM2_CC_HIER_CONTROL), /* Command code */
669 tpm_u32(TPM2_RH_PLATFORM), /* Primary platform seed */
671 /* session header 9 bytes */
672 tpm_u32(9), /* Header size */
673 tpm_u32(TPM2_RS_PW), /* Password authorisation */
674 tpm_u16(0), /* nonce_size */
675 0, /* session_attrs */
676 tpm_u16(0), /* auth_size */
678 /* payload 5 bytes */
679 tpm_u32(TPM2_RH_PLATFORM), /* Hierarchy to disable */
684 ret = tpm_sendrecv_command(dev, command_v2, NULL, NULL);
685 log_info("ret=%s, %x\n", dev->name, ret);
689 priv->plat_hier_disabled = true;
694 u32 tpm2_submit_command(struct udevice *dev, const u8 *sendbuf,
695 u8 *recvbuf, size_t *recv_size)
697 return tpm_sendrecv_command(dev, sendbuf, recvbuf, recv_size);
700 u32 tpm2_report_state(struct udevice *dev, uint vendor_cmd, uint vendor_subcmd,
701 u8 *recvbuf, size_t *recv_size)
703 u8 command_v2[COMMAND_BUFFER_SIZE] = {
704 /* header 10 bytes */
705 tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
706 tpm_u32(10 + 2), /* Length */
707 tpm_u32(vendor_cmd), /* Command code */
709 tpm_u16(vendor_subcmd),
713 ret = tpm_sendrecv_command(dev, command_v2, recvbuf, recv_size);
714 log_debug("ret=%s, %x\n", dev->name, ret);
720 memcpy(recvbuf, recvbuf + 12, *recv_size);
725 u32 tpm2_enable_nvcommits(struct udevice *dev, uint vendor_cmd,
728 u8 command_v2[COMMAND_BUFFER_SIZE] = {
729 /* header 10 bytes */
730 tpm_u16(TPM2_ST_NO_SESSIONS), /* TAG */
731 tpm_u32(10 + 2), /* Length */
732 tpm_u32(vendor_cmd), /* Command code */
734 tpm_u16(vendor_subcmd),
738 ret = tpm_sendrecv_command(dev, command_v2, NULL, NULL);
739 log_debug("ret=%s, %x\n", dev->name, ret);