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

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