OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright (C) 2011 Infineon Technologies |
| 3 * |
| 4 * Authors: |
| 5 * |
| 6 * |
| 7 * Device driver for TCG/TCPA TPM (trusted platform module). |
| 8 * Specifications at www.trustedcomputinggroup.org |
| 9 * |
| 10 * It is based on the Linux kernel driver tpm.c from Leendert van |
| 11 * Dorn, Dave Safford, Reiner Sailer, and Kyleen Hall. |
| 12 * |
| 13 * This program is free software; you can redistribute it and/or |
| 14 * modify it under the terms of the GNU General Public License as |
| 15 * published by the Free Software Foundation, version 2 of the |
| 16 * License. |
| 17 * |
| 18 */ |
| 19 |
| 20 #include <malloc.h> |
| 21 |
| 22 #include "tpm.h" |
| 23 #include "tpm_tis_i2c.h" |
| 24 |
| 25 /* global structure for tpm chip data */ |
| 26 struct tpm_chip g_chip; |
| 27 |
| 28 enum tpm_duration { |
| 29 TPM_SHORT = 0, |
| 30 TPM_MEDIUM = 1, |
| 31 TPM_LONG = 2, |
| 32 TPM_UNDEFINED, |
| 33 }; |
| 34 |
| 35 #define TPM_MAX_ORDINAL 243 |
| 36 #define TPM_MAX_PROTECTED_ORDINAL 12 |
| 37 #define TPM_PROTECTED_ORDINAL_MASK 0xFF |
| 38 |
| 39 /* |
| 40 * Array with one entry per ordinal defining the maximum amount |
| 41 * of time the chip could take to return the result. The ordinal |
| 42 * designation of short, medium or long is defined in a table in |
| 43 * TCG Specification TPM Main Part 2 TPM Structures Section 17. The |
| 44 * values of the SHORT, MEDIUM, and LONG durations are retrieved |
| 45 * from the chip during initialization with a call to tpm_get_timeouts. |
| 46 */ |
| 47 static const u8 tpm_protected_ordinal_duration[TPM_MAX_PROTECTED_ORDINAL] = { |
| 48 TPM_UNDEFINED, /* 0 */ |
| 49 TPM_UNDEFINED, |
| 50 TPM_UNDEFINED, |
| 51 TPM_UNDEFINED, |
| 52 TPM_UNDEFINED, |
| 53 TPM_UNDEFINED, /* 5 */ |
| 54 TPM_UNDEFINED, |
| 55 TPM_UNDEFINED, |
| 56 TPM_UNDEFINED, |
| 57 TPM_UNDEFINED, |
| 58 TPM_SHORT, /* 10 */ |
| 59 TPM_SHORT, |
| 60 }; |
| 61 |
| 62 static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = { |
| 63 TPM_UNDEFINED, /* 0 */ |
| 64 TPM_UNDEFINED, |
| 65 TPM_UNDEFINED, |
| 66 TPM_UNDEFINED, |
| 67 TPM_UNDEFINED, |
| 68 TPM_UNDEFINED, /* 5 */ |
| 69 TPM_UNDEFINED, |
| 70 TPM_UNDEFINED, |
| 71 TPM_UNDEFINED, |
| 72 TPM_UNDEFINED, |
| 73 TPM_SHORT, /* 10 */ |
| 74 TPM_SHORT, |
| 75 TPM_MEDIUM, |
| 76 TPM_LONG, |
| 77 TPM_LONG, |
| 78 TPM_MEDIUM, /* 15 */ |
| 79 TPM_SHORT, |
| 80 TPM_SHORT, |
| 81 TPM_MEDIUM, |
| 82 TPM_LONG, |
| 83 TPM_SHORT, /* 20 */ |
| 84 TPM_SHORT, |
| 85 TPM_MEDIUM, |
| 86 TPM_MEDIUM, |
| 87 TPM_MEDIUM, |
| 88 TPM_SHORT, /* 25 */ |
| 89 TPM_SHORT, |
| 90 TPM_MEDIUM, |
| 91 TPM_SHORT, |
| 92 TPM_SHORT, |
| 93 TPM_MEDIUM, /* 30 */ |
| 94 TPM_LONG, |
| 95 TPM_MEDIUM, |
| 96 TPM_SHORT, |
| 97 TPM_SHORT, |
| 98 TPM_SHORT, /* 35 */ |
| 99 TPM_MEDIUM, |
| 100 TPM_MEDIUM, |
| 101 TPM_UNDEFINED, |
| 102 TPM_UNDEFINED, |
| 103 TPM_MEDIUM, /* 40 */ |
| 104 TPM_LONG, |
| 105 TPM_MEDIUM, |
| 106 TPM_SHORT, |
| 107 TPM_SHORT, |
| 108 TPM_SHORT, /* 45 */ |
| 109 TPM_SHORT, |
| 110 TPM_SHORT, |
| 111 TPM_SHORT, |
| 112 TPM_LONG, |
| 113 TPM_MEDIUM, /* 50 */ |
| 114 TPM_MEDIUM, |
| 115 TPM_UNDEFINED, |
| 116 TPM_UNDEFINED, |
| 117 TPM_UNDEFINED, |
| 118 TPM_UNDEFINED, /* 55 */ |
| 119 TPM_UNDEFINED, |
| 120 TPM_UNDEFINED, |
| 121 TPM_UNDEFINED, |
| 122 TPM_UNDEFINED, |
| 123 TPM_MEDIUM, /* 60 */ |
| 124 TPM_MEDIUM, |
| 125 TPM_MEDIUM, |
| 126 TPM_SHORT, |
| 127 TPM_SHORT, |
| 128 TPM_MEDIUM, /* 65 */ |
| 129 TPM_UNDEFINED, |
| 130 TPM_UNDEFINED, |
| 131 TPM_UNDEFINED, |
| 132 TPM_UNDEFINED, |
| 133 TPM_SHORT, /* 70 */ |
| 134 TPM_SHORT, |
| 135 TPM_UNDEFINED, |
| 136 TPM_UNDEFINED, |
| 137 TPM_UNDEFINED, |
| 138 TPM_UNDEFINED, /* 75 */ |
| 139 TPM_UNDEFINED, |
| 140 TPM_UNDEFINED, |
| 141 TPM_UNDEFINED, |
| 142 TPM_UNDEFINED, |
| 143 TPM_LONG, /* 80 */ |
| 144 TPM_UNDEFINED, |
| 145 TPM_MEDIUM, |
| 146 TPM_LONG, |
| 147 TPM_SHORT, |
| 148 TPM_UNDEFINED, /* 85 */ |
| 149 TPM_UNDEFINED, |
| 150 TPM_UNDEFINED, |
| 151 TPM_UNDEFINED, |
| 152 TPM_UNDEFINED, |
| 153 TPM_SHORT, /* 90 */ |
| 154 TPM_SHORT, |
| 155 TPM_SHORT, |
| 156 TPM_SHORT, |
| 157 TPM_SHORT, |
| 158 TPM_UNDEFINED, /* 95 */ |
| 159 TPM_UNDEFINED, |
| 160 TPM_UNDEFINED, |
| 161 TPM_UNDEFINED, |
| 162 TPM_UNDEFINED, |
| 163 TPM_MEDIUM, /* 100 */ |
| 164 TPM_SHORT, |
| 165 TPM_SHORT, |
| 166 TPM_UNDEFINED, |
| 167 TPM_UNDEFINED, |
| 168 TPM_UNDEFINED, /* 105 */ |
| 169 TPM_UNDEFINED, |
| 170 TPM_UNDEFINED, |
| 171 TPM_UNDEFINED, |
| 172 TPM_UNDEFINED, |
| 173 TPM_SHORT, /* 110 */ |
| 174 TPM_SHORT, |
| 175 TPM_SHORT, |
| 176 TPM_SHORT, |
| 177 TPM_SHORT, |
| 178 TPM_SHORT, /* 115 */ |
| 179 TPM_SHORT, |
| 180 TPM_SHORT, |
| 181 TPM_UNDEFINED, |
| 182 TPM_UNDEFINED, |
| 183 TPM_LONG, /* 120 */ |
| 184 TPM_LONG, |
| 185 TPM_MEDIUM, |
| 186 TPM_UNDEFINED, |
| 187 TPM_SHORT, |
| 188 TPM_SHORT, /* 125 */ |
| 189 TPM_SHORT, |
| 190 TPM_LONG, |
| 191 TPM_SHORT, |
| 192 TPM_SHORT, |
| 193 TPM_SHORT, /* 130 */ |
| 194 TPM_MEDIUM, |
| 195 TPM_UNDEFINED, |
| 196 TPM_SHORT, |
| 197 TPM_MEDIUM, |
| 198 TPM_UNDEFINED, /* 135 */ |
| 199 TPM_UNDEFINED, |
| 200 TPM_UNDEFINED, |
| 201 TPM_UNDEFINED, |
| 202 TPM_UNDEFINED, |
| 203 TPM_SHORT, /* 140 */ |
| 204 TPM_SHORT, |
| 205 TPM_UNDEFINED, |
| 206 TPM_UNDEFINED, |
| 207 TPM_UNDEFINED, |
| 208 TPM_UNDEFINED, /* 145 */ |
| 209 TPM_UNDEFINED, |
| 210 TPM_UNDEFINED, |
| 211 TPM_UNDEFINED, |
| 212 TPM_UNDEFINED, |
| 213 TPM_SHORT, /* 150 */ |
| 214 TPM_MEDIUM, |
| 215 TPM_MEDIUM, |
| 216 TPM_SHORT, |
| 217 TPM_SHORT, |
| 218 TPM_UNDEFINED, /* 155 */ |
| 219 TPM_UNDEFINED, |
| 220 TPM_UNDEFINED, |
| 221 TPM_UNDEFINED, |
| 222 TPM_UNDEFINED, |
| 223 TPM_SHORT, /* 160 */ |
| 224 TPM_SHORT, |
| 225 TPM_SHORT, |
| 226 TPM_SHORT, |
| 227 TPM_UNDEFINED, |
| 228 TPM_UNDEFINED, /* 165 */ |
| 229 TPM_UNDEFINED, |
| 230 TPM_UNDEFINED, |
| 231 TPM_UNDEFINED, |
| 232 TPM_UNDEFINED, |
| 233 TPM_LONG, /* 170 */ |
| 234 TPM_UNDEFINED, |
| 235 TPM_UNDEFINED, |
| 236 TPM_UNDEFINED, |
| 237 TPM_UNDEFINED, |
| 238 TPM_UNDEFINED, /* 175 */ |
| 239 TPM_UNDEFINED, |
| 240 TPM_UNDEFINED, |
| 241 TPM_UNDEFINED, |
| 242 TPM_UNDEFINED, |
| 243 TPM_MEDIUM, /* 180 */ |
| 244 TPM_SHORT, |
| 245 TPM_MEDIUM, |
| 246 TPM_MEDIUM, |
| 247 TPM_MEDIUM, |
| 248 TPM_MEDIUM, /* 185 */ |
| 249 TPM_SHORT, |
| 250 TPM_UNDEFINED, |
| 251 TPM_UNDEFINED, |
| 252 TPM_UNDEFINED, |
| 253 TPM_UNDEFINED, /* 190 */ |
| 254 TPM_UNDEFINED, |
| 255 TPM_UNDEFINED, |
| 256 TPM_UNDEFINED, |
| 257 TPM_UNDEFINED, |
| 258 TPM_UNDEFINED, /* 195 */ |
| 259 TPM_UNDEFINED, |
| 260 TPM_UNDEFINED, |
| 261 TPM_UNDEFINED, |
| 262 TPM_UNDEFINED, |
| 263 TPM_SHORT, /* 200 */ |
| 264 TPM_UNDEFINED, |
| 265 TPM_UNDEFINED, |
| 266 TPM_UNDEFINED, |
| 267 TPM_SHORT, |
| 268 TPM_SHORT, /* 205 */ |
| 269 TPM_SHORT, |
| 270 TPM_SHORT, |
| 271 TPM_SHORT, |
| 272 TPM_SHORT, |
| 273 TPM_MEDIUM, /* 210 */ |
| 274 TPM_UNDEFINED, |
| 275 TPM_MEDIUM, |
| 276 TPM_MEDIUM, |
| 277 TPM_MEDIUM, |
| 278 TPM_UNDEFINED, /* 215 */ |
| 279 TPM_MEDIUM, |
| 280 TPM_UNDEFINED, |
| 281 TPM_UNDEFINED, |
| 282 TPM_SHORT, |
| 283 TPM_SHORT, /* 220 */ |
| 284 TPM_SHORT, |
| 285 TPM_SHORT, |
| 286 TPM_SHORT, |
| 287 TPM_SHORT, |
| 288 TPM_UNDEFINED, /* 225 */ |
| 289 TPM_UNDEFINED, |
| 290 TPM_UNDEFINED, |
| 291 TPM_UNDEFINED, |
| 292 TPM_UNDEFINED, |
| 293 TPM_SHORT, /* 230 */ |
| 294 TPM_LONG, |
| 295 TPM_MEDIUM, |
| 296 TPM_UNDEFINED, |
| 297 TPM_UNDEFINED, |
| 298 TPM_UNDEFINED, /* 235 */ |
| 299 TPM_UNDEFINED, |
| 300 TPM_UNDEFINED, |
| 301 TPM_UNDEFINED, |
| 302 TPM_UNDEFINED, |
| 303 TPM_SHORT, /* 240 */ |
| 304 TPM_UNDEFINED, |
| 305 TPM_MEDIUM, |
| 306 }; |
| 307 |
| 308 /* |
| 309 * Returns max number of jiffies to wait |
| 310 */ |
| 311 unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) |
| 312 { |
| 313 int duration_idx = TPM_UNDEFINED; |
| 314 int duration = 0; |
| 315 |
| 316 if (ordinal < TPM_MAX_ORDINAL) |
| 317 duration_idx = tpm_ordinal_duration[ordinal]; |
| 318 else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) < |
| 319 TPM_MAX_PROTECTED_ORDINAL) |
| 320 duration_idx = |
| 321 tpm_protected_ordinal_duration[ordinal & |
| 322 TPM_PROTECTED_ORDINAL_MASK]; |
| 323 |
| 324 if (duration_idx != TPM_UNDEFINED) |
| 325 duration = chip->vendor.duration[duration_idx]; |
| 326 if (duration <= 0) |
| 327 return 2 * 60 * HZ; |
| 328 else |
| 329 return duration; |
| 330 } |
| 331 EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); |
| 332 |
| 333 #define TPM_CMD_COUNT_BYTE 2 |
| 334 #define TPM_CMD_ORDINAL_BYTE 6 |
| 335 |
| 336 ssize_t tpm_transmit(const unsigned char *buf, size_t bufsiz) |
| 337 { |
| 338 ssize_t rc; |
| 339 u32 count, ordinal; |
| 340 unsigned long stop; |
| 341 |
| 342 struct tpm_chip *chip = &g_chip; |
| 343 |
| 344 /* switch endianess: big->little */ |
| 345 count = switch_endian32(&buf[TPM_CMD_COUNT_BYTE]); |
| 346 ordinal = switch_endian32(&buf[TPM_CMD_ORDINAL_BYTE]); |
| 347 |
| 348 if (count == 0) { |
| 349 dev_err(chip->dev, "no data\n"); |
| 350 return -ENODATA; |
| 351 } |
| 352 if (count > bufsiz) { |
| 353 dev_err(chip->dev, |
| 354 "invalid count value %x %zx\n", count, bufsiz); |
| 355 return -E2BIG; |
| 356 } |
| 357 |
| 358 rc = chip->vendor.send(chip, (u8 *) buf, count); |
| 359 if (rc < 0) { |
| 360 dev_err(chip->dev, "tpm_transmit: tpm_send: error %zd\n", rc); |
| 361 goto out; |
| 362 } |
| 363 |
| 364 if (chip->vendor.irq) |
| 365 goto out_recv; |
| 366 |
| 367 jiffies = 0; |
| 368 stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); |
| 369 do { |
| 370 dbg_printf("waiting for status...\n"); |
| 371 u8 status = chip->vendor.status(chip); |
| 372 if ((status & chip->vendor.req_complete_mask) == |
| 373 chip->vendor.req_complete_val) { |
| 374 dbg_printf("...got it;\n"); |
| 375 goto out_recv; |
| 376 } |
| 377 |
| 378 if ((status == chip->vendor.req_canceled)) { |
| 379 dev_err(chip->dev, "Operation Canceled\n"); |
| 380 rc = -ECANCELED; |
| 381 goto out; |
| 382 } |
| 383 |
| 384 msleep2(TPM_TIMEOUT); /* CHECK */ |
| 385 } while (time_before(jiffies, stop)); |
| 386 |
| 387 chip->vendor.cancel(chip); |
| 388 dev_err(chip->dev, "Operation Timed out\n"); |
| 389 rc = -ETIME; |
| 390 goto out; |
| 391 |
| 392 out_recv: |
| 393 |
| 394 dbg_printf("out_recv: reading response...\n"); |
| 395 rc = chip->vendor.recv(chip, (u8 *) buf, bufsiz); |
| 396 if (rc < 0) |
| 397 dev_err(chip->dev, "tpm_transmit: tpm_recv: error %zd\n", rc); |
| 398 out: |
| 399 return rc; |
| 400 } |
| 401 |
| 402 #define TPM_ERROR_SIZE 10 |
| 403 |
| 404 enum tpm_capabilities { |
| 405 TPM_CAP_PROP = cpu_to_be32(5), |
| 406 }; |
| 407 |
| 408 enum tpm_sub_capabilities { |
| 409 TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115), |
| 410 TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120), |
| 411 }; |
| 412 |
| 413 static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd, |
| 414 int len, const char *desc) |
| 415 { |
| 416 int err; |
| 417 |
| 418 len = tpm_transmit((u8 *) cmd, len); |
| 419 if (len < 0) |
| 420 return len; |
| 421 if (len == TPM_ERROR_SIZE) { |
| 422 err = be32_to_cpu(cmd->header.out.return_code); |
| 423 dev_dbg(chip->dev, "A TPM error (%d) occurred %s\n", err, desc); |
| 424 return err; |
| 425 } |
| 426 return 0; |
| 427 } |
| 428 |
| 429 #define TPM_INTERNAL_RESULT_SIZE 200 |
| 430 #define TPM_TAG_RQU_COMMAND cpu_to_be16(193) |
| 431 #define TPM_ORD_GET_CAP cpu_to_be32(101) |
| 432 |
| 433 static const struct tpm_input_header tpm_getcap_header = { |
| 434 .tag = TPM_TAG_RQU_COMMAND, |
| 435 .length = cpu_to_be32(22), |
| 436 .ordinal = TPM_ORD_GET_CAP |
| 437 }; |
| 438 |
| 439 void tpm_get_timeouts(struct tpm_chip *chip) |
| 440 { |
| 441 struct tpm_cmd_t tpm_cmd; |
| 442 struct timeout_t *timeout_cap; |
| 443 struct duration_t *duration_cap; |
| 444 ssize_t rc; |
| 445 u32 timeout; |
| 446 |
| 447 tpm_cmd.header.in = tpm_getcap_header; |
| 448 tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; |
| 449 tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); |
| 450 tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT; |
| 451 |
| 452 rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, |
| 453 "attempting to determine the timeouts"); |
| 454 if (rc) |
| 455 goto duration; |
| 456 |
| 457 if (be32_to_cpu(tpm_cmd.header.out.length) |
| 458 != 4 * sizeof(u32)) |
| 459 goto duration; |
| 460 |
| 461 timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout; |
| 462 /* Don't overwrite default if value is 0 */ |
| 463 timeout = be32_to_cpu(timeout_cap->a); |
| 464 if (timeout) |
| 465 chip->vendor.timeout_a = usecs_to_jiffies(timeout); |
| 466 timeout = be32_to_cpu(timeout_cap->b); |
| 467 if (timeout) |
| 468 chip->vendor.timeout_b = usecs_to_jiffies(timeout); |
| 469 timeout = be32_to_cpu(timeout_cap->c); |
| 470 if (timeout) |
| 471 chip->vendor.timeout_c = usecs_to_jiffies(timeout); |
| 472 timeout = be32_to_cpu(timeout_cap->d); |
| 473 if (timeout) |
| 474 chip->vendor.timeout_d = usecs_to_jiffies(timeout); |
| 475 |
| 476 duration: |
| 477 tpm_cmd.header.in = tpm_getcap_header; |
| 478 tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; |
| 479 tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); |
| 480 tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION; |
| 481 |
| 482 rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, |
| 483 "attempting to determine the durations"); |
| 484 if (rc) |
| 485 return; |
| 486 |
| 487 if (be32_to_cpu(tpm_cmd.header.out.return_code) |
| 488 != 3 * sizeof(u32)) |
| 489 return; |
| 490 duration_cap = &tpm_cmd.params.getcap_out.cap.duration; |
| 491 chip->vendor.duration[TPM_SHORT] = |
| 492 usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short)); |
| 493 /* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above |
| 494 * value wrong and apparently reports msecs rather than usecs. So we |
| 495 * fix up the resulting too-small TPM_SHORT value to make things work. |
| 496 */ |
| 497 if (chip->vendor.duration[TPM_SHORT] < (HZ / 100)) |
| 498 chip->vendor.duration[TPM_SHORT] = HZ; |
| 499 |
| 500 chip->vendor.duration[TPM_MEDIUM] = |
| 501 usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium)); |
| 502 chip->vendor.duration[TPM_LONG] = |
| 503 usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long)); |
| 504 } |
| 505 EXPORT_SYMBOL_GPL(tpm_get_timeouts); |
| 506 |
| 507 void tpm_continue_selftest(struct tpm_chip *chip) |
| 508 { |
| 509 u8 data[] = { |
| 510 0, 193, /* TPM_TAG_RQU_COMMAND */ |
| 511 0, 0, 0, 10, /* length */ |
| 512 0, 0, 0, 83, /* TPM_ORD_ContinueSelfTest */ |
| 513 }; |
| 514 |
| 515 tpm_transmit(data, sizeof(data)); |
| 516 } |
| 517 EXPORT_SYMBOL_GPL(tpm_continue_selftest); |
| 518 |
| 519 /* |
| 520 void tpm_startup(struct tpm_chip *chip) |
| 521 { |
| 522 u8 data[] = { |
| 523 0, 193, *//* TPM_TAG_RQU_COMMAND *//* |
| 524 0, 0, 0, 12, *//* length *//* |
| 525 0, 0, 0, 153, *//* TPM_ORD_Startup *//* |
| 526 0, 0 |
| 527 }; |
| 528 |
| 529 tpm_transmit(data, sizeof(data)); |
| 530 } |
| 531 */ |
| 532 EXPORT_SYMBOL_GPL(tpm_startup); |
| 533 |
| 534 struct tpm_chip *tpm_register_hardware(struct device *dev, |
| 535 const struct tpm_vendor_specific *entry) |
| 536 { |
| 537 struct tpm_chip *chip; |
| 538 |
| 539 /* Driver specific per-device data */ |
| 540 chip = &g_chip; |
| 541 memcpy(&chip->vendor, entry, sizeof(struct tpm_vendor_specific)); |
| 542 chip->is_open = 1; |
| 543 |
| 544 return chip; |
| 545 } |
| 546 EXPORT_SYMBOL_GPL(tpm_register_hardware); |
| 547 |
| 548 int tpm_open(void) |
| 549 { |
| 550 if (g_chip.is_open) |
| 551 return 0; |
| 552 else |
| 553 return tpm_tis_i2c_init(NULL); |
| 554 } |
| 555 |
| 556 void tpm_close(void) |
| 557 { |
| 558 if (g_chip.is_open) { |
| 559 tpm_tis_i2c_cleanup(&g_chip); |
| 560 g_chip.is_open = 0; |
| 561 } |
| 562 } |
OLD | NEW |