DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/serialport/libserial.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include "libserial.h"
00021 
00022 #include "config.h"
00023 
00024 #ifdef WIN32
00025 
00026 #include <windows.h>
00027 #include <stdio.h>
00028 
00029 struct _COMPORT {
00030         HANDLE porthandle;
00031         bool breakstatus;
00032         DCB orig_dcb;
00033 };
00034 
00035 bool SERIAL_open(const char* portname, COMPORT* port) {
00036         // allocate COMPORT structure
00037         COMPORT cp = (_COMPORT*)malloc(sizeof(_COMPORT));
00038         if(cp == NULL) return false;
00039         
00040         cp->breakstatus=false;
00041 
00042         // open the port in NT object space (recommended by Microsoft)
00043         // allows the user to open COM10+ and custom port names.
00044     size_t len = strlen(portname);
00045         if(len > 240) {
00046                 SetLastError(ERROR_BUFFER_OVERFLOW);
00047                 free(cp);
00048                 return false;
00049         }
00050         char extended_portname[256] = "\\\\.\\";
00051         memcpy(extended_portname+4,portname,len+1);
00052         
00053         cp->porthandle = CreateFile (extended_portname,
00054                                            GENERIC_READ | GENERIC_WRITE, 0,
00055                                                                           // must be opened with exclusive-access
00056                            NULL,          // no security attributes
00057                            OPEN_EXISTING, // must use OPEN_EXISTING
00058                            0,             // non overlapped I/O
00059                            NULL           // hTemplate must be NULL for comm devices
00060                           );
00061 
00062         if (cp->porthandle == INVALID_HANDLE_VALUE) goto cleanup_error;
00063         
00064         cp->orig_dcb.DCBlength=sizeof(DCB);
00065 
00066         if(!GetCommState(cp->porthandle, &cp->orig_dcb)) {
00067                 goto cleanup_error;
00068         }
00069 
00070         // configure the port for polling
00071         DCB newdcb;
00072         memcpy(&newdcb,&cp->orig_dcb,sizeof(DCB));
00073 
00074         newdcb.fBinary=true;
00075         newdcb.fParity=true;
00076         newdcb.fOutxCtsFlow=false;
00077         newdcb.fOutxDsrFlow=false;
00078         newdcb.fDtrControl=DTR_CONTROL_DISABLE;
00079         newdcb.fDsrSensitivity=false;
00080         
00081         newdcb.fOutX=false;
00082         newdcb.fInX=false;
00083         newdcb.fErrorChar=0;
00084         newdcb.fNull=false;
00085         newdcb.fRtsControl=RTS_CONTROL_DISABLE;
00086         newdcb.fAbortOnError=false;
00087 
00088         if(!SetCommState(cp->porthandle, &newdcb)) {
00089                 goto cleanup_error;
00090         }
00091 
00092         // Configure timeouts to effectively use polling
00093         COMMTIMEOUTS ct;
00094         ct.ReadIntervalTimeout = MAXDWORD;
00095         ct.ReadTotalTimeoutConstant = 0;
00096         ct.ReadTotalTimeoutMultiplier = 0;
00097         ct.WriteTotalTimeoutConstant = 0;
00098         ct.WriteTotalTimeoutMultiplier = 0;
00099         if(!SetCommTimeouts(cp->porthandle, &ct)) {
00100                 goto cleanup_error;
00101         }
00102         if(!ClearCommBreak(cp->porthandle)) {
00103                 // Bluetooth Bluesoleil seems to not implement it
00104                 //goto cleanup_error;
00105         }
00106         DWORD errors;
00107         if(!ClearCommError(cp->porthandle, &errors, NULL)) {
00108                 goto cleanup_error;
00109         }
00110         *port = cp;
00111         return true;
00112 
00113 cleanup_error:
00114         if (cp->porthandle != INVALID_HANDLE_VALUE) CloseHandle(cp->porthandle);
00115         free(cp);
00116         return false;
00117 }
00118 
00119 void SERIAL_close(COMPORT port) {
00120         // restore original DCB, close handle, free the COMPORT struct
00121         if (port->porthandle != INVALID_HANDLE_VALUE) {
00122                 SetCommState(port->porthandle, &port->orig_dcb);
00123                 CloseHandle(port->porthandle);
00124         }
00125         free(port);
00126 }
00127 
00128 void SERIAL_getErrorString(char* buffer, size_t length) {
00129         int error = GetLastError();
00130         if(length < 50) return;
00131         memset(buffer,0,length);
00132         // get the error message text from the operating system
00133         LPVOID sysmessagebuffer;
00134         FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
00135                 NULL,
00136                 error,
00137                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
00138                 (LPTSTR) &sysmessagebuffer,
00139                 0,NULL);
00140 
00141     size_t sysmsg_offset = 0;
00142 
00143         if(error == 5) {
00144                 const char* err5text = "The specified port is already in use.\n";
00145                 sysmsg_offset = (int)strlen(err5text);
00146                 memcpy(buffer,err5text,sysmsg_offset);
00147 
00148         } else if(error == 2) {
00149                 const char* err2text = "The specified port does not exist.\n";
00150                 sysmsg_offset = (int)strlen(err2text);
00151                 memcpy(buffer,err2text,sysmsg_offset);
00152         }
00153 
00154         // Go for length > so there will be bytes left afterwards.
00155         // (which are 0 due to memset, thus the buffer is 0 terminated
00156         if ( length > (sysmsg_offset + (int)strlen((const char*)sysmessagebuffer)) ) {
00157                 memcpy(buffer + sysmsg_offset, sysmessagebuffer,
00158                 strlen((const char*)sysmessagebuffer));
00159         }
00160                 
00161         LocalFree(sysmessagebuffer);
00162 }
00163 
00164 
00165 void SERIAL_setDTR(COMPORT port, bool value) {
00166         EscapeCommFunction(port->porthandle, value ? SETDTR:CLRDTR);
00167 }
00168 
00169 void SERIAL_setRTS(COMPORT port, bool value) {
00170         EscapeCommFunction(port->porthandle, value ? SETRTS:CLRRTS);
00171 }
00172 
00173 void SERIAL_setBREAK(COMPORT port, bool value) {
00174         EscapeCommFunction(port->porthandle, value ? SETBREAK:CLRBREAK);
00175         port->breakstatus = value;
00176 }
00177 
00178 int SERIAL_getmodemstatus(COMPORT port) {
00179         DWORD retval = 0;
00180         GetCommModemStatus (port->porthandle, &retval);
00181         return (int)retval;
00182 }
00183 
00184 bool SERIAL_sendchar(COMPORT port, char data) {
00185         DWORD bytesWritten;
00186 
00187         // mean bug: with break = 1, WriteFile will never return.
00188         if(port->breakstatus) return true; // true or false?!
00189 
00190         WriteFile (port->porthandle, &data, 1, &bytesWritten, NULL);
00191         if(bytesWritten==1) return true;
00192         else return false;
00193 }
00194 
00195 // 0-7 char data, higher=flags
00196 int SERIAL_getextchar(COMPORT port) {
00197         DWORD errors = 0;       // errors from API
00198         DWORD dwRead = 0;       // Number of chars read
00199         char chRead;
00200 
00201         int retval = 0;
00202         // receive a byte; TODO communicate failure
00203         if (ReadFile (port->porthandle, &chRead, 1, &dwRead, NULL)) {
00204                 if (dwRead) {
00205                         // check for errors
00206                         ClearCommError(port->porthandle, &errors, NULL);
00207                         // mask bits are identical
00208                         errors &= CE_BREAK|CE_FRAME|CE_RXPARITY|CE_OVERRUN;
00209                         retval |= (errors<<8);
00210                         retval |= (chRead & 0xff);
00211                         retval |= 0x10000; 
00212                 }
00213         }
00214         return retval;
00215 }
00216 
00217 bool SERIAL_setCommParameters(COMPORT port,
00218                         int baudrate, char parity, int stopbits, int length) {
00219         
00220         DCB dcb;
00221         dcb.DCBlength=sizeof(dcb);
00222         GetCommState(port->porthandle,&dcb);
00223 
00224         // parity
00225         switch (parity) {
00226         case 'n': dcb.Parity = NOPARITY; break;
00227         case 'o': dcb.Parity = ODDPARITY; break;
00228         case 'e': dcb.Parity = EVENPARITY; break;
00229         case 'm': dcb.Parity = MARKPARITY; break;
00230         case 's': dcb.Parity = SPACEPARITY;     break;
00231         default:
00232                 SetLastError(ERROR_INVALID_PARAMETER);
00233                 return false;
00234         }
00235 
00236         // stopbits
00237         switch(stopbits) {
00238         case SERIAL_1STOP: dcb.StopBits = ONESTOPBIT; break;
00239         case SERIAL_2STOP: dcb.StopBits = TWOSTOPBITS; break;
00240         case SERIAL_15STOP: dcb.StopBits = ONE5STOPBITS; break;
00241         default:
00242                 SetLastError(ERROR_INVALID_PARAMETER);
00243                 return false;
00244         }
00245 
00246         // byte length
00247         if(length > 8 || length < 5) {
00248                 SetLastError(ERROR_INVALID_PARAMETER);
00249                 return false;
00250         }
00251         dcb.ByteSize = length;
00252         dcb.BaudRate = baudrate;
00253 
00254         if (!SetCommState (port->porthandle, &dcb)) return false;
00255         return true;
00256 }
00257 #endif
00258 
00259 #if defined (LINUX) || defined (MACOSX) || defined (BSD) || defined (HAIKU)
00260 
00261 #include <string.h> // strlen
00262 #include <stdlib.h>
00263 
00264 #include <termios.h>
00265 #include <unistd.h>
00266 
00267 #include <sys/types.h>
00268 #include <sys/stat.h>
00269 #include <sys/ioctl.h>
00270 
00271 #include <errno.h>
00272 #include <fcntl.h>
00273 #include <stdio.h> // sprinf
00274 
00275 struct _COMPORT {
00276         int porthandle;
00277         bool breakstatus;
00278         termios backup;
00279 };
00280 
00281 bool SERIAL_open(const char* portname, COMPORT* port) {
00282         int result;
00283         // allocate COMPORT structure
00284         COMPORT cp = (_COMPORT*)malloc(sizeof(_COMPORT));
00285         if(cp == NULL) return false;
00286 
00287         cp->breakstatus=false;
00288 
00289     size_t len = strlen(portname);
00290         if(len > 240) {
00292                 return false;
00293         }
00294         char extended_portname[256] = "/dev/";
00295         memcpy(extended_portname+5,portname,(size_t)len);
00296 
00297         cp->porthandle = open (extended_portname, O_RDWR | O_NOCTTY | O_NONBLOCK);
00298         if (cp->porthandle < 0) goto cleanup_error;
00299 
00300         result = tcgetattr(cp->porthandle,&cp->backup);
00301         if (result==-1) goto cleanup_error;
00302 
00303         // get port settings
00304         termios termInfo;
00305         memcpy(&termInfo,&cp->backup,sizeof(termios));
00306 
00307         // initialize the port
00308         termInfo.c_cflag = CS8 | CREAD | CLOCAL; // noparity, 1 stopbit
00309         termInfo.c_iflag = PARMRK | INPCK;
00310         termInfo.c_oflag = 0;
00311         termInfo.c_lflag = 0;
00312         termInfo.c_cc[VMIN] = 0;
00313         termInfo.c_cc[VTIME] = 0;
00314 
00315         tcflush (cp->porthandle, TCIFLUSH);
00316         tcsetattr (cp->porthandle, TCSANOW, &termInfo);
00317 
00318         *port = cp;
00319         return true;
00320 
00321 cleanup_error:
00322         if (cp->porthandle != 0) close(cp->porthandle);
00323         free(cp);
00324         return false;
00325 }
00326 
00327 void SERIAL_close(COMPORT port) {
00328         // restore original termios, close handle, free the COMPORT struct
00329         if (port->porthandle >= 0) {
00330                 tcsetattr(port->porthandle, TCSANOW, &port->backup);
00331                 close(port->porthandle);
00332         }
00333         free(port);
00334 }
00335 
00336 void SERIAL_getErrorString(char* buffer, size_t length) {
00337         int error = errno;
00338         if(length < 50) return;
00339         memset(buffer,0,(size_t)length);
00340         // get the error message text from the operating system
00341         // TODO (or not)
00342         
00343         const char* err5text = "The specified port is already in use.\n";
00344         const char* err2text = "The specified port does not exist.\n";
00345         
00346     size_t sysmsg_offset = 0;
00347 
00348         if(error == EBUSY) {
00349                 sysmsg_offset = strlen(err5text);
00350                 memcpy(buffer,err5text,(size_t)sysmsg_offset);
00351 
00352         } else if(error == 2) {
00353                 sysmsg_offset = strlen(err2text);
00354                 memcpy(buffer,err2text,(size_t)sysmsg_offset);
00355         }
00356         
00357         sprintf(buffer + sysmsg_offset, "System error %d.",error);
00358         
00359 }
00360 
00361 int SERIAL_getmodemstatus(COMPORT port) {
00362         long flags = 0;
00363         ioctl (port->porthandle, TIOCMGET, &flags);
00364         int retval = 0;
00365         if (flags & TIOCM_CTS) retval |= SERIAL_CTS;
00366         if (flags & TIOCM_DSR) retval |= SERIAL_DSR;
00367         if (flags & TIOCM_RI) retval |= SERIAL_RI;
00368         if (flags & TIOCM_CD) retval |= SERIAL_CD;
00369         return retval;
00370 }
00371 
00372 bool SERIAL_sendchar(COMPORT port, char data) {
00373         if(port->breakstatus) return true; // true or false?!; Does POSIX need this check?
00374         int bytesWritten = write(port->porthandle, &data, 1);
00375         if(bytesWritten==1) return true;
00376         else return false;
00377 }
00378 
00379 int SERIAL_getextchar(COMPORT port) {
00380         unsigned char chRead = 0;
00381         int dwRead = 0;
00382         unsigned char error = 0;
00383         int retval = 0;
00384 
00385         dwRead=read(port->porthandle,&chRead,1);
00386         if (dwRead==1) {
00387                 if(chRead==0xff) // error escape
00388                 {
00389                         dwRead=read(port->porthandle,&chRead,1);
00390                         if(chRead==0x00) // an error 
00391                         {
00392                                 dwRead=read(port->porthandle,&chRead,1);
00393                                 if(chRead==0x0) error=SERIAL_BREAK_ERR;
00394                                 else error=SERIAL_FRAMING_ERR;
00395                         }
00396                 }
00397                 retval |= (error<<8);
00398                 retval |= chRead;
00399                 retval |= 0x10000; 
00400         }
00401         return retval;
00402 }
00403 
00404 bool SERIAL_setCommParameters(COMPORT port,
00405                         int baudrate, char parity, int stopbits, int length) {
00406         
00407         termios termInfo;
00408         int result = tcgetattr(port->porthandle, &termInfo);
00409         if (result==-1) return false;
00410         termInfo.c_cflag = CREAD | CLOCAL;
00411 
00412         // parity
00413         // "works on many systems"
00414         #define CMSPAR 010000000000
00415         switch (parity) {
00416         case 'n': break;
00417         case 'o': termInfo.c_cflag |= (PARODD | PARENB); break;
00418         case 'e': termInfo.c_cflag |= PARENB; break;
00419         case 'm': termInfo.c_cflag |= (PARENB | CMSPAR | PARODD); break;
00420         case 's': termInfo.c_cflag |= (PARENB | CMSPAR); break;
00421         default:
00422                 return false;
00423         }
00424         // stopbits
00425         switch(stopbits) {
00426         case SERIAL_1STOP: break;
00427         case SERIAL_2STOP: 
00428         case SERIAL_15STOP: termInfo.c_cflag |= CSTOPB; break;
00429         default:
00430                 return false;
00431         }
00432         // byte length
00433         if(length > 8 || length < 5) return false;
00434         switch (length) {
00435         case 5: termInfo.c_cflag |= CS5; break;
00436         case 6: termInfo.c_cflag |= CS6; break;
00437         case 7: termInfo.c_cflag |= CS7; break;
00438         case 8: termInfo.c_cflag |= CS8; break;
00439         }
00440         // baudrate
00441         int posix_baudrate=0;
00442         switch(baudrate) {
00443                 case 115200: posix_baudrate = B115200; break;
00444                 case  57600: posix_baudrate = B57600; break;
00445                 case  38400: posix_baudrate = B38400; break;
00446                 case  19200: posix_baudrate = B19200; break;
00447                 case   9600: posix_baudrate = B9600; break;
00448                 case   4800: posix_baudrate = B4800; break;
00449                 case   2400: posix_baudrate = B2400; break;
00450                 case   1200: posix_baudrate = B1200; break;
00451                 case    600: posix_baudrate = B600; break;
00452                 case    300: posix_baudrate = B300; break;
00453                 case    110: posix_baudrate = B110; break;
00454                 default: return false;
00455         }
00456         cfsetospeed (&termInfo, (unsigned int)posix_baudrate);
00457         cfsetispeed (&termInfo, (unsigned int)posix_baudrate);
00458 
00459         int retval = tcsetattr(port->porthandle, TCSANOW, &termInfo);
00460         if(retval==-1) return false;
00461         return true;
00462 }
00463 
00464 void SERIAL_setBREAK(COMPORT port, bool value) {
00465         ioctl(port->porthandle, value?TIOCSBRK:TIOCCBRK);
00466 }
00467 
00468 void SERIAL_setDTR(COMPORT port, bool value) {
00469         long flag = TIOCM_DTR;
00470         ioctl(port->porthandle, value?TIOCMBIS:TIOCMBIC, &flag);
00471 }
00472 
00473 void SERIAL_setRTS(COMPORT port, bool value) {
00474         long flag = TIOCM_RTS;
00475         ioctl(port->porthandle, value?TIOCMBIS:TIOCMBIC, &flag);
00476 }
00477 #endif
00478 
00479 #ifdef OS2
00480 // OS/2 related headers
00481 #define INCL_DOSFILEMGR
00482 #define INCL_DOSERRORS
00483 #define INCL_DOSDEVICES
00484 #define INCL_DOSDEVIOCTL
00485 #define INCL_DOSPROCESS
00486 #include <os2.h>
00487 #include <malloc.h>
00488 #include <string.h>
00489 #include <stdio.h>
00490 
00491 struct _COMPORT {
00492         HFILE porthandle;
00493         DCBINFO orig_dcb;
00494 };
00495 // TODO: THIS IS INCOMPLETE and UNTESTED.
00496 
00497 bool SERIAL_open(const char* portname, COMPORT* port) {
00498         // allocate COMPORT structure
00499         COMPORT cp = (_COMPORT*)malloc(sizeof(_COMPORT));
00500         if(cp == NULL) return false;
00501         cp->porthandle=0;
00502 
00503         USHORT errors = 0;
00504         ULONG ulAction = 0;
00505         ULONG ulParmLen = sizeof(DCBINFO);
00506         APIRET rc = DosOpen((PSZ)portname, &cp->porthandle,
00507                 &ulAction, 0L, FILE_NORMAL, FILE_OPEN,
00508                 OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE | OPEN_FLAGS_SEQUENTIAL, 0L);
00509         if (rc != NO_ERROR) {
00510                 goto cleanup_error;
00511         }
00512 
00513         rc = DosDevIOCtl(cp->porthandle, IOCTL_ASYNC, ASYNC_GETDCBINFO,
00514                 0, 0, 0, &cp->orig_dcb, ulParmLen, &ulParmLen);
00515         if ( rc != NO_ERROR) {
00516                 goto cleanup_error;
00517         }
00518         // configure the port for polling
00519         DCBINFO newdcb;
00520         memcpy(&newdcb,&cp->orig_dcb,sizeof(DCBINFO));
00521 
00522         newdcb.usWriteTimeout = 0;
00523         newdcb.usReadTimeout = 0; //65535;
00524         newdcb.fbCtlHndShake = cp->orig_dcb.fbFlowReplace = 0;
00525         newdcb.fbTimeout = 6;
00526 
00527         rc = DosDevIOCtl(cp->porthandle, IOCTL_ASYNC, ASYNC_SETDCBINFO,
00528                 &newdcb, ulParmLen, &ulParmLen, 0, 0, 0);
00529         if ( rc != NO_ERROR) {
00530                 goto cleanup_error;
00531         }
00532 
00533         ulParmLen = sizeof(errors);
00534         rc = DosDevIOCtl(cp->porthandle, IOCTL_ASYNC, ASYNC_GETCOMMERROR,
00535                 0, 0, 0, &errors, ulParmLen, &ulParmLen);
00536         if ( rc != NO_ERROR) {
00537                 goto cleanup_error;
00538         }
00539 
00540         *port = cp;
00541         return true;
00542 
00543 cleanup_error:
00544         // TODO error string - rc value
00545         if (cp->porthandle != 0) DosClose(cp->porthandle);
00546         free(cp);
00547         return false;
00548 }
00549 
00550 void SERIAL_getErrorString(char* buffer, size_t length) {
00551         sprintf(buffer, "TODO: error handling is not fun");
00552 }
00553 void SERIAL_close(COMPORT port) {
00554         ULONG ulParmLen = sizeof(DCBINFO);
00555         // restore original DCB, close handle, free the COMPORT struct
00556         if (port->porthandle != 0) {
00557                 DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_SETDCBINFO,
00558                         &port->orig_dcb, ulParmLen, &ulParmLen, 0, 0, 0);
00559                 DosClose (port->porthandle);
00560         }
00561         free(port);
00562 }
00563 bool SERIAL_sendchar(COMPORT port, char data) {
00564         ULONG bytesWritten = 0;
00565 
00566         APIRET rc = DosWrite(port->porthandle, &data, 1, &bytesWritten);
00567         if (rc == NO_ERROR && bytesWritten > 0) return true;
00568         else return false;
00569 }
00570 
00571 void SERIAL_setBREAK(COMPORT port, bool value) {
00572         USHORT error;
00573         ULONG ulParmLen = sizeof(error);
00574         DosDevIOCtl(port->porthandle, IOCTL_ASYNC,
00575                 value ? ASYNC_SETBREAKON : ASYNC_SETBREAKOFF,
00576                 0,0,0, &error, ulParmLen, &ulParmLen);
00577 }
00578 
00579 int SERIAL_getextchar(COMPORT port) {
00580         ULONG dwRead = 0;       // Number of chars read
00581         char chRead;
00582 
00583         int retval = 0;
00584         // receive a byte; TODO communicate failure
00585         if (DosRead(port->porthandle, &chRead, 1, &dwRead) == NO_ERROR) {
00586                 if (dwRead) {
00587                         // check for errors; will OS/2 clear the error on reading its data?
00588                         // if yes then this is in wrong order
00589                         USHORT errors = 0, event = 0;
00590                         ULONG ulParmLen = sizeof(errors);
00591                         DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_GETCOMMEVENT,
00592                                 0, 0, 0, &event, ulParmLen, &ulParmLen);
00593                         if (event & (64 + 128) ) { // Break (Bit 6) or Frame or Parity (Bit 7) error
00594                                 Bit8u errreg = 0;
00595                                 if (event & 64) retval |= SERIAL_BREAK_ERR;
00596                                 if (event & 128) {
00597                                         DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_GETCOMMERROR,
00598                                                 0, 0, 0, &errors, ulParmLen, &ulParmLen);
00599                                         if (errors & 8) retval |= SERIAL_FRAMING_ERR;
00600                                         if (errors & 4) retval |= SERIAL_PARITY_ERR;
00601                                 }
00602                         }
00603                         retval |= (chRead & 0xff);
00604                         retval |= 0x10000; 
00605                 }
00606         }
00607         return retval;
00608 }
00609 
00610 
00611 int SERIAL_getmodemstatus(COMPORT port) {
00612         UCHAR dptr = 0;
00613         ULONG ulParmLen = sizeof(dptr);
00614         DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_GETMODEMINPUT,
00615                 0, 0, 0, &dptr, ulParmLen, &ulParmLen);
00616         // bits are the same as return value
00617         return (int)dptr;
00618 }
00619 void SERIAL_setDTR(COMPORT port, bool value) {
00620         UCHAR masks[2];
00621         ULONG ulParmLen = sizeof(masks);
00622         if(value) {
00623                 masks[0]=0x01;
00624                 masks[1]=0xFF;
00625         } else {
00626                 masks[0]=0x00;
00627                 masks[1]=0xFE;
00628         }
00629         DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_SETMODEMCTRL,
00630                 0,0,0, &masks, ulParmLen, &ulParmLen);
00631 }
00632 
00633 void SERIAL_setRTS(COMPORT port, bool value) {
00634         UCHAR masks[2];
00635         ULONG ulParmLen = sizeof(masks);
00636         if(value) {
00637                 masks[0]=0x02;
00638                 masks[1]=0xFF;
00639         } else {
00640                 masks[0]=0x00;
00641                 masks[1]=0xFD;
00642         }
00643         DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_SETMODEMCTRL,
00644                 0,0,0, &masks, ulParmLen, &ulParmLen);
00645 }
00646 
00647 
00648 
00649 bool SERIAL_setCommParameters(COMPORT port,
00650                         int baudrate, char parity, int stopbits, int length) {
00651         // baud
00652         struct {
00653                 ULONG baud;
00654                 BYTE fraction;
00655         } setbaud;
00656 
00657         setbaud.baud = baudrate;
00658         setbaud.fraction = 0;
00659         ULONG ulParmLen = sizeof(setbaud);
00660         APIRET rc = DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_EXTSETBAUDRATE,
00661                 &setbaud, ulParmLen, &ulParmLen, 0, 0, 0);
00662         if (rc != NO_ERROR) {
00663                 return false;
00664         }
00665 
00666         struct {
00667                 UCHAR data;
00668                 UCHAR parity;
00669                 UCHAR stop;
00670         } paramline;
00671 
00672         // byte length
00673         if(length > 8 || length < 5) {
00674                 // TODO SetLastError(ERROR_INVALID_PARAMETER);
00675                 return false;
00676         }
00677         paramline.data = length;
00678 
00679         // parity
00680         switch (parity) {
00681         case 'n': paramline.parity = 0; break;
00682         case 'o': paramline.parity = 1; break;
00683         case 'e': paramline.parity = 2; break;
00684         case 'm': paramline.parity = 3; break;
00685         case 's': paramline.parity = 4; break;
00686         default:
00687                 // TODO SetLastError(ERROR_INVALID_PARAMETER);
00688                 return false;
00689         }
00690         // stopbits
00691         switch(stopbits) {
00692         case SERIAL_1STOP: paramline.stop = 0; break;
00693         case SERIAL_2STOP: paramline.stop = 2; break;
00694         case SERIAL_15STOP: paramline.stop = 1; break;
00695         default:
00696                 // TODO SetLastError(ERROR_INVALID_PARAMETER);
00697                 return false;
00698         }
00699         // set it
00700         ulParmLen = sizeof(paramline);
00701         rc = DosDevIOCtl(port->porthandle, IOCTL_ASYNC, ASYNC_SETLINECTRL,
00702                 &paramline, ulParmLen, &ulParmLen, 0, 0, 0);
00703         if ( rc != NO_ERROR)
00704                 return false;
00705         return true;
00706 }
00707 #endif