root/drivers/char/pcxx.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. memwinon
  2. memwinoff
  3. globalwinon
  4. rxwinon
  5. txwinon
  6. memoff
  7. assertgwinon
  8. assertmemoff
  9. pcxe_sched_event
  10. pcxx_error
  11. pcxx_waitcarrier
  12. pcxe_open
  13. shutdown
  14. pcxe_close
  15. pcxe_hangup
  16. pcxe_write
  17. pcxe_put_char
  18. pcxe_write_room
  19. pcxe_chars_in_buffer
  20. pcxe_flush_buffer
  21. pcxe_flush_chars
  22. pcxe_init
  23. pcxxpoll
  24. doevent
  25. pcxxdelay
  26. fepcmd
  27. termios2digi_c
  28. termios2digi_i
  29. termios2digi_h
  30. pcxxparam
  31. receive_data
  32. pcxe_ioctl
  33. pcxe_set_termios
  34. do_pcxe_bh
  35. do_softint
  36. pcxe_stop
  37. pcxe_throttle
  38. pcxe_unthrottle
  39. pcxe_start
  40. digi_send_break
  41. setup_empty_event

   1 /*
   2  *  linux/drivers/char/pcxe.c
   3  * 
   4  *  Written by Troy De Jongh, November, 1994
   5  *
   6  *  Copyright (C) 1994,1995 Troy De Jongh
   7  *  This software may be used and distributed according to the terms 
   8  *  of the GNU Public License.
   9  *
  10  *  This driver is for the DigiBoard PC/Xe and PC/Xi line of products.
  11  *
  12  *  This driver does NOT support DigiBoard's fastcook FEP option and
  13  *  does not support the transparent print (i.e. digiprint) option.
  14  *
  15  * This Driver is currently maintained by Christoph Lameter (clameter@fuller.edu)
  16  * Please contact the mailing list for problems first. 
  17  *
  18  * Sources of Information:
  19  * 1. The Linux Digiboard Page at http://private.fuller.edu/clameter/digi.html
  20  * 2. The Linux Digiboard Mailing list at digiboard@list.fuller.edu
  21  *    (Simply write a message to introduce yourself to subscribe)
  22  *
  23  *  1.5.2 Fall 1995 Bug fixes by David Nugent
  24  *  1.5.3 March 9, 1996 Christoph Lameter: Fixed 115.2K Support. Memory
  25  *              allocation harmonized with 1.3.X Series.
  26  *  1.5.4 March 30, 1996 Christoph Lameter: Fixup for 1.3.81. Use init_bh
  27  *              instead of direct assigment to kernel arrays.
  28  *
  29  */
  30 
  31 #undef SPEED_HACK
  32 /* If you define SPEED_HACK then you get the following Baudrate translation
  33    19200 = 57600
  34    38400 = 115K
  35    The driver supports the native 57.6K and 115K Baudrates under Linux, but
  36    some distributions like Slackware 3.0 dont like these high baudrates.
  37 */
  38 
  39 #include <linux/mm.h>
  40 #include <linux/ioport.h>
  41 #include <linux/errno.h>
  42 #include <linux/signal.h>
  43 #include <linux/sched.h>
  44 #include <linux/timer.h>
  45 #include <linux/interrupt.h>
  46 #include <linux/tty.h>
  47 #include <linux/tty_flip.h>
  48 #include <linux/major.h>
  49 #include <linux/string.h>
  50 #include <linux/fcntl.h>
  51 #include <linux/ptrace.h>
  52 #include <linux/major.h>
  53 #include <linux/delay.h>
  54 #include <linux/serial.h>
  55 #include <linux/tty_driver.h>
  56 #include <linux/malloc.h>
  57 
  58 #include <asm/system.h>
  59 #include <asm/io.h>
  60 #include <asm/segment.h>
  61 #include <asm/bitops.h>
  62 
  63 #define VERSION         "1.5.4"
  64 /*#define       DEFAULT_HW_FLOW 1 */
  65 /*#define       DEBUG_IOCTL */
  66 
  67 #include "digi.h"
  68 #include "fep.h"
  69 #include "pcxx.h"
  70 #include "digi_fep.h"
  71 #include "digi_bios.h"
  72 #include "pcxxconfig.h"
  73 
  74 #define DIGIMAJOR   30
  75 #define DIGICUMAJOR 31 
  76 
  77 #define MAXPORTS 16     /* Max ports per PC/Xx type board */
  78 #define NBDEVS   (NUMCARDS * MAXPORTS)
  79 #define PORTNUM(x)  ((x)->dev % MAXPORTS)
  80 #define LINENUM(x)  (MINOR((x)->device) - (x)->driver.minor_start)
  81 
  82 int pcxx_ncook=sizeof(pcxx_cook);
  83 int pcxx_nbios=sizeof(pcxx_bios);
  84 
  85 struct channel digi_channels[NBDEVS];
  86 
  87 #define MIN(a,b)        ((a) < (b) ? (a) : (b))
  88 #define pcxxassert(x, msg)  if(!(x)) pcxx_error(__LINE__, msg)
  89 
  90 #define FEPTIMEOUT 200000  
  91 #define SERIAL_TYPE_NORMAL      1
  92 #define SERIAL_TYPE_CALLOUT     2
  93 #define PCXE_EVENT_HANGUP   1
  94 
  95 struct tty_driver pcxe_driver;
  96 struct tty_driver pcxe_callout;
  97 static int pcxe_refcount;
  98 
  99 DECLARE_TASK_QUEUE(tq_pcxx);
 100 static struct tty_struct *pcxe_table[NBDEVS];
 101 static struct termios *pcxe_termios[NBDEVS];
 102 static struct termios *pcxe_termios_locked[NBDEVS];
 103 
 104 static void pcxxpoll(void);
 105 static void pcxxdelay(int);
 106 static void fepcmd(struct channel *, int, int, int, int, int);
 107 static void pcxe_put_char(struct tty_struct *, unsigned char);
 108 static void pcxe_flush_chars(struct tty_struct *);
 109 static void pcxx_error(int, char *);
 110 static void pcxe_close(struct tty_struct *, struct file *);
 111 static int pcxe_ioctl(struct tty_struct *, struct file *, unsigned int, unsigned long);
 112 static void pcxe_set_termios(struct tty_struct *, struct termios *);
 113 static int pcxe_write(struct tty_struct *, int, const unsigned char *, int);
 114 static int pcxe_write_room(struct tty_struct *);
 115 static int pcxe_chars_in_buffer(struct tty_struct *);
 116 static void pcxe_flush_buffer(struct tty_struct *);
 117 static void doevent(int);
 118 static void receive_data(struct channel *);
 119 static void pcxxparam(struct tty_struct *, struct channel *ch);
 120 static void do_softint(void *);
 121 static inline void pcxe_sched_event(struct channel *, int);
 122 static void do_pcxe_bh(void);
 123 static void pcxe_start(struct tty_struct *);
 124 static void pcxe_stop(struct tty_struct *);
 125 static void pcxe_throttle(struct tty_struct *);
 126 static void pcxe_unthrottle(struct tty_struct *);
 127 static void digi_send_break(struct channel *ch, int msec);
 128 static void shutdown(struct channel *);
 129 static void setup_empty_event(struct tty_struct *tty, struct channel *ch);
 130 static inline void memwinon(struct board_info *b, unsigned int win);
 131 static inline void memwinoff(struct board_info *b, unsigned int win);
 132 static inline void globalwinon(struct channel *ch);
 133 static inline void rxwinon(struct channel *ch);
 134 static inline void txwinon(struct channel *ch);
 135 static inline void memoff(struct channel *ch);
 136 static inline void assertgwinon(struct channel *ch);
 137 static inline void assertmemoff(struct channel *ch);
 138 
 139 #define TZ_BUFSZ 4096
 140 
 141 static inline struct channel *chan(register struct tty_struct *tty)
 142 {
 143         if (tty) {
 144                 register struct channel *ch=(struct channel *)tty->driver_data;
 145                 if ((ch >= &digi_channels[0]) && (ch < &digi_channels[NBDEVS])) {
 146                         if (ch->magic==PCXX_MAGIC)
 147                                 return ch;
 148                 }
 149         }
 150         return NULL;
 151 }
 152 
 153 /* These inline routines are to turn board memory on and off */
 154 static inline void memwinon(struct board_info *b, unsigned int win)
     /* [previous][next][first][last][top][bottom][index][help] */
 155 {
 156         if(b->type == PCXEVE)
 157                 outb_p(FEPWIN|win, b->port+1);
 158         else
 159                 outb_p(inb(b->port)|FEPMEM, b->port);
 160 }
 161 
 162 static inline void memwinoff(struct board_info *b, unsigned int win)
     /* [previous][next][first][last][top][bottom][index][help] */
 163 {
 164         outb_p(inb(b->port)&~FEPMEM, b->port);
 165         if(b->type == PCXEVE)
 166                 outb_p(0, b->port + 1);
 167 }
 168 
 169 static inline void globalwinon(struct channel *ch)
     /* [previous][next][first][last][top][bottom][index][help] */
 170 {
 171         if(ch->board->type == PCXEVE)
 172                 outb_p(FEPWIN, ch->board->port+1);
 173         else
 174                 outb_p(FEPMEM, ch->board->port);
 175 }
 176 
 177 static inline void rxwinon(struct channel *ch)
     /* [previous][next][first][last][top][bottom][index][help] */
 178 {
 179         if(ch->rxwin == 0)
 180                 outb_p(FEPMEM, ch->board->port);
 181         else 
 182                 outb_p(ch->rxwin, ch->board->port+1);
 183 }
 184 
 185 static inline void txwinon(struct channel *ch)
     /* [previous][next][first][last][top][bottom][index][help] */
 186 {
 187         if(ch->txwin == 0)
 188                 outb_p(FEPMEM, ch->board->port);
 189         else
 190                 outb_p(ch->txwin, ch->board->port+1);
 191 }
 192 
 193 static inline void memoff(struct channel *ch)
     /* [previous][next][first][last][top][bottom][index][help] */
 194 {
 195         outb_p(0, ch->board->port);
 196         if(ch->board->type == PCXEVE)
 197                 outb_p(0, ch->board->port+1);
 198 }
 199 
 200 static inline void assertgwinon(struct channel *ch)
     /* [previous][next][first][last][top][bottom][index][help] */
 201 {
 202         if(ch->board->type != PCXEVE)
 203                 pcxxassert(inb(ch->board->port) & FEPMEM, "Global memory off");
 204 }
 205 
 206 static inline void assertmemoff(struct channel *ch)
     /* [previous][next][first][last][top][bottom][index][help] */
 207 {
 208         if(ch->board->type != PCXEVE)
 209                 pcxxassert(!(inb(ch->board->port) & FEPMEM), "Memory on");
 210 }
 211 
 212 static inline void pcxe_sched_event(struct channel *info, int event)
     /* [previous][next][first][last][top][bottom][index][help] */
 213 {
 214         info->event |= 1 << event;
 215         queue_task_irq_off(&info->tqueue, &tq_pcxx);
 216         mark_bh(DIGI_BH);
 217 }
 218 
 219 static void pcxx_error(int line, char *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 220 {
 221         printk("pcxx_error (DigiBoard): line=%d %s\n", line, msg);
 222 }
 223 
 224 static int pcxx_waitcarrier(struct tty_struct *tty,struct file *filp,struct channel *info)
     /* [previous][next][first][last][top][bottom][index][help] */
 225 {
 226         struct wait_queue wait = { current, NULL };
 227         int     retval = 0;
 228         int     do_clocal = 0;
 229 
 230         if (info->asyncflags & ASYNC_CALLOUT_ACTIVE) {
 231                 if (info->normal_termios.c_cflag & CLOCAL)
 232                         do_clocal = 1;
 233         } else {
 234                 if (tty->termios->c_cflag & CLOCAL)
 235                         do_clocal = 1;
 236         }
 237 
 238         /*
 239          * Block waiting for the carrier detect and the line to become free
 240          */
 241 
 242         retval = 0;
 243         add_wait_queue(&info->open_wait, &wait);
 244         info->count--;
 245         info->blocked_open++;
 246 
 247         for (;;) {
 248                 cli();
 249                 if ((info->asyncflags & ASYNC_CALLOUT_ACTIVE) == 0) {
 250                         globalwinon(info);
 251                         info->omodem |= DTR|RTS;
 252                         fepcmd(info, SETMODEM, DTR|RTS, 0, 10, 1);
 253                         memoff(info);
 254                 }
 255                 sti();
 256                 current->state = TASK_INTERRUPTIBLE;
 257                 if(tty_hung_up_p(filp) || (info->asyncflags & ASYNC_INITIALIZED) == 0) {
 258                         if(info->asyncflags & ASYNC_HUP_NOTIFY)
 259                                 retval = -EAGAIN;
 260                         else
 261                                 retval = -ERESTARTSYS;  
 262                         break;
 263                 }
 264                 if ((info->asyncflags & ASYNC_CALLOUT_ACTIVE) == 0 &&
 265                     (info->asyncflags & ASYNC_CLOSING) == 0 &&
 266                         (do_clocal || (info->imodem & info->dcd)))
 267                         break;
 268                 if(current->signal & ~current->blocked) {
 269                         retval = -ERESTARTSYS;
 270                         break;
 271                 }
 272                 schedule();
 273         }
 274         current->state = TASK_RUNNING;
 275         remove_wait_queue(&info->open_wait, &wait);
 276 
 277         if(!tty_hung_up_p(filp))
 278                 info->count++;
 279         info->blocked_open--;
 280 
 281         return retval;
 282 }       
 283 
 284 
 285 /* static  ???why static??? */
 286 int pcxe_open(struct tty_struct *tty, struct file * filp)
     /* [previous][next][first][last][top][bottom][index][help] */
 287 {
 288         volatile struct board_chan *bc;
 289         struct channel *ch;
 290         unsigned long flags;
 291         int line;
 292         int boardnum;
 293         int retval;
 294 
 295         line = MINOR(tty->device) - tty->driver.minor_start;
 296         if(line < 0 || line >= NBDEVS) {
 297                 printk("line out of range in pcxe_open\n");
 298                 tty->driver_data = NULL;
 299                 return(-ENODEV);
 300         }
 301 
 302         boardnum = line / 16;
 303         if(boardnum >= NUMCARDS || boards[boardnum].status == DISABLED ||
 304                                                                 (line % MAXPORTS) >= boards[boardnum].numports) {
 305                 tty->driver_data = NULL;   /* Mark this device as 'down' */
 306                 return(-ENODEV);
 307         }
 308         
 309         ch = &digi_channels[line];
 310 
 311         if(ch->brdchan == 0) {
 312                 tty->driver_data = NULL;
 313                 return(-ENODEV);
 314         }
 315 
 316         /*
 317          * If the device is in the middle of being closed, then block
 318          * until it's done, and then try again.
 319          */
 320         if(ch->asyncflags & ASYNC_CLOSING) {
 321                 interruptible_sleep_on(&ch->close_wait);
 322                 if(ch->asyncflags & ASYNC_HUP_NOTIFY)
 323                         return -EAGAIN;
 324                 else
 325                         return -ERESTARTSYS;
 326         }
 327 
 328         save_flags(flags);
 329         cli();
 330         ch->count++;
 331         tty->driver_data = ch;
 332         ch->tty = tty;
 333 
 334         if ((ch->asyncflags & ASYNC_INITIALIZED) == 0) {
 335                 unsigned int head;
 336 
 337                 globalwinon(ch);
 338                 ch->statusflags = 0;
 339                 bc=ch->brdchan;
 340                 ch->imodem = bc->mstat;
 341                 head = bc->rin;
 342                 bc->rout = head;
 343                 ch->tty = tty;
 344                 pcxxparam(tty,ch);
 345                 ch->imodem = bc->mstat;
 346                 bc->idata = 1;
 347                 ch->omodem = DTR|RTS;
 348                 fepcmd(ch, SETMODEM, DTR|RTS, 0, 10, 1);
 349                 memoff(ch);
 350                 ch->asyncflags |= ASYNC_INITIALIZED;
 351         }
 352         restore_flags(flags);
 353 
 354         if(ch->asyncflags & ASYNC_CLOSING) {
 355                 interruptible_sleep_on(&ch->close_wait);
 356                 if(ch->asyncflags & ASYNC_HUP_NOTIFY)
 357                         return -EAGAIN;
 358                 else
 359                         return -ERESTARTSYS;
 360         }
 361         /*
 362          * If this is a callout device, then just make sure the normal
 363          * device isn't being used.
 364          */
 365         if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
 366                 if (ch->asyncflags & ASYNC_NORMAL_ACTIVE)
 367                         return -EBUSY;
 368                 if (ch->asyncflags & ASYNC_CALLOUT_ACTIVE) {
 369                         if ((ch->asyncflags & ASYNC_SESSION_LOCKOUT) &&
 370                                 (ch->session != current->session))
 371                             return -EBUSY;
 372                         if((ch->asyncflags & ASYNC_PGRP_LOCKOUT) &&
 373                             (ch->pgrp != current->pgrp))
 374                             return -EBUSY;
 375                 }
 376                 ch->asyncflags |= ASYNC_CALLOUT_ACTIVE;
 377         }
 378         else {
 379                 if (filp->f_flags & O_NONBLOCK) {
 380                         if(ch->asyncflags & ASYNC_CALLOUT_ACTIVE)
 381                                 return -EBUSY;
 382                 }
 383                 else {
 384                         if ((retval = pcxx_waitcarrier(tty, filp, ch)) != 0)
 385                                 return retval;
 386                 }
 387                 ch->asyncflags |= ASYNC_NORMAL_ACTIVE;
 388         }
 389         
 390         save_flags(flags);
 391         cli();
 392         if((ch->count == 1) && (ch->asyncflags & ASYNC_SPLIT_TERMIOS)) {
 393                 if(tty->driver.subtype == SERIAL_TYPE_NORMAL)
 394                         *tty->termios = ch->normal_termios;
 395                 else 
 396                         *tty->termios = ch->callout_termios;
 397                 globalwinon(ch);
 398                 pcxxparam(tty,ch);
 399                 memoff(ch);
 400         }
 401 
 402         ch->session = current->session;
 403         ch->pgrp = current->pgrp;
 404         restore_flags(flags);
 405         return 0;
 406 } 
 407 
 408 static void shutdown(struct channel *info)
     /* [previous][next][first][last][top][bottom][index][help] */
 409 {
 410         unsigned long flags;
 411         volatile struct board_chan *bc;
 412         struct tty_struct *tty;
 413 
 414         if (!(info->asyncflags & ASYNC_INITIALIZED)) 
 415                 return;
 416 
 417         save_flags(flags);
 418         cli();
 419         globalwinon(info);
 420 
 421         bc = info->brdchan;
 422         if(bc)
 423                 bc->idata = 0;
 424 
 425         tty = info->tty;
 426 
 427         /*
 428          * If we're a modem control device and HUPCL is on, drop RTS & DTR.
 429          */
 430         if(tty->termios->c_cflag & HUPCL) {
 431                 info->omodem &= ~(RTS|DTR);
 432                 fepcmd(info, SETMODEM, 0, DTR|RTS, 10, 1);
 433         }
 434 
 435         memoff(info);
 436         info->asyncflags &= ~ASYNC_INITIALIZED;
 437         restore_flags(flags);
 438 }
 439 
 440 
 441 static void pcxe_close(struct tty_struct * tty, struct file * filp)
     /* [previous][next][first][last][top][bottom][index][help] */
 442 {
 443         struct channel *info;
 444 
 445         if ((info=chan(tty))!=NULL) {
 446                 unsigned long flags;
 447                 save_flags(flags);
 448                 cli();
 449 
 450                 if(tty_hung_up_p(filp)) {
 451                         restore_flags(flags);
 452                         return;
 453                 }
 454                 if (info->count-- > 1) {
 455                         restore_flags(flags);
 456                         return;
 457                 }
 458                 if (info->count < 0) {
 459                         info->count = 0;
 460                 }
 461 
 462                 info->asyncflags |= ASYNC_CLOSING;
 463         
 464                 /*
 465                 * Save the termios structure, since this port may have
 466                 * separate termios for callout and dialin.
 467                 */
 468                 if(info->asyncflags & ASYNC_NORMAL_ACTIVE)
 469                         info->normal_termios = *tty->termios;
 470                 if(info->asyncflags & ASYNC_CALLOUT_ACTIVE)
 471                         info->callout_termios = *tty->termios;
 472                 tty->closing = 1;
 473                 if(info->asyncflags & ASYNC_INITIALIZED) {
 474                         setup_empty_event(tty,info);            
 475                         tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */
 476                 }
 477         
 478                 if(tty->driver.flush_buffer)
 479                         tty->driver.flush_buffer(tty);
 480                 if(tty->ldisc.flush_buffer)
 481                         tty->ldisc.flush_buffer(tty);
 482                 shutdown(info);
 483                 tty->closing = 0;
 484                 info->event = 0;
 485                 info->tty = NULL;
 486                 if(tty->ldisc.num != ldiscs[N_TTY].num) {
 487                         if(tty->ldisc.close)
 488                                 (tty->ldisc.close)(tty);
 489                         tty->ldisc = ldiscs[N_TTY];
 490                         tty->termios->c_line = N_TTY;
 491                         if(tty->ldisc.open)
 492                                 (tty->ldisc.open)(tty);
 493                 }
 494                 if(info->blocked_open) {
 495                         if(info->close_delay) {
 496                                 current->state = TASK_INTERRUPTIBLE;
 497                                 current->timeout = jiffies + info->close_delay;
 498                                 schedule();
 499                         }
 500                         wake_up_interruptible(&info->open_wait);
 501                 }
 502                 info->asyncflags &= ~(ASYNC_NORMAL_ACTIVE|
 503                                                           ASYNC_CALLOUT_ACTIVE|ASYNC_CLOSING);
 504                 wake_up_interruptible(&info->close_wait);
 505                 restore_flags(flags);
 506         }
 507 }
 508 
 509 
 510 void pcxe_hangup(struct tty_struct *tty)
     /* [previous][next][first][last][top][bottom][index][help] */
 511 {
 512         struct channel *ch;
 513 
 514         if ((ch=chan(tty))!=NULL) {
 515                 unsigned long flags;
 516 
 517                 save_flags(flags);
 518                 cli();
 519                 shutdown(ch);
 520                 ch->event = 0;
 521                 ch->count = 0;
 522                 ch->tty = NULL;
 523                 ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
 524                 wake_up_interruptible(&ch->open_wait);
 525                 restore_flags(flags);
 526         }
 527 }
 528 
 529 
 530 
 531 static int pcxe_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
     /* [previous][next][first][last][top][bottom][index][help] */
 532 {
 533         struct channel *ch;
 534         volatile struct board_chan *bc;
 535         int total, remain, size, stlen;
 536         unsigned int head, tail;
 537         unsigned long flags;
 538 
 539         /* printk("Entering pcxe_write()\n"); */
 540 
 541         if ((ch=chan(tty))==NULL)
 542                 return 0;
 543 
 544         bc = ch->brdchan;
 545         size = ch->txbufsize;
 546 
 547         if (from_user) {
 548 
 549                 save_flags(flags);
 550                 cli();
 551                 globalwinon(ch);
 552                 head = bc->tin & (size - 1);
 553                 tail = bc->tout;
 554                 if (tail != bc->tout)
 555                         tail = bc->tout;
 556                 tail &= (size - 1);
 557                 stlen = (head >= tail) ? (size - (head - tail) - 1) : (tail - head - 1);
 558                 count = MIN(stlen, count);
 559                 if (count) {
 560                         if (verify_area(VERIFY_READ, (char*)buf, count))
 561                                 count=0;
 562                         else memcpy_fromfs(ch->tmp_buf, buf, count);
 563                 }
 564                 buf = ch->tmp_buf;
 565                 memoff(ch);
 566                 restore_flags(flags);
 567         }
 568 
 569         /*
 570          * All data is now local
 571          */
 572 
 573         total = 0;
 574         save_flags(flags);
 575         cli();
 576         globalwinon(ch);
 577         head = bc->tin & (size - 1);
 578         tail = bc->tout;
 579         if (tail != bc->tout)
 580                 tail = bc->tout;
 581         tail &= (size - 1);
 582         if (head >= tail) {
 583                 remain = size - (head - tail) - 1;
 584                 stlen = size - head;
 585         }
 586         else {
 587                 remain = tail - head - 1;
 588                 stlen = remain;
 589         }
 590         count = MIN(remain, count);
 591 
 592         txwinon(ch);
 593         while (count > 0) {
 594                 stlen = MIN(count, stlen);
 595                 memcpy(ch->txptr + head, buf, stlen);
 596                 buf += stlen;
 597                 count -= stlen;
 598                 total += stlen;
 599                 head += stlen;
 600                 if (head >= size) {
 601                         head = 0;
 602                         stlen = tail;
 603                 }
 604         }
 605         ch->statusflags |= TXBUSY;
 606         globalwinon(ch);
 607         bc->tin = head;
 608         if ((ch->statusflags & LOWWAIT) == 0) {
 609                 ch->statusflags |= LOWWAIT;
 610                 bc->ilow = 1;
 611         }
 612         memoff(ch);
 613         restore_flags(flags);
 614 
 615         return(total);
 616 }
 617 
 618 
 619 static void pcxe_put_char(struct tty_struct *tty, unsigned char c)
     /* [previous][next][first][last][top][bottom][index][help] */
 620 {
 621         pcxe_write(tty, 0, &c, 1);
 622         return;
 623 }
 624 
 625 
 626 static int pcxe_write_room(struct tty_struct *tty)
     /* [previous][next][first][last][top][bottom][index][help] */
 627 {
 628         struct channel *ch;
 629         int remain;
 630 
 631         remain = 0;
 632         if ((ch=chan(tty))!=NULL) {
 633                 volatile struct board_chan *bc;
 634                 unsigned int head, tail;
 635                 unsigned long flags;
 636 
 637                 save_flags(flags);
 638                 cli();
 639                 globalwinon(ch);
 640 
 641                 bc = ch->brdchan;
 642                 head = bc->tin & (ch->txbufsize - 1);
 643                 tail = bc->tout;
 644                 if (tail != bc->tout)
 645                         tail = bc->tout;
 646                 tail &= (ch->txbufsize - 1);
 647 
 648                 if((remain = tail - head - 1) < 0 )
 649                         remain += ch->txbufsize;
 650 
 651                 if (remain && (ch->statusflags & LOWWAIT) == 0) {
 652                         ch->statusflags |= LOWWAIT;
 653                         bc->ilow = 1;
 654                 }
 655                 memoff(ch);
 656                 restore_flags(flags);
 657         }
 658 
 659         return remain;
 660 }
 661 
 662 
 663 static int pcxe_chars_in_buffer(struct tty_struct *tty)
     /* [previous][next][first][last][top][bottom][index][help] */
 664 {
 665         int chars;
 666         unsigned int ctail, head, tail;
 667         int remain;
 668         unsigned long flags;
 669         struct channel *ch;
 670         volatile struct board_chan *bc;
 671 
 672         if ((ch=chan(tty))==NULL)
 673                 return(0);
 674 
 675         save_flags(flags);
 676         cli();
 677         globalwinon(ch);
 678 
 679         bc = ch->brdchan;
 680         tail = bc->tout;
 681         head = bc->tin;
 682         ctail = ch->mailbox->cout;
 683         if(tail == head && ch->mailbox->cin == ctail && bc->tbusy == 0)
 684                 chars = 0;
 685         else {
 686                 head = bc->tin & (ch->txbufsize - 1);
 687                 tail &= (ch->txbufsize - 1);
 688                 if((remain = tail - head - 1) < 0 )
 689                         remain += ch->txbufsize;
 690 
 691                 chars = (int)(ch->txbufsize - remain);
 692 
 693                 /* 
 694                  * Make it possible to wakeup anything waiting for output
 695                  * in tty_ioctl.c, etc.
 696                  */
 697                 if(!(ch->statusflags & EMPTYWAIT))
 698                         setup_empty_event(tty,ch);
 699         }
 700 
 701         memoff(ch);
 702         restore_flags(flags);
 703 
 704         return(chars);
 705 }
 706 
 707 
 708 static void pcxe_flush_buffer(struct tty_struct *tty)
     /* [previous][next][first][last][top][bottom][index][help] */
 709 {
 710         unsigned int tail;
 711         volatile struct board_chan *bc;
 712         struct channel *ch;
 713         unsigned long flags;
 714 
 715         if ((ch=chan(tty))==NULL)
 716                 return;
 717 
 718         save_flags(flags);
 719         cli();
 720 
 721         globalwinon(ch);
 722         bc = ch->brdchan;
 723         tail = bc->tout;
 724         fepcmd(ch, STOUT, (unsigned) tail, 0, 0, 0);
 725 
 726         memoff(ch);
 727         restore_flags(flags);
 728 
 729         wake_up_interruptible(&tty->write_wait);
 730         if((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)
 731                 (tty->ldisc.write_wakeup)(tty);
 732 }
 733 
 734 static void pcxe_flush_chars(struct tty_struct *tty)
     /* [previous][next][first][last][top][bottom][index][help] */
 735 {
 736         struct channel * ch;
 737 
 738         if ((ch=chan(tty))!=NULL) {
 739                 unsigned long flags;
 740 
 741                 save_flags(flags);
 742                 cli();
 743                 if ((ch->statusflags & TXBUSY) && !(ch->statusflags & EMPTYWAIT))
 744                         setup_empty_event(tty,ch);
 745                 restore_flags(flags);
 746         }
 747 }
 748 
 749 
 750 int pcxe_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 751 {
 752         ulong flags, save_loops_per_sec, memory_seg=0, memory_size;
 753         int lowwater, i, crd, shrinkmem=0, topwin = 0xff00L, botwin=0x100L;
 754         unchar *fepos, *memaddr, *bios, v;
 755         volatile struct global_data *gd;
 756         struct board_info *bd;
 757         volatile struct board_chan *bc;
 758         struct channel *ch;
 759 
 760         init_bh(DIGI_BH,do_pcxe_bh);
 761         enable_bh(DIGI_BH);
 762 
 763         timer_table[DIGI_TIMER].fn = pcxxpoll;
 764         timer_table[DIGI_TIMER].expires = 0;
 765 
 766         memset(&pcxe_driver, 0, sizeof(struct tty_driver));
 767         pcxe_driver.magic = TTY_DRIVER_MAGIC;
 768         pcxe_driver.name = "ttyd";
 769         pcxe_driver.major = DIGIMAJOR; 
 770         pcxe_driver.minor_start = 0;
 771         pcxe_driver.num = NBDEVS;
 772         pcxe_driver.type = TTY_DRIVER_TYPE_SERIAL;
 773         pcxe_driver.subtype = SERIAL_TYPE_NORMAL;
 774         pcxe_driver.init_termios = tty_std_termios;
 775         pcxe_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL;
 776         pcxe_driver.flags = TTY_DRIVER_REAL_RAW;
 777         pcxe_driver.refcount = &pcxe_refcount;
 778         pcxe_driver.table = pcxe_table;
 779         pcxe_driver.termios = pcxe_termios;
 780         pcxe_driver.termios_locked = pcxe_termios_locked;
 781 
 782         pcxe_driver.open = pcxe_open;
 783         pcxe_driver.close = pcxe_close;
 784         pcxe_driver.write = pcxe_write;
 785         pcxe_driver.put_char = pcxe_put_char;
 786         pcxe_driver.flush_chars = pcxe_flush_chars;
 787         pcxe_driver.write_room = pcxe_write_room;
 788         pcxe_driver.chars_in_buffer = pcxe_chars_in_buffer;
 789         pcxe_driver.flush_buffer = pcxe_flush_buffer;
 790         pcxe_driver.ioctl = pcxe_ioctl;
 791         pcxe_driver.throttle = pcxe_throttle;
 792         pcxe_driver.unthrottle = pcxe_unthrottle;
 793         pcxe_driver.set_termios = pcxe_set_termios;
 794         pcxe_driver.stop = pcxe_stop;
 795         pcxe_driver.start = pcxe_start;
 796         pcxe_driver.hangup = pcxe_hangup;
 797 
 798         pcxe_callout = pcxe_driver;
 799         pcxe_callout.name = "ttyD";
 800         pcxe_callout.major = DIGICUMAJOR;
 801         pcxe_callout.subtype = SERIAL_TYPE_CALLOUT;
 802         pcxe_callout.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL;
 803 
 804         /* 
 805          * loops_per_sec hasn't been set at this point :-(, so fake it out... 
 806          * I set it so that I can use the __delay() function.
 807          */
 808         save_loops_per_sec = loops_per_sec;
 809         loops_per_sec = 13L*500000L;
 810 
 811         save_flags(flags);
 812         cli();
 813 
 814         for(crd=0; crd < NUMCARDS; crd++) {
 815                 bd = &boards[crd];
 816                 outb(FEPRST, bd->port);
 817                 pcxxdelay(1);
 818 
 819                 for(i=0; (inb(bd->port) & FEPMASK) != FEPRST; i++) {
 820                         if(i > 1000) {
 821                                 printk("Board not found at port 0x%x! Check switch settings.\n",
 822                                         bd->port);
 823                                 bd->status = DISABLED;
 824                                 break;
 825                         }
 826                         pcxxdelay(1);
 827                 }
 828                 if(bd->status == DISABLED)
 829                         continue;
 830 
 831                 v = inb(bd->port);
 832 
 833                 if((v & 0x1) == 0x1) {
 834                         if((v & 0x30) == 0) {        /* PC/Xi 64K card */
 835                                 memory_seg = 0xf000;
 836                                 memory_size = 0x10000;
 837                         } 
 838 
 839                         if((v & 0x30) == 0x10) {     /* PC/Xi 128K card */
 840                                 memory_seg = 0xe000;
 841                                 memory_size = 0x20000;
 842                         }
 843                         
 844                         if((v & 0x30) == 0x20) {     /* PC/Xi 256K card */
 845                                 memory_seg = 0xc000;
 846                                 memory_size = 0x40000;
 847                         }
 848 
 849                         if((v & 0x30) == 0x30) {     /* PC/Xi 512K card */
 850                                 memory_seg = 0x8000;
 851                                 memory_size = 0x80000;
 852                         }
 853                         bd->type = PCXI;
 854                 } else {
 855                         if((v & 0x1) == 0x1) {
 856                                 bd->status = DISABLED;   /* PC/Xm unsupported card */
 857                                 printk("PC/Xm at 0x%x not supported!!\n", bd->port);
 858                                 continue;
 859                         } else {
 860                                 if(v & 0xC0) {    
 861                                         topwin = 0x1f00L;
 862                                         outb((((ulong)bd->membase>>8) & 0xe0) | 0x10, bd->port+2);
 863                                         outb(((ulong)bd->membase>>16) & 0xff, bd->port+3);
 864                                         bd->type = PCXEVE; /* PC/Xe 8K card */
 865                                 } else { 
 866                                         bd->type = PCXE;    /* PC/Xe 64K card */
 867                                 }
 868                                         
 869                                 memory_seg = 0xf000;
 870                                 memory_size = 0x10000;
 871                         }
 872                 }
 873 
 874                 memaddr = (unchar *) bd->membase;
 875 
 876                 outb(FEPRST|FEPMEM, bd->port);
 877 
 878                 for(i=0; (inb(bd->port) & FEPMASK) != (FEPRST|FEPMEM); i++) {
 879                         if(i > 10000) {
 880                                 printk("%s not resetting at port 0x%x! Check switch settings.\n",
 881                                         board_desc[bd->type], bd->port);
 882                                 bd->status = DISABLED;
 883                                 break;
 884                         }
 885                         pcxxdelay(1);
 886                 }
 887                 if(bd->status == DISABLED)
 888                         continue;
 889 
 890                 memwinon(bd,0);
 891                 *(ulong *)(memaddr + botwin) = 0xa55a3cc3;
 892                 *(ulong *)(memaddr + topwin) = 0x5aa5c33c;
 893 
 894                 if(*(ulong *)(memaddr + botwin) != 0xa55a3cc3 ||
 895                                         *(ulong *)(memaddr + topwin) != 0x5aa5c33c) {
 896                         printk("Failed memory test at %lx for %s at port %x, check switch settings.\n",
 897                                 bd->membase, board_desc[bd->type], bd->port);
 898                         bd->status = DISABLED;
 899                         continue;
 900                 }
 901 
 902                 for(i=0; i < 16; i++) {
 903                         memaddr[MISCGLOBAL+i] = 0;
 904                 }
 905 
 906                 if(bd->type == PCXI || bd->type == PCXE) {
 907                         bios = memaddr + BIOSCODE + ((0xf000 - memory_seg) << 4);
 908 
 909                         memcpy(bios, pcxx_bios, pcxx_nbios);
 910 
 911                         outb(FEPMEM, bd->port);
 912 
 913                         for(i=0; i <= 10000; i++) {
 914                                 if(*(ushort *)((ulong)memaddr + MISCGLOBAL) == *(ushort *)"GD" ) {
 915                                         goto load_fep;
 916                                 }
 917                                 pcxxdelay(1);
 918                         }
 919 
 920                         printk("BIOS download failed on the %s at 0x%x!\n",
 921                                                         board_desc[bd->type], bd->port);
 922                         bd->status = DISABLED;
 923                         continue;
 924                 }
 925 
 926                 if(bd->type == PCXEVE) {
 927                         bios = memaddr + (BIOSCODE & 0x1fff);
 928                         memwinon(bd,0xff);
 929                         
 930                         memcpy(bios, pcxx_bios, pcxx_nbios);
 931 
 932                         outb(FEPCLR, bd->port);
 933                         memwinon(bd,0);
 934 
 935                         for(i=0; i <= 10000; i++) {
 936                                 if(*(ushort *)((ulong)memaddr + MISCGLOBAL) == *(ushort *)"GD" ) {
 937                                         goto load_fep;
 938                                 }
 939                                 pcxxdelay(1);
 940                         }
 941 
 942                         printk("BIOS download failed on the %s at 0x%x!\n",
 943                                 board_desc[bd->type], bd->port);
 944                         bd->status = DISABLED;
 945                         continue;
 946                 }
 947 
 948 load_fep:
 949                 fepos = memaddr + FEPCODE;
 950                 if(bd->type == PCXEVE)
 951                         fepos = memaddr + (FEPCODE & 0x1fff);
 952 
 953                 memwinon(bd, (FEPCODE >> 13));
 954                 memcpy(fepos, pcxx_cook, pcxx_ncook);
 955                 memwinon(bd, 0);
 956 
 957                 *(ushort *)((ulong)memaddr + MBOX +  0) = 2;
 958                 *(ushort *)((ulong)memaddr + MBOX +  2) = memory_seg + FEPCODESEG;
 959                 *(ushort *)((ulong)memaddr + MBOX +  4) = 0;
 960                 *(ushort *)((ulong)memaddr + MBOX +  6) = FEPCODESEG;
 961                 *(ushort *)((ulong)memaddr + MBOX +  8) = 0;
 962                 *(ushort *)((ulong)memaddr + MBOX + 10) = pcxx_ncook;
 963 
 964                 outb(FEPMEM|FEPINT, bd->port);
 965                 outb(FEPMEM, bd->port);
 966 
 967                 for(i=0; *(ushort *)((ulong)memaddr + MBOX); i++) {
 968                         if(i > 2000) {
 969                                 printk("Command failed for the %s at 0x%x!\n",
 970                                         board_desc[bd->type], bd->port);
 971                                 bd->status = DISABLED;
 972                                 break;
 973                         }
 974                         pcxxdelay(1);
 975                 }
 976 
 977                 if(bd->status == DISABLED)
 978                         continue;
 979 
 980                 *(ushort *)(memaddr + FEPSTAT) = 0;
 981                 *(ushort *)(memaddr + MBOX + 0) = 1;
 982                 *(ushort *)(memaddr + MBOX + 2) = FEPCODESEG;
 983                 *(ushort *)(memaddr + MBOX + 4) = 0x4L;
 984 
 985                 outb(FEPINT, bd->port);
 986                 outb(FEPCLR, bd->port);
 987                 memwinon(bd, 0);
 988 
 989                 for(i=0; *(ushort *)((ulong)memaddr + FEPSTAT) != *(ushort *)"OS"; i++) {
 990                         if(i > 10000) {
 991                                 printk("FEP/OS download failed on the %s at 0x%x!\n",
 992                                         board_desc[bd->type], bd->port);
 993                                 bd->status = DISABLED;
 994                                 break;
 995                         }
 996                         pcxxdelay(1);
 997                 }
 998                 if(bd->status == DISABLED)
 999                         continue;
1000 
1001                 ch = &digi_channels[MAXPORTS * crd];
1002                 pcxxassert(ch <= &digi_channels[NBDEVS-1], "ch out of range");
1003 
1004                 bc = (volatile struct board_chan *)((ulong)memaddr + CHANSTRUCT);
1005                 gd = (volatile struct global_data *)((ulong)memaddr + GLOBAL);
1006 
1007                 if((bd->type == PCXEVE) && (*(ushort *)((ulong)memaddr+NPORT) < 3))
1008                         shrinkmem = 1;
1009 
1010                 request_region(bd->port, 4, "PC/Xx");
1011 
1012                 for(i=0; i < bd->numports; i++, ch++, bc++) {
1013                         if(((ushort *)((ulong)memaddr + PORTBASE))[i] == 0) {
1014                                 ch->brdchan = 0;
1015                                 continue;
1016                         }
1017                         ch->brdchan = bc;
1018                         ch->mailbox = gd;
1019                         ch->tqueue.routine = do_softint;
1020                         ch->tqueue.data = ch;
1021                         ch->board = &boards[crd];
1022 #ifdef DEFAULT_HW_FLOW
1023                         ch->digiext.digi_flags = RTSPACE|CTSPACE;
1024 #endif
1025                         if(boards[crd].altpin) {
1026                                 ch->dsr = CD;
1027                                 ch->dcd = DSR;
1028                                 ch->digiext.digi_flags |= DIGI_ALTPIN;
1029                         } else { 
1030                                 ch->dcd = CD;
1031                                 ch->dsr = DSR;
1032                         }
1033 
1034                         ch->magic = PCXX_MAGIC;
1035                         ch->boardnum = crd;
1036                         ch->channelnum = i;
1037 
1038                         ch->dev = (MAXPORTS * crd) + i;
1039                         ch->tty = 0;
1040 
1041                         if(shrinkmem) {
1042                                 fepcmd(ch, SETBUFFER, 32, 0, 0, 0);
1043                                 shrinkmem = 0;
1044                         }
1045                         
1046                         if(bd->type != PCXEVE) {
1047                                 ch->txptr = memaddr+((bc->tseg-memory_seg) << 4);
1048                                 ch->rxptr = memaddr+((bc->rseg-memory_seg) << 4);
1049                                 ch->txwin = ch->rxwin = 0;
1050                         } else {
1051                                 ch->txptr = memaddr+(((bc->tseg-memory_seg) << 4) & 0x1fff);
1052                                 ch->txwin = FEPWIN | ((bc->tseg-memory_seg) >> 9);
1053                                 ch->rxptr = memaddr+(((bc->rseg-memory_seg) << 4) & 0x1fff);
1054                                 ch->rxwin = FEPWIN | ((bc->rseg-memory_seg) >>9 );
1055                         }
1056 
1057                         ch->txbufsize = bc->tmax + 1;
1058                         ch->rxbufsize = bc->rmax + 1;
1059 
1060                         ch->tmp_buf = kmalloc(ch->txbufsize,GFP_KERNEL);
1061 
1062                         lowwater = ch->txbufsize >= 2000 ? 1024 : ch->txbufsize/2;
1063                         fepcmd(ch, STXLWATER, lowwater, 0, 10, 0);
1064                         fepcmd(ch, SRXLWATER, ch->rxbufsize/4, 0, 10, 0);
1065                         fepcmd(ch, SRXHWATER, 3 * ch->rxbufsize/4, 0, 10, 0);
1066 
1067                         bc->edelay = 100;
1068                         bc->idata = 1;
1069 
1070                         ch->startc = bc->startc;
1071                         ch->stopc = bc->stopc;
1072                         ch->startca = bc->startca;
1073                         ch->stopca = bc->stopca;
1074 
1075                         ch->fepcflag = 0;
1076                         ch->fepiflag = 0;
1077                         ch->fepoflag = 0;
1078                         ch->fepstartc = 0;
1079                         ch->fepstopc = 0;
1080                         ch->fepstartca = 0;
1081                         ch->fepstopca = 0;
1082 
1083                         ch->close_delay = 50;
1084                         ch->count = 0;
1085                         ch->blocked_open = 0;
1086                         ch->callout_termios = pcxe_callout.init_termios;
1087                         ch->normal_termios = pcxe_driver.init_termios;
1088                         ch->open_wait = 0;
1089                         ch->close_wait = 0;
1090                 }
1091 
1092                 printk("DigiBoard PC/Xx Driver V%s:  %s I/O=0x%x Mem=0x%lx Ports=%d\n", 
1093                                 VERSION, board_desc[bd->type], bd->port, bd->membase, bd->numports);
1094 
1095                 memwinoff(bd, 0);
1096         }
1097 
1098         if(tty_register_driver(&pcxe_driver))
1099                 panic("Couldn't register PC/Xe driver");
1100 
1101         if(tty_register_driver(&pcxe_callout))
1102                 panic("Couldn't register PC/Xe callout");
1103 
1104 
1105         loops_per_sec = save_loops_per_sec;  /* reset it to what it should be */
1106 
1107         /*
1108          * Start up the poller to check for events on all enabled boards
1109          */
1110         timer_active |= 1 << DIGI_TIMER;
1111         restore_flags(flags);
1112 
1113         return 0;
1114 }
1115 
1116 
1117 static void pcxxpoll(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1118 {
1119         unsigned long flags;
1120         int crd;
1121         volatile unsigned int head, tail;
1122         struct channel *ch;
1123         struct board_info *bd;
1124 
1125         save_flags(flags);
1126         cli();
1127 
1128         for(crd=0; crd < NUMCARDS; crd++) {
1129                 bd = &boards[crd];
1130                 ch = &digi_channels[MAXPORTS*crd];
1131 
1132                 if(bd->status == DISABLED)
1133                         continue;
1134 
1135                 assertmemoff(ch);
1136 
1137                 globalwinon(ch);
1138                 head = ch->mailbox->ein;
1139                 tail = ch->mailbox->eout;
1140 
1141                 if(head != tail)
1142                         doevent(crd);
1143 
1144                 memoff(ch);
1145         }
1146 
1147         timer_table[DIGI_TIMER].fn = pcxxpoll;
1148         timer_table[DIGI_TIMER].expires = jiffies + HZ/25;
1149         timer_active |= 1 << DIGI_TIMER;
1150         restore_flags(flags);
1151 }
1152 
1153 static void doevent(int crd)
     /* [previous][next][first][last][top][bottom][index][help] */
1154 {
1155         volatile struct board_info *bd;
1156         static struct tty_struct *tty;
1157         volatile struct board_chan *bc;
1158         volatile unchar *eventbuf;
1159         volatile unsigned int head;
1160         volatile unsigned int tail;
1161         struct channel *ch;
1162         struct channel *chan0;
1163         int channel, event, mstat, lstat;
1164 
1165         bd = &boards[crd];
1166 
1167         chan0 = &digi_channels[MAXPORTS * crd];
1168         pcxxassert(chan0 <= &digi_channels[NBDEVS-1], "ch out of range");
1169 
1170         assertgwinon(chan0);
1171 
1172         while ((tail = chan0->mailbox->eout) != (head = chan0->mailbox->ein)) {
1173                 assertgwinon(chan0);
1174                 eventbuf = (volatile unchar *)bd->membase + tail + ISTART;
1175                 channel = eventbuf[0];
1176                 event = eventbuf[1];
1177                 mstat = eventbuf[2];
1178                 lstat = eventbuf[3];
1179 
1180                 ch=chan0+channel;
1181 
1182                 if ((unsigned)channel >= bd->numports || !ch) { 
1183                         printk("physmem=%lx, tail=%x, head=%x\n", bd->membase, tail, head);
1184                         printk("doevent(%x) channel %x, event %x, mstat %x, lstat %x\n",
1185                                         crd, (unsigned)channel, event, (unsigned)mstat, lstat);
1186                         if(channel >= bd->numports)
1187                                 ch = chan0;
1188                         bc = ch->brdchan;
1189                         goto next;
1190                 }
1191                 if ((bc = ch->brdchan) == NULL)
1192                         goto next;
1193 
1194                 if (event & DATA_IND) {
1195                         receive_data(ch);
1196                         assertgwinon(ch);
1197                 }
1198 
1199                 if (event & MODEMCHG_IND) {
1200                         ch->imodem = mstat;
1201                         if (ch->asyncflags & (ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE)) {
1202                                 if (ch->asyncflags & ASYNC_CHECK_CD) {
1203                                         if (mstat & ch->dcd) {
1204                                                 wake_up_interruptible(&ch->open_wait);
1205                                         } else {
1206                                                 pcxe_sched_event(ch, PCXE_EVENT_HANGUP);
1207                                         }
1208                                 }
1209                         }
1210                 }
1211 
1212                 tty = ch->tty;
1213 
1214                 if (tty) {
1215 
1216                         if (event & BREAK_IND) {
1217                                 tty->flip.count++;
1218                                 *tty->flip.flag_buf_ptr++ = TTY_BREAK;
1219                                 *tty->flip.char_buf_ptr++ = 0;
1220 #if 0
1221                                 if (ch->asyncflags & ASYNC_SAK)
1222                                         do_SAK(tty);
1223 #endif
1224                                 tty_schedule_flip(tty); 
1225                         }
1226 
1227                         if (event & LOWTX_IND) {
1228                                 if (ch->statusflags & LOWWAIT) {
1229                                         ch->statusflags &= ~LOWWAIT;
1230                                         if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
1231                                                 tty->ldisc.write_wakeup)
1232                                                 (tty->ldisc.write_wakeup)(tty);
1233                                         wake_up_interruptible(&tty->write_wait);
1234                                 }
1235                         }
1236 
1237                         if (event & EMPTYTX_IND) {
1238                                 ch->statusflags &= ~TXBUSY;
1239                                 if (ch->statusflags & EMPTYWAIT) {
1240                                         ch->statusflags &= ~EMPTYWAIT;
1241                                         if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
1242                                                 tty->ldisc.write_wakeup)
1243                                                 (tty->ldisc.write_wakeup)(tty);
1244                                         wake_up_interruptible(&tty->write_wait);
1245                                 }
1246                         }
1247                 }
1248 
1249         next:
1250                 globalwinon(ch);
1251                 if(!bc) printk("bc == NULL in doevent!\n");
1252                 else bc->idata = 1;
1253 
1254                 chan0->mailbox->eout = (tail+4) & (IMAX-ISTART-4);
1255                 globalwinon(chan0);
1256         }
1257 
1258 }
1259 
1260 
1261 /*
1262  * pcxxdelay - delays a specified number of milliseconds
1263  */
1264 static void pcxxdelay(int msec)
     /* [previous][next][first][last][top][bottom][index][help] */
