root/kernel/chr_drv/serial.c

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

DEFINITIONS

This source file includes following definitions.
  1. send_break
  2. send_intr
  3. receive_intr
  4. line_status_intr
  5. modem_status_intr
  6. UART_ISR_proc
  7. do_rs_write
  8. FourPort_ISR_proc
  9. rs_interrupt
  10. rs_timer
  11. init
  12. rs_write
  13. rs_close
  14. startup
  15. change_speed
  16. get_serial_info
  17. set_serial_info
  18. get_modem_info
  19. set_modem_info
  20. rs_ioctl
  21. rs_open
  22. rs_init

   1 /*
   2  *  linux/kernel/serial.c
   3  *
   4  *  Copyright (C) 1991, 1992  Linus Torvalds
   5  *
   6  *  Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92.  Now
   7  *  much more extensible to support other serial cards based on the
   8  *  16450/16550A UART's.  Added support for the AST FourPort and the
   9  *  Accent Async board.  We use the async_ISR structure to allow
  10  *  multiple ports (or boards, if the hardware permits) to share a
  11  *  single IRQ channel.
  12  *
  13  * This module exports the following rs232 io functions:
  14  *
  15  *      long rs_init(long);
  16  *      int  rs_open(struct tty_struct * tty, struct file * filp)
  17  *      void change_speed(unsigned int line)
  18  */
  19 
  20 #include <linux/errno.h>
  21 #include <linux/signal.h>
  22 #include <linux/sched.h>
  23 #include <linux/timer.h>
  24 #include <linux/tty.h>
  25 #include <linux/serial.h>
  26 
  27 #include <asm/system.h>
  28 #include <asm/io.h>
  29 #include <asm/segment.h>
  30 
  31 #define WAKEUP_CHARS (3*TTY_BUF_SIZE/4)
  32 
  33 /*
  34  * rs_read_process      - Bitfield of serial lines that have I/O processing
  35  *                              to be done at the next clock tick.
  36  * rs_write_timeout     - Bitfield of serial lines that have a possible 
  37  *                              write timeout pending.  (In case the THRE
  38  *                              interrupt gets lost.)
  39  */
  40 static unsigned long rs_read_process = 0; 
  41 static unsigned long rs_write_timeout = 0;
  42 
  43 static void UART_ISR_proc(async_ISR ISR, int line);
  44 static void FourPort_ISR_proc(async_ISR ISR, int line);
  45 
  46 struct struct_ISR COM1_ISR = { 4, 0x3f8, UART_ISR_proc, 0, };
  47 struct struct_ISR COM2_ISR = { 3, 0x2f8, UART_ISR_proc, 0, };
  48 struct struct_ISR COM3_ISR = { 4, 0x3e8, UART_ISR_proc, 0, };
  49 struct struct_ISR COM4_ISR = { 3, 0x2e8, UART_ISR_proc, 0, };
  50 
  51 struct struct_ISR FourPort1_ISR = { 2, 0x1bf, FourPort_ISR_proc, 0, };
  52 struct struct_ISR FourPort2_ISR = { 5, 0x2bf, FourPort_ISR_proc, 0, };
  53 
  54 struct struct_ISR Accent3_ISR = { 4, 0x330, UART_ISR_proc, 0, };
  55 struct struct_ISR Accent4_ISR = { 4, 0x338, UART_ISR_proc, 0, };
  56 
  57 /*
  58  * This assumes you have a 1.8432 MHz clock for your UART.
  59  *
  60  * It'd be nice if someone built a serial card with a 24.576 MHz
  61  * clock, since the 16550A is capable of handling a top speed of 1.5
  62  * megabits/second; but this requires the faster clock.
  63  */
  64 #define BASE_BAUD ( 1843200 / 16 ) 
  65 
  66 struct async_struct rs_table[] = {
  67         { BASE_BAUD, 0x3F8, &COM1_ISR, 0, },
  68         { BASE_BAUD, 0x2F8, &COM2_ISR, 0, },
  69         { BASE_BAUD, 0x3E8, &COM3_ISR, 0, },
  70         { BASE_BAUD, 0x2E8, &COM4_ISR, 0, },
  71         
  72         { BASE_BAUD, 0x1A0, &FourPort1_ISR, ASYNC_FOURPORT },
  73         { BASE_BAUD, 0x1A8, &FourPort1_ISR, ASYNC_FOURPORT },
  74         { BASE_BAUD, 0x1B0, &FourPort1_ISR, ASYNC_FOURPORT },
  75         { BASE_BAUD, 0x1B8, &FourPort1_ISR, ASYNC_FOURPORT | ASYNC_NOSCRATCH },
  76 
  77         { BASE_BAUD, 0x2A0, &FourPort2_ISR, ASYNC_FOURPORT },
  78         { BASE_BAUD, 0x2A8, &FourPort2_ISR, ASYNC_FOURPORT },
  79         { BASE_BAUD, 0x2B0, &FourPort2_ISR, ASYNC_FOURPORT },
  80         { BASE_BAUD, 0x2B8, &FourPort2_ISR, ASYNC_FOURPORT | ASYNC_NOSCRATCH },
  81 
  82         { BASE_BAUD, 0x330, &Accent3_ISR, 0 },
  83         { BASE_BAUD, 0x338, &Accent4_ISR, 0 },
  84 };
  85 
  86 #define NR_PORTS        (sizeof(rs_table)/sizeof(struct async_struct))
  87 
  88 static async_ISR IRQ_ISR[16];   /* Used to store the head of the */
  89                                 /* ISR linked list chain for each IRQ */
  90 
  91 /*
  92  * This is used to figure out the divsor speeds and the timeouts
  93  */
  94 static int baud_table[] = {
  95         0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
  96         9600, 19200, 38400, 56000, 115200, 0 };
  97 
  98 static void send_break( struct async_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
  99 {
 100         unsigned short port;
 101 
 102         if (!(port = info->port))
 103                 return;
 104         port += UART_LCR;
 105         current->state = TASK_INTERRUPTIBLE;
 106         current->timeout = jiffies + 25;
 107         outb_p(inb_p(port) | UART_LCR_SBC, port);
 108         schedule();
 109         outb_p(inb_p(port) & ~UART_LCR_SBC, port);
 110 }
 111 
 112 /*
 113  * There are several races here: we avoid most of them by disabling
 114  * timer_active for the crucial part of the process.. That's a good
 115  * idea anyway. 
 116  *
 117  * The problem is that we have to output characters /both/ from interrupts
 118  * and from the normal write: the latter to be sure the interrupts start up
 119  * again. With serial lines, the interrupts can happen so often that the
 120  * races actually are noticeable.
 121  */
 122 static void send_intr(struct async_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
 123 {
 124         unsigned short port = info->port;
 125         int line = info->line;
 126         struct tty_queue * queue = &info->tty->write_q;
 127         int c, count = 0;
 128 
 129         if (info->tty->stopped)
 130                 return;
 131 
 132         if (info->type == PORT_16550A)
 133                 count = 16;     
 134         else
 135                 count = 1;
 136         
 137         rs_write_timeout &= ~(1 << line);
 138 
 139         if (inb_p(UART_LSR + info->port) & UART_LSR_THRE) {
 140                 while (count-- && !info->tty->stopped) {
 141                         if (queue->tail == queue->head)
 142                                 goto end_send;
 143                         c = queue->buf[queue->tail];
 144                         queue->tail++;
 145                         queue->tail &= TTY_BUF_SIZE-1;
 146                         outb(c, UART_TX + port);
 147                 }
 148         }
 149         info->timer = jiffies + info->timeout;
 150         if (info->timer < timer_table[RS_TIMER].expires)
 151                 timer_table[RS_TIMER].expires = info->timer;
 152         rs_write_timeout |= 1 << line;
 153         timer_active |= 1 << RS_TIMER;
 154 end_send:
 155         if (LEFT(queue) > WAKEUP_CHARS)
 156                 wake_up(&queue->proc_list);
 157 }
 158 
 159 static void receive_intr(struct async_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
 160 {
 161         unsigned short port = info->port;
 162         struct tty_queue * queue = &info->tty->read_q;
 163         int head = queue->head;
 164         int maxhead = (queue->tail-1) & (TTY_BUF_SIZE-1);
 165         int count = 0;
 166 
 167         rs_read_process &= ~(1 << info->line);
 168         do {
 169                 count++;
 170                 queue->buf[head] = inb(UART_TX + port);
 171                 if (head != maxhead) {
 172                         head++;
 173                         head &= TTY_BUF_SIZE-1;
 174                 }
 175         } while (inb(UART_LSR + port) & UART_LSR_DR);
 176         queue->head = head;
 177         rs_read_process |= 1 << info->line;
 178         timer_table[RS_TIMER].expires = 0;
 179         timer_active |= 1<<RS_TIMER;
 180 }
 181 
 182 static void line_status_intr(struct async_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
 183 {
 184         unsigned char status = inb(UART_LSR + info->port);
 185 
 186 /*      printk("line status: %02x\n",status);  */
 187 }
 188 
 189 static void modem_status_intr(struct async_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
 190 {
 191         unsigned char status = inb(UART_MSR + info->port);
 192 
 193         if (!(info->tty->termios->c_cflag & CLOCAL)) {
 194                 if (((status & (UART_MSR_DCD|UART_MSR_DDCD)) == UART_MSR_DDCD)
 195                     && info->tty->pgrp > 0)
 196                         kill_pg(info->tty->pgrp,SIGHUP,1);
 197 
 198                 if (info->tty->termios->c_cflag & CRTSCTS)
 199                         info->tty->stopped = !(status & UART_MSR_CTS);
 200 
 201                 if (!info->tty->stopped)
 202                         send_intr(info);
 203         }
 204 }
 205 
 206 static void (*jmp_table[4])(struct async_struct *) = {
 207         modem_status_intr,
 208         send_intr,
 209         receive_intr,
 210         line_status_intr
 211 };
 212 
 213 /*
 214  * This ISR handles the COM1-4 8250, 16450, and 16550A UART's.  It is
 215  * also called by the FourPort ISR, since the FourPort also uses the
 216  * same National Semiconduct UART's, with some interrupt multiplexing
 217  * thrown in.
 218  */
 219 static void UART_ISR_proc(async_ISR ISR, int line)
     /* [previous][next][first][last][top][bottom][index][help] */
 220 {
 221         unsigned char ident;
 222         struct async_struct * info = rs_table + line;
 223 
 224         if (!info || !info->tty || !info->port)
 225                   return;
 226         while (1) {
 227                 ident = inb(UART_IIR + info->port) & 7;
 228                 if (ident & 1)
 229                         return;
 230                 ident = ident >> 1;
 231                 jmp_table[ident](info);
 232         }
 233 }
 234 
 235 /*
 236  * Again, we disable interrupts to be sure there aren't any races:
 237  * see send_intr for details.
 238  */
 239 static inline void do_rs_write(struct async_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
 240 {
 241         if (!info->tty || !info->port)
 242                 return;
 243         if (EMPTY(&info->tty->write_q))
 244                 return;
 245         cli();
 246         send_intr(info);
 247         sti();
 248 }
 249 
 250 /*
 251  * Here is the fourport ISR
 252  */
 253 static void FourPort_ISR_proc(async_ISR ISR, int line)
     /* [previous][next][first][last][top][bottom][index][help] */
 254 {
 255         int             i;
 256         unsigned char ivec;
 257 
 258         ivec = ~inb(ISR->port) & 0x0F;
 259         for (i = line; ivec; i++) {
 260                 if (ivec & 1)
 261                         UART_ISR_proc(ISR, i);
 262                 ivec = ivec >> 1;
 263         }
 264 }
 265 
 266 /*
 267  * This is the serial driver's generic interrupt routine
 268  */
 269 static void rs_interrupt(int irq)
     /* [previous][next][first][last][top][bottom][index][help] */
 270 {
 271         async_ISR p = IRQ_ISR[irq];
 272 
 273         while (p) {
 274                 (p->ISR_proc)(p, p->line);
 275                 p = p->next_ISR;
 276         }
 277 }
 278 
 279 /*
 280  * This subroutine handles all of the timer functionality required for
 281  * the serial ports.
 282  */
 283 
 284 #define END_OF_TIME 0xffffffff
 285 static void rs_timer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 286 {
 287         unsigned long           mask;
 288         struct async_struct     *info;
 289         unsigned long           next_timeout;
 290 
 291         info = rs_table;
 292         next_timeout = END_OF_TIME;
 293         for (mask = 1 ; mask ; info++, mask <<= 1) {
 294                 if ((mask > rs_read_process) &&
 295                     (mask > rs_write_timeout))
 296                         break;
 297                 if (mask & rs_read_process) {
 298                         TTY_READ_FLUSH(info->tty);
 299                         rs_read_process &= ~mask;
 300                 }
 301                 if (mask & rs_write_timeout) {
 302                         if (info->timer > jiffies) {
 303                                 rs_write_timeout &= ~mask;
 304                                 do_rs_write(info);
 305                         }
 306                         if ((mask & rs_write_timeout) &&
 307                             (info->timer < next_timeout))
 308                                 next_timeout = info->timer;
 309                 }
 310         }
 311         if (next_timeout != END_OF_TIME) {
 312                 timer_table[RS_TIMER].expires = next_timeout;
 313                 timer_active |= 1 << RS_TIMER;
 314         }
 315 }
 316 
 317 static void init(struct async_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
 318 {
 319         unsigned char status1, status2, scratch, scratch2;
 320         unsigned short port = info->port;
 321 
 322         /* 
 323          * Check to see if a UART is really there.  
 324          */
 325         scratch = inb_p(UART_MCR + port);
 326         outb_p(UART_MCR_LOOP | scratch, UART_MCR + port);
 327         scratch2 = inb_p(UART_MSR + port);
 328         outb_p(UART_MCR_LOOP | 0x0A, UART_MCR + port);
 329         status1 = inb_p(UART_MSR + port) & 0xF0;
 330         outb_p(scratch, UART_MCR + port);
 331         outb_p(scratch2, UART_MSR + port);
 332         if (status1 != 0x90) {
 333                 info->type = PORT_UNKNOWN;
 334                 return;
 335         }
 336         
 337         if (!(info->flags & ASYNC_NOSCRATCH)) {
 338                 scratch = inb(UART_SCR + port);
 339                 outb_p(0xa5, UART_SCR + port);
 340                 status1 = inb(UART_SCR + port);
 341                 outb_p(0x5a, UART_SCR + port);
 342                 status2 = inb(UART_SCR + port);
 343                 outb_p(scratch, UART_SCR + port);
 344         } else {
 345                 status1 = 0xa5;
 346                 status2 = 0x5a;
 347         }
 348         if (status1 == 0xa5 && status2 == 0x5a) {
 349                 outb_p(UART_FCR_ENABLE_FIFO, UART_FCR + port);
 350                 scratch = inb(UART_IIR + port) >> 6;
 351                 info->xmit_fifo_size = 1;
 352                 switch (scratch) {
 353                         case 0:
 354                                 info->type = PORT_16450;
 355                                 break;
 356                         case 1:
 357                                 info->type = PORT_UNKNOWN;
 358                                 break;
 359                         case 2:
 360                                 info->type = PORT_16550;
 361                                 break;
 362                         case 3:
 363                                 info->type = PORT_16550A;
 364                                 info->xmit_fifo_size = 16;
 365                                 break;
 366                 }
 367         } else
 368                 info->type = PORT_8250;
 369         change_speed(info->line);
 370         outb_p(0x00,            UART_IER + port);       /* disable all intrs */
 371         outb_p(0x00,            UART_MCR + port);  /* reset DTR,RTS,OUT_2 */
 372         (void)inb(UART_RX + port);     /* read data port to reset things (?) */
 373 }
 374 
 375 /*
 376  * This routine gets called when tty_write has put something into
 377  * the write_queue. It must check wheter the queue is empty, and
 378  * set the interrupt register accordingly
 379  */
 380 void rs_write(struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
 381 {
 382         do_rs_write(rs_table+DEV_TO_SL(tty->line));
 383 }
 384 
 385 /*
 386  * This routine is called when the serial port gets closed.  First, we
 387  * wait for the last remaining data to be sent.  Then, we unlink its
 388  * ISR from the interrupt chain if necessary, and we free that IRQ if
 389  * nothing is left in the chain.
 390  */
 391 static void rs_close(struct tty_struct *tty, struct file * filp)
     /* [previous][next][first][last][top][bottom][index][help] */
 392 {
 393         struct async_struct * info;
 394         async_ISR               ISR;
 395         int irq, line;
 396 
 397         line = DEV_TO_SL(tty->line);
 398         if ((line < 0) || (line >= NR_PORTS))
 399                 return;
 400         wait_until_sent(tty);
 401         info = rs_table + line;
 402         if (!info->port)
 403                 return;
 404         info->tty = 0;
 405         outb_p(0x00, UART_IER + info->port);    /* disable all intrs */
 406         outb_p(0x00, UART_MCR + info->port);    /* reset DTR, RTS, */
 407         outb(UART_FCR_CLEAR_CMD, UART_FCR + info->port); /* disable FIFO's */
 408         ISR = info->ISR;
 409         irq = ISR->irq;
 410         if (irq == 2)
 411                 irq = 9;
 412         if (--ISR->refcnt == 0) {
 413                 if (ISR->next_ISR)
 414                         ISR->next_ISR->prev_ISR = ISR->prev_ISR;
 415                 if (ISR->prev_ISR)
 416                         ISR->prev_ISR->next_ISR = ISR->next_ISR;
 417                 else
 418                         IRQ_ISR[irq] = ISR->next_ISR;
 419                 if (!IRQ_ISR[irq])
 420                         free_irq(irq);
 421         }
 422 }
 423 
 424 static void startup(struct async_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
 425 {
 426         unsigned short port = info->port;
 427         unsigned short ICP;
 428 
 429         /*
 430          * First, clear the FIFO buffers and disable them
 431          */
 432         if (info->type == PORT_16550A)
 433                 outb_p(UART_FCR_CLEAR_CMD, UART_FCR + port);
 434 
 435         /*
 436          * Next, clear the interrupt registers.
 437          */
 438         (void)inb_p(UART_LSR + port);
 439         (void)inb_p(UART_RX + port);
 440         (void)inb_p(UART_IIR + port);
 441         (void)inb_p(UART_MSR + port);
 442 
 443         /*
 444          * Now, initialize the UART 
 445          */
 446         outb_p(UART_LCR_WLEN8, UART_LCR + port);        /* reset DLAB */
 447         if (info->flags & ASYNC_FOURPORT) 
 448                 outb_p(UART_MCR_DTR | UART_MCR_RTS, 
 449                        UART_MCR + port);
 450         else
 451                 outb_p(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, 
 452                        UART_MCR + port);
 453         
 454         /*
 455          * Enable FIFO's if necessary
 456          */
 457         if (info->type == PORT_16550A) {
 458                 outb_p(UART_FCR_SETUP_CMD, UART_FCR + port);
 459                 info->xmit_fifo_size = 16;
 460         } else {
 461                 info->xmit_fifo_size = 1;
 462         }
 463 
 464         /*
 465          * Finally, enable interrupts
 466          */
 467         outb_p(0x0f,UART_IER + port);   /* enable all intrs */
 468         if (info->flags & ASYNC_FOURPORT) {
 469                 /* Enable interrupts on the AST Fourport board */
 470                 ICP = (port & 0xFE0) | 0x01F;
 471                 outb_p(0x80, ICP);
 472                 (void) inb(ICP);
 473         }
 474 }
 475 
 476 void change_speed(unsigned int line)
     /* [previous][next][first][last][top][bottom][index][help] */
 477 {
 478         struct async_struct * info;
 479         unsigned short port;
 480         int     quot = 0;
 481         unsigned cflag,cval;
 482         int     i;
 483 
 484         if (line >= NR_PORTS)
 485                 return;
 486         info = rs_table + line;
 487         cflag = info->tty->termios->c_cflag;
 488         if (!(port = info->port))
 489                 return;
 490         i = cflag & CBAUD;
 491         if (i == 15) {
 492                 if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
 493                         i += 1;
 494                 if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
 495                         i += 2;
 496                 if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
 497                         quot = info->custom_divisor;
 498         }
 499         if (quot) {
 500                 info->timeout = ((info->xmit_fifo_size*HZ*15*quot) /
 501                                  info->baud_base) + 2;
 502         } else if (baud_table[i] == 134) {
 503                 quot = (2*info->baud_base / 269);
 504                 info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
 505         } else if (baud_table[i]) {
 506                 quot = info->baud_base / baud_table[i];
 507                 info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
 508         } else {
 509                 quot = 0;
 510                 info->timeout = 0;
 511         }
 512         if (!quot)
 513                 outb(0x00,UART_MCR + port);
 514         else if (!inb(UART_MCR + port))
 515                 startup(info);
 516 /* byte size and parity */
 517         cval = cflag & (CSIZE | CSTOPB);
 518         cval >>= 4;
 519         if (cflag & PARENB)
 520                 cval |= 8;
 521         if (!(cflag & PARODD))
 522                 cval |= 16;
 523         cli();
 524         outb_p(cval | UART_LCR_DLAB, UART_LCR + port);  /* set DLAB */
 525         outb_p(quot & 0xff, UART_DLL + port);   /* LS of divisor */
 526         outb_p(quot >> 8, UART_DLM + port);     /* MS of divisor */
 527         outb(cval, UART_LCR + port);            /* reset DLAB */
 528         sti();
 529 }
 530 
 531 static int get_serial_info(struct async_struct * info,
     /* [previous][next][first][last][top][bottom][index][help] */
 532                            struct serial_struct * retinfo)
 533 {
 534         struct serial_struct tmp;
 535   
 536         if (!retinfo)
 537                 return -EFAULT;
 538         tmp.type = info->type;
 539         tmp.line = info->line;
 540         tmp.port = info->port;
 541         tmp.irq = info->ISR->irq;
 542 /*      tmp.flags = info->flags; */
 543         memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
 544         return 0;
 545 }
 546 
 547 static int set_serial_info(struct async_struct * info,
     /* [previous][next][first][last][top][bottom][index][help] */
 548                            struct serial_struct * new_info)
 549 {
 550         struct serial_struct tmp;
 551         async_ISR               ISR;
 552         unsigned int            new_port;
 553         unsigned int            irq,new_irq;
 554         int                     retval;
 555         struct                  sigaction sa;
 556 
 557         if (!suser())
 558                 return -EPERM;
 559         if (!new_info)
 560                 return -EFAULT;
 561         memcpy_fromfs(&tmp,new_info,sizeof(tmp));
 562         new_port = tmp.port;
 563         new_irq = tmp.irq;
 564         if (new_irq > 15 || new_port > 0xffff)
 565                 return -EINVAL;
 566         if (new_irq == 2)
 567                 new_irq = 9;
 568         ISR = info->ISR;
 569         irq = ISR->irq;
 570         if (irq == 2)
 571                 irq = 9;
 572         if (!new_irq)
 573                 new_irq = irq;
 574         if (!new_port)
 575                 new_port = info->port;
 576         if (irq != new_irq) {
 577                 /*
 578                  * We need to change the IRQ for this board.  OK, if
 579                  * necessary, first we try to grab the new IRQ for
 580                  * serial interrupts.
 581                  */
 582                 if (!IRQ_ISR[new_irq]) {
 583                         sa.sa_handler = rs_interrupt;
 584                         sa.sa_flags = (SA_INTERRUPT);
 585                         sa.sa_mask = 0;
 586                         sa.sa_restorer = NULL;
 587                         retval = irqaction(new_irq,&sa);
 588                         if (retval)
 589                                 return retval;
 590                 }
 591 
 592                 /*
 593                  * If the new IRQ is OK, now we unlink the ISR from
 594                  * the existing interrupt chain.
 595                  */
 596                 if (ISR->next_ISR)
 597                         ISR->next_ISR->prev_ISR = ISR->prev_ISR;
 598                 if (ISR->prev_ISR)
 599                         ISR->prev_ISR->next_ISR = ISR->next_ISR;
 600                 else
 601                         IRQ_ISR[irq] = ISR->next_ISR;
 602                 if (!IRQ_ISR[irq])
 603                         free_irq(irq);
 604 
 605                 /*
 606                  * Now link in the interrupt to the new interrupt chain.
 607                  */
 608                 ISR->prev_ISR = 0;
 609                 ISR->next_ISR = IRQ_ISR[new_irq];
 610                 if (ISR->next_ISR)
 611                         ISR->next_ISR->prev_ISR = ISR;
 612                 IRQ_ISR[new_irq] = ISR;
 613                 ISR->irq = new_irq;
 614         }
 615         cli();
 616         if (new_port != info->port) {
 617                 outb_p(0x00, UART_IER + info->port);    /* disable all intrs */
 618                 outb(0x00, UART_MCR + info->port);      /* reset DTR, RTS, */
 619                 info->port = new_port;
 620                 init(info);
 621                 startup(info);
 622         }
 623         sti();
 624         return 0;
 625 }
 626 
 627 static int get_modem_info(struct async_struct * info, unsigned int *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 628 {
 629         unsigned port;
 630         unsigned char control, status;
 631         unsigned int result;
 632 
 633         port = info->port;
 634         control = inb(UART_MCR + port);
 635         status = inb(UART_MSR + port);
 636         result =  ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
 637                 | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
 638                 | ((status  & UART_MSR_DCD) ? TIOCM_CAR : 0)
 639                 | ((status  & UART_MSR_RI) ? TIOCM_RNG : 0)
 640                 | ((status  & UART_MSR_DSR) ? TIOCM_DSR : 0)
 641                 | ((status  & UART_MSR_CTS) ? TIOCM_CTS : 0);
 642         put_fs_long(result,(unsigned long *) value);
 643         return 0;
 644 }
 645 
 646 static int set_modem_info(struct async_struct * info, unsigned int cmd,
     /* [previous][next][first][last][top][bottom][index][help] */
 647                           unsigned int *value)
 648 {
 649         unsigned port;
 650         unsigned char control;
 651         unsigned int arg = get_fs_long((unsigned long *) value);
 652         
 653         port = info->port;
 654         control = inb(UART_MCR + port);
 655 
 656         switch (cmd) {
 657                 case TIOCMBIS:
 658                         if (arg & TIOCM_RTS)
 659                                 control |= UART_MCR_RTS;
 660                         if (arg & TIOCM_DTR)
 661                                 control |= UART_MCR_DTR;
 662                         break;
 663                 case TIOCMBIC:
 664                         if (arg & TIOCM_RTS)
 665                                 control &= ~UART_MCR_RTS;
 666                         if (arg & TIOCM_DTR)
 667                                 control &= ~UART_MCR_DTR;
 668                         break;
 669                 case TIOCMSET:
 670                         control = (control & ~0x03)
 671                                 | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
 672                                 | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0);
 673                         break;
 674                 default:
 675                         return -EINVAL;
 676         }
 677         outb(UART_MCR + port,control);
 678         return 0;
 679 }
 680 
 681 static int rs_ioctl(struct tty_struct *tty, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
 682                     unsigned int cmd, unsigned int arg)
 683 {
 684         int     line;
 685         struct async_struct * info;
 686 
 687         line = DEV_TO_SL(tty->line);
 688         if (line < 0 || line >= NR_PORTS)
 689                 return -ENODEV;
 690         info = rs_table + line;
 691         
 692         switch (cmd) {
 693                 case TCSBRK:
 694                         wait_until_sent(tty);
 695                         if (!arg)
 696                                 send_break(info);
 697                         return 0;
 698                 case TIOCMGET:
 699                         verify_area((void *) arg,sizeof(unsigned int *));
 700                         return get_modem_info(info, (unsigned int *) arg);
 701                 case TIOCMBIS:
 702                 case TIOCMBIC:
 703                 case TIOCMSET:
 704                         return set_modem_info(info, cmd, (unsigned int *) arg);
 705                 case TIOCGSERIAL:
 706                         verify_area((void *) arg,sizeof(struct serial_struct));
 707                         return get_serial_info(info,
 708                                                (struct serial_struct *) arg);
 709                 case TIOCSSERIAL:
 710                         return set_serial_info(info,
 711                                                (struct serial_struct *) arg);
 712                 
 713         default:
 714                 return -EINVAL;
 715         }
 716         return 0;
 717 }       
 718 
 719 /*
 720  * This routine is called whenever a serial port is opened.  It
 721  * enables interrupts for a serial port, linking in its interrupt into
 722  * the ISR chain.   It also performs the serial-speicific
 723  * initalization for the tty structure.
 724  */
 725 int rs_open(struct tty_struct *tty, struct file * filp)
     /* [previous][next][first][last][top][bottom][index][help] */
 726 {
 727         struct async_struct     *info;
 728         async_ISR               ISR;
 729         int                     irq, retval, line;
 730         struct sigaction        sa;
 731 
 732         if (!tty)
 733                 return -ENODEV;
 734         if (tty->count > 1)
 735                 return 0;               /* We've already been initialized */
 736         line = DEV_TO_SL(tty->line);
 737         if ((line < 0) || (line >= NR_PORTS))
 738                 return -ENODEV;
 739         info = rs_table + line;
 740         if (!info->port)
 741                 return -ENODEV;
 742         info->tty = tty;
 743         tty->write = rs_write;
 744         tty->close = rs_close;
 745         tty->ioctl = rs_ioctl;
 746         ISR = info->ISR;
 747         irq = ISR->irq;
 748         if (irq == 2)
 749                 irq = 9;
 750         if (!IRQ_ISR[irq]) {
 751                 sa.sa_handler = rs_interrupt;
 752                 sa.sa_flags = (SA_INTERRUPT);
 753                 sa.sa_mask = 0;
 754                 sa.sa_restorer = NULL;
 755                 retval = irqaction(irq,&sa);
 756                 if (retval)
 757                         return retval;
 758         }
 759         if (!ISR->refcnt++) {
 760                 /*
 761                  * If this is the first time we're using this ISR,
 762                  * link it in.
 763                  */
 764                 ISR->prev_ISR = 0;
 765                 ISR->next_ISR = IRQ_ISR[irq];
 766                 if (ISR->next_ISR)
 767                         ISR->next_ISR->prev_ISR = ISR;
 768                 IRQ_ISR[irq] = ISR;
 769         }
 770         startup(info);
 771         return 0;
 772 }
 773 
 774 long rs_init(long kmem_start)
     /* [previous][next][first][last][top][bottom][index][help] */
 775 {
 776         int i;
 777         struct async_struct * info;
 778 
 779         timer_table[RS_TIMER].fn = rs_timer;
 780         timer_table[RS_TIMER].expires = 0;
 781         for (i = 0; i < 16; i++) {
 782                 IRQ_ISR[i] = 0;
 783         }
 784         for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
 785                 info->line = i;
 786                 info->tty = 0;
 787                 info->type = PORT_UNKNOWN;
 788                 info->timer = 0;
 789                 info->custom_divisor = 0;
 790                 if (!info->ISR->line) {
 791                         info->ISR->line = i;
 792                         info->ISR->refcnt = 0;
 793                         info->ISR->next_ISR = 0;
 794                         info->ISR->prev_ISR = 0;
 795                 }
 796                 init(info);
 797                 if (info->type == PORT_UNKNOWN)
 798                         continue;
 799                 printk("ttys%d%s at 0x%04x (irq = %d)", info->line, 
 800                        (info->flags & ASYNC_FOURPORT) ? " FourPort" : "",
 801                        info->port, info->ISR->irq);
 802                 switch (info->type) {
 803                         case PORT_8250:
 804                                 printk(" is a 8250\n");
 805                                 break;
 806                         case PORT_16450:
 807                                 printk(" is a 16450\n");
 808                                 break;
 809                         case PORT_16550:
 810                                 printk(" is a 16550\n");
 811                                 break;
 812                         case PORT_16550A:
 813                                 printk(" is a 16550A\n");
 814                                 break;
 815                         default:
 816                                 printk("\n");
 817                                 break;
 818                 }
 819         }
 820         return kmem_start;
 821 }
 822 

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