Implemented async comm buffers (but probably not bugfree), and along
with them, the functionality of async event notifications, mong them
EnableCommNotification. Removed a previous hack that was faking the
buffers, since we now have real buffers...
diff --git a/include/comm.h b/include/comm.h
index 3cd3ba4..d1c1478 100644
--- a/include/comm.h
+++ b/include/comm.h
@@ -7,13 +7,14 @@
char *devicename; /* /dev/cua1 */
int fd;
int suspended;
- int unget;
- int unget_byte;
+ int unget,xmit;
int baudrate;
/* events */
int commerror, eventmask;
/* buffers */
char *inbuf,*outbuf;
+ unsigned ibuf_size,ibuf_head,ibuf_tail;
+ unsigned obuf_size,obuf_head,obuf_tail;
/* notifications */
int wnd, n_read, n_write;
};
diff --git a/misc/comm.c b/misc/comm.c
index 72c77a7..940d005 100644
--- a/misc/comm.c
+++ b/misc/comm.c
@@ -3,6 +3,9 @@
*
* Copyright 1996 Marcus Meissner
*
+ * Mar 31, 1999. Ove Kåven <ovek@arcticnet.no>
+ * - Implemented buffers and EnableCommNotification.
+ *
* Mar 3, 1999. Ove Kåven <ovek@arcticnet.no>
* - Use port indices instead of unixfds for win16
* - Moved things around (separated win16 and win32 routines)
@@ -19,22 +22,6 @@
* <lawson_whitney@juno.com>
* July 6, 1998. Fixes and comments by Valentijn Sessink
* <vsessink@ic.uva.nl> [V]
- * I only quick-fixed an error for the output buffers. The thing is this: if a
- * WinApp starts using serial ports, it calls OpenComm, asking it to open two
- * buffers, cbInQueue and cbOutQueue size, to hold data to/from the serial
- * ports. Wine OpenComm only returns "OK". Now the kernel buffer size for
- * serial communication is only 4096 bytes large. Error: (App asks for
- * a 104,000 bytes size buffer, Wine returns "OK", App asks "How many char's
- * are in the buffer", Wine returns "4000" and App thinks "OK, another
- * 100,000 chars left, good!")
- * The solution below is a bad but working quickfix for the transmit buffer:
- * the cbInQueue is saved in a variable; when the program asks how many chars
- * there are in the buffer, GetCommError returns # in buffer PLUS
- * the additional (cbOutQeueu - 4096), which leaves the application thinking
- * "wow, almost full".
- * Sorry for the rather chatty explanation - but I think comm.c needs to be
- * redefined with real working buffers make it work; maybe these comments are
- * of help.
* Oktober 98, Rein Klazes [RHK]
* A program that wants to monitor the modem status line (RLSD/DCD) may
* poll the modem status register in the commMask structure. I update the bit
@@ -84,14 +71,6 @@
#endif
#define COMM_MSR_OFFSET 35 /* see knowledge base Q101417 */
-/*
- * [V] If above globals are wrong, the one below will be wrong as well. It
- * should probably be in the DosDeviceStruct on per port basis too.
-*/
-int iGlobalOutQueueFiller;
-
-#define SERIAL_XMIT_SIZE 4096
-
struct DosDeviceStruct COM[MAX_PORTS];
struct DosDeviceStruct LPT[MAX_PORTS];
/* pointers to unknown(==undocumented) comm structure */
@@ -221,16 +200,83 @@
}
}
-static void WINE_UNUSED comm_notification(int fd,void*private)
+static unsigned comm_inbuf(struct DosDeviceStruct *ptr)
{
- /* in here, we need to:
- 1. read any data from the comm port
- 2. save it into our own internal buffers
- (we need our own buffers to implement notifications properly!)
- 3. write data from our own internal buffers to the comm port
- 4. if wnd is set, send WM_COMMNOTIFY (using PostMessage) when
- thresholds set by EnableCommNotification are passed */
-
+ return ((ptr->ibuf_tail > ptr->ibuf_head) ? ptr->ibuf_size : 0)
+ + ptr->ibuf_head - ptr->ibuf_tail;
+}
+
+static unsigned comm_outbuf(struct DosDeviceStruct *ptr)
+{
+ return ((ptr->obuf_tail > ptr->obuf_head) ? ptr->obuf_size : 0)
+ + ptr->obuf_head - ptr->obuf_tail;
+}
+
+static void comm_notification(int fd,void*private)
+{
+ struct DosDeviceStruct *ptr = (struct DosDeviceStruct *)private;
+ int prev, bleft, len;
+ WORD mask = 0;
+ int cid = GetCommPort_fd(fd);
+
+ /* read data from comm port */
+ prev = comm_inbuf(ptr);
+ do {
+ bleft = ((ptr->ibuf_tail > ptr->ibuf_head) ? (ptr->ibuf_tail-1) : ptr->ibuf_size)
+ - ptr->ibuf_head;
+ len = read(fd, ptr->inbuf + ptr->ibuf_head, bleft?bleft:1);
+ if (len > 0) {
+ if (!bleft) {
+ ptr->commerror = CE_RXOVER;
+ } else {
+ ptr->ibuf_head += len;
+ if (ptr->ibuf_head >= ptr->ibuf_size)
+ ptr->ibuf_head = 0;
+ /* flag event */
+ if (ptr->eventmask & EV_RXCHAR)
+ *(WORD*)(unknown[cid]) |= EV_RXCHAR;
+ /* FIXME: check for event character (EV_RXFLAG) */
+ }
+ }
+ } while (len > 0);
+ /* check for notification */
+ if (ptr->wnd && (ptr->n_read>0) && (prev<ptr->n_read) &&
+ (comm_inbuf(ptr)>=ptr->n_read)) {
+ /* passed the receive notification threshold */
+ mask |= CN_RECEIVE;
+ }
+
+ /* write any TransmitCommChar character */
+ if (ptr->xmit>=0) {
+ len = write(fd, &(ptr->xmit), 1);
+ if (len > 0) ptr->xmit = -1;
+ }
+ /* write from output queue */
+ prev = comm_outbuf(ptr);
+ do {
+ bleft = ((ptr->obuf_tail < ptr->obuf_head) ? ptr->obuf_head : ptr->obuf_size)
+ - ptr->obuf_tail;
+ len = bleft ? write(fd, ptr->outbuf + ptr->obuf_tail, bleft) : 0;
+ if (len > 0) {
+ ptr->obuf_tail += len;
+ if (ptr->obuf_tail >= ptr->obuf_size)
+ ptr->obuf_tail = 0;
+ /* flag event */
+ if ((ptr->obuf_tail == ptr->obuf_head) && (ptr->eventmask & EV_TXEMPTY))
+ *(WORD*)(unknown[cid]) |= EV_TXEMPTY;
+ }
+ } while (len > 0);
+ /* check for notification */
+ if (ptr->wnd && (ptr->n_write>0) && (prev>=ptr->n_write) &&
+ (comm_outbuf(ptr)<ptr->n_write)) {
+ /* passed the transmit notification threshold */
+ mask |= CN_TRANSMIT;
+ }
+
+ /* send notifications, if any */
+ if (ptr->wnd && mask) {
+ PostMessage16(ptr->wnd, WM_COMMNOTIFY, cid, mask);
+ }
}
/**************************************************************************
@@ -340,10 +386,6 @@
ERR(comm, "BUG ! COM0 doesn't exist !\n");
}
- /* to help GetCommError return left buffsize [V] */
- iGlobalOutQueueFiller = (cbOutQueue - SERIAL_XMIT_SIZE);
- if (iGlobalOutQueueFiller < 0) iGlobalOutQueueFiller = 0;
-
TRACE(comm, "%s = %s\n", device, COM[port].devicename);
if (!ValidCOMPort(port)) {
@@ -374,12 +416,30 @@
*/
SetCommState16( &dcb);
}
-#if 0
+ /* init priority characters */
+ COM[port].unget = -1;
+ COM[port].xmit = -1;
/* allocate buffers */
- /* ... */
+ COM[port].ibuf_size = cbInQueue;
+ COM[port].ibuf_head = COM[port].ibuf_tail= 0;
+ COM[port].obuf_size = cbOutQueue;
+ COM[port].obuf_head = COM[port].obuf_tail = 0;
+
+ COM[port].inbuf = malloc(cbInQueue);
+ if (COM[port].inbuf) {
+ COM[port].outbuf = malloc(cbOutQueue);
+ if (!COM[port].outbuf)
+ free(COM[port].inbuf);
+ } else COM[port].outbuf = NULL;
+ if (!COM[port].outbuf) {
+ /* not enough memory */
+ tcsetattr(COM[port].fd,TCSANOW,&m_stat[port]);
+ close(COM[port].fd);
+ return IE_MEMORY;
+ }
+
/* enable async notifications */
ASYNC_RegisterFD(COM[port].fd,comm_notification,&COM[port]);
-#endif
return port;
}
}
@@ -425,12 +485,13 @@
if (!(cid&0x80)) {
/* COM port */
SEGPTR_FREE(unknown[cid]); /* [LW] */
-#if 0
+
/* disable async notifications */
ASYNC_UnregisterFD(COM[cid].fd,comm_notification);
/* free buffers */
- /* ... */
-#endif
+ free(ptr->outbuf);
+ free(ptr->inbuf);
+
/* reset modem lines */
tcsetattr(ptr->fd,TCSANOW,&m_stat[cid]);
}
@@ -572,13 +633,18 @@
return -1;
}
switch (fnQueue) {
- case 0: queue = TCOFLUSH;
- break;
- case 1: queue = TCIFLUSH;
- break;
- default:WARN(comm,"(cid=%d,fnQueue=%d):Unknown queue\n",
- cid, fnQueue);
- return -1;
+ case 0:
+ queue = TCOFLUSH;
+ ptr->obuf_tail = ptr->obuf_head;
+ break;
+ case 1:
+ queue = TCIFLUSH;
+ ptr->ibuf_head = ptr->ibuf_tail;
+ break;
+ default:
+ WARN(comm,"(cid=%d,fnQueue=%d):Unknown queue\n",
+ cid, fnQueue);
+ return -1;
}
if (tcflush(ptr->fd, queue)) {
ptr->commerror = WinError();
@@ -595,8 +661,6 @@
INT16 WINAPI GetCommError16(INT16 cid,LPCOMSTAT16 lpStat)
{
int temperror;
- unsigned long cnt;
- int rc;
struct DosDeviceStruct *ptr;
unsigned char *stol;
unsigned int mstat;
@@ -618,13 +682,8 @@
if (lpStat) {
lpStat->status = 0;
- rc = ioctl(ptr->fd, TIOCOUTQ, &cnt);
- if (rc) WARN(comm, "Error !\n");
- lpStat->cbOutQue = cnt + iGlobalOutQueueFiller;
-
- rc = ioctl(ptr->fd, TIOCINQ, &cnt);
- if (rc) WARN(comm, "Error !\n");
- lpStat->cbInQue = cnt;
+ lpStat->cbOutQue = comm_outbuf(ptr);
+ lpStat->cbInQue = comm_inbuf(ptr);
TRACE(comm, "cid %d, error %d, lpStat %d %d %d stol %x\n",
cid, ptr->commerror, lpStat->status, lpStat->cbInQue,
@@ -654,7 +713,7 @@
if ((ptr = GetDeviceStruct(cid)) == NULL) {
return -1;
}
- ptr->eventmask |= fuEvtMask;
+ ptr->eventmask = fuEvtMask;
if (cid&0x80) {
WARN(comm," cid %d not comm port\n",cid);
return SEGPTR_GET(NULL);
@@ -673,44 +732,21 @@
*/
UINT16 WINAPI GetCommEventMask16(INT16 cid,UINT16 fnEvtClear)
{
- int events = 0;
struct DosDeviceStruct *ptr;
+ WORD events;
TRACE(comm, "cid %d, mask %d\n", cid, fnEvtClear);
if ((ptr = GetDeviceStruct(cid)) == NULL) {
return -1;
}
+ if (cid&0x80) {
+ WARN(comm," cid %d not comm port\n",cid);
+ return 0;
+ }
- /*
- * Determine if any characters are available
- */
- if (fnEvtClear & EV_RXCHAR)
- {
- int rc;
- unsigned long cnt;
-
- rc = ioctl(ptr->fd, TIOCINQ, &cnt);
- if (cnt) events |= EV_RXCHAR;
-
- TRACE(comm, "rxchar %ld\n", cnt);
- }
-
- /*
- * There are other events that need to be checked for
- */
- /* TODO */
-
- TRACE(comm, "return events %d\n", events);
+ events = *(WORD*)(unknown[cid]) & fnEvtClear;
+ *(WORD*)(unknown[cid]) &= ~fnEvtClear;
return events;
-
- /*
- * [RER] The following was gibberish
- */
-#if 0
- tempmask = ptr->eventmask;
- ptr->eventmask &= ~fnEvtClear;
- return ptr->eventmask;
-#endif
}
/*****************************************************************************
@@ -1080,13 +1116,26 @@
return -1;
}
- if (write(ptr->fd, (void *) &chTransmit, 1) == -1) {
- ptr->commerror = WinError();
- return -1;
- } else {
- ptr->commerror = 0;
- return 0;
+ if (ptr->xmit >= 0) {
+ /* character already queued */
+ /* FIXME: which error would Windows return? */
+ ptr->commerror = CE_TXFULL;
+ return -1;
}
+
+ if (ptr->obuf_head == ptr->obuf_tail) {
+ /* transmit queue empty, try to transmit directly */
+ if (write(ptr->fd, &chTransmit, 1) == -1) {
+ /* didn't work, queue it */
+ ptr->xmit = chTransmit;
+ }
+ } else {
+ /* data in queue, let this char be transmitted next */
+ ptr->xmit = chTransmit;
+ }
+
+ ptr->commerror = 0;
+ return 0;
}
/*****************************************************************************
@@ -1106,8 +1155,15 @@
return -1;
}
- ptr->unget = 1;
- ptr->unget_byte = chUnget;
+ if (ptr->unget>=0) {
+ /* character already queued */
+ /* FIXME: which error would Windows return? */
+ ptr->commerror = CE_RXOVER;
+ return -1;
+ }
+
+ ptr->unget = chUnget;
+
ptr->commerror = 0;
return 0;
}
@@ -1119,6 +1175,7 @@
{
int status, length;
struct DosDeviceStruct *ptr;
+ LPSTR orgBuf = lpvBuf;
TRACE(comm, "cid %d, ptr %p, length %d\n", cid, lpvBuf, cbRead);
if ((ptr = GetDeviceStruct(cid)) == NULL) {
@@ -1130,30 +1187,34 @@
return -1;
}
- if (ptr->unget) {
- *lpvBuf = ptr->unget_byte;
- lpvBuf++;
- ptr->unget = 0;
+ /* read unget character */
+ if (ptr->unget>=0) {
+ *lpvBuf++ = ptr->unget;
+ ptr->unget = -1;
length = 1;
} else
length = 0;
- status = read(ptr->fd, (void *) lpvBuf, cbRead);
+ /* read from receive buffer */
+ while (length < cbRead) {
+ status = ((ptr->ibuf_head < ptr->ibuf_tail) ?
+ ptr->ibuf_size : ptr->ibuf_head) - ptr->ibuf_tail;
+ if (!status) break;
+ if ((cbRead - length) < status)
+ status = cbRead - length;
- if (status == -1) {
- if (errno != EAGAIN) {
- ptr->commerror = WinError();
- return -1 - length;
- } else {
- ptr->commerror = 0;
- return length;
- }
- } else {
- TRACE(comm,"%.*s\n", length+status, lpvBuf);
- ptr->commerror = 0;
- return length + status;
+ memcpy(lpvBuf, ptr->inbuf + ptr->ibuf_tail, status);
+ ptr->ibuf_tail += status;
+ if (ptr->ibuf_tail >= ptr->ibuf_size)
+ ptr->ibuf_tail = 0;
+ lpvBuf += status;
+ length += status;
}
+
+ TRACE(comm,"%.*s\n", length, orgBuf);
+ ptr->commerror = 0;
+ return length;
}
/*****************************************************************************
@@ -1161,7 +1222,7 @@
*/
INT16 WINAPI WriteComm16(INT16 cid, LPSTR lpvBuf, INT16 cbWrite)
{
- int length;
+ int status, length;
struct DosDeviceStruct *ptr;
TRACE(comm,"cid %d, ptr %p, length %d\n",
@@ -1176,15 +1237,34 @@
}
TRACE(comm,"%.*s\n", cbWrite, lpvBuf );
- length = write(ptr->fd, (void *) lpvBuf, cbWrite);
-
- if (length == -1) {
- ptr->commerror = WinError();
- return -1;
- } else {
- ptr->commerror = 0;
- return length;
+
+ length = 0;
+ while (length < cbWrite) {
+ if ((ptr->obuf_head == ptr->obuf_tail) && (ptr->xmit < 0)) {
+ /* no data queued, try to write directly */
+ status = write(ptr->fd, lpvBuf, cbWrite - length);
+ if (status > 0) {
+ lpvBuf += status;
+ length += status;
+ continue;
+ }
+ }
+ /* can't write directly, put into transmit buffer */
+ status = ((ptr->obuf_tail > ptr->obuf_head) ?
+ (ptr->obuf_tail-1) : ptr->obuf_size) - ptr->obuf_head;
+ if (!status) break;
+ if ((cbWrite - length) < status)
+ status = cbWrite - length;
+ memcpy(lpvBuf, ptr->outbuf + ptr->obuf_head, status);
+ ptr->obuf_head += status;
+ if (ptr->obuf_head >= ptr->obuf_size)
+ ptr->obuf_head = 0;
+ lpvBuf += status;
+ length += status;
}
+
+ ptr->commerror = 0;
+ return length;
}
/***********************************************************************
@@ -1195,7 +1275,7 @@
{
struct DosDeviceStruct *ptr;
- FIXME(comm, "(%d, %x, %d, %d):stub.\n", cid, hwnd, cbWriteNotify, cbOutQueue);
+ TRACE(comm, "(%d, %x, %d, %d)\n", cid, hwnd, cbWriteNotify, cbOutQueue);
if ((ptr = GetDeviceStruct(cid)) == NULL) {
ptr->commerror = IE_BADID;
return -1;