1265 {
1266         while(msec-- > 0)
1267                 __delay(loops_per_sec/1000);
1268 }
1269 
1270 
1271 static void 
1272 fepcmd(struct channel *ch, int cmd, int word_or_byte, int byte2, int ncmds,
     /* [previous][next][first][last][top][bottom][index][help] */
1273                                                 int bytecmd)
1274 {
1275         unchar *memaddr;
1276         unsigned int head, tail;
1277         long count;
1278         int n;
1279 
1280         if(ch->board->status == DISABLED)
1281                 return;
1282 
1283         assertgwinon(ch);
1284 
1285         memaddr = (unchar *)ch->board->membase;
1286         head = ch->mailbox->cin;
1287 
1288         if(head >= (CMAX-CSTART) || (head & 03)) {
1289                 printk("line %d: Out of range, cmd=%x, head=%x\n", __LINE__, cmd, head);
1290                 return;
1291         }
1292 
1293         if(bytecmd) {
1294                 *(unchar *)(memaddr+head+CSTART+0) = cmd;
1295                 *(unchar *)(memaddr+head+CSTART+1) = PORTNUM(ch);
1296                 *(unchar *)(memaddr+head+CSTART+2) = word_or_byte;
1297                 *(unchar *)(memaddr+head+CSTART+3) = byte2;
1298         } else {
1299                 *(unchar *)(memaddr+head+CSTART+0) = cmd;
1300                 *(unchar *)(memaddr+head+CSTART+1) = PORTNUM(ch);
1301                 *(ushort*)(memaddr+head+CSTART+2) = word_or_byte;
1302         }
1303 
1304         head = (head+4) & (CMAX-CSTART-4);
1305         ch->mailbox->cin = head;
1306 
1307         count = FEPTIMEOUT;
1308 
1309         while(1) {
1310                 count--;
1311                 if(count == 0) {
1312                         printk("Fep not responding in fepcmd()\n");
1313                         return;
1314                 }
1315 
1316                 head = ch->mailbox->cin;
1317                 tail = ch->mailbox->cout;
1318 
1319                 n = (head-tail) & (CMAX-CSTART-4);
1320 
1321                 if(n <= ncmds * (sizeof(short)*4))
1322                         break;
1323         }
1324 }
1325 
1326 
1327 static unsigned termios2digi_c(struct channel *ch, unsigned cflag)
     /* [previous][next][first][last][top][bottom][index][help] */
