Authors: Chris Morgan <cmorgan@wpi.edu>, James Abbatiello <abbeyj@wpi.edu>
[wine] / misc / comm.c
1  /*
2  * DEC 93 Erik Bos <erik@xs4all.nl>
3  *
4  * Copyright 1996 Marcus Meissner
5  *
6  * Mar 31, 1999. Ove Kåven <ovek@arcticnet.no>
7  * - Implemented buffers and EnableCommNotification.
8  *
9  * Apr 3, 1999.  Lawson Whitney <lawson_whitney@juno.com>
10  * - Fixed the modem control part of EscapeCommFunction16.
11  *
12  * Mar 3, 1999. Ove Kåven <ovek@arcticnet.no>
13  * - Use port indices instead of unixfds for win16
14  * - Moved things around (separated win16 and win32 routines)
15  * - Added some hints on how to implement buffers and EnableCommNotification.
16  *
17  * May 26, 1997.  Fixes and comments by Rick Richardson <rick@dgii.com> [RER]
18  * - ptr->fd wasn't getting cleared on close.
19  * - GetCommEventMask() and GetCommError() didn't do much of anything.
20  *   IMHO, they are still wrong, but they at least implement the RXCHAR
21  *   event and return I/O queue sizes, which makes the app I'm interested
22  *   in (analog devices EZKIT DSP development system) work.
23  *
24  * August 12, 1997.  Take a bash at SetCommEventMask - Lawson Whitney
25  *                                     <lawson_whitney@juno.com>
26  * July 6, 1998. Fixes and comments by Valentijn Sessink
27  *                                     <vsessink@ic.uva.nl> [V]
28  * Oktober 98, Rein Klazes [RHK]
29  * A program that wants to monitor the modem status line (RLSD/DCD) may
30  * poll the modem status register in the commMask structure. I update the bit
31  * in GetCommError, waiting for an implementation of communication events.
32  * 
33  */
34
35 #include "config.h"
36
37 #include <stdlib.h>
38 #include <termios.h>
39 #include <fcntl.h>
40 #include <string.h>
41 #ifdef HAVE_STRINGS_H
42 # include <strings.h>
43 #endif
44 #include <errno.h>
45 #include <ctype.h>
46 #include <sys/stat.h>
47 #ifdef HAVE_SYS_FILIO_H
48 # include <sys/filio.h>
49 #endif
50 #include <sys/ioctl.h>
51 #include <unistd.h>
52
53 #include "wine/winuser16.h"
54 #include "comm.h"
55 #ifdef HAVE_SYS_MODEM_H
56 # include <sys/modem.h>
57 #endif
58 #ifdef HAVE_SYS_STRTIO_H
59 # include <sys/strtio.h>
60 #endif
61 #include "heap.h"
62 #include "options.h"
63
64 #include "server/request.h"
65 #include "server.h"
66 #include "process.h"
67 #include "winerror.h"
68 #include "async.h"
69
70 #include "debug.h"
71
72 DEFAULT_DEBUG_CHANNEL(comm)
73
74 #ifndef TIOCINQ
75 #define TIOCINQ FIONREAD
76 #endif
77 #define COMM_MSR_OFFSET  35       /* see knowledge base Q101417 */
78 #define FLAG_LPT 0x80
79
80 struct DosDeviceStruct COM[MAX_PORTS];
81 struct DosDeviceStruct LPT[MAX_PORTS];
82 /* pointers to unknown(==undocumented) comm structure */ 
83 LPCVOID *unknown[MAX_PORTS];
84 /* save terminal states */
85 static struct termios m_stat[MAX_PORTS];
86
87 void COMM_Init(void)
88 {
89         int x;
90         char option[10], temp[256], *btemp;
91         struct stat st;
92
93         for (x=0; x!=MAX_PORTS; x++) {
94                 strcpy(option,"COMx");
95                 option[3] = '1' + x;
96                 option[4] = '\0';
97
98                 PROFILE_GetWineIniString( "serialports", option, "*",
99                                           temp, sizeof(temp) );
100                 if (!strcmp(temp, "*") || *temp == '\0') 
101                         COM[x].devicename = NULL;
102                 else {
103                         btemp = strchr(temp,',');
104                         if (btemp != NULL) {
105                                 *btemp++ = '\0';
106                                 COM[x].baudrate = atoi(btemp);
107                         } else {
108                                 COM[x].baudrate = -1;
109                         }
110                         stat(temp, &st);
111                         if (!S_ISCHR(st.st_mode)) 
112                                 WARN(comm,"Can't use `%s' as %s !\n", temp, option);
113                         else
114                                 if ((COM[x].devicename = malloc(strlen(temp)+1)) == NULL) 
115                                         WARN(comm,"Can't malloc for device info!\n");
116                                 else {
117                                         COM[x].fd = 0;
118                                         strcpy(COM[x].devicename, temp);
119                                 }
120                 TRACE(comm, "%s = %s\n", option, COM[x].devicename);
121                 }
122
123                 strcpy(option, "LPTx");
124                 option[3] = '1' + x;
125                 option[4] = '\0';
126
127                 PROFILE_GetWineIniString( "parallelports", option, "*",
128                                           temp, sizeof(temp) );
129                 if (!strcmp(temp, "*") || *temp == '\0')
130                         LPT[x].devicename = NULL;
131                 else {
132                         stat(temp, &st);
133                         if (!S_ISCHR(st.st_mode)) 
134                                 WARN(comm,"Can't use `%s' as %s !\n", temp, option);
135                         else 
136                                 if ((LPT[x].devicename = malloc(strlen(temp)+1)) == NULL) 
137                                         WARN(comm,"Can't malloc for device info!\n");
138                                 else {
139                                         LPT[x].fd = 0;
140                                         strcpy(LPT[x].devicename, temp);
141                                 }
142                 TRACE(comm, "%s = %s\n", option, LPT[x].devicename);
143                 }
144
145         }
146 }
147
148
149 struct DosDeviceStruct *GetDeviceStruct_fd(int fd)
150 {
151         int x;
152
153         for (x=0; x!=MAX_PORTS; x++) {
154             if (COM[x].fd == fd)
155                 return &COM[x];
156             if (LPT[x].fd == fd)
157                 return &LPT[x];
158         }
159
160         return NULL;
161 }
162
163 struct DosDeviceStruct *GetDeviceStruct(int fd)
164 {
165         if ((fd&0x7F)<=MAX_PORTS) {
166             if (!(fd&FLAG_LPT)) {
167                 if (COM[fd].fd)
168                     return &COM[fd];
169             } else {
170                 if (LPT[fd].fd)
171                     return &LPT[fd];
172             }
173         }
174
175         return NULL;
176 }
177
178 int    GetCommPort_fd(int fd)
179 {
180         int x;
181         
182         for (x=0; x<MAX_PORTS; x++) {
183              if (COM[x].fd == fd)
184                  return x;
185        }
186        
187        return -1;
188
189
190 int ValidCOMPort(int x)
191 {
192         return(x < MAX_PORTS ? (int) COM[x].devicename : 0); 
193 }
194
195 int ValidLPTPort(int x)
196 {
197         return(x < MAX_PORTS ? (int) LPT[x].devicename : 0); 
198 }
199
200 int WinError(void)
201 {
202         TRACE(comm, "errno = %d\n", errno);
203         switch (errno) {
204                 default:
205                         return CE_IOE;
206                 }
207 }
208
209 static unsigned comm_inbuf(struct DosDeviceStruct *ptr)
210 {
211   return ((ptr->ibuf_tail > ptr->ibuf_head) ? ptr->ibuf_size : 0)
212     + ptr->ibuf_head - ptr->ibuf_tail;
213 }
214
215 static unsigned comm_outbuf(struct DosDeviceStruct *ptr)
216 {
217   return ((ptr->obuf_tail > ptr->obuf_head) ? ptr->obuf_size : 0)
218     + ptr->obuf_head - ptr->obuf_tail;
219 }
220
221 static int COMM_WhackModem(int fd, unsigned int andy, unsigned int orrie)
222 {
223     unsigned int mstat, okay;
224     okay = ioctl(fd, TIOCMGET, &mstat);
225     if (okay) return okay;
226     if (andy) mstat &= andy;
227     mstat |= orrie;
228     return ioctl(fd, TIOCMSET, &mstat);
229 }
230         
231 static void comm_notification(int fd,void*private)
232 {
233   struct DosDeviceStruct *ptr = (struct DosDeviceStruct *)private;
234   int prev, bleft, len;
235   WORD mask = 0;
236   int cid = GetCommPort_fd(fd);
237
238   /* read data from comm port */
239   prev = comm_inbuf(ptr);
240   do {
241     bleft = ((ptr->ibuf_tail > ptr->ibuf_head) ? (ptr->ibuf_tail-1) : ptr->ibuf_size)
242       - ptr->ibuf_head;
243     len = read(fd, ptr->inbuf + ptr->ibuf_head, bleft?bleft:1);
244     if (len > 0) {
245       if (!bleft) {
246         ptr->commerror = CE_RXOVER;
247       } else {
248         ptr->ibuf_head += len;
249         if (ptr->ibuf_head >= ptr->ibuf_size)
250           ptr->ibuf_head = 0;
251         /* flag event */
252         if (ptr->eventmask & EV_RXCHAR)
253           *(WORD*)(unknown[cid]) |= EV_RXCHAR;
254         /* FIXME: check for event character (EV_RXFLAG) */
255       }
256     }
257   } while (len > 0);
258   /* check for notification */
259   if (ptr->wnd && (ptr->n_read>0) && (prev<ptr->n_read) &&
260       (comm_inbuf(ptr)>=ptr->n_read)) {
261     /* passed the receive notification threshold */
262     mask |= CN_RECEIVE;
263   }
264
265   /* write any TransmitCommChar character */
266   if (ptr->xmit>=0) {
267     len = write(fd, &(ptr->xmit), 1);
268     if (len > 0) ptr->xmit = -1;
269   }
270   /* write from output queue */
271   prev = comm_outbuf(ptr);
272   do {
273     bleft = ((ptr->obuf_tail <= ptr->obuf_head) ? ptr->obuf_head : ptr->obuf_size)
274       - ptr->obuf_tail;
275     len = bleft ? write(fd, ptr->outbuf + ptr->obuf_tail, bleft) : 0;
276     if (len > 0) {
277       ptr->obuf_tail += len;
278       if (ptr->obuf_tail >= ptr->obuf_size)
279         ptr->obuf_tail = 0;
280       /* flag event */
281       if ((ptr->obuf_tail == ptr->obuf_head) && (ptr->eventmask & EV_TXEMPTY))
282         *(WORD*)(unknown[cid]) |= EV_TXEMPTY;
283     }
284   } while (len > 0);
285   /* check for notification */
286   if (ptr->wnd && (ptr->n_write>0) && (prev>=ptr->n_write) &&
287       (comm_outbuf(ptr)<ptr->n_write)) {
288     /* passed the transmit notification threshold */
289     mask |= CN_TRANSMIT;
290   }
291
292   /* send notifications, if any */
293   if (ptr->wnd && mask) {
294     PostMessage16(ptr->wnd, WM_COMMNOTIFY, cid, mask);
295   }
296 }
297
298 /**************************************************************************
299  *         BuildCommDCB         (USER.213)
300  */
301 BOOL16 WINAPI BuildCommDCB16(LPCSTR device, LPDCB16 lpdcb)
302 {
303         /* "COM1:9600,n,8,1"    */
304         /*  012345              */
305         int port;
306         char *ptr, temp[256];
307
308         TRACE(comm, "(%s), ptr %p\n", device, lpdcb);
309
310         if (!lstrncmpiA(device,"COM",3)) {
311                 port = device[3] - '0';
312         
313
314                 if (port-- == 0) {
315                         ERR(comm, "BUG ! COM0 can't exist!.\n");
316                         return -1;
317                 }
318
319                 if (!ValidCOMPort(port)) {
320                         return -1;
321                 }
322                 
323                 memset(lpdcb, 0, sizeof(DCB16)); /* initialize */
324
325                 lpdcb->Id = port;
326                 
327                 if (!*(device+4))
328                         return 0;
329
330                 if (*(device+4) != ':')
331                         return -1;
332                 
333                 strcpy(temp,device+5);
334                 ptr = strtok(temp, ", "); 
335
336                 if (COM[port].baudrate > 0)
337                         lpdcb->BaudRate = COM[port].baudrate;
338                 else
339                         lpdcb->BaudRate = atoi(ptr);
340                 TRACE(comm,"baudrate (%d)\n", lpdcb->BaudRate);
341
342                 ptr = strtok(NULL, ", ");
343                 if (islower(*ptr))
344                         *ptr = toupper(*ptr);
345
346                 TRACE(comm,"parity (%c)\n", *ptr);
347                 lpdcb->fParity = TRUE;
348                 switch (*ptr) {
349                         case 'N':
350                                 lpdcb->Parity = NOPARITY;
351                                 lpdcb->fParity = FALSE;
352                                 break;                  
353                         case 'E':
354                                 lpdcb->Parity = EVENPARITY;
355                                 break;                  
356                         case 'M':
357                                 lpdcb->Parity = MARKPARITY;
358                                 break;                  
359                         case 'O':
360                                 lpdcb->Parity = ODDPARITY;
361                                 break;                  
362                         default:
363                                 WARN(comm,"Unknown parity `%c'!\n", *ptr);
364                                 return -1;
365                 }
366
367                 ptr = strtok(NULL, ", "); 
368                 TRACE(comm, "charsize (%c)\n", *ptr);
369                 lpdcb->ByteSize = *ptr - '0';
370
371                 ptr = strtok(NULL, ", ");
372                 TRACE(comm, "stopbits (%c)\n", *ptr);
373                 switch (*ptr) {
374                         case '1':
375                                 lpdcb->StopBits = ONESTOPBIT;
376                                 break;                  
377                         case '2':
378                                 lpdcb->StopBits = TWOSTOPBITS;
379                                 break;                  
380                         default:
381                                 WARN(comm,"Unknown # of stopbits `%c'!\n", *ptr);
382                                 return -1;
383                 }
384         }       
385
386         return 0;
387 }
388
389 /*****************************************************************************
390  *      OpenComm                (USER.200)
391  */
392 INT16 WINAPI OpenComm16(LPCSTR device,UINT16 cbInQueue,UINT16 cbOutQueue)
393 {
394         int port,fd;
395
396         TRACE(comm, "%s, %d, %d\n", device, cbInQueue, cbOutQueue);
397
398                 port = device[3] - '0';
399
400         if (port-- == 0)
401                 ERR(comm, "BUG ! COM0 or LPT0 don't exist !\n");
402
403         if (!lstrncmpiA(device,"COM",3)) {
404                 
405                 TRACE(comm, "%s = %s\n", device, COM[port].devicename);
406
407                 if (!ValidCOMPort(port))
408                         return IE_BADID;
409
410                 if (COM[port].fd)
411                         return IE_OPEN;
412
413                 fd = open(COM[port].devicename, O_RDWR | O_NONBLOCK);
414                 if (fd == -1) {
415                         return IE_HARDWARE;
416                 } else {
417                         unknown[port] = SEGPTR_ALLOC(40);
418                         bzero(unknown[port],40);
419                         COM[port].fd = fd;
420                         COM[port].commerror = 0;
421                         COM[port].eventmask = 0;
422                         /* save terminal state */
423                         tcgetattr(fd,&m_stat[port]);
424                         /* set default parameters */
425                         if(COM[port].baudrate>-1){
426                             DCB16 dcb;
427                             GetCommState16(port, &dcb);
428                             dcb.BaudRate=COM[port].baudrate;
429                             /* more defaults:
430                              * databits, parity, stopbits
431                              */
432                             SetCommState16( &dcb);
433                         }
434                         /* init priority characters */
435                         COM[port].unget = -1;
436                         COM[port].xmit = -1;
437                         /* allocate buffers */
438                         COM[port].ibuf_size = cbInQueue;
439                         COM[port].ibuf_head = COM[port].ibuf_tail= 0;
440                         COM[port].obuf_size = cbOutQueue;
441                         COM[port].obuf_head = COM[port].obuf_tail = 0;
442
443                         COM[port].inbuf = malloc(cbInQueue);
444                         if (COM[port].inbuf) {
445                           COM[port].outbuf = malloc(cbOutQueue);
446                           if (!COM[port].outbuf)
447                             free(COM[port].inbuf);
448                         } else COM[port].outbuf = NULL;
449                         if (!COM[port].outbuf) {
450                           /* not enough memory */
451                           tcsetattr(COM[port].fd,TCSANOW,&m_stat[port]);
452                           close(COM[port].fd);
453                           return IE_MEMORY;
454                         }
455
456                         /* enable async notifications */
457                         ASYNC_RegisterFD(COM[port].fd,comm_notification,&COM[port]);
458                         return port;
459                 }
460         } 
461         else 
462         if (!lstrncmpiA(device,"LPT",3)) {
463         
464                 if (!ValidLPTPort(port))
465                         return IE_BADID;
466
467                 if (LPT[port].fd)
468                         return IE_OPEN;
469
470                 fd = open(LPT[port].devicename, O_RDWR | O_NONBLOCK, 0);
471                 if (fd == -1) {
472                         return IE_HARDWARE;
473                 } else {
474                         LPT[port].fd = fd;
475                         LPT[port].commerror = 0;
476                         LPT[port].eventmask = 0;
477                         return port|FLAG_LPT;
478                 }
479         }
480         return 0;
481 }
482
483 /*****************************************************************************
484  *      CloseComm               (USER.207)
485  */
486 INT16 WINAPI CloseComm16(INT16 cid)
487 {
488         struct DosDeviceStruct *ptr;
489         
490         TRACE(comm,"cid=%d\n", cid);
491         if ((ptr = GetDeviceStruct(cid)) == NULL) {
492                 return -1;
493         }
494         if (!(cid&FLAG_LPT)) {
495                 /* COM port */
496                 SEGPTR_FREE(unknown[cid]); /* [LW] */
497
498                 /* disable async notifications */
499                 ASYNC_UnregisterFD(COM[cid].fd,comm_notification);
500                 /* free buffers */
501                 free(ptr->outbuf);
502                 free(ptr->inbuf);
503
504                 /* reset modem lines */
505                 tcsetattr(ptr->fd,TCSANOW,&m_stat[cid]);
506         }
507
508         if (close(ptr->fd) == -1) {
509                 ptr->commerror = WinError();
510                 /* FIXME: should we clear ptr->fd here? */
511                 return -1;
512         } else {
513                 ptr->commerror = 0;
514                 ptr->fd = 0;
515                 return 0;
516         }
517 }
518
519 /*****************************************************************************
520  *      SetCommBreak            (USER.210)
521  */
522 INT16 WINAPI SetCommBreak16(INT16 cid)
523 {
524         struct DosDeviceStruct *ptr;
525
526         TRACE(comm,"cid=%d\n", cid);
527         if ((ptr = GetDeviceStruct(cid)) == NULL) {
528                 return -1;
529         }
530
531         ptr->suspended = 1;
532         ptr->commerror = 0;
533         return 0;
534 }
535
536 /*****************************************************************************
537  *      ClearCommBreak          (USER.211)
538  */
539 INT16 WINAPI ClearCommBreak16(INT16 cid)
540 {
541         struct DosDeviceStruct *ptr;
542
543         TRACE(comm,"cid=%d\n", cid);
544         if ((ptr = GetDeviceStruct(cid)) == NULL) {
545                 return -1;
546         }
547
548         ptr->suspended = 0;
549         ptr->commerror = 0;
550         return 0;
551 }
552
553 /*****************************************************************************
554  *      EscapeCommFunction      (USER.214)
555  */
556 LONG WINAPI EscapeCommFunction16(UINT16 cid,UINT16 nFunction)
557 {
558         int     max;
559         struct DosDeviceStruct *ptr;
560         struct termios port;
561
562         TRACE(comm,"cid=%d, function=%d\n", cid, nFunction);
563         if ((ptr = GetDeviceStruct(cid)) == NULL) {
564                 return -1;
565         }
566         if (tcgetattr(ptr->fd,&port) == -1) {
567                 ptr->commerror=WinError();      
568                 return -1;
569         }
570
571         switch (nFunction) {
572                 case RESETDEV:
573                         break;                                  
574
575                 case GETMAXCOM:
576                         for (max = MAX_PORTS;!COM[max].devicename;max--)
577                                 ;
578                         return max;
579                         break;
580
581                 case GETMAXLPT:
582                         for (max = MAX_PORTS;!LPT[max].devicename;max--)
583                                 ;
584                         return FLAG_LPT + max;
585                         break;
586
587 #ifdef TIOCM_DTR
588                 case CLRDTR:
589                         return COMM_WhackModem(ptr->fd, ~TIOCM_DTR, 0);
590 #endif
591 #ifdef TIOCM_RTS
592                 case CLRRTS:
593                         return COMM_WhackModem(ptr->fd, ~TIOCM_RTS, 0);
594 #endif
595         
596 #ifdef TIOCM_DTR
597                 case SETDTR:
598                         return COMM_WhackModem(ptr->fd, 0, TIOCM_DTR);
599 #endif
600
601 #ifdef TIOCM_RTS                        
602                 case SETRTS:
603                         return COMM_WhackModem(ptr->fd, 0, TIOCM_RTS);
604 #endif
605
606                 case SETXOFF:
607                         port.c_iflag |= IXOFF;
608                         break;
609
610                 case SETXON:
611                         port.c_iflag |= IXON;
612                         break;
613
614                 default:
615                         WARN(comm,"(cid=%d,nFunction=%d): Unknown function\n", 
616                         cid, nFunction);
617                         break;                          
618         }
619         
620         if (tcsetattr(ptr->fd, TCSADRAIN, &port) == -1) {
621                 ptr->commerror = WinError();
622                 return -1;      
623         } else {
624                 ptr->commerror = 0;
625                 return 0;
626         }
627 }
628
629 /*****************************************************************************
630  *      FlushComm       (USER.215)
631  */
632 INT16 WINAPI FlushComm16(INT16 cid,INT16 fnQueue)
633 {
634         int queue;
635         struct DosDeviceStruct *ptr;
636
637         TRACE(comm,"cid=%d, queue=%d\n", cid, fnQueue);
638         if ((ptr = GetDeviceStruct(cid)) == NULL) {
639                 return -1;
640         }
641         switch (fnQueue) {
642                 case 0:
643                   queue = TCOFLUSH;
644                   ptr->obuf_tail = ptr->obuf_head;
645                   break;
646                 case 1:
647                   queue = TCIFLUSH;
648                   ptr->ibuf_head = ptr->ibuf_tail;
649                   break;
650                 default:
651                   WARN(comm,"(cid=%d,fnQueue=%d):Unknown queue\n", 
652                             cid, fnQueue);
653                   return -1;
654                 }
655         if (tcflush(ptr->fd, queue)) {
656                 ptr->commerror = WinError();
657                 return -1;      
658         } else {
659                 ptr->commerror = 0;
660                 return 0;
661         }
662 }  
663
664 /********************************************************************
665  *      GetCommError    (USER.203)
666  */
667 INT16 WINAPI GetCommError16(INT16 cid,LPCOMSTAT16 lpStat)
668 {
669         int             temperror;
670         struct DosDeviceStruct *ptr;
671         unsigned char *stol;
672         unsigned int mstat;
673
674         if ((ptr = GetDeviceStruct(cid)) == NULL) {
675                 return -1;
676         }
677         if (cid&FLAG_LPT) {
678             WARN(comm," cid %d not comm port\n",cid);
679             return CE_MODE;
680         }
681         stol = (unsigned char *)unknown[cid] + COMM_MSR_OFFSET;
682         ioctl(ptr->fd,TIOCMGET,&mstat);
683         if( mstat&TIOCM_CAR ) 
684             *stol |= 0x80;
685         else 
686             *stol &=0x7f;
687
688         if (lpStat) {
689                 lpStat->status = 0;
690
691                 lpStat->cbOutQue = comm_outbuf(ptr);
692                 lpStat->cbInQue = comm_inbuf(ptr);
693
694                 TRACE(comm, "cid %d, error %d, lpStat %d %d %d stol %x\n",
695                              cid, ptr->commerror, lpStat->status, lpStat->cbInQue, 
696                              lpStat->cbOutQue, *stol);
697         }
698         else
699                 TRACE(comm, "cid %d, error %d, lpStat NULL stol %x\n",
700                              cid, ptr->commerror, *stol);
701
702         /* Return any errors and clear it */
703         temperror = ptr->commerror;
704         ptr->commerror = 0;
705         return(temperror);
706 }
707
708 /*****************************************************************************
709  *      SetCommEventMask        (USER.208)
710  */
711 SEGPTR WINAPI SetCommEventMask16(INT16 cid,UINT16 fuEvtMask)
712 {
713         struct DosDeviceStruct *ptr;
714         unsigned char *stol;
715         int repid;
716         unsigned int mstat;
717
718         TRACE(comm,"cid %d,mask %d\n",cid,fuEvtMask);
719         if ((ptr = GetDeviceStruct(cid)) == NULL)
720             return (SEGPTR)NULL;
721
722         ptr->eventmask = fuEvtMask;
723
724         if ((cid&FLAG_LPT) || !ValidCOMPort(cid)) {
725             WARN(comm," cid %d not comm port\n",cid);
726             return (SEGPTR)NULL;
727         }
728         /* it's a COM port ? -> modify flags */
729         stol = (unsigned char *)unknown[cid] + COMM_MSR_OFFSET;
730         repid = ioctl(ptr->fd,TIOCMGET,&mstat);
731         TRACE(comm, " ioctl  %d, msr %x at %p %p\n",repid,mstat,stol,unknown[cid]);
732         if ((mstat&TIOCM_CAR))
733             *stol |= 0x80;
734         else
735             *stol &=0x7f;
736
737         TRACE(comm," modem dcd construct %x\n",*stol);
738         return SEGPTR_GET(unknown[cid]);
739 }
740
741 /*****************************************************************************
742  *      GetCommEventMask        (USER.209)
743  */
744 UINT16 WINAPI GetCommEventMask16(INT16 cid,UINT16 fnEvtClear)
745 {
746         struct DosDeviceStruct *ptr;
747         WORD events;
748
749         TRACE(comm, "cid %d, mask %d\n", cid, fnEvtClear);
750         if ((ptr = GetDeviceStruct(cid)) == NULL)
751             return 0;
752
753         if ((cid&FLAG_LPT) || !ValidCOMPort(cid)) {
754             WARN(comm," cid %d not comm port\n",cid);
755             return 0;
756         }
757
758         events = *(WORD*)(unknown[cid]) & fnEvtClear;
759         *(WORD*)(unknown[cid]) &= ~fnEvtClear;
760         return events;
761 }
762
763 /*****************************************************************************
764  *      SetCommState16  (USER.201)
765  */
766 INT16 WINAPI SetCommState16(LPDCB16 lpdcb)
767 {
768         struct termios port;
769         struct DosDeviceStruct *ptr;
770
771         TRACE(comm, "cid %d, ptr %p\n", lpdcb->Id, lpdcb);
772         if ((ptr = GetDeviceStruct(lpdcb->Id)) == NULL) {
773                 return -1;
774         }
775         if (tcgetattr(ptr->fd, &port) == -1) {
776                 ptr->commerror = WinError();    
777                 return -1;
778         }
779
780         port.c_cc[VMIN] = 0;
781         port.c_cc[VTIME] = 1;
782
783 #ifdef IMAXBEL
784         port.c_iflag &= ~(ISTRIP|BRKINT|IGNCR|ICRNL|INLCR|IMAXBEL);
785 #else
786         port.c_iflag &= ~(ISTRIP|BRKINT|IGNCR|ICRNL|INLCR);
787 #endif
788         port.c_iflag |= (IGNBRK);
789
790         port.c_oflag &= ~(OPOST);
791
792         port.c_cflag &= ~(HUPCL);
793         port.c_cflag |= CLOCAL | CREAD;
794
795         port.c_lflag &= ~(ICANON|ECHO|ISIG);
796         port.c_lflag |= NOFLSH;
797
798         TRACE(comm,"baudrate %d\n",lpdcb->BaudRate);
799 #ifdef CBAUD
800         port.c_cflag &= ~CBAUD;
801         switch (lpdcb->BaudRate) {
802                 case 110:
803                 case CBR_110:
804                         port.c_cflag |= B110;
805                         break;          
806                 case 300:
807                 case CBR_300:
808                         port.c_cflag |= B300;
809                         break;          
810                 case 600:
811                 case CBR_600:
812                         port.c_cflag |= B600;
813                         break;          
814                 case 1200:
815                 case CBR_1200:
816                         port.c_cflag |= B1200;
817                         break;          
818                 case 2400:
819                 case CBR_2400:
820                         port.c_cflag |= B2400;
821                         break;          
822                 case 4800:
823                 case CBR_4800:
824                         port.c_cflag |= B4800;
825                         break;          
826                 case 9600:
827                 case CBR_9600:
828                         port.c_cflag |= B9600;
829                         break;          
830                 case 19200:
831                 case CBR_19200:
832                         port.c_cflag |= B19200;
833                         break;          
834                 case 38400:
835                 case CBR_38400:
836                         port.c_cflag |= B38400;
837                         break;          
838 #ifdef B57600
839                 case 57600:
840                         port.c_cflag |= B57600;
841                         break;          
842 #endif
843 #ifdef B115200
844                 case 57601:
845                         port.c_cflag |= B115200;
846                         break;          
847 #endif
848                 default:
849                         ptr->commerror = IE_BAUDRATE;
850                         return -1;
851         }
852 #elif !defined(__EMX__)
853         switch (lpdcb->BaudRate) {
854                 case 110:
855                 case CBR_110:
856                         port.c_ospeed = B110;
857                         break;
858                 case 300:
859                 case CBR_300:
860                         port.c_ospeed = B300;
861                         break;
862                 case 600:
863                 case CBR_600:
864                         port.c_ospeed = B600;
865                         break;
866                 case 1200:
867                 case CBR_1200:
868                         port.c_ospeed = B1200;
869                         break;
870                 case 2400:
871                 case CBR_2400:
872                         port.c_ospeed = B2400;
873                         break;
874                 case 4800:
875                 case CBR_4800:
876                         port.c_ospeed = B4800;
877                         break;
878                 case 9600:
879                 case CBR_9600:
880                         port.c_ospeed = B9600;
881                         break;
882                 case 19200:
883                 case CBR_19200:
884                         port.c_ospeed = B19200;
885                         break;
886                 case 38400:
887                 case CBR_38400:
888                         port.c_ospeed = B38400;
889                         break;
890                 default:
891                         ptr->commerror = IE_BAUDRATE;
892                         return -1;
893         }
894         port.c_ispeed = port.c_ospeed;
895 #endif
896         TRACE(comm,"bytesize %d\n",lpdcb->ByteSize);
897         port.c_cflag &= ~CSIZE;
898         switch (lpdcb->ByteSize) {
899                 case 5:
900                         port.c_cflag |= CS5;
901                         break;
902                 case 6:
903                         port.c_cflag |= CS6;
904                         break;
905                 case 7:
906                         port.c_cflag |= CS7;
907                         break;
908                 case 8:
909                         port.c_cflag |= CS8;
910                         break;
911                 default:
912                         ptr->commerror = IE_BYTESIZE;
913                         return -1;
914         }
915
916         TRACE(comm,"fParity %d Parity %d\n",lpdcb->fParity, lpdcb->Parity);
917         port.c_cflag &= ~(PARENB | PARODD);
918         if (lpdcb->fParity)
919             port.c_iflag |= INPCK;
920         else
921             port.c_iflag &= ~INPCK;
922         switch (lpdcb->Parity) {
923                 case NOPARITY:
924                         break;
925                 case ODDPARITY:
926                         port.c_cflag |= (PARENB | PARODD);
927                         break;
928                 case EVENPARITY:
929                         port.c_cflag |= PARENB;
930                         break;
931                 default:
932                         ptr->commerror = IE_BYTESIZE;
933                         return -1;
934         }
935         
936
937         TRACE(comm,"stopbits %d\n",lpdcb->StopBits);
938
939         switch (lpdcb->StopBits) {
940                 case ONESTOPBIT:
941                                 port.c_cflag &= ~CSTOPB;
942                                 break;
943                 case TWOSTOPBITS:
944                                 port.c_cflag |= CSTOPB;
945                                 break;
946                 default:
947                         ptr->commerror = IE_BYTESIZE;
948                         return -1;
949         }
950 #ifdef CRTSCTS
951
952         if (lpdcb->fDtrflow || lpdcb->fRtsflow || lpdcb->fOutxCtsFlow)
953                 port.c_cflag |= CRTSCTS;
954
955         if (lpdcb->fDtrDisable) 
956                 port.c_cflag &= ~CRTSCTS;
957 #endif  
958         if (lpdcb->fInX)
959                 port.c_iflag |= IXON;
960         else
961                 port.c_iflag &= ~IXON;
962         if (lpdcb->fOutX)
963                 port.c_iflag |= IXOFF;
964         else
965                 port.c_iflag &= ~IXOFF;
966
967         if (tcsetattr(ptr->fd, TCSADRAIN, &port) == -1) {
968                 ptr->commerror = WinError();    
969                 return FALSE;
970         } else {
971                 ptr->commerror = 0;
972                 return 0;
973         }
974 }
975
976 /*****************************************************************************
977  *      GetCommState    (USER.202)
978  */
979 INT16 WINAPI GetCommState16(INT16 cid, LPDCB16 lpdcb)
980 {
981         struct DosDeviceStruct *ptr;
982         struct termios port;
983
984         TRACE(comm,"cid %d, ptr %p\n", cid, lpdcb);
985         if ((ptr = GetDeviceStruct(cid)) == NULL) {
986                 return -1;
987         }
988         if (tcgetattr(ptr->fd, &port) == -1) {
989                 ptr->commerror = WinError();    
990                 return -1;
991         }
992         lpdcb->Id = cid;
993 #ifndef __EMX__
994 #ifdef CBAUD
995         switch (port.c_cflag & CBAUD) {
996 #else
997         switch (port.c_ospeed) {
998 #endif
999                 case B110:
1000                         lpdcb->BaudRate = 110;
1001                         break;
1002                 case B300:
1003                         lpdcb->BaudRate = 300;
1004                         break;
1005                 case B600:
1006                         lpdcb->BaudRate = 600;
1007                         break;
1008                 case B1200:
1009                         lpdcb->BaudRate = 1200;
1010                         break;
1011                 case B2400:
1012                         lpdcb->BaudRate = 2400;
1013                         break;
1014                 case B4800:
1015                         lpdcb->BaudRate = 4800;
1016                         break;
1017                 case B9600:
1018                         lpdcb->BaudRate = 9600;
1019                         break;
1020                 case B19200:
1021                         lpdcb->BaudRate = 19200;
1022                         break;
1023                 case B38400:
1024                         lpdcb->BaudRate = 38400;
1025                         break;
1026 #ifdef B57600
1027                 case B57600:
1028                         lpdcb->BaudRate = 57600;
1029                         break;
1030 #endif
1031 #ifdef B115200
1032                 case B115200:
1033                         lpdcb->BaudRate = 57601;
1034                         break;
1035 #endif
1036         }
1037 #endif
1038         switch (port.c_cflag & CSIZE) {
1039                 case CS5:
1040                         lpdcb->ByteSize = 5;
1041                         break;
1042                 case CS6:
1043                         lpdcb->ByteSize = 6;
1044                         break;
1045                 case CS7:
1046                         lpdcb->ByteSize = 7;
1047                         break;
1048                 case CS8:
1049                         lpdcb->ByteSize = 8;
1050                         break;
1051         }       
1052         
1053         if(port.c_iflag & INPCK)
1054             lpdcb->fParity = TRUE;
1055         else
1056             lpdcb->fParity = FALSE;
1057         switch (port.c_cflag & (PARENB | PARODD)) {
1058                 case 0:
1059                         lpdcb->Parity = NOPARITY;
1060                         break;
1061                 case PARENB:
1062                         lpdcb->Parity = EVENPARITY;
1063                         break;
1064                 case (PARENB | PARODD):
1065                         lpdcb->Parity = ODDPARITY;              
1066                         break;
1067         }
1068
1069         if (port.c_cflag & CSTOPB)
1070                 lpdcb->StopBits = TWOSTOPBITS;
1071         else
1072                 lpdcb->StopBits = ONESTOPBIT;
1073
1074         lpdcb->RlsTimeout = 50;
1075         lpdcb->CtsTimeout = 50; 
1076         lpdcb->DsrTimeout = 50;
1077         lpdcb->fNull = 0;
1078         lpdcb->fChEvt = 0;
1079         lpdcb->fBinary = 1;
1080         lpdcb->fDtrDisable = 0;
1081
1082 #ifdef CRTSCTS
1083
1084         if (port.c_cflag & CRTSCTS) {
1085                 lpdcb->fDtrflow = 1;
1086                 lpdcb->fRtsflow = 1;
1087                 lpdcb->fOutxCtsFlow = 1;
1088                 lpdcb->fOutxDsrFlow = 1;
1089         } else 
1090 #endif
1091                 lpdcb->fDtrDisable = 1;
1092
1093         if (port.c_iflag & IXON)
1094                 lpdcb->fInX = 1;
1095         else
1096                 lpdcb->fInX = 0;
1097
1098         if (port.c_iflag & IXOFF)
1099                 lpdcb->fOutX = 1;
1100         else
1101                 lpdcb->fOutX = 0;
1102 /*
1103         lpdcb->XonChar = 
1104         lpdcb->XoffChar = 
1105  */
1106         lpdcb->XonLim = 10;
1107         lpdcb->XoffLim = 10;
1108
1109         ptr->commerror = 0;
1110         return 0;
1111 }
1112
1113 /*****************************************************************************
1114  *      TransmitCommChar        (USER.206)
1115  */
1116 INT16 WINAPI TransmitCommChar16(INT16 cid,CHAR chTransmit)
1117 {
1118         struct DosDeviceStruct *ptr;
1119
1120         TRACE(comm, "cid %d, data %d \n", cid, chTransmit);
1121         if ((ptr = GetDeviceStruct(cid)) == NULL) {
1122                 return -1;
1123         }
1124
1125         if (ptr->suspended) {
1126                 ptr->commerror = IE_HARDWARE;
1127                 return -1;
1128         }       
1129
1130         if (ptr->xmit >= 0) {
1131           /* character already queued */
1132           /* FIXME: which error would Windows return? */
1133           ptr->commerror = CE_TXFULL;
1134           return -1;
1135         }
1136
1137         if (ptr->obuf_head == ptr->obuf_tail) {
1138           /* transmit queue empty, try to transmit directly */
1139           if (write(ptr->fd, &chTransmit, 1) == -1) {
1140             /* didn't work, queue it */
1141             ptr->xmit = chTransmit;
1142           }
1143         } else {
1144           /* data in queue, let this char be transmitted next */
1145           ptr->xmit = chTransmit;
1146         }
1147
1148         ptr->commerror = 0;
1149         return 0;
1150 }
1151
1152 /*****************************************************************************
1153  *      UngetCommChar   (USER.212)
1154  */
1155 INT16 WINAPI UngetCommChar16(INT16 cid,CHAR chUnget)
1156 {
1157         struct DosDeviceStruct *ptr;
1158
1159         TRACE(comm,"cid %d (char %d)\n", cid, chUnget);
1160         if ((ptr = GetDeviceStruct(cid)) == NULL) {
1161                 return -1;
1162         }
1163
1164         if (ptr->suspended) {
1165                 ptr->commerror = IE_HARDWARE;
1166                 return -1;
1167         }       
1168
1169         if (ptr->unget>=0) {
1170           /* character already queued */
1171           /* FIXME: which error would Windows return? */
1172           ptr->commerror = CE_RXOVER;
1173           return -1;
1174         }
1175
1176         ptr->unget = chUnget;
1177
1178         ptr->commerror = 0;
1179         return 0;
1180 }
1181
1182 /*****************************************************************************
1183  *      ReadComm        (USER.204)
1184  */
1185 INT16 WINAPI ReadComm16(INT16 cid,LPSTR lpvBuf,INT16 cbRead)
1186 {
1187         int status, length;
1188         struct DosDeviceStruct *ptr;
1189         LPSTR orgBuf = lpvBuf;
1190
1191         TRACE(comm, "cid %d, ptr %p, length %d\n", cid, lpvBuf, cbRead);
1192         if ((ptr = GetDeviceStruct(cid)) == NULL) {
1193                 return -1;
1194         }
1195
1196         if (ptr->suspended) {
1197                 ptr->commerror = IE_HARDWARE;
1198                 return -1;
1199         }       
1200
1201         /* read unget character */
1202         if (ptr->unget>=0) {
1203                 *lpvBuf++ = ptr->unget;
1204                 ptr->unget = -1;
1205
1206                 length = 1;
1207         } else
1208                 length = 0;
1209
1210         /* read from receive buffer */
1211         while (length < cbRead) {
1212           status = ((ptr->ibuf_head < ptr->ibuf_tail) ?
1213                     ptr->ibuf_size : ptr->ibuf_head) - ptr->ibuf_tail;
1214           if (!status) break;
1215           if ((cbRead - length) < status)
1216             status = cbRead - length;
1217
1218           memcpy(lpvBuf, ptr->inbuf + ptr->ibuf_tail, status);
1219           ptr->ibuf_tail += status;
1220           if (ptr->ibuf_tail >= ptr->ibuf_size)
1221             ptr->ibuf_tail = 0;
1222           lpvBuf += status;
1223           length += status;
1224         }
1225
1226         TRACE(comm,"%.*s\n", length, orgBuf);
1227         ptr->commerror = 0;
1228         return length;
1229 }
1230
1231 /*****************************************************************************
1232  *      WriteComm       (USER.205)
1233  */
1234 INT16 WINAPI WriteComm16(INT16 cid, LPSTR lpvBuf, INT16 cbWrite)
1235 {
1236         int status, length;
1237         struct DosDeviceStruct *ptr;
1238
1239         TRACE(comm,"cid %d, ptr %p, length %d\n", 
1240                 cid, lpvBuf, cbWrite);
1241         if ((ptr = GetDeviceStruct(cid)) == NULL) {
1242                 return -1;
1243         }
1244
1245         if (ptr->suspended) {
1246                 ptr->commerror = IE_HARDWARE;
1247                 return -1;
1248         }       
1249         
1250         TRACE(comm,"%.*s\n", cbWrite, lpvBuf );
1251
1252         length = 0;
1253         while (length < cbWrite) {
1254           if ((ptr->obuf_head == ptr->obuf_tail) && (ptr->xmit < 0)) {
1255             /* no data queued, try to write directly */
1256             status = write(ptr->fd, lpvBuf, cbWrite - length);
1257             if (status > 0) {
1258               lpvBuf += status;
1259               length += status;
1260               continue;
1261             }
1262           }
1263           /* can't write directly, put into transmit buffer */
1264           status = ((ptr->obuf_tail > ptr->obuf_head) ?
1265                     (ptr->obuf_tail-1) : ptr->obuf_size) - ptr->obuf_head;
1266           if (!status) break;
1267           if ((cbWrite - length) < status)
1268             status = cbWrite - length;
1269           memcpy(lpvBuf, ptr->outbuf + ptr->obuf_head, status);
1270           ptr->obuf_head += status;
1271           if (ptr->obuf_head >= ptr->obuf_size)
1272             ptr->obuf_head = 0;
1273           lpvBuf += status;
1274           length += status;
1275         }
1276
1277         ptr->commerror = 0;     
1278         return length;
1279 }
1280
1281 /***********************************************************************
1282  *           EnableCommNotification   (USER.246)
1283  */
1284 BOOL16 WINAPI EnableCommNotification16( INT16 cid, HWND16 hwnd,
1285                                       INT16 cbWriteNotify, INT16 cbOutQueue )
1286 {
1287         struct DosDeviceStruct *ptr;
1288
1289         TRACE(comm, "(%d, %x, %d, %d)\n", cid, hwnd, cbWriteNotify, cbOutQueue);
1290         if ((ptr = GetDeviceStruct(cid)) == NULL) {
1291                 ptr->commerror = IE_BADID;
1292                 return -1;
1293         }
1294         ptr->wnd = hwnd;
1295         ptr->n_read = cbWriteNotify;
1296         ptr->n_write = cbOutQueue;
1297         return TRUE;
1298 }
1299
1300
1301 /**************************************************************************
1302  *         BuildCommDCBA                (KERNEL32.14)
1303  */
1304 BOOL WINAPI BuildCommDCBA(LPCSTR device,LPDCB lpdcb)
1305 {
1306         return BuildCommDCBAndTimeoutsA(device,lpdcb,NULL);
1307 }
1308
1309 /**************************************************************************
1310  *         BuildCommDCBAndTimeoutsA     (KERNEL32.15)
1311  */
1312 BOOL WINAPI BuildCommDCBAndTimeoutsA(LPCSTR device, LPDCB lpdcb,
1313                                          LPCOMMTIMEOUTS lptimeouts)
1314 {
1315         int     port;
1316         char    *ptr,*temp;
1317
1318         TRACE(comm,"(%s,%p,%p)\n",device,lpdcb,lptimeouts);
1319
1320         if (!lstrncmpiA(device,"COM",3)) {
1321                 port=device[3]-'0';
1322                 if (port--==0) {
1323                         ERR(comm,"BUG! COM0 can't exists!.\n");
1324                         return FALSE;
1325                 }
1326                 if (!ValidCOMPort(port))
1327                         return FALSE;
1328                 if (*(device+4)!=':')
1329                         return FALSE;
1330                 temp=(LPSTR)(device+5);
1331         } else
1332                 temp=(LPSTR)device;
1333
1334         memset(lpdcb, 0, sizeof(DCB)); /* initialize */
1335
1336         lpdcb->DCBlength        = sizeof(DCB);
1337         if (strchr(temp,',')) { /* old style */
1338                 DCB16   dcb16;
1339                 BOOL16  ret;
1340                 char    last=temp[strlen(temp)-1];
1341
1342                 ret=BuildCommDCB16(device,&dcb16);
1343                 if (!ret)
1344                         return FALSE;
1345                 lpdcb->BaudRate         = dcb16.BaudRate;
1346                 lpdcb->ByteSize         = dcb16.ByteSize;
1347                 lpdcb->fBinary          = dcb16.fBinary;
1348                 lpdcb->Parity           = dcb16.Parity;
1349                 lpdcb->fParity          = dcb16.fParity;
1350                 lpdcb->fNull            = dcb16.fNull;
1351                 lpdcb->StopBits         = dcb16.StopBits;
1352                 if (last == 'x') {
1353                         lpdcb->fInX             = TRUE;
1354                         lpdcb->fOutX            = TRUE;
1355                         lpdcb->fOutxCtsFlow     = FALSE;
1356                         lpdcb->fOutxDsrFlow     = FALSE;
1357                         lpdcb->fDtrControl      = DTR_CONTROL_ENABLE;
1358                         lpdcb->fRtsControl      = RTS_CONTROL_ENABLE;
1359                 } else if (last=='p') {
1360                         lpdcb->fInX             = FALSE;
1361                         lpdcb->fOutX            = FALSE;
1362                         lpdcb->fOutxCtsFlow     = TRUE;
1363                         lpdcb->fOutxDsrFlow     = TRUE;
1364                         lpdcb->fDtrControl      = DTR_CONTROL_HANDSHAKE;
1365                         lpdcb->fRtsControl      = RTS_CONTROL_HANDSHAKE;
1366                 } else {
1367                         lpdcb->fInX             = FALSE;
1368                         lpdcb->fOutX            = FALSE;
1369                         lpdcb->fOutxCtsFlow     = FALSE;
1370                         lpdcb->fOutxDsrFlow     = FALSE;
1371                         lpdcb->fDtrControl      = DTR_CONTROL_ENABLE;
1372                         lpdcb->fRtsControl      = RTS_CONTROL_ENABLE;
1373                 }
1374                 lpdcb->XonChar  = dcb16.XonChar;
1375                 lpdcb->XoffChar = dcb16.XoffChar;
1376                 lpdcb->ErrorChar= dcb16.PeChar;
1377                 lpdcb->fErrorChar= dcb16.fPeChar;
1378                 lpdcb->EofChar  = dcb16.EofChar;
1379                 lpdcb->EvtChar  = dcb16.EvtChar;
1380                 lpdcb->XonLim   = dcb16.XonLim;
1381                 lpdcb->XoffLim  = dcb16.XoffLim;
1382                 return TRUE;
1383         }
1384         ptr=strtok(temp," "); 
1385         while (ptr) {
1386                 DWORD   flag,x;
1387
1388                 flag=0;
1389                 if (!strncmp("baud=",ptr,5)) {
1390                         if (!sscanf(ptr+5,"%ld",&x))
1391                                 WARN(comm,"Couldn't parse %s\n",ptr);
1392                         lpdcb->BaudRate = x;
1393                         flag=1;
1394                 }
1395                 if (!strncmp("stop=",ptr,5)) {
1396                         if (!sscanf(ptr+5,"%ld",&x))
1397                                 WARN(comm,"Couldn't parse %s\n",ptr);
1398                         lpdcb->StopBits = x;
1399                         flag=1;
1400                 }
1401                 if (!strncmp("data=",ptr,5)) {
1402                         if (!sscanf(ptr+5,"%ld",&x))
1403                                 WARN(comm,"Couldn't parse %s\n",ptr);
1404                         lpdcb->ByteSize = x;
1405                         flag=1;
1406                 }
1407                 if (!strncmp("parity=",ptr,7)) {
1408                         lpdcb->fParity  = TRUE;
1409                         switch (ptr[8]) {
1410                         case 'N':case 'n':
1411                                 lpdcb->fParity  = FALSE;
1412                                 lpdcb->Parity   = NOPARITY;
1413                                 break;
1414                         case 'E':case 'e':
1415                                 lpdcb->Parity   = EVENPARITY;
1416                                 break;
1417                         case 'O':case 'o':
1418                                 lpdcb->Parity   = ODDPARITY;
1419                                 break;
1420                         case 'M':case 'm':
1421                                 lpdcb->Parity   = MARKPARITY;
1422                                 break;
1423                         }
1424                         flag=1;
1425                 }
1426                 if (!flag)
1427                         ERR(comm,"Unhandled specifier '%s', please report.\n",ptr);
1428                 ptr=strtok(NULL," ");
1429         }
1430         if (lpdcb->BaudRate==110)
1431                 lpdcb->StopBits = 2;
1432         return TRUE;
1433 }
1434
1435 /**************************************************************************
1436  *         BuildCommDCBAndTimeoutsW             (KERNEL32.16)
1437  */
1438 BOOL WINAPI BuildCommDCBAndTimeoutsW( LPCWSTR devid, LPDCB lpdcb,
1439                                           LPCOMMTIMEOUTS lptimeouts )
1440 {
1441         LPSTR   devidA;
1442         BOOL    ret;
1443
1444         TRACE(comm,"(%p,%p,%p)\n",devid,lpdcb,lptimeouts);
1445         devidA = HEAP_strdupWtoA( GetProcessHeap(), 0, devid );
1446         ret=BuildCommDCBAndTimeoutsA(devidA,lpdcb,lptimeouts);
1447         HeapFree( GetProcessHeap(), 0, devidA );
1448         return ret;
1449 }
1450
1451 /**************************************************************************
1452  *         BuildCommDCBW                (KERNEL32.17)
1453  */
1454 BOOL WINAPI BuildCommDCBW(LPCWSTR devid,LPDCB lpdcb)
1455 {
1456         return BuildCommDCBAndTimeoutsW(devid,lpdcb,NULL);
1457 }
1458
1459 /*****************************************************************************
1460  *      COMM_Handle2fd
1461  *  returns a file descriptor for reading from or writing to
1462  *  mode is GENERIC_READ or GENERIC_WRITE. Make sure to close
1463  *  the handle afterwards!
1464  */
1465 int COMM_Handle2fd(HANDLE handle, int mode) {
1466     struct get_read_fd_request r_req;
1467     struct get_write_fd_request w_req;
1468     int fd;
1469
1470     w_req.handle = r_req.handle = handle;
1471
1472     switch(mode) {
1473     case GENERIC_WRITE:
1474         CLIENT_SendRequest( REQ_GET_WRITE_FD, -1, 1, &w_req, sizeof(w_req) );
1475         break;
1476     case GENERIC_READ:
1477         CLIENT_SendRequest( REQ_GET_READ_FD, -1, 1, &r_req, sizeof(r_req) );
1478         break;
1479     default:
1480         ERR(comm,"COMM_Handle2fd: Don't know what type of fd is required.\n");
1481         return -1;
1482     }
1483     CLIENT_WaitReply( NULL, &fd, 0 );
1484
1485     return fd;
1486 }
1487
1488 /* FIXME: having these global for win32 for now */
1489 int commerror=0,eventmask=0;
1490
1491 /*****************************************************************************
1492  *      SetCommBreak            (KERNEL32.449)
1493  */
1494 BOOL WINAPI SetCommBreak(HANDLE handle)
1495 {
1496         FIXME(comm,"handle %d, stub!\n", handle);
1497         return TRUE;
1498 }
1499
1500 /*****************************************************************************
1501  *      ClearCommBreak          (KERNEL32.20)
1502  */
1503 BOOL WINAPI ClearCommBreak(HANDLE handle)
1504 {
1505         FIXME(comm,"handle %d, stub!\n", handle);
1506         return TRUE;
1507 }
1508
1509 /*****************************************************************************
1510  *      EscapeCommFunction      (KERNEL32.214)
1511  */
1512 BOOL WINAPI EscapeCommFunction(HANDLE handle,UINT nFunction)
1513 {
1514         int fd;
1515         struct termios  port;
1516
1517         TRACE(comm,"handle %d, function=%d\n", handle, nFunction);
1518         fd = COMM_Handle2fd(handle, GENERIC_WRITE);
1519         if(fd<0)
1520                 return FALSE;
1521
1522         if (tcgetattr(fd,&port) == -1) {
1523                 commerror=WinError();
1524                 close(fd);
1525                 return FALSE;
1526         }
1527
1528         switch (nFunction) {
1529                 case RESETDEV:
1530                         break;                                  
1531
1532 #ifdef TIOCM_DTR
1533                 case CLRDTR:
1534                         port.c_cflag &= TIOCM_DTR;
1535                         break;
1536 #endif
1537
1538 #ifdef TIOCM_RTS
1539                 case CLRRTS:
1540                         port.c_cflag &= TIOCM_RTS;
1541                         break;
1542 #endif
1543         
1544 #ifdef CRTSCTS
1545                 case SETDTR:
1546                         port.c_cflag |= CRTSCTS;
1547                         break;
1548
1549                 case SETRTS:
1550                         port.c_cflag |= CRTSCTS;
1551                         break;
1552 #endif
1553
1554                 case SETXOFF:
1555                         port.c_iflag |= IXOFF;
1556                         break;
1557
1558                 case SETXON:
1559                         port.c_iflag |= IXON;
1560                         break;
1561                 case SETBREAK:
1562                         FIXME(comm,"setbreak, stub\n");
1563 /*                      ptr->suspended = 1; */
1564                         break;
1565                 case CLRBREAK:
1566                         FIXME(comm,"clrbreak, stub\n");
1567 /*                      ptr->suspended = 0; */
1568                         break;
1569                 default:
1570                         WARN(comm,"(handle=%d,nFunction=%d): Unknown function\n", 
1571                         handle, nFunction);
1572                         break;                          
1573         }
1574         
1575         if (tcsetattr(fd, TCSADRAIN, &port) == -1) {
1576                 commerror = WinError();
1577                 close(fd);
1578                 return FALSE;   
1579         } else {
1580                 commerror = 0;
1581                 close(fd);
1582                 return TRUE;
1583         }
1584 }
1585
1586 /********************************************************************
1587  *      PurgeComm        (KERNEL32.557)
1588  */
1589 BOOL WINAPI PurgeComm( HANDLE handle, DWORD flags) 
1590 {
1591      int fd;
1592
1593      TRACE(comm,"handle %d, flags %lx\n", handle, flags);
1594
1595      fd = COMM_Handle2fd(handle, GENERIC_WRITE);
1596      if(fd<0)
1597          return FALSE;
1598
1599      /*
1600      ** not exactly sure how these are different
1601      ** Perhaps if we had our own internal queues, one flushes them
1602      ** and the other flushes the kernel's buffers.
1603      */
1604      if(flags&PURGE_TXABORT)
1605      {
1606          tcflush(fd,TCOFLUSH);
1607      }
1608      if(flags&PURGE_RXABORT)
1609      {
1610          tcflush(fd,TCIFLUSH);
1611      }
1612      if(flags&PURGE_TXCLEAR)
1613      {
1614          tcflush(fd,TCOFLUSH);
1615      }
1616      if(flags&PURGE_RXCLEAR)
1617      {
1618          tcflush(fd,TCIFLUSH);
1619      }
1620      close(fd);
1621
1622      return 1;
1623 }
1624
1625 /*****************************************************************************
1626  *      ClearCommError  (KERNEL32.21)
1627  */
1628 BOOL WINAPI ClearCommError(INT handle,LPDWORD errors,LPCOMSTAT lpStat)
1629 {
1630     int fd;
1631
1632     fd=COMM_Handle2fd(handle,GENERIC_READ);
1633     if(0>fd) 
1634     {
1635         return FALSE;
1636     }
1637
1638     if (lpStat) 
1639     {
1640         lpStat->status = 0;
1641
1642         if(ioctl(fd, TIOCOUTQ, &lpStat->cbOutQue))
1643             WARN(comm, "ioctl returned error\n");
1644
1645         if(ioctl(fd, TIOCINQ, &lpStat->cbInQue))
1646             WARN(comm, "ioctl returned error\n");
1647     }
1648
1649     close(fd);
1650
1651     TRACE(comm,"handle %d cbInQue = %ld cbOutQue = %ld\n",
1652                 handle,
1653                 lpStat->cbInQue,
1654                 lpStat->cbOutQue);
1655
1656     if(errors)
1657         *errors = 0;
1658
1659     /*
1660     ** After an asynchronous write opperation, the
1661     ** app will call ClearCommError to see if the
1662     ** results are ready yet. It waits for ERROR_IO_PENDING
1663     */
1664     commerror = ERROR_IO_PENDING;
1665
1666     return TRUE;
1667 }
1668
1669 /*****************************************************************************
1670  *      SetupComm       (KERNEL32.676)
1671  */
1672 BOOL WINAPI SetupComm( HANDLE handle, DWORD insize, DWORD outsize)
1673 {
1674     int fd;
1675
1676     FIXME(comm, "insize %ld outsize %ld unimplemented stub\n", insize, outsize);
1677     fd=COMM_Handle2fd(handle,GENERIC_WRITE);
1678     if(0>fd)
1679     {
1680         return FALSE;
1681     }
1682     close(fd);
1683     return TRUE;
1684
1685
1686 /*****************************************************************************
1687  *      GetCommMask     (KERNEL32.156)
1688  */
1689 BOOL WINAPI GetCommMask(HANDLE handle,LPDWORD evtmask)
1690 {
1691     int fd;
1692
1693     TRACE(comm, "handle %d, mask %p\n", handle, evtmask);
1694     if(0>(fd=COMM_Handle2fd(handle,GENERIC_READ))) 
1695     {
1696         return FALSE;
1697     }
1698     close(fd);
1699     *evtmask = eventmask;
1700     return TRUE;
1701 }
1702
1703 /*****************************************************************************
1704  *      SetCommMask     (KERNEL32.451)
1705  */
1706 BOOL WINAPI SetCommMask(INT handle,DWORD evtmask)
1707 {
1708     int fd;
1709
1710     TRACE(comm, "handle %d, mask %lx\n", handle, evtmask);
1711     if(0>(fd=COMM_Handle2fd(handle,GENERIC_WRITE))) {
1712         return FALSE;
1713     }
1714     close(fd);
1715     eventmask = evtmask;
1716     return TRUE;
1717 }
1718
1719 /*****************************************************************************
1720  *      SetCommState    (KERNEL32.452)
1721  */
1722 BOOL WINAPI SetCommState(INT handle,LPDCB lpdcb)
1723 {
1724      struct termios port;
1725      int fd;
1726      struct get_write_fd_request req;
1727
1728      TRACE(comm,"handle %d, ptr %p\n", handle, lpdcb);
1729
1730      req.handle = handle;
1731      CLIENT_SendRequest( REQ_GET_WRITE_FD, -1, 1, &req, sizeof(req) );
1732      CLIENT_WaitReply( NULL, &fd, 0 );
1733
1734      if(fd<0)
1735          return FALSE;
1736
1737      if (tcgetattr(fd,&port) == -1) {
1738          commerror = WinError();        
1739          return FALSE;
1740      }
1741
1742         port.c_cc[VMIN] = 0;
1743         port.c_cc[VTIME] = 1;
1744
1745 #ifdef IMAXBEL
1746         port.c_iflag &= ~(ISTRIP|BRKINT|IGNCR|ICRNL|INLCR|IMAXBEL);
1747 #else
1748         port.c_iflag &= ~(ISTRIP|BRKINT|IGNCR|ICRNL|INLCR);
1749 #endif
1750         port.c_iflag |= (IGNBRK);
1751
1752         port.c_oflag &= ~(OPOST);
1753
1754         port.c_cflag &= ~(HUPCL);
1755         port.c_cflag |= CLOCAL | CREAD;
1756
1757         port.c_lflag &= ~(ICANON|ECHO|ISIG);
1758         port.c_lflag |= NOFLSH;
1759
1760      /*
1761      ** MJM - removed default baudrate settings
1762      ** TRACE(comm,"baudrate %ld\n",lpdcb->BaudRate);
1763      */
1764 #ifdef CBAUD
1765         port.c_cflag &= ~CBAUD;
1766         switch (lpdcb->BaudRate) {
1767                 case 110:
1768                 case CBR_110:
1769                         port.c_cflag |= B110;
1770                         break;          
1771                 case 300:
1772                 case CBR_300:
1773                         port.c_cflag |= B300;
1774                         break;          
1775                 case 600:
1776                 case CBR_600:
1777                         port.c_cflag |= B600;
1778                         break;          
1779                 case 1200:
1780                 case CBR_1200:
1781                         port.c_cflag |= B1200;
1782                         break;          
1783                 case 2400:
1784                 case CBR_2400:
1785                         port.c_cflag |= B2400;
1786                         break;          
1787                 case 4800:
1788                 case CBR_4800:
1789                         port.c_cflag |= B4800;
1790                         break;          
1791                 case 9600:
1792                 case CBR_9600:
1793                         port.c_cflag |= B9600;
1794                         break;          
1795                 case 19200:
1796                 case CBR_19200:
1797                         port.c_cflag |= B19200;
1798                         break;          
1799                 case 38400:
1800                 case CBR_38400:
1801                         port.c_cflag |= B38400;
1802                         break;          
1803                 default:
1804                         commerror = IE_BAUDRATE;
1805                         return FALSE;
1806         }
1807 #elif !defined(__EMX__)
1808         switch (lpdcb->BaudRate) {
1809                 case 110:
1810                 case CBR_110:
1811                         port.c_ospeed = B110;
1812                         break;
1813                 case 300:
1814                 case CBR_300:
1815                         port.c_ospeed = B300;
1816                         break;
1817                 case 600:
1818                 case CBR_600:
1819                         port.c_ospeed = B600;
1820                         break;
1821                 case 1200:
1822                 case CBR_1200:
1823                         port.c_ospeed = B1200;
1824                         break;
1825                 case 2400:
1826                 case CBR_2400:
1827                         port.c_ospeed = B2400;
1828                         break;
1829                 case 4800:
1830                 case CBR_4800:
1831                         port.c_ospeed = B4800;
1832                         break;
1833                 case 9600:
1834                 case CBR_9600:
1835                         port.c_ospeed = B9600;
1836                         break;
1837                 case 19200:
1838                 case CBR_19200:
1839                         port.c_ospeed = B19200;
1840                         break;
1841                 case 38400:
1842                 case CBR_38400:
1843                         port.c_ospeed = B38400;
1844                         break;
1845                 default:
1846                         commerror = IE_BAUDRATE;
1847                         return FALSE;
1848         }
1849         port.c_ispeed = port.c_ospeed;
1850 #endif
1851         TRACE(comm,"bytesize %d\n",lpdcb->ByteSize);
1852         port.c_cflag &= ~CSIZE;
1853         switch (lpdcb->ByteSize) {
1854                 case 5:
1855                         port.c_cflag |= CS5;
1856                         break;
1857                 case 6:
1858                         port.c_cflag |= CS6;
1859                         break;
1860                 case 7:
1861                         port.c_cflag |= CS7;
1862                         break;
1863                 case 8:
1864                         port.c_cflag |= CS8;
1865                         break;
1866                 default:
1867                         commerror = IE_BYTESIZE;
1868                         return FALSE;
1869         }
1870
1871         TRACE(comm,"fParity %d Parity %d\n",lpdcb->fParity, lpdcb->Parity);
1872         port.c_cflag &= ~(PARENB | PARODD);
1873         if (lpdcb->fParity)
1874             port.c_iflag |= INPCK;
1875         else
1876             port.c_iflag &= ~INPCK;
1877         switch (lpdcb->Parity) {
1878                 case NOPARITY:
1879                         break;
1880                 case ODDPARITY:
1881                         port.c_cflag |= (PARENB | PARODD);
1882                         break;
1883                 case EVENPARITY:
1884                         port.c_cflag |= PARENB;
1885                         break;
1886                 default:
1887                         commerror = IE_BYTESIZE;
1888                         return FALSE;
1889         }
1890         
1891
1892         TRACE(comm,"stopbits %d\n",lpdcb->StopBits);
1893         switch (lpdcb->StopBits) {
1894                 case ONESTOPBIT:
1895                                 port.c_cflag &= ~CSTOPB;
1896                                 break;
1897                 case TWOSTOPBITS:
1898                                 port.c_cflag |= CSTOPB;
1899                                 break;
1900                 default:
1901                         commerror = IE_BYTESIZE;
1902                         return FALSE;
1903         }
1904 #ifdef CRTSCTS
1905         if (    lpdcb->fOutxCtsFlow                     ||
1906                 lpdcb->fDtrControl == DTR_CONTROL_ENABLE||
1907                 lpdcb->fRtsControl == RTS_CONTROL_ENABLE
1908         )
1909                 port.c_cflag |= CRTSCTS;
1910         if (lpdcb->fDtrControl == DTR_CONTROL_DISABLE)
1911                 port.c_cflag &= ~CRTSCTS;
1912
1913 #endif  
1914         if (lpdcb->fInX)
1915                 port.c_iflag |= IXON;
1916         else
1917                 port.c_iflag &= ~IXON;
1918         if (lpdcb->fOutX)
1919                 port.c_iflag |= IXOFF;
1920         else
1921                 port.c_iflag &= ~IXOFF;
1922
1923         if (tcsetattr(fd,TCSADRAIN,&port)==-1) {
1924                 commerror = WinError(); 
1925                 return FALSE;
1926         } else {
1927                 commerror = 0;
1928                 return TRUE;
1929         }
1930 }
1931
1932
1933 /*****************************************************************************
1934  *      GetCommState    (KERNEL32.159)
1935  */
1936 BOOL WINAPI GetCommState(INT handle, LPDCB lpdcb)
1937 {
1938      struct termios port;
1939      int fd;
1940      struct get_read_fd_request req;
1941
1942      TRACE(comm,"handle %d, ptr %p\n", handle, lpdcb);
1943      req.handle = handle;
1944      CLIENT_SendRequest( REQ_GET_READ_FD, -1, 1, &req, sizeof(req) );
1945      CLIENT_WaitReply( NULL, &fd, 0 );
1946
1947      if(fd<0)
1948          return FALSE;
1949
1950      if (tcgetattr(fd, &port) == -1) {
1951         TRACE(comm,"tcgetattr(%d, ...) returned -1",fd);
1952                 commerror = WinError(); 
1953                 return FALSE;
1954         }
1955 #ifndef __EMX__
1956 #ifdef CBAUD
1957         switch (port.c_cflag & CBAUD) {
1958 #else
1959         switch (port.c_ospeed) {
1960 #endif
1961                 case B110:
1962                         lpdcb->BaudRate = 110;
1963                         break;
1964                 case B300:
1965                         lpdcb->BaudRate = 300;
1966                         break;
1967                 case B600:
1968                         lpdcb->BaudRate = 600;
1969                         break;
1970                 case B1200:
1971                         lpdcb->BaudRate = 1200;
1972                         break;
1973                 case B2400:
1974                         lpdcb->BaudRate = 2400;
1975                         break;
1976                 case B4800:
1977                         lpdcb->BaudRate = 4800;
1978                         break;
1979                 case B9600:
1980                         lpdcb->BaudRate = 9600;
1981                         break;
1982                 case B19200:
1983                         lpdcb->BaudRate = 19200;
1984                         break;
1985                 case B38400:
1986                         lpdcb->BaudRate = 38400;
1987                         break;
1988         }
1989 #endif
1990         switch (port.c_cflag & CSIZE) {
1991                 case CS5:
1992                         lpdcb->ByteSize = 5;
1993                         break;
1994                 case CS6:
1995                         lpdcb->ByteSize = 6;
1996                         break;
1997                 case CS7:
1998                         lpdcb->ByteSize = 7;
1999                         break;
2000                 case CS8:
2001                         lpdcb->ByteSize = 8;
2002                         break;
2003         }       
2004         
2005         if(port.c_iflag & INPCK)
2006             lpdcb->fParity = TRUE;
2007         else
2008             lpdcb->fParity = FALSE;
2009         switch (port.c_cflag & (PARENB | PARODD)) {
2010                 case 0:
2011                         lpdcb->Parity = NOPARITY;
2012                         break;
2013                 case PARENB:
2014                         lpdcb->Parity = EVENPARITY;
2015                         break;
2016                 case (PARENB | PARODD):
2017                         lpdcb->Parity = ODDPARITY;              
2018                         break;
2019         }
2020
2021         if (port.c_cflag & CSTOPB)
2022                 lpdcb->StopBits = TWOSTOPBITS;
2023         else
2024                 lpdcb->StopBits = ONESTOPBIT;
2025
2026         lpdcb->fNull = 0;
2027         lpdcb->fBinary = 1;
2028
2029 #ifdef CRTSCTS
2030
2031         if (port.c_cflag & CRTSCTS) {
2032                 lpdcb->fDtrControl = DTR_CONTROL_ENABLE;
2033                 lpdcb->fRtsControl = RTS_CONTROL_ENABLE;
2034                 lpdcb->fOutxCtsFlow = 1;
2035                 lpdcb->fOutxDsrFlow = 1;
2036         } else 
2037 #endif
2038         {
2039                 lpdcb->fDtrControl = DTR_CONTROL_DISABLE;
2040                 lpdcb->fRtsControl = RTS_CONTROL_DISABLE;
2041         }
2042         if (port.c_iflag & IXON)
2043                 lpdcb->fInX = 1;
2044         else
2045                 lpdcb->fInX = 0;
2046
2047         if (port.c_iflag & IXOFF)
2048                 lpdcb->fOutX = 1;
2049         else
2050                 lpdcb->fOutX = 0;
2051 /*
2052         lpdcb->XonChar = 
2053         lpdcb->XoffChar = 
2054  */
2055         lpdcb->XonLim = 10;
2056         lpdcb->XoffLim = 10;
2057
2058         commerror = 0;
2059
2060      TRACE(comm,"OK\n");
2061  
2062         return TRUE;
2063 }
2064
2065 /*****************************************************************************
2066  *      TransmitCommChar        (KERNEL32.535)
2067  */
2068 BOOL WINAPI TransmitCommChar(INT cid,CHAR chTransmit)
2069 {
2070         struct DosDeviceStruct *ptr;
2071
2072         FIXME(comm,"(%d,'%c'), use win32 handle!\n",cid,chTransmit);
2073         if ((ptr = GetDeviceStruct(cid)) == NULL) {
2074                 return FALSE;
2075         }
2076
2077         if (ptr->suspended) {
2078                 ptr->commerror = IE_HARDWARE;
2079                 return FALSE;
2080         }
2081         if (write(ptr->fd, (void *) &chTransmit, 1) == -1) {
2082                 ptr->commerror = WinError();
2083                 return FALSE;
2084         }  else {
2085                 ptr->commerror = 0;
2086                 return TRUE;
2087         }
2088 }
2089
2090 /*****************************************************************************
2091  *      GetCommTimeouts         (KERNEL32.160)
2092  */
2093 BOOL WINAPI GetCommTimeouts(INT cid,LPCOMMTIMEOUTS lptimeouts)
2094 {
2095         FIXME(comm,"(%x,%p):stub.\n",cid,lptimeouts);
2096         return TRUE;
2097 }
2098
2099 /*****************************************************************************
2100  *      SetCommTimeouts         (KERNEL32.453)
2101  */
2102 BOOL WINAPI SetCommTimeouts(INT cid,LPCOMMTIMEOUTS lptimeouts) {
2103         FIXME(comm,"(%x,%p):stub.\n",cid,lptimeouts);
2104         return TRUE;
2105 }
2106
2107 /***********************************************************************
2108  *           GetCommModemStatus   (KERNEL32.285)
2109  */
2110 BOOL WINAPI GetCommModemStatus(HANDLE hFile,LPDWORD lpModemStat )
2111 {
2112         FIXME(comm, "(%d %p)\n",hFile,lpModemStat );
2113         return TRUE;
2114 }
2115 /***********************************************************************
2116  *           WaitCommEvent   (KERNEL32.719)
2117  */
2118 BOOL WINAPI WaitCommEvent(HANDLE hFile,LPDWORD eventmask ,LPOVERLAPPED overlapped)
2119 {
2120         FIXME(comm, "(%d %p %p )\n",hFile, eventmask,overlapped);
2121         return TRUE;
2122 }
2123
2124 /***********************************************************************
2125  *           GetCommProperties   (KERNEL32.???)
2126  */
2127 BOOL WINAPI GetCommProperties(HANDLE hFile, LPDCB *dcb)
2128 {
2129     FIXME(comm, "(%d %p )\n",hFile,dcb);
2130     return TRUE;
2131 }
2132
2133 /***********************************************************************
2134  *           SetCommProperties   (KERNEL32.???)
2135  */
2136 BOOL WINAPI SetCommProperties(HANDLE hFile, LPDCB dcb)
2137 {
2138     FIXME(comm, "(%d %p )\n",hFile,dcb);
2139     return TRUE;
2140 }
2141