/* SCCSID = "src/dev/usb/COM/COMIDC.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:  COMIDC.C                                              */
/*                                                                            */
/*   DESCRIPTIVE NAME:  USB Communication Device Class Driver                 */
/*                      inter-device driver communication routines            */
/*                                                                            */
/*   FUNCTION: These routines handle the PDD-PDD IDC for the                  */
/*             USB Communication Device Class Driver.                         */
/*                                                                            */
/*   NOTES:                                                                   */
/*      DEPENDENCIES: None                                                    */
/*      RESTRICTIONS: None                                                    */
/*                                                                            */
/*   ENTRY POINTS: COMidc          PDD - PDD IDC worker switch                */
/*                 MDMService                                                 */
/*                 MDMDetach                                                  */
/*                 COMInfoInit                                                */
/*                 GetFuncDescriptor                                          */
/*                                                                            */
/*   EXTERNAL REFERENCES:                                                     */
/*                                                                            */
/* Change Log                                                                 */
/*                                                                            */
/*  Mark    yy/mm/dd  Programmer      Comment                                 */
/*  ----    --------  ----------      -------                                 */
/*          98/10/26  LR              Original developer.                     */
/*                                                                            */
/**************************** END OF SPECIFICATIONS ***************************/

#include "usbcom.h"

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  COMidc                                           */
/*                                                                    */
/* DESCRIPTIVE NAME:  PDD-PDD IDC entry point and request router      */
/*                                                                    */
/* FUNCTION:  This routine is the PDD-PDD IDC entry point and         */
/*            request router. IDC function requests are routed        */
/*            to the appropriate worker routine. The address of       */
/*            this routine is returned to other device drivers via    */
/*            the DevHelp AttachDD call.                              */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  COMidc                                              */
/*    LINKAGE  :  CALL FAR                                            */
/*                                                                    */
/* INPUT:  PRP_GENIOCTL pRP_GENIOCTL                                  */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  sets error code in pRP_GENIOCTL->rph.Status              */
/*           USB_IDC_RC_PARMERR - 1) request command code is not      */
/*                                CMDGenIOCTL; 2) parameter packet    */
/*                                address is NULL;                    */
/*           USB_IDC_RC_WRONGFUNC - wrong function requested          */
/*           USB_IDC_RC_WRONGCAT - wrong request category             */
/*                                                                    */
/* INTERNAL REFERENCES:  MDMService                                   */
/*    ROUTINES:          MDMDetach                                    */
/*                                                                    */
/* EXTERNAL REFERENCES:  COMirq                                       */
/*    ROUTINES:                                                       */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void FAR COMidc (PRP_GENIOCTL pRP_GENIOCTL)
{
   USHORT status = pRP_GENIOCTL->rph.Status;

#ifdef DEBUG
   dsPrint2 (DBG_HLVLFLOW, "USBCOM: IDC C = 0x%x, F = 0x%x\r\n",
             pRP_GENIOCTL->Category, pRP_GENIOCTL->Function);
#endif

   pRP_GENIOCTL->rph.Status = USB_IDC_RC_OK;

   if (pRP_GENIOCTL->rph.Cmd != CMDGenIOCTL ||!pRP_GENIOCTL->ParmPacket)
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_PARMERR;

   if (pRP_GENIOCTL->rph.Status == USB_IDC_RC_OK)
      if (pRP_GENIOCTL->Category == USB_IDC_CATEGORY_CLASS)
         switch (pRP_GENIOCTL->Function)
         {
         case USB_IDC_FUNCTION_CHKSERV:
            MDMService (pRP_GENIOCTL);
            break;

         case USB_IDC_FUNCTION_DETDEV:
            MDMDetach (pRP_GENIOCTL);
            break;

         case USB_IDC_FUNCTION_PRCIRQ:
            pRP_GENIOCTL->rph.Status = status;
            COMirq (pRP_GENIOCTL);
            break;

         default:
            pRP_GENIOCTL->rph.Status = USB_IDC_RC_WRONGFUNC;
            break;
         }
      else pRP_GENIOCTL->rph.Status = USB_IDC_RC_WRONGCAT;

   pRP_GENIOCTL->rph.Status |= STATUS_DONE;   

#ifdef DEBUG
   dsPrint1 (DBG_HLVLFLOW, "USBCOM: IDC S = 0x%x\r\n", pRP_GENIOCTL->rph.Status);
#endif
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  MDMService                                       */
/*                                                                    */
/* DESCRIPTIVE NAME:  Check for service worker routine                */
/*                                                                    */
/* FUNCTION:  This routine is called to check device descriptor data  */
/*            COM class conformance.                                  */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  MDMService                                          */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  PRP_GENIOCTL pRP_GENIOCTL                                  */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  sets return code in  pRP_GENIOCTL->rph.Status            */
/*                                                                    */
/* INTERNAL REFERENCES:  GetFuncDescriptor                            */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  SearchConfiguration                          */
/*    ROUTINES:          GetInterruptPipeAddr                         */
/*                       GetPipeAddr                                  */
/*                       GetEndpointDPtr                              */
/*                       GetDS                                        */
/*                       USBCallIDC                                   */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void MDMService (PRP_GENIOCTL pRP_GENIOCTL)
{
   DeviceInfo FAR *pDevInfo;
   USBSetConf      setConf;
   RP_GENIOCTL     rp;
   USHORT          mdmIndex;
   USHORT          ifconf[NUMBER_COM_INTERFACES];  // high byte = interface, low byte = config

   if (gNoOfMDMs < MAX_MDMS)
   {
      for (mdmIndex = 0; mdmIndex < MAX_MDMS; mdmIndex++) if (!gMDM[mdmIndex].active) break;
   }
   else
   {
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_EXCEEDSMAX;

#ifdef DEBUG
      dsPrint1 (DBG_CRITICAL, "USBCOM: Service. gNoOfMDMs = %d \r\n", gNoOfMDMs);
#endif
      return;
   }
#ifdef DEBUG
   dsPrint1 (DBG_HLVLFLOW, "USBCOM: Service, mdmIndex = %d\r\n", mdmIndex);
#endif

   pDevInfo = ((USBCDServe FAR *)pRP_GENIOCTL->ParmPacket)->pDeviceInfo;

   if ((pDevInfo->descriptor.bDeviceClass    != DEV_CLASS_COMMUNICATIONS &&
        pDevInfo->descriptor.bDeviceClass    != DEV_CLASS_RESERVED)  || // look at the interfaces
        pDevInfo->descriptor.bDeviceSubClass != DEV_SUBCL_RESERVED   ||
        pDevInfo->descriptor.bDeviceProtocol != DEV_PROTOCOL_RESERVED)
   {
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_SERVREJCTD;
#ifdef DEBUG
      dsPrint3 (DBG_CRITICAL, "USBCOM: Device = 0x%x, 0x%x, 0x%x REJECTED.\r\n",
                pDevInfo->descriptor.bDeviceClass,
                pDevInfo->descriptor.bDeviceSubClass,
                pDevInfo->descriptor.bDeviceProtocol);
#endif
      return;
   }
   if (!(ifconf[FIRST] = SearchConfiguration ((PUCHAR)&pDevInfo->configurationData,
                                               pDevInfo->descriptor.bNumConfigurations,
                                               INTERFACE_CLASS_COM,
                                               INTERFACE_SUBCL_ACM,
                                               INTERFACE_PROTOCOL_AT)))
   {
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_SERVREJCTD;
#ifdef DEBUG
      dsPrint (DBG_CRITICAL, "USBCOM: COM interface?\r\n");
#endif
      return;
   }
   if (!(ifconf[SECOND] = SearchConfiguration ((PUCHAR)&pDevInfo->configurationData,
                                                pDevInfo->descriptor.bNumConfigurations,
                                                INTERFACE_CLASS_DATA,
                                                INTERFACE_SUBCL_RESERVED,
                                                INTERFACE_PROTOCOL_RESERVED)))
   {
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_SERVREJCTD;
#ifdef DEBUG
      dsPrint (DBG_CRITICAL, "USBCOM: Data interface?\r\n");
#endif
      return;
   }
   if (LOBYTE(ifconf[FIRST]) != LOBYTE(ifconf[SECOND]))
   {
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_SERVREJCTD;
#ifdef DEBUG
      dsPrint (DBG_CRITICAL, "USBCOM: Configs?\r\n");
#endif
      return;
   }
   /*
      Update list of modems currently connected to USB
   */
   gMDM[mdmIndex].bConfValue = LOBYTE(ifconf[FIRST]);
   gMDM[mdmIndex].bComInterface = HIBYTE(ifconf[FIRST]);
   gMDM[mdmIndex].cmCapabilities  = GetFuncDescriptor (pDevInfo->configurationData,
                                                       pDevInfo->descriptor.bNumConfigurations,
                                                       gMDM[mdmIndex].bConfValue,
                                                       CS_CALL)->bmCapabilities;
   gMDM[mdmIndex].acmCapabilities = GetFuncDescriptor (pDevInfo->configurationData,
                                                       pDevInfo->descriptor.bNumConfigurations,
                                                       gMDM[mdmIndex].bConfValue,
                                                       CS_ACM)->bmCapabilities;
   if (!(gMDM[mdmIndex].cmCapabilities & CM_DEVICE_SUPPORT) ||
       !(gMDM[mdmIndex].acmCapabilities & LINE_SUPPORT))
   {
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_SERVREJCTD;
#ifdef DEBUG
      dsPrint (DBG_CRITICAL, "USBCOM: Capabilities?\r\n");
#endif
      return;
   }
   gMDM[mdmIndex].bIntEndpoint = GetInterruptPipeAddr (pDevInfo->configurationData, 
                                                       pDevInfo->descriptor.bNumConfigurations,
                                                       gMDM[mdmIndex].bConfValue,
                                                       gMDM[mdmIndex].bComInterface);
   gMDM[mdmIndex].bDataInterface = HIBYTE(ifconf[SECOND]);
   gMDM[mdmIndex].bInEndpoint = GetPipeAddr (pDevInfo->configurationData,
                                             pDevInfo->descriptor.bNumConfigurations,
                                             gMDM[mdmIndex].bConfValue,
                                             gMDM[mdmIndex].bDataInterface, 0,
                                             DEV_ENDPT_DIRIN, DEV_ENDPT_DIRMASK, DEV_ENDPT_BULK);
   if (gMDM[mdmIndex].bInEndpoint)
   {
      gMDM[mdmIndex].wMaxInSize = GetEndpointDPtr (pDevInfo->configurationData,
                                                   pDevInfo->descriptor.bNumConfigurations,
                                                   gMDM[mdmIndex].bConfValue, 0,
                                                   gMDM[mdmIndex].bInEndpoint)->wMaxPacketSize;
      if (gMDM[mdmIndex].wMaxInSize > COM_RECDATA_LENGTH)
      {
         pRP_GENIOCTL->rph.Status = USB_IDC_RC_SERVREJCTD;
         return;
      }
   }
   else
   {
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_SERVREJCTD;
      return;
   }
   gMDM[mdmIndex].bOutEndpoint = GetPipeAddr (pDevInfo->configurationData,
                                              pDevInfo->descriptor.bNumConfigurations,
                                              gMDM[mdmIndex].bConfValue,
                                              gMDM[mdmIndex].bDataInterface, 0,
                                              DEV_ENDPT_DIROUT, DEV_ENDPT_DIRMASK, DEV_ENDPT_BULK);
   if (gMDM[mdmIndex].bOutEndpoint)
   {
      gMDM[mdmIndex].wMaxOutSize = GetEndpointDPtr (pDevInfo->configurationData,
                                                    pDevInfo->descriptor.bNumConfigurations,
                                                    gMDM[mdmIndex].bConfValue, 0,
                                                    gMDM[mdmIndex].bOutEndpoint)->wMaxPacketSize;
      if (gMDM[mdmIndex].wMaxOutSize > COM_SENDATA_LENGTH)
      {
         pRP_GENIOCTL->rph.Status = USB_IDC_RC_SERVREJCTD;
         return;
      }
   }
   else
   {
      pRP_GENIOCTL->rph.Status = USB_IDC_RC_SERVREJCTD;
      return;
   }
   gMDM[mdmIndex].pDeviceInfo = pDevInfo;
   /*
      3Com specific configuration and interface. For debugging.
      
   gMDM[mdmIndex].bConfValue = 1;
   gMDM[mdmIndex].bComInterface = 0;
   gMDM[mdmIndex].bDataInterface = 0;
   */
   gMDM[mdmIndex].active = TURNON;
   gNoOfMDMs++;
   /*
      Set Modem Configuration. The request and the request’s parameters
      are sent to the device in the setup packet.
   */
   setConf.setConfiguration = &gCOM.setPack;
   setConf.controllerId = pDevInfo->ctrlID;
   setConf.deviceAddress = pDevInfo->deviceAddress;
   setConf.classDriverDS = GetDS();
   setConf.configurationValue = gMDM[mdmIndex].bConfValue;  // desired configuration
   setConf.irqSwitchValue = COM_IRQ_SETCONF;
   setConf.category = USB_IDC_CATEGORY_CLASS;               // IRQ processor category

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

   USBCallIDC (gpUSBDIDC, gdsUSBDIDC, &rp);

   pRP_GENIOCTL->rph.Status = USB_IDC_RC_OK;
}

/******************* START OF SPECIFICATIONS **************************/
/*                                                                    */
/* SUBROUTINE NAME:  MDMDetach                                        */
/*                                                                    */
/* DESCRIPTIVE NAME:  Modem detach worker routine                     */
/*                                                                    */
/* FUNCTION:  This routine is called when USBD driver detects device  */
/*            detach condition. Modem list entry released.            */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task Time                                                 */
/*                                                                    */
/* ENTRY POINT :  MDMDetach                                           */
/*    LINKAGE  :  CALL NEAR                                           */
/*                                                                    */
/* INPUT:  PRP_GENIOCTL pRP_GENIOCTL                                  */
/*                                                                    */
/* EXIT-NORMAL:  n/a                                                  */
/*                                                                    */
/* EXIT-ERROR:  n/a                                                   */
/*                                                                    */
/* EFFECTS:  none                                                     */
/*                                                                    */
/* INTERNAL REFERENCES:  COMInfoInit                                  */
/*    ROUTINES:                                                       */
/*                                                                    */
/* EXTERNAL REFERENCES:  SetSignals                                   */
/*    ROUTINES:          SetLineCoding                                */
/*                       EnableNotifications                          */
/*                       ReceiveData                                  */
/*                       GetFeature                                   */
/*                                                                    */
/******************* END  OF  SPECIFICATIONS **************************/

void MDMDetach (PRP_GENIOCTL pRP_GENIOCTL)
{
   USBDetach FAR *pDetData = (USBDetach FAR *)pRP_GENIOCTL->ParmPacket;
   USHORT         mdmIndex;

   if (gNoOfMDMs)
      for (mdmIndex = 0; mdmIndex < MAX_MDMS; mdmIndex++)
      {
         if (!gMDM[mdmIndex].active) continue;
         if (gMDM[mdmIndex].pDeviceInfo->ctrlID == pDetData->controllerId &&
             gMDM[mdmIndex].pDeviceInfo->deviceAddress == pDetData->deviceAddress)
         {
            gMDM[mdmIndex].active = TURNOFF;
            gNoOfMDMs--;
            if (gCOM.iModem == (UCHAR)mdmIndex) gCOM.iModem = MAX_MDMS;
#ifdef DEBUG
            dsPrint1 (DBG_HLVLFLOW, "USBCOM: Detach mdmIndex = %d\r\n", mdmIndex);
#endif
            gCOM.bMSR = 0;
            break;
         }
      }
   if (gNoOfMDMs && gCOM.iModem >= MAX_MDMS)
      for (mdmIndex = 0; mdmIndex < MAX_MDMS; mdmIndex++)
         if (gMDM[mdmIndex].active)
         {
#ifdef DEBUG
            dsPrint1 (DBG_HLVLFLOW, "USBCOM: Detach. New 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();
            }
            break;
         }
}

/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  GetFuncDescriptor                                */
/*                                                                    */
/* DESCRIPTIVE NAME:  Get pointer to functional descriptor            */
/*                                                                    */
/* FUNCTION:  The function of this routine is retrieve pointer to     */
/*            required functional descriptor.                         */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  GetFuncDescriptor                                    */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:  UCHAR FAR *configurationData - far pointer to device       */
/*                      configuration data buffer                     */
/*         UCHAR bNumConfigurations - number of configurations for    */
/*                      device                                        */
/*         UCHAR configurationValue - current configuration           */
/*         UCHAR subtype - functional descriptor subtype              */
/*                                                                    */
/* EXIT-NORMAL: pointer to functional descriptor                      */
/*                                                                    */
/* EXIT-ERROR:  NULL, no functional descriptor found                  */
/*                                                                    */
/* EFFECTS: none                                                      */
/*                                                                    */
/* INTERNAL REFERENCES:                                               */
/*                                                                    */
/* EXTERNAL REFERENCES:  GetNextDescriptor                            */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/

FuncDescriptor FAR *GetFuncDescriptor (UCHAR FAR *configurationData,
                                       UCHAR bNumConfigurations,
                                       UCHAR configurationValue,
                                       UCHAR subtype)
{
   DeviceConfiguration FAR *devConf;
   UCHAR                   configIndex;
   UCHAR FAR               *currBytePtr, FAR *lastBytePtr;
   DeviceDescHead FAR      *descHead;

   devConf = (DeviceConfiguration FAR *)configurationData;
   for (configIndex=0; configIndex < bNumConfigurations; configIndex++)
   {
      currBytePtr = (UCHAR FAR *)devConf;
      lastBytePtr = currBytePtr+devConf->wTotalLength;
      descHead = (DeviceDescHead FAR *)currBytePtr+devConf->bLength;
      if (devConf->bConfigurationValue == configurationValue)
      {
         for (descHead = (DeviceDescHead FAR *)(currBytePtr+devConf->bLength); descHead;
             descHead = GetNextDescriptor (descHead, lastBytePtr))
         {
            if (descHead->bDescriptorType == CS_INTERFACE)
               if (descHead->bDescriptorSubtype == subtype)
                   return (FuncDescriptor FAR *)descHead;
         }
         break;
      }
      devConf = (DeviceConfiguration FAR *)lastBytePtr;  // point to next configuration block
   }
   return (NULL);
}

/********************** START OF SPECIFICATIONS ***********************/
/*                                                                    */
/* SUBROUTINE NAME:  COMInfoInit                                      */
/*                                                                    */
/* DESCRIPTIVE NAME: COMInfo initialization                           */
/*                                                                    */
/* FUNCTION:  The function of this routine is to initialize the       */
/*            COMInfo structure.                                      */
/*                                                                    */
/* NOTES:                                                             */
/*                                                                    */
/* CONTEXT: Initialization time                                       */
/*          Task time                                                 */
/*                                                                    */
/* ENTRY POINT:  COMInfoInit                                          */
/*    LINKAGE:  CALL NEAR                                             */
/*                                                                    */
/* INPUT:                                                             */
/*                                                                    */
/* EXIT-NORMAL: N/A                                                   */
/*                                                                    */
/* EXIT-ERROR:  N/A                                                   */
/*                                                                    */
/* EFFECTS:                                                           */
/*                                                                    */
/* INTERNAL REFERENCES:  none                                         */
/*                                                                    */
/* EXTERNAL REFERENCES:  none                                         */
/*                                                                    */
/************************ END OF SPECIFICATIONS ***********************/

void COMInfoInit (void)
{
   gCOM.line.dwDTERate    = MIN_BITRATE;
   gCOM.line.bDataBits    = DEFAULT_DATABITS;
   gCOM.line.bParityType  = DEFAULT_PARITY;
   gCOM.line.bCharFormat  = DEFAULT_STOPBITS;

   gCOM.dwMinDTERate = gCOM.dwMaxDTERate = 0;
   gCOM.wFeature = gCOM.wDataToggle = 0;
   gCOM.bMCR = gCOM.bMSR = gCOM.bTxBreak = gCOM.bValidLineInit = 0;
}