1328 {
1329         unsigned res = 0;
1330 #ifdef SPEED_HACK
1331         /* CL: HACK to force 115200 at 38400 and 57600 at 19200 Baud */
1332         if ((cflag & CBAUD)== B38400) cflag=cflag - B38400 + B115200;
1333         if ((cflag & CBAUD)== B19200) cflag=cflag - B19200 + B57600;
1334 #endif
1335         if (cflag & CBAUDEX)
1336         {
1337                 ch->digiext.digi_flags |= DIGI_FAST;
1338                 res |= FEP_HUPCL;
1339                 /* This gets strange but if we dont do this we will get 78600
1340                  * instead of 115200. 57600 is mapped to 50 baud yielding 57600 in
1341                  * FAST mode. 115200 is mapped to 75. We need to map it to 110 to
1342                  * do 115K
1343                  */
1344                 if (cflag & B115200) res|=1;
1345         }
1346         else ch->digiext.digi_flags &= ~DIGI_FAST;
1347         res |= cflag & (CBAUD | PARODD | PARENB | CSTOPB | CSIZE);
1348         return res;
1349 }
1350 
1351 static unsigned termios2digi_i(struct channel *ch, unsigned iflag)
     /* [previous][next][first][last][top][bottom][index][help] */
1352 {
1353         unsigned res = iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|IXON|IXANY|IXOFF);
1354         
1355         if(ch->digiext.digi_flags & DIGI_AIXON)
1356                 res |= IAIXON;
1357         return res;
1358 }
1359 
1360 static unsigned termios2digi_h(struct channel *ch, unsigned cflag)
     /* [previous][next][first][last][top][bottom][index][help] */
