text: /*********************************************************************
   * Microsoft Diagnostics Version 2.0
   *
   * A diagnostic utility to detect as much useful information about a
   *   customer's computer system as is possible.
   *
   * Microsoft Diagnostics:  We detect the World.
   *
   * COMINFO.C - Source file for serial port detection code.
   ********************************************************************/
  
  
  /* Include Files */
  
  #include "msd.h"
  
  
  /*********************************************************************
   * GetComInfo - Gets the COM port information.
   *
   * Returns:  TRUE if an error occured.
   *********************************************************************/
  
  BOOL GetComInfo (COM_STRUCT *pCom, BOOL fMinimumInfo)
  {
    WORD  i, wWait;           /* Looping variables                    */
    WORD  wIndex;             /* Index for strings                    */
    WORD  wPort;              /* COM port address                     */
    WORD  wLCR;               /* Line Control Register value          */
    WORD  wMSR;               /* Modem Status Register value          */
    WORD  wNewIID;            /* Used to store changed IID value      */
    DWORD dwBaudRateDivisor;  /* Used to calculate the baud rate      */
    BOOL  fArcnetCard;        /* True if the 02E0-02EF range should   */
                              /*   be avoided                         */
  
    /* 40:0 is the port address of the first valid COM port */
    WORD FAR * fwPortAddress = (WORD FAR *) 0x00400000;
  
  
    /* Determine if an Arcnet card is present at 02E0 */
    fArcnetCard = ArcnetCardPresent();
  
    /* Set the values in the structure for each port */
  
    /* Zero out the count of COM ports */
    pCom->wNmbrComPorts = 0;
  
    for (i = 0; i < MAX_COM_PORTS; ++i)
      {
        {
          /* Load the port address from the DOS Device table, if it exists */
          pCom->ComxInfo[i].wPortAddress = *(fwPortAddress++);
          wPort = pCom->ComxInfo[i].wPortAddress;
  
          /* Determine if the port exists in the right range */
  
          if (wPort >= 0x220  &&  wPort <= 0x3FF)
            {
              ++(pCom->wNmbrComPorts);
              pCom->ComxInfo[i].fComPortDetected = TRUE;
            }
          else
            pCom->ComxInfo[i].fComPortDetected = FALSE;
  
          /* If the Arcnet card is present, disable COM4 testing */
          if (fArcnetCard && wPort >= 0x2E0 && wPort <= 0x2FF)
            pCom->ComxInfo[i].fComPortDetected = FALSE;
  
          if (fMinimumInfo || !(pCom->ComxInfo[i].fComPortDetected))
            continue;
        }
  
        {
          /* Read the Line Control Register Port */
  
          wLCR = inp (wPort + 3);
  
          /* If the DLAB bit (bit 7) is 1, we can obtain the Baud Rate    */
          /*   Divisor from *fwPortAddress (LSB) and *fwPortAddress + 1   */
          /*   (MSB)                                                      */
  
          /* If the DLAB bit is not set, set it, then read the Baud Rate  */
          /*   Divisor                                                    */
  
          if (!(0x80 & wLCR))
            outp (wPort + 3, 0x80 | wLCR);
  
          /* Pause briefly for the port to respond */
          for (wWait = 0; wWait < 25; ++wWait)
            ;
  
          dwBaudRateDivisor = (WORD) (inp (wPort)) +
                              (WORD) (inp (wPort + 1) << 8);
  
          /* Pause briefly for the port to respond */
          for (wWait = 0; wWait < 25; ++wWait)
            ;
  
          /* If the DLAB bit was not set originally, put it back to it's  */
          /*   original state.                                            */
  
          if (!(0x80 & wLCR))
            outp (wPort + 3, wLCR);
        }
  
        {
          /* Calculate the BAUD rate */
  
          if (dwBaudRateDivisor == 0)
            pCom->ComxInfo[i].dwBaudRate = 0;
          else
            pCom->ComxInfo[i].dwBaudRate = 115200L / dwBaudRateDivisor;
        }
  
        {
          /* Parity - If bit 3 of the LCR register is off, parity is none. */
          /*          none.  If bit 3 is on, bits 4 and 5 come into play   */
          /*          like this:                                           */
          /*                                                               */
          /*              Bits: 5 4                                        */
          /*                    0 0 - Odd parity                           */
          /*                    0 1 - Even parity                          */
          /*                    1 0 - Mark parity                          */
          /*                    1 1 - Space parity                         */
  
          if ((wLCR & 0x08) == 0)
            pCom->ComxInfo[i].wParity = 0;
          else
            pCom->ComxInfo[i].wParity = ((wLCR & 0x30) >> 4) + 1;
  
          memset (pCom->ComxInfo[i].szParity, '\0', MAX_PARITY);
  
          wIndex = pCom->ComxInfo[i].wParity;
          strcpy (pCom->ComxInfo[i].szParity, paszParityDesc[wIndex]);
        }
  
        {
          /* Data Bits - Stored in the LCR in bits 0 and 1: */
          /*                                                */
          /*              Bits: 1 0                         */
          /*                    0 0 - 5 data bits           */
          /*                    0 1 - 6 data bits           */
          /*                    1 0 - 7 data bits           */
          /*                    1 1 - 8 data bits           */
  
          pCom->ComxInfo[i].wDataBits = (wLCR & 0x03) + 5;
        }
  
        {
          /* Stop Bits - Stored in the LCR in bit 2.  Off is 1 stop bit, */
          /*             On is two stop bits.                            */
          /*             Note:  If data bits is 5 and LCR bit 2 is on,   */
          /*                    stop bits = 1.5.                         */
  
          pCom->ComxInfo[i].wStopBits = (wLCR & 0x04) ? 2 : 1;
  
          if (pCom->ComxInfo[i].wDataBits == 5 &&
              pCom->ComxInfo[i].wStopBits == 2)
            pCom->ComxInfo[i].wStopBits = 3;
        }
  
        {
          /* Read the Modem Status Register values */
  
          wMSR = inp (wPort + 6);
        }
  
        {
          /* Carrier Detect is bit 7 of the MSR */
  
          pCom->ComxInfo[i].fCarrierDetect = (wMSR & 0x80) ? TRUE : FALSE;
        }
  
        {
          /* Ring Indicator is bit 6 of the MSR */
  
          pCom->ComxInfo[i].fRingIndicator = (wMSR & 0x40) ? TRUE : FALSE;
        }
  
        {
          /* Data Set Ready (DSR) is bit 5 of the MSR */
  
          pCom->ComxInfo[i].fDataSetReady = (wMSR & 0x20) ? TRUE : FALSE;
        }
  
        {
          /* Clear to Send is bit 4 of the MSR */
  
          pCom->ComxInfo[i].fClearToSend = (wMSR & 0x10) ? TRUE : FALSE;
        }
  
        {
          /* Determine the UART chip type.  This is accomplished by       */
          /*   outputing 11000001 to the FIFO Control Register (FCR).     */
          /*   Then, bits 6 and 7 are checked to see if they are on.  The */
          /*   list below shows how the chip type is determined:          */
          /*                                                              */
          /* Bits: 7 6                                                    */
          /*       0 0 - INS8250 UART                                     */
          /*       0 1 - Unknown                                          */
          /*       1 0 - NS16550 UART                                     */
          /*       1 1 - NS16550AN UART                                   */
          /*                                                              */
          /* This may cause problems with TSR communications programs and */
          /*   COM programs running in OS/2 or Windows while MSD runs,    */
          /*   because in order to determine the chip type, I must modify */
          /*   the behavior of the UART chip.                             */
  
          /* Turn on bits 6 and 7 */
          outp (wPort + 2, 0xC1);
  
          /* Pause for a moment to allow the port to take the change. */
  
          for (wWait = 0; wWait < 25; ++wWait)
            ;
  
          /* Read the changed value */
          wNewIID = inp (wPort + 2);
  
          /* Set the chip type, based on bits 6 and 7 */
          wIndex = pCom->ComxInfo[i].wUartChip = wNewIID >> 6;
  
          memset (pCom->ComxInfo[i].szUartChip, '\0', MAX_UART_CHIP);
          strcpy (pCom->ComxInfo[i].szUartChip, paszUartChips[wIndex]);
        }
      }
  
    return (FALSE);
  }
  
  
  /*********************************************************************
   * SprintComInfo - Put Com port information into a set of strings to
   *                 be printed or displayed.
   *
   * Returns:  NULL if an error occured.
   *********************************************************************/
  
  QSZ * SprintComInfo (COM_STRUCT *pCom,
                       CHAR szSumStrings[][MAX_SUMM_INFO + 5])
  {
    WORD wNmbrStrings;         /* Number of strings                    */
    WORD wNmbrChars;           /* Number of characters in the strings  */
    WORD wComIndex;            /* Index to the structure of COM data   */
    WORD i;                    /* Looping variables                    */
    QSZ  *pqszStrings = NULL;  /* Location for storing string pointers */
  
  
    /* Summary information */
    if (szSumStrings != NULL)
      {
        sprintf (szSumStrings[0], "%d", pCom->wNmbrComPorts);
        return (NULL);
      }
  
  
    /* Calculate the amount of space required for the strings */
  
    wNmbrChars   = (MAX_COM_TITLES + 3) * (MAX_COM_INFO_LINE + 1);
  
    wNmbrStrings = MAX_COM_TITLES + 3;
  
  
    /* Allocate space for the pointer area and string area */
    pqszStrings = AllocStringSpace (wNmbrStrings, wNmbrChars);
    if (pqszStrings == NULL)
      return (NULL);
  
  
  
    /* Put the first two strings in place */
  
    Qmemset (pqszStrings[0], ' ', COM_TITLE_WIDTH + COM_INFO_COL_WIDTH - 5);
    Qstrcpy (&pqszStrings[0][COM_TITLE_WIDTH + COM_INFO_COL_WIDTH - 5],
            pszComHeader);
    pqszStrings[1] = pqszStrings[0] + Qstrlen (pqszStrings[0]) + 1;
  
    Qmemset (pqszStrings[1], ' ', COM_TITLE_WIDTH + COM_INFO_COL_WIDTH - 5);
    Qstrcpy (&pqszStrings[1][COM_TITLE_WIDTH + COM_INFO_COL_WIDTH - 5],
            pszComUnderline);
    pqszStrings[2] = pqszStrings[1] + Qstrlen (pqszStrings[1]) + 1;
  
  
    /* Put the COM port information in place */
  
    for (i = 2, wComIndex = 0; wComIndex < MAX_COM_TITLES;
         ++i, ++wComIndex)
      {
        WORD wPort;             /* Port number for each display line */
        WORD wLength;           /* Current length of string          */
        WORD wInfoColumn;       /* Column to place the information   */
        CHAR chBuffer[80];      /* Buffer for string data            */
        PSZ  pszString = NULL;  /* String pointer                    */
  
        /* Clear out the string */
        Qmemset (pqszStrings[i], ' ', MAX_COM_INFO_LINE);
        pqszStrings[i][MAX_COM_INFO_LINE] = '\0';
  
        /* Put on the title */
        Qstrncpy (pqszStrings[i], paszComTitles[wComIndex],
                 strlen (paszComTitles[wComIndex]));
  
        for (wPort = 0; wPort < MAX_COM_PORTS; ++wPort)
          {
            wInfoColumn = wPort * COM_INFO_COL_WIDTH + COM_TITLE_WIDTH;
  
  
            /* Put the N/A under the Port Address for ports that don't exist */
  
            if (wComIndex == COM_PORT_ADDRESS &&
                pCom->ComxInfo[wPort].fComPortDetected == FALSE)
              {
                QstrncpyAlign (&pqszStrings[i][wInfoColumn], pszNA,
                              strlen (pszNA), COM_INFO_COL_WIDTH);
              }
  
  
            /* Add the strings only if the COM port exists */
  
            if (pCom->ComxInfo[wPort].fComPortDetected)
              {
                switch (wComIndex)
                  {
                    case COM_PORT_ADDRESS:
                      {
                        sprintf (chBuffer, "%04XH",
                                 pCom->ComxInfo[wPort].wPortAddress);
  
                        QstrncpyAlign (&pqszStrings[i][wInfoColumn], chBuffer,
                                      strlen (chBuffer), COM_INFO_COL_WIDTH);
                        break;
                      }
  
                    case COM_BAUD_RATE:
                      {
                        sprintf (chBuffer, "%Lu",
                                 pCom->ComxInfo[wPort].dwBaudRate);
                        QstrncpyAlign (&pqszStrings[i][wInfoColumn], chBuffer,
                                      strlen (chBuffer), COM_INFO_COL_WIDTH);
                        break;
                      }
  
                    case COM_PARITY:
                      {
                        wLength = strlen (pCom->ComxInfo[wPort].szParity);
  
                        QstrncpyAlign (&pqszStrings[i][wInfoColumn],
                                      pCom->ComxInfo[wPort].szParity,
                                      wLength, COM_INFO_COL_WIDTH);
                        break;
                      }
  
                    case COM_DATA_BITS:
                      {
                        sprintf (chBuffer, "%d",
                                 pCom->ComxInfo[wPort].wDataBits);
                        QstrncpyAlign (&pqszStrings[i][wInfoColumn], chBuffer,
                                      strlen (chBuffer), COM_INFO_COL_WIDTH);
                        break;
                      }
  
                    case COM_STOP_BITS:
                      {
                        if (pCom->ComxInfo[wPort].wStopBits < 3)
                          {
                            sprintf (chBuffer, "%d",
                                     pCom->ComxInfo[wPort].wStopBits);
                            QstrncpyAlign (&pqszStrings[i][wInfoColumn],
                                          chBuffer,
                                          strlen (chBuffer),
                                          COM_INFO_COL_WIDTH);
                          }
                        else
                          {
                            wLength = strlen (psz1point5);
                            QstrncpyAlign (&pqszStrings[i][wInfoColumn],
                                          psz1point5,
                                          wLength,
                                          COM_INFO_COL_WIDTH);
                          }
                        break;
                      }
  
                    case COM_CARRIER_DETECT:
                      {
                        pszString = (pCom->ComxInfo[wPort].fCarrierDetect) ?
                                    pszYes : pszNo;
  
                        wLength = strlen (pszString);
  
                        QstrncpyAlign (&pqszStrings[i][wInfoColumn], pszString,
                                      wLength, COM_INFO_COL_WIDTH);
                        break;
                      }
  
                    case COM_RING_INDICATOR:
                      {
                        pszString = (pCom->ComxInfo[wPort].fRingIndicator) ?
                                    pszYes : pszNo;
  
                        wLength = strlen (pszString);
  
                        QstrncpyAlign (&pqszStrings[i][wInfoColumn], pszString,
                                      wLength, COM_INFO_COL_WIDTH);
                        break;
                      }
  
                    case COM_DATA_SET_READY:
                      {
                        pszString = (pCom->ComxInfo[wPort].fDataSetReady) ?
                                    pszYes : pszNo;
  
                        wLength = strlen (pszString);
  
                        QstrncpyAlign (&pqszStrings[i][wInfoColumn], pszString,
                                      wLength, COM_INFO_COL_WIDTH);
                        break;
                      }
  
                    case COM_CLEAR_TO_SEND:
                      {
                        pszString = (pCom->ComxInfo[wPort].fClearToSend) ?
                                    pszYes : pszNo;
  
                        wLength = strlen (pszString);
  
                        QstrncpyAlign (&pqszStrings[i][wInfoColumn], pszString,
                                      wLength, COM_INFO_COL_WIDTH);
                        break;
                      }
  
                    case COM_UART_CHIP:
                      {
                        wLength = strlen (pCom->ComxInfo[wPort].szUartChip);
  
                        QstrncpyAlign (&pqszStrings[i][wInfoColumn],
                                      pCom->ComxInfo[wPort].szUartChip,
                                      wLength, COM_INFO_COL_WIDTH);
                        break;
                      }
                  }
              }
          }
  
        PrepNextString (pqszStrings, i);
      }
  
    /* Set the last pointer to a NULL */
    pqszStrings[i] = NULL;
  
    /* Return the pointer to pqszStrings */
    return (pqszStrings);
  }
  