home *** CD-ROM | disk | FTP | other *** search
/ Amiga Elysian Archive / AmigaElysianArchive.iso / prog / utils / sercli.shr / sercli / src / serial.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-16  |  16.5 KB  |  664 lines

  1. /*
  2. **  $Source: WB_2.1:homes/rkr/prog/sercli/src/RCS/serial.c,v $
  3. **  $Author: rkr $
  4. **  $Revision: 1.6 $
  5. **  $Locker: rkr $
  6. **  $State: Exp $
  7. **  $Date: 1993/06/16 23:26:42 $
  8. **
  9. **  sercli (an Amiga .device <-> FIFO interface tool)
  10. **  Copyright (C) 1993  Richard Rauch
  11. **
  12. **  See /doc/sercli.doc and /COPYING for use and distribution license.
  13. **
  14. */
  15.  
  16. /*
  17. **  ToDo:
  18. **
  19. **  **    Change {SER_BUCKET_SIZE} into a variable that can be set by the
  20. **    main program.  (Perhaps only through a special function, though, so
  21. **    that unreasonably small/large values can be filtered...?)
  22. **
  23. **  **    In write_ser(), read_ser_line(), and their asynch counterparts, I
  24. **    do not set the .io_Flags field...  I believe this to be a mistake,
  25. **    although it's not likely to cause much/any _harm_ at the moment.
  26. **
  27. **    I clear IOF_QUICK, at least, when creating the asynch requests
  28. **    (they are cloned from the possibly-already-used {ser_write_req}
  29. **    request, which could very well have the IOF_QUICK bit set...)
  30. **
  31. **  **    Also, a synchronous read_ser() that is non-line-oriented, which
  32. **    reads up to {n} characters into a {buf} would be appropriate.
  33. **
  34. **  **    Time-outable i/o.
  35. **
  36. **  **    Add error-handling (started; need to add for SafeAbortIO()...pro'ly
  37. **    best done by having SafeAbortIO() return whatever AbortIO()
  38. **    returns, and then using that from this context...)
  39. **
  40. **  **    Get rid of the __stkargs declaration of BeginIO().  There should be
  41. **    a '@' version of the thing in the .lib (but there isn't, right
  42. **    now).
  43. **
  44. **  **    Some names are still not of the form <verb>_<noun>_<modifier>.    The
  45. **    ones that I have not changed are left as they are because the
  46. **    "correct" form is ambiguous.  Arguably, they are all "correct."
  47. **
  48. **  **    When doing a set_ser_soft(), we should "clone" the param-setting
  49. **    request onto all other requests (the global read/write requests and
  50. **    all the ones dynamically allocated).
  51. **
  52. **    Doing a "ser query" while running seems to suggest that the
  53. **    software is getting confused.
  54. **
  55. */
  56.  
  57.  
  58. /*
  59. **  Some includes...
  60. **
  61. */
  62. #include <exec/types.h>
  63.  
  64. #include <clib/alib_protos.h>
  65. #include <clib/exec_protos.h>
  66.  
  67. #include <stdlib.h>
  68. #include <string.h>
  69.  
  70. #include "ser_supp.h"
  71. #include "misc.h"
  72. #include "sercli-config.h"
  73.  
  74.  
  75. #define SER_BUCKET_SIZE 256
  76.  
  77. /*
  78. **  No '@' entry point for BeginIO.  (frown)
  79. **
  80. */
  81. __stkargs void BeginIO (struct IORequest *ior);
  82.  
  83.  
  84.  
  85.  
  86. /*
  87. **  Some globals...
  88. **
  89. */
  90. IOExtSer *ser_read_req        = 0;
  91. IOExtSer *ser_write_req     = 0;
  92. IOExtSer *ser_status_req    = 0;    /*** uses read port for now ***/
  93.  
  94. List *ser_bucket_list        = 0;
  95. List *ser_writes_p_list     = 0;    /*** SERial WRITES Pending LIST ***/
  96.  
  97. MsgPort *ser_read_reply_port    = 0;
  98. MsgPort *ser_write_reply_port    = 0;
  99.  
  100. ULONG ser_dev            = 0;    /*** flag ***/
  101. ULONG ser_read_reply_mask    = 0;
  102. ULONG ser_write_reply_mask    = 0;
  103.  
  104.  
  105.  
  106. /*
  107. **  Functions needed to access the above structures...
  108. **
  109. */
  110.  
  111. /*
  112. **  Use atexit() to make sure close_ser() is called by exit() before
  113. **  calling this function, as this function relies on auto-cleanup.
  114. **
  115. **  Non-DICE compilers may have a limit to the number of functions to be
  116. **  called through atexit() (Aztec, for instance, says only 16 are valid in
  117. **  their 5.0a docs).
  118. **
  119. **  For the sake of Lattice/SAS (about which I know little), I have set up
  120. **  ser_test.c to call its own clean_up(), which calls close_ser().
  121. **
  122. **  Besides, since ANSI (K&R2, anyway) doesn't say how many functions can
  123. **  be set with atexit(), it's not clear that DICE/Aztec/SAS would all
  124. **  execute multiple atexit()-set-functions in the same order.
  125. **
  126. */
  127. void open_ser (void)
  128. {
  129.     if (ser_dev)    /*** AM ALREADY OPEN??? ***/
  130.     return ();  /*** do nothing, then.. ***/
  131.  
  132.     if (!(ser_read_reply_port = CreatePort (0, 0) ) )
  133.     exit (10);
  134.     if (!(ser_write_reply_port = CreatePort (0, 0) ) )
  135.     exit (10);
  136.  
  137.     ser_read_reply_mask  = 1 << ser_read_reply_port->mp_SigBit;
  138.     ser_write_reply_mask = 1 << ser_write_reply_port->mp_SigBit;
  139.  
  140.     if (!(ser_read_req  = (IOExtSer *) CreateExtIO (ser_read_reply_port, sizeof (IOExtSer) ) ) )
  141.     exit (10);
  142.     if (!(ser_write_req = (IOExtSer *) CreateExtIO (ser_write_reply_port, sizeof (IOExtSer) ) ) )
  143.     exit (10);
  144.     if (!(ser_status_req  = (IOExtSer *) CreateExtIO (ser_read_reply_port, sizeof (IOExtSer) ) ) )
  145.     exit (10);
  146.  
  147.     /*
  148.     **    Actuallly open the thing...
  149.     **
  150.     */
  151.     {
  152.     ser_status_req->io_SerFlags
  153.         = (ser_xon_xoff ? 0 : SERF_XDISABLED)
  154.         | (ser_exclusive ? 0 : SERF_SHARED)
  155.         | (ser_rad_boogie ? SERF_RAD_BOOGIE : 0)
  156.         | (ser_7wire ? SERF_7WIRE : 0)
  157.         |
  158.         (
  159.         ser_parity ?
  160.         (
  161.             1 == ser_parity
  162.             ? (SERF_PARTY_ODD | SERF_PARTY_ON)
  163.             : (2 == ser_parity ? SERF_PARTY_ON : 0)
  164.         ) : 0
  165.         );
  166.  
  167.     if
  168.     (
  169.         OpenDevice
  170.         (
  171.         ser_device_name,
  172.         ser_device_unit,
  173.         (IORequest *) ser_status_req,
  174.         0
  175.         )
  176.     )
  177.         exit (10);
  178.     ser_dev = 1;
  179.     }
  180.  
  181.     CopyMem (ser_status_req, ser_read_req, sizeof (IOExtSer) );
  182.     ser_read_req->IOSer.io_Message.mn_ReplyPort = ser_read_reply_port;
  183.     CopyMem (ser_status_req, ser_write_req, sizeof (IOExtSer) );
  184.     ser_write_req->IOSer.io_Message.mn_ReplyPort = ser_write_reply_port;
  185.  
  186.     NEW (ser_bucket_list);
  187.     if (!ser_bucket_list)
  188.     exit (10);
  189.     NewList (ser_bucket_list);
  190.  
  191.     NEW (ser_writes_p_list);
  192.     if (!ser_writes_p_list)
  193.     exit (10);
  194.     NewList (ser_writes_p_list);
  195.  
  196.     set_ser_soft ();
  197. }
  198.  
  199.  
  200.  
  201. /*
  202. **  Closes down the serial ports, removes message ports & IORequests, frees
  203. **  memory.
  204. **
  205. **  If you spin of other tasks that could access these structures, you may
  206. **  want to wrap a Forbid()/Permit() pair around this function.  Be warned,
  207. **  however, that some of the cleanup (SafeAbortIO(), particularly) can
  208. **  cause this task to Wait(), thus breaking the Forbid().
  209. **
  210. **  (Semaphores might be more apt than Forbid()/Permit()...)
  211. **
  212. */
  213. void close_ser (void)
  214. {
  215.     SafeAbortIO ( (IORequest *) ser_read_req);
  216.     SafeAbortIO ( (IORequest *) ser_write_req);
  217.     SafeAbortIO ( (IORequest *) ser_status_req);
  218.  
  219.     if (ser_writes_p_list)
  220.     {
  221.     ser_bucket_t *b;
  222.     ser_bucket_keeper_t *sbk;
  223.  
  224.     while (sbk = (ser_bucket_keeper_t *) GetHead (ser_writes_p_list) )
  225.     {
  226.         Remove ( (Node *) sbk);
  227.         b = sbk->sbk_alter_ego;
  228.         SafeAbortIO ( (IORequest *) b);
  229.  
  230.         FREEN (b->b_buf, b->b_size);
  231.         FREE (b);
  232.     }
  233.     FREE (ser_writes_p_list);
  234.     ser_writes_p_list = 0;
  235.     }
  236.  
  237.     if (ser_bucket_list)
  238.     {
  239.     ser_bucket_t *b;
  240.     ser_bucket_keeper_t *sbk;
  241.  
  242.     while (sbk = (ser_bucket_keeper_t *) GetHead (ser_bucket_list) )
  243.     {
  244.         Remove ( (Node *) sbk);
  245.         b = sbk->sbk_alter_ego;
  246.  
  247.         FREEN (b->b_buf, b->b_size);
  248.         FREE (b);
  249.     }
  250.  
  251.     FREE (ser_bucket_list);
  252.     ser_bucket_list = 0;
  253.     }
  254.  
  255.  
  256.     if (ser_dev)
  257.     {
  258.     CloseDevice ( (IORequest *) ser_status_req);
  259.     ser_dev = 0;
  260.     }
  261.  
  262.     if (ser_read_reply_port)
  263.     {
  264.     DeletePort (ser_read_reply_port);
  265.     ser_read_reply_mask  = 0;
  266.     ser_read_reply_port  = 0;
  267.     }
  268.     if (ser_write_reply_port)
  269.     {
  270.     DeletePort (ser_write_reply_port);
  271.     ser_write_reply_mask = 0;
  272.     ser_write_reply_port = 0;
  273.     }
  274.  
  275.     if (ser_status_req)
  276.     {
  277.     DeleteExtIO ( (IORequest *) ser_status_req);
  278.     ser_status_req    = 0;
  279.     }
  280.     if (ser_read_req)
  281.     {
  282.     DeleteExtIO ( (IORequest *) ser_read_req);
  283.     ser_read_req  = 0;
  284.     }
  285.     if (ser_write_req)
  286.     {
  287.     DeleteExtIO ( (IORequest *) ser_write_req);
  288.     ser_write_req = 0;
  289.     }
  290. }
  291.  
  292.  
  293.  
  294. /*
  295. **  The strlen() isn't needed.  The built-in serial.device (and probably
  296. **  all 3rd party .devices for other serial ports) supports a ~0
  297. **  {io_Length} to tell the .device to look for an ASCII nul to signal
  298. **  end-of-buf.
  299. **
  300. **  However, I don't see that it could affect performance much/at all one
  301. **  way or the other...  What I do might even help performance, depending
  302. **  on how the CMD_READ is implemented on the .device.
  303. **
  304. **  And this is more `secure' against changes, since write_ser() is a black
  305. **  box, with a clear entrance and exit.  Any changes to write_ser() will
  306. **  be `inherited' into write_ser_str().
  307. **
  308. */
  309. ULONG write_ser_str (char *str)
  310. {
  311.     return (write_ser (str, strlen (str) ) );
  312. }
  313.  
  314.  
  315.  
  316. /*
  317. **  Writes {len} bytes of {str} to the .device opened with ser_open().
  318. **
  319. **  Returns actual number of bytes written.
  320. **
  321. **  If an error occurs, handle_error() will be called (if the user has not
  322. **  installed an error handler, errors will be ignored).
  323. **
  324. */
  325. ULONG write_ser (char *str, ULONG len)
  326. {
  327.     ser_write_req->IOSer.io_Command = CMD_WRITE;
  328.     ser_write_req->IOSer.io_Length  = len;
  329.     ser_write_req->IOSer.io_Data    = str;
  330.  
  331.     if (DoIO ( (IO_Request *) ser_write_req) )
  332.     handle_error (error_type_serial, ser_write_req);
  333.  
  334.     return (ser_write_req->IOSer.io_Actual);
  335. }
  336.  
  337.  
  338.  
  339. /*
  340. **  Not set:
  341. **  {
  342. **    io_CtlChar,
  343. **    io_RBufLen,
  344. **    io_extFlags,
  345. **    io_BrkTime,
  346. **    io_TermArray,
  347. **    io_status,    // izzis settable at all?
  348. **  }
  349. **
  350. **  Returns {!ser_write_req->IOSer.io_Error} (1 == ERROR, 0 == Safe).
  351. **
  352. */
  353. ULONG set_ser_soft (void)
  354. {
  355.     SafeAbortIO ( (IORequest *) ser_read_req);
  356.     SafeAbortIO ( (IORequest *) ser_write_req);
  357.     SafeAbortIO ( (IORequest *) ser_status_req);
  358.  
  359.     /*
  360.     **    Put this in its own function & re-use for close_ser()...
  361.     **
  362.     */
  363.     if (ser_writes_p_list)
  364.     {
  365.     ser_bucket_t *b;
  366.     ser_bucket_keeper_t *sbk;
  367.  
  368.     while (sbk = (ser_bucket_keeper_t *) GetHead (ser_writes_p_list) )
  369.     {
  370.         Remove ( (Node *) sbk);
  371.         b = sbk->sbk_alter_ego;
  372.         SafeAbortIO ( (IORequest *) b);
  373.  
  374.         AddTail (ser_bucket_list, (Node *) sbk);    /*** ?? ***/
  375.     }
  376.     }
  377.  
  378.     ser_write_req->IOSer.io_Command = SDCMD_SETPARAMS;
  379.     ser_write_req->io_Baud    = ser_bps;
  380.     ser_write_req->io_ReadLen    = ser_read_bits;
  381.     ser_write_req->io_WriteLen    = ser_write_bits;
  382.     ser_write_req->io_StopBits    = ser_stop_bits;
  383.  
  384.     ser_write_req->io_SerFlags
  385.     = (ser_xon_xoff ? 0 : SERF_XDISABLED)
  386.     | (ser_exclusive ? 0 : SERF_SHARED)
  387.     | (ser_rad_boogie ? SERF_RAD_BOOGIE : 0)
  388.     | (ser_7wire ? SERF_7WIRE : 0)
  389.     |
  390.     (
  391.         ser_parity ?
  392.         (
  393.         1 == ser_parity
  394.         ? (SERF_PARTY_ODD | SERF_PARTY_ON)
  395.         : (2 == ser_parity ? SERF_PARTY_ON : 0)
  396.         ) : 0
  397.     );
  398.  
  399.     /*
  400.     **    I don't s'pose it's necessary to "update" any of the other IO
  401.     **    requests...  We'll see, soon enough!  *grin*
  402.     **
  403.     */
  404.     if (DoIO ( (IORequest *) ser_write_req) )
  405.     handle_error (error_type_serial, ser_write_req);
  406.  
  407.     return (!ser_write_req->IOSer.io_Error);
  408. }
  409.  
  410.  
  411.  
  412. /*
  413. **  Get a line of data up to and include '\n' (or {max_len} bytes,
  414. **  whichever happens first).
  415. **
  416. **  A terminating null is alaways applied.
  417. **
  418. **  The {buf} ptr is returned.
  419. **
  420. **  Loosely speaking, this behaves like fgets(), only this works with a
  421. **  serial port.
  422. **
  423. **
  424. **  NOTES:
  425. **    Unlike fgets(), read_ser_line() will always return the {buf},
  426. **    regardless of any IO errors.
  427. **
  428. */
  429. char *read_ser_line (char *buf, ULONG max_len)
  430. {
  431.     int c;
  432.     ULONG len = 0;
  433.  
  434.     if (!max_len)
  435.     return (0);
  436.     do
  437.     {
  438.     ser_read_req->IOSer.io_Command = CMD_READ;
  439.     ser_read_req->IOSer.io_Length  = 1;
  440.     ser_read_req->IOSer.io_Data    = &(buf [len] );
  441.     if (DoIO ( (IORequest *) ser_read_req) )
  442.         handle_error (error_type_serial, ser_read_req);
  443.  
  444.     c = buf [len];
  445.     write_ser (&(buf [len] ), 1);
  446.     ++len;
  447.     }
  448.     while ( (c != '\n') && (len < (max_len - 1) ) );
  449.  
  450.     buf [len] = 0;
  451.     return (buf);
  452. }
  453.  
  454.  
  455.  
  456. /*
  457. **  Initiate an asynch write to the serial port of {size} bytes from {buf}.
  458. **
  459. **  This could actually be made a little more efficient by taking advantage
  460. **  of the fact that everything in our {ser_bucket_list} is a recycled
  461. **  {CMD_WRITE} request for the serial port.
  462. **
  463. **  (For that matter, I could take advantage of the serial port's
  464. **  non-destructive handling of i/o requests elsewhere, as well...)
  465. **
  466. **
  467. **  NOTES:
  468. **  **    If you send enough data through write_ser_asynch(), you could
  469. **    conceivably run out of memory.    write_ser_asynch() would then
  470. **    exit()...
  471. **
  472. **    This should be a clean, graceful behavior (haven't tested this, yet
  473. **    *grin*), but it's not a very robust way to handle memory; a
  474. **    'better' solution would be to wait a while, and reattempt the
  475. **    allocation, and/or give some previous write requests a chance to
  476. **    return..
  477. **
  478. **  **    Put a 'faucet' on the front end of this function if you will be
  479. **    sending long continuous streams of data (esp. if you want to
  480. **    error-check!) or else use the synchronous i/o write_ser(), above.
  481. **    Otherwise, you will end up copying your outbound data into buffers
  482. **    allocated by write_ser_async(); even though you probably won't run
  483. **    out of memory, an XPR (say) would probably then exit having 'sent'
  484. **    the entire file to the serial port, without mishap...  This won't
  485. **    be a problem for protocols like XModem that require continuous
  486. **    re-synching between sender/receiver.
  487. **
  488. **  **    IOF_QUICK is used, so this function may not _really_ be asynch.
  489. **    Depends on the serial.device software/hardware, and the current
  490. **    state of things.
  491. **
  492. **    If this gets to be a problem, a new function can be split off.
  493. **    However, since I (presently) don't need to worry about when a write
  494. **    request completes, this suits me better.
  495. **
  496. **    Use SendIO() if "real" asynch i/o is required.
  497. **
  498. **    Or, perhaps we could signal ourselves, so that if the caller does a
  499. **    Wait(), the effect will be as if our task switched out, the .device
  500. **    satisfied the i/o and returned the message, and we woke up again...
  501. **
  502. */
  503. void write_ser_asynch (char *buf, ULONG size)
  504. {
  505.     ULONG copy_size;
  506.  
  507.     for (;size ; size -= copy_size, buf += copy_size)
  508.     {
  509.     IOExtSer *ior;
  510.     ser_bucket_t *b;
  511.     ser_bucket_keeper_t *sbk;
  512.  
  513.     if (sbk = (ser_bucket_keeper_t *) GetHead (ser_bucket_list) )
  514.     {
  515.         Remove ( (Node *) sbk);
  516.         b = sbk->sbk_alter_ego;
  517.         copy_size = b->b_size;
  518.     }
  519.     else
  520.     {
  521.         NEW (b);
  522.         if (!b)
  523.         {
  524.         exit (10);
  525.         }
  526.         copy_size = b->b_size = SER_BUCKET_SIZE;
  527.         NEWN (b->b_buf, b->b_size);
  528.         if (!b->b_buf)
  529.         {
  530.         FREE (b);
  531.         exit (10);
  532.         }
  533.         b->b_self = b;
  534.         sbk = &(b->b_link);
  535.     }
  536.     ior = &(b->b_ior);
  537.     copy_size = MIN (copy_size, size);
  538.  
  539.     CopyMem (ser_write_req, ior, sizeof (IOExtSer) );
  540.     CopyMem (buf, b->b_buf, copy_size);
  541.  
  542.     /*
  543.     **  Pro'ly not all of these need to be reset (any except {io_Length}
  544.     **  ({io_Data} could be set when the buffer is allocated?)...
  545.     **
  546.     */
  547.     ior->IOSer.io_Flags            = IOF_QUICK;
  548.     ior->IOSer.io_Message.mn_ReplyPort  = ser_write_reply_port;
  549.     ior->IOSer.io_Command            = CMD_WRITE;
  550.     ior->IOSer.io_Length            = copy_size;
  551.     ior->IOSer.io_Data            = b->b_buf;
  552.  
  553.     BeginIO ( (IORequest *) ior);
  554.     if (!(ior->IOSer.io_Flags & IOF_QUICK) )
  555.         AddTail (ser_writes_p_list, (Node *) sbk);
  556.     else
  557.     {
  558.         AddTail (ser_bucket_list, (Node *) sbk);
  559.         ior->IOSer.io_Flags = 0;
  560.     }
  561.     }
  562. }
  563.  
  564.  
  565.  
  566. /*
  567. **  Remove replies to write_ser_asynch(), above, and stash the
  568. **  {ser_bucket_t} buffers in another List.
  569. **
  570. **  Call this when signaled with {ser_write_reply_mask}.
  571. **
  572. **  (Question: Is it worth generalizing to include Read replies, as well?
  573. **  The internal serial.device can only handle one read request at a
  574. **  time...  Keeping it with no args and only one list is simpler.)
  575. **
  576. */
  577. void clear_write_replies (void)
  578. {
  579.     ser_bucket_t *b;
  580.     ser_bucket_keeper_t *sbk;
  581.  
  582.     while (b = (ser_bucket_t *) GetMsg (ser_write_reply_port) )
  583.     {
  584.     if (b->b_ior.IOSer.io_Error)
  585.         handle_error (error_type_serial, &(b->b_ior) );
  586.  
  587.     /*
  588.     **  REDUNDANT???
  589.     **  Remove (&(b->b_link) );
  590.     **
  591.     */
  592.  
  593.     sbk = &(b->b_link);
  594.     Remove (&(sbk->sbk_link) );
  595.  
  596.     AddTail (ser_bucket_list, (Node *) sbk);
  597.     }
  598. }
  599.  
  600.  
  601.  
  602. /*
  603. **  Initiate an asynch read from the serial port of up to {size} bytes,
  604. **  or 1 byte if the serial port is empty.
  605. **
  606. **  Caller must examine IOSer.io_Actual when it eventually comes back in
  607. **  order to determine how many bytes were read.
  608. **
  609. */
  610. void read_ser_asynch (char *buf, ULONG max)
  611. {
  612.     ULONG read_len;
  613.  
  614.     /*
  615.     **    Old stuff????  Why not BeginIO(), below, rather than SendIO()?
  616.     **        (Because the caller may Wait() on a signal mask including that
  617.     **        for this IO operation...  See NOTES for write_ser_async()...)
  618.     **
  619.     **    ser_status_req->IOSer.io_Command = SDCMD_QUERY;
  620.     **    ser_status_req->IOSer.io_Flags     = IOF_QUICK;
  621.     **    BeginIO ( (IORequest *) ser_status_req);
  622.     **
  623.     **    if (!(ser_status_req->IOSer.io_Flags & IOF_QUICK) )
  624.     **        WaitIO ( (IORequest *) ser_status_req);
  625.     **
  626.     */
  627.  
  628.     read_len = ser_chars_pending ();
  629.  
  630.     ser_read_req->IOSer.io_Command = CMD_READ;
  631.     ser_read_req->IOSer.io_Length  = MIN (max, MAX (1, read_len) );
  632.     ser_read_req->IOSer.io_Data    = buf;
  633.     SendIO ( (IORequest *) ser_read_req);
  634. }
  635.  
  636.  
  637.  
  638. /*
  639. **  Find out how many characters are pending for read from the serial
  640. **  device.
  641. **
  642. **  {SDCMD_QUERY} "always succeeds," say autodocs...
  643. **
  644. */
  645. ULONG ser_chars_pending (void)
  646. {
  647.     query_ser ();
  648.     return (ser_status_req->IOSer.io_Actual);
  649. }
  650.  
  651.  
  652.  
  653. /*
  654. **  Query the state of the serial port.  Presently, returns an {IOExtSer *}.
  655. **
  656. */
  657. IOExtSer *query_ser (void)
  658. {
  659.     ser_status_req->IOSer.io_Command = SDCMD_QUERY;
  660.     DoIO ( (IORequest *) ser_status_req);
  661.     return (ser_status_req);
  662. }
  663.  
  664.