1361 {
1362         unsigned res = 0;
1363 
1364         if(cflag & CRTSCTS) {
1365                 ch->digiext.digi_flags |= (RTSPACE|CTSPACE);
1366                 res |= (CTS | RTS);
1367         }
1368         if(ch->digiext.digi_flags & RTSPACE)
1369                 res |= RTS;
1370         if(ch->digiext.digi_flags & DTRPACE)
1371                 res |= DTR;
1372         if(ch->digiext.digi_flags & CTSPACE)
1373                 res |= CTS;
1374         if(ch->digiext.digi_flags & DSRPACE)
1375                 res |= ch->dsr;
1376         if(ch->digiext.digi_flags & DCDPACE)
1377                 res |= ch->dcd;
1378 
1379         if (res & RTS)
1380                 ch->digiext.digi_flags |= RTSPACE;
1381         if (res & CTS)
1382                 ch->digiext.digi_flags |= CTSPACE;
1383 
1384         return res;
1385 }
1386 
1387 static void pcxxparam(struct tty_struct *tty, struct channel *ch)
     /* [previous][next][first][last][top][bottom][index][help] */
1388 {
1389         volatile struct board_chan *bc;
1390         unsigned int head;
1391         unsigned mval, hflow, cflag, iflag;
1392         struct termios *ts;
1393 
1394         bc = ch->brdchan;
1395         assertgwinon(ch);
1396         ts = tty->termios;
1397 
1398         if((ts->c_cflag & CBAUD) == 0) {
1399                 head = bc->rin;
1400                 bc->rout = head;
1401                 head = bc->tin;
1402                 fepcmd(ch, STOUT, (unsigned) head, 0, 0, 0);
1403                 mval = 0;
1404         } else {
1405 
1406                 cflag = termios2digi_c(ch, ts->c_cflag);
1407 
1408                 if(cflag != ch->fepcflag) {
1409                         ch->fepcflag = cflag;
1410                         fepcmd(ch, SETCTRLFLAGS, (unsigned) cflag, 0, 0, 0);
1411                 }
1412 
1413                 if(cflag & CLOCAL)
1414                         ch->asyncflags &= ~ASYNC_CHECK_CD;
1415                 else {
1416                         ch->asyncflags |= ASYNC_CHECK_CD;
1417                 }
1418 
1419                 mval = DTR | RTS;
1420         }
1421 
1422         iflag = termios2digi_i(ch, ts->c_iflag);
1423 
1424         if(iflag != ch->fepiflag) {
1425                 ch->fepiflag = iflag;
1426                 fepcmd(ch, SETIFLAGS, (unsigned int) ch->fepiflag, 0, 0, 0);
1427         }
1428 
1429         bc->mint = ch->dcd;
1430         if((ts->c_cflag & CLOCAL) || (ch->digiext.digi_flags & DIGI_FORCEDCD))
1431                 if(ch->digiext.digi_flags & DIGI_FORCEDCD)
1432                         bc->mint = 0;
1433 
1434         ch->imodem = bc->mstat;
1435 
1436         hflow = termios2digi_h(ch, ts->c_cflag);
1437 
1438         if(hflow != ch->hflow) {
1439                 ch->hflow = hflow;
1440                 fepcmd(ch, SETHFLOW, hflow, 0xff, 0, 1);
1441         }
1442 
1443         /* mval ^= ch->modemfake & (mval ^ ch->modem); */
1444 
1445         if(ch->omodem != mval) {
1446                 ch->omodem = mval;
1447                 fepcmd(ch, SETMODEM, mval, RTS|DTR, 0, 1);
1448         }
1449 
1450         if(ch->startc != ch->fepstartc || ch->stopc != ch->fepstopc) {
1451                 ch->fepstartc = ch->startc;
1452                 ch->fepstopc = ch->stopc;
1453                 fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1);
1454         }
1455 
1456         if(ch->startca != ch->fepstartca || ch->stopca != ch->fepstopca) {
1457                 ch->fepstartca = ch->startca;
1458                 ch->fepstopca = ch->stopca;
1459                 fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1);
1460         }
1461 }
1462 
1463 
1464 static void receive_data(struct channel *ch)
     /* [previous][next][first][last][top][bottom][index][help] */
