home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / os2sdk / os2sdk10 / apps / terminal / terminal.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-08-11  |  15.5 KB  |  591 lines

  1. /***
  2.  *
  3.  * TITLE 
  4.  *
  5.  *    terminal.c
  6.  *    Copyright (C) Microsoft Corporation 1987
  7.  *    March 1987
  8.  *
  9.  * DESCRIPTION
  10.  *
  11.  *     This program emulates an ANSI terminal device. 
  12.  *
  13.  *     The device driver COM.SYS must be installed at IPL  (Initial Program
  14.  *     Load) by specifying the following in config.sys:
  15.  *        device = COM01.SYS
  16.  *
  17.  *    To run this program, type the following to the MS OS/2 prompt:
  18.  *
  19.  *        terminal [filename]
  20.  *
  21.  *     If a filename is specified, the initialisation of the COM port and
  22.  *     the MODEM will be performed as indicated in the file (creation of this
  23.  *     file is described in options.c). For more details on setting options
  24.  *    pertaining to the COM port and the MODEM, see options.c
  25.  *
  26.  *    To exit the terminal emulator, type ALT F1.
  27.  *
  28.  ***/
  29.  
  30. #include    <stdio.h>
  31. #include    <doscalls.h>
  32. #include    <subcalls.h>
  33. #include    <malloc.h>
  34. #include    <string.h>
  35. #include     <memory.h>
  36. #include     <conio.h>
  37. #include     "term.h"
  38.  
  39. extern void get_options(int, char *[]);   /* get com port and modem options */
  40. extern void get_com_options(structComOptions *); /* get com options */
  41. extern make_modem_conn(void);          /* make modem connection */
  42. extern modem(void);       /* returns TRUE if modem connection was requested */
  43. extern void discon_modem(void);       /* disconnect modem */
  44.  
  45. void init_com_port(void);          /* initialise com port */
  46. void far read_com_port(void);          /* routine addr for a thread */
  47. void write_com_port(void);          /* routine addr for another thread */    
  48. void far pascal handle_signals(int, int); /* BREAK signals handler */
  49. void close_conn(void);              /*close modem connection & com port*/
  50. void far xit(void);              /* exit routine for this program */
  51.  
  52. char *ErrMsg[] = {
  53.                "VIOGETCONFIG",
  54.                "KBDSETSTATUS",
  55.                "OUT OF MEMORY",
  56.                "DOSCREATETHREAD",
  57.                "DOSCLOSE COMPORT",
  58.                "DOSDEVIOCTL SETDCB",
  59.                "DOSREAD",
  60.                "VIOWRTTTY",
  61.                "DOSOPEN: check if COM driver is installed",
  62.                "DOSDEVIOCTL SETBAUD",
  63.                "DOSDEVIOCTL SETLINECHAR",
  64.                "DOSDEVIOCTL GETDCB",
  65.                "KBDCHARIN",
  66.                "DOSWRITE",
  67.                "VIOWRTCHARSTRATT",
  68.                "VIOWRTNCELL",
  69.                "DOSEXITLIST ADD_ADDR",
  70.                "VIOSETCURPOS",
  71.                "VIOGETMODE",
  72.                "DOSDEVIOCTL FLUSH_XMIT_RECV_QUEUE",
  73.                "DOSDEVIOCTL GETCOM",
  74.                "DOSSETSIGHANDLER",
  75.                "DOSDEVIOCTL SETBREAKON",
  76.                "DOSDEVIOCTL SETBREAKOFF",
  77.                "INVALID SIGNAL",
  78.                "do_option: invalid option type",
  79.                "mod_option: invalid option type",
  80.                "show_option: invalid option type"
  81. };
  82. unsigned    FileHndl = NULL;    /* COM port file handle */
  83.  
  84. static char    ExitPgm = FALSE;    /* indicate if program should terminate */
  85. static int    ErrorNumber = -1,   /* used as index to ErrMsg array */
  86.         ReturnCode  = -1;   /* retcode from system/subsystem call */
  87.  
  88.  
  89.  
  90.  
  91. /***    main - entry point to TERMINAL program
  92.  *
  93.  *    This routine obtains the com port and the modem options from the file
  94.  *      (specified in the command line) or directly from the user. It opens and
  95.  *    initialises the com port. It sets up the modem connection (if one was
  96.  *    requested). It creates a thread which loops continuously waiting to 
  97.  *    receive data from the com port and writing it out to the display. It 
  98.  *    loops continuosly waiting for an input from the keyboard and writing 
  99.  *    it out to the com port. Both the loops terminate when the user presses 
  100.  *    ALT-F1. It closes the modem connection (if one was made), and the com 
  101.  *    port and then terminates.
  102.  *
  103.  *    main(argc, argv)
  104.  *
  105.  *    ENTRY    
  106.  *        argc - number of command line arguments
  107.  *        argv - pointer to an array of pointers (to command line args)
  108.  *
  109.  *    EXIT
  110.  *        the program terminates
  111.  *
  112.  *    WARNING
  113.  *
  114.  *    EFFECTS
  115.  *
  116.  ***/
  117.  
  118. main(argc,argv)
  119. int     argc;
  120. char     *argv[];
  121. {
  122.     unsigned char    *Stack1;          /* stack for a thread */
  123.     unsigned    ThreadID,
  124.             RetCode,         /* return code */
  125.             NumBytes,         /* number of bytes to be written */
  126.             Result = TRUE;
  127.     long        PrevAddress;
  128.     unsigned    PrevAction;
  129.     static struct KbdStatus OurKbdStatus = 
  130.                                    {sizeof(OurKbdStatus),KBD_BITMASK,};
  131.  
  132.     /* get COM port and modem options */
  133.     get_options(argc, argv);    
  134.  
  135.     /* open and initialise COM port */
  136.     init_com_port();            
  137.  
  138.     /* establish xit() as the exit routine */
  139.     if ((RetCode = DOSEXITLIST(ADD_ADDR, xit)) != 0)
  140.       error(ERR_DOSEXITLIST, RetCode);
  141.  
  142.     /* make modem connection if requested */
  143.     if (modem())
  144.       Result = make_modem_conn();    
  145.  
  146.     if (Result) {
  147.       /* allocate memory for separate thread execution */
  148.       if (!(Stack1 = (char *) _nmalloc(STACKSIZE)))
  149.         error(ERR_OUTOFMEMORY, NO_RETCODE);
  150.  
  151.       /* create a thread that will execute the read_com_port() */
  152.       Stack1 += STACKSIZE;
  153.       if ((RetCode = DOSCREATETHREAD(read_com_port, &ThreadID,
  154.                                          Stack1)) != 0)
  155.         error(ERR_DOSCREATETHREAD, RetCode);
  156.  
  157.       /* set the keyboard status */
  158.       if ((RetCode = KBDSETSTATUS((struct KbdStatus *) (&OurKbdStatus), 
  159.                                       RESERVED)) != 0) 
  160.         error(ERR_KBDSETSTATUS, RetCode);
  161.  
  162.       /* set signal handler for BREAK signal */
  163.       if ((RetCode = DOSSETSIGHANDLER(handle_signals,
  164.              &PrevAddress,
  165.              &PrevAction,
  166.              RECV_CTRL, BREAK)) != 0)
  167.         error(ERR_DOSSETSIGHANDLER, RetCode);
  168.  
  169.       /* display "connected" message */
  170.       printf("connected... \n");
  171.  
  172.       /* read chars from the keyboard and write to COM port */
  173.       write_com_port();
  174.     }    /* if (Result) */
  175. }
  176.  
  177.  
  178.  
  179.  
  180. /***    init_com_port - open the COM port and initialise line charecteristics
  181.  *
  182.  *    This routine opens the com port. It sets the com port options 
  183.  *    BaudRate, DataBits, Parity and the StopBits. It sets the read timeout.
  184.  *    It enables the automatic transmit and receive flow control.
  185.  *
  186.  *    init_com_port()
  187.  *        
  188.  *    ENTRY
  189.  *        
  190.  *    EXIT
  191.  *        FileHndl = handle to com port
  192.  *
  193.  *    WARNING
  194.  *
  195.  *    EFFECTS
  196.  *
  197.  ***/
  198.  
  199. void init_com_port()
  200. {
  201.     unsigned     ActionTaken, /* action: file existed,created,replaced */
  202.              RetCode;
  203.     structLineChar     sLineChar;    /* line characteristics */
  204.     structDCB    sDCB;        /* device control block information */
  205.     structComOptions sComOptions;
  206.  
  207.     get_com_options(&sComOptions);
  208.  
  209.     /* open the com port */
  210.     if ((RetCode = DOSOPEN(sComOptions.pPortName, &FileHndl, 
  211.                                &ActionTaken, 0L, 0, 0x0001, 0x0042, 0L)) != 0)
  212.       error(ERR_DOSOPEN, RetCode);
  213.  
  214.     /* set the baud rate */
  215.     if ((RetCode = DOSDEVIOCTL(0L, (char *) &(sComOptions.iBaudRate), 
  216.                        SETBAUD, SERIAL, FileHndl)) != 0)
  217.       error(ERR_IOCTLSETBAUD, RetCode);
  218.  
  219.     /* set Data Bits, Stop Bits, Parity */
  220.     sLineChar.DataBits = sComOptions.chDataBits; 
  221.         sLineChar.Parity   = sComOptions.chParity; 
  222.     sLineChar.StopBits = sComOptions.chStopBits;
  223.     if ((RetCode = DOSDEVIOCTL(0L, (char *) &sLineChar, SETLINECHAR, 
  224.                        SERIAL, FileHndl)) != 0)
  225.       error(ERR_IOCTLSETLINECHAR, RetCode);
  226.  
  227.     /* get device control block info */
  228.     if ((RetCode = DOSDEVIOCTL((char *) &sDCB, 0L, GETDCB, SERIAL, 
  229.                                    FileHndl)) != 0)
  230.       error(ERR_IOCTLGETDCB, RetCode);
  231.  
  232.     sDCB.Flags2 |= 0x03;    /* enable auto Xmit and recv flow control */
  233.     sDCB.Flags3 &= 0xf9;    /* clear read timeout flags */
  234.     sDCB.Flags3 |= 0x04;    /* set wait for something read timeout */
  235.     sDCB.ReadTimeOut = READTIMEOUT;    /* set read timout value */
  236.  
  237.     /* set device control block info */
  238.     if ((RetCode = DOSDEVIOCTL(0L, (char *) &sDCB, SETDCB, SERIAL, 
  239.                     FileHndl)) != 0)
  240.       error(ERR_IOCTLSETDCB, RetCode);
  241. }
  242.  
  243.  
  244.  
  245.  
  246. /***    write_com_port    - read chars from the keyboard and write to COM port
  247.  *
  248.  *    This routine loops continuosly waiting for a keyboard input and
  249.  *    writing it out to the com port. The loop terminates when the user
  250.  *    presses the ALT-F1.
  251.  *
  252.  *    write_com_port()
  253.  *
  254.  *    ENTRY
  255.  *
  256.  *    EXIT
  257.  *
  258.  *    WARNING
  259.  *
  260.  *    EFFECTS
  261.  *
  262.  ***/
  263.  
  264. void write_com_port()
  265. {
  266.     unsigned    NumBytes,    /* number of bytes actually written */
  267.             RetCode;
  268.     char        OutBuffer;    /* output buffer */
  269.     struct KeyData    OurKeyData;       /* struc to read a char from kbd */
  270.  
  271.  
  272.     while (!ExitPgm) {
  273.       /* read input from the keyboard */
  274.       if ((RetCode = KBDCHARIN((struct KeyData *) (&OurKeyData), 
  275.                                    IOWAIT, RESERVED)) != 0) 
  276.             error(ERR_KBDCHARIN, RetCode);
  277.       OutBuffer = OurKeyData.char_code;
  278.       if ((OutBuffer == 0) || (OutBuffer == 0xE0)) {
  279.         OutBuffer = OurKeyData.scan_code;
  280.         switch (OutBuffer) {
  281.           case DEL_SCAN  : OutBuffer = DEL_ASCII;
  282.                                break;
  283.           case ALT_F1    : ExitPgm = TRUE;
  284.                                break;
  285.               default        : break;
  286.             };
  287.             if (OutBuffer != ALT_F1) 
  288.           /* write the input from the keyboard to the com port */
  289.           if ((RetCode = DOSWRITE(FileHndl, &OutBuffer, 1, 
  290.                                       &NumBytes)) != 0)
  291.             error(ERR_DOSWRITE, RetCode);
  292.       }
  293.           else {
  294.         /* write the input from the keyboard to the com port */
  295.         if ((RetCode = DOSWRITE(FileHndl, &OutBuffer, 1, 
  296.                                     &NumBytes)) != 0)
  297.           error(ERR_DOSWRITE, RetCode);
  298.       };
  299.     };
  300. }
  301.  
  302.  
  303.  
  304.  
  305. /***     read_com_port - read chars from com port and display on CRT screen 
  306.  *
  307.  *     This routine is executed by a thread. It loops continuously waiting
  308.  *      to receive data from the com port and writing it out to the display.
  309.  *    The loop terminates when the user presses ALT-F1.
  310.  *
  311.  *    read_com_port()
  312.  *
  313.  *    ENTRY
  314.  *        FileHndl (handle to com port) setup
  315.  *
  316.  *    EXIT
  317.  *
  318.  *    WARNING
  319.  *
  320.  *    EFFECTS
  321.  *
  322.  ***/
  323.  
  324. void far read_com_port()
  325. {
  326.     unsigned    NumBytes,    /* number of bytes actually read */
  327.             RetCode;
  328.     struct CharsInQue {     /*data ret'ned by get num chars in que IOCTL*/
  329.             unsigned     NumCharsInQue;
  330.             unsigned    SizeQue;
  331.     } sCharsInQue;
  332.     char        InBuffer[INBUFLENGTH];    /* input buffer  */
  333.  
  334.     while (!ExitPgm) {
  335.       /* get number of characters in receive queue */
  336.       if ((RetCode = DOSDEVIOCTL((char *) &sCharsInQue, 0L, GETNUMCHARS,
  337.                                      SERIAL, FileHndl)) != 0)
  338.         setup_error_msg(ERR_IOCTLSETDCB, RetCode);
  339.  
  340.       if (sCharsInQue.NumCharsInQue == 0)
  341.          sCharsInQue.NumCharsInQue++;
  342.  
  343.       if ((RetCode = DOSREAD(FileHndl, InBuffer, sCharsInQue.NumCharsInQue,
  344.                                  &NumBytes)) != 0)
  345.         setup_error_msg(ERR_DOSREAD, RetCode);
  346.     
  347.       if (NumBytes) {
  348.         /* write to the tty display */
  349.         if ((RetCode = VIOWRTTTY(InBuffer, NumBytes, RESERVED)) != 0)
  350.           setup_error_msg(ERR_VIOWRTTTY, RetCode);
  351.       }
  352.     }
  353. }
  354.  
  355.  
  356.  
  357.  
  358. /***    handle_signals  - handle BREAK signal
  359.  *
  360.  *    This routine is the handler for the BREAK signal. When the user presses
  361.  *    the BREAK key, this routine sends a BREAK on the com line.
  362.  *
  363.  *    handle_signals(SigArg, SigNumber)
  364.  *
  365.  *    ENTRY
  366.  *        SigArg - not used
  367.  *        SigNumber - signal number being processed
  368.  *
  369.  *    EXIT
  370.  *
  371.  *    WARNING
  372.  *
  373.  *    EFFECTS
  374.  *        Since this is a signal handler it must be declared FAR.
  375.  *
  376.  ***/
  377.  
  378. void far pascal handle_signals(SigArg, SigNumber)
  379. int     SigArg,
  380.     SigNumber;
  381. {
  382.     unsigned     RetCode,
  383.             NumBytes,
  384.             Status;
  385.     char        OutBuffer;
  386.  
  387.     switch (SigNumber) {
  388.     case BREAK :/* send BREAK to the com port */
  389.               if ((RetCode = DOSDEVIOCTL((char *) &Status, 0L, 
  390.                                        SETBREAKON, SERIAL, FileHndl)) != 0)
  391.                   error(ERR_IOCTLSETBREAKON, RetCode);
  392.             DOSSLEEP((long) 1);
  393.               if ((RetCode = DOSDEVIOCTL((char *) &Status, 0L, 
  394.                                        SETBREAKOFF, SERIAL, FileHndl)) != 0)
  395.                   error(ERR_IOCTLSETBREAKOFF, RetCode);
  396.             break;
  397.     default    :
  398.             error(ERR_INVALIDSIGNAL, NO_RETCODE);
  399.             break;
  400.     }
  401. }
  402.  
  403.  
  404.  
  405.  
  406. /***    close_conn - close modem connection and the com port 
  407.  *
  408.  *    This routine closes the modem connection if one was made and then
  409.  *    closes the com port.
  410.  *
  411.  *    close_conn()
  412.  *
  413.  *    ENTRY
  414.  *
  415.  *    EXIT
  416.  *
  417.  *    WARNING
  418.  *
  419.  *    EFFECTS
  420.  *
  421.  ***/
  422.  
  423. void close_conn()
  424. {
  425.     int        RetCode;
  426.     static char     CloseMsg[] = "exiting terminal...";
  427.  
  428.     /* send closing message to the display */
  429.     if ((RetCode = VIOWRTTTY(CloseMsg, sizeof(CloseMsg), RESERVED)) != 0)
  430.       print_err_msg(ErrMsg[ERR_VIOWRTTTY], RetCode);
  431.  
  432.     /* if modem connection was made, close it */
  433.     if (modem())
  434.       discon_modem();
  435.  
  436.     /* close the com port */
  437.     if ((RetCode = DOSCLOSE(FileHndl)) != 0)
  438.       print_err_msg(ErrMsg[ERR_DOSCLOSECOMPORT], RetCode);
  439. }
  440.  
  441.  
  442.  
  443.  
  444. /***        ERROR HANDLING
  445.  *
  446.  *    There are two error handling routines:
  447.  *        - error() which is invoked from the main thread
  448.  *        - setup_err_msg() which is invoked from the thread executing 
  449.  *          the read_com_port() routine
  450.  *    The two routines perform different functions (described below). This
  451.  *    is done so that if an error is encountered "simultaneously" in both 
  452.  *    the threads, there will be no race condition in error reporting.
  453.  *    
  454.  *    The following routines are also part of the error handling:
  455.  *            - xit() is a DOSEXITLIST routine 
  456.  *        - print_err_msg() does the actual printing of the error message
  457.  *    
  458.  *    All the routines discussed above are defined below.
  459.  *
  460.  ***/
  461.  
  462.  
  463.  
  464. /***    error
  465.  *
  466.  *    This routine is invoked when there is an error. It prints an
  467.  *    an error message and calls DOSEXIT.
  468.  *
  469.  *    error(ErrNum, RetCode)
  470.  *
  471.  *    ENTRY
  472.  *        ErrNum - error number (used as index to error message array)
  473.  *        RetCode - return code from a DOS system/subsystem call
  474.  *
  475.  *    EXIT
  476.  *            global variables ErrorNumber & ReturnCode still contain the
  477.  *        initial value of -1. At program termination (via DOSEXIT call)
  478.  *        the DOSEXITLIST routine xit() is invoked (described below). 
  479.  *        On return from xit(), this program will terminate.
  480.  *
  481.  *    WARNING
  482.  *
  483.  *    EFFECTS
  484.  *
  485.  ***/
  486.  
  487. error(ErrNum, RetCode)
  488. int  ErrNum;
  489. int  RetCode;
  490. {
  491.     print_err_msg(ErrMsg[ErrNum], RetCode);
  492.         DOSEXIT(1, 1);
  493. }
  494.  
  495.  
  496.  
  497.  
  498. /***    setup_error_msg - setup error message
  499.  *
  500.  *    Sets up the global ErrorNumber and ReturnCode. It then calls DOSEXIT.
  501.  *
  502.  *    setup_error_msg(ErrNum, RetCode)
  503.  *
  504.  *    ENTRY
  505.  *        ErrNum - error number (used as index to error message array)
  506.  *        RetCode - return code from a DOS system/subsystem call
  507.  *
  508.  *    EXIT
  509.  *        global variables ErrorNumber & ReturnCode are setup. At 
  510.  *        program termination (via DOSEXIT call) the DOSEXITLIST 
  511.  *        routine xit() is invoked (described below). xit() will print
  512.  *        an error message. On return from xit(), this program will 
  513.  *        terminate.
  514.  *
  515.  *    WARNING
  516.  *
  517.  *    EFFECTS
  518.  *
  519.  ***/
  520.  
  521. setup_error_msg(ErrNum, RetCode)
  522. int    ErrNum,
  523.     RetCode;
  524. {
  525.     ErrorNumber = ErrNum;
  526.     ReturnCode  = RetCode;
  527.     DOSEXIT(1, 1);
  528. }
  529.  
  530.  
  531.  
  532.  
  533. /***    xit    - exit function executed at program termination
  534.  *
  535.  *    This is a DOSEXITLIST routine. It prints an error message if
  536.  *    the global variable ErrorNumber is non-negative.
  537.  *
  538.  *    xit()
  539.  *
  540.  *    ENTRY
  541.  *
  542.  *    EXIT
  543.  *        If ErrorNumber is non-negative, an error message is printed.
  544.  *        The program will then terminate.
  545.  *
  546.  *    WARNING
  547.  *
  548.  *    EFFECTS
  549.  *
  550.  ***/
  551.  
  552. void far xit()
  553. {
  554.     if (ErrorNumber != -1) 
  555.       print_err_msg(ErrMsg[ErrorNumber], ReturnCode);
  556.     close_conn();         /* close modem connection and the com port */
  557.     DOSEXITLIST(XFER, 0L);
  558. }
  559.  
  560.  
  561.  
  562.  
  563. /***    print_err_msg - print error mesage
  564.  *
  565.  *    This routine prints an error message and the returncode.
  566.  *
  567.  *    print_err_msg(Msg, Retcode)
  568.  *
  569.  *    ENTRY
  570.  *        Msg - error message string
  571.  *        Retcode - returncode from DOS system/subsystem call
  572.  *
  573.  *    EXIT
  574.  *
  575.  *    WARNING
  576.  *
  577.  *    EFFECTS
  578.  *
  579.  ***/
  580.  
  581. print_err_msg(Msg, RetCode)
  582. char    *Msg;
  583. int    RetCode;
  584.  
  585. {
  586.       printf("*** ERROR %s *** ", Msg);
  587.      if (RetCode != -1)
  588.       printf("ReturnCode = %d ", RetCode);
  589.     printf("\n");
  590. }
  591.