teensy_loader_cli.c 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183
  1. /* Teensy Loader, Command Line Interface
  2. * Program and Reboot Teensy Board with HalfKay Bootloader
  3. * http://www.pjrc.com/teensy/loader_cli.html
  4. * Copyright 2008-2016, PJRC.COM, LLC
  5. *
  6. * You may redistribute this program and/or modify it under the terms
  7. * of the GNU General Public License as published by the Free Software
  8. * Foundation, version 3 of the License.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see http://www.gnu.org/licenses/
  17. */
  18. /* Want to incorporate this code into a proprietary application??
  19. * Just email paul@pjrc.com to ask. Usually it's not a problem,
  20. * but you do need to ask to use this code in any way other than
  21. * those permitted by the GNU General Public License, version 3 */
  22. /* For non-root permissions on ubuntu or similar udev-based linux
  23. * http://www.pjrc.com/teensy/49-teensy.rules
  24. */
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <stdint.h>
  28. #include <stdarg.h>
  29. #include <string.h>
  30. #include <unistd.h>
  31. void usage(const char *err)
  32. {
  33. if(err != NULL) fprintf(stderr, "%s\n\n", err);
  34. fprintf(stderr,
  35. "Usage: teensy_loader_cli --mcu=<MCU> [-w] [-h] [-n] [-b] [-v] <file.hex>\n"
  36. "\t-w : Wait for device to appear\n"
  37. "\t-r : Use hard reboot if device not online\n"
  38. "\t-s : Use soft reboot if device not online (Teensy3.x only)\n"
  39. "\t-n : No reboot after programming\n"
  40. "\t-b : Boot only, do not program\n"
  41. "\t-v : Verbose output\n"
  42. "\nUse `teensy_loader_cli --list-mcus` to list supported MCUs.\n"
  43. "\nFor more information, please visit:\n"
  44. "http://www.pjrc.com/teensy/loader_cli.html\n");
  45. exit(1);
  46. }
  47. // USB Access Functions
  48. int teensy_open(void);
  49. int teensy_write(void *buf, int len, double timeout);
  50. void teensy_close(void);
  51. int hard_reboot(void);
  52. int soft_reboot(void);
  53. // Intel Hex File Functions
  54. int read_intel_hex(const char *filename);
  55. int ihex_bytes_within_range(int begin, int end);
  56. void ihex_get_data(int addr, int len, unsigned char *bytes);
  57. int memory_is_blank(int addr, int block_size);
  58. // Misc stuff
  59. int printf_verbose(const char *format, ...);
  60. void delay(double seconds);
  61. void die(const char *str, ...);
  62. void parse_options(int argc, char **argv);
  63. void boot(unsigned char *buf, int write_size);
  64. // options (from user via command line args)
  65. int wait_for_device_to_appear = 0;
  66. int hard_reboot_device = 0;
  67. int soft_reboot_device = 0;
  68. int reboot_after_programming = 1;
  69. int verbose = 0;
  70. int boot_only = 0;
  71. int code_size = 0, block_size = 0;
  72. const char *filename=NULL;
  73. /****************************************************************/
  74. /* */
  75. /* Main Program */
  76. /* */
  77. /****************************************************************/
  78. int main(int argc, char **argv)
  79. {
  80. unsigned char buf[2048];
  81. int num, addr, r, write_size;
  82. int first_block=1, waited=0;
  83. // parse command line arguments
  84. parse_options(argc, argv);
  85. if (!filename && !boot_only) {
  86. usage("Filename must be specified");
  87. }
  88. if (!code_size) {
  89. usage("MCU type must be specified");
  90. }
  91. printf_verbose("Teensy Loader, Command Line, Version 2.1\n");
  92. if (block_size == 512 || block_size == 1024) {
  93. write_size = block_size + 64;
  94. } else {
  95. write_size = block_size + 2;
  96. };
  97. if (boot_only) {
  98. if (! teensy_open()) {
  99. die("Could not find HalfKay");
  100. }
  101. printf_verbose("Found HalfKay Bootloader\n");
  102. boot(buf, write_size);
  103. exit(0);
  104. }
  105. // read the intel hex file
  106. // this is done first so any error is reported before using USB
  107. num = read_intel_hex(filename);
  108. if (num < 0) die("error reading intel hex file \"%s\"", filename);
  109. printf_verbose("Read \"%s\": %d bytes, %.1f%% usage\n",
  110. filename, num, (double)num / (double)code_size * 100.0);
  111. // open the USB device
  112. while (1) {
  113. if (teensy_open()) break;
  114. if (hard_reboot_device) {
  115. if (!hard_reboot()) die("Unable to find rebootor\n");
  116. printf_verbose("Hard Reboot performed\n");
  117. hard_reboot_device = 0; // only hard reboot once
  118. wait_for_device_to_appear = 1;
  119. }
  120. if (soft_reboot_device) {
  121. if (soft_reboot()) {
  122. printf_verbose("Soft reboot performed\n");
  123. }
  124. soft_reboot_device = 0;
  125. wait_for_device_to_appear = 1;
  126. }
  127. if (!wait_for_device_to_appear) die("Unable to open device\n");
  128. if (!waited) {
  129. printf_verbose("Waiting for Teensy device...\n");
  130. printf_verbose(" (hint: press the reset button)\n");
  131. waited = 1;
  132. }
  133. delay(0.25);
  134. }
  135. printf_verbose("Found HalfKay Bootloader\n");
  136. // if we waited for the device, read the hex file again
  137. // perhaps it changed while we were waiting?
  138. if (waited) {
  139. num = read_intel_hex(filename);
  140. if (num < 0) die("error reading intel hex file \"%s\"", filename);
  141. printf_verbose("Read \"%s\": %d bytes, %.1f%% usage\n",
  142. filename, num, (double)num / (double)code_size * 100.0);
  143. }
  144. // program the data
  145. printf_verbose("Programming");
  146. fflush(stdout);
  147. for (addr = 0; addr < code_size; addr += block_size) {
  148. if (!first_block && !ihex_bytes_within_range(addr, addr + block_size - 1)) {
  149. // don't waste time on blocks that are unused,
  150. // but always do the first one to erase the chip
  151. continue;
  152. }
  153. if (!first_block && memory_is_blank(addr, block_size)) continue;
  154. printf_verbose(".");
  155. if (block_size <= 256 && code_size < 0x10000) {
  156. buf[0] = addr & 255;
  157. buf[1] = (addr >> 8) & 255;
  158. ihex_get_data(addr, block_size, buf + 2);
  159. write_size = block_size + 2;
  160. } else if (block_size == 256) {
  161. buf[0] = (addr >> 8) & 255;
  162. buf[1] = (addr >> 16) & 255;
  163. ihex_get_data(addr, block_size, buf + 2);
  164. write_size = block_size + 2;
  165. } else if (block_size == 512 || block_size == 1024) {
  166. buf[0] = addr & 255;
  167. buf[1] = (addr >> 8) & 255;
  168. buf[2] = (addr >> 16) & 255;
  169. memset(buf + 3, 0, 61);
  170. ihex_get_data(addr, block_size, buf + 64);
  171. write_size = block_size + 64;
  172. } else {
  173. die("Unknown code/block size\n");
  174. }
  175. r = teensy_write(buf, write_size, first_block ? 5.0 : 0.5);
  176. if (!r) die("error writing to Teensy\n");
  177. first_block = 0;
  178. }
  179. printf_verbose("\n");
  180. // reboot to the user's new code
  181. if (reboot_after_programming) {
  182. boot(buf, write_size);
  183. }
  184. teensy_close();
  185. return 0;
  186. }
  187. /****************************************************************/
  188. /* */
  189. /* USB Access - libusb (Linux & FreeBSD) */
  190. /* */
  191. /* Uses libusb v0.1. To install: */
  192. /* - [debian, ubuntu, mint] apt install libusb-dev */
  193. /* - [redhat, centos] yum install libusb-devel */
  194. /* - [fedora] dnf install libusb-devel */
  195. /* - [arch linux] pacman -S libusb-compat */
  196. /* - [gentoo] emerge dev-libs/libusb-compat */
  197. /* */
  198. /* - [freebsd] seems to be preinstalled */
  199. /****************************************************************/
  200. #if defined(USE_LIBUSB)
  201. // http://libusb.sourceforge.net/doc/index.html
  202. #include <usb.h>
  203. usb_dev_handle * open_usb_device(int vid, int pid)
  204. {
  205. struct usb_bus *bus;
  206. struct usb_device *dev;
  207. usb_dev_handle *h;
  208. char buf[128];
  209. int r;
  210. usb_init();
  211. usb_find_busses();
  212. usb_find_devices();
  213. //printf_verbose("\nSearching for USB device:\n");
  214. for (bus = usb_get_busses(); bus; bus = bus->next) {
  215. for (dev = bus->devices; dev; dev = dev->next) {
  216. //printf_verbose("bus \"%s\", device \"%s\" vid=%04X, pid=%04X\n",
  217. // bus->dirname, dev->filename,
  218. // dev->descriptor.idVendor,
  219. // dev->descriptor.idProduct
  220. //);
  221. if (dev->descriptor.idVendor != vid) continue;
  222. if (dev->descriptor.idProduct != pid) continue;
  223. h = usb_open(dev);
  224. if (!h) {
  225. printf_verbose("Found device but unable to open\n");
  226. continue;
  227. }
  228. #ifdef LIBUSB_HAS_GET_DRIVER_NP
  229. r = usb_get_driver_np(h, 0, buf, sizeof(buf));
  230. if (r >= 0) {
  231. r = usb_detach_kernel_driver_np(h, 0);
  232. if (r < 0) {
  233. usb_close(h);
  234. printf_verbose("Device is in use by \"%s\" driver\n", buf);
  235. continue;
  236. }
  237. }
  238. #endif
  239. // Mac OS-X - removing this call to usb_claim_interface() might allow
  240. // this to work, even though it is a clear misuse of the libusb API.
  241. // normally Apple's IOKit should be used on Mac OS-X
  242. #if !defined(MACOSX)
  243. r = usb_claim_interface(h, 0);
  244. if (r < 0) {
  245. usb_close(h);
  246. printf_verbose("Unable to claim interface, check USB permissions\n");
  247. continue;
  248. }
  249. #endif
  250. return h;
  251. }
  252. }
  253. return NULL;
  254. }
  255. static usb_dev_handle *libusb_teensy_handle = NULL;
  256. int teensy_open(void)
  257. {
  258. teensy_close();
  259. libusb_teensy_handle = open_usb_device(0x16C0, 0x0478);
  260. if (libusb_teensy_handle) return 1;
  261. return 0;
  262. }
  263. int teensy_write(void *buf, int len, double timeout)
  264. {
  265. int r;
  266. if (!libusb_teensy_handle) return 0;
  267. while (timeout > 0) {
  268. r = usb_control_msg(libusb_teensy_handle, 0x21, 9, 0x0200, 0,
  269. (char *)buf, len, (int)(timeout * 1000.0));
  270. if (r >= 0) return 1;
  271. //printf("teensy_write, r=%d\n", r);
  272. usleep(10000);
  273. timeout -= 0.01; // TODO: subtract actual elapsed time
  274. }
  275. return 0;
  276. }
  277. void teensy_close(void)
  278. {
  279. if (!libusb_teensy_handle) return;
  280. usb_release_interface(libusb_teensy_handle, 0);
  281. usb_close(libusb_teensy_handle);
  282. libusb_teensy_handle = NULL;
  283. }
  284. int hard_reboot(void)
  285. {
  286. usb_dev_handle *rebootor;
  287. int r;
  288. rebootor = open_usb_device(0x16C0, 0x0477);
  289. if (!rebootor) return 0;
  290. r = usb_control_msg(rebootor, 0x21, 9, 0x0200, 0, "reboot", 6, 100);
  291. usb_release_interface(rebootor, 0);
  292. usb_close(rebootor);
  293. if (r < 0) return 0;
  294. return 1;
  295. }
  296. int soft_reboot(void)
  297. {
  298. usb_dev_handle *serial_handle = NULL;
  299. serial_handle = open_usb_device(0x16C0, 0x0483);
  300. if (!serial_handle) {
  301. char *error = usb_strerror();
  302. printf("Error opening USB device: %s\n", error);
  303. return 0;
  304. }
  305. char reboot_command = 134;
  306. int response = usb_control_msg(serial_handle, 0x21, 0x20, 0, 0, &reboot_command, 1, 10000);
  307. usb_release_interface(serial_handle, 0);
  308. usb_close(serial_handle);
  309. if (response < 0) {
  310. char *error = usb_strerror();
  311. printf("Unable to soft reboot with USB error: %s\n", error);
  312. return 0;
  313. }
  314. return 1;
  315. }
  316. #endif
  317. /****************************************************************/
  318. /* */
  319. /* USB Access - Microsoft WIN32 */
  320. /* */
  321. /****************************************************************/
  322. #if defined(USE_WIN32)
  323. // http://msdn.microsoft.com/en-us/library/ms790932.aspx
  324. #include <windows.h>
  325. #include <setupapi.h>
  326. #include <ddk/hidsdi.h>
  327. #include <ddk/hidclass.h>
  328. HANDLE open_usb_device(int vid, int pid)
  329. {
  330. GUID guid;
  331. HDEVINFO info;
  332. DWORD index, required_size;
  333. SP_DEVICE_INTERFACE_DATA iface;
  334. SP_DEVICE_INTERFACE_DETAIL_DATA *details;
  335. HIDD_ATTRIBUTES attrib;
  336. HANDLE h;
  337. BOOL ret;
  338. HidD_GetHidGuid(&guid);
  339. info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
  340. if (info == INVALID_HANDLE_VALUE) return NULL;
  341. for (index=0; 1 ;index++) {
  342. iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
  343. ret = SetupDiEnumDeviceInterfaces(info, NULL, &guid, index, &iface);
  344. if (!ret) {
  345. SetupDiDestroyDeviceInfoList(info);
  346. break;
  347. }
  348. SetupDiGetInterfaceDeviceDetail(info, &iface, NULL, 0, &required_size, NULL);
  349. details = (SP_DEVICE_INTERFACE_DETAIL_DATA *)malloc(required_size);
  350. if (details == NULL) continue;
  351. memset(details, 0, required_size);
  352. details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
  353. ret = SetupDiGetDeviceInterfaceDetail(info, &iface, details,
  354. required_size, NULL, NULL);
  355. if (!ret) {
  356. free(details);
  357. continue;
  358. }
  359. h = CreateFile(details->DevicePath, GENERIC_READ|GENERIC_WRITE,
  360. FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
  361. FILE_FLAG_OVERLAPPED, NULL);
  362. free(details);
  363. if (h == INVALID_HANDLE_VALUE) continue;
  364. attrib.Size = sizeof(HIDD_ATTRIBUTES);
  365. ret = HidD_GetAttributes(h, &attrib);
  366. if (!ret) {
  367. CloseHandle(h);
  368. continue;
  369. }
  370. if (attrib.VendorID != vid || attrib.ProductID != pid) {
  371. CloseHandle(h);
  372. continue;
  373. }
  374. SetupDiDestroyDeviceInfoList(info);
  375. return h;
  376. }
  377. return NULL;
  378. }
  379. int write_usb_device(HANDLE h, void *buf, int len, int timeout)
  380. {
  381. static HANDLE event = NULL;
  382. unsigned char tmpbuf[1089];
  383. OVERLAPPED ov;
  384. DWORD n, r;
  385. if (len > sizeof(tmpbuf) - 1) return 0;
  386. if (event == NULL) {
  387. event = CreateEvent(NULL, TRUE, TRUE, NULL);
  388. if (!event) return 0;
  389. }
  390. ResetEvent(&event);
  391. memset(&ov, 0, sizeof(ov));
  392. ov.hEvent = event;
  393. tmpbuf[0] = 0;
  394. memcpy(tmpbuf + 1, buf, len);
  395. if (!WriteFile(h, tmpbuf, len + 1, NULL, &ov)) {
  396. if (GetLastError() != ERROR_IO_PENDING) return 0;
  397. r = WaitForSingleObject(event, timeout);
  398. if (r == WAIT_TIMEOUT) {
  399. CancelIo(h);
  400. return 0;
  401. }
  402. if (r != WAIT_OBJECT_0) return 0;
  403. }
  404. if (!GetOverlappedResult(h, &ov, &n, FALSE)) return 0;
  405. if (n <= 0) return 0;
  406. return 1;
  407. }
  408. void print_win32_err(void)
  409. {
  410. char buf[256];
  411. DWORD err;
  412. err = GetLastError();
  413. FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err,
  414. 0, buf, sizeof(buf), NULL);
  415. printf("err %ld: %s\n", err, buf);
  416. }
  417. static HANDLE win32_teensy_handle = NULL;
  418. int teensy_open(void)
  419. {
  420. teensy_close();
  421. win32_teensy_handle = open_usb_device(0x16C0, 0x0478);
  422. if (win32_teensy_handle) return 1;
  423. return 0;
  424. }
  425. int teensy_write(void *buf, int len, double timeout)
  426. {
  427. int r;
  428. uint32_t begin, now, total;
  429. if (!win32_teensy_handle) return 0;
  430. total = (uint32_t)(timeout * 1000.0);
  431. begin = timeGetTime();
  432. now = begin;
  433. do {
  434. r = write_usb_device(win32_teensy_handle, buf, len, total - (now - begin));
  435. if (r > 0) return 1;
  436. Sleep(10);
  437. now = timeGetTime();
  438. } while (now - begin < total);
  439. return 0;
  440. }
  441. void teensy_close(void)
  442. {
  443. if (!win32_teensy_handle) return;
  444. CloseHandle(win32_teensy_handle);
  445. win32_teensy_handle = NULL;
  446. }
  447. int hard_reboot(void)
  448. {
  449. HANDLE rebootor;
  450. int r;
  451. rebootor = open_usb_device(0x16C0, 0x0477);
  452. if (!rebootor) return 0;
  453. r = write_usb_device(rebootor, "reboot", 6, 100);
  454. CloseHandle(rebootor);
  455. return r;
  456. }
  457. int soft_reboot(void)
  458. {
  459. printf("Soft reboot is not implemented for Win32\n");
  460. return 0;
  461. }
  462. #endif
  463. /****************************************************************/
  464. /* */
  465. /* USB Access - Apple's IOKit, Mac OS-X */
  466. /* */
  467. /****************************************************************/
  468. #if defined(USE_APPLE_IOKIT)
  469. // http://developer.apple.com/technotes/tn2007/tn2187.html
  470. #include <IOKit/IOKitLib.h>
  471. #include <IOKit/hid/IOHIDLib.h>
  472. #include <IOKit/hid/IOHIDDevice.h>
  473. struct usb_list_struct {
  474. IOHIDDeviceRef ref;
  475. int pid;
  476. int vid;
  477. struct usb_list_struct *next;
  478. };
  479. static struct usb_list_struct *usb_list=NULL;
  480. static IOHIDManagerRef hid_manager=NULL;
  481. void attach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev)
  482. {
  483. CFTypeRef type;
  484. struct usb_list_struct *n, *p;
  485. int32_t pid, vid;
  486. if (!dev) return;
  487. type = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDVendorIDKey));
  488. if (!type || CFGetTypeID(type) != CFNumberGetTypeID()) return;
  489. if (!CFNumberGetValue((CFNumberRef)type, kCFNumberSInt32Type, &vid)) return;
  490. type = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDProductIDKey));
  491. if (!type || CFGetTypeID(type) != CFNumberGetTypeID()) return;
  492. if (!CFNumberGetValue((CFNumberRef)type, kCFNumberSInt32Type, &pid)) return;
  493. n = (struct usb_list_struct *)malloc(sizeof(struct usb_list_struct));
  494. if (!n) return;
  495. //printf("attach callback: vid=%04X, pid=%04X\n", vid, pid);
  496. n->ref = dev;
  497. n->vid = vid;
  498. n->pid = pid;
  499. n->next = NULL;
  500. if (usb_list == NULL) {
  501. usb_list = n;
  502. } else {
  503. for (p = usb_list; p->next; p = p->next) ;
  504. p->next = n;
  505. }
  506. }
  507. void detach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev)
  508. {
  509. struct usb_list_struct *p, *tmp, *prev=NULL;
  510. p = usb_list;
  511. while (p) {
  512. if (p->ref == dev) {
  513. if (prev) {
  514. prev->next = p->next;
  515. } else {
  516. usb_list = p->next;
  517. }
  518. tmp = p;
  519. p = p->next;
  520. free(tmp);
  521. } else {
  522. prev = p;
  523. p = p->next;
  524. }
  525. }
  526. }
  527. void init_hid_manager(void)
  528. {
  529. CFMutableDictionaryRef dict;
  530. IOReturn ret;
  531. if (hid_manager) return;
  532. hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
  533. if (hid_manager == NULL || CFGetTypeID(hid_manager) != IOHIDManagerGetTypeID()) {
  534. if (hid_manager) CFRelease(hid_manager);
  535. printf_verbose("no HID Manager - maybe this is a pre-Leopard (10.5) system?\n");
  536. return;
  537. }
  538. dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
  539. &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  540. if (!dict) return;
  541. IOHIDManagerSetDeviceMatching(hid_manager, dict);
  542. CFRelease(dict);
  543. IOHIDManagerScheduleWithRunLoop(hid_manager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  544. IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, attach_callback, NULL);
  545. IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, detach_callback, NULL);
  546. ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone);
  547. if (ret != kIOReturnSuccess) {
  548. IOHIDManagerUnscheduleFromRunLoop(hid_manager,
  549. CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
  550. CFRelease(hid_manager);
  551. printf_verbose("Error opening HID Manager\n");
  552. }
  553. }
  554. static void do_run_loop(void)
  555. {
  556. while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == kCFRunLoopRunHandledSource) ;
  557. }
  558. IOHIDDeviceRef open_usb_device(int vid, int pid)
  559. {
  560. struct usb_list_struct *p;
  561. IOReturn ret;
  562. init_hid_manager();
  563. do_run_loop();
  564. for (p = usb_list; p; p = p->next) {
  565. if (p->vid == vid && p->pid == pid) {
  566. ret = IOHIDDeviceOpen(p->ref, kIOHIDOptionsTypeNone);
  567. if (ret == kIOReturnSuccess) return p->ref;
  568. }
  569. }
  570. return NULL;
  571. }
  572. void close_usb_device(IOHIDDeviceRef dev)
  573. {
  574. struct usb_list_struct *p;
  575. do_run_loop();
  576. for (p = usb_list; p; p = p->next) {
  577. if (p->ref == dev) {
  578. IOHIDDeviceClose(dev, kIOHIDOptionsTypeNone);
  579. return;
  580. }
  581. }
  582. }
  583. static IOHIDDeviceRef iokit_teensy_reference = NULL;
  584. int teensy_open(void)
  585. {
  586. teensy_close();
  587. iokit_teensy_reference = open_usb_device(0x16C0, 0x0478);
  588. if (iokit_teensy_reference) return 1;
  589. return 0;
  590. }
  591. int teensy_write(void *buf, int len, double timeout)
  592. {
  593. IOReturn ret;
  594. // timeouts do not work on OS-X
  595. // IOHIDDeviceSetReportWithCallback is not implemented
  596. // even though Apple documents it with a code example!
  597. // submitted to Apple on 22-sep-2009, problem ID 7245050
  598. if (!iokit_teensy_reference) return 0;
  599. double start = CFAbsoluteTimeGetCurrent();
  600. while (CFAbsoluteTimeGetCurrent() - timeout < start) {
  601. ret = IOHIDDeviceSetReport(iokit_teensy_reference,
  602. kIOHIDReportTypeOutput, 0, buf, len);
  603. if (ret == kIOReturnSuccess) return 1;
  604. usleep(10000);
  605. }
  606. return 0;
  607. }
  608. void teensy_close(void)
  609. {
  610. if (!iokit_teensy_reference) return;
  611. close_usb_device(iokit_teensy_reference);
  612. iokit_teensy_reference = NULL;
  613. }
  614. int hard_reboot(void)
  615. {
  616. IOHIDDeviceRef rebootor;
  617. IOReturn ret;
  618. rebootor = open_usb_device(0x16C0, 0x0477);
  619. if (!rebootor) return 0;
  620. ret = IOHIDDeviceSetReport(rebootor,
  621. kIOHIDReportTypeOutput, 0, (uint8_t *)("reboot"), 6);
  622. close_usb_device(rebootor);
  623. if (ret == kIOReturnSuccess) return 1;
  624. return 0;
  625. }
  626. int soft_reboot(void)
  627. {
  628. printf("Soft reboot is not implemented for OSX\n");
  629. return 0;
  630. }
  631. #endif
  632. /****************************************************************/
  633. /* */
  634. /* USB Access - BSD's UHID driver */
  635. /* */
  636. /****************************************************************/
  637. #if defined(USE_UHID)
  638. // Thanks to Todd T Fries for help getting this working on OpenBSD
  639. // and to Chris Kuethe for the initial patch to use UHID.
  640. #include <sys/ioctl.h>
  641. #include <fcntl.h>
  642. #include <dirent.h>
  643. #include <dev/usb/usb.h>
  644. #ifndef USB_GET_DEVICEINFO
  645. #include <dev/usb/usb_ioctl.h>
  646. #endif
  647. int open_usb_device(int vid, int pid)
  648. {
  649. int r, fd;
  650. DIR *dir;
  651. struct dirent *d;
  652. struct usb_device_info info;
  653. char buf[256];
  654. dir = opendir("/dev");
  655. if (!dir) return -1;
  656. while ((d = readdir(dir)) != NULL) {
  657. if (strncmp(d->d_name, "uhid", 4) != 0) continue;
  658. snprintf(buf, sizeof(buf), "/dev/%s", d->d_name);
  659. fd = open(buf, O_RDWR);
  660. if (fd < 0) continue;
  661. r = ioctl(fd, USB_GET_DEVICEINFO, &info);
  662. if (r < 0) {
  663. // NetBSD: added in 2004
  664. // OpenBSD: added November 23, 2009
  665. // FreeBSD: missing (FreeBSD 8.0) - USE_LIBUSB works!
  666. die("Error: your uhid driver does not support"
  667. " USB_GET_DEVICEINFO, please upgrade!\n");
  668. close(fd);
  669. closedir(dir);
  670. exit(1);
  671. }
  672. //printf("%s: v=%d, p=%d\n", buf, info.udi_vendorNo, info.udi_productNo);
  673. if (info.udi_vendorNo == vid && info.udi_productNo == pid) {
  674. closedir(dir);
  675. return fd;
  676. }
  677. close(fd);
  678. }
  679. closedir(dir);
  680. return -1;
  681. }
  682. static int uhid_teensy_fd = -1;
  683. int teensy_open(void)
  684. {
  685. teensy_close();
  686. uhid_teensy_fd = open_usb_device(0x16C0, 0x0478);
  687. if (uhid_teensy_fd < 0) return 0;
  688. return 1;
  689. }
  690. int teensy_write(void *buf, int len, double timeout)
  691. {
  692. int r;
  693. // TODO: imeplement timeout... how??
  694. r = write(uhid_teensy_fd, buf, len);
  695. if (r == len) return 1;
  696. return 0;
  697. }
  698. void teensy_close(void)
  699. {
  700. if (uhid_teensy_fd >= 0) {
  701. close(uhid_teensy_fd);
  702. uhid_teensy_fd = -1;
  703. }
  704. }
  705. int hard_reboot(void)
  706. {
  707. int r, rebootor_fd;
  708. rebootor_fd = open_usb_device(0x16C0, 0x0477);
  709. if (rebootor_fd < 0) return 0;
  710. r = write(rebootor_fd, "reboot", 6);
  711. delay(0.1);
  712. close(rebootor_fd);
  713. if (r == 6) return 1;
  714. return 0;
  715. }
  716. int soft_reboot(void)
  717. {
  718. printf("Soft reboot is not implemented for UHID\n");
  719. return 0;
  720. }
  721. #endif
  722. /****************************************************************/
  723. /* */
  724. /* Read Intel Hex File */
  725. /* */
  726. /****************************************************************/
  727. // the maximum flash image size we can support
  728. // chips with larger memory may be used, but only this
  729. // much intel-hex data can be loaded into memory!
  730. #define MAX_MEMORY_SIZE 0x100000
  731. static unsigned char firmware_image[MAX_MEMORY_SIZE];
  732. static unsigned char firmware_mask[MAX_MEMORY_SIZE];
  733. static int end_record_seen=0;
  734. static int byte_count;
  735. static unsigned int extended_addr = 0;
  736. static int parse_hex_line(char *line);
  737. int read_intel_hex(const char *filename)
  738. {
  739. FILE *fp;
  740. int i, lineno=0;
  741. char buf[1024];
  742. byte_count = 0;
  743. end_record_seen = 0;
  744. for (i=0; i<MAX_MEMORY_SIZE; i++) {
  745. firmware_image[i] = 0xFF;
  746. firmware_mask[i] = 0;
  747. }
  748. extended_addr = 0;
  749. fp = fopen(filename, "r");
  750. if (fp == NULL) {
  751. //printf("Unable to read file %s\n", filename);
  752. return -1;
  753. }
  754. while (!feof(fp)) {
  755. *buf = '\0';
  756. if (!fgets(buf, sizeof(buf), fp)) break;
  757. lineno++;
  758. if (*buf) {
  759. if (parse_hex_line(buf) == 0) {
  760. printf("Warning, HEX parse error line %d\n", lineno);
  761. return -2;
  762. }
  763. }
  764. if (end_record_seen) break;
  765. if (feof(stdin)) break;
  766. }
  767. fclose(fp);
  768. return byte_count;
  769. }
  770. /* from ihex.c, at http://www.pjrc.com/tech/8051/pm2_docs/intel-hex.html */
  771. /* parses a line of intel hex code, stores the data in bytes[] */
  772. /* and the beginning address in addr, and returns a 1 if the */
  773. /* line was valid, or a 0 if an error occured. The variable */
  774. /* num gets the number of bytes that were stored into bytes[] */
  775. int
  776. parse_hex_line(char *line)
  777. {
  778. int addr, code, num;
  779. int sum, len, cksum, i;
  780. char *ptr;
  781. num = 0;
  782. if (line[0] != ':') return 0;
  783. if (strlen(line) < 11) return 0;
  784. ptr = line+1;
  785. if (!sscanf(ptr, "%02x", &len)) return 0;
  786. ptr += 2;
  787. if ((int)strlen(line) < (11 + (len * 2)) ) return 0;
  788. if (!sscanf(ptr, "%04x", &addr)) return 0;
  789. ptr += 4;
  790. /* printf("Line: length=%d Addr=%d\n", len, addr); */
  791. if (!sscanf(ptr, "%02x", &code)) return 0;
  792. if (addr + extended_addr + len >= MAX_MEMORY_SIZE) return 0;
  793. ptr += 2;
  794. sum = (len & 255) + ((addr >> 8) & 255) + (addr & 255) + (code & 255);
  795. if (code != 0) {
  796. if (code == 1) {
  797. end_record_seen = 1;
  798. return 1;
  799. }
  800. if (code == 2 && len == 2) {
  801. if (!sscanf(ptr, "%04x", &i)) return 1;
  802. ptr += 4;
  803. sum += ((i >> 8) & 255) + (i & 255);
  804. if (!sscanf(ptr, "%02x", &cksum)) return 1;
  805. if (((sum & 255) + (cksum & 255)) & 255) return 1;
  806. extended_addr = i << 4;
  807. //printf("ext addr = %05X\n", extended_addr);
  808. }
  809. if (code == 4 && len == 2) {
  810. if (!sscanf(ptr, "%04x", &i)) return 1;
  811. ptr += 4;
  812. sum += ((i >> 8) & 255) + (i & 255);
  813. if (!sscanf(ptr, "%02x", &cksum)) return 1;
  814. if (((sum & 255) + (cksum & 255)) & 255) return 1;
  815. extended_addr = i << 16;
  816. //printf("ext addr = %08X\n", extended_addr);
  817. }
  818. return 1; // non-data line
  819. }
  820. byte_count += len;
  821. while (num != len) {
  822. if (sscanf(ptr, "%02x", &i) != 1) return 0;
  823. i &= 255;
  824. firmware_image[addr + extended_addr + num] = i;
  825. firmware_mask[addr + extended_addr + num] = 1;
  826. ptr += 2;
  827. sum += i;
  828. (num)++;
  829. if (num >= 256) return 0;
  830. }
  831. if (!sscanf(ptr, "%02x", &cksum)) return 0;
  832. if (((sum & 255) + (cksum & 255)) & 255) return 0; /* checksum error */
  833. return 1;
  834. }
  835. int ihex_bytes_within_range(int begin, int end)
  836. {
  837. int i;
  838. if (begin < 0 || begin >= MAX_MEMORY_SIZE ||
  839. end < 0 || end >= MAX_MEMORY_SIZE) {
  840. return 0;
  841. }
  842. for (i=begin; i<=end; i++) {
  843. if (firmware_mask[i]) return 1;
  844. }
  845. return 0;
  846. }
  847. void ihex_get_data(int addr, int len, unsigned char *bytes)
  848. {
  849. int i;
  850. if (addr < 0 || len < 0 || addr + len >= MAX_MEMORY_SIZE) {
  851. for (i=0; i<len; i++) {
  852. bytes[i] = 255;
  853. }
  854. return;
  855. }
  856. for (i=0; i<len; i++) {
  857. if (firmware_mask[addr]) {
  858. bytes[i] = firmware_image[addr];
  859. } else {
  860. bytes[i] = 255;
  861. }
  862. addr++;
  863. }
  864. }
  865. int memory_is_blank(int addr, int block_size)
  866. {
  867. if (addr < 0 || addr > MAX_MEMORY_SIZE) return 1;
  868. while (block_size && addr < MAX_MEMORY_SIZE) {
  869. if (firmware_mask[addr] && firmware_image[addr] != 255) return 0;
  870. addr++;
  871. block_size--;
  872. }
  873. return 1;
  874. }
  875. /****************************************************************/
  876. /* */
  877. /* Misc Functions */
  878. /* */
  879. /****************************************************************/
  880. int printf_verbose(const char *format, ...)
  881. {
  882. va_list ap;
  883. int r;
  884. va_start(ap, format);
  885. if (verbose) {
  886. r = vprintf(format, ap);
  887. fflush(stdout);
  888. return r;
  889. }
  890. return 0;
  891. }
  892. void delay(double seconds)
  893. {
  894. #ifdef WIN32
  895. Sleep(seconds * 1000.0);
  896. #else
  897. usleep(seconds * 1000000.0);
  898. #endif
  899. }
  900. void die(const char *str, ...)
  901. {
  902. va_list ap;
  903. va_start(ap, str);
  904. vfprintf(stderr, str, ap);
  905. fprintf(stderr, "\n");
  906. exit(1);
  907. }
  908. #if defined(WIN32)
  909. #define strcasecmp stricmp
  910. #endif
  911. static const struct {
  912. const char *name;
  913. int code_size;
  914. int block_size;
  915. } MCUs[] = {
  916. {"at90usb162", 15872, 128},
  917. {"atmega32u4", 32256, 128},
  918. {"at90usb646", 64512, 256},
  919. {"at90usb1286", 130048, 256},
  920. #if defined(USE_LIBUSB) || defined(USE_APPLE_IOKIT) || defined(USE_WIN32)
  921. {"mkl26z64", 63488, 512},
  922. {"mk20dx128", 131072, 1024},
  923. {"mk20dx256", 262144, 1024},
  924. {"mk66fx1m0", 1048576, 1024},
  925. {"mk64fx512", 524288, 1024},
  926. // Add duplicates that match friendly Teensy Names
  927. // Match board names in boards.txt
  928. {"TEENSY2", 32256, 128},
  929. {"TEENSY2PP", 130048, 256},
  930. {"TEENSYLC", 63488, 512},
  931. {"TEENSY30", 131072, 1024},
  932. {"TEENSY31", 262144, 1024},
  933. {"TEENSY35", 524288, 1024},
  934. {"TEENSY36", 1048576, 1024},
  935. #endif
  936. {NULL, 0, 0},
  937. };
  938. void list_mcus()
  939. {
  940. int i;
  941. printf("Supported MCUs are:\n");
  942. for(i=0; MCUs[i].name != NULL; i++)
  943. printf(" - %s\n", MCUs[i].name);
  944. exit(1);
  945. }
  946. void read_mcu(char *name)
  947. {
  948. int i;
  949. if(name == NULL) {
  950. fprintf(stderr, "No MCU specified.\n");
  951. list_mcus();
  952. }
  953. for(i=0; MCUs[i].name != NULL; i++) {
  954. if(strcasecmp(name, MCUs[i].name) == 0) {
  955. code_size = MCUs[i].code_size;
  956. block_size = MCUs[i].block_size;
  957. return;
  958. }
  959. }
  960. fprintf(stderr, "Unknown MCU type \"%s\"\n", name);
  961. list_mcus();
  962. }
  963. void parse_flag(char *arg)
  964. {
  965. int i;
  966. for(i=1; arg[i]; i++) {
  967. switch(arg[i]) {
  968. case 'w': wait_for_device_to_appear = 1; break;
  969. case 'r': hard_reboot_device = 1; break;
  970. case 's': soft_reboot_device = 1; break;
  971. case 'n': reboot_after_programming = 0; break;
  972. case 'v': verbose = 1; break;
  973. case 'b': boot_only = 1; break;
  974. default:
  975. fprintf(stderr, "Unknown flag '%c'\n\n", arg[i]);
  976. usage(NULL);
  977. }
  978. }
  979. }
  980. void parse_options(int argc, char **argv)
  981. {
  982. int i;
  983. char *arg;
  984. for (i=1; i<argc; i++) {
  985. arg = argv[i];
  986. //backward compatibility with previous versions.
  987. if(strncmp(arg, "-mmcu=", 6) == 0) {
  988. read_mcu(strchr(arg, '=') + 1);
  989. }
  990. else if(arg[0] == '-') {
  991. if(arg[1] == '-') {
  992. char *name = &arg[2];
  993. char *val = strchr(name, '=');
  994. if(val == NULL) {
  995. //value must be the next string.
  996. val = argv[++i];
  997. }
  998. else {
  999. //we found an =, so split the string at it.
  1000. *val = '\0';
  1001. val = &val[1];
  1002. }
  1003. if(strcasecmp(name, "help") == 0) usage(NULL);
  1004. else if(strcasecmp(name, "mcu") == 0) read_mcu(val);
  1005. else if(strcasecmp(name, "list-mcus") == 0) list_mcus();
  1006. else {
  1007. fprintf(stderr, "Unknown option \"%s\"\n\n", arg);
  1008. usage(NULL);
  1009. }
  1010. }
  1011. else parse_flag(arg);
  1012. }
  1013. else filename = arg;
  1014. }
  1015. }
  1016. void boot(unsigned char *buf, int write_size)
  1017. {
  1018. printf_verbose("Booting\n");
  1019. memset(buf, 0, write_size);
  1020. buf[0] = 0xFF;
  1021. buf[1] = 0xFF;
  1022. buf[2] = 0xFF;
  1023. teensy_write(buf, write_size, 0.5);
  1024. }