tyLib.c
上传用户:nvosite88
上传日期:2007-01-17
资源大小:4983k
文件大小:43k
源码类别:

VxWorks

开发平台:

C/C++

  1. /* tyLib.c - tty driver support library */
  2. /* Copyright 1984 - 2001 Wind River Systems, Inc. */
  3. #include "copyright_wrs.h"
  4. /*
  5. modification history
  6. --------------------
  7. 03j,07feb01,spm  merged from version 03i of tor2_0_x branch (base version 03h):
  8.                  added removal of pty device (SPR #28675)
  9. 03i,30jun99,cno  telnet to output return-newline instead of newline-return
  10.                  (SPR27682)
  11. 03h,14jul97,dgp  doc: correct bold, S_ioLib_UNKNOWN_REQUEST, & "^" to "CTRL-"
  12. 03g,20jun96,jmb  harmless errno from tyFlushRd getting back to users (SPR #6698)
  13. 03f,09dec94,rhp  fixed doc for FIOCANCEL (SPR #3828)
  14.                  fixed library descriptions of tyIRd() and tyITx() (SPR #2484)
  15. 03e,19jul94,dvs  fixed doc for FIORBUFSET and FIOWBUFSET (SPR #2444).
  16. 03d,21jan93,jdi  documentation cleanup for 5.1.
  17. 03c,24oct92,jcf  added peculiar 3xx hack to make serial driver work.
  18. 03b,23aug92,jcf  removed select stuff so tyLib will standalone.
  19.  made excJobAdd call indirect.
  20. 03a,18jul92,smb  Changed errno.h to errnoLib.h.
  21. 03z,04jul92,jcf  scalable/ANSI/cleanup effort.
  22. 03y,26may92,rrr  the tree shuffle
  23. 03x,04oct91,rrr  passed through the ansification filter
  24.                   -changed functions to ansi style
  25.   -changed VOID to void
  26.   -changed copyright notice
  27. 03w,01aug91,yao  fixed to pass 6 args to excJobAdd() call.
  28. 03v,03apr91,jaa  documentation cleanup; doc review by dnw.
  29. 03u,10aug90,kdl  added forward declarations for functions returning void.
  30. 03t,15jul90,dnw  lint: removed unused variable in tyIoctl
  31. 03s,05jul90,dnw  fixed potential races in read and write.
  32.  isolated all interrupt lockouts in 3 new routines:
  33. tyRdXoff/tyWrtXOff/tyTxStartup
  34.  optimized code.
  35.  rewrote library documentation.
  36. 03r,26jun90,dab  removed tyDevDelete().
  37. 03q,26jun90,jcf  changed binary semaphore interface.
  38. 03p,02apr90,rdc  changed to use binary semaphores.
  39.     hjb  beefed up ioctl documentation.
  40. 03o,29mar90,rdc  reworked to lower interrupt latency.
  41.  took out "semNeeded" mechanism, created seperate mutual
  42.  exclusion and synchronization semaphores.  Interrupt level
  43.  now checks to make sure task level isn't flushing or
  44.  replacing ring buffers.
  45. 03n,19mar90,dab  added tyDevDelete().
  46. 03m,16mar90,rdc  added select support.
  47. 03l,27jul89,hjb  added FIOPROTOHOOK, FIOPROTOARG, FIOWFLUSH, FIORFLUSH,
  48.  FIORBUFSET, FIOWBUFSET support for SLIP.
  49. 03k,14oct88,gae  fixed bug in FIOCANCEL; couldn't cancel twice in a row.
  50. 03j,07sep88,gae  documentation.
  51. 03i,01sep88,gae  documentation; changed RNG_MACRO parameters to int's.
  52. 03h,30may88,dnw  changed to v4 names.
  53. 03g,28may88,dnw  removed NOT_GENERIC stuff.
  54. 03f,04may88,jcf  changed semInits to semClear.
  55. 03e,29mar88,gae  added FIOISATTY to tyIoctl repertoire.
  56. 03d,26feb88,jcf  changed reboot to excToDoAdd (reboot ..) so reboot routines
  57.   can print stuff out.
  58. 03c,22feb88,dnw  fix coding conventions to keep f2cgen happy.
  59. 03b,23nov87,ecs  added VARARGS2 to tyIoctl to keep lint happy.
  60. 03a,23oct87,ecs  fixed mangen problems in tlRdRaw/tyRead & tyIRdRaw/tyIRd.
  61.  documentation.
  62.     jcf  changed call sysToMonitor into reboot
  63. 02z,03oct87,gae  added FIO[GS}ETOPTIONS.
  64. 02y,24jul87,gae  made ty{Backspace,DeleteLine,Eof}Char's global for shellLib.c.
  65. 02x,25jun87,ecs  changed tyIRdRaw to return ERROR on full ring buffer.
  66. 02w,05jun87,ecs  added FIOCANCEL to tyIoctl.
  67. 02v,13may87,dnw  fixed checksum bug caused by new "correct" compiler.
  68.    +gae
  69. 02u,23mar87,jlf  documentation.
  70. 02t,23feb87,jlf  improved documentation of tyAbortFunc.
  71. ...deleted pre 87 history - see RCS
  72. */
  73. /*
  74. This library provides routines used to implement drivers for serial
  75. devices.  It provides all the necessary device-independent functions of a
  76. normal serial channel, including:
  77. .PD .1
  78. .iP "" 4
  79. ring buffering of input and output
  80. .iP
  81. raw mode
  82. .iP
  83. optional line mode with backspace and line-delete functions
  84. .iP
  85. optional processing of X-on/X-off
  86. .iP
  87. optional RETURN/LINEFEED conversion
  88. .iP
  89. optional echoing of input characters
  90. .iP
  91. optional stripping of the parity bit from 8-bit input
  92. .iP
  93. optional special characters for shell abort and system restart
  94. .PD
  95. .LP
  96. Most of the routines in this library are called only by device drivers.
  97. Functions that normally might be called by an application or
  98. interactive user are the routines to set special characters, ty...Set().
  99. USE IN SERIAL DEVICE DRIVERS
  100. Each device that uses tyLib is described by a data structure of type TY_DEV.
  101. This structure begins with an I/O system device header so that it can be added
  102. directly to the I/O system's device list.  A driver calls tyDevInit() to
  103. initialize a TY_DEV structure for a specific device and then calls iosDevAdd()
  104. to add the device to the I/O system.
  105. The call to tyDevInit() takes three parameters: the pointer to the TY_DEV
  106. structure to initialize, the desired size of the read and write ring buffers,
  107. and the address of a transmitter start-up routine.  This routine will be
  108. called when characters are added for output and the transmitter is idle.  
  109. Thereafter, the driver can call the following routines to perform the
  110. usual device functions:
  111. .iP "tyRead()" 12
  112. user read request to get characters that have been input
  113. .iP "tyWrite()"
  114. user write request to put characters to be output
  115. .iP "tyIoctl()"
  116. user I/O control request
  117. .iP "tyIRd()"
  118. interrupt-level routine to get an input character
  119. .iP "tyITx()"
  120. interrupt-level routine to deliver the next output character
  121. .LP
  122. Thus, tyRead(), tyWrite(), and tyIoctl() are called from the driver's read,
  123. write, and I/O control functions.  The routines tyIRd() and tyITx() are called
  124. from the driver's interrupt handler in response to receive and transmit
  125. interrupts, respectively.
  126. Examples of using tyLib in a driver can be found in the source file(s) 
  127. included by tyCoDrv.  Source files are located in src/drv/serial.
  128. TTY OPTIONS
  129. A full range of options affects the behavior of tty devices.  These
  130. options are selected by setting bits in the device option word using the
  131. FIOSETOPTIONS function in the ioctl() routine (see "I/O Control Functions"
  132. below for more information).  The following is a list of available
  133. options.  The options are defined in the header file ioLib.h.
  134. .iP `OPT_LINE' 20 3
  135. Selects line mode.  A tty device operates in one of two modes:  raw mode
  136. (unbuffered) or line mode.  Raw mode is the default.  In raw mode, each byte
  137. of input from the device is immediately available to readers, and the input is
  138. not modified except as directed by other options below.  In line mode, input
  139. from the device is not available to readers until a NEWLINE character is
  140. received, and the input may be modified by backspace, line-delete, and
  141. end-of-file special characters.
  142. .iP `OPT_ECHO'
  143. Causes all input characters to be echoed to the output of the same channel.
  144. This is done simply by putting incoming characters in the output ring as well
  145. as the input ring.  If the output ring is full, the echoing is lost without
  146. affecting the input.
  147. .iP `OPT_CRMOD'
  148. C language conventions use the NEWLINE character as the line terminator
  149. on both input and output.  Most terminals, however, supply a RETURN character
  150. when the return key is hit, and require both a RETURN and a LINEFEED character
  151. to advance the output line.  This option enables the appropriate translation:
  152. NEWLINEs are substituted for input RETURN characters, and NEWLINEs in the
  153. output file are automatically turned into a RETURN-LINEFEED sequence.
  154. .iP `OPT_TANDEM'
  155. Causes the driver to generate and respond to the special flow control
  156. characters CTRL-Q and CTRL-S in what is commonly known as X-on/X-off protocol.  Receipt
  157. of a CTRL-S input character will suspend output to that channel.  Subsequent receipt
  158. of a CTRL-Q will resume the output.  Also, when the VxWorks input buffer is almost
  159. full, a CTRL-S will be output to signal the other side to suspend transmission.
  160. When the input buffer is almost empty, a CTRL-Q will be output to signal the other
  161. side to resume transmission.
  162. .iP `OPT_7_BIT'
  163. Strips the most significant bit from all bytes input from the device.
  164. .iP `OPT_MON_TRAP'
  165. Enables the special monitor trap character, by default CTRL-X.  When this character
  166. is received and this option is enabled, VxWorks will trap to the ROM resident
  167. monitor program.  Note that this is quite drastic.  All normal VxWorks
  168. functioning is suspended, and the computer system is entirely controlled by the
  169. monitor.  Depending on the particular monitor, it may or may not be possible to
  170. restart VxWorks from the point of interruption.  The default monitor trap
  171. character can be changed by calling tyMonitorTrapSet().
  172. .iP `OPT_ABORT'
  173. Enables the special shell abort character, by default CTRL-C.  When this character
  174. is received and this option is enabled, the VxWorks shell is restarted.  This is
  175. useful for freeing a shell stuck in an unfriendly routine, such as one caught in
  176. an infinite loop or one that has taken an unavailable semaphore.  For more
  177. information, see the
  178. .I "VxWorks Programmer's Guide: Shell."
  179. .iP `OPT_TERMINAL'
  180. This is not a separate option bit.  It is the value of the option word
  181. with all the above bits set.
  182. .iP `OPT_RAW'
  183. This is not a separate option bit.  It is the value of the option word with
  184. none of the above bits set.
  185. I/O CONTROL FUNCTIONS
  186. The tty devices respond to the following ioctl() functions.  The functions
  187. are defined in the header ioLib.h.
  188. .iP `FIOGETNAME' 20 3
  189. Gets the file name of the file descriptor and copies it to the buffer 
  190. referenced to by <nameBuf>:
  191. .CS
  192.     status = ioctl (fd, FIOGETNAME, &nameBuf);
  193. .CE
  194. This function is common to all file descriptors for all devices.
  195. .LP
  196. .iP "`FIOSETOPTIONS', `FIOOPTIONS'" 20
  197. Sets the device option word to the specified argument.  For example, the call:
  198. .CS
  199.     status = ioctl (fd, FIOOPTIONS, OPT_TERMINAL);
  200.     status = ioctl (fd, FIOSETOPTIONS, OPT_TERMINAL);
  201. .CE
  202. enables all the tty options described above, putting the device in a "normal"
  203. terminal mode.  If the line protocol  (OPT_LINE) is changed, the input
  204. buffer is flushed.  The various options are described in ioLib.h.
  205. .iP `FIOGETOPTIONS'
  206. Returns the current device option word:
  207. .CS
  208.     options = ioctl (fd, FIOGETOPTIONS, 0);
  209. .CE
  210. .iP `FIONREAD'
  211. Copies to <nBytesUnread> the number of bytes available to be read in the
  212. device's input buffer:
  213. .CS
  214.     status = ioctl (fd, FIONREAD, &nBytesUnread);
  215. .CE
  216. In line mode (OPT_LINE set), the FIONREAD function actually returns the number
  217. of characters available plus the number of lines in the buffer.  Thus, if five
  218. lines of just NEWLINEs were in the input buffer, it would return the value 10
  219. (5 characters + 5 lines).
  220. .iP `FIONWRITE'
  221. Copies to <nBytes> the number of bytes queued to be output in the device's
  222. output buffer:
  223. .CS
  224.     status = ioctl (fd, FIONWRITE, &nBytes);
  225. .CE
  226. .iP `FIOFLUSH'
  227. Discards all the bytes currently in both the input and the output buffers:
  228. .CS
  229.     status = ioctl (fd, FIOFLUSH, 0);
  230. .CE
  231. .iP `FIOWFLUSH'
  232. Discards all the bytes currently in the output buffer:
  233. .CS
  234.     status = ioctl (fd, FIOWFLUSH, 0);
  235. .CE
  236. .iP `FIORFLUSH'
  237. Discards all the bytes currently in the input buffers:
  238. .CS
  239.     status = ioctl (fd, FIORFLUSH, 0);
  240. .CE
  241. .iP `FIOCANCEL'
  242. Cancels a read or write.  A task blocked on a read or write may be
  243. released by a second task using this ioctl() call.  For example, a
  244. task doing a read can set a watchdog timer before attempting the read;
  245. the auxiliary task would wait on a semaphore.  The watchdog routine
  246. can give the semaphore to the auxiliary task, which would then use the
  247. following call on the appropriate file descriptor:
  248. .CS
  249.     status = ioctl (fd, FIOCANCEL, 0);
  250. .CE
  251. .iP `FIOBAUDRATE'
  252. Sets the baud rate of the device to the specified argument.  For example, the
  253. call:
  254. .CS
  255.     status = ioctl (fd, FIOBAUDRATE, 9600);
  256. .CE
  257. Sets the device to operate at 9600 baud.  This request has no meaning on a
  258. pseudo terminal.
  259. .iP `FIOISATTY'
  260. Returns TRUE for a tty device:
  261. .CS
  262.     status = ioctl (fd, FIOISATTY, 0);
  263. .CE
  264. .iP `FIOPROTOHOOK'
  265. Adds a protocol hook function to be called for each input character.
  266. <pfunction> is a pointer to the protocol hook routine which takes two
  267. arguments of type <int> and returns values of type STATUS (TRUE or
  268. FALSE).  The first argument passed is set by the user via the FIOPROTOARG
  269. function.  The second argument is the input character.  If no further
  270. processing of the character is required by the calling routine (the input
  271. routine of the driver), the protocol hook routine <pFunction> should
  272. return TRUE.  Otherwise, it should return FALSE:
  273. .CS
  274.     status = ioctl (fd, FIOPROTOHOOK, pFunction);
  275. .CE
  276. .iP `FIOPROTOARG'
  277. Sets the first argument to be passed to the protocol hook routine set by
  278. FIOPROTOHOOK function:
  279. .CS
  280.     status = ioctl (fd, FIOPROTOARG, arg);
  281. .CE
  282. .iP `FIORBUFSET'
  283. Changes the size of the receive-side buffer to <size>:
  284. .CS
  285.     status = ioctl (fd, FIORBUFSET, size);
  286. .CE
  287. .iP `FIOWBUFSET'
  288. Changes the size of the send-side buffer to <size>:
  289. .CS
  290.     status = ioctl (fd, FIOWBUFSET, size);
  291. .CE
  292. .LP
  293. Any other ioctl() request will return an error and set the status to
  294. S_ioLib_UNKNOWN_REQUEST.
  295. INCLUDE FILES: tyLib.h, ioLib.h
  296. SEE ALSO: ioLib, iosLib, tyCoDrv,
  297. .pG "I/O System"
  298. */
  299. /* LINTLIBRARY */
  300. #include "vxWorks.h"
  301. #include "ioLib.h"
  302. #include "memLib.h"
  303. #include "rngLib.h"
  304. #include "wdLib.h"
  305. #include "sysLib.h"
  306. #include "excLib.h"
  307. #include "intLib.h"
  308. #include "string.h"
  309. #include "errnoLib.h"
  310. #include "rebootLib.h"
  311. #include "selectLib.h"
  312. #include "tyLib.h"
  313. #include "private/funcBindP.h"
  314. /* special characters */
  315. #define XON 0x11 /* ctrl-Q XON handshake */
  316. #define XOFF 0x13 /* ctrl-S XOFF handshake */
  317. /* global variables */
  318. char tyBackspaceChar   = 0x08; /* default is control-H */
  319. char tyDeleteLineChar   = 0x15; /* default is control-U */
  320. char tyEofChar   = 0x04; /* default is control-D */
  321. int mutexOptionsTyLib = SEM_Q_FIFO | SEM_DELETE_SAFE;
  322. /* local variables */
  323. LOCAL char tyAbortChar   = 0x03; /* default is control-C */
  324. LOCAL char tyMonTrapChar  = 0x18; /* default is control-X */
  325. LOCAL int tyXoffThreshold = 80; /* max bytes free in input buffer
  326.  * before XOFF will be sent in
  327.  * OPT_TANDEM mode */
  328. LOCAL int tyXonThreshold  = 100; /* min bytes free in input buffer
  329.  * before XON will be sent in
  330.  * OPT_TANDEM mode */
  331. LOCAL int tyWrtThreshold  = 20; /* min bytes free in output buffer
  332.  * before the next writer will
  333.  * be enabled */
  334. LOCAL FUNCPTR tyAbortFunc = NULL; /* function to call when abort char
  335.  * received */
  336. /* we keep track of the maximum number of chars received after we sent
  337.  * out xoff.  This was used to debug a lazy unix system that
  338.  * sent as many as 60 characters after we had sent xoff.
  339.  * we'll leave the code in just for grins.
  340.  */
  341. LOCAL int tyXoffChars = 0; /** TEMP **/
  342. LOCAL int tyXoffMax   = 0; /** TEMP **/
  343. /* forward static functions */
  344. static void tyFlush (TY_DEV_ID pTyDev);
  345. static void tyFlushRd (TY_DEV_ID pTyDev);
  346. static void tyFlushWrt (TY_DEV_ID pTyDev);
  347. static void tyRdXoff (TY_DEV_ID pTyDev, BOOL xoff);
  348. static void tyWrtXoff (TY_DEV_ID pTyDev, BOOL xoff);
  349. static void tyTxStartup (TY_DEV_ID pTyDev);
  350. /*******************************************************************************
  351. *
  352. * tyDevInit - initialize the tty device descriptor
  353. *
  354. * This routine initializes a tty device descriptor according to the
  355. * specified parameters.  The initialization includes allocating read and
  356. * write buffers of the specified sizes from the memory pool,
  357. * and initializing their respective buffer descriptors.
  358. * The semaphores are initialized and the write semaphore is given
  359. * to enable writers.  Also, the transmitter start-up routine pointer is set
  360. * to the specified routine.  All other fields in the descriptor are zeroed.
  361. *
  362. * This routine should be called only by serial drivers.
  363. *
  364. * RETURNS
  365. * OK, or
  366. * ERROR if there is not enough memory to allocate data structures.
  367. */
  368. STATUS tyDevInit
  369.     (
  370.     FAST TY_DEV_ID pTyDev, /* ptr to tty dev descriptor to init */
  371.     int rdBufSize,         /* size of read buffer in bytes     */
  372.     int wrtBufSize,        /* size of write buffer in bytes    */
  373.     FUNCPTR txStartup      /* device transmit start-up routine */
  374.     )
  375.     {
  376.     /* clear out device */
  377.     bzero ((char *) pTyDev, sizeof (TY_DEV));
  378.     /* allocate read and write ring buffers */
  379.     if ((pTyDev->wrtBuf = rngCreate (wrtBufSize)) == NULL)
  380. return (ERROR);
  381.     if ((pTyDev->rdBuf = rngCreate (rdBufSize)) == NULL)
  382. return (ERROR);
  383.     /* initialize rest of device descriptor */
  384.     pTyDev->txStartup = txStartup;
  385.     /* initialize semaphores */
  386.     semBInit (&pTyDev->rdSyncSem, SEM_Q_PRIORITY, SEM_EMPTY);
  387.     semBInit (&pTyDev->wrtSyncSem, SEM_Q_PRIORITY, SEM_EMPTY);
  388.     semMInit (&pTyDev->mutexSem, mutexOptionsTyLib);
  389.     /* initialize the select stuff */
  390.     if (_func_selWakeupListInit != NULL)
  391. (* _func_selWakeupListInit) (&pTyDev->selWakeupList);
  392.     tyFlush (pTyDev);
  393.     return (OK);
  394.     }
  395. /*******************************************************************************
  396. *
  397. * tyDevRemove - remove the tty device descriptor
  398. *
  399. * This routine removes an existing tty device descriptor. It releases the
  400. * read and write buffers and the descriptor data structure.
  401. *
  402. * RETURNS
  403. * OK, or ERROR if expected data structures are not found
  404. */
  405. STATUS tyDevRemove
  406.     (
  407.     TY_DEV_ID pTyDev /* ptr to tty dev descriptor to remove */
  408.     )
  409.     {
  410.     /* clear out device */
  411.     if (pTyDev->rdBuf == NULL)
  412. return (ERROR);
  413.     if (pTyDev->wrtBuf == NULL)
  414. return (ERROR);
  415.     /* remove read and write ring buffers */
  416.     semTake (&pTyDev->mutexSem, WAIT_FOREVER);
  417.     pTyDev->rdState.flushingRdBuf = TRUE;
  418.     pTyDev->wrtState.flushingWrtBuf = TRUE;
  419.     if (pTyDev->rdBuf)
  420.         rngDelete (pTyDev->rdBuf);
  421.     if (pTyDev->wrtBuf)
  422.         rngDelete (pTyDev->wrtBuf);
  423.     semGive (&pTyDev->mutexSem);
  424.     return (OK);
  425.     }
  426. /*******************************************************************************
  427. *
  428. * tyFlush - clear out a tty device descriptor's buffers
  429. *
  430. * This routine resets a tty device's buffers to be empty and in the
  431. * "initial" state.  Thus any characters input but not read by a task,
  432. * and those written by a task but not yet output, are lost.
  433. *
  434. * RETURNS: N/A
  435. */
  436. LOCAL void tyFlush
  437.     (
  438.     FAST TY_DEV_ID pTyDev       /* ptr to tty dev descriptor to clear */
  439.     )
  440.     {
  441.     tyFlushRd (pTyDev);
  442.     tyFlushWrt (pTyDev);
  443.     }
  444. /*******************************************************************************
  445. *
  446. * tyFlushRd - clear out a tty device descriptor's read buffer
  447. */
  448. LOCAL void tyFlushRd
  449.     (
  450.     FAST TY_DEV_ID pTyDev       /* ptr to tty dev descriptor to clear */
  451.     )
  452.     {
  453.     int prevErrno;
  454.     /* get exclusive access to the device */
  455.     semTake (&pTyDev->mutexSem, WAIT_FOREVER);
  456.     /* mark as flushing so read interrupts won't attempt to manipulate ring */
  457.     pTyDev->rdState.flushingRdBuf = TRUE;
  458.     rngFlush (pTyDev->rdBuf);
  459.     prevErrno = errno;
  460.     semTake (&pTyDev->rdSyncSem, NO_WAIT);
  461.     if (errno == S_objLib_OBJ_UNAVAILABLE)  /* OK if semaphore not avail. */
  462. errno = prevErrno;
  463.     pTyDev->lnNBytes = 0;
  464.     pTyDev->lnBytesLeft = 0;
  465.     tyRdXoff (pTyDev, FALSE); /* output an XON if necessary */
  466.     pTyDev->rdState.flushingRdBuf = FALSE;
  467.     semGive (&pTyDev->mutexSem);
  468.     }
  469. /*******************************************************************************
  470. *
  471. * tyFlushWrt - clear out a tty device descriptor's write buffer
  472. */
  473. LOCAL void tyFlushWrt
  474.     (
  475.     FAST TY_DEV_ID pTyDev       /* ptr to tty dev descriptor to clear */
  476.     )
  477.     {
  478.     /* get exclusive access to the device */
  479.     semTake (&pTyDev->mutexSem, WAIT_FOREVER);
  480.     /* mark as flushing so tx interrupts won't attempt to manipulate ring */
  481.     pTyDev->wrtState.flushingWrtBuf = TRUE;
  482.     rngFlush (pTyDev->wrtBuf);
  483.     semGive (&pTyDev->wrtSyncSem);
  484.     pTyDev->wrtState.flushingWrtBuf = FALSE;
  485.     semGive (&pTyDev->mutexSem);
  486.     if (_func_selWakeupAll != NULL) /* wake up any writers in select */
  487. (* _func_selWakeupAll) (&pTyDev->selWakeupList, SELWRITE);
  488.     }
  489. /*******************************************************************************
  490. *
  491. * tyAbortFuncSet - set the abort function
  492. *
  493. * This routine sets the function that will be called when the abort
  494. * character is received on a tty.  There is only one global abort function,
  495. * used for any tty on which OPT_ABORT is enabled.  When the abort character is
  496. * received from a tty with OPT_ABORT set, the function specified in <func> will
  497. * be called, with no parameters, from interrupt level.
  498. *
  499. * Setting an abort function of NULL will disable the abort function.
  500. *
  501. * RETURNS: N/A
  502. *
  503. * SEE ALSO: tyAbortSet()
  504. */
  505. void tyAbortFuncSet
  506.     (
  507.     FUNCPTR func        /* routine to call when abort char received */
  508.     )
  509.     {
  510.     tyAbortFunc = func;
  511.     }
  512. /*******************************************************************************
  513. *
  514. * tyAbortSet - change the abort character
  515. *
  516. * This routine sets the abort character to <ch>.
  517. * The default abort character is CTRL-C.
  518. *
  519. * Typing the abort character to any device whose OPT_ABORT option is set
  520. * will cause the shell task to be killed and restarted.
  521. * Note that the character set by this routine applies to all devices
  522. * whose handlers use the standard tty package tyLib.
  523. *
  524. * RETURNS: N/A
  525. *
  526. * SEE ALSO: tyAbortFuncSet()
  527. */
  528. void tyAbortSet
  529.     (
  530.     char ch             /* char to be abort */
  531.     )
  532.     {
  533.     tyAbortChar = ch;
  534.     }
  535. /*******************************************************************************
  536. *
  537. * tyBackspaceSet - change the backspace character
  538. *
  539. * This routine sets the backspace character to <ch>.
  540. * The default backspace character is CTRL-H.
  541. *
  542. * Typing the backspace character to any device operating in line protocol
  543. * mode (OPT_LINE set) will cause the previous character typed to be
  544. * deleted, up to the beginning of the current line.
  545. * Note that the character set by this routine applies to all devices
  546. * whose handlers use the standard tty package tyLib.
  547. *
  548. *
  549. * RETURNS: N/A
  550. */
  551. void tyBackspaceSet
  552.     (
  553.     char ch             /* char to be backspace */
  554.     )
  555.     {
  556.     tyBackspaceChar = ch;
  557.     }
  558. /*******************************************************************************
  559. *
  560. * tyDeleteLineSet - change the line-delete character
  561. *
  562. * This routine sets the line-delete character to <ch>.
  563. * The default line-delete character is CTRL-U.
  564. *
  565. * Typing the delete character to any device operating in line protocol
  566. * mode (OPT_LINE set) will cause all characters in the current
  567. * line to be deleted.
  568. * Note that the character set by this routine applies to all devices
  569. * whose handlers use the standard tty package tyLib.
  570. *
  571. *
  572. * RETURNS: N/A
  573. */
  574. void tyDeleteLineSet
  575.     (
  576.     char ch             /* char to be line-delete */
  577.     )
  578.     {
  579.     tyDeleteLineChar = ch;
  580.     }
  581. /*******************************************************************************
  582. *
  583. * tyEOFSet - change the end-of-file character
  584. *
  585. * This routine sets the EOF character to <ch>.
  586. * The default EOF character is CTRL-D.
  587. *
  588. * Typing the EOF character to any device operating in line protocol mode
  589. * (OPT_LINE set) will cause no character to be entered in the current
  590. * line, but will cause the current line to be terminated (thus without a newline
  591. * character).  The line is made available to reading tasks.  Thus, if the EOF
  592. * character is the first character input on a line, a line length of zero
  593. * characters is returned to the reader.   This is the standard end-of-file
  594. * indication on a read call.  Note that the EOF character set by this routine
  595. * will apply to all devices whose handlers use the standard tty package tyLib.
  596. *
  597. *
  598. * RETURNS: N/A
  599. */
  600. void tyEOFSet
  601.     (
  602.     char ch             /* char to be EOF */
  603.     )
  604.     {
  605.     tyEofChar = ch;
  606.     }
  607. /*******************************************************************************
  608. *
  609. * tyMonitorTrapSet - change the trap-to-monitor character
  610. *
  611. * This routine sets the trap-to-monitor character to <ch>.
  612. * The default trap-to-monitor character is CTRL-X.
  613. *
  614. * Typing the trap-to-monitor character to any device whose OPT_MON_TRAP option
  615. * is set will cause the resident ROM monitor to be entered, if one is present.
  616. * Once the ROM monitor is entered, the normal multitasking system is halted.
  617. *
  618. * Note that the trap-to-monitor character set by this routine will apply to all
  619. * devices whose handlers use the standard tty package tyLib.  Also note that
  620. * not all systems have a monitor trap available.
  621. *
  622. * RETURNS: N/A
  623. */
  624. void tyMonitorTrapSet
  625.     (
  626.     char ch             /* char to be monitor trap */
  627.     )
  628.     {
  629.     tyMonTrapChar = ch;
  630.     }
  631. /*******************************************************************************
  632. *
  633. * tyIoctl - handle device control requests
  634. *
  635. * This routine handles ioctl() requests for tty devices.  The I/O control
  636. * functions for tty devices are described in the manual entry for tyLib.
  637. *
  638. * BUGS:
  639. * In line protocol mode (OPT_LINE option set), the FIONREAD function
  640. * actually returns the number of characters available plus the number of
  641. * lines in the buffer.  Thus, if five lines consisting of just NEWLINEs were
  642. * in the input buffer, the FIONREAD function would return the value ten
  643. * (five characters + five lines).
  644. *
  645. * RETURNS: OK or ERROR.
  646. *
  647. * VARARGS2 - not all requests include an arg.
  648. */
  649. STATUS tyIoctl
  650.     (
  651.     FAST TY_DEV_ID pTyDev,      /* ptr to device to control */
  652.     int request,                /* request code             */
  653.     int arg                     /* some argument            */
  654.     )
  655.     {
  656.     FAST int status = OK;
  657.     int oldOptions;
  658.     switch (request)
  659. {
  660. case FIONREAD:
  661.     *((int *) arg) = rngNBytes (pTyDev->rdBuf);
  662.     break;
  663. case FIONWRITE:
  664.     *((int *) arg) = rngNBytes (pTyDev->wrtBuf);
  665.     break;
  666. case FIOFLUSH:
  667.     tyFlush (pTyDev);
  668.     break;
  669. case FIOWFLUSH:
  670.     tyFlushWrt (pTyDev);
  671.     break;
  672. case FIORFLUSH:
  673.     tyFlushRd (pTyDev);
  674.     break;
  675. case FIOGETOPTIONS:
  676.     return (pTyDev->options);
  677. case FIOSETOPTIONS:   /* now same as FIOOPTIONS */
  678.     oldOptions = pTyDev->options;
  679.     pTyDev->options = arg;
  680.     if ((oldOptions & OPT_LINE) != (pTyDev->options & OPT_LINE))
  681. tyFlushRd (pTyDev);
  682.     if ((oldOptions & OPT_TANDEM) && !(pTyDev->options & OPT_TANDEM))
  683. {
  684. /* TANDEM option turned off: XON receiver and transmitter */
  685. tyRdXoff (pTyDev, FALSE); /* output XON if necessary */
  686. tyWrtXoff (pTyDev, FALSE); /* restart xmitter if nec. */
  687. }
  688.     break;
  689. case FIOCANCEL:
  690.     semTake (&pTyDev->mutexSem, WAIT_FOREVER);
  691.     pTyDev->rdState.canceled = TRUE;
  692.     semGive (&pTyDev->rdSyncSem);
  693.     pTyDev->wrtState.canceled = TRUE;
  694.     semGive (&pTyDev->wrtSyncSem);
  695.     semGive (&pTyDev->mutexSem);
  696.     break;
  697. case FIOISATTY:
  698.     status = TRUE;
  699.     break;
  700. case FIOPROTOHOOK:
  701.     pTyDev->protoHook = (FUNCPTR) arg;
  702.     break;
  703. case FIOPROTOARG:
  704.     pTyDev->protoArg = arg;
  705.     break;
  706. case FIORBUFSET:
  707.     semTake (&pTyDev->mutexSem, WAIT_FOREVER);
  708.     pTyDev->rdState.flushingRdBuf = TRUE;
  709.     if (pTyDev->rdBuf)
  710. rngDelete (pTyDev->rdBuf);
  711.     if ((pTyDev->rdBuf = rngCreate (arg)) == NULL)
  712. status = ERROR;
  713.     pTyDev->rdState.flushingRdBuf = FALSE;
  714.     semGive (&pTyDev->mutexSem);
  715.     break;
  716. case FIOWBUFSET:
  717.     semTake (&pTyDev->mutexSem, WAIT_FOREVER);
  718.     pTyDev->wrtState.flushingWrtBuf = TRUE;
  719.     if (pTyDev->wrtBuf)
  720. rngDelete (pTyDev->wrtBuf);
  721.     if ((pTyDev->wrtBuf = rngCreate (arg)) == NULL)
  722. status = ERROR;
  723.     pTyDev->wrtState.flushingWrtBuf = FALSE;
  724.     semGive (&pTyDev->mutexSem);
  725.     break;
  726. case FIOSELECT:
  727.     if (_func_selTyAdd != NULL)
  728.         (* _func_selTyAdd) (pTyDev, arg);
  729.     break;
  730. case FIOUNSELECT:
  731.     if (_func_selTyDelete != NULL)
  732.         (* _func_selTyDelete) (pTyDev, arg);
  733.     break;
  734. default:
  735.     errnoSet (S_ioLib_UNKNOWN_REQUEST);
  736.     status = ERROR;
  737. }
  738.     return (status);
  739.     }
  740. /* raw mode I/O routines */
  741. /*******************************************************************************
  742. *
  743. * tyWrite - do a task-level write for a tty device
  744. *
  745. * This routine handles the task-level portion of the tty handler's
  746. * write function.
  747. *
  748. * INTERNAL
  749. * This routine is not the only place characters are put into
  750. * the buffer: the read echo in tyIRd may put chars in output
  751. * ring as well.  tyIRd is of course not blocked by the mutexSem,
  752. * and we don't want to lock out interrupts for the entire time
  753. * we are copying data to ring.  So we we set wrtBufBusy while we
  754. * are putting into the ring buffer, and tyIRd drops echo chars
  755. * while this flag is set.  The problem with this is that some
  756. * input chars may not get echoed.  This shouldn't be a problem
  757. * for interactive stuff, since it only occurs when there is
  758. * output going on.  But for a serial protocol that relied on
  759. * echo, like echoplex, it could be a problem.
  760. *
  761. * RETURNS: The number of bytes actually written to the device.
  762. */
  763. int tyWrite
  764.     (
  765.     FAST TY_DEV_ID pTyDev,      /* ptr to device structure */
  766.     char *buffer,               /* buffer of data to write  */
  767.     FAST int nbytes             /* number of bytes in buffer */
  768.     )
  769.     {
  770.     FAST int bytesput;
  771.     int nbStart = nbytes;
  772.     pTyDev->wrtState.canceled = FALSE;
  773.     while (nbytes > 0)
  774.         {
  775. /* XXX
  776.  * mutexSem is "safe" from task deletion, but sync semaphore can't be;
  777.  * if task is deleted after getting syncSem but before getting mutexSem,
  778.  * tty channel will be hung.
  779.  */
  780.         semTake (&pTyDev->wrtSyncSem, WAIT_FOREVER);
  781. semTake (&pTyDev->mutexSem, WAIT_FOREVER);
  782.         if (pTyDev->wrtState.canceled)
  783.             {
  784.     semGive (&pTyDev->mutexSem);
  785.             errnoSet (S_ioLib_CANCELLED);
  786.             return (nbStart - nbytes);
  787.             }
  788. /* set wrtBufBusy while are putting data into the ring buffer to
  789.  * inhibit tyIRd echoing chars - see description of problem above
  790.  */
  791. pTyDev->wrtState.wrtBufBusy = TRUE;
  792.         bytesput = rngBufPut (pTyDev->wrtBuf, buffer, nbytes);
  793. pTyDev->wrtState.wrtBufBusy = FALSE;
  794. tyTxStartup (pTyDev); /* if xmitter not busy, start it */
  795.         nbytes -= bytesput;
  796.         buffer += bytesput;
  797.         /* If more room in ringId, enable next writer. */
  798. if (rngFreeBytes (pTyDev->wrtBuf) > 0)
  799.     semGive (&pTyDev->wrtSyncSem);
  800. semGive (&pTyDev->mutexSem);
  801. }
  802.     return (nbStart);
  803.     }
  804. /*******************************************************************************
  805. *
  806. * tyRead - do a task-level read for a tty device
  807. *
  808. * This routine handles the task-level portion of the tty handler's read
  809. * function.  It reads into the buffer up to <maxbytes> available bytes.
  810. *
  811. * This routine should only be called from serial device drivers.
  812. *
  813. * RETURNS: The number of bytes actually read into the buffer.
  814. */
  815. int tyRead
  816.     (
  817.     FAST TY_DEV_ID pTyDev,      /* device to read         */
  818.     char *buffer,               /* buffer to read into    */
  819.     int maxbytes                /* maximum length of read */
  820.     )
  821.     {
  822.     FAST int nbytes;
  823.     FAST RING_ID ringId;
  824.     FAST int nn;
  825.     int freeBytes;
  826.     pTyDev->rdState.canceled = FALSE;
  827.     FOREVER
  828. {
  829. /* XXX
  830.  * mutexSem is "safe" from task deletion, but sync semaphore can't be;
  831.  * if task is deleted after getting syncSem but before getting mutexSem,
  832.  * tty channel will be hung.
  833.  */
  834. semTake (&pTyDev->rdSyncSem, WAIT_FOREVER);
  835. semTake (&pTyDev->mutexSem, WAIT_FOREVER);
  836. if (pTyDev->rdState.canceled)
  837.     {
  838.     semGive (&pTyDev->mutexSem);
  839.     errnoSet (S_ioLib_CANCELLED);
  840.     return (0);
  841.     }
  842. ringId = pTyDev->rdBuf;
  843. if (!rngIsEmpty (ringId))
  844.     break;
  845. semGive (&pTyDev->mutexSem);
  846. }
  847.     /* we exit the above loop with the mutex semaphore taken and certain
  848.      * that the ring buffer is not empty */
  849.     /* get characters from ring buffer */
  850.     if (pTyDev->options & OPT_LINE)
  851. {
  852. if (pTyDev->lnBytesLeft == 0)
  853.     RNG_ELEM_GET (ringId, &pTyDev->lnBytesLeft, nn);
  854. nbytes = min ((int)pTyDev->lnBytesLeft, maxbytes);
  855. rngBufGet (ringId, buffer, nbytes);
  856. pTyDev->lnBytesLeft -= nbytes;
  857. }
  858.     else
  859. nbytes = rngBufGet (ringId, buffer, maxbytes);
  860.     /* check if XON needs to be output */
  861.     if ((pTyDev->options & OPT_TANDEM) && pTyDev->rdState.xoff)
  862. {
  863. freeBytes = rngFreeBytes (ringId);
  864. if (pTyDev->options & OPT_LINE)
  865.     freeBytes -= pTyDev->lnNBytes + 1;
  866. if (freeBytes > tyXonThreshold)
  867.     tyRdXoff (pTyDev, FALSE);
  868. }
  869.     /* If more characters in ring, enable next reader.
  870.      * This is necessary in case several tasks blocked when ring was empty -
  871.      * each must be awoken now
  872.      */
  873.     if (!rngIsEmpty (ringId))
  874. semGive (&pTyDev->rdSyncSem);
  875.     semGive (&pTyDev->mutexSem);
  876.     return (nbytes);
  877.     }
  878. /*******************************************************************************
  879. *
  880. * tyITx - interrupt-level output
  881. *
  882. * This routine gets a single character to be output to a device.  It looks at
  883. * the ring buffer for <pTyDev> and gives the caller the next available
  884. * character, if there is one.  The character to be output is copied to <pChar>.
  885. *
  886. * RETURNS:
  887. * OK if there are more characters to send, or
  888. * ERROR if there are no more characters.
  889. */
  890. STATUS tyITx
  891.     (
  892.     FAST TY_DEV_ID pTyDev,   /* pointer to tty device descriptor */
  893.     char *pChar              /* where to put character to be output */
  894.     )
  895.     {
  896.     FAST RING_ID ringId = pTyDev->wrtBuf;
  897.     FAST int nn;
  898.     /* check if we need to output XON/XOFF for the read side */
  899.     if (pTyDev->rdState.pending)
  900. {
  901. pTyDev->rdState.pending = FALSE;
  902. *pChar = pTyDev->rdState.xoff ? XOFF : XON;
  903. /* keep track of the max chars received after we sent xoff - see note
  904.  * at declaration of tyXoffChars
  905.  */
  906. if (pTyDev->rdState.xoff) /** TEMP **/
  907.     { /** TEMP **/
  908.     if (tyXoffChars > tyXoffMax) /** TEMP **/
  909. tyXoffMax = tyXoffChars; /** TEMP **/
  910.     tyXoffChars = 0; /** TEMP **/
  911.     } /** TEMP **/
  912. }
  913.     /* if output is in XOFF, or task level is flushing output buffers,
  914.      * then just turn off xmitter and return */
  915.     else if (pTyDev->wrtState.xoff || pTyDev->wrtState.flushingWrtBuf)
  916. pTyDev->wrtState.busy = FALSE;
  917.     /* check for linefeed needed after carriage return (SPR27682) */
  918.     /* formerly, this was a check for carriage return needed after 
  919.      * linefeed */
  920.     else if (pTyDev->wrtState.cr)
  921. {
  922. *pChar = 'n';
  923. pTyDev->wrtState.cr   = FALSE;
  924. }
  925.     /* check for more characters to output */
  926.     else if (RNG_ELEM_GET (ringId, pChar, nn) == 0) /* no more chars */
  927. pTyDev->wrtState.busy = FALSE;
  928.     else
  929. {
  930. /* got a character to be output */
  931. pTyDev->wrtState.busy = TRUE;
  932. /* check for linefeed needs to be added after carriage return (SPR27682) */
  933. /* formerly, this was a check for carriage return needs to be added after 
  934.      * linefeed */
  935. if ((pTyDev->options & OPT_CRMOD) && (*pChar == 'n'))
  936.     {
  937.         *pChar = 'r';
  938.     pTyDev->wrtState.cr = TRUE;
  939.     }
  940. /* when we pass the write threshhold, give write synchonization
  941.  * and release tasks pended in select */
  942. if (rngFreeBytes (ringId) == tyWrtThreshold)
  943.     {
  944.     semGive (&pTyDev->wrtSyncSem);
  945.     if (_func_selWakeupAll != NULL)
  946. (* _func_selWakeupAll) (&pTyDev->selWakeupList, SELWRITE);
  947.     }
  948. }
  949.     return (pTyDev->wrtState.busy ? OK : ERROR);
  950.     }
  951. /*******************************************************************************
  952. *
  953. * tyIRd - interrupt-level input
  954. *
  955. * This routine handles interrupt-level character input for tty devices.  A
  956. * device driver calls this routine when it has received a character.  This
  957. * routine adds the character to the ring buffer for the specified device, and
  958. * gives a semaphore if a task is waiting for it.
  959. *
  960. * This routine also handles all the special characters, as specified in
  961. * the option word for the device, such as X-on, X-off, NEWLINE, or backspace.
  962. *
  963. * RETURNS: OK, or ERROR if the ring buffer is full.
  964. */
  965. STATUS tyIRd
  966.     (
  967.     FAST TY_DEV_ID pTyDev,      /* ptr to tty device descriptor */
  968.     FAST char inchar            /* character read */
  969.     )
  970.     {
  971.     FAST RING_ID ringId;
  972.     FAST int nn;
  973.     int freeBytes;
  974.     BOOL releaseTaskLevel;
  975.     FAST int options = pTyDev->options;
  976.     BOOL charEchoed = FALSE;
  977.     STATUS status = OK;
  978.     /* if task level is flushing input buffers, then just return */
  979.     if (pTyDev->rdState.flushingRdBuf)
  980. return (ERROR);
  981.     /* If there is an input hook routine to call, call it now.
  982.      * The hook routine will return TRUE if it doesn't want us
  983.      * to process the character further.
  984.      */
  985.     if (pTyDev->protoHook &&
  986. (*pTyDev->protoHook)(pTyDev->protoArg, inchar) == TRUE)
  987. {
  988. return (status);
  989. }
  990.     /* strip off parity bit if '7 bit' option set */
  991.     if (options & OPT_7_BIT)
  992. inchar &= 0x7f;
  993.     /* check for abort */
  994.     if ((inchar == tyAbortChar) && (options & OPT_ABORT) && tyAbortFunc != NULL)
  995. (*tyAbortFunc) ();
  996.     /* check for trap to rom monitor */
  997.     else if ((inchar == tyMonTrapChar) && (options & OPT_MON_TRAP))
  998. {
  999. /* attempt to do the reboot from task level, go down no matter what */
  1000. if (_func_excJobAdd != NULL)
  1001.     (* _func_excJobAdd) (reboot, BOOT_WARM_AUTOBOOT);
  1002. else
  1003.     reboot (BOOT_WARM_AUTOBOOT);
  1004. }
  1005.     /* check for XON/XOFF received */
  1006.     else if (((inchar == XOFF) || (inchar == XON)) && (options & OPT_TANDEM))
  1007. tyWrtXoff (pTyDev, (inchar == XOFF));
  1008.     else
  1009. {
  1010. /* count number of chars received while in xoff - see note at
  1011.  * declaration of tyXoffChars
  1012.  */
  1013. if (pTyDev->rdState.xoff) /** TEMP **/
  1014.     tyXoffChars++; /** TEMP **/
  1015. /* check for carriage return needs to be turned into linefeed */
  1016. if ((options & OPT_CRMOD) && (inchar == 'r'))
  1017.     inchar = 'n';
  1018. /* check for output echo required
  1019.  * only echo if the write buffer isn't being deleted or
  1020.  * written to at task level - note that this implies that characters
  1021.  * aren't guaranteed to be echoed.
  1022.  */
  1023. if ((options & OPT_ECHO) &&
  1024.     (!pTyDev->wrtState.wrtBufBusy && !pTyDev->wrtState.flushingWrtBuf))
  1025.     {
  1026.     ringId = pTyDev->wrtBuf;
  1027.     /* echo the char.  some special chars are echoed differently */
  1028.     if (options & OPT_LINE)
  1029. {
  1030. if (inchar == tyDeleteLineChar)
  1031.     {
  1032.     /* echo a newline */
  1033.     RNG_ELEM_PUT (ringId, 'n', nn);
  1034.     charEchoed = TRUE;
  1035.     }
  1036. else if (inchar == tyBackspaceChar)
  1037.     {
  1038.     if (pTyDev->lnNBytes != 0)
  1039. {
  1040. /* echo BS-space-BS */
  1041. rngBufPut (ringId, " ", 3);
  1042. charEchoed = TRUE;
  1043. }
  1044.     }
  1045. else if ((inchar < 0x20) && (inchar != 'n'))
  1046.     {
  1047.     /* echo ^-char */
  1048.     RNG_ELEM_PUT (ringId, '^', nn);
  1049.     RNG_ELEM_PUT (ringId, inchar + '@', nn);
  1050.     charEchoed = TRUE;
  1051.     }
  1052. else
  1053.     {
  1054.     RNG_ELEM_PUT (ringId, inchar, nn);
  1055.     charEchoed = TRUE;
  1056.     }
  1057. }
  1058.     else
  1059. {
  1060. /* just echo the char */
  1061. RNG_ELEM_PUT (ringId, inchar, nn);
  1062. charEchoed = TRUE;
  1063. }
  1064.     if (charEchoed)
  1065. tyTxStartup (pTyDev);
  1066.     }
  1067. /* put the char in the read buffer. */
  1068. ringId = pTyDev->rdBuf;
  1069. releaseTaskLevel = FALSE;
  1070. if (!(options & OPT_LINE))
  1071.     {
  1072.     /* not line-mode;
  1073.      * just enter character and make immediately available to tasks */
  1074.     if (RNG_ELEM_PUT (ringId, inchar, nn) == 0)
  1075. status = ERROR; /* buffer full, not entered */
  1076.     /* only give the sync sem on first char */
  1077.     if (rngNBytes (ringId) == 1)
  1078. releaseTaskLevel = TRUE;
  1079.     }
  1080. else
  1081.     {
  1082.     /* line-mode;
  1083.      * process backspace, line-delete, EOF, and newline chars special */
  1084.     freeBytes = rngFreeBytes (ringId);
  1085.     if (inchar == tyBackspaceChar)
  1086. {
  1087. if (pTyDev->lnNBytes != 0)
  1088.     pTyDev->lnNBytes--;
  1089. }
  1090.     else if (inchar == tyDeleteLineChar)
  1091. pTyDev->lnNBytes = 0;
  1092.     else if (inchar == tyEofChar)
  1093. {
  1094. /* EOF - check for at least one free byte so there is room
  1095.  * to put the line byte count below */
  1096. if (freeBytes > 0)
  1097.     releaseTaskLevel = TRUE;
  1098. }
  1099.     else
  1100. {
  1101. if (freeBytes >= 2)
  1102.     {
  1103.     if (freeBytes >= (pTyDev->lnNBytes + 2))
  1104. pTyDev->lnNBytes++;
  1105.     else
  1106. status = ERROR; /* no room, overwriting last char */
  1107.     rngPutAhead (ringId, inchar, (int)pTyDev->lnNBytes);
  1108.     if ((inchar == 'n') || (pTyDev->lnNBytes == 255))
  1109. releaseTaskLevel = TRUE;
  1110.     }
  1111. else
  1112.     status = ERROR; /* no room, not even for overwriting */
  1113. }
  1114.     /* if line termination indicated, put line byte count
  1115.      * in 0th character of line and advance ring buffer pointers */
  1116.     if (releaseTaskLevel)
  1117. {
  1118. rngPutAhead (ringId, (char) pTyDev->lnNBytes, 0);
  1119. rngMoveAhead (ringId, (int) pTyDev->lnNBytes + 1);
  1120. pTyDev->lnNBytes = 0;
  1121. }
  1122.     }
  1123. /* check if XON/XOFF needs to be output */
  1124. if (options & OPT_TANDEM)
  1125.     {
  1126.     freeBytes = rngFreeBytes (ringId);
  1127.     if (pTyDev->options & OPT_LINE)
  1128. freeBytes -= pTyDev->lnNBytes + 1;
  1129.     if (!pTyDev->rdState.xoff)
  1130. {
  1131. /* if input buffer is close to full, send XOFF */
  1132. if (freeBytes < tyXoffThreshold)
  1133.     tyRdXoff (pTyDev, TRUE);
  1134. }
  1135.     else
  1136. {
  1137. /* if input buffer has enough room now, send XON */
  1138. if (freeBytes > tyXonThreshold)
  1139.     tyRdXoff (pTyDev, FALSE);
  1140. }
  1141.     }
  1142. /* if task level has new input then give read semaphore and
  1143.  * release tasks pended in select
  1144.  */
  1145. if (releaseTaskLevel)
  1146.     {
  1147.     semGive (&pTyDev->rdSyncSem);
  1148.     if (_func_selWakeupAll != NULL)
  1149. (* _func_selWakeupAll) (&pTyDev->selWakeupList, SELREAD);
  1150.     }
  1151. }
  1152.     return (status);
  1153.     }
  1154. /******************************************************************************
  1155. *
  1156. * tyRdXoff - set read side xon/xoff
  1157. *
  1158. * This routine sets the read side xon/xoff to the specified state.
  1159. * A flag will be set indicating that the next character sent by the
  1160. * transmit side should be the xon or xoff character.  If the transmitter
  1161. * is idle, it is started.
  1162. */
  1163. LOCAL void tyRdXoff
  1164.     (
  1165.     FAST TY_DEV_ID pTyDev,      /* pointer to device structure */
  1166.     FAST BOOL xoff
  1167.     )
  1168.     {
  1169.     FAST int oldlevel;
  1170.     oldlevel = intLock (); /* LOCK INTERRUPTS */
  1171.     if (pTyDev->rdState.xoff != xoff)
  1172. {
  1173. pTyDev->rdState.xoff = xoff;
  1174. pTyDev->rdState.pending = TRUE;
  1175. if (!pTyDev->wrtState.busy)
  1176.     {
  1177.     pTyDev->wrtState.busy = TRUE;
  1178.     intUnlock (oldlevel); /* UNLOCK INTERRUPTS */
  1179.     (*pTyDev->txStartup) (pTyDev);
  1180.     return;
  1181.     }
  1182. }
  1183.     intUnlock (oldlevel);                    /* UNLOCK INTERRUPTS */
  1184.     }
  1185. /******************************************************************************
  1186. *
  1187. * tyWrtXoff - set write side xon/xoff
  1188. *
  1189. * This routine sets the write side xon/xoff to the specified state.
  1190. * If the new state is xon and the transmitter is idle, it is started.
  1191. *
  1192. */
  1193. LOCAL void tyWrtXoff
  1194.     (
  1195.     FAST TY_DEV_ID pTyDev,      /* pointer to device structure */
  1196.     BOOL xoff
  1197.     )
  1198.     {
  1199.     FAST int oldlevel;
  1200.     /* restart an XOFF'd transmitter */
  1201.     oldlevel = intLock (); /* LOCK INTERRUPTS */
  1202.     if (pTyDev->wrtState.xoff != xoff)
  1203. {
  1204. pTyDev->wrtState.xoff = xoff;
  1205. if (!xoff)
  1206.     {
  1207.     if (!pTyDev->wrtState.busy)
  1208. {
  1209. pTyDev->wrtState.busy = TRUE;
  1210. intUnlock (oldlevel);                   /* UNLOCK INTERRUPTS */
  1211. (*pTyDev->txStartup) (pTyDev);
  1212. return;
  1213. }
  1214.     }
  1215. }
  1216.     intUnlock (oldlevel);                     /* UNLOCK INTERRUPTS */
  1217.     }
  1218. /******************************************************************************
  1219. *
  1220. * tyTxStartup - startup transmitter if necessary
  1221. *
  1222. * This routine starts the transmitter if it is not already busy.
  1223. * A flag is maintained so that starting the transmitter can be properly
  1224. * interlocked with interrupt level, but the startup routine itself is
  1225. * called at the interrupt level of the caller.
  1226. */
  1227. LOCAL void tyTxStartup
  1228.     (
  1229.     FAST TY_DEV_ID pTyDev       /* pointer to device structure */
  1230.     )
  1231.     {
  1232.     FAST int oldlevel;
  1233.     /* if xmitter not busy, start it */
  1234.     if (!pTyDev->wrtState.busy)
  1235. {
  1236. oldlevel = intLock (); /* LOCK INTERRUPTS */
  1237. /* check xmitter busy again, now that we're locked out */
  1238. if (!pTyDev->wrtState.busy)
  1239.     {
  1240.     pTyDev->wrtState.busy = TRUE;
  1241.     intUnlock (oldlevel);                    /* UNLOCK INTERRUPTS */
  1242.     (*pTyDev->txStartup) (pTyDev);
  1243.     return;
  1244.     }
  1245. intUnlock (oldlevel);                    /* UNLOCK INTERRUPTS */
  1246. }
  1247.     }