1465 {
1466         volatile struct board_chan *bc;
1467         struct tty_struct *tty;
1468         unsigned int tail, head, wrapmask;
1469         int n;
1470         int piece;
1471         struct termios *ts=0;
1472         unchar *rptr;
1473         int rc;
1474         int wrapgap;
1475 
1476     globalwinon(ch);
1477 
1478         if (ch->statusflags & RXSTOPPED)
1479                 return;
1480 
1481         tty = ch->tty;
1482         if(tty)
1483                 ts = tty->termios;
1484 
1485         bc = ch->brdchan;
1486 
1487         if(!bc) {
1488                 printk("bc is NULL in receive_data!\n");
1489                 return;
1490         }
1491 
1492         wrapmask = ch->rxbufsize - 1;
1493 
1494         head = bc->rin;
1495         head &= wrapmask;
1496         tail = bc->rout & wrapmask;
1497 
1498         n = (head-tail) & wrapmask;
1499 
1500         if(n == 0)
1501                 return;
1502 
1503         /*
1504          * If CREAD bit is off or device not open, set TX tail to head
1505          */
1506         if(!tty || !ts || !(ts->c_cflag & CREAD)) {
1507                 bc->rout = head;
1508                 return;
1509         }
1510 
1511         if(tty->flip.count == TTY_FLIPBUF_SIZE) {
1512                 /* printk("tty->flip.count = TTY_FLIPBUF_SIZE\n"); */
1513                 return;
1514         }
1515 
1516         if(bc->orun) {
1517                 bc->orun = 0;
1518                 printk("overrun! DigiBoard device minor=%d\n",MINOR(tty->device));
1519         }
1520 
1521         rxwinon(ch);
1522         rptr = tty->flip.char_buf_ptr;
1523         rc = tty->flip.count;
1524         while(n > 0) {
1525                 wrapgap = (head >= tail) ? head - tail : ch->rxbufsize - tail;
1526                 piece = (wrapgap < n) ? wrapgap : n;
1527 
1528                 /*
1529                  * Make sure we don't overflow the buffer
1530                  */
1531 
1532                 if ((rc + piece) > TTY_FLIPBUF_SIZE)
1533                         piece = TTY_FLIPBUF_SIZE - rc;
1534 
1535                 if (piece == 0)
1536                         break;
1537 
1538                 memcpy(rptr, ch->rxptr + tail, piece);
1539                 rptr += piece;
1540                 rc += piece;
1541                 tail = (tail + piece) & wrapmask;
1542                 n -= piece;
1543         }
1544         tty->flip.count = rc;
1545         tty->flip.char_buf_ptr = rptr;
1546     globalwinon(ch);
1547         bc->rout = tail;
1548 
1549         /* Must be called with global data */
1550         tty_schedule_flip(ch->tty); 
1551         return;
1552 }
1553 
1554 
1555 static int pcxe_ioctl(struct tty_struct *tty, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
1556                     unsigned int cmd, unsigned long arg)
1557 {
1558         int error;
1559         struct channel *ch = (struct channel *) tty->driver_data;
1560         volatile struct board_chan *bc;
1561         int retval;
1562         unsigned int mflag, mstat;
1563         unsigned char startc, stopc;
1564         unsigned long flags;
1565         digiflow_t dflow;
1566 
1567         if(ch)
1568                 bc = ch->brdchan;
1569         else {
1570                 printk("ch is NULL in pcxe_ioctl!\n");
1571                 return(-EINVAL);
1572         }
1573 
1574         save_flags(flags);
1575 
1576         switch(cmd) {
1577                 case TCSBRK:    /* SVID version: non-zero arg --> no break */
1578                         retval = tty_check_change(tty);
1579                         if(retval)
1580                                 return retval;
1581                         setup_empty_event(tty,ch);              
1582                         tty_wait_until_sent(tty, 0);
1583                         if(!arg)
1584                                 digi_send_break(ch, HZ/4);    /* 1/4 second */
1585                         return 0;
1586 
1587                 case TCSBRKP:   /* support for POSIX tcsendbreak() */
1588                         retval = tty_check_change(tty);
1589                         if(retval)
1590                                 return retval;
1591                         setup_empty_event(tty,ch);              
1592                         tty_wait_until_sent(tty, 0);
1593                         digi_send_break(ch, arg ? arg*(HZ/10) : HZ/4);
1594                         return 0;
1595 
1596                 case TIOCGSOFTCAR:
1597                         error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long));
1598                         if(error)
1599                                 return error;
1600                         put_fs_long(C_CLOCAL(tty) ? 1 : 0,
1601                                     (unsigned long *) arg);
1602                         return 0;
1603 
1604                 case TIOCSSOFTCAR:
1605                         arg = get_fs_long((unsigned long *) arg);
1606                         tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0));
1607                         return 0;
1608 
1609                 case TIOCMODG:
1610                 case TIOCMGET:
1611                         mflag = 0;
1612 
1613                         cli();
1614                         globalwinon(ch);
1615                         mstat = bc->mstat;
1616                         memoff(ch);
1617                         restore_flags(flags);
1618 
1619                         if(mstat & DTR)
1620                                 mflag |= TIOCM_DTR;
1621                         if(mstat & RTS)
1622                                 mflag |= TIOCM_RTS;
1623                         if(mstat & CTS)
1624                                 mflag |= TIOCM_CTS;
1625                         if(mstat & ch->dsr)
1626                                 mflag |= TIOCM_DSR;
1627                         if(mstat & RI)
1628                                 mflag |= TIOCM_RI;
1629                         if(mstat & ch->dcd)
1630                                 mflag |= TIOCM_CD;
1631 
1632                         error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long));
1633                         if(error)
1634                                 return error;
1635                         put_fs_long(mflag, (unsigned long *) arg);
1636                         break;
1637 
1638                 case TIOCMBIS:
1639                 case TIOCMBIC:
1640                 case TIOCMODS:
1641                 case TIOCMSET:
1642                         mstat = get_fs_long((unsigned long *) arg);
1643 
1644                         mflag = 0;
1645                         if(mstat & TIOCM_DTR)
1646                                 mflag |= DTR;
1647                         if(mstat & TIOCM_RTS)
1648                                 mflag |= RTS;
1649 
1650                         switch(cmd) {
1651                                 case TIOCMODS:
1652                                 case TIOCMSET:
1653                                         ch->modemfake = DTR|RTS;
1654                                         ch->modem = mflag;
1655                                         break;
1656 
1657                                 case TIOCMBIS:
1658                                         ch->modemfake |= mflag;
1659                                         ch->modem |= mflag;
1660                                         break;
1661 
1662                                 case TIOCMBIC:
1663                                         ch->modemfake |= mflag;
1664                                         ch->modem &= ~mflag;
1665                                         break;
1666                         }
1667 
1668                         cli();
1669                         globalwinon(ch);
1670                         pcxxparam(tty,ch);
1671                         memoff(ch);
1672                         restore_flags(flags);
1673                         break;
1674 
1675                 case TIOCSDTR:
1676                         cli();
1677                         ch->omodem |= DTR;
1678                         globalwinon(ch);
1679                         fepcmd(ch, SETMODEM, DTR, 0, 10, 1);
1680                         memoff(ch);
1681                         restore_flags(flags);
1682                         break;
1683 
1684                 case TIOCCDTR:
1685                         ch->omodem &= ~DTR;
1686                         cli();
1687                         globalwinon(ch);
1688                         fepcmd(ch, SETMODEM, 0, DTR, 10, 1);
1689                         memoff(ch);
1690                         restore_flags(flags);
1691                         break;
1692 
1693                 case DIGI_GETA:
1694                         if((error=verify_area(VERIFY_WRITE, (char*)arg, sizeof(digi_t))))
1695                                 return(error);
1696 
1697                         memcpy_tofs((char*)arg, &ch->digiext, sizeof(digi_t));
1698                         break;
1699 
1700                 case DIGI_SETAW:
1701                 case DIGI_SETAF:
1702                         if(cmd == DIGI_SETAW) {
1703                                 setup_empty_event(tty,ch);              
1704                                 tty_wait_until_sent(tty, 0);
1705                         }
1706                         else {
1707                                 if(tty->ldisc.flush_buffer)
1708                                         tty->ldisc.flush_buffer(tty);
1709                         }
1710 
1711                         /* Fall Thru */
1712 
1713                 case DIGI_SETA:
1714                         if((error=verify_area(VERIFY_READ, (char*)arg,sizeof(digi_t))))
1715                                 return(error);
1716 
1717                         memcpy_fromfs(&ch->digiext, (char*)arg, sizeof(digi_t));
1718 #ifdef DEBUG_IOCTL
1719                         printk("ioctl(DIGI_SETA): flags = %x\n", ch->digiext.digi_flags);
1720 #endif
1721                         
1722                         if(ch->digiext.digi_flags & DIGI_ALTPIN) {
1723                                 ch->dcd = DSR;
1724                                 ch->dsr = CD;
1725                         } else {
1726                                 ch->dcd = CD;
1727                                 ch->dsr = DSR;
1728                         }
1729                 
1730                         cli();
1731                         globalwinon(ch);
1732                         pcxxparam(tty,ch);
1733                         memoff(ch);
1734                         restore_flags(flags);
1735                         break;
1736 
1737                 case DIGI_GETFLOW:
1738                 case DIGI_GETAFLOW:
1739                         cli();  
1740                         globalwinon(ch);
1741                         if(cmd == DIGI_GETFLOW) {
1742                                 dflow.startc = bc->startc;
1743                                 dflow.stopc = bc->stopc;
1744                         } else {
1745                                 dflow.startc = bc->startca;
1746                                 dflow.stopc = bc->stopca;
1747                         }
1748                         memoff(ch);
1749                         restore_flags(flags);
1750 
1751                         if((error=verify_area(VERIFY_WRITE, (char*)arg,sizeof(dflow))))
1752                                 return(error);
1753 
1754                         memcpy_tofs((char*)arg, &dflow, sizeof(dflow));
1755                         break;
1756 
1757                 case DIGI_SETAFLOW:
1758                 case DIGI_SETFLOW:
1759                         if(cmd == DIGI_SETFLOW) {
1760                                 startc = ch->startc;
1761                                 stopc = ch->stopc;
1762                         } else {
1763                                 startc = ch->startca;
1764                                 stopc = ch->stopca;
1765                         }
1766 
1767                         if((error=verify_area(VERIFY_READ, (char*)arg,sizeof(dflow))))
1768                                 return(error);
1769 
1770                         memcpy_fromfs(&dflow, (char*)arg, sizeof(dflow));
1771 
1772                         if(dflow.startc != startc || dflow.stopc != stopc) {
1773                                 cli();
1774                                 globalwinon(ch);
1775 
1776                                 if(cmd == DIGI_SETFLOW) {
1777                                         ch->fepstartc = ch->startc = dflow.startc;
1778                                         ch->fepstopc = ch->stopc = dflow.stopc;
1779                                         fepcmd(ch,SONOFFC,ch->fepstartc,ch->fepstopc,0, 1);
1780                                 } else {
1781                                         ch->fepstartca = ch->startca = dflow.startc;
1782                                         ch->fepstopca  = ch->stopca = dflow.stopc;
1783                                         fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1);
1784                                 }
1785 
1786                                 if(ch->statusflags & TXSTOPPED)
1787                                         pcxe_start(tty);
1788 
1789                                 memoff(ch);
1790                                 restore_flags(flags);
1791                         }
1792                         break;
1793 
1794                 default:
1795                         return -ENOIOCTLCMD;
1796         }
1797 
1798         return 0;
1799 }
1800 
1801 static void pcxe_set_termios(struct tty_struct *tty, struct termios *old_termios)
     /* [previous][next][first][last][top][bottom][index][help] */
1802 {
1803         struct channel *info;
1804 
1805         if ((info=chan(tty))!=NULL) {
1806                 unsigned long flags;
1807                 save_flags(flags);
1808                 cli();
1809                 globalwinon(info);
1810                 pcxxparam(tty,info);
1811                 memoff(info);
1812 
1813                 if ((old_termios->c_cflag & CRTSCTS) &&
1814                         ((tty->termios->c_cflag & CRTSCTS) == 0))
1815                         tty->hw_stopped = 0;
1816                 if(!(old_termios->c_cflag & CLOCAL) &&
1817                         (tty->termios->c_cflag & CLOCAL))
1818                         wake_up_interruptible(&info->open_wait);
1819                 restore_flags(flags);
1820         }
1821 }
1822 
1823 
1824 static void do_pcxe_bh(void)
     /* [previous][next][first][last][top][bottom][index][help] */
1825 {
1826         run_task_queue(&tq_pcxx);
1827 }
1828 
1829 
1830 static void do_softint(void *private_)
     /* [previous][next][first][last][top][bottom][index][help] */
1831 {
1832         struct channel *info = (struct channel *) private_;
1833         
1834         if(info && info->magic == PCXX_MAGIC) {
1835                 struct tty_struct *tty = info->tty;
1836                 if (tty && tty->driver_data) {
1837                         if(clear_bit(PCXE_EVENT_HANGUP, &info->event)) {
1838                                 tty_hangup(tty);
1839                                 wake_up_interruptible(&info->open_wait);
1840                                 info->asyncflags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
1841                         }
1842                 }
1843         }
1844 }
1845 
1846 
1847 static void pcxe_stop(struct tty_struct *tty)
     /* [previous][next][first][last][top][bottom][index][help] */
1848 {
1849         struct channel *info;
1850 
1851         if ((info=chan(tty))!=NULL) {
1852                 unsigned long flags;
1853                 save_flags(flags); 
1854                 cli();
1855                 if ((info->statusflags & TXSTOPPED) == 0) {
1856                         globalwinon(info);
1857                         fepcmd(info, PAUSETX, 0, 0, 0, 0);
1858                         info->statusflags |= TXSTOPPED;
1859                         memoff(info);
1860                 }
1861                 restore_flags(flags);
1862         }
1863 }
1864 
1865 static void pcxe_throttle(struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
1866 {
1867         struct channel *info;
1868 
1869         if ((info=chan(tty))!=NULL) {
1870                 unsigned long flags;
1871                 save_flags(flags);
1872                 cli();
1873                 if ((info->statusflags & RXSTOPPED) == 0) {
1874                         globalwinon(info);
1875                         fepcmd(info, PAUSERX, 0, 0, 0, 0);
1876                         info->statusflags |= RXSTOPPED;
1877                         memoff(info);
1878                 }
1879                 restore_flags(flags);
1880         }
1881 }
1882 
1883 static void pcxe_unthrottle(struct tty_struct *tty)
     /* [previous][next][first][last][top][bottom][index][help] */
1884 {
1885         struct channel *info;
1886 
1887         if ((info=chan(tty)) != NULL) {
1888                 unsigned long flags;
1889 
1890                 /* Just in case output was resumed because of a change in Digi-flow */
1891                 save_flags(flags);
1892                 cli();
1893                 if(info->statusflags & RXSTOPPED) {
1894                         volatile struct board_chan *bc;
1895                         globalwinon(info);
1896                         bc = info->brdchan;
1897                         fepcmd(info, RESUMERX, 0, 0, 0, 0);
1898                         info->statusflags &= ~RXSTOPPED;
1899                         memoff(info);
1900                 }
1901                 restore_flags(flags);
1902         }
1903 }
1904 
1905 
1906 static void pcxe_start(struct tty_struct *tty)
     /* [previous][next][first][last][top][bottom][index][help] */
1907 {
1908         struct channel *info;
1909 
1910         if ((info=chan(tty))!=NULL) {
1911                 unsigned long flags;
1912 
1913                 save_flags(flags);
1914                 cli();
1915                 /* Just in case output was resumed because of a change in Digi-flow */
1916                 if(info->statusflags & TXSTOPPED) {
1917                         volatile struct board_chan *bc;
1918                         globalwinon(info);
1919                         bc = info->brdchan;
1920                         if(info->statusflags & LOWWAIT)
1921                                 bc->ilow = 1;
1922                         fepcmd(info, RESUMETX, 0, 0, 0, 0);
1923                         info->statusflags &= ~TXSTOPPED;
1924                         memoff(info);
1925                 }
1926                 restore_flags(flags);
1927         }
1928 }
1929 
1930 
1931 void digi_send_break(struct channel *ch, int msec)
     /* [previous][next][first][last][top][bottom][index][help] */
1932 {
1933         unsigned long flags;
1934 
1935         save_flags(flags);
1936         cli();
1937         globalwinon(ch);
1938 
1939         /* 
1940          * Maybe I should send an infinite break here, schedule() for
1941          * msec amount of time, and then stop the break.  This way,
1942          * the user can't screw up the FEP by causing digi_send_break()
1943          * to be called (i.e. via an ioctl()) more than once in msec amount 
1944          * of time.  Try this for now...
1945          */
1946 
1947         fepcmd(ch, SENDBREAK, msec, 0, 10, 0);
1948         memoff(ch);
1949 
1950         restore_flags(flags);
1951 }
1952 
1953 static void setup_empty_event(struct tty_struct *tty, struct channel *ch)
     /* [previous][next][first][last][top][bottom][index][help] */
1954 {
1955         volatile struct board_chan *bc;
1956         unsigned long flags;
1957 
1958         save_flags(flags);
1959         cli();
1960         globalwinon(ch);
1961         ch->statusflags |= EMPTYWAIT;
1962         bc = ch->brdchan;
1963         bc->iempty = 1;
1964         memoff(ch);
1965         restore_flags(flags);
1966 }
1967 

/* [previous][next][first][last][top][bottom][index][help] */