Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 1 | #include "private/dvr/revision.h" |
| 2 | |
| 3 | #include <errno.h> |
| 4 | #include <fcntl.h> |
| 5 | #include <stdlib.h> |
| 6 | #include <string.h> |
| 7 | #include <sys/mman.h> |
| 8 | #include <sys/stat.h> |
| 9 | #include <sys/types.h> |
| 10 | #include <unistd.h> |
| 11 | |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame^] | 12 | #include <log/log.h> |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 13 | |
| 14 | #include "revision_path.h" |
| 15 | |
| 16 | namespace { |
| 17 | |
| 18 | // Allows quicker access to the product revision. If non-zero, then |
| 19 | // the product revision file has already been processed. |
| 20 | static bool global_product_revision_processed = false; |
| 21 | |
| 22 | static bool global_serial_number_processed = false; |
| 23 | |
| 24 | // The product. |
| 25 | static DvrProduct global_product = DVR_PRODUCT_UNKNOWN; |
| 26 | |
| 27 | // The revision. |
| 28 | static DvrRevision global_revision = DVR_REVISION_UNKNOWN; |
| 29 | |
| 30 | // Maximum size of the product revision string. |
| 31 | constexpr int kProductRevisionStringSize = 32; |
| 32 | |
| 33 | // Maximum size of the serial number. |
| 34 | constexpr int kSerialNumberStringSize = 32; |
| 35 | |
| 36 | // The product revision string. |
| 37 | static char global_product_revision_str[kProductRevisionStringSize + 1] = ""; |
| 38 | |
| 39 | // The serial number string |
| 40 | static char global_serial_number[kSerialNumberStringSize + 1] = ""; |
| 41 | |
| 42 | // Product and revision combinations. |
| 43 | struct DvrProductRevision { |
| 44 | const char* str; |
| 45 | DvrProduct product; |
| 46 | DvrRevision revision; |
| 47 | }; |
| 48 | |
| 49 | // Null-terminated list of all product and revision combinations. |
| 50 | static constexpr DvrProductRevision kProductRevisions[] = { |
| 51 | {"a00-p1", DVR_PRODUCT_A00, DVR_REVISION_P1}, |
| 52 | {"a00-p2", DVR_PRODUCT_A00, DVR_REVISION_P2}, |
| 53 | {"a00-p3", DVR_PRODUCT_A00, DVR_REVISION_P3}, |
| 54 | {"twilight-p1", DVR_PRODUCT_A65R, DVR_REVISION_P1}, |
| 55 | {"twilight-p2", DVR_PRODUCT_A65R, DVR_REVISION_P2}, |
| 56 | {NULL, DVR_PRODUCT_UNKNOWN, DVR_REVISION_UNKNOWN}}; |
| 57 | |
| 58 | // Read the product revision string, and store the global data. |
| 59 | static void process_product_revision() { |
| 60 | int fd; |
| 61 | ssize_t read_rc; |
| 62 | const DvrProductRevision* product_revision = kProductRevisions; |
| 63 | |
| 64 | // Of course in a multi-threaded environment, for a few microseconds |
| 65 | // during process startup, it is possible that this function will be |
| 66 | // called and execute fully multiple times. That is why the product |
| 67 | // revision string is statically allocated. |
| 68 | |
| 69 | if (global_product_revision_processed) |
| 70 | return; |
| 71 | |
| 72 | // Whether there was a failure or not, we don't want to do this again. |
| 73 | // Upon failure it's most likely to fail again anyway. |
| 74 | |
| 75 | fd = open(dvr_product_revision_file_path(), O_RDONLY); |
| 76 | if (fd < 0) { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame^] | 77 | ALOGE("Could not open '%s' to get product revision: %s", |
| 78 | dvr_product_revision_file_path(), strerror(errno)); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 79 | global_product_revision_processed = true; |
| 80 | return; |
| 81 | } |
| 82 | |
| 83 | read_rc = read(fd, global_product_revision_str, kProductRevisionStringSize); |
| 84 | if (read_rc <= 0) { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame^] | 85 | ALOGE("Could not read from '%s': %s", dvr_product_revision_file_path(), |
| 86 | strerror(errno)); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 87 | global_product_revision_processed = true; |
| 88 | return; |
| 89 | } |
| 90 | |
| 91 | close(fd); |
| 92 | |
| 93 | global_product_revision_str[read_rc] = '\0'; |
| 94 | |
| 95 | while (product_revision->str) { |
| 96 | if (!strcmp(product_revision->str, global_product_revision_str)) |
| 97 | break; |
| 98 | product_revision++; |
| 99 | } |
| 100 | |
| 101 | if (product_revision->str) { |
| 102 | global_product = product_revision->product; |
| 103 | global_revision = product_revision->revision; |
| 104 | } else { |
Alex Vakulenko | 4fe6058 | 2017-02-02 11:35:59 -0800 | [diff] [blame^] | 105 | ALOGE("Unable to match '%s' to a product/revision.", |
| 106 | global_product_revision_str); |
Alex Vakulenko | e4eec20 | 2017-01-27 14:41:04 -0800 | [diff] [blame] | 107 | } |
| 108 | |
| 109 | global_product_revision_processed = true; |
| 110 | } |
| 111 | |
| 112 | } // anonymous namespace |
| 113 | |
| 114 | extern "C" DvrProduct dvr_get_product() { |
| 115 | process_product_revision(); |
| 116 | return global_product; |
| 117 | } |
| 118 | |
| 119 | extern "C" DvrRevision dvr_get_revision() { |
| 120 | process_product_revision(); |
| 121 | return global_revision; |
| 122 | } |
| 123 | |
| 124 | extern "C" const char* dvr_get_product_revision_str() { |
| 125 | process_product_revision(); |
| 126 | return global_product_revision_str; |
| 127 | } |
| 128 | |
| 129 | extern "C" const char* dvr_get_serial_number() { |
| 130 | process_product_revision(); |
| 131 | if (global_product == DVR_PRODUCT_A00) { |
| 132 | if (!global_serial_number_processed) { |
| 133 | #ifdef DVR_HOST |
| 134 | global_serial_number_processed = true; |
| 135 | #else |
| 136 | int width = 4; |
| 137 | uintptr_t addr = 0x00074138; |
| 138 | uintptr_t endaddr = addr + width - 1; |
| 139 | |
| 140 | int fd = open("/dev/mem", O_RDWR | O_SYNC); |
| 141 | if (fd < 0) { |
| 142 | if (errno == EPERM) |
| 143 | global_serial_number_processed = true; |
| 144 | fprintf(stderr, "cannot open /dev/mem\n"); |
| 145 | return global_serial_number; |
| 146 | } |
| 147 | |
| 148 | off64_t mmap_start = addr & ~(PAGE_SIZE - 1); |
| 149 | size_t mmap_size = endaddr - mmap_start + 1; |
| 150 | mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); |
| 151 | |
| 152 | void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, |
| 153 | mmap_start); |
| 154 | |
| 155 | if (page == MAP_FAILED) { |
| 156 | global_serial_number_processed = true; |
| 157 | fprintf(stderr, "cannot mmap region\n"); |
| 158 | close(fd); |
| 159 | return global_serial_number; |
| 160 | } |
| 161 | |
| 162 | uint32_t* x = |
| 163 | reinterpret_cast<uint32_t*>((((uintptr_t)page) + (addr & 4095))); |
| 164 | snprintf(global_serial_number, kSerialNumberStringSize, "%08x", *x); |
| 165 | global_serial_number_processed = true; |
| 166 | |
| 167 | munmap(page, mmap_size); |
| 168 | close(fd); |
| 169 | #endif |
| 170 | } |
| 171 | return global_serial_number; |
| 172 | } else { |
| 173 | return nullptr; |
| 174 | } |
| 175 | } |