You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gbser_win.cc 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. /*
  2. Serial interface - Windows layer.
  3. Copyright (C) 2006 Robert Lipe, robertlipe+source@gpsbabel.org
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
  15. */
  16. #include "defs.h"
  17. #include "gbser.h"
  18. #include "gbser_private.h"
  19. #include <windows.h>
  20. #include <setupapi.h>
  21. #include <assert.h>
  22. #include <stdarg.h>
  23. #include <stdio.h>
  24. typedef struct {
  25. HANDLE comport;
  26. DWORD timeout;
  27. unsigned long magic;
  28. unsigned char inbuf[BUFSIZE];
  29. unsigned inbuf_used;
  30. } gbser_handle;
  31. #define DEV_PREFIX "\\\\.\\\\"
  32. /* Wrapper to safely cast a void * into a gbser_handle */
  33. static gbser_handle* gbser__get_handle(void* p)
  34. {
  35. gbser_handle* h = (gbser_handle*) p;
  36. assert(h->magic == MYMAGIC);
  37. return h;
  38. }
  39. DWORD mkspeed(unsigned br)
  40. {
  41. switch (br) {
  42. case 1200:
  43. return CBR_1200;
  44. case 2400:
  45. return CBR_2400;
  46. case 4800:
  47. return CBR_4800;
  48. case 9600:
  49. return CBR_9600;
  50. case 19200:
  51. return CBR_19200;
  52. case 38400:
  53. return CBR_38400;
  54. case 57600:
  55. return CBR_57600;
  56. case 115200:
  57. return CBR_115200;
  58. case 230400:
  59. // Per https://msdn.microsoft.com/en-us/library/windows/desktop/aa363214
  60. // "This member can be an actual baud rate value, or one of the
  61. // following indexes."
  62. // They provide a CBR_25600, which would be programmable on a 16450 only
  63. // with a bizarre oscillator crystal, but don't provide a 230400, such
  64. // as is used as the default in skytraq.
  65. #if defined CBR_230400
  66. return CBR_230400;
  67. #else
  68. return 230400;
  69. #endif
  70. default:
  71. fatal("Unsupported serial speed: %d\n", br);
  72. return 0; /* keep compiler happy */
  73. }
  74. }
  75. typedef LARGE_INTEGER hp_time;
  76. static void get_time(hp_time* tv)
  77. {
  78. QueryPerformanceCounter(tv);
  79. }
  80. static double elapsed(hp_time* tv)
  81. {
  82. hp_time now;
  83. LARGE_INTEGER tps;
  84. QueryPerformanceFrequency(&tps);
  85. QueryPerformanceCounter(&now);
  86. return ((double)(now.QuadPart - tv->QuadPart) /
  87. (double) tps.QuadPart) * 1000;
  88. }
  89. static int set_rx_timeout(gbser_handle* h, DWORD timeout)
  90. {
  91. if (timeout != h->timeout) {
  92. COMMTIMEOUTS to;
  93. if (!GetCommTimeouts(h->comport, &to)) {
  94. return gbser_ERROR;
  95. }
  96. to.ReadIntervalTimeout = timeout;
  97. to.ReadTotalTimeoutMultiplier = 0;
  98. to.ReadTotalTimeoutConstant = timeout;
  99. to.WriteTotalTimeoutMultiplier = 0;
  100. to.WriteTotalTimeoutConstant = 0;
  101. if (!SetCommTimeouts(h->comport, &to)) {
  102. return gbser_ERROR;
  103. } else {
  104. h->timeout = timeout;
  105. return gbser_OK;
  106. }
  107. } else {
  108. return gbser_OK;
  109. }
  110. }
  111. /* This isn't part of the above abstraction; it's just a helper for
  112. * the other serial modules in the tree.
  113. *
  114. * Windows does a weird thing with serial ports.
  115. * COM ports 1 - 9 are "COM1:" through "COM9:"
  116. * The one after that is \\.\\com10 - this function tries to plaster over
  117. * that.
  118. *
  119. * Worse still, Win98 and ME fail the open if you rename com1 to be \\.\\com1:
  120. *
  121. * It returns a pointer to a staticly allocated buffer and is therefore not
  122. * thread safe. The buffer pointed to remains valid only until the next
  123. * call to this function.
  124. */
  125. const char*
  126. fix_win_serial_name_r(const char* comname, char* obuf, size_t len)
  127. {
  128. if (!gbser_is_serial(comname) ||
  129. ((strlen(comname) == 5) && (comname[4] == ':')) ||
  130. ((strlen(comname) == 4) && (case_ignore_strncmp(comname, "com", 3) == 0))
  131. ) {
  132. strncpy(obuf, comname, len);
  133. } else {
  134. size_t l;
  135. snprintf(obuf, len, DEV_PREFIX "%s", comname);
  136. l = strlen(obuf);
  137. if (obuf[l - 1] == ':') {
  138. obuf[l - 1] = '\0';
  139. }
  140. }
  141. return obuf;
  142. }
  143. static char gb_com_buffer[100];
  144. const char* fix_win_serial_name(const char* comname)
  145. {
  146. return fix_win_serial_name_r(comname, gb_com_buffer, sizeof(gb_com_buffer));
  147. }
  148. /* Open a serial port. |port_name| is the (platform specific) name
  149. * of the serial device to open. Under WIN32 familiar DOS port names
  150. * ('com1:') are translated into the equivalent name required by
  151. * WIN32
  152. */
  153. void* gbser_init(const char* port_name)
  154. {
  155. HANDLE comport;
  156. gbser_handle* h = (gbser_handle*) xcalloc(1, sizeof(*h));
  157. const char* xname = fix_win_serial_name(port_name);
  158. gbser__db(2, "Translated port name: \"%s\"\n", xname);
  159. h->magic = MYMAGIC;
  160. comport = CreateFileA(xname, GENERIC_READ | GENERIC_WRITE,
  161. 0, NULL, OPEN_EXISTING, 0, NULL);
  162. if (comport == INVALID_HANDLE_VALUE) {
  163. goto failed;
  164. }
  165. h->comport = comport;
  166. h->timeout = 1;
  167. if (gbser_set_port(h, 4800, 8, 0, 1) || set_rx_timeout(h, 0)) {
  168. goto failed;
  169. }
  170. return h;
  171. failed:
  172. if (comport) {
  173. CloseHandle(h->comport);
  174. }
  175. xfree(h);
  176. return NULL;
  177. }
  178. /* Close a serial port
  179. */
  180. void gbser_deinit(void* handle)
  181. {
  182. gbser_handle* h = gbser__get_handle(handle);
  183. CloseHandle(h->comport);
  184. xfree(h);
  185. }
  186. int gbser_set_port(void* handle, unsigned speed, unsigned bits, unsigned parity, unsigned stop)
  187. {
  188. gbser_handle* h = gbser__get_handle(handle);
  189. DCB tio;
  190. if (bits < 5 || bits > 8) {
  191. fatal("Unsupported bits setting: %d\n", bits);
  192. }
  193. if (parity > 2) {
  194. fatal("Unsupported parity setting: %d\n", parity);
  195. }
  196. if (stop < 1 || stop > 2) {
  197. fatal("Unsupported stop setting: %d\n", stop);
  198. }
  199. tio.DCBlength = sizeof(DCB);
  200. GetCommState(h->comport, &tio);
  201. tio.BaudRate = mkspeed(speed);
  202. tio.fBinary = TRUE;
  203. tio.fParity = TRUE;
  204. tio.fOutxCtsFlow = FALSE;
  205. tio.fOutxDsrFlow = FALSE;
  206. tio.fDtrControl = DTR_CONTROL_ENABLE;
  207. tio.fDsrSensitivity = FALSE;
  208. tio.fTXContinueOnXoff = TRUE;
  209. tio.fOutX = FALSE;
  210. tio.fInX = FALSE;
  211. tio.fErrorChar = FALSE;
  212. tio.fNull = FALSE;
  213. tio.fRtsControl = RTS_CONTROL_ENABLE;
  214. tio.fAbortOnError = FALSE;
  215. tio.ByteSize = bits;
  216. tio.Parity = parity == 0 ? NOPARITY :
  217. (parity == 1 ? ODDPARITY : EVENPARITY);
  218. tio.StopBits = stop == 1 ? ONESTOPBIT : TWOSTOPBITS;
  219. if (!SetCommState(h->comport, &tio)) {
  220. return gbser_ERROR;
  221. }
  222. return gbser_OK;
  223. }
  224. unsigned gbser__read_buffer(void* handle, void** buf, unsigned* len)
  225. {
  226. gbser_handle* h = gbser__get_handle(handle);
  227. unsigned count = *len;
  228. unsigned char* cp = (unsigned char*) *buf;
  229. if (count > h->inbuf_used) {
  230. count = h->inbuf_used;
  231. }
  232. memcpy(cp, h->inbuf, count);
  233. memmove(h->inbuf, h->inbuf + count,
  234. h->inbuf_used - count);
  235. h->inbuf_used -= count;
  236. *len -= count;
  237. cp += count;
  238. *buf = (void*) cp;
  239. return count;
  240. }
  241. /* Return when the input buffer contains at least |want| bytes or |*ms|
  242. * milliseconds have elapsed. |ms| may be NULL or |*ms| may be zero to
  243. * poll the port for available bytes and return immediately. |*ms| will
  244. * be updated to indicate the remaining time on exit.
  245. * Returns the number of bytes available (>=0) or an error code (<0).
  246. */
  247. int gbser__fill_buffer(void* handle, unsigned want, unsigned* ms)
  248. {
  249. int rc;
  250. gbser_handle* h = gbser__get_handle(handle);
  251. if (want > BUFSIZE) {
  252. want = BUFSIZE;
  253. }
  254. /* Already got enough bytes? */
  255. if (h->inbuf_used >= want) {
  256. return h->inbuf_used;
  257. }
  258. if (NULL == ms || 0 == *ms) {
  259. DWORD err, nread;
  260. COMSTAT stat;
  261. ClearCommError(h->comport, &err, &stat);
  262. if (stat.cbInQue > 0) {
  263. DWORD count = want - h->inbuf_used;
  264. if (count > stat.cbInQue) {
  265. count = stat.cbInQue;
  266. }
  267. if (rc = set_rx_timeout(h, 1), rc) {
  268. return rc;
  269. }
  270. if (!ReadFile(h->comport, h->inbuf + h->inbuf_used,
  271. count, &nread, NULL)) {
  272. err = GetLastError();
  273. if (err != ERROR_COUNTER_TIMEOUT && err != ERROR_TIMEOUT) {
  274. return gbser_ERROR;
  275. }
  276. }
  277. h->inbuf_used += nread;
  278. }
  279. } else {
  280. hp_time tv;
  281. double time_left;
  282. DWORD err, nread;
  283. get_time(&tv);
  284. if (rc = set_rx_timeout(h, *ms), rc) {
  285. return rc;
  286. }
  287. if (!ReadFile(h->comport, h->inbuf + h->inbuf_used,
  288. want - h->inbuf_used,
  289. &nread, NULL)) {
  290. err = GetLastError();
  291. if (err != ERROR_COUNTER_TIMEOUT && err != ERROR_TIMEOUT) {
  292. return gbser_ERROR;
  293. }
  294. }
  295. h->inbuf_used += nread;
  296. time_left = *ms - elapsed(&tv);
  297. *ms = time_left < 0 ? 0 : (unsigned) time_left;
  298. }
  299. return h->inbuf_used;
  300. }
  301. /* Discard any pending input on the serial port.
  302. */
  303. int gbser_flush(void* handle)
  304. {
  305. gbser_handle* h = gbser__get_handle(handle);
  306. h->inbuf_used = 0;
  307. if (!PurgeComm(h->comport, PURGE_RXCLEAR)) {
  308. return gbser_ERROR;
  309. }
  310. return gbser_OK;
  311. }
  312. /* Write |len| bytes from |buf| to the serial port.
  313. */
  314. int gbser_write(void* handle, const void* buf, unsigned len)
  315. {
  316. gbser_handle* h = gbser__get_handle(handle);
  317. DWORD nwritten;
  318. const char* bp = (const char*) buf;
  319. /* Not sure we need to spin here - but this'll work even if we don't */
  320. while (len > 0) {
  321. if (!WriteFile(h->comport, bp, len, &nwritten, NULL)) {
  322. return gbser_ERROR;
  323. }
  324. len -= nwritten;
  325. bp += nwritten;
  326. }
  327. return gbser_OK;
  328. }
  329. /* Return true if a port name seems to refer to a serial port.
  330. * On Windows this tests the filename (against the regex
  331. * /^(\\\\\.\\\\)?com\d+:?$/i). On Posix it returns the value of
  332. * isatty()
  333. */
  334. int gbser_is_serial(const char* port_name)
  335. {
  336. const char* pfx = DEV_PREFIX;
  337. size_t pfx_l = strlen(pfx);
  338. const char* com = "COM";
  339. size_t com_l = strlen(com);
  340. unsigned digits;
  341. if (NULL == port_name) {
  342. return 0;
  343. }
  344. /* Skip any prefix */
  345. if (memcmp(port_name, pfx, pfx_l) == 0) {
  346. port_name += pfx_l;
  347. }
  348. if (case_ignore_strncmp(port_name, com, com_l) != 0) {
  349. return 0;
  350. }
  351. port_name += com_l;
  352. for (digits = 0; isdigit(*port_name); port_name++, digits++) {
  353. /* do nothing */
  354. }
  355. if (digits == 0) {
  356. return 0;
  357. }
  358. if (*port_name == ':') {
  359. port_name++;
  360. }
  361. if (*port_name != '\0') {
  362. return 0;
  363. }
  364. /* Success! */
  365. return 1;
  366. }
  367. /* Read from the serial port until the specified |eol| character is
  368. * found. Any character matching |discard| will be discarded. To
  369. * read lines terminated by 0x0A0x0D discarding linefeeds use
  370. * gbser_read_line(h, buf, len, 1000, 0x0D, 0x0A);
  371. */
  372. int gbser_read_line(void* handle, void* buf,
  373. unsigned len, unsigned ms,
  374. int eol, int discard)
  375. {
  376. char* bp = (char*) buf;
  377. unsigned pos = 0;
  378. hp_time tv;
  379. get_time(&tv);
  380. bp[pos] = '\0';
  381. for (;;) {
  382. signed time_left = ms - elapsed(&tv);
  383. int c;
  384. if (time_left <= 0) {
  385. return gbser_TIMEOUT;
  386. }
  387. c = gbser_readc_wait(handle, time_left);
  388. if (c == gbser_ERROR) {
  389. return c;
  390. } else if (c == eol) {
  391. return gbser_OK;
  392. }
  393. if (c != gbser_NOTHING && c != discard && pos < len - 1) {
  394. bp[pos++] = c;
  395. bp[pos] = '\0';
  396. }
  397. }
  398. }