2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
16 #define WIN32_LEAN_AND_MEAN
29 #define ASCII_XON 0x11
30 #define ASCII_XOFF 0x13
32 #define MODEM_REGISTRY_PATH "System\\CurrentControlSet\\Services\\Class\\Modem"
35 static long comm_delay_val = 0;
37 DWORD FAR PASCAL comm_thread_proc(LPSTR lpdata);
38 int FAR PASCAL comm_idle_function(COMM_OBJ *obj);
39 void comm_dump_info(COMM_OBJ *obj);
42 // ---------------------------------------------------------------------
44 int comm_get_modem_info(int modem, COMM_OBJ *obj)
51 memset(obj, 0, sizeof(COMM_OBJ));
57 obj->port = (char)(modem * -1);
62 sprintf(buf, "\\%04d", modem);
64 strcpy(path, MODEM_REGISTRY_PATH);
67 mprintf((0, "Looking for modem in HKEY_LOCAL_MACHINE\\%s\n", path));
69 err = RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0, KEY_READ, &hKey);
71 if (err != ERROR_SUCCESS) return 0;
73 // We have a key into the modem 000x
74 // Retreive all pertinant info.
76 err = RegQueryValueEx(hKey, "Model", 0, &type, buf, &len);
77 if (err != ERROR_SUCCESS) {
82 strcpy(obj->name, buf);
85 err = RegQueryValueEx(hKey, "PortSubClass", 0, &type, buf, &len);
86 if (err != ERROR_SUCCESS) {
90 mprintf((0, "COM:%d\n", buf[0]));
95 err = RegQueryValueEx(hKey, "Reset", 0, &type, buf, &len);
96 if (err != ERROR_SUCCESS) {
101 strcpy(obj->cmd[COMM_RESET_CMD], buf);
103 // Retreive some settings stuff
104 err = RegOpenKeyEx(hKey, "Answer", 0, KEY_READ, &hSubKey);
105 if (err !=ERROR_SUCCESS) {
110 err = RegQueryValueEx(hSubKey, "1", 0, &type, buf, &len);
111 if (err != ERROR_SUCCESS) {
112 RegCloseKey(hSubKey);
116 strcpy(obj->cmd[COMM_ANSWER_CMD], buf);
117 RegCloseKey(hSubKey);
119 err = RegOpenKeyEx(hKey, "Hangup", 0, KEY_READ, &hSubKey);
120 if (err !=ERROR_SUCCESS) {
125 err = RegQueryValueEx(hSubKey, "1", 0, &type, buf, &len);
126 if (err != ERROR_SUCCESS) {
127 RegCloseKey(hSubKey);
131 strcpy(obj->cmd[COMM_HANGUP_CMD], buf);
132 RegCloseKey(hSubKey);
134 err = RegOpenKeyEx(hKey, "Settings", 0, KEY_READ, &hSubKey);
135 if (err !=ERROR_SUCCESS) {
140 err = RegQueryValueEx(hSubKey, "DialPrefix", 0, &type, buf, &len);
141 if (err != ERROR_SUCCESS) {
142 RegCloseKey(hSubKey);
146 strcpy(obj->cmd[COMM_DIALPREF_CMD], buf);
149 err = RegQueryValueEx(hSubKey, "Tone", 0, &type, buf, &len);
150 if (err != ERROR_SUCCESS) {
151 RegCloseKey(hSubKey);
155 strcat(obj->cmd[COMM_DIALPREF_CMD], buf);
158 err = RegQueryValueEx(hSubKey, "Prefix", 0, &type, buf, &len);
159 if (err != ERROR_SUCCESS) {
160 RegCloseKey(hSubKey);
164 strcpy(obj->cmd[COMM_PREFIX_CMD], buf);
166 RegCloseKey(hSubKey);
175 // int comm_open_connection(COMM_OBJ *obj);
176 // ----------------------------------------------------------------------------
177 int comm_open_connection(COMM_OBJ *obj)
180 COMMTIMEOUTS ctimeouts;
182 sprintf(filename, "COM%d", obj->port);
184 obj->handle = CreateFile(filename,
185 GENERIC_READ | GENERIC_WRITE,
189 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
190 if (obj->handle == INVALID_HANDLE_VALUE)
193 // Modem COMport is open.
194 SetCommMask(obj->handle, EV_RXCHAR);
195 SetupComm(obj->handle, 4096, 4096);
196 PurgeComm( obj->handle,
199 PURGE_TXCLEAR | PURGE_RXCLEAR ) ;
201 // Timeout after 10 sec.
202 ctimeouts.ReadIntervalTimeout = 0xffffffff;
203 ctimeouts.ReadTotalTimeoutMultiplier = 0;
204 ctimeouts.ReadTotalTimeoutConstant = 10000;
205 ctimeouts.WriteTotalTimeoutMultiplier = 0;
206 ctimeouts.WriteTotalTimeoutConstant = 10000;
207 SetCommTimeouts(obj->handle, &ctimeouts);
209 memset(&obj->rov, 0, sizeof(OVERLAPPED));
210 memset(&obj->wov, 0, sizeof(OVERLAPPED));
212 obj->rov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
213 if (obj->rov.hEvent == NULL) {
214 mprintf((0, "COMM: Unable to create read event.\n"));
215 CloseHandle(obj->handle);
218 obj->wov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
219 if (obj->wov.hEvent == NULL) {
220 mprintf((0, "COMM: Unable to create write event.\n"));
221 CloseHandle(obj->rov.hEvent);
222 CloseHandle(obj->handle);
226 // Setup parameters for Connection
228 obj->dcb.DCBlength = sizeof(DCB);
229 GetCommState(obj->handle, &obj->dcb);
232 obj->dcb.BaudRate = obj->baud;
233 else obj->baud = obj->dcb.BaudRate;
235 mprintf((0, "COM%d (%d) is opened.\n", obj->port, obj->baud));
237 obj->dcb.fBinary = 1;
238 obj->dcb.Parity = NOPARITY;
240 obj->dcb.XonChar = ASCII_XON;
241 obj->dcb.XoffChar = ASCII_XOFF;
242 obj->dcb.XonLim = 1024;
243 obj->dcb.XoffLim = 1024;
244 obj->dcb.EofChar = 0;
245 obj->dcb.EvtChar = 0;
246 obj->dcb.fDtrControl = DTR_CONTROL_ENABLE;// dtr=on
247 obj->dcb.fRtsControl = RTS_CONTROL_ENABLE;
249 obj->dcb.ByteSize = 8;
250 obj->dcb.StopBits = ONESTOPBIT;
251 obj->dcb.fParity = FALSE;
253 obj->dcb.fOutxDsrFlow = FALSE;
254 obj->dcb.fOutxCtsFlow = FALSE; // rts/cts off
256 // obj->dcb.fInX = obj->dcb.fOutX = 1; // Software flow control XON/XOFF
258 if (SetCommState(obj->handle, &obj->dcb) == TRUE)
261 obj->threadID = (DWORD)(-1);
264 EscapeCommFunction(obj->handle, SETDTR);
268 mprintf((1, "COMM: Unable to set CommState: (%x)\n", GetLastError()));
271 obj->threadID = (DWORD)(-1);
272 obj->connect = FALSE;
273 // CloseHandle(obj->wov.hEvent);
274 // CloseHandle(obj->rov.hEvent);
275 CloseHandle(obj->handle);
284 // int comm_close_connection(COMM_OBJ *obj);
285 // ----------------------------------------------------------------------------
286 int comm_close_connection(COMM_OBJ *obj)
288 CloseHandle(obj->wov.hEvent);
289 CloseHandle(obj->rov.hEvent);
291 SetCommMask(obj->handle, 0);
292 EscapeCommFunction(obj->handle, CLRDTR);
293 PurgeComm( obj->handle,
296 PURGE_TXCLEAR | PURGE_RXCLEAR ) ;
297 CloseHandle(obj->handle);
299 obj->connect = FALSE;
301 mprintf((0, "COM%d closed.\n", obj->port));
306 // int comm_read_char(COMM_OBJ *obj);
307 // ----------------------------------------------------------------------------
309 #define ROTBUF_SIZE 16
311 static char rotbuf[ROTBUF_SIZE];
312 static int rotbuf_cur=0, rotbuf_tail=0;
314 int comm_read_char(COMM_OBJ *obj)
316 DWORD errflags, result;
318 DWORD length, length_to_read;
320 if (!obj->connect) return COMM_BUF_EMPTY;
322 // Do read buffer stuff
323 if (rotbuf_cur < rotbuf_tail) {
324 return (int)rotbuf[rotbuf_cur++];
327 // Now if we have a condition of cur = tail, then the buffer is empty
328 // and we have to refill it.
330 ClearCommError(obj->handle, &errflags, &comstat);
332 if (comstat.cbInQue > 0) {
333 if (comstat.cbInQue < ROTBUF_SIZE)
334 length_to_read = comstat.cbInQue;
336 length_to_read = ROTBUF_SIZE;
338 result = ReadFile(obj->handle, rotbuf, length_to_read, &length, &obj->rov);
341 if ((errflags = GetLastError()) != ERROR_IO_PENDING) {
342 mprintf((1, "CommReadChar err: %X\n", errflags));
343 ClearCommError(obj->handle, &errflags, &comstat);
346 while (!GetOverlappedResult(obj->handle, &obj->rov, &length, TRUE))
348 errflags = GetLastError();
349 if (errflags == ERROR_IO_INCOMPLETE) {
353 mprintf((1, "GetOverlappedResult error: %d.\n", errflags));
354 rotbuf_cur = rotbuf_tail;
355 return COMM_BUF_EMPTY;
362 Assert(length <= ROTBUF_SIZE);
363 rotbuf_tail = length;
364 return (int)rotbuf[rotbuf_cur++];
366 else return COMM_BUF_EMPTY;
370 // int comm_read_char_timed(COMM_OBJ *obj, int msecs);
371 // ----------------------------------------------------------------------------
372 int comm_read_char_timed(COMM_OBJ *obj, int msecs)
377 long timeout = timeGetTime() + (long)msecs;
379 ClearCommError(obj->handle, &err, &cstat);
380 while (cstat.cbInQue == 0)
382 status = comm_idle_function(obj);
383 if (!status) return -1;
384 ClearCommError(obj->handle, &err, &cstat);
385 if (timeout <= timeGetTime()) return -1;
387 return comm_read_char(obj);
391 // int comm_write_char(COMM_OBJ *obj, int ch);
392 // ----------------------------------------------------------------------------
393 int comm_write_char(COMM_OBJ *obj, int ch)
400 if (!obj->connect) return 0;
404 // mprintf((0, "%c", c));
406 if (!WriteFile(obj->handle, &c, 1, &length, &obj->wov)) {
407 if (GetLastError() == ERROR_IO_PENDING) {
408 while (!GetOverlappedResult(obj->handle, &obj->wov, &length, TRUE))
410 errflags = GetLastError();
411 if (errflags == ERROR_IO_INCOMPLETE)
414 ClearCommError(obj->handle, &errflags, &comstat);
415 mprintf((1, "Comm: Write error!\n"));
421 ClearCommError(obj->handle, &errflags, &comstat);
422 mprintf((1, "Comm: Write error!\n"));
427 //mprintf((0, "[%c]", c));
433 // int comm_get_cd(COMM_OBJ *obj);
434 // ----------------------------------------------------------------------------
435 int comm_get_cd(COMM_OBJ *obj)
439 if (!obj->connect) return 0;
441 GetCommModemStatus(obj->handle, &status);
442 if (status & MS_RLSD_ON) return 1;
447 // int comm_get_line_status(COMM_OBJ *obj);
448 // ----------------------------------------------------------------------------
449 int comm_get_line_status(COMM_OBJ *obj)
455 if (!obj->connect) return 0;
457 ClearCommError(obj->handle, &err, &comstat);
459 if (err & CE_BREAK) status |= COMM_STATUS_BREAK;
460 if (err & CE_FRAME) status |= COMM_STATUS_FRAME;
461 if (err & CE_OVERRUN) status |= COMM_STATUS_OVERRUN;
462 if (err & CE_RXPARITY) status |= COMM_STATUS_RXPARITY;
468 // void comm_clear_line_status(COMM_OBJ *obj)
469 // ----------------------------------------------------------------------------
470 void comm_clear_line_status(COMM_OBJ *obj)
476 // void comm_set_dtr(COMM_OBJ *obj, int flag);
477 // ----------------------------------------------------------------------------
478 void comm_set_dtr(COMM_OBJ *obj, int flag)
480 if (!obj->connect) return;
482 GetCommState(obj->handle, &obj->dcb);
484 if (flag == 1) obj->dcb.fDtrControl = DTR_CONTROL_ENABLE;
485 else obj->dcb.fDtrControl = DTR_CONTROL_DISABLE;
487 SetCommState(obj->handle, &obj->dcb);
491 // void comm_set_rtscts(COMM_OBJ *obj, int flag);
492 // ----------------------------------------------------------------------------
493 void comm_set_rtscts(COMM_OBJ *obj, int flag)
496 if (!obj->connect) return;
498 GetCommState(obj->handle, &obj->dcb);
501 obj->dcb.fOutxCtsFlow = TRUE; // rts/cts off
502 obj->dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
505 obj->dcb.fOutxCtsFlow = FALSE; // rts/cts off
506 obj->dcb.fRtsControl = RTS_CONTROL_ENABLE;
509 SetCommState(obj->handle, &obj->dcb);
513 // int comm_modem_input_line(COMM_OBJ *obj, int msecs, char *buffer, int chars);
514 // ----------------------------------------------------------------------------
515 int comm_modem_input_line(COMM_OBJ *obj, int msecs, char *buffer, int chars)
524 timeout = timeGetTime() + msecs;
526 if (chars <= 0) return 0;
530 ch = comm_read_char(obj);
533 // mprintf((0, "\n"));
536 if (ch == '\n') continue;
537 buffer[i++] = (char)ch;
539 if (chars == 0) break;
541 else if (ch == COMM_BUF_EMPTY) {
549 if (timeout <= timeGetTime()) break;
553 if (retval < 0) return retval;
555 retval = timeout - timeGetTime();
556 if (retval < 0) retval = 0;
561 // void comm_set_delay(COMM_OBJ *obj, int msecs)
562 // ----------------------------------------------------------------------------
563 void comm_set_delay(COMM_OBJ *obj, int msecs)
565 comm_delay_val = (long)msecs;
569 // void comm_wait(COMM_OBJ *obj, int msecs)
570 // ----------------------------------------------------------------------------
571 int comm_wait(COMM_OBJ *obj, int msecs)
575 delaytime = timeGetTime() + (long)msecs;
577 while (timeGetTime() < delaytime) {
578 if (!comm_idle_function(obj)) {
579 mprintf((1, "Comm_wait: THIS IS NOT GOOD!!\n"));
587 // int comm_modem_send_string(COMM_OBJ *obj, char *str);
588 // ----------------------------------------------------------------------------
589 int comm_modem_send_string(COMM_OBJ *obj, char *str)
595 mprintf((0, "Commodem: send %s\n", str));
596 retval = comm_modem_send_string_nowait(obj, str, -2);
597 if (!retval) return 0;
599 if (comm_delay_val > 0) {
600 if (!comm_wait(obj, comm_delay_val)) return 0;
603 // Checking for OK response
604 curtime = comm_delay_val;
607 curtime = comm_modem_input_line(obj, curtime, buf, 64);
609 mprintf((1, "CommSendString: Didn't get an OK!\n"));
612 if (strcmp("OK", buf) == 0) {
613 return comm_wait(obj, 500);
620 // int comm_modem_send_string_nowait(COMM_OBJ *obj, char *str, int term);
621 // ----------------------------------------------------------------------------
622 int comm_modem_send_string_nowait(COMM_OBJ *obj, char *str, int term)
626 if (term < -2 || term > 255) return 0;
628 // mprintf((0, "<%s", str));
629 for (i= 0; i < lstrlen(str); i++)
630 comm_write_char(obj, str[i]);
632 //@@ if (!WriteFile(obj->handle, &str, lstrlen(str), &length, &obj->wov)) {
633 //@@ if (GetLastError() == ERROR_IO_PENDING) {
634 //@@ mprintf((0,"Comm: SendStringNoWait IO pending...\n"));
635 //@@ while (!GetOverlappedResult(obj->handle, &obj->wov, &length, TRUE))
637 //@@ errflags = GetLastError();
638 //@@ if (errflags == ERROR_IO_INCOMPLETE)
641 //@@ ClearCommError(obj->handle, &errflags, &comstat);
642 //@@ mprintf((1, "Comm: SendStringNoWait error!\n"));
648 //@@ ClearCommError(obj->handle, &errflags, &comstat);
649 //@@ mprintf((1, "Comm: SendStringNoWait error!\n"));
655 comm_write_char(obj, term);
656 else if (term == -2) {
657 comm_write_char(obj, '\r');
658 comm_write_char(obj, '\n');
665 // int comm_modem_reset(COMM_OBJ *obj);
666 // ----------------------------------------------------------------------------
667 int comm_modem_reset(COMM_OBJ *obj)
669 return comm_modem_send_string(obj, obj->cmd[COMM_RESET_CMD]);
673 // int comm_modem_dial(COMM_OBJ *obj, char *phonenum);
674 // ----------------------------------------------------------------------------
675 int comm_modem_dial(COMM_OBJ *obj, char *phonenum)
679 strcpy(str, obj->cmd[COMM_PREFIX_CMD]);
680 strcat(str, obj->cmd[COMM_DIALPREF_CMD]);
681 strcat(str, phonenum);
682 return comm_modem_send_string_nowait(obj, str, '\r');
686 // int comm_modem_answer(COMM_OBJ *obj);
687 // ----------------------------------------------------------------------------
688 int comm_modem_answer(COMM_OBJ *obj)
690 return comm_modem_send_string_nowait(obj, obj->cmd[COMM_ANSWER_CMD], '\r');
695 //@@ DWORD FAR PASCAL comm_thread_proc (LPSTR lpData);
696 //@@ -------------------------------------------------------------------------
697 //@@DWORD FAR PASCAL comm_thread_proc (LPSTR lpData)
699 //@@ DWORD event_mask;
701 //@@ COMM_OBJ *obj = (COMM_OBJ *)lpData;
703 //@@ memset(&os, 0, sizeof(OVERLAPPED));
704 //@@ os.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
705 //@@ if (os.hEvent == NULL) {
706 //@@ mprintf((1, "CommThread: unable to create event!\n"));
710 //@@ if (!SetCommMask(obj->handle, EV_RXCHAR)) return FALSE;
712 //@@ while (obj->connected)
715 //@@ WaitCommEvent(obj->handle, &event_mask, NULL);
716 //@@ if ((event_mask & EV_RXCHAR) == EV_RXCHAR) {
722 // int FAR PASCAL comm_idle_function(COMM_OBJ *obj);
723 // ----------------------------------------------------------------------------
724 int FAR PASCAL comm_idle_function(COMM_OBJ *obj)
728 while (PeekMessage(&msg, 0,0,0, PM_REMOVE)) {
729 TranslateMessage(&msg);
730 DispatchMessage(&msg);
738 // ----------------------------------------------------------------------------
739 void comm_dump_info(COMM_OBJ *obj)
743 switch (obj->dcb.BaudRate)
745 case CBR_9600: baud = 9600; break;
746 case CBR_14400: baud = 14400; break;
747 case CBR_19200: baud = 19200; break;
748 case CBR_38400: baud = 38400; break;
749 case CBR_57600: baud = 57600; break;
753 mprintf((0, "CommObj %x:\n", obj->handle));
754 mprintf((0, "\tConnect: %d\n\tDevice: %s\n", obj->connect, obj->name));
755 mprintf((0, "\tPort: COM%d\n", obj->port));
756 mprintf((0, "\tBaud: %d\n\tDTR:[%d] RTS/CTS:[%d|%d]\n", baud, obj->dcb.fDtrControl,obj->dcb.fRtsControl,obj->dcb.fOutxCtsFlow));
757 mprintf((0, "\tXON/XOFF [In|Out]:[%d|%d] XON/OFF thresh:[%d|%d]\n", obj->dcb.fInX, obj->dcb.fOutX, obj->dcb.XonLim, obj->dcb.XoffLim));