rtl_eeprom.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. /*
  2. * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver
  3. * rtl_eeprom, EEPROM modification tool
  4. * Copyright (C) 2012 by Steve Markgraf <steve@steve-m.de>
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <string.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #ifndef _WIN32
  23. #include <unistd.h>
  24. #else
  25. #include <windows.h>
  26. #include "getopt/getopt.h"
  27. #endif
  28. #include "rtl-sdr.h"
  29. #define EEPROM_SIZE 256
  30. #define MAX_STR_SIZE 256
  31. #define STR_OFFSET 0x09
  32. static rtlsdr_dev_t *dev = NULL;
  33. typedef struct rtlsdr_config {
  34. uint16_t vendor_id;
  35. uint16_t product_id;
  36. char manufacturer[MAX_STR_SIZE];
  37. char product[MAX_STR_SIZE];
  38. char serial[MAX_STR_SIZE];
  39. int have_serial;
  40. int enable_ir;
  41. int remote_wakeup;
  42. } rtlsdr_config_t;
  43. void dump_config(rtlsdr_config_t *conf)
  44. {
  45. fprintf(stderr, "__________________________________________\n");
  46. fprintf(stderr, "Vendor ID:\t\t0x%04x\n", conf->vendor_id);
  47. fprintf(stderr, "Product ID:\t\t0x%04x\n", conf->product_id);
  48. fprintf(stderr, "Manufacturer:\t\t%s\n", conf->manufacturer);
  49. fprintf(stderr, "Product:\t\t%s\n", conf->product);
  50. fprintf(stderr, "Serial number:\t\t%s\n", conf->serial);
  51. fprintf(stderr, "Serial number enabled:\t");
  52. fprintf(stderr, conf->have_serial ? "yes\n": "no\n");
  53. fprintf(stderr, "IR endpoint enabled:\t");
  54. fprintf(stderr, conf->enable_ir ? "yes\n": "no\n");
  55. fprintf(stderr, "Remote wakeup enabled:\t");
  56. fprintf(stderr, conf->remote_wakeup ? "yes\n": "no\n");
  57. fprintf(stderr, "__________________________________________\n");
  58. }
  59. void usage(void)
  60. {
  61. fprintf(stderr,
  62. "rtl_eeprom, an EEPROM programming tool for "
  63. "RTL2832 based DVB-T receivers\n\n"
  64. "Usage:\n"
  65. "\t[-d device_index (default: 0)]\n"
  66. "\t[-m <str> set manufacturer string]\n"
  67. "\t[-p <str> set product string]\n"
  68. "\t[-s <str> set serial number string]\n"
  69. "\t[-i <0,1> disable/enable IR-endpoint]\n"
  70. "\t[-g <conf> generate default config and write to device]\n"
  71. "\t[ <conf> can be one of:]\n"
  72. "\t[ realtek\t\tRealtek default (as without EEPROM)]\n"
  73. "\t[ realtek_oem\t\tRealtek default OEM with EEPROM]\n"
  74. "\t[ noxon\t\tTerratec NOXON DAB Stick]\n"
  75. "\t[ terratec_black\tTerratec T Stick Black]\n"
  76. "\t[ terratec_plus\tTerratec T Stick+ (DVB-T/DAB)]\n"
  77. "\t[-w <filename> write dumped file to device]\n"
  78. "\t[-r <filename> dump EEPROM to file]\n"
  79. "\t[-h display this help text]\n"
  80. "\nUse on your own risk, especially -w!\n");
  81. exit(1);
  82. }
  83. int get_string_descriptor(int pos, uint8_t *data, char *str)
  84. {
  85. int len, i, j = 0;
  86. len = data[pos];
  87. if (data[pos + 1] != 0x03)
  88. fprintf(stderr, "Error: invalid string descriptor!\n");
  89. for (i = 2; i < len; i += 2)
  90. str[j++] = data[pos + i];
  91. str[j] = 0x00;
  92. return pos + i;
  93. }
  94. int set_string_descriptor(int pos, uint8_t *data, char *str)
  95. {
  96. int i = 0, j = 2;
  97. if (pos < 0)
  98. return -1;
  99. data[pos + 1] = 0x03;
  100. while (str[i] != 0x00) {
  101. if ((pos + j) >= 78) {
  102. fprintf(stderr, "Error: string too long, truncated!\n");
  103. return -1;
  104. }
  105. data[pos + j++] = str[i++];
  106. data[pos + j++] = 0x00;
  107. }
  108. data[pos] = j;
  109. return pos + j;
  110. }
  111. int parse_eeprom_to_conf(rtlsdr_config_t *conf, uint8_t *dat)
  112. {
  113. int pos;
  114. if ((dat[0] != 0x28) || (dat[1] != 0x32))
  115. fprintf(stderr, "Error: invalid RTL2832 EEPROM header!\n");
  116. conf->vendor_id = dat[2] | (dat[3] << 8);
  117. conf->product_id = dat[4] | (dat[5] << 8);
  118. conf->have_serial = (dat[6] == 0xa5) ? 1 : 0;
  119. conf->remote_wakeup = (dat[7] & 0x01) ? 1 : 0;
  120. conf->enable_ir = (dat[7] & 0x02) ? 1 : 0;
  121. pos = get_string_descriptor(STR_OFFSET, dat, conf->manufacturer);
  122. pos = get_string_descriptor(pos, dat, conf->product);
  123. get_string_descriptor(pos, dat, conf->serial);
  124. return 0;
  125. }
  126. int gen_eeprom_from_conf(rtlsdr_config_t *conf, uint8_t *dat)
  127. {
  128. int pos;
  129. dat[0] = 0x28;
  130. dat[1] = 0x32;
  131. dat[2] = conf->vendor_id & 0xff;
  132. dat[3] = (conf->vendor_id >> 8) & 0xff ;
  133. dat[4] = conf->product_id & 0xff;
  134. dat[5] = (conf->product_id >> 8) & 0xff;
  135. dat[6] = conf->have_serial ? 0xa5 : 0x00;
  136. dat[7] = 0x14;
  137. dat[7] |= conf->remote_wakeup ? 0x01 : 0x00;
  138. dat[7] |= conf->enable_ir ? 0x02 : 0x00;
  139. dat[8] = 0x02;
  140. pos = set_string_descriptor(STR_OFFSET, dat, conf->manufacturer);
  141. pos = set_string_descriptor(pos, dat, conf->product);
  142. pos = set_string_descriptor(pos, dat, conf->serial);
  143. dat[78] = 0x00; /* length of IR config */
  144. return pos;
  145. }
  146. enum configs {
  147. CONF_NONE = 0,
  148. REALTEK,
  149. REALTEK_EEPROM,
  150. TERRATEC_NOXON,
  151. TERRATEC_T_BLACK,
  152. TERRATEC_T_PLUS,
  153. };
  154. void gen_default_conf(rtlsdr_config_t *conf, int config)
  155. {
  156. switch (config) {
  157. case REALTEK:
  158. fprintf(stderr, "Realtek default (as without EEPROM)\n");
  159. conf->vendor_id = 0x0bda;
  160. conf->product_id = 0x2832;
  161. strcpy(conf->manufacturer, "Generic");
  162. strcpy(conf->product, "RTL2832U DVB-T");
  163. strcpy(conf->serial, "0");
  164. conf->have_serial = 1;
  165. conf->enable_ir = 0;
  166. conf->remote_wakeup = 1;
  167. break;
  168. case REALTEK_EEPROM:
  169. fprintf(stderr, "Realtek default OEM with EEPROM\n");
  170. conf->vendor_id = 0x0bda;
  171. conf->product_id = 0x2838;
  172. strcpy(conf->manufacturer, "Realtek");
  173. strcpy(conf->product, "RTL2838UHIDIR");
  174. strcpy(conf->serial, "00000001");
  175. conf->have_serial = 1;
  176. conf->enable_ir = 1;
  177. conf->remote_wakeup = 0;
  178. break;
  179. case TERRATEC_NOXON:
  180. fprintf(stderr, "Terratec NOXON DAB Stick\n");
  181. conf->vendor_id = 0x0ccd;
  182. conf->product_id = 0x00b3;
  183. strcpy(conf->manufacturer, "NOXON");
  184. strcpy(conf->product, "DAB Stick");
  185. strcpy(conf->serial, "0");
  186. conf->have_serial = 1;
  187. conf->enable_ir = 0;
  188. conf->remote_wakeup = 1;
  189. break;
  190. case TERRATEC_T_BLACK:
  191. fprintf(stderr, "Terratec T Stick Black\n");
  192. conf->vendor_id = 0x0ccd;
  193. conf->product_id = 0x00a9;
  194. strcpy(conf->manufacturer, "Realtek");
  195. strcpy(conf->product, "RTL2838UHIDIR");
  196. strcpy(conf->serial, "00000001");
  197. conf->have_serial = 1;
  198. conf->enable_ir = 1;
  199. conf->remote_wakeup = 0;
  200. break;
  201. case TERRATEC_T_PLUS:
  202. fprintf(stderr, "Terratec ran T Stick+\n");
  203. conf->vendor_id = 0x0ccd;
  204. conf->product_id = 0x00d7;
  205. strcpy(conf->manufacturer, "Realtek");
  206. strcpy(conf->product, "RTL2838UHIDIR");
  207. strcpy(conf->serial, "00000001");
  208. conf->have_serial = 1;
  209. conf->enable_ir = 1;
  210. conf->remote_wakeup = 0;
  211. break;
  212. default:
  213. break;
  214. };
  215. }
  216. int main(int argc, char **argv)
  217. {
  218. int i, r, opt;
  219. uint32_t dev_index = 0;
  220. int device_count;
  221. char *filename = NULL;
  222. FILE *file = NULL;
  223. char *manuf_str = NULL;
  224. char *product_str = NULL;
  225. char *serial_str = NULL;
  226. uint8_t buf[EEPROM_SIZE];
  227. rtlsdr_config_t conf;
  228. int flash_file = 0;
  229. int default_config = 0;
  230. int change = 0;
  231. int ir_endpoint = 0;
  232. char ch;
  233. while ((opt = getopt(argc, argv, "d:m:p:s:i:g:w:r:h?")) != -1) {
  234. switch (opt) {
  235. case 'd':
  236. dev_index = atoi(optarg);
  237. break;
  238. case 'm':
  239. manuf_str = optarg;
  240. change = 1;
  241. break;
  242. case 'p':
  243. product_str = optarg;
  244. change = 1;
  245. break;
  246. case 's':
  247. serial_str = optarg;
  248. change = 1;
  249. break;
  250. case 'i':
  251. ir_endpoint = (atoi(optarg) > 0) ? 1 : -1;
  252. change = 1;
  253. break;
  254. case 'g':
  255. if (!strcmp(optarg, "realtek"))
  256. default_config = REALTEK;
  257. else if (!strcmp(optarg, "realtek_oem"))
  258. default_config = REALTEK_EEPROM;
  259. else if (!strcmp(optarg, "noxon"))
  260. default_config = TERRATEC_NOXON;
  261. else if (!strcmp(optarg, "terratec_black"))
  262. default_config = TERRATEC_T_BLACK;
  263. else if (!strcmp(optarg, "terratec_plus"))
  264. default_config = TERRATEC_T_PLUS;
  265. if (default_config != CONF_NONE)
  266. change = 1;
  267. break;
  268. case 'w':
  269. flash_file = 1;
  270. change = 1;
  271. case 'r':
  272. filename = optarg;
  273. break;
  274. default:
  275. usage();
  276. break;
  277. }
  278. }
  279. device_count = rtlsdr_get_device_count();
  280. if (!device_count) {
  281. fprintf(stderr, "No supported devices found.\n");
  282. exit(1);
  283. }
  284. fprintf(stderr, "Found %d device(s):\n", device_count);
  285. for (i = 0; i < device_count; i++)
  286. fprintf(stderr, " %d: %s\n", i, rtlsdr_get_device_name(i));
  287. fprintf(stderr, "\n");
  288. fprintf(stderr, "Using device %d: %s\n",
  289. dev_index,
  290. rtlsdr_get_device_name(dev_index));
  291. r = rtlsdr_open(&dev, dev_index);
  292. if (r < 0) {
  293. fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index);
  294. exit(1);
  295. }
  296. fprintf(stderr, "\n");
  297. r = rtlsdr_read_eeprom(dev, buf, 0, EEPROM_SIZE);
  298. if (r < 0) {
  299. if (r == -3)
  300. fprintf(stderr, "No EEPROM has been found.\n");
  301. else
  302. fprintf(stderr, "Failed to read EEPROM, err %i.\n", r);
  303. goto exit;
  304. }
  305. if (r < 0)
  306. return -1;
  307. fprintf(stderr, "Current configuration:\n");
  308. parse_eeprom_to_conf(&conf, buf);
  309. dump_config(&conf);
  310. if (filename) {
  311. file = fopen(filename, flash_file ? "rb" : "wb");
  312. if (!file) {
  313. fprintf(stderr, "Error opening file!\n");
  314. goto exit;
  315. }
  316. if (flash_file) {
  317. if (fread(buf, 1, sizeof(buf), file) != sizeof(buf))
  318. fprintf(stderr, "Error reading file!\n");
  319. } else {
  320. if (fwrite(buf, 1, sizeof(buf), file) != sizeof(buf))
  321. fprintf(stderr, "Short write, exiting!\n");
  322. else
  323. fprintf(stderr, "\nDump to %s successful.\n", filename);
  324. }
  325. }
  326. if (manuf_str)
  327. strncpy((char*)&conf.manufacturer, manuf_str, MAX_STR_SIZE);
  328. if (product_str)
  329. strncpy((char*)&conf.product, product_str, MAX_STR_SIZE);
  330. if (serial_str) {
  331. conf.have_serial = 1;
  332. strncpy((char*)&conf.serial, serial_str, MAX_STR_SIZE);
  333. }
  334. if (ir_endpoint != 0)
  335. conf.enable_ir = (ir_endpoint > 0) ? 1 : 0;
  336. if (!change)
  337. goto exit;
  338. fprintf(stderr, "\nNew configuration:\n");
  339. if (default_config != CONF_NONE)
  340. gen_default_conf(&conf, default_config);
  341. if (!flash_file) {
  342. if (gen_eeprom_from_conf(&conf, buf) < 0)
  343. goto exit;
  344. }
  345. parse_eeprom_to_conf(&conf, buf);
  346. dump_config(&conf);
  347. fprintf(stderr, "Write new configuration to device [y/n]? ");
  348. while ((ch = getchar())) {
  349. if (ch != 'y')
  350. goto exit;
  351. else
  352. break;
  353. }
  354. r = rtlsdr_write_eeprom(dev, buf, 0, flash_file ? EEPROM_SIZE : 128);
  355. if (r < 0)
  356. fprintf(stderr, "Error while writing EEPROM: %i\n", r);
  357. else
  358. fprintf(stderr, "Configuration successfully written.\n");
  359. exit:
  360. if (file)
  361. fclose(file);
  362. rtlsdr_close(dev);
  363. return r >= 0 ? r : -r;
  364. }