Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2. Copyright (C) 1996-1997 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20. // net_comx.c
  21.  
  22. #include <dos.h>
  23. #include <dpmi.h>
  24.  
  25. #define NUM_COM_PORTS   2
  26.  
  27. #define ERR_TTY_LINE_STATUS             -1
  28. #define ERR_TTY_MODEM_STATUS    -2
  29. #define ERR_TTY_NODATA                  -3
  30.  
  31. #define QUEUESIZE       8192
  32. #define QUEUEMASK       (QUEUESIZE - 1)
  33.  
  34. typedef struct
  35. {
  36.         volatile int  head;
  37.         volatile int  tail;
  38.         volatile byte data[QUEUESIZE];
  39. } queue;
  40.  
  41. #define FULL(q)                 (q.head == ((q.tail-1) & QUEUEMASK))
  42. #define EMPTY(q)                (q.tail == q.head)
  43. #define ENQUEUE(q,b)    (q.data[q.head] = b, q.head = (q.head + 1) & QUEUEMASK)
  44. #define DEQUEUE(q,b)    (b = q.data[q.tail], q.tail = (q.tail + 1) & QUEUEMASK)
  45.  
  46. extern cvar_t   config_com_port;
  47. extern cvar_t   config_com_irq;
  48. extern cvar_t   config_com_baud;
  49. extern cvar_t   config_com_modem;
  50. extern cvar_t   config_modem_dialtype;
  51. extern cvar_t   config_modem_clear;
  52. extern cvar_t   config_modem_init;
  53. extern cvar_t   config_modem_hangup;
  54.  
  55. extern int m_return_state;
  56. extern int m_state;
  57. extern qboolean m_return_onerror;
  58. extern char m_return_reason[32];
  59.  
  60. // 8250, 16550 definitions
  61. #define TRANSMIT_HOLDING_REGISTER            0x00
  62. #define RECEIVE_BUFFER_REGISTER              0x00
  63. #define INTERRUPT_ENABLE_REGISTER            0x01
  64. #define   IER_RX_DATA_READY                  0x01
  65. #define   IER_TX_HOLDING_REGISTER_EMPTY      0x02
  66. #define   IER_LINE_STATUS                    0x04
  67. #define   IER_MODEM_STATUS                   0x08
  68. #define INTERRUPT_ID_REGISTER                0x02
  69. #define   IIR_MODEM_STATUS_INTERRUPT         0x00
  70. #define   IIR_TX_HOLDING_REGISTER_INTERRUPT  0x02
  71. #define   IIR_RX_DATA_READY_INTERRUPT        0x04
  72. #define   IIR_LINE_STATUS_INTERRUPT          0x06
  73. #define   IIR_FIFO_TIMEOUT                   0x0c
  74. #define   IIR_FIFO_ENABLED                   0xc0
  75. #define FIFO_CONTROL_REGISTER                0x02
  76. #define   FCR_FIFO_ENABLE                    0x01
  77. #define   FCR_RCVR_FIFO_RESET                0x02
  78. #define   FCR_XMIT_FIFO_RESET                0x04
  79. #define   FCR_TRIGGER_01                     0x00
  80. #define   FCR_TRIGGER_04                     0x40
  81. #define   FCR_TRIGGER_08                     0x80
  82. #define   FCR_TRIGGER_16                     0xc0
  83. #define LINE_CONTROL_REGISTER                0x03
  84. #define   LCR_DATA_BITS_5                    0x00
  85. #define   LCR_DATA_BITS_6                    0x01
  86. #define   LCR_DATA_BITS_7                    0x02
  87. #define   LCR_DATA_BITS_8                    0x03
  88. #define   LCR_STOP_BITS_1                    0x00
  89. #define   LCR_STOP_BITS_2                    0x04
  90. #define   LCR_PARITY_NONE                    0x00
  91. #define   LCR_PARITY_ODD                     0x08
  92. #define   LCR_PARITY_EVEN                    0x18
  93. #define   LCR_PARITY_MARK                    0x28
  94. #define   LCR_PARITY_SPACE                   0x38
  95. #define   LCR_SET_BREAK                      0x40
  96. #define   LCR_DLAB                           0x80
  97. #define MODEM_CONTROL_REGISTER               0x04
  98. #define   MCR_DTR                            0x01
  99. #define   MCR_RTS                            0x02
  100. #define   MCR_OUT1                           0x04
  101. #define   MCR_OUT2                           0x08
  102. #define   MCR_LOOPBACK                       0x10
  103. #define LINE_STATUS_REGISTER                 0x05
  104. #define   LSR_DATA_READY                     0x01
  105. #define   LSR_OVERRUN_ERROR                  0x02
  106. #define   LSR_PARITY_ERROR                   0x04
  107. #define   LSR_FRAMING_ERROR                  0x08
  108. #define   LSR_BREAK_DETECT                   0x10
  109. #define   LSR_TRANSMITTER_BUFFER_EMPTY       0x20
  110. #define   LSR_TRANSMITTER_EMPTY              0x40
  111. #define   LSR_FIFO_DIRTY                     0x80
  112. #define MODEM_STATUS_REGISTER                0x06
  113. #define   MSR_DELTA_CTS                      0x01
  114. #define   MSR_DELTA_DSR                      0x02
  115. #define   MSR_DELTA_RI                       0x04
  116. #define   MSR_DELTA_CD                       0x08
  117. #define   MSR_CTS                            0x10
  118. #define   MSR_DSR                            0x20
  119. #define   MSR_RI                             0x40
  120. #define   MSR_CD                             0x80
  121. #define DIVISOR_LATCH_LOW                    0x00
  122. #define DIVISOR_LATCH_HIGH                   0x01
  123.  
  124. #define MODEM_STATUS_MASK       (MSR_CTS | MSR_DSR | MSR_CD)
  125.  
  126. #define UART_AUTO       0
  127. #define UART_8250       1
  128. #define UART_16550      2
  129.  
  130. static int ISA_uarts[]  = {0x3f8,0x2f8,0x3e8,0x2e8};
  131. static int ISA_IRQs[]   = {4,3,4,3};
  132.  
  133. typedef struct ComPort_s
  134. {
  135.         struct ComPort_s                *next;
  136.         _go32_dpmi_seginfo              protectedModeInfo;
  137.         _go32_dpmi_seginfo              protectedModeSaveInfo;
  138.         int                                             uart;
  139.         volatile byte                   modemStatus;
  140.         byte                                    modemStatusIgnore;
  141.         byte                                    lineStatus;
  142.         byte                                    bufferUsed;
  143.         qboolean                                enabled;
  144.         volatile qboolean               statusUpdated;
  145.         qboolean                                useModem;
  146.         qboolean                                modemInitialized;
  147.         qboolean                                modemRang;
  148.         qboolean                                modemConnected;
  149.         queue                                   inputQueue;
  150.         queue                                   outputQueue;
  151.         char                                    clear[16];
  152.         char                                    startup[32];
  153.         char                                    shutdown[16];
  154.         char                                    buffer[128];
  155.         PollProcedure                   poll;
  156.         double                                  timestamp;
  157.         byte                                    uartType;
  158.         byte                                    irq;
  159.         byte                                    baudBits;
  160.         byte                                    lineControl;
  161.         byte                                    portNumber;
  162.         char                                    dialType;
  163.         char                                    name[4];
  164. } ComPort;
  165.  
  166. ComPort *portList = NULL;
  167. ComPort *handleToPort [NUM_COM_PORTS];
  168.  
  169. static int Modem_Command(ComPort *p, char *commandString);
  170. static char *Modem_Response(ComPort *p);
  171. static void Modem_Hangup(ComPort *p);
  172.  
  173. int TTY_Init(void);
  174. void TTY_Shutdown(void);
  175. int TTY_Open(int serialPortNumber);
  176. void TTY_Close(int handle);
  177. int TTY_ReadByte(int handle);
  178. int TTY_WriteByte(int handle, byte data);
  179. void TTY_Flush(int handle);
  180. int TTY_Connect(int handle, char *host);
  181. void TTY_Disconnect(int handle);
  182. qboolean TTY_CheckForConnection(int handle);
  183. qboolean TTY_IsEnabled(int serialPortNumber);
  184. qboolean TTY_IsModem(int serialPortNumber);
  185. qboolean TTY_OutputQueueIsEmpty(int handle);
  186.  
  187. static void ISR_8250 (ComPort *p)
  188. {
  189.         byte    source = 0;
  190.         byte    b;
  191.  
  192.         disable();
  193.  
  194.         while((source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07) != 1)
  195.         {
  196.                 switch (source)
  197.                 {
  198.                         case IIR_RX_DATA_READY_INTERRUPT:
  199.                                 b = inportb (p->uart + RECEIVE_BUFFER_REGISTER);
  200.                                 if (! FULL(p->inputQueue))
  201.                                 {
  202.                                         ENQUEUE (p->inputQueue, b);
  203.                                 }
  204.                                 else
  205.                                 {
  206.                                         p->lineStatus |= LSR_OVERRUN_ERROR;
  207.                                         p->statusUpdated = true;
  208.                                 }
  209.                                 break;
  210.  
  211.                         case IIR_TX_HOLDING_REGISTER_INTERRUPT:
  212.                                 if (! EMPTY(p->outputQueue))
  213.                                 {
  214.                                         DEQUEUE (p->outputQueue, b);
  215.                                         outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b);
  216.                                 }
  217.                                 break;
  218.  
  219.                         case IIR_MODEM_STATUS_INTERRUPT:
  220.                                 p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore;
  221.                                 p->statusUpdated = true;
  222.                                 break;
  223.  
  224.                         case IIR_LINE_STATUS_INTERRUPT:
  225.                                 p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER);
  226.                                 p->statusUpdated = true;
  227.                                 break;
  228.                 }
  229.                 source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07;
  230.         }
  231.         outportb (0x20, 0x20);
  232. }
  233.  
  234. static void COM1_ISR_8250 (void)
  235. {
  236.         ISR_8250 (handleToPort[0]);
  237. }
  238.  
  239. static void COM2_ISR_8250 (void)
  240. {
  241.         ISR_8250 (handleToPort[1]);
  242. }
  243.  
  244.  
  245.  
  246. static void ISR_16550 (ComPort *p)
  247. {
  248.         int             count;
  249.         byte    source;
  250.         byte    b;
  251.  
  252.         disable();
  253.         while((source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07) != 1)
  254.         {
  255.                 switch (source)
  256.                 {
  257.                         case IIR_RX_DATA_READY_INTERRUPT:
  258.                                 do
  259.                                 {
  260.                                         b = inportb (p->uart + RECEIVE_BUFFER_REGISTER);
  261.                                         if (!FULL(p->inputQueue))
  262.                                         {
  263.                                                 ENQUEUE (p->inputQueue, b);
  264.                                         }
  265.                                         else
  266.                                         {
  267.                                                 p->lineStatus |= LSR_OVERRUN_ERROR;
  268.                                                 p->statusUpdated = true;
  269.                                         }
  270.                                 } while (inportb (p->uart + LINE_STATUS_REGISTER) & LSR_DATA_READY);
  271.                                 break;
  272.  
  273.                         case IIR_TX_HOLDING_REGISTER_INTERRUPT:
  274.                                 count = 16;
  275.                                 while ((! EMPTY(p->outputQueue)) && count--)
  276.                                 {
  277.                                         DEQUEUE (p->outputQueue, b);
  278.                                         outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b);
  279.                                 }
  280.                                 break;
  281.  
  282.                         case IIR_MODEM_STATUS_INTERRUPT:
  283.                                 p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore;
  284.                                 p->statusUpdated = true;
  285.                                 break;
  286.  
  287.                         case IIR_LINE_STATUS_INTERRUPT:
  288.                                 p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER);
  289.                                 p->statusUpdated = true;
  290.                                 break;
  291.                 }
  292.                 source = inportb (p->uart + INTERRUPT_ID_REGISTER) & 0x07;
  293.         }
  294.  
  295.         // check for lost IIR_TX_HOLDING_REGISTER_INTERRUPT on 16550a!
  296.         if (inportb (p->uart + LINE_STATUS_REGISTER ) & LSR_TRANSMITTER_EMPTY)
  297.         {
  298.                 count = 16;
  299.                 while ((! EMPTY(p->outputQueue)) && count--)
  300.                 {
  301.                         DEQUEUE (p->outputQueue, b);
  302.                         outportb (p->uart + TRANSMIT_HOLDING_REGISTER, b);
  303.                 }
  304.         }
  305.  
  306.         outportb (0x20, 0x20);
  307. }
  308.  
  309. static void COM1_ISR_16550 (void)
  310. {
  311.         ISR_16550 (handleToPort[0]);
  312. }
  313.  
  314. static void COM2_ISR_16550 (void)
  315. {
  316.         ISR_16550 (handleToPort[1]);
  317. }
  318.  
  319.  
  320. void TTY_GetComPortConfig (int portNumber, int *port, int *irq, int *baud, qboolean *useModem)
  321. {
  322.         ComPort *p;
  323.  
  324.         p = handleToPort[portNumber];
  325.         *port = p->uart;
  326.         *irq = p->irq;
  327.         *baud = 115200 / p->baudBits;
  328.         *useModem = p->useModem;
  329. }
  330.  
  331. void TTY_SetComPortConfig (int portNumber, int port, int irq, int baud, qboolean useModem)
  332. {
  333.         ComPort *p;
  334.         float   temp;
  335.  
  336.         if (useModem)
  337.         {
  338.                 if (baud == 14400)
  339.                         baud = 19200;
  340.                 if (baud == 28800)
  341.                         baud = 38400;
  342.         }
  343.  
  344.         p = handleToPort[portNumber];
  345.         p->uart = port;
  346.         p->irq = irq;
  347.         p->baudBits = 115200 / baud;
  348.         p->useModem = useModem;
  349.  
  350.         if (useModem)
  351.                 temp = 1.0;
  352.         else
  353.                 temp = 0.0;
  354.  
  355.         Cvar_SetValue ("_config_com_port", (float)port);
  356.         Cvar_SetValue ("_config_com_irq", (float)irq);
  357.         Cvar_SetValue ("_config_com_baud", (float)baud);
  358.         Cvar_SetValue ("_config_com_modem", temp);
  359. }
  360.  
  361. void TTY_GetModemConfig (int portNumber, char *dialType, char *clear, char *init, char *hangup)
  362. {
  363.         ComPort *p;
  364.  
  365.         p = handleToPort[portNumber];
  366.         *dialType = p->dialType;
  367.         Q_strcpy(clear, p->clear);
  368.         Q_strcpy(init, p->startup);
  369.         Q_strcpy(hangup, p->shutdown);
  370. }
  371.  
  372. void TTY_SetModemConfig (int portNumber, char *dialType, char *clear, char *init, char *hangup)
  373. {
  374.         ComPort *p;
  375.  
  376.         p = handleToPort[portNumber];
  377.         p->dialType = dialType[0];
  378.         Q_strcpy(p->clear, clear);
  379.         Q_strcpy(p->startup, init);
  380.         Q_strcpy(p->shutdown, hangup);
  381.  
  382.         p->modemInitialized = false;
  383.  
  384.         Cvar_Set ("_config_modem_dialtype", dialType);
  385.         Cvar_Set ("_config_modem_clear", clear);
  386.         Cvar_Set ("_config_modem_init", init);
  387.         Cvar_Set ("_config_modem_hangup", hangup);
  388. }
  389.  
  390.  
  391. static void ResetComPortConfig (ComPort *p)
  392. {
  393.         p->useModem = false;
  394.         p->uartType = UART_AUTO;
  395.         p->uart = ISA_uarts[p->portNumber];
  396.         p->irq = ISA_IRQs[p->portNumber];
  397.         p->modemStatusIgnore = MSR_CD | MSR_CTS | MSR_DSR;
  398.         p->baudBits = 115200 / 57600;
  399.         p->lineControl = LCR_DATA_BITS_8 | LCR_STOP_BITS_1 | LCR_PARITY_NONE;
  400.         Q_strcpy(p->clear, "ATZ");
  401.         Q_strcpy(p->startup, "");
  402.         Q_strcpy(p->shutdown, "AT H");
  403.         p->modemRang = false;
  404.         p->modemConnected = false;
  405.         p->statusUpdated = false;
  406.         p->outputQueue.head = p->outputQueue.tail = 0;
  407.         p->inputQueue.head = p->inputQueue.tail = 0;
  408. }
  409.  
  410.  
  411. static void ComPort_Enable(ComPort *p)
  412. {
  413.         void    (*isr)(void);
  414.         int             n;
  415.         byte    b;
  416.  
  417.         if (p->enabled)
  418.         {
  419.                 Con_Printf("Already enabled\n");
  420.                 return;
  421.         }
  422.  
  423.         // disable all UART interrupts
  424.         outportb (p->uart + INTERRUPT_ENABLE_REGISTER, 0);
  425.  
  426.         // clear out any buffered uncoming data
  427.         while((inportb (p->uart + LINE_STATUS_REGISTER)) & LSR_DATA_READY)
  428.                 inportb (p->uart + RECEIVE_BUFFER_REGISTER);
  429.  
  430.         // get the current line and modem status
  431.         p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore;
  432.         p->lineStatus = inportb (p->uart + LINE_STATUS_REGISTER);
  433.  
  434.         // clear any UART interrupts
  435.         do
  436.         {
  437.                 n = inportb (p->uart + INTERRUPT_ID_REGISTER) & 7;
  438.                 if (n == IIR_RX_DATA_READY_INTERRUPT)
  439.                         inportb (p->uart + RECEIVE_BUFFER_REGISTER);
  440.         } while (!(n & 1));
  441.  
  442.         if (p->uartType == UART_AUTO)
  443.         {
  444.                 outportb (p->uart + FIFO_CONTROL_REGISTER, FCR_FIFO_ENABLE);
  445.                 b = inportb (p->uart + INTERRUPT_ID_REGISTER);
  446.                 if ((b & IIR_FIFO_ENABLED) == IIR_FIFO_ENABLED)
  447.                         p->uartType = UART_16550;
  448.                 else
  449.                         p->uartType = UART_8250;
  450.         }
  451.  
  452.         // save the old interrupt handler
  453.         _go32_dpmi_get_protected_mode_interrupt_vector(p->irq + 8, &p->protectedModeSaveInfo);
  454.  
  455.         if (p->uartType == UART_8250)
  456.         {
  457.                 outportb (p->uart + FIFO_CONTROL_REGISTER, 0);
  458.                 if (p == handleToPort[0])
  459.                         isr = COM1_ISR_8250;
  460.                 else
  461.                         isr = COM2_ISR_8250;
  462.         }
  463.         else
  464.         {
  465.                 outportb (p->uart + FIFO_CONTROL_REGISTER, FCR_FIFO_ENABLE | FCR_RCVR_FIFO_RESET | FCR_XMIT_FIFO_RESET | FCR_TRIGGER_08);
  466.                 if (p == handleToPort[0])
  467.                         isr = COM1_ISR_16550;
  468.                 else
  469.                         isr = COM2_ISR_16550;
  470.         }
  471.  
  472.         p->protectedModeInfo.pm_offset = (int)isr;
  473.  
  474.         n = _go32_dpmi_allocate_iret_wrapper(&p->protectedModeInfo);
  475.         if (n)
  476.         {
  477.                 Con_Printf("serial: protected mode callback allocation failed\n");
  478.                 return;
  479.         }
  480.  
  481.         // disable interrupts at the processor
  482.         disable();
  483.  
  484.         // install our interrupt handlers now
  485.         _go32_dpmi_set_protected_mode_interrupt_vector(p->irq + 8, &p->protectedModeInfo);
  486.  
  487.         // enable our interrupt at the PIC
  488.         outportb (0x21, inportb (0x21) & ~(1<<p->irq));
  489.  
  490.         // enable interrupts at the processor
  491.         enable();
  492.  
  493.         // enable interrupts at the PIC
  494.         outportb (0x20, 0xc2);
  495.  
  496.         // set baud rate & line control
  497.         outportb (p->uart + LINE_CONTROL_REGISTER, LCR_DLAB | p->lineControl);
  498.         outportb (p->uart, p->baudBits);
  499.         outportb (p->uart + 1, 0);
  500.         outportb (p->uart + LINE_CONTROL_REGISTER, p->lineControl);
  501.  
  502.         // set modem control register & enable uart interrupt generation
  503.         outportb(p->uart + MODEM_CONTROL_REGISTER, MCR_OUT2 | MCR_RTS | MCR_DTR);
  504.  
  505.         // enable the individual interrupts at the uart
  506.         outportb (p->uart + INTERRUPT_ENABLE_REGISTER, IER_RX_DATA_READY | IER_TX_HOLDING_REGISTER_EMPTY | IER_LINE_STATUS | IER_MODEM_STATUS);
  507.  
  508.         p->enabled = true;
  509. }
  510.  
  511.  
  512. static void ComPort_Disable(ComPort *p)
  513. {
  514.         if (!p->enabled)
  515.         {
  516.                 Con_Printf("Already disabled\n");
  517.                 return;
  518.         }
  519.  
  520.         // disable interrupts at the uart
  521.         outportb (p->uart + INTERRUPT_ENABLE_REGISTER, 0);
  522.  
  523.         // disable our interrupt at the PIC
  524.         outportb (0x21, inportb (0x21) | (1<<p->irq));
  525.  
  526.         // disable interrupts at the processor
  527.         disable();
  528.  
  529.         // restore the old interrupt handler
  530.         _go32_dpmi_set_protected_mode_interrupt_vector(p->irq + 8, &p->protectedModeSaveInfo);
  531.         _go32_dpmi_free_iret_wrapper(&p->protectedModeInfo);
  532.  
  533.         // enable interrupts at the processor
  534.         enable();
  535.  
  536.         p->enabled = false;
  537. }
  538.  
  539.  
  540. static int CheckStatus (ComPort *p)
  541. {
  542.         int             ret = 0;
  543.  
  544.         if (p->statusUpdated)
  545.         {
  546.                 p->statusUpdated = false;
  547.  
  548.                 if (p->lineStatus & (LSR_OVERRUN_ERROR | LSR_PARITY_ERROR | LSR_FRAMING_ERROR | LSR_BREAK_DETECT))
  549.                 {
  550.                         if (p->lineStatus & LSR_OVERRUN_ERROR)
  551.                                 Con_DPrintf ("Serial overrun error\n");
  552.                         if (p->lineStatus & LSR_PARITY_ERROR)
  553.                                 Con_DPrintf ("Serial parity error\n");
  554.                         if (p->lineStatus & LSR_FRAMING_ERROR)
  555.                                 Con_DPrintf ("Serial framing error\n");
  556.                         if (p->lineStatus & LSR_BREAK_DETECT)
  557.                                 Con_DPrintf ("Serial break detect\n");
  558.                         ret = ERR_TTY_LINE_STATUS;
  559.                 }
  560.  
  561.                 if ((p->modemStatus & MODEM_STATUS_MASK) != MODEM_STATUS_MASK)
  562.                 {
  563.                         if (!(p->modemStatus & MSR_CTS))
  564.                                 Con_Printf ("Serial lost CTS\n");
  565.                         if (!(p->modemStatus & MSR_DSR))
  566.                                 Con_Printf ("Serial lost DSR\n");
  567.                         if (!(p->modemStatus & MSR_CD))
  568.                                 Con_Printf ("Serial lost Carrier\n");
  569.                         ret = ERR_TTY_MODEM_STATUS;
  570.                 }
  571.         }
  572.  
  573.         return ret;
  574. }
  575.  
  576.  
  577. static void Modem_Init(ComPort *p)
  578. {
  579.         double  start;
  580.         char    *response;
  581.  
  582.         Con_Printf ("Initializing modem...\n");
  583.  
  584.         // write 0 to MCR, wait 1/2 sec, then write the real value back again
  585.         // I got this from the guys at head-to-head who say it's necessary.
  586.         outportb(p->uart + MODEM_CONTROL_REGISTER, 0);
  587.         start = Sys_FloatTime();
  588.         while ((Sys_FloatTime() - start) < 0.5)
  589.                 ;
  590.         outportb(p->uart + MODEM_CONTROL_REGISTER, MCR_OUT2 | MCR_RTS | MCR_DTR);
  591.         start = Sys_FloatTime();
  592.         while ((Sys_FloatTime() - start) < 0.25)
  593.                 ;
  594.  
  595.         if (*p->clear)
  596.         {
  597.                 Modem_Command (p, p->clear);
  598.                 start = Sys_FloatTime();
  599.                 while(1)
  600.                 {
  601.                         if ((Sys_FloatTime() - start) > 3.0)
  602.                         {
  603.                                 Con_Printf("No response - clear failed\n");
  604.                                 p->enabled = false;
  605.                                 goto failed;
  606.                         }
  607.                         response = Modem_Response(p);
  608.                         if (!response)
  609.                                 continue;
  610.                         if (Q_strncmp(response, "OK", 2) == 0)
  611.                                 break;
  612.                         if (Q_strncmp(response, "ERROR", 5) == 0)
  613.                         {
  614.                                 p->enabled = false;
  615.                                 goto failed;
  616.                         }
  617.                 }
  618.         }
  619.  
  620.         if (*p->startup)
  621.         {
  622.                 Modem_Command (p, p->startup);
  623.                 start = Sys_FloatTime();
  624.                 while(1)
  625.                 {
  626.                         if ((Sys_FloatTime() - start) > 3.0)
  627.                         {
  628.                                 Con_Printf("No response - init failed\n");
  629.                                 p->enabled = false;
  630.                                 goto failed;
  631.                         }
  632.                         response = Modem_Response(p);
  633.                         if (!response)
  634.                                 continue;
  635.                         if (Q_strncmp(response, "OK", 2) == 0)
  636.                                 break;
  637.                         if (Q_strncmp(response, "ERROR", 5) == 0)
  638.                         {
  639.                                 p->enabled = false;
  640.                                 goto failed;
  641.                         }
  642.                 }
  643.         }
  644.  
  645.         p->modemInitialized = true;
  646.         return;
  647.  
  648. failed:
  649.         if (m_return_onerror)
  650.         {
  651.                 key_dest = key_menu;
  652.                 m_state = m_return_state;
  653.                 m_return_onerror = false;
  654.                 Q_strcpy(m_return_reason, "Initialization Failed");
  655.         }
  656.         return;
  657. }
  658.  
  659.  
  660. void TTY_Enable(int handle)
  661. {
  662.         ComPort *p;
  663.  
  664.         p = handleToPort [handle];
  665.         if (p->enabled)
  666.                 return;
  667.  
  668.         ComPort_Enable(p);
  669.  
  670.         if (p->useModem && !p->modemInitialized)
  671.                 Modem_Init (p);
  672. }
  673.  
  674.  
  675. int TTY_Open(int serialPortNumber)
  676. {
  677.         return serialPortNumber;
  678. }
  679.  
  680.  
  681. void TTY_Close(int handle)
  682. {
  683.         ComPort *p;
  684.         double          startTime;
  685.  
  686.         p = handleToPort [handle];
  687.  
  688.         startTime = Sys_FloatTime();
  689.         while ((Sys_FloatTime() - startTime) < 1.0)
  690.                 if (EMPTY(p->outputQueue))
  691.                         break;
  692.  
  693.         if (p->useModem)
  694.         {
  695.                 if (p->modemConnected)
  696.                         Modem_Hangup(p);
  697.         }
  698. }
  699.  
  700.  
  701. int TTY_ReadByte(int handle)
  702. {
  703.         int             ret;
  704.         ComPort *p;
  705.  
  706.         p = handleToPort [handle];
  707.  
  708.         if ((ret = CheckStatus (p)) != 0)
  709.                 return ret;
  710.        
  711.         if (EMPTY (p->inputQueue))
  712.                 return ERR_TTY_NODATA;
  713.  
  714.         DEQUEUE (p->inputQueue, ret);
  715.         return (ret & 0xff);
  716. }
  717.  
  718.  
  719. int TTY_WriteByte(int handle, byte data)
  720. {
  721.         ComPort *p;
  722.  
  723.         p = handleToPort [handle];
  724.         if (FULL(p->outputQueue))
  725.                 return -1;
  726.  
  727.         ENQUEUE (p->outputQueue, data);
  728.         return 0;
  729. }
  730.  
  731.  
  732. void TTY_Flush(int handle)
  733. {
  734.         byte b;
  735.         ComPort *p;
  736.  
  737.         p = handleToPort [handle];
  738.  
  739.         if (inportb (p->uart + LINE_STATUS_REGISTER ) & LSR_TRANSMITTER_EMPTY)
  740.         {
  741.                 DEQUEUE (p->outputQueue, b);
  742.                 outportb(p->uart, b);
  743.         }
  744. }
  745.  
  746.  
  747. int TTY_Connect(int handle, char *host)
  748. {
  749.         double  start;
  750.         ComPort *p;
  751.         char    *response = NULL;
  752.         keydest_t       save_key_dest;
  753.         byte    dialstring[64];
  754.         byte    b;
  755.  
  756.         p = handleToPort[handle];
  757.  
  758.         if ((p->modemStatus & MODEM_STATUS_MASK) != MODEM_STATUS_MASK)
  759.         {
  760.                 Con_Printf ("Serial: line not ready (");
  761.                 if ((p->modemStatus & MSR_CTS) == 0)
  762.                         Con_Printf(" CTS");
  763.                 if ((p->modemStatus & MSR_DSR) == 0)
  764.                         Con_Printf(" DSR");
  765.                 if ((p->modemStatus & MSR_CD) == 0)
  766.                         Con_Printf(" CD");
  767.                 Con_Printf(" )");
  768.                 return -1;
  769.         }
  770.  
  771.         // discard any scraps in the input buffer
  772.         while (! EMPTY (p->inputQueue))
  773.                 DEQUEUE (p->inputQueue, b);
  774.  
  775.         CheckStatus (p);
  776.  
  777.         if (p->useModem)
  778.         {
  779.                 save_key_dest = key_dest;
  780.                 key_dest = key_console;
  781.                 key_count = -2;
  782.  
  783.                 Con_Printf ("Dialing...\n");
  784.                 sprintf(dialstring, "AT D%c %s\r", p->dialType, host);
  785.                 Modem_Command (p, dialstring);
  786.                 start = Sys_FloatTime();
  787.                 while(1)
  788.                 {
  789.                         if ((Sys_FloatTime() - start) > 60.0)
  790.                         {
  791.                                 Con_Printf("Dialing failure!\n");
  792.                                 break;
  793.                         }
  794.  
  795.                         Sys_SendKeyEvents ();
  796.                         if (key_count == 0)
  797.                         {
  798.                                 if (key_lastpress != K_ESCAPE)
  799.                                 {
  800.                                         key_count = -2;
  801.                                         continue;
  802.                                 }
  803.                                 Con_Printf("Aborting...\n");
  804.                                 while ((Sys_FloatTime() - start) < 5.0)
  805.                                         ;
  806.                                 disable();
  807.                                 p->outputQueue.head = p->outputQueue.tail = 0;
  808.                                 p->inputQueue.head = p->inputQueue.tail = 0;
  809.                                 outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) & ~MCR_DTR);
  810.                                 enable();
  811.                                 start = Sys_FloatTime();
  812.                                 while ((Sys_FloatTime() - start) < 0.75)
  813.                                         ;
  814.                                 outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) | MCR_DTR);
  815.                                 response = "Aborted";
  816.                                 break;
  817.                         }
  818.  
  819.                         response = Modem_Response(p);
  820.                         if (!response)
  821.                                 continue;
  822.                         if (Q_strncmp(response, "CONNECT", 7) == 0)
  823.                         {
  824.                                 disable();
  825.                                 p->modemRang = true;
  826.                                 p->modemConnected = true;
  827.                                 p->outputQueue.head = p->outputQueue.tail = 0;
  828.                                 p->inputQueue.head = p->inputQueue.tail = 0;
  829.                                 enable();
  830.                                 key_dest = save_key_dest;
  831.                                 key_count = 0;
  832.                                 m_return_onerror = false;
  833.                                 return 0;
  834.                         }
  835.                         if (Q_strncmp(response, "NO CARRIER", 10) == 0)
  836.                                 break;
  837.                         if (Q_strncmp(response, "NO DIALTONE", 11) == 0)
  838.                                 break;
  839.                         if (Q_strncmp(response, "NO DIAL TONE", 12) == 0)
  840.                                 break;
  841.                         if (Q_strncmp(response, "NO ANSWER", 9) == 0)
  842.                                 break;
  843.                         if (Q_strncmp(response, "BUSY", 4) == 0)
  844.                                 break;
  845.                         if (Q_strncmp(response, "ERROR", 5) == 0)
  846.                                 break;
  847.                 }
  848.                 key_dest = save_key_dest;
  849.                 key_count = 0;
  850.                 if (m_return_onerror)
  851.                 {
  852.                         key_dest = key_menu;
  853.                         m_state = m_return_state;
  854.                         m_return_onerror = false;
  855.                         Q_strncpy(m_return_reason, response, 31);
  856.                 }
  857.                 return -1;
  858.         }
  859.         m_return_onerror = false;
  860.         return 0;
  861. }
  862.  
  863.  
  864. void TTY_Disconnect(int handle)
  865. {
  866.         ComPort *p;
  867.  
  868.         p = handleToPort[handle];
  869.  
  870.         if (p->useModem && p->modemConnected)
  871.                 Modem_Hangup(p);
  872. }
  873.  
  874.  
  875. qboolean TTY_CheckForConnection(int handle)
  876. {
  877.         ComPort *p;
  878.  
  879.         p = handleToPort[handle];
  880.  
  881.         CheckStatus (p);
  882.  
  883.         if (p->useModem)
  884.         {
  885.                 if (!p->modemRang)
  886.                 {
  887.                         if (!Modem_Response(p))
  888.                                 return false;
  889.  
  890.                         if (Q_strncmp(p->buffer, "RING", 4) == 0)
  891.                         {
  892.                                 Modem_Command (p, "ATA");
  893.                                 p->modemRang = true;
  894.                                 p->timestamp = net_time;
  895.                         }
  896.                         return false;
  897.                 }
  898.                 if (!p->modemConnected)
  899.                 {
  900.                         if ((net_time - p->timestamp) > 35.0)
  901.                         {
  902.                                 Con_Printf("Unable to establish modem connection\n");
  903.                                 p->modemRang = false;
  904.                                 return false;
  905.                         }
  906.  
  907.                         if (!Modem_Response(p))
  908.                                 return false;
  909.  
  910.                         if (Q_strncmp (p->buffer, "CONNECT", 7) != 0)
  911.                                 return false;
  912.  
  913.                         disable();
  914.                         p->modemConnected = true;
  915.                         p->outputQueue.head = p->outputQueue.tail = 0;
  916.                         p->inputQueue.head = p->inputQueue.tail = 0;
  917.                         enable();
  918.                         Con_Printf("Modem Connect\n");
  919.                         return true;
  920.                 }
  921.                 return true;
  922.         }
  923.  
  924.         // direct connect case
  925.         if (EMPTY (p->inputQueue))
  926.                 return false;
  927.         return true;
  928. }
  929.  
  930.  
  931. qboolean TTY_IsEnabled(int serialPortNumber)
  932. {
  933.         return handleToPort[serialPortNumber]->enabled;
  934. }
  935.  
  936.  
  937. qboolean TTY_IsModem(int serialPortNumber)
  938. {
  939.         return handleToPort[serialPortNumber]->useModem;
  940. }
  941.  
  942.  
  943. qboolean TTY_OutputQueueIsEmpty(int handle)
  944. {
  945.         return EMPTY(handleToPort[handle]->outputQueue);
  946. }
  947.  
  948.  
  949. void Com_f (void)
  950. {
  951.         ComPort *p;
  952.         int             portNumber;
  953.         int             i;
  954.         int             n;
  955.  
  956.         // first, determine which port they're messing with
  957.         portNumber = Q_atoi(Cmd_Argv (0) + 3) - 1;
  958.         if (portNumber > 1)
  959.                 return;
  960.         p = handleToPort[portNumber];
  961.  
  962.         if (Cmd_Argc() == 1)
  963.         {
  964.                 Con_Printf("Settings for COM%i\n", portNumber + 1);
  965.                 Con_Printf("enabled:   %s\n", p->enabled ? "true" : "false");
  966.                 Con_Printf("uart:      ");
  967.                 if (p->uartType == UART_AUTO)
  968.                         Con_Printf("auto\n");
  969.                 else if (p->uartType == UART_8250)
  970.                         Con_Printf("8250\n");
  971.                 else
  972.                         Con_Printf("16550\n");
  973.                 Con_Printf("port:      %x\n", p->uart);
  974.                 Con_Printf("irq:       %i\n", p->irq);
  975.                 Con_Printf("baud:      %i\n", 115200 / p->baudBits);   
  976.                 Con_Printf("CTS:       %s\n", (p->modemStatusIgnore & MSR_CTS) ? "ignored" : "honored");
  977.                 Con_Printf("DSR:       %s\n", (p->modemStatusIgnore & MSR_DSR) ? "ignored" : "honored");
  978.                 Con_Printf("CD:        %s\n", (p->modemStatusIgnore & MSR_CD) ? "ignored" : "honored");
  979.                 if (p->useModem)
  980.                 {
  981.                         Con_Printf("type:      Modem\n");
  982.                         Con_Printf("clear:     %s\n", p->clear);
  983.                         Con_Printf("startup:   %s\n", p->startup);
  984.                         Con_Printf("shutdown:  %s\n", p->shutdown);
  985.                 }
  986.                 else
  987.                         Con_Printf("type:      Direct connect\n");
  988.  
  989.                 return;
  990.         }
  991.  
  992.  
  993.         if (Cmd_CheckParm ("disable"))
  994.         {
  995.                 if (p->enabled)
  996.                         ComPort_Disable(p);
  997.                 p->modemInitialized = false;
  998.                 return;
  999.         }
  1000.  
  1001.         if (Cmd_CheckParm ("reset"))
  1002.         {
  1003.                 ComPort_Disable(p);
  1004.                 ResetComPortConfig (p);
  1005.                 return;
  1006.         }
  1007.  
  1008.         if ((i = Cmd_CheckParm ("port")) != 0)
  1009.         {
  1010.                 if (p->enabled)
  1011.                         {
  1012.                                 Con_Printf("COM port must be disabled to change port\n");
  1013.                                 return;
  1014.                         }
  1015.                 p->uart = Q_atoi (Cmd_Argv (i+1));
  1016.         }
  1017.  
  1018.         if ((i = Cmd_CheckParm ("irq")) != 0)
  1019.         {
  1020.                 if (p->enabled)
  1021.                         {
  1022.                                 Con_Printf("COM port must be disabled to change irq\n");
  1023.                                 return;
  1024.                         }
  1025.                 p->irq = Q_atoi (Cmd_Argv (i+1));
  1026.         }
  1027.  
  1028.         if ((i = Cmd_CheckParm ("baud")) != 0)
  1029.         {
  1030.                 if (p->enabled)
  1031.                         {
  1032.                                 Con_Printf("COM port must be disabled to change baud\n");
  1033.                                 return;
  1034.                         }
  1035.                 n = Q_atoi (Cmd_Argv (i+1));
  1036.                 if (n == 0)
  1037.                         Con_Printf("Invalid baud rate specified\n");
  1038.                 else
  1039.                         p->baudBits = 115200 / n;
  1040.         }
  1041.  
  1042.         if (Cmd_CheckParm ("8250"))
  1043.         {
  1044.                 if (p->enabled)
  1045.                         {
  1046.                                 Con_Printf("COM port must be disabled to change uart\n");
  1047.                                 return;
  1048.                         }
  1049.                 p->uartType = UART_8250;
  1050.                 }
  1051.         if (Cmd_CheckParm ("16550"))
  1052.         {
  1053.                 if (p->enabled)
  1054.                         {
  1055.                                 Con_Printf("COM port must be disabled to change uart\n");
  1056.                                 return;
  1057.                         }
  1058.                 p->uartType = UART_16550;
  1059.         }
  1060.         if (Cmd_CheckParm ("auto"))
  1061.         {
  1062.                 if (p->enabled)
  1063.                         {
  1064.                                 Con_Printf("COM port must be disabled to change uart\n");
  1065.                                 return;
  1066.                         }
  1067.                 p->uartType = UART_AUTO;
  1068.         }
  1069.  
  1070.         if (Cmd_CheckParm ("pulse"))
  1071.                 p->dialType = 'P';
  1072.         if (Cmd_CheckParm ("tone"))
  1073.                 p->dialType = 'T';
  1074.  
  1075.         if (Cmd_CheckParm ("direct"))
  1076.                 p->useModem = false;
  1077.         if (Cmd_CheckParm ("modem"))
  1078.                 p->useModem = true;
  1079.  
  1080.         if ((i = Cmd_CheckParm ("clear")) != 0)
  1081.         {
  1082.                 Q_strncpy (p->clear, Cmd_Argv (i+1), 16);
  1083.         }
  1084.  
  1085.         if ((i = Cmd_CheckParm ("startup")) != 0)
  1086.         {
  1087.                 Q_strncpy (p->startup, Cmd_Argv (i+1), 32);
  1088.                 p->modemInitialized = false;
  1089.         }
  1090.  
  1091.         if ((i = Cmd_CheckParm ("shutdown")) != 0)
  1092.         {
  1093.                 Q_strncpy (p->shutdown, Cmd_Argv (i+1), 16);
  1094.         }
  1095.  
  1096.         if (Cmd_CheckParm ("-cts"))
  1097.         {
  1098.                 p->modemStatusIgnore |= MSR_CTS;
  1099.                 p->modemStatus |= MSR_CTS;
  1100.         }
  1101.  
  1102.         if (Cmd_CheckParm ("+cts"))
  1103.         {
  1104.                 p->modemStatusIgnore &= (~MSR_CTS);
  1105.                 p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore;
  1106.         }
  1107.  
  1108.         if (Cmd_CheckParm ("-dsr"))
  1109.         {
  1110.                 p->modemStatusIgnore |= MSR_DSR;
  1111.                 p->modemStatus |= MSR_DSR;
  1112.         }
  1113.  
  1114.         if (Cmd_CheckParm ("+dsr"))
  1115.         {
  1116.                 p->modemStatusIgnore &= (~MSR_DSR);
  1117.                 p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore;
  1118.         }
  1119.  
  1120.         if (Cmd_CheckParm ("-cd"))
  1121.         {
  1122.                 p->modemStatusIgnore |= MSR_CD;
  1123.                 p->modemStatus |= MSR_CD;
  1124.         }
  1125.  
  1126.         if (Cmd_CheckParm ("+cd"))
  1127.         {
  1128.                 p->modemStatusIgnore &= (~MSR_CD);
  1129.                 p->modemStatus = (inportb (p->uart + MODEM_STATUS_REGISTER) & MODEM_STATUS_MASK) | p->modemStatusIgnore;
  1130.         }
  1131.  
  1132.         if (Cmd_CheckParm ("enable"))
  1133.         {
  1134.                 if (!p->enabled)
  1135.                         ComPort_Enable(p);
  1136.                 if (p->useModem && !p->modemInitialized)
  1137.                         Modem_Init (p);
  1138.         }
  1139. }
  1140.  
  1141.  
  1142. int TTY_Init(void)
  1143. {
  1144.         int             n;
  1145.         ComPort *p;
  1146.  
  1147.         for (n = 0; n < NUM_COM_PORTS; n++)
  1148.         {
  1149.                 p = (ComPort *)Hunk_AllocName(sizeof(ComPort), "comport");
  1150.                 if (p == NULL)
  1151.                         Sys_Error("Hunk alloc failed for com port\n");
  1152.                 p->next = portList;
  1153.                 portList = p;
  1154.                 handleToPort[n] = p;
  1155.                 p->portNumber = n;
  1156.                 p->dialType = 'T';
  1157.                 sprintf(p->name, "com%u", n+1);
  1158.                 Cmd_AddCommand (p->name, Com_f);
  1159.                 ResetComPortConfig (p);
  1160.         }
  1161.  
  1162.         GetComPortConfig = TTY_GetComPortConfig;
  1163.         SetComPortConfig = TTY_SetComPortConfig;
  1164.         GetModemConfig = TTY_GetModemConfig;
  1165.         SetModemConfig = TTY_SetModemConfig;
  1166.  
  1167.         return 0;
  1168. }
  1169.  
  1170.  
  1171. void TTY_Shutdown(void)
  1172. {
  1173.         int             n;
  1174.         ComPort *p;
  1175.  
  1176.         for (n = 0; n < NUM_COM_PORTS; n++)
  1177.         {
  1178.                 p = handleToPort[n];
  1179.                 if (p->enabled)
  1180.                 {
  1181.                         while (p->modemConnected)
  1182.                                 NET_Poll();
  1183.                         ComPort_Disable (p);
  1184.                 }
  1185.         }
  1186. }
  1187.  
  1188.  
  1189. static int Modem_Command(ComPort *p, char *commandString)
  1190. {
  1191.         byte    b;
  1192.  
  1193.         if (CheckStatus (p))
  1194.                 return -1;
  1195.  
  1196.         disable();
  1197.         p->outputQueue.head = p->outputQueue.tail = 0;
  1198.         p->inputQueue.head = p->inputQueue.tail = 0;
  1199.         enable();
  1200.         p->bufferUsed = 0;
  1201.  
  1202.         while (*commandString)
  1203.                 ENQUEUE (p->outputQueue, *commandString++);
  1204.         ENQUEUE (p->outputQueue, '\r');
  1205.  
  1206.         // get the transmit rolling
  1207.         DEQUEUE (p->outputQueue, b);
  1208.         outportb(p->uart, b);
  1209.  
  1210.         return 0;
  1211. }
  1212.  
  1213.  
  1214. static char *Modem_Response(ComPort *p)
  1215. {
  1216.         byte    b;
  1217.  
  1218.         if (CheckStatus (p))
  1219.                 return NULL;
  1220.  
  1221.         while (! EMPTY(p->inputQueue))
  1222.         {
  1223.                 DEQUEUE (p->inputQueue, b);
  1224.  
  1225.                 if (p->bufferUsed == (sizeof(p->buffer) - 1))
  1226.                         b = '\r';
  1227.  
  1228.                 if (b == '\r' && p->bufferUsed)
  1229.                 {
  1230.                         p->buffer[p->bufferUsed] = 0;
  1231.                         Con_Printf("%s\n", p->buffer);
  1232.                         SCR_UpdateScreen ();
  1233.                         p->bufferUsed = 0;
  1234.                         return p->buffer;
  1235.                 }
  1236.  
  1237.                 if (b < ' ' || b > 'z')
  1238.                         continue;
  1239.                 p->buffer[p->bufferUsed] = b;
  1240.                 p->bufferUsed++;
  1241.         }
  1242.  
  1243.         return NULL;
  1244. }
  1245.  
  1246.  
  1247. static void Modem_Hangup2(ComPort *p);
  1248. static void Modem_Hangup3(ComPort *p);
  1249. static void Modem_Hangup4(ComPort *p);
  1250.  
  1251. static void Modem_Hangup(ComPort *p)
  1252. {
  1253.         Con_Printf("Hanging up modem...\n");
  1254.         disable();
  1255.         p->modemRang = false;
  1256.         p->outputQueue.head = p->outputQueue.tail = 0;
  1257.         p->inputQueue.head = p->inputQueue.tail = 0;
  1258.         outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) & ~MCR_DTR);
  1259.         enable();
  1260.         p->poll.procedure = Modem_Hangup2;
  1261.         p->poll.arg = p;
  1262.         SchedulePollProcedure(&p->poll, 1.5);
  1263. }
  1264.  
  1265. static void Modem_Hangup2(ComPort *p)
  1266. {
  1267.         outportb(p->uart + MODEM_CONTROL_REGISTER, inportb(p->uart + MODEM_CONTROL_REGISTER) | MCR_DTR);
  1268.         Modem_Command(p, "+++");
  1269.         p->poll.procedure = Modem_Hangup3;
  1270.         SchedulePollProcedure(&p->poll, 1.5);
  1271. }
  1272.  
  1273. static void Modem_Hangup3(ComPort *p)
  1274. {
  1275.         Modem_Command(p, p->shutdown);
  1276.         p->poll.procedure = Modem_Hangup4;
  1277.         SchedulePollProcedure(&p->poll, 1.5);
  1278. }
  1279.  
  1280. static void Modem_Hangup4(ComPort *p)
  1281. {
  1282.         Modem_Response(p);
  1283.         Con_Printf("Hangup complete\n");
  1284.         p->modemConnected = false;
  1285. }
  1286.