blob: ae8603fa991e02560a2b0f050b6c00b3ee5992db [file] [log] [blame]
Alex Vakulenkoe4eec202017-01-27 14:41:04 -08001#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
12#include <base/logging.h>
13
14#include "revision_path.h"
15
16namespace {
17
18// Allows quicker access to the product revision. If non-zero, then
19// the product revision file has already been processed.
20static bool global_product_revision_processed = false;
21
22static bool global_serial_number_processed = false;
23
24// The product.
25static DvrProduct global_product = DVR_PRODUCT_UNKNOWN;
26
27// The revision.
28static DvrRevision global_revision = DVR_REVISION_UNKNOWN;
29
30// Maximum size of the product revision string.
31constexpr int kProductRevisionStringSize = 32;
32
33// Maximum size of the serial number.
34constexpr int kSerialNumberStringSize = 32;
35
36// The product revision string.
37static char global_product_revision_str[kProductRevisionStringSize + 1] = "";
38
39// The serial number string
40static char global_serial_number[kSerialNumberStringSize + 1] = "";
41
42// Product and revision combinations.
43struct DvrProductRevision {
44 const char* str;
45 DvrProduct product;
46 DvrRevision revision;
47};
48
49// Null-terminated list of all product and revision combinations.
50static 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.
59static 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) {
77 PLOG(ERROR) << "Could not open '" << dvr_product_revision_file_path()
78 << "' to get product revision";
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) {
85 PLOG(ERROR) << "Could not read from '" << dvr_product_revision_file_path()
86 << "'";
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 {
105 LOG(ERROR) << "Unable to match '" << global_product_revision_str
106 << "' to a product/revision.";
107 }
108
109 global_product_revision_processed = true;
110}
111
112} // anonymous namespace
113
114extern "C" DvrProduct dvr_get_product() {
115 process_product_revision();
116 return global_product;
117}
118
119extern "C" DvrRevision dvr_get_revision() {
120 process_product_revision();
121 return global_revision;
122}
123
124extern "C" const char* dvr_get_product_revision_str() {
125 process_product_revision();
126 return global_product_revision_str;
127}
128
129extern "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}