home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / devices / modm0dev.zoo / modm0dev.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-25  |  30.1 KB  |  1,142 lines

  1. /*
  2.  * modm0dev.c: Serial device driver for the modem1 port, dial-in
  3.  * (/dev/ttyd0) and dial-out (/dev/cua0).
  4.  * This is free software without any warranty; see the file "COPYING" for
  5.  * details.
  6.  *
  7.  * This file must be compiled with 16-bit integers.
  8.  *
  9.  * Author:  Thierry Bousch (bousch@suntopo.matups.fr)
  10.  * Version: 1.0 (oct 93)
  11.  *
  12.  * Revision history:
  13.  *
  14.  * 0.1    First working version. Very few ioctl's are implemented.
  15.  * 0.2    Setting the terminal flags (break, echo...) is no longer a
  16.  *    privileged instruction!
  17.  *    Speed can be changed via ioctl's.
  18.  *    We now raise the SIGHUP signal only when writing to a disconnected
  19.  *    /dev/ttyd0, not when reading.
  20.  * 0.3    Handles MFP error conditions, especially breaks. Fixed a bug in
  21.  *    the RAISE macro (signals should work now).
  22.  *    The dial-in and dial-out devices now have different tty structures,
  23.  *    so they can be configured independently.
  24.  *    The ioctl stuff has been cleaned.
  25.  * 0.4    We used to check for the carrier (and control characters) only in
  26.  *    cua_read and cua_write, so programs which didn't read from the
  27.  *    standard input couldn't get signals. So we install a daemon which
  28.  *    will check this periodically.
  29.  * 0.5    Cleaned the cua_read/cua_write/daemon code; now signals can only
  30.  *    be raised by the daemon, using the non-kernel Pkill().
  31.  * 0.6    Much cleanup; select() stuff removed; added utility function for
  32.  *    ioctl(FIONREAD/FIONWRITE). Fixed the assembly file also.
  33.  *    Many bugfixes in the daemon code. Now hardware errors are logged
  34.  *    even when the device isn't a controlling terminal.
  35.  *    Added ioctls to read and modify soft_carrier and hangup_control.
  36.  *    Moved code between modm0dev.c and modm0dev.h.
  37.  * 0.7    ^S/^Q weren't handled by the daemon, bummer. Rewrote this part.
  38.  *    We also keep track of the connection number in a static register,
  39.  *    so that we can track stale FILEPTR's.
  40.  *    Complete rewrite of the mutual exclusion stuff; now there can be
  41.  *    several FILEPTR's to /dev/ttyd0 (useful for talk); we use f->pos
  42.  *    to store the expected connection_id, or 0 for the dial-out device.
  43.  *    Added the dont_emit flag, to inform the interrupt routines that the
  44.  *    terminal has been stopped or restarted (useful if the transmission
  45.  *    buffer is big).
  46.  *    Added support for the O_NDELAY flag, and a sanity check.
  47.  *    The bytes_available() function now returns UNLIMITED for stale
  48.  *    fileptrs, because read/write operations won't block.
  49.  *    More checks in the installation procedure.
  50.  * 0.8    Added select() support. Increased the time in some sleep-loops.
  51.  *    The main program now simply terminates, instead of being a TSR.
  52.  *    Added (optional) syslog support. Macro-ized some constants.
  53.  * 0.9    Some tasks (closing the connection, hanging up) are deferred to
  54.  *    the daemon in order to make open/close operations atomic.
  55.  *    Shutdown should work now (the mdm_close() function used to be
  56.  *    interruptible by a signal, thus causing the FILEPTR to be closed
  57.  *    twice, and MiNT crashed with a fatal error).
  58.  * 1.0    Always release the SEMA_MDM semaphore on hangup, without clearing
  59.  *    p_curr_tty so that we can still send SIGHUPs.
  60.  *    Get rid of the ttyd_opened variable, since mdm_tty.use_cnt contains
  61.  *    the same information.
  62.  *    Clear p_curr_tty on hangup when the signal can't be sent immediately
  63.  *    (we want to kill the current process group, not the future ones
  64.  *    which will control the terminal -- getty should work better now.)
  65.  */
  66.  
  67. #define  MDM_VERSION    "1.0"
  68.  
  69. #include <minimal.h>
  70. #include <string.h>
  71. #include <stdlib.h>
  72. #include <osbind.h>
  73. #include <basepage.h>
  74. #include <mintbind.h>
  75. #include <signal.h>
  76.  
  77. #include "atarierr.h"
  78. #include "filesys.h"
  79. #include "modm0dev.h"
  80.  
  81. #ifdef USE_SYSLOG
  82. #include <syslog.h>
  83. #endif
  84.  
  85. /*
  86.  * Constants for ioctl, missing in filesys.h
  87.  */
  88.  
  89. #define T_TANDEM    0x1000
  90. #define T_RTSCTS    0x2000
  91. #define T_EVENP        0x4000    /* EVENP and ODDP are mutually exclusive */
  92. #define T_ODDP        0x8000
  93.  
  94. #define TF_FLAGS    0xF000
  95.  
  96. /* some flags for TIOC[GS]FLAGS */
  97. #define TF_STOPBITS    0x0003
  98. #define TF_1STOP    0x0001
  99. #define TF_15STOP    0x0002
  100. #define    TF_2STOP    0x0003
  101.  
  102. #define TF_CHARBITS    0x000C
  103. #define TF_8BIT        0
  104. #define TF_7BIT        0x4
  105. #define TF_6BIT        0x8
  106. #define TF_5BIT        0xC
  107.  
  108. /*
  109.  * kernel functions
  110.  */
  111.  
  112. #define TGETTIME    (*kernel->dos_tab[0x2c])
  113. #define TGETDATE    (*kernel->dos_tab[0x2a])
  114. #define YIELD        (*kernel->dos_tab[0xff])
  115. #define PGETPID        (*kernel->dos_tab[0x10b])
  116. #define PKILL        (*kernel->dos_tab[0x111])
  117. #define FSELECT        (*kernel->dos_tab[0x11d])
  118. #define PGETEUID    (*kernel->dos_tab[0x138])
  119.  
  120. #define NAP        (*kernel->nap)
  121. #define SLEEP        (*kernel->sleep)
  122. #define WAKE        (*kernel->wake)
  123. #define WAKESELECT    (*kernel->wakeselect)
  124.  
  125. /* 
  126.  * Debugging stuff
  127.  */
  128.  
  129. #ifdef NDEBUG
  130. #define DEBUG(x)
  131. #define ALERT(x)
  132. #define TRACE(x)
  133. #define FATAL(x)
  134. #else
  135. #define DEBUG(x)    (*kernel->debug)x
  136. #define ALERT(x)    (*kernel->alert)x
  137. #define TRACE(x)    (*kernel->trace)x
  138. #define FATAL(x)    (*kernel->fatal)x
  139. #endif
  140.  
  141. /* daemon stack size */
  142. #define D_STACK  512L
  143.  
  144. #define spl7()            \
  145. ({  register short retvalue;    \
  146.     __asm__ volatile("        \
  147.     movew sr,%0;         \
  148.     oriw  #0x0700,sr "     \
  149.     : "=d"(retvalue)         \
  150.     ); retvalue; })
  151.  
  152. #define spl(N)            \
  153. ({                  \
  154.     __asm__ volatile("        \
  155.     movew %0,sr "         \
  156.     :                \
  157.     : "d"(N) ); })
  158.  
  159. /*
  160.  * Symbols from modm0asm.s
  161.  */
  162.  
  163. extern long old_evt_timer;
  164. extern long old_txrint, old_rcvint, old_txerror, old_rxerror;
  165. extern int dont_emit;
  166. void dtr_on(void), dtr_off(void);
  167. void rts_on(void), rts_off(void);
  168. void carrier_monitor(void);
  169. void txrint(void), rcvint(void), txerror(void), rxerror(void);
  170. void clear_errors(void), check_errors(void);
  171. void setstack(long sp);
  172.  
  173. /*
  174.  * Forward definitions of the device driver functions
  175.  */
  176.  
  177. long    cua_open    (FILEPTR *f);
  178. long    cua_write    (FILEPTR *f, char *buf, long bytes);
  179. long    cua_read    (FILEPTR *f, char *buf, long bytes);
  180. long    cua_lseek    (FILEPTR *f, long where, int whence);
  181. long    cua_ioctl    (FILEPTR *f, int mode, void *buf);
  182. long    cua_datime    (FILEPTR *f, int *timeptr, int rwflag);
  183. long    cua_close    (FILEPTR *f, int pid);
  184. long    cua_select    (FILEPTR *f, long proc, int mode);
  185. void    cua_unselect    (FILEPTR *f, long proc, int mode);
  186.  
  187. long    mdm_open    (FILEPTR *f);
  188. long    mdm_write    (FILEPTR *f, char *buf, long bytes);
  189. long    mdm_read    (FILEPTR *f, char *buf, long bytes);
  190. long    mdm_lseek    (FILEPTR *f, long where, int whence);
  191. long    mdm_ioctl    (FILEPTR *f, int mode, void *buf);
  192. long    mdm_datime    (FILEPTR *f, int *timeptr, int rwflag);
  193. long    mdm_close    (FILEPTR *f, int pid);
  194. long    mdm_select    (FILEPTR *f, long proc, int mode);
  195. void    mdm_unselect    (FILEPTR *f, long proc, int mode);
  196.  
  197. DEVDRV mdm_device = {
  198.     mdm_open, mdm_write, mdm_read, mdm_lseek, mdm_ioctl,
  199.     mdm_datime, mdm_close, mdm_select, mdm_unselect
  200. };
  201.  
  202. DEVDRV cua_device = {
  203.     cua_open, cua_write, cua_read, cua_lseek, cua_ioctl,
  204.     cua_datime, cua_close, cua_select, cua_unselect
  205. };
  206.  
  207. struct tty mdm_tty, cua_tty;
  208.  
  209. struct dev_descr mdm_devinfo = { &mdm_device,   0, O_TTY, &mdm_tty },
  210.          cua_devinfo = { &cua_device, 128, O_TTY, &cua_tty };
  211.  
  212. char    rx_buf_start[RX_BUF_LENGTH], 
  213.     *rx_buf_end, 
  214.     *rx_buf_tail;
  215.  
  216. /*
  217.  * The "volatile" variables can be modified by an interrupt, so the
  218.  * compiler will not optimize instructions containing them. This is
  219.  * sometimes a problem: instructions like rx_buf_used-- and
  220.  * tx_buf_used++ will translate into several assembly instructions,
  221.  * and if an interrupt occurs inbetween, then we lose. This explains
  222.  * the hacks in cua_read and cua_write to make them atomic.
  223.  */
  224.  
  225. volatile char *rx_buf_head;
  226. volatile long rx_buf_used;
  227.  
  228. char    tx_buf_start[TX_BUF_LENGTH], 
  229.     *tx_buf_end, 
  230.     *tx_buf_head;
  231.  
  232. volatile char *tx_buf_tail;
  233. volatile long tx_buf_used;
  234.  
  235. volatile int hard_carrier;
  236.  
  237. /*
  238.  * Which device is currently in use ? The "semaphore" variable contains
  239.  * SEMA_MDM if data have been read from/written to the dial-in device,
  240.  * SEMA_CUA if the dial-out device is open, and 0 otherwise. Once the
  241.  * semaphore is owned (i.e. != 0) by one of the devices, it is released
  242.  * on the last close of this device.
  243.  */
  244.  
  245. #define SEMA_NONE    0
  246. #define SEMA_MDM    1
  247. #define SEMA_CUA    2
  248.  
  249. int semaphore = 0;    /* no direction selected yet */
  250. int sema_pid;        /* the process owning the semaphore */
  251.  
  252. /*
  253.  * Daemon state flags
  254.  */
  255.  
  256. #define CLOSING_CONN    0x1
  257. #define HANGING_UP    0x2
  258.  
  259. int daemon_state = 0;
  260.  
  261. /*
  262.  * Other global information
  263.  */
  264.  
  265. int connection_id = 0;        /* initial state, no connection  */
  266. struct tty *p_curr_tty = NULL;    /* which tty will get signals?   */
  267. long _stksize = 2048L;        /* 2kb of stack should be enough */
  268. long rx_sel = 0L, tx_sel = 0L;    /* selecting processes         */
  269. FILEPTR *rx_fsel, *tx_fsel;    /* the corresponding FILEPTRs     */
  270. int soft_carrier;        /* soft carrier flag         */
  271. int hangup_control;        /* hangup-on-close flag         */
  272. int rts_cts;            /* RTS/CTS control flag         */
  273. long rx_lowmark, rx_highmark;
  274. long baudrate;
  275. int parity, charstopbits;
  276. struct kerinfo *kernel;
  277.  
  278. #if !defined(USE_SYSLOG) && !defined(NDEBUG)
  279. int logfile;            /* where shall we log errors?     */
  280. #endif
  281.  
  282. /*
  283.  * Various utility macros and functions
  284.  */
  285.  
  286. #define ATOMIC(x) \
  287.     do { int sr=spl7(); x; spl(sr); } while(0)
  288.  
  289. void flush_rx_buffer (void)
  290. {
  291.     ATOMIC(rx_buf_head = rx_buf_tail; rx_buf_used = 0L);
  292. }
  293.  
  294. void flush_tx_buffer (void)
  295. {
  296.     ATOMIC(tx_buf_tail = tx_buf_head; tx_buf_used = 0L);
  297. }
  298.  
  299. /* Get the semaphore (if possible) and make some initializations */
  300.  
  301. int get_semaphore (int sem)
  302. {
  303.     if (semaphore)
  304.         return semaphore;    /* Impossible */
  305.     semaphore = sem;
  306.     sema_pid = PGETPID();
  307.  
  308.     clear_errors();
  309.     /* Select the controlling tty structure (for signals) */
  310.     p_curr_tty = (semaphore == SEMA_CUA) ? &cua_tty : &mdm_tty;
  311.     return 0;            /* Success */
  312. }
  313.  
  314. /* 
  315.  * Hardware configuration functions: set speed, bits/char, parity,
  316.  * and break handling.
  317.  */
  318.  
  319. void set_baudrate (long speed)
  320. {
  321.     int a;
  322.  
  323.     switch (speed) {
  324.       case   300: a = 9; break;
  325.       case   600: a = 8; break;
  326.       case  1200: a = 7; break;
  327.       case  1800: a = 6; break;
  328.       case  2400: a = 4; break;
  329.       case  4800: a = 2; break;
  330.       case  9600: a = 1; break;
  331.       case 19200: a = 0; break;
  332.       default: return;
  333.     }
  334.     Rsconf(a,-1,-1,-1,-1,-1);
  335.     baudrate = speed;
  336. }
  337.       
  338. void set_bits (int flags)
  339. {
  340.     unsigned char *ucr = (unsigned char *)(0xfffffa29UL);
  341.  
  342.     if (!(flags & TF_STOPBITS))    /* it would mean "synchronous" */
  343.         flags |= TF_1STOP;    /* assume 1-stopbit mode */
  344.     *ucr = (*ucr & 0x87) | (flags << 3);
  345.     charstopbits = flags;
  346. }
  347.  
  348. void set_parity (int flags)
  349. {
  350.     unsigned char *ucr = (unsigned char *)(0xfffffa29UL), tmp;
  351.     
  352.     tmp = *ucr & 0xf9;    /* clear bits 1 and 2 */
  353.     if (flags == T_ODDP)
  354.         *ucr = tmp | 4;    /* odd parity */
  355.     else if (flags == T_EVENP)
  356.         *ucr = tmp | 6;    /* even parity */
  357.     else {    
  358.         *ucr = tmp;    /* no parity */
  359.         flags = 0;
  360.     }
  361.     parity = flags;
  362. }
  363.  
  364. void break_condition (int flag)
  365. {
  366.     unsigned char *tsr = (unsigned char *)(0xfffffa2dUL);
  367.     
  368.     *tsr = flag ? (*tsr | 8) : (*tsr & ~8);
  369. }
  370.  
  371. /* 
  372.  * Are we connected? We need two macros for that; CONNECTED() has a little
  373.  * delay, but is synchronous with the daemon. The REALLY_CONNECTED() macro
  374.  * is only used by the daemon.
  375.  */
  376.  
  377. #define REALLY_CONNECTED()    (soft_carrier || hard_carrier)
  378. #define CONNECTED()        (connection_id % 2)
  379.  
  380. /*
  381.  * Opening the device
  382.  */
  383.  
  384. long cua_open (FILEPTR *f)
  385. {
  386. #ifdef SECURE_OPEN
  387.     if (PGETEUID()) {
  388.         DEBUG(("cua_open: only root can open this device"));
  389.         return EACCDN;
  390.     }
  391. #endif
  392.     if (get_semaphore(SEMA_CUA)) {
  393.         DEBUG(("cua_open: serial interface is busy"));
  394.         return EACCDN;
  395.     }
  396.     TRACE(("cua_open: dial-out device opened"));
  397.     daemon_state |= HANGING_UP;
  398.     /* f->pos = 0; */
  399.  
  400.     f->flags |= O_TTY;
  401.     return 0;
  402. }
  403.  
  404. long mdm_open (FILEPTR *f)
  405. {
  406. #ifdef SECURE_OPEN
  407.     if (PGETEUID()) {
  408.         DEBUG(("mdm_open: only root can open this device"));
  409.         return EACCDN;
  410.     }
  411. #endif
  412.     TRACE(("mdm_open: dial-in device opened"));
  413.     /* For which connection_id is this fileptr valid ? */
  414.     f->pos = CONNECTED() ? connection_id : connection_id+1;
  415.  
  416.     f->flags |= O_TTY;
  417.     return 0;
  418. }
  419.  
  420. /* 
  421.  * This is a terminal, thus seek() should always return 0 
  422.  */
  423.  
  424. long cua_lseek (FILEPTR *f, long where, int whence)
  425. {
  426.     TRACE(("cua_lseek: returns always 0"));
  427.     return 0;
  428. }
  429.  
  430. #if 0
  431. long mdm_lseek (FILEPTR *f, long where, int whence)
  432. {
  433.     return cua_lseek(f, where, whence);
  434. }
  435. #else
  436. __asm__ (".stabs \"_mdm_lseek\", 5,0,0, _cua_lseek");
  437. #endif
  438.  
  439. /*
  440.  * Time and date -- always return the current time. Anyway, MiNT
  441.  * will never call this function since we're a terminal (at least in
  442.  * the current version).
  443.  */
  444.  
  445. long cua_datime (FILEPTR *f, int *timeptr, int rwflag)
  446. {
  447.     if (rwflag) {
  448.         DEBUG(("cua_datime: can't modify date/time"));
  449.         return EACCDN;
  450.     }
  451.     TRACE(("cua_datime: read time and date"));
  452.     *timeptr++ = TGETTIME();
  453.     *timeptr   = TGETDATE();
  454.     return 0;
  455. }
  456.  
  457. #if 0
  458. long mdm_datime (FILEPTR *f, int *timeptr, int rwflag)
  459. {
  460.     return cua_datime(f, timeptr, rwflag);
  461. }
  462. #else
  463. __asm__ (".stabs \"_mdm_datime\", 5,0,0, _cua_datime");
  464. #endif
  465.  
  466. /*
  467.  * Closing the device. We shouldn't hang-up straight away, or data still
  468.  * in the buffer could be lost.
  469.  */
  470.  
  471. long cua_close (FILEPTR *f, int pid)
  472. {
  473.     if (f->links == 0) {
  474.         TRACE(("cua_close: closing dial-out device"));
  475.         p_curr_tty = NULL;
  476.         semaphore = 0;
  477.         daemon_state |= (CLOSING_CONN|HANGING_UP);
  478.     }
  479.     return 0;
  480. }
  481.  
  482. long mdm_close (FILEPTR *f, int pid)
  483. {
  484.     if (p_curr_tty == &mdm_tty && mdm_tty.use_cnt == 0) {
  485.         TRACE(("mdm_close: closing dial-in device"));
  486.         p_curr_tty = NULL;
  487.         semaphore = 0;
  488.         daemon_state |= CLOSING_CONN;
  489.         if (hangup_control)
  490.             daemon_state |= HANGING_UP;
  491.     }
  492.     return 0;
  493. }
  494.  
  495. /*
  496.  * Utility function: returns the number of bytes which can be read from
  497.  * or written to this device without blocking.
  498.  */
  499.  
  500. long bytes_available (FILEPTR *f, int mode)
  501. {
  502.     if (daemon_state)
  503.         return 0;            /* daemon is busy */
  504.     if (f->pos) {
  505.         if (semaphore == SEMA_CUA)
  506.             return 0;        /* we don't own the stream */
  507.         if (f->pos > connection_id)
  508.             return 0;        /* no connection yet */
  509.         if (f->pos < connection_id)
  510.             return UNLIMITED;    /* stale fileptr */
  511.     }
  512.     return (mode==FIONREAD) ? rx_buf_used : (TX_BUF_LENGTH-tx_buf_used);
  513. }
  514.  
  515. /*
  516.  * Selecting and unselecting one of the devices.
  517.  * BUG: there can be only one select'ing process in each direction; this
  518.  * means that you cannot simultaneously select on the dial-in and dial-out
  519.  * devices; fortunately, getty doesn't use select.
  520.  */
  521.  
  522. long cua_select (FILEPTR *f, long proc, int mode)
  523. {
  524.     TRACE(("cua_select: mode %d", mode));
  525.     if (mode == O_WRONLY) {
  526.         if (tx_sel || bytes_available(f,FIONWRITE))
  527.             return 1;
  528.         TRACE(("cua_select: going to sleep -- can't write"));
  529.         tx_sel = proc;
  530.         tx_fsel = f;
  531.         return 0;
  532.     }
  533.     if (mode == O_RDONLY) {
  534.         if (rx_sel || bytes_available(f,FIONREAD))
  535.             return 1;
  536.         TRACE(("cua_select: going to sleep -- can't read"));
  537.         rx_sel = proc;
  538.         rx_fsel = f;
  539.         return 0;
  540.     }
  541.     DEBUG(("cua_select: invalid mode"));
  542.     return EINVFN;    
  543. }
  544.  
  545. #if 0
  546. long mdm_select (FILEPTR *f, long proc, int mode)
  547. {
  548.     return cua_select(f, proc, mode);
  549. }
  550. #else
  551. __asm__ (".stabs \"_mdm_select\", 5,0,0, _cua_select");
  552. #endif
  553.  
  554. void cua_unselect (FILEPTR *f, long proc, int mode)
  555. {
  556.     TRACE(("cua_unselect: mode %d", mode));
  557.     if (mode == O_WRONLY)
  558.         tx_sel = 0L;
  559.     if (mode == O_RDONLY)
  560.         rx_sel = 0L;
  561. }
  562.  
  563. #if 0
  564. void mdm_unselect (FILEPTR *f, long proc, int mode)
  565. {
  566.     return cua_unselect(f, proc, mode);
  567. }
  568. #else
  569. __asm__ (".stabs \"_mdm_unselect\", 5,0,0, _cua_unselect");
  570. #endif
  571.  
  572. /*
  573.  * Reading from / writing to the device.
  574.  *
  575.  * Since it is a terminal, we know that "bytes" will always be a multiple
  576.  * of four; but we check it anyway.
  577.  */
  578.  
  579. long cua_read (FILEPTR *f, char *buf, long bytes)
  580. {
  581.     long left=bytes;
  582.     char c;
  583.  
  584.     if (bytes<0 || bytes%4) {
  585.         DEBUG(("cua_read: bytes out of range"));
  586.         return ERANGE;
  587.     }
  588.     TRACE(("cua_read: read %ld bytes", bytes));
  589.     while (left) {
  590.         if (daemon_state != 0) {
  591.             TRACE(("mdm_read: the daemon has something to do"));
  592.             if (f->flags & O_NDELAY)  break;
  593.             NAP(100);
  594.             continue;
  595.         }
  596.         if (f->pos) {    /* dial-in fileptr? */
  597.             if (semaphore == SEMA_CUA) {
  598.                 TRACE(("mdm_read: dial-out device in use"));
  599.                 if (f->flags & O_NDELAY)  break;
  600.                 NAP(1000);
  601.                 continue;
  602.             }
  603.             /* Too early? */
  604.             if (f->pos > connection_id) {
  605.                 TRACE(("mdm_read: no connection yet"));
  606.                 if (f->flags & O_NDELAY)  break;
  607.                 NAP(100);
  608.                 continue;
  609.             }
  610.             /* Too late? */
  611.             if (f->pos < connection_id) {
  612.                 DEBUG(("mdm_read: stale fileptr"));
  613.                 break;
  614.             }
  615.             /* All OK! grab the semaphore if possible */
  616.             get_semaphore(SEMA_MDM);
  617.         }
  618.         /* Wait for data */
  619.         if (rx_buf_used == 0) {
  620.             TRACE(("cua_read: no data present in buffer"));
  621.             if (f->flags & O_NDELAY)  break;
  622.             NAP(100);
  623.             continue;
  624.         }
  625.         c = *rx_buf_tail++;
  626.         if (rx_buf_tail == rx_buf_end)
  627.             rx_buf_tail = rx_buf_start;
  628.         *buf++ = 0;
  629.         *buf++ = 0;
  630.         *buf++ = 0;
  631.         *buf++ = c;
  632. #if 0
  633.         ATOMIC(rx_buf_used--);
  634. #else
  635.         __asm__ volatile ("subql #1, _rx_buf_used");
  636. #endif
  637.         left -= 4;    /* four bytes read */
  638.     }
  639.     return bytes - left;
  640. }
  641.  
  642. #if 0
  643. long mdm_read (FILEPTR *f, char *buf, long bytes)
  644.     return cua_read(f, buf, bytes); 
  645. }
  646. #else
  647. __asm__ (".stabs \"_mdm_read\", 5,0,0, _cua_read");
  648. #endif
  649.  
  650. long cua_write (FILEPTR *f, char *buf, long bytes)
  651. {
  652.     long left=bytes;
  653.     char c;
  654.     
  655.     if (bytes<0 || bytes%4) {
  656.         DEBUG(("cua_write: bytes out of range"));
  657.         return ERANGE;
  658.     }
  659.     TRACE(("cua_write: write %ld bytes", bytes));
  660.     while (left) {
  661.         if (daemon_state != 0) {
  662.             TRACE(("mdm_write: the daemon has something to do"));
  663.             if (f->flags & O_NDELAY)  break;
  664.             NAP(100);
  665.             continue;
  666.         }
  667.         if (f->pos) {    /* dial-in fileptr? */
  668.             if (semaphore == SEMA_CUA) {
  669.                 TRACE(("mdm_write: dial-out device in use"));
  670.                 if (f->flags & O_NDELAY)  break;
  671.                 NAP(1000);
  672.                 continue;
  673.             }
  674.             /* Too early? */
  675.             if (f->pos > connection_id) {
  676.                 TRACE(("mdm_write: no connection yet"));
  677.                 if (f->flags & O_NDELAY)  break;
  678.                 NAP(100);
  679.                 continue;
  680.             }
  681.             /* Too late? */
  682.             if (f->pos < connection_id) {
  683.                 DEBUG(("mdm_write: stale fileptr"));
  684.                 break;
  685.             }
  686.             /* All OK! grab the semaphore if possible */
  687.             get_semaphore(SEMA_MDM);
  688.         }
  689.         /* Wait to transmit data */
  690.         if (tx_buf_used == TX_BUF_LENGTH) {
  691.             TRACE(("cua_write: no room left in buffer"));
  692.             if (f->flags & O_NDELAY)  break;
  693.             NAP(100);
  694.             continue;
  695.         }
  696.         buf += 3;    /* skip first three bytes */
  697.         c = *buf++;    /* this one only is significant */
  698.         *tx_buf_head++ = c;
  699.         if (tx_buf_head == tx_buf_end)
  700.             tx_buf_head = tx_buf_start;
  701. #if 0
  702.         ATOMIC(tx_buf_used++);
  703. #else
  704.         __asm__ volatile("addql #1, _tx_buf_used");
  705. #endif
  706.         left -= 4;    /* four bytes written */
  707.     }
  708.     return bytes - left;
  709. }
  710.  
  711. #if 0
  712. long mdm_write (FILEPTR *f, char *buf, long bytes)
  713.     return cua_write(f, buf, bytes); 
  714. }
  715. #else
  716. __asm__ (".stabs \"_mdm_write\", 5,0,0, _cua_write");
  717. #endif
  718.  
  719. /*
  720.  * Ioctl operations: we can flush the buffers or get/set the baudrate
  721.  */
  722.  
  723. long cua_ioctl (FILEPTR *f, int mode, void *buf)
  724. {
  725.     long free;
  726.     long new_baudrate;
  727.     int flags, new_flags;
  728.  
  729.     if (mode == FIONREAD || mode == FIONWRITE) {
  730.         free = bytes_available(f,mode);
  731.         TRACE(("cua_ioctl(0x%x) returned %ld", mode, free));
  732.         *(long *)buf = free;
  733.     } 
  734.     else if (mode == TIOCFLUSH) {
  735.         TRACE(("cua_ioctl(TIOCFLUSH): clear buffers"));
  736.         flush_rx_buffer();
  737.         flush_tx_buffer();
  738.     } 
  739.     else if (mode == TIOCIBAUD || mode == TIOCOBAUD) {
  740.         new_baudrate = *(long *)buf;
  741.         *(long *)buf = baudrate;
  742.         TRACE(("cua_ioctl(TIOC?BAUD): was %ld now %ld", 
  743.             baudrate, new_baudrate));
  744.         if (new_baudrate == 0)
  745.             daemon_state |= HANGING_UP;
  746.         else if (new_baudrate != baudrate && PGETEUID() == 0)
  747.             set_baudrate(new_baudrate);
  748.     }
  749.     else if (mode == TIOCCBRK) {
  750.         TRACE(("cua_ioctl(TIOCCBRK): clear break condition"));
  751.         break_condition(0);
  752.     }
  753.     else if (mode == TIOCSBRK) {
  754.         TRACE(("cua_ioctl(TIOCSBRK): set break condition"));
  755.         break_condition(1);
  756.     }
  757.     else if (mode == TIOCGFLAGS || mode == TIOCSFLAGS) {
  758.         flags = parity | charstopbits;
  759.         if (rts_cts)
  760.             flags |= T_RTSCTS;
  761.         new_flags = *(int *)buf;
  762.         *(int *)buf = flags;
  763.         TRACE(("cua_ioctl(TIOCGFLAGS): flags were 0x%02x", flags));
  764.         if (mode == TIOCSFLAGS && PGETEUID() == 0) {
  765.             TRACE(("cua_ioctl(TIOCSFLAGS): new flags are 0x%02x",
  766.                 new_flags));
  767.             set_bits(new_flags & (TF_CHARBITS|TF_STOPBITS));
  768.             set_parity(new_flags & (T_ODDP|T_EVENP));
  769.             rts_cts = !!(new_flags & T_RTSCTS);
  770.         }
  771.     }
  772. #ifdef NON_OFFICIAL_IOCTLS
  773.     else if (mode == TIOCGHUPCL || mode == TIOCSHUPCL) {
  774.         new_flags = *(int *)buf;
  775.         *(int *)buf = hangup_control;
  776.         TRACE(("cua_ioctl(TIOCGHUPCL) returned %d", hangup_control));
  777.         if (mode == TIOCSHUPCL && PGETEUID() == 0) {
  778.             hangup_control = !!new_flags;
  779.             TRACE(("cua_ioctl(TIOCSHUPCL) is now %d",
  780.                 hangup_control));
  781.         }
  782.     }
  783.     else if (mode == TIOCGSOFTCAR || mode == TIOCSSOFTCAR) {
  784.         new_flags = *(int *)buf;
  785.         *(int *)buf = soft_carrier;
  786.         TRACE(("cua_ioctl(TIOCGSOFTCAR) returned %d", soft_carrier));
  787.         if (mode == TIOCSSOFTCAR && PGETEUID() == 0) {
  788.             soft_carrier = !!new_flags;
  789.             TRACE(("cua_ioctl(TIOCSSOFTCAR) is now %d",
  790.                 soft_carrier));
  791.         }
  792.     }
  793. #endif
  794.     else {
  795.         TRACE(("cua_ioctl(0x%x): invalid mode", mode));
  796.         return EINVFN;
  797.     }
  798.     return 0;
  799. }
  800.  
  801. #if 0
  802. long mdm_ioctl (FILEPTR *f, int mode, void *buf)
  803. {
  804.     return cua_ioctl(f, mode, buf);
  805. }
  806. #else
  807. __asm__ (".stabs \"_mdm_ioctl\", 5,0,0, _cua_ioctl");
  808. #endif
  809.  
  810. /*
  811.  * Hardware error handlers, invoked by check_errors(). Note that they are
  812.  * called by the daemon process, so they can only use non-kernel functions.
  813.  */
  814.  
  815. #define Raise_And_Flush(sig)                        \
  816.     do {                                \
  817.         flush_rx_buffer();                        \
  818.         flush_tx_buffer();                        \
  819.         if (p_curr_tty && p_curr_tty->pgrp) {                \
  820.             p_curr_tty->state &= ~TS_HOLD;                \
  821.             (void) Pkill(-(p_curr_tty->pgrp),(int)(sig)); }        \
  822.     } while(0)
  823.  
  824. /*
  825.  * Syslog(pri,s): logs message "s" with priority "pri"; this parameter is
  826.  * ignored if USE_SYSLOG is not defined.
  827.  */
  828.  
  829. #if defined(USE_SYSLOG)
  830. #define Syslog(pri,s)            \
  831.   do {                    \
  832.       openlog(D_NAME,0,LOG_DAEMON);    \
  833.       syslog(pri, s);            \
  834.       closelog();            \
  835.   } while(0)
  836. #elif defined(NDEBUG)
  837. #define Syslog(pri,s) /*nothing*/
  838. #else
  839. #define Syslog(pri,s) \
  840.     Fwrite(logfile,strlen(s)+17,"SERIAL LINE 1: " s "\r\n")
  841. #endif
  842.  
  843. void Underrun_Error (void)
  844. {
  845.     /*
  846.      * This is hardly an error, it just means that characters
  847.      * are not transmitted at full speed. So we do nothing.
  848.      */
  849. }
  850.  
  851. void Overrun_Error (void)
  852. {
  853.     /*
  854.      * Reception error: a character received hasn't been treated fast
  855.      * enough, and has been overwritten by the next one.
  856.      * A serious error, but it doesn't deserve a signal.
  857.      */
  858.     Syslog(LOG_ERROR, "Overrun error");
  859. }
  860.  
  861. void Parity_Error (void)
  862. {
  863.     /*
  864.      * A character with bad parity has been received. Probably
  865.      * a bad modem configuration.
  866.      */
  867.     Syslog(LOG_ERROR, "Parity error");
  868. }
  869.  
  870. void Frame_Error (void)
  871. {
  872.     /*
  873.      * A non-null character with a null stop-bit has been
  874.      * received. Probably a bad modem configuration.
  875.      */
  876.     Syslog(LOG_ERROR, "Frame error");
  877. }
  878.  
  879. void Break_Detected (void)
  880. {
  881.     /*
  882.      * Not an error, rather a "signal" sent by the remote
  883.      * user (a null character with a null stop-bit). It should
  884.      * flush the buffers and raise the SIGINT signal.
  885.      */
  886.     Syslog(LOG_NOTICE, "Break condition detected");
  887.     Raise_And_Flush(SIGINT);
  888. }
  889.  
  890. void Buffer_Overflow (void)
  891. {
  892.     /*
  893.      * The reception buffer has wrapped around; this is a serious
  894.      * error, and should not happen if you use RTS/CTS.
  895.      * Maybe we should flush the rx buffer, since the data in it
  896.      * is likely to be corrupted.
  897.      */
  898.     Syslog(LOG_ERROR, "Buffer overflow");
  899. }
  900.  
  901. /*
  902.  * This routine is called periodically (every 200 ms) by the daemon process.
  903.  * It should check for hardware errors (and report them, maybe even raise
  904.  * a signal), pending interrupt characters in the buffer (^C, ^S, etc.),
  905.  * wake up selecting processes if necessary.
  906.  *
  907.  * According to the flags in daemon_state, it should also be able to wait
  908.  * for a connection to terminate, and to hang-up.
  909.  */
  910.  
  911. void check_signals (void)
  912. {
  913.     char c, *p, *end;
  914.     int sig;
  915.  
  916.     /* Check for hardware errors */
  917.     check_errors();
  918.  
  919.     /* Check for connection changes */
  920.     if (semaphore != SEMA_CUA) {
  921.         if (!CONNECTED() && REALLY_CONNECTED()) {
  922.             Syslog(LOG_NOTICE, "Communication established");
  923.             ++connection_id;
  924.         } else if (CONNECTED() && !REALLY_CONNECTED()) {
  925.             Syslog(LOG_NOTICE, "Communication lost");
  926.             ++connection_id;
  927.         }
  928.     }
  929.     if (!CONNECTED() && p_curr_tty == &mdm_tty) {
  930.         semaphore = 0;
  931.         if (mdm_tty.pgrp) {
  932.             /* Kill the controlled process group */
  933.             Raise_And_Flush(SIGHUP);
  934.         } else {
  935.             /* If we can't send signal now, never try again */
  936.             p_curr_tty = NULL;
  937.         }
  938.     }
  939.     /* Is this a controlling terminal in cooked mode? */
  940.     if (rx_buf_used && p_curr_tty && p_curr_tty->pgrp &&
  941.        (p_curr_tty->state & TS_COOKED)) {
  942.         /* Now check the rx buffer for control characters */
  943.         p = rx_buf_tail;
  944.         end = (char *)rx_buf_head;    /* to make GCC happy */
  945.         while (p < end) {
  946.             c = *p;
  947.             sig = 0;
  948.             if (c == p_curr_tty->tc.t_intrc)
  949.                 sig = SIGINT;
  950.             else if (c == p_curr_tty->tc.t_quitc)
  951.                 sig = SIGQUIT;
  952.             else if (c == p_curr_tty->ltc.t_suspc)
  953.                 sig = SIGTSTP;
  954.             else if (c == p_curr_tty->tc.t_startc)
  955.                 p_curr_tty->state &= ~TS_HOLD;
  956.             else if (c == p_curr_tty->tc.t_stopc)
  957.                 p_curr_tty->state |= TS_HOLD;
  958.         
  959.             if (sig) {
  960.                 Raise_And_Flush(sig);
  961.                 break;
  962.             }
  963.             if (++p == rx_buf_end)
  964.                 p = rx_buf_start;
  965.         }
  966.     }
  967.     /* Update the hardware transmission flag */
  968.     dont_emit = (p_curr_tty ? !!(p_curr_tty->state & TS_HOLD) : 0);
  969.  
  970.     /* Wake up the selecting processes, if any */
  971.     if (tx_sel && bytes_available(tx_fsel,FIONWRITE))
  972.         { WAKESELECT(tx_sel); tx_sel = 0L; }
  973.     if (rx_sel && bytes_available(rx_fsel,FIONREAD))
  974.         { WAKESELECT(rx_sel); rx_sel = 0L; }
  975.         
  976.     /* Have we got something to do? */
  977.     if (daemon_state & CLOSING_CONN) {
  978.         /* Wait for all bytes to be transmitted */
  979.         dont_emit = 0;    /* paranoia */
  980.         if (tx_buf_used == 0) {
  981.             daemon_state &= ~CLOSING_CONN;
  982.             flush_rx_buffer();
  983.             break_condition(0);
  984.         }
  985.     }
  986.     else if (daemon_state & HANGING_UP) {
  987.         /* Hangup for 1.2 second */
  988.         dtr_off();
  989.         (void)Fselect(1200,0L,0L,0L);
  990.         dtr_on();
  991.         flush_rx_buffer();
  992.         flush_tx_buffer();
  993.         break_condition(0);
  994.         daemon_state &= ~HANGING_UP;
  995.     }
  996.     /* Pooh, that was a lot of job! */        
  997. }
  998.  
  999. /*
  1000.  * Installs all the interrupt routines, and initializes the RS232 port.
  1001.  */
  1002.  
  1003. void install_things (void)
  1004. {
  1005.     int sr;
  1006.     long old_ssp;
  1007.     
  1008.     old_ssp = Super(0L);
  1009.     sr = spl7();
  1010.  
  1011.     /* First, initialize the buffers */
  1012.     rx_buf_head = rx_buf_tail = rx_buf_start;
  1013.     tx_buf_tail = tx_buf_head = tx_buf_start;
  1014.     rx_buf_end = rx_buf_start + RX_BUF_LENGTH;
  1015.     tx_buf_end = tx_buf_start + TX_BUF_LENGTH;
  1016.     rx_lowmark  = RX_BUF_LENGTH * 1/8;
  1017.     rx_highmark = RX_BUF_LENGTH * 7/8; 
  1018.  
  1019.     /* Then, install the interrupt routines */
  1020.     Jdisint(2);                /* MFP interrupt #2 disabled */
  1021.     old_evt_timer = (long) Setexc(0x100, carrier_monitor);
  1022.     old_txerror = (long)Setexc(0x49, txerror);  /* MFP interrupt #9  */
  1023.     old_txrint  = (long)Setexc(0x4A, txrint);   /* MFP interrupt #10 */
  1024.     old_rxerror = (long)Setexc(0x4B, rxerror);  /* MFP interrupt #11 */
  1025.     old_rcvint  = (long)Setexc(0x4C, rcvint);   /* MFP interrupt #12 */
  1026.  
  1027.     /* Initialize the serial port */
  1028.     set_baudrate(INITIAL_SPEED);
  1029.     set_bits(INITIAL_FLAGS & (TF_CHARBITS|TF_STOPBITS));
  1030.     set_parity(INITIAL_FLAGS & (T_ODDP|T_EVENP));
  1031.     rts_cts = !!(INITIAL_FLAGS & T_RTSCTS);
  1032.     soft_carrier = INITIAL_SOFTCAR;
  1033.     hangup_control = INITIAL_HUPCL;
  1034.     break_condition(0);
  1035.  
  1036.     spl(sr);
  1037.     Super(old_ssp);
  1038. }
  1039.  
  1040. /* Copyright information */
  1041.  
  1042. void Version (void)
  1043. {
  1044.     Cconws("Serial port 1 device driver (version " MDM_VERSION 
  1045.     ", compiled " __DATE__ ") by T.Bousch\r\n"
  1046.     "This program is FREE SOFTWARE, and comes with NO WARRANTY.\r\n"
  1047.     "Read the file COPYING for more information.\r\n"
  1048. #ifdef USE_SYSLOG
  1049.     "SYSLOG version\r\n"
  1050. #endif
  1051.     );
  1052. }
  1053.  
  1054. /*
  1055.  * The daemon process; it executes in non-kernel mode, so it cannot use
  1056.  * kernel functions (apart from WAKESELECT).
  1057.  */
  1058.  
  1059. void modm0_daemon (long bp)
  1060. {
  1061.     setstack(bp + D_STACK - 16L);    /* abandon 16 bytes for security */
  1062.     Psigblock(0x7fffffffUL);      /* mask all maskable signals */
  1063. #if !defined(USE_SYSLOG) && !defined(NDEBUG)
  1064.     logfile = Fopen(LOGFILE, O_WRONLY);
  1065. #endif
  1066.     Super(0L);
  1067.  
  1068.     while(1) {
  1069.         (void)Fselect(200, 0L, 0L, 0L);  /* sleep 200 ms */
  1070.         check_signals();
  1071.     }
  1072. }
  1073.  
  1074. /*
  1075.  * Main routine. It would be nice if we could pass arguments on the command
  1076.  * line, but it's not possible if we include <minimal.h>, and we don't want
  1077.  * to add ~15kb to the code only for that. If you don't like the default
  1078.  * settings, change them in modm0dev.h and recompile. Or use stty.
  1079.  */
  1080.  
  1081. int main()
  1082. {
  1083.     BASEPAGE *b;
  1084.  
  1085.     if (Syield() == EINVFN) {
  1086.         Cconws("modm0dev: MiNT is not running\r\n");
  1087.         return EACCDN;
  1088.     }
  1089.     if (!Fsfirst(MDM_DEVNAME,0) || !Fsfirst(CUA_DEVNAME,0)) {
  1090.         Cconws("modm0dev: device already installed\r\n");
  1091.         return EACCDN;
  1092.     }
  1093.     /*
  1094.      * The daemon won't work properly if not super-user
  1095.      */
  1096.     if (Pgeteuid()) {
  1097.         Cconws("modm0dev: must be root to execute this program\r\n");
  1098.         return EACCDN;
  1099.     }
  1100.     kernel = (struct kerinfo *)
  1101.         Dcntl(DEV_INSTALL, MDM_DEVNAME, &mdm_devinfo);
  1102.     if ((long)kernel <= 0L) {
  1103.         Cconws("modm0dev: can't install " MDM_DEVNAME "\r\n");
  1104.         return EACCDN;
  1105.     }
  1106.     if (Dcntl(DEV_INSTALL, CUA_DEVNAME, &cua_devinfo) <= 0L) {
  1107.         Cconws("modm0dev: can't install " CUA_DEVNAME "\r\n");
  1108.         Fdelete(MDM_DEVNAME);
  1109.         return EACCDN;
  1110.     }
  1111.     /*
  1112.      * Install the daemon (just like in Stephen Henson's Minixfs).
  1113.      * If something goes wrong, it is probably an out of memory
  1114.      * error.
  1115.      */
  1116.     b = (BASEPAGE *) Pexec(5, 0L, "", 0L);
  1117.     if ((long)b <= 0L)
  1118.         goto no_memory;
  1119.     Mshrink(b, D_STACK);
  1120.     b->p_tbase = (char *)modm0_daemon;    /* start address */
  1121.     b->p_hitpa = (char *)b + D_STACK;    /* top of block  */
  1122.     if (Pexec(104, D_NAME, b, 0L) < 0L)
  1123.         goto no_memory;
  1124.     Version();
  1125.     install_things();
  1126. #if 1
  1127.     /* There's no need to keep the program resident, since all its
  1128.      * memory is also owned by the daemon. So we simply exit. */
  1129.     return 0;
  1130. #else
  1131.     Ptermres(256L + _base->p_tlen + _base->p_dlen + _base->p_blen, 0);
  1132. #endif
  1133.     /* Fall through if Ptermres() failed */
  1134. no_memory:
  1135.     Cconws("modm0dev: running out of memory\r\n");
  1136.     Fdelete(MDM_DEVNAME); 
  1137.     Fdelete(CUA_DEVNAME);
  1138.     return ENSMEM;
  1139. }
  1140.