/* SCCSID = "src/dev/usb/COM/COMIRQ.C, usb, c.basedd 98/10/26" */
/*
*   Licensed Material -- Property of IBM
*
*   (c) Copyright IBM Corp. 1998  All Rights Reserved
*/
/************************** START OF SPECIFICATIONS ***************************/
/*                                                                            */
/*   SOURCE FILE NAME:  COMIRQ.C                                              */
/*                                                                            */
/*   DESCRIPTIVE NAME:  USB Communication Device Class Driver                 */
/*                      interrupt processing routines                         */
/*                                                                            */
/*   FUNCTION: These routines handle the IRQ IDC calls for the                */
/*             USB Communication Device Class Driver.                         */
/*                                                                            */
/*   NOTES:                                                                   */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS:                                                            */
/*             COMirq - IRQ processing switch routine                         */
/*             MDMSetConf                                                     */
/*             EnableNotifications                                            */
/*                                                                            */
/*   EXTERNAL REFERENCES:                                                     */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer      Comment                                 */
/*  ----    --------  ----------      -------                                 */
/*          98/10/26  LR              Original developer.                     */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/

#include "usbcom.h"

static void MDMSetConf (PRP_GENIOCTL pRP_GENIOCTL);
static void NotificationService (PRP_GENIOCTL pRP_GENIOCTL);
static void ReceiveResponse (void);
static void SetLineService (PRP_GENIOCTL pRP_GENIOCTL);
static void RecDataService (PRP_GENIOCTL pRP_GENIOCTL);
static void SendDataService (PRP_GENIOCTL pRP_GENIOCTL);

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  COMirq                                           */
/*                                                                    */
/* DESCRIPTIVE NAME:  USB Communication Device Class Driver           */
/*                    IRQ procesing switch routine                    */
/*                                                                    */
/* FUNCTION:  This routine processes USB Communication Device Class   */
/*            Driver IRQ calls by calling appropriate worker routines.*/
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  COMirq                                              */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  PRP_GENIOCTL pRP_GENIOCTL                                  */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:   n/a                                                  */
/*                                                                    */
/* EFFECTS:  sets error code in pRP_GENIOCTL->rph.Status              */
/*                                                                    */
/* INTERNAL REFERENCES:  MDMSetConf                                   */
/*    ROUTINES:          NotificationService                          */
/*                       EnableNotifications                          */
/*                       SetLineService                               */
/*                       ReceiveResponse                              */
/*                       TransmitCommand                              */
/*                       RecDataService                               */
/*                       SendDataService                              */
/*                       SendData                                     */
/*                                                                    */
/* EXTERNAL REFERENCES:  ClearFeature                                 */
/*    ROUTINES:          SetSignals                                   */
/*                       SetLineCoding                                */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void COMirq (PRP_GENIOCTL pRP_GENIOCTL)
{
   USBRB FAR *pRB = (USBRB FAR *)pRP_GENIOCTL->ParmPacket;
   USHORT awakeCount;

#ifdef DEBUG
   dsPrint3 (DBG_IRQFLOW, "USBCOM: IRQ %d, S = 0x%x, s = 0x%x\r\n",
             pRB->requestData1, pRP_GENIOCTL->rph.Status, pRB->status);
#endif

   if (pRP_GENIOCTL->rph.Status & STERR && pRB->requestData1 != COM_IRQ_LINECOD) return;

   switch (pRB->requestData1)
   {
   case COM_IRQ_SETCONF: MDMSetConf (pRP_GENIOCTL); break;

   case COM_IRQ_NOTIFICATION:
      NotificationService (pRP_GENIOCTL);
      gCOM.wDataToggle ^= NOTIF_DATA_TOGGLE;
      EnableNotifications();
      break;

   case COM_IRQ_LINECOD: SetLineService (pRP_GENIOCTL); break;

   case COM_IRQ_GETRESP:
      if (pRB->buffer2Length)
      {
         gCOM.inq.wCount += pRB->buffer2Length;
         gCOM.inq.iIn = (gCOM.inq.iIn + pRB->buffer2Length) % IO_QUEUE_LENGTH;
         gCOM.wEvent |= COMEVENT_INQ;
         DevHelp_ProcRun ((ULONG)(PUCHAR)&gCOM.inq.bBuffer[0], &awakeCount);
#ifdef DEBUG
         dsPrint2 (DBG_DETAILED, "USBCOM: IRQ GetResp, L = %d, awakeC = %d\r\n",
                   pRB->buffer2Length, awakeCount);
#endif
         ReceiveResponse();
      }
      break;

   case COM_IRQ_SENDCMD:
      if (pRB->buffer2Length)
      {
         gCOM.outq.wCount -= pRB->buffer2Length;
         gCOM.outq.iOut = (gCOM.outq.iOut + pRB->buffer2Length) % IO_QUEUE_LENGTH;
         DevHelp_ProcRun ((ULONG)(PUCHAR)&gCOM.outq.bBuffer[0], &awakeCount);
#ifdef DEBUG
         dsPrint2 (DBG_DETAILED, "USBCOM: IRQ SendCmd, L = %d, awakeC = %d\r\n",
                   pRB->buffer2Length, awakeCount);
#endif
      }
      TransmitCommand();
      break;

   case COM_IRQ_RECDATA: RecDataService (pRP_GENIOCTL); break;

   case COM_IRQ_SENDATA: SendDataService (pRP_GENIOCTL); break;

   case COM_IRQ_BREAKON:  gCOM.bTxBreak = TURNON;  break;
   case COM_IRQ_BREAKOFF: gCOM.bTxBreak = TURNOFF; break;

   case COM_IRQ_SENDBYTE:
      gCOM.wDataToggle &= ~SEND_BYTE_INPROGRESS;
      gCOM.wDataToggle ^= SEND_DATA_TOGGLE;
      SendData();
      break;

   case COM_IRQ_FEATURE:
      if (gCOM.wFeature)
      {
         gCOM.wFeature = 0;
         ClearFeature();
      }
      else
      {
         SetSignals();
         SetLineCoding();
         EnableNotifications();
         ReceiveData();
      }
      break;
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  MDMSetConf                                       */
/*                                                                    */
/* DESCRIPTIVE NAME:  Modem Set Configuration worker routine          */
/*                                                                    */
/* FUNCTION:  This routine is called when "Set Configuration" I/O     */
/*            issued after device is accepted for service is          */
/*            completed.                                              */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  MDMSetConf                                          */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  PRP_GENIOCTL pRP_GENIOCTL                                  */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:   n/a                                                  */
/*                                                                    */
/* EFFECTS:  pRP_GENIOCTL->rph.Status                                 */
/*                                                                    */
/* INTERNAL REFERENCES:  EnableNotifications                          */
/*    ROUTINES:          ReceiveData                                  */
/*                                                                    */
/* EXTERNAL REFERENCES:  COMInfoInit                                  */
/*    ROUTINES:          GetFeature                                   */
/*                       SetSignals                                   */
/*                       SetLineCoding                                */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void MDMSetConf (PRP_GENIOCTL pRP_GENIOCTL)
{
   USBRB  FAR *pRB = (USBRB FAR *)pRP_GENIOCTL->ParmPacket;
   USHORT      mdmIndex;

   for (mdmIndex = 0; mdmIndex < MAX_MDMS; mdmIndex++)
      if (gMDM[mdmIndex].active)
         if (gMDM[mdmIndex].pDeviceInfo->deviceAddress == pRB->deviceAddress)
            break;

   if (pRP_GENIOCTL->rph.Status != USB_IDC_RC_OK)  // failed to set configuration
   {
      gMDM[mdmIndex].active = TURNOFF;
      gNoOfMDMs--;
   }
   else
   {
      if (gCOM.iModem >= MAX_MDMS)
      {
#ifdef DEBUG
   dsPrint1 (DBG_IRQFLOW, "USBCOM: IRQ SetConf mdmIndex = %d\r\n", mdmIndex);
#endif
         COMInfoInit();
         gCOM.iModem = (UCHAR)mdmIndex;
         if (gMDM[gCOM.iModem].acmCapabilities & COMM_FEATURE_SUPPORT) GetFeature();
         else
         {
            SetSignals();
            SetLineCoding();
            EnableNotifications();
            ReceiveData();
         }
      }
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  EnableNotifications                              */
/*                                                                    */
/* DESCRIPTIVE NAME:  Enable Notifications from modem                 */
/*                                                                    */
/* FUNCTION:  This routine opens modem interrupt pipe to              */
/*            receive notifications.                                  */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  EnableNotifications                                 */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  PRP_GENIOCTL pRP_GENIOCTL                                  */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:   n/a                                                  */
/*                                                                    */
/* EFFECTS:  pRP_GENIOCTL->rph.Status                                 */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  USBCallIDC                                   */
/*    ROUTINES:          GetDS                                        */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void EnableNotifications (void)
{
   USBRB       rb;
   RP_GENIOCTL rp;

   rb.controllerId = gMDM[gCOM.iModem].pDeviceInfo->ctrlID;
   rb.deviceAddress = gMDM[gCOM.iModem].pDeviceInfo->deviceAddress;
   rb.endPointId = gMDM[gCOM.iModem].bIntEndpoint;
   rb.status = 0;
   rb.flags = USRB_FLAGS_TTYPE_IN | USRB_FLAGS_DET_INTRPT;
   if (gCOM.wDataToggle & NOTIF_DATA_TOGGLE) rb.flags |= USRB_FLAGS_DET_DTGGLEON;
   rb.buffer1 = gCOM.bRecNotif;
   rb.buffer1Length = MAX_NOTIF_LEN;
   rb.buffer2 = 0;
   rb.buffer2Length = 0;
   rb.serviceTime = USB_DEFAULT_SRV_INTV;
   rb.maxPacketSize = USB_DEFAULT_PKT_SIZE;
   rb.maxErrorCount = USB_MAX_ERROR_COUNT;
   rb.usbIDC = (PUSBIDCEntry)COMidc;
   rb.usbDS = GetDS();
   rb.category = USB_IDC_CATEGORY_CLASS;
   rb.requestData1 = COM_IRQ_NOTIFICATION;
   rb.requestData2 = 0;
   rb.requestData3 = 0;

   setmem((PSZ)&rp, 0, sizeof(rp));
   rp.rph.Cmd = CMDGenIOCTL;
   rp.Category = USB_IDC_CATEGORY_USBD;
   rp.Function = USB_IDC_FUNCTION_ACCIO;
   rp.ParmPacket = (PVOID)&rb;

   USBCallIDC (gpUSBDIDC, gdsUSBDIDC, (RP_GENIOCTL FAR *)&rp);

#ifdef DEBUG
   dsPrint1 (DBG_IRQFLOW, "USBCOM: EnableNotifications S = 0x%x\r\n", rp.rph.Status);
#endif
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME: NotificationService                               */
/*                                                                    */
/* DESCRIPTIVE NAME: Notification Service routine                     */
/*                                                                    */
/* FUNCTION:                                                          */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT: NotificationService                                   */
/*     LINKAGE: CALL NEAR                                             */
/*                                                                    */
/* INPUT:                                                             */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:   n/a                                                  */
/*                                                                    */
/* EFFECTS:                                                           */
/*                                                                    */
/* INTERNAL REFERENCES:  ReceiveResponse                              */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void NotificationService (PRP_GENIOCTL pRP_GENIOCTL)
{
   USHORT      length = ((USBRB FAR *)pRP_GENIOCTL->ParmPacket)->buffer1Length;
   SetupPacket *pRecNotif;
   USHORT      serialState;
   UCHAR       oldreg;

#ifdef DEBUG
   dsPrint2 (DBG_DETAILED, "USBCOM: Notif = 0x%x, L = %d\r\n", gCOM.bRecNotif[1], length);
#endif

   if (length == sizeof(SetupPacket) || length == MAX_NOTIF_LEN)
   {
      pRecNotif = (SetupPacket *)&gCOM.bRecNotif;
      switch (pRecNotif->bRequest)
      {
      case NETWORK_CONNECTION:
         // 3Com U.S.Robotics USB modem (model 5605) does not support this notification
         if (pRecNotif->wValue) gCOM.bMSR |=  MSR_DCD;
         else                   gCOM.bMSR &= ~MSR_DCD;
         break;

      case RESPONSE_AVAILABLE:
         ReceiveResponse();
         break;

      case SERIAL_STATE:
         if (length == MAX_NOTIF_LEN && pRecNotif->wLength == SERIAL_STATE_LENGTH)
         {
            serialState = MAKEUSHORT(gCOM.bRecNotif[SERIAL_STATE_INDEX],
                                     gCOM.bRecNotif[SERIAL_STATE_INDEX+1]);
#ifdef DEBUG
            dsPrint1 (DBG_DETAILED, "USBCOM: SerialState = 0x%x\r\n", serialState);
#endif
            oldreg = gCOM.bMSR;                            // Modem Status Register
            if (serialState & SERIAL_STATE_bTxCarrier)  gCOM.bMSR |=  MSR_CTS | MSR_DSR;
            else                                        gCOM.bMSR &= ~(MSR_CTS | MSR_DSR);
            if (serialState & SERIAL_STATE_bRxCarrier)  gCOM.bMSR |=  MSR_DCD;
            else                                        gCOM.bMSR &= ~MSR_DCD;
            if (serialState & SERIAL_STATE_bRingSignal) gCOM.bMSR |=  MSR_RI;
            else                                        gCOM.bMSR &= ~MSR_RI;

            if ((oldreg ^ gCOM.bMSR) & MSR_CTS) gCOM.wEvent |= COMEVENT_CTS;
            if ((oldreg ^ gCOM.bMSR) & MSR_DSR) gCOM.wEvent |= COMEVENT_DSR;
            if ((oldreg ^ gCOM.bMSR) & MSR_DCD) gCOM.wEvent |= COMEVENT_DCD;
            if ((oldreg ^ gCOM.bMSR) & MSR_RI)  gCOM.wEvent |= COMEVENT_RING;

            if (serialState & SERIAL_STATE_bBreak)
            {
               gCOM.wEvent |= COMEVENT_BREAK;
               if (gCOM.dcb.bmF2 & F2_BRK_CHAR)
               {  // Break Replacement Character Processing
                  if (gCOM.inq.iIn + 1 <= IO_QUEUE_LENGTH) gCOM.inq.bBuffer[gCOM.inq.iIn] = gCOM.dcb.bBreak;
                  else                                     gCOM.inq.bBuffer[0] = gCOM.dcb.bBreak;
                  gCOM.inq.wCount++;
                  gCOM.inq.iIn = (gCOM.inq.iIn + 1) % IO_QUEUE_LENGTH;
                  gCOM.wEvent |= COMEVENT_INQ;
               }
            }
            if (serialState & SERIAL_STATE_bFraming) gCOM.wError |= COMERR_FRAMING;
            if (serialState & SERIAL_STATE_bParity)  gCOM.wError |= COMERR_PARITY;
            if (serialState & SERIAL_STATE_bOverRun) gCOM.wError |= COMERR_OVERRUN;

            if (gCOM.wError & COMERR_MASK)
            {
               gCOM.wEvent |= COMEVENT_ERROR;
               if (gCOM.dcb.bmF2 & F2_ERR_CHAR)
               {  // Error Replacement Character Processing
                  if (gCOM.inq.iIn + 1 <= IO_QUEUE_LENGTH) gCOM.inq.bBuffer[gCOM.inq.iIn] = gCOM.dcb.bError;
                  else                                     gCOM.inq.bBuffer[0] = gCOM.dcb.bError;
                  gCOM.inq.wCount++;
                  gCOM.inq.iIn = (gCOM.inq.iIn + 1) % IO_QUEUE_LENGTH;
                  gCOM.wEvent |= COMEVENT_INQ;
               }
            }
         }
         break;
      }
   }
}

/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  ReceiveResponse                                  */
/*                                                                    */
/* DESCRIPTIVE NAME:  Receive Response                                */
/*                                                                    */
/* FUNCTION:                                                          */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  ReceiveResponse                                      */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:                                                             */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS:                                                           */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  GetResponse                                  */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/

void ReceiveResponse (void)
{
   USHORT length;

   while (IO_QUEUE_LENGTH - gCOM.inq.wCount < COM_RESPONSE_LENGTH)
   {
#ifdef DEBUG
      dsPrint3 (DBG_SPECIFIC, "USBCOM: ReceiveResponse Block = %d, iIn = %d, iOut = %d\r\n",
                gCOM.inq.wCount, gCOM.inq.iIn, gCOM.inq.iOut);
#endif
      gCOM.wError |= COMERR_QOVERRUN;
      gCOM.wEvent |= COMEVENT_ERROR;
      if (gCOM.dcb.bmF2 & F2_ERR_CHAR)
      {  // Error Replacement Character Processing
         gCOM.inq.bBuffer[gCOM.inq.iIn-1] = gCOM.dcb.bError;
         gCOM.wEvent |= COMEVENT_INQ;
      }
      DevHelp_ProcBlock ((ULONG)(PUCHAR)&gCOM.inq.bBuffer[IO_QUEUE_LENGTH-1], -1, WAIT_IS_INTERRUPTABLE);
   }
   if (gCOM.inq.iIn >= gCOM.inq.iOut)
      length = (IO_QUEUE_LENGTH - gCOM.inq.iIn >= COM_RESPONSE_LENGTH)?
               COM_RESPONSE_LENGTH : IO_QUEUE_LENGTH - gCOM.inq.iIn;
   else
      length = (gCOM.inq.iOut - gCOM.inq.iIn >= COM_RESPONSE_LENGTH)?
               COM_RESPONSE_LENGTH : gCOM.inq.iOut - gCOM.inq.iIn;
   GetResponse (length);
}
/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  TransmitCommand                                  */
/*                                                                    */
/* DESCRIPTIVE NAME:  Transmit Command                                */
/*                                                                    */
/* FUNCTION:                                                          */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  TransmitCommand                                      */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:                                                             */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS:                                                           */
/*                                                                    */
/* INTERNAL REFERENCES:  None                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  SendCommand                                  */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/

void TransmitCommand (void)
{
   USHORT length;

   if (gCOM.outq.wCount)
   {
      if (gCOM.outq.iOut >= gCOM.outq.iIn)
         length = (IO_QUEUE_LENGTH - gCOM.outq.iOut >= COM_COMMAND_LENGTH)?
                  COM_COMMAND_LENGTH : IO_QUEUE_LENGTH - gCOM.outq.iOut;
      else
         length = (gCOM.outq.iIn - gCOM.outq.iOut >= COM_COMMAND_LENGTH)?
                  COM_COMMAND_LENGTH : gCOM.outq.iIn - gCOM.outq.iOut;

      SendCommand (length);
      gCOM.wDataToggle |= SEND_CMD_INPROGRESS;
   }
   else
   {
      gCOM.wDataToggle &= ~SEND_CMD_INPROGRESS;
      gCOM.wEvent |= COMEVENT_LAST;
   }
}
/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  ReceiveData                                      */
/*                                                                    */
/* DESCRIPTIVE NAME:  Receive Data from modem                         */
/*                                                                    */
/* FUNCTION:                                                          */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  ReceiveData                                         */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  PRP_GENIOCTL pRP_GENIOCTL                                  */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:   n/a                                                  */
/*                                                                    */
/* EFFECTS:  pRP_GENIOCTL->rph.Status                                 */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  USBCallIDC                                   */
/*    ROUTINES:          GetDS                                        */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void ReceiveData (void)
{
   USBRB       rb;
   RP_GENIOCTL rp;

   while ((USHORT)IO_QUEUE_LENGTH - gCOM.inq.wCount < gMDM[gCOM.iModem].wMaxInSize)
   {
#ifdef DEBUG
      dsPrint3 (DBG_SPECIFIC, "USBCOM: ReceiveDataBlock = %d, iIn = %d, iOut = %d\r\n",
                gCOM.inq.wCount, gCOM.inq.iIn, gCOM.inq.iOut);
#endif
      gCOM.wError |= COMERR_QOVERRUN;
      gCOM.wEvent |= COMEVENT_ERROR;
      if (gCOM.dcb.bmF2 & F2_ERR_CHAR)
      {  // Error Replacement Character Processing
         gCOM.inq.bBuffer[gCOM.inq.iIn-1] = gCOM.dcb.bError;
         gCOM.wEvent |= COMEVENT_INQ;
      }
      DevHelp_ProcBlock ((ULONG)(PUCHAR)&gCOM.inq.bBuffer[IO_QUEUE_LENGTH-1], -1, WAIT_IS_INTERRUPTABLE);
   }
   rb.controllerId = gMDM[gCOM.iModem].pDeviceInfo->ctrlID;
   rb.deviceAddress = gMDM[gCOM.iModem].pDeviceInfo->deviceAddress;
   rb.endPointId = gMDM[gCOM.iModem].bInEndpoint;
   rb.status = 0;
   rb.flags = USRB_FLAGS_TTYPE_IN | USRB_FLAGS_DET_BULK;
   if (gCOM.wDataToggle & RECEIVE_DATA_TOGGLE) rb.flags |= USRB_FLAGS_DET_DTGGLEON;
   rb.buffer1 = gCOM.bRecData;
   rb.buffer1Length = gMDM[gCOM.iModem].wMaxInSize;
   rb.buffer2 = 0;
   rb.buffer2Length = 0;
   rb.serviceTime = USB_DEFAULT_SRV_INTV;
   rb.maxPacketSize = USB_DEFAULT_PKT_SIZE;
   rb.maxErrorCount = USB_MAX_ERROR_COUNT;
   rb.usbIDC = (PUSBIDCEntry)COMidc;
   rb.usbDS = GetDS();
   rb.category = USB_IDC_CATEGORY_CLASS;
   rb.requestData1 = COM_IRQ_RECDATA;
   rb.requestData2 = 0;
   rb.requestData3 = 0;

   setmem((PSZ)&rp, 0, sizeof(rp));
   rp.rph.Cmd = CMDGenIOCTL;
   rp.Category = USB_IDC_CATEGORY_USBD;
   rp.Function = USB_IDC_FUNCTION_ACCIO;
   rp.ParmPacket = (PVOID)&rb;

   USBCallIDC (gpUSBDIDC, gdsUSBDIDC, (RP_GENIOCTL FAR *)&rp);

#ifdef DEBUG
   dsPrint1 (DBG_IRQFLOW, "USBCOM: ReceiveData. S = 0x%x\r\n", rp.rph.Status);
#endif
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  SendData                                         */
/*                                                                    */
/* DESCRIPTIVE NAME:  Send Data to modem                              */
/*                                                                    */
/* FUNCTION:                                                          */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  SendData                                            */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  PRP_GENIOCTL pRP_GENIOCTL                                  */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:   n/a                                                  */
/*                                                                    */
/* EFFECTS:  pRP_GENIOCTL->rph.Status                                 */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  USBCallIDC                                   */
/*    ROUTINES:          GetDS                                        */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void SendData (void)
{
   USBRB       rb;
   RP_GENIOCTL rp;
   USHORT      length;

   if (gCOM.outq.wCount && !gCOM.bTxBreak)
   {
      if (gCOM.outq.iOut >= gCOM.outq.iIn)
         length = (IO_QUEUE_LENGTH - gCOM.outq.iOut >= COM_SENDATA_LENGTH)?
                  COM_SENDATA_LENGTH : IO_QUEUE_LENGTH - gCOM.outq.iOut;
      else
         length = (gCOM.outq.iIn - gCOM.outq.iOut >= COM_SENDATA_LENGTH)?
                  COM_SENDATA_LENGTH : gCOM.outq.iIn - gCOM.outq.iOut;

      rb.controllerId = gMDM[gCOM.iModem].pDeviceInfo->ctrlID;
      rb.deviceAddress = gMDM[gCOM.iModem].pDeviceInfo->deviceAddress;
      rb.endPointId = gMDM[gCOM.iModem].bOutEndpoint;
      rb.status = 0;
      rb.flags = USRB_FLAGS_TTYPE_OUT | USRB_FLAGS_DET_BULK;
      if (gCOM.wDataToggle & SEND_DATA_TOGGLE) rb.flags |= USRB_FLAGS_DET_DTGGLEON;
      rb.buffer1 = &gCOM.outq.bBuffer[gCOM.outq.iOut];
      rb.buffer1Length = length;
      rb.buffer2 = 0;
      rb.buffer2Length = 0;
      rb.serviceTime = USB_DEFAULT_SRV_INTV;
      rb.maxPacketSize = USB_DEFAULT_PKT_SIZE;
      rb.maxErrorCount = USB_MAX_ERROR_COUNT;
      rb.usbIDC = (PUSBIDCEntry)COMidc;
      rb.usbDS = GetDS();
      rb.category = USB_IDC_CATEGORY_CLASS;
      rb.requestData1 = COM_IRQ_SENDATA;
      rb.requestData2 = 0;
      rb.requestData3 = 0;

      setmem((PSZ)&rp, 0, sizeof(rp));
      rp.rph.Cmd = CMDGenIOCTL;
      rp.Category = USB_IDC_CATEGORY_USBD;
      rp.Function = USB_IDC_FUNCTION_ACCIO;
      rp.ParmPacket = (PVOID)&rb;

      USBCallIDC (gpUSBDIDC, gdsUSBDIDC, (RP_GENIOCTL FAR *)&rp);
      gCOM.wDataToggle |= SEND_DATA_INPROGRESS;

#ifdef DEBUG
      dsPrint2 (DBG_IRQFLOW, "USBCOM: SendData, L = %d, S = 0x%x, \r\n", length, rp.rph.Status);
#endif

   }
   else
   {
      gCOM.wDataToggle &= ~SEND_DATA_INPROGRESS;
      gCOM.wEvent |= COMEVENT_LAST;
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  SendByte                                         */
/*                                                                    */
/* DESCRIPTIVE NAME:  Send Byte to modem                              */
/*                                                                    */
/* FUNCTION:                                                          */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  SendByte                                            */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:                                                             */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:   n/a                                                  */
/*                                                                    */
/* EFFECTS:                                                           */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  USBCallIDC                                   */
/*    ROUTINES:          GetDS                                        */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void SendByte (PUCHAR pByte, ULONG comIRQ)
{
   USBRB       rb;
   RP_GENIOCTL rp;

   rb.controllerId = gMDM[gCOM.iModem].pDeviceInfo->ctrlID;
   rb.deviceAddress = gMDM[gCOM.iModem].pDeviceInfo->deviceAddress;
   rb.endPointId = gMDM[gCOM.iModem].bOutEndpoint;
   rb.status = 0;
   rb.flags = USRB_FLAGS_TTYPE_OUT | USRB_FLAGS_DET_BULK;
   if (gCOM.wDataToggle & SEND_DATA_TOGGLE) rb.flags |= USRB_FLAGS_DET_DTGGLEON;
   rb.buffer1 = pByte;
   rb.buffer1Length = 1;
   rb.buffer2 = 0;
   rb.buffer2Length = 0;
   rb.serviceTime = USB_DEFAULT_SRV_INTV;
   rb.maxPacketSize = USB_DEFAULT_PKT_SIZE;
   rb.maxErrorCount = USB_MAX_ERROR_COUNT;
   rb.usbIDC = (PUSBIDCEntry)COMidc;
   rb.usbDS = GetDS();
   rb.category = USB_IDC_CATEGORY_CLASS;
   rb.requestData1 = comIRQ;
   rb.requestData2 = 0;
   rb.requestData3 = 0;

   setmem((PSZ)&rp, 0, sizeof(rp));
   rp.rph.Cmd = CMDGenIOCTL;
   rp.Category = USB_IDC_CATEGORY_USBD;
   rp.Function = USB_IDC_FUNCTION_ACCIO;
   rp.ParmPacket = (PVOID)&rb;

   USBCallIDC (gpUSBDIDC, gdsUSBDIDC, (RP_GENIOCTL FAR *)&rp);
   gCOM.wDataToggle |= SEND_DATA_INPROGRESS;

#ifdef DEBUG
   dsPrint1 (DBG_DETAILED, "USBCOM: SendByte = 0x%x\r\n", *pByte);
#endif
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  SetLineService                                   */
/*                                                                    */
/* DESCRIPTIVE NAME:  Set Line coding Service routine                 */
/*                                                                    */
/* FUNCTION:                                                          */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  SetLineService                                      */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:                                                             */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:   n/a                                                  */
/*                                                                    */
/* EFFECTS:                                                           */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  SetLineCoding                                */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void SetLineService (PRP_GENIOCTL pRP_GENIOCTL)
{
   USBRB FAR *pRB = (USBRB FAR *)pRP_GENIOCTL->ParmPacket;
   USHORT    awakeCount;

   if (gCOM.dwMinDTERate == 0)
   {
      if (pRB->status & USRB_STATUS_STALLED) gCOM.line.dwDTERate *= 2;
      else
      {
         gCOM.dwMinDTERate = gCOM.line.dwDTERate;
         gCOM.line.dwDTERate = MAX_BITRATE;
      }
      SetLineCoding();
   }
   else
   {
      if (gCOM.dwMaxDTERate == 0)
      {
         if (pRB->status & USRB_STATUS_STALLED) gCOM.line.dwDTERate /= 2;
         else
         {
            gCOM.dwMaxDTERate = gCOM.line.dwDTERate;
            gCOM.line.dwDTERate = gCOM.prevline.dwDTERate;
         }
         SetLineCoding();
      }
      else
      {
         if (gCOM.bValidLineInit == TURNOFF)
         {
            if (pRB->status & USRB_STATUS_STALLED)
               gCOM.bValidLine[gCOM.line.bCharFormat][gCOM.line.bParityType][gCOM.line.bDataBits-MIN_DATABITS] = TURNOFF;
            else
               gCOM.bValidLine[gCOM.line.bCharFormat][gCOM.line.bParityType][gCOM.line.bDataBits-MIN_DATABITS] = TURNON;

            if (--gCOM.line.bDataBits < MIN_DATABITS)
            {
               gCOM.line.bDataBits = MAX_DATABITS;
               if (++gCOM.line.bParityType > SPACE_PARITY)
               {
                  gCOM.line.bParityType = NO_PARITY;
                  if (++gCOM.line.bCharFormat > TWO_STOPBITS)
                  {
                     gCOM.bValidLineInit = TURNON;
                     gCOM.line.bDataBits = gCOM.prevline.bDataBits;
                     gCOM.line.bParityType = gCOM.prevline.bParityType;
                     gCOM.line.bCharFormat = gCOM.prevline.bCharFormat;
                  }
               }
            }
            SetLineCoding();
         }
         else
         {
            if (pRB->status & USRB_STATUS_STALLED)
            {
               gCOM.line.dwDTERate = gCOM.prevline.dwDTERate;
               gCOM.line.bDataBits = gCOM.prevline.bDataBits;
               gCOM.line.bParityType = gCOM.prevline.bParityType;
               gCOM.line.bCharFormat = gCOM.prevline.bCharFormat;
            }
            DevHelp_ProcRun ((ULONG)(PUCHAR)&gCOM.line, &awakeCount);
#ifdef DEBUG
   dsPrint1 (DBG_DETAILED, "USBCOM: SetLineService = %d\r\n", awakeCount);
#endif
         }
      }
   }
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  RecDataService                                   */
/*                                                                    */
/* DESCRIPTIVE NAME:  Receive Data Service routine                    */
/*                                                                    */
/* FUNCTION:                                                          */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  RecDataService                                      */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:                                                             */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:   n/a                                                  */
/*                                                                    */
/* EFFECTS:                                                           */
/*                                                                    */
/* INTERNAL REFERENCES:  ReceiveData                                  */
/*    ROUTINES:          SendData                                     */
/*                       SendByte                                     */
/*                                                                    */
/* EXTERNAL REFERENCES:  none                                         */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void RecDataService (PRP_GENIOCTL pRP_GENIOCTL)
{
   USBRB FAR *pRB = (USBRB FAR *)pRP_GENIOCTL->ParmPacket;
   USHORT awakeCount, index;

#ifdef DEBUG
   dsPrint1 (DBG_DETAILED, "USBCOM: RecDataService. L = %d\r\n", pRB->buffer1Length);
#endif

   if (pRB->buffer1Length && !(gCOM.wDataToggle & INPUT_FLUSH_INPROGRESS))
   {
      if (gCOM.wError & COMERR_MASK)
      {  // Error Processing
         if (gCOM.dcb.bmF2 & F2_ERR_CHAR)
         {  // Error Replacement Character Processing
            if (gCOM.wError & COMERR_FRAMING ||
                gCOM.wError & COMERR_PARITY  ||
                gCOM.wError & COMERR_OVERRUN)
               setmem (gCOM.bRecData, gCOM.dcb.bError, pRB->buffer1Length);
            else gCOM.bRecData[pRB->buffer1Length-1] = gCOM.dcb.bError;
         }
         else
         {
            if (gCOM.wError & COMERR_OVERRUN ||
                gCOM.wError & COMERR_QOVERRUN)  pRB->buffer1Length = 0;
         }
         if (pRB->buffer1Length == 0)
         {
            gCOM.wDataToggle ^= RECEIVE_DATA_TOGGLE;
            ReceiveData();
            return;
         }
      }
      else
      {
         if (gCOM.dcb.bmF2 & F2_NULL_STRIP)
         { // Null Stripping
            for (index = 0; index < pRB->buffer1Length; index++)
            {
               if (gCOM.bRecData[index] == 0)
               {
                  if (index == pRB->buffer1Length - 1) --pRB->buffer1Length;
                  else movmem (&gCOM.bRecData[index], &gCOM.bRecData[index+1],
                               --pRB->buffer1Length - index--);
               }
            }
            if (pRB->buffer1Length == 0)
            {
               gCOM.wDataToggle ^= RECEIVE_DATA_TOGGLE;
               ReceiveData();
               return;
            }
         }
         if (gCOM.dcb.bmF2 & F2_OUT_XO)
         {  // Automatic Transmit Flow Control      
            if (gCOM.wDataToggle & STOP_TRANSMIT)
            {
               for (index = 0; index < pRB->buffer1Length; index++)
               {
                  if (gCOM.bRecData[index] == gCOM.dcb.bXON)
                  {
                     gCOM.wDataToggle &= ~STOP_TRANSMIT;
                     if (index == pRB->buffer1Length - 1) --pRB->buffer1Length;
                     else movmem (&gCOM.bRecData[index], &gCOM.bRecData[index+1],
                                  --pRB->buffer1Length - index);
                     SendData();
                     break;
                  }
               }
            }
            else
            {
               for (index = 0; index < pRB->buffer1Length; index++)
               {
                  if (gCOM.bRecData[index] == gCOM.dcb.bXOFF)
                  {
                     gCOM.wDataToggle |= STOP_TRANSMIT;
                     if (index == pRB->buffer1Length - 1) --pRB->buffer1Length;
                     else movmem (&gCOM.bRecData[index], &gCOM.bRecData[index+1],
                                  --pRB->buffer1Length - index);
                     break;
                  }
               }
            }
            if (pRB->buffer1Length == 0)
            {
               gCOM.wDataToggle ^= RECEIVE_DATA_TOGGLE;
               ReceiveData();
               return;
            }
         }
      }
      if (gCOM.inq.iIn + pRB->buffer1Length <= IO_QUEUE_LENGTH)
         movmem (&gCOM.inq.bBuffer[gCOM.inq.iIn], gCOM.bRecData,
                 pRB->buffer1Length);
      else
      {
         movmem (&gCOM.inq.bBuffer[gCOM.inq.iIn], gCOM.bRecData,
                 IO_QUEUE_LENGTH - gCOM.inq.iIn);
         movmem (&gCOM.inq.bBuffer[0], &gCOM.bRecData[IO_QUEUE_LENGTH - gCOM.inq.iIn],
                 pRB->buffer1Length - (IO_QUEUE_LENGTH - gCOM.inq.iIn));
      }
      gCOM.inq.wCount += pRB->buffer1Length;
      gCOM.inq.iIn = (gCOM.inq.iIn + pRB->buffer1Length) % IO_QUEUE_LENGTH;
      gCOM.wEvent |= COMEVENT_INQ;
      DevHelp_ProcRun ((ULONG)(PUCHAR)&gCOM.inq.bBuffer[0], &awakeCount);

      if (gCOM.dcb.bmF2 & F2_IN_XO)
      {  // Automatic Receive Flow Control
         if (gCOM.inq.wCount > IN_QUEUE_HIGH)
         {
            if (!(gCOM.wDataToggle & XOFF_SENT ||
                  gCOM.bTxBreak || gCOM.wDataToggle & SEND_BYTE_INPROGRESS))
            {
               gCOM.bTxImm = gCOM.dcb.bXOFF;
               gCOM.wDataToggle |= SEND_BYTE_INPROGRESS | XOFF_SENT;
               if (!(gCOM.dcb.bmF2 & F2_FULL_DUP))                // Normal mode
                  gCOM.wDataToggle |= STOP_TRANSMIT;
               if (!(gCOM.wDataToggle & SEND_DATA_INPROGRESS))
                  SendByte (&gCOM.bTxImm, COM_IRQ_SENDBYTE);
            }
         }
         if (gCOM.inq.wCount < IN_QUEUE_LOW)
         {
            if (gCOM.wDataToggle & XOFF_SENT &&
                !(gCOM.bTxBreak || gCOM.wDataToggle & SEND_BYTE_INPROGRESS))
            {
               gCOM.bTxImm = gCOM.dcb.bXON;
               gCOM.wDataToggle |= SEND_BYTE_INPROGRESS;
               gCOM.wDataToggle &= ~XOFF_SENT;
               if (!(gCOM.dcb.bmF2 & F2_FULL_DUP))                // Normal mode
                  gCOM.wDataToggle &= ~STOP_TRANSMIT;
               if (!(gCOM.wDataToggle & SEND_DATA_INPROGRESS))
                  SendByte (&gCOM.bTxImm, COM_IRQ_SENDBYTE);
            }
         }
      }
   }
   gCOM.wDataToggle ^= RECEIVE_DATA_TOGGLE;
   ReceiveData();
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  SendDataService                                  */
/*                                                                    */
/* DESCRIPTIVE NAME:  Send Data Service routine                       */
/*                                                                    */
/* FUNCTION:                                                          */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  SendDataService                                     */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:                                                             */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:   n/a                                                  */
/*                                                                    */
/* EFFECTS:                                                           */
/*                                                                    */
/* INTERNAL REFERENCES:  SendByte                                     */
/*    ROUTINES:          SendData                                     */
/*                                                                    */
/* EXTERNAL REFERENCES:                                               */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void SendDataService (PRP_GENIOCTL pRP_GENIOCTL)
{
   USBRB FAR *pRB = (USBRB FAR *)pRP_GENIOCTL->ParmPacket;
   USHORT awakeCount;

   if (pRB->buffer1Length && !(gCOM.wDataToggle & OUTPUT_FLUSH_INPROGRESS))
   {
      gCOM.outq.wCount -= pRB->buffer1Length;
      gCOM.outq.iOut = (gCOM.outq.iOut + pRB->buffer1Length) % IO_QUEUE_LENGTH;
      DevHelp_ProcRun ((ULONG)(PUCHAR)&gCOM.outq.bBuffer[0], &awakeCount);
#ifdef DEBUG
      dsPrint2 (DBG_DETAILED, "USBCOM: SendDataService, L = %d, awakeC = %d\r\n",
                pRB->buffer1Length, awakeCount);
#endif
   }
   gCOM.wDataToggle ^= SEND_DATA_TOGGLE;
   if (gCOM.wDataToggle & SEND_BYTE_INPROGRESS)  SendByte (&gCOM.bTxImm, COM_IRQ_SENDBYTE);
   else if (!(gCOM.wDataToggle & STOP_TRANSMIT)) SendData();
}

