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

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