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. check_tty
  7. do_rs_write
  8. com1_IRQ
  9. com2_IRQ
  10. com3_IRQ
  11. com4_IRQ
  12. com1_timer
  13. com2_timer
  14. com3_timer
  15. com4_timer
  16. com1_timeout
  17. com2_timeout
  18. com3_timeout
  19. com4_timeout
  20. init
  21. serial_close
  22. startup
  23. change_speed
  24. serial_open
  25. get_serial_info
  26. set_serial_info
  27. rs_init
  28. rs_write

   1 /*
   2  *  linux/kernel/serial.c
   3  *
   4  *  Copyright (C) 1991, 1992  Linus Torvalds
   5  */
   6 
   7 /*
   8  *      serial.c
   9  *
  10  * This module implements the rs232 io functions
  11  *      void rs_write(struct tty_struct * queue);
  12  *      long rs_init(long);
  13  * and all interrupts pertaining to serial IO.
  14  */
  15 
  16 #include <linux/errno.h>
  17 #include <linux/signal.h>
  18 #include <linux/sched.h>
  19 #include <linux/timer.h>
  20 #include <linux/tty.h>
  21 
  22 #include <asm/system.h>
  23 #include <asm/io.h>
  24 #include <asm/segment.h>
  25 
  26 #define WAKEUP_CHARS (3*TTY_BUF_SIZE/4)
  27 
  28 struct serial_struct serial_table[NR_SERIALS] = {
  29         { PORT_UNKNOWN, 0, 0x3F8, 4, NULL},
  30         { PORT_UNKNOWN, 1, 0x2F8, 3, NULL},
  31         { PORT_UNKNOWN, 2, 0x3E8, 4, NULL},
  32         { PORT_UNKNOWN, 3, 0x2E8, 3, NULL},
  33 };
  34 
  35 void send_break(unsigned int line)
     /* [previous][next][first][last][top][bottom][index][help] */
  36 {
  37         unsigned short port;
  38         struct serial_struct * info;
  39 
  40         if (line >= NR_SERIALS)
  41                 return;
  42         info = serial_table + line;
  43         if (!(port = info->port))
  44                 return;
  45         port += 3;
  46         current->state = TASK_INTERRUPTIBLE;
  47         current->timeout = jiffies + 25;
  48         outb_p(inb_p(port) | 0x40,port);
  49         schedule();
  50         outb_p(inb_p(port) & 0xbf,port);
  51 }
  52 
  53 /*
  54  * There are several races here: we avoid most of them by disabling timer_active
  55  * for the crucial part of the process.. That's a good idea anyway.
  56  *
  57  * The problem is that we have to output characters /both/ from interrupts
  58  * and from the normal write: the latter to be sure the interrupts start up
  59  * again. With serial lines, the interrupts can happen so often that the
  60  * races actually are noticeable.
  61  */
  62 static void send_intr(struct serial_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
  63 {
  64         unsigned short port = info->port;
  65         unsigned int timer = SER1_TIMEOUT + info->line;
  66         struct tty_queue * queue = info->tty->write_q;
  67         int c, i = 0;
  68 
  69         if (info->tty->stopped) return;
  70 
  71         timer_active &= ~(1 << timer);
  72         while (inb_p(info->port+5) & 0x20) {
  73                 if (queue->tail == queue->head)
  74                         goto end_send;
  75                 c = queue->buf[queue->tail];
  76                 queue->tail++;
  77                 queue->tail &= TTY_BUF_SIZE-1;
  78                 outb(c,port);
  79                 if ((info->type != PORT_16550A) || (++i >= 14) || info->tty->stopped)
  80                         break;
  81         }
  82         timer_table[timer].expires = jiffies + 10;
  83         timer_active |= 1 << timer;
  84 end_send:
  85         if (LEFT(queue) > WAKEUP_CHARS)
  86                 wake_up(&queue->proc_list);
  87 }
  88 
  89 static void receive_intr(struct serial_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
  90 {
  91         unsigned short port = info->port;
  92         struct tty_queue * queue = info->tty->read_q;
  93         int head = queue->head;
  94         int maxhead = (queue->tail-1) & (TTY_BUF_SIZE-1);
  95 
  96         timer_active &= ~((1<<SER1_TIMER)<<info->line);
  97         do {
  98                 queue->buf[head] = inb(port);
  99                 if (head != maxhead) {
 100                         head++;
 101                         head &= TTY_BUF_SIZE-1;
 102                 }
 103         } while (inb(port+5) & 1);
 104         queue->head = head;
 105         timer_active |= (1<<SER1_TIMER)<<info->line;
 106 }
 107 
 108 static void line_status_intr(struct serial_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
 109 {
 110         unsigned char status = inb(info->port+5);
 111 
 112 /*      printk("line status: %02x\n",status); */
 113 }
 114 
 115 static void modem_status_intr(struct serial_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
 116 {
 117         unsigned char status = inb(info->port+6);
 118 
 119         if (!(info->tty->termios.c_cflag & CLOCAL)) {
 120                 if ((status & 0x88) == 0x08 && info->tty->pgrp > 0)
 121                         kill_pg(info->tty->pgrp,SIGHUP,1);
 122 
 123                 if (info->tty->termios.c_cflag & CRTSCTS)
 124                         info->tty->stopped = !(status & 0x10);
 125 
 126                 if (!info->tty->stopped)
 127                         send_intr(info);
 128         }
 129 }
 130 
 131 static void (*jmp_table[4])(struct serial_struct *) = {
 132         modem_status_intr,
 133         send_intr,
 134         receive_intr,
 135         line_status_intr
 136 };
 137 
 138 static void check_tty(struct serial_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
 139 {
 140         unsigned char ident;
 141 
 142         if (!info || !info->tty || !info->port)
 143                 return;
 144         while (1) {
 145                 ident = inb(info->port+2) & 7;
 146                 if (ident & 1)
 147                         return;
 148                 ident >>= 1;
 149                 if (ident > 3)
 150                         return;
 151                 jmp_table[ident](info);
 152         }
 153 }
 154 
 155 /*
 156  * Again, we disable interrupts to be sure there aren't any races:
 157  * see send_intr for details.
 158  */
 159 static inline void do_rs_write(struct serial_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
 160 {
 161         if (!info->tty || !info->port)
 162                 return;
 163         if (!info->tty->write_q || EMPTY(info->tty->write_q))
 164                 return;
 165         cli();
 166         send_intr(info);
 167         sti();
 168 }
 169 
 170 /*
 171  * IRQ routines: one per line
 172  */
 173 static void com1_IRQ(int unused)
     /* [previous][next][first][last][top][bottom][index][help] */
 174 {
 175         check_tty(serial_table+0);
 176 }
 177 
 178 static void com2_IRQ(int unused)
     /* [previous][next][first][last][top][bottom][index][help] */
 179 {
 180         check_tty(serial_table+1);
 181 }
 182 
 183 static void com3_IRQ(int unused)
     /* [previous][next][first][last][top][bottom][index][help] */
 184 {
 185         check_tty(serial_table+2);
 186 }
 187 
 188 static void com4_IRQ(int unused)
     /* [previous][next][first][last][top][bottom][index][help] */
 189 {
 190         check_tty(serial_table+3);
 191 }
 192 
 193 /*
 194  * Receive timer routines: one per line
 195  */
 196 static void com1_timer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 197 {
 198         TTY_READ_FLUSH(tty_table+64);
 199 }
 200 
 201 static void com2_timer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 202 {
 203         TTY_READ_FLUSH(tty_table+65);
 204 }
 205 
 206 static void com3_timer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 207 {
 208         TTY_READ_FLUSH(tty_table+66);
 209 }
 210 
 211 static void com4_timer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 212 {
 213         TTY_READ_FLUSH(tty_table+67);
 214 }
 215 
 216 /*
 217  * Send timeout routines: one per line
 218  */
 219 static void com1_timeout(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 220 {
 221         do_rs_write(serial_table);
 222 }
 223 
 224 static void com2_timeout(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 225 {
 226         do_rs_write(serial_table + 1);
 227 }
 228 
 229 static void com3_timeout(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 230 {
 231         do_rs_write(serial_table + 2);
 232 }
 233 
 234 static void com4_timeout(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 235 {
 236         do_rs_write(serial_table + 3);
 237 }
 238 
 239 static void init(struct serial_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
 240 {
 241         unsigned char status1, status2, scratch;
 242         unsigned short port = info->port;
 243 
 244         if (inb(port+5) == 0xff) {
 245                 info->type = PORT_UNKNOWN;
 246                 return;
 247         }
 248         
 249         scratch = inb(port+7);
 250         outb_p(0xa5, port+7);
 251         status1 = inb(port+7);
 252         outb_p(0x5a, port+7);
 253         status2 = inb(port+7);
 254         if (status1 == 0xa5 && status2 == 0x5a) {
 255                 outb_p(scratch, port+7);
 256                 outb_p(0x01, port+2);
 257                 scratch = inb(port+2) >> 6;
 258                 switch (scratch) {
 259                         case 0:
 260                                 info->type = PORT_16450;
 261                                 break;
 262                         case 1:
 263                                 info->type = PORT_UNKNOWN;
 264                                 break;
 265                         case 2:
 266                                 info->type = PORT_16550;
 267                                 outb_p(0x00, port+2);
 268                                 break;
 269                         case 3:
 270                                 info->type = PORT_16550A;
 271                                 outb_p(0xc7, port+2);
 272                                 break;
 273                 }
 274         } else
 275                 info->type = PORT_8250;
 276         outb_p(0x80,port+3);    /* set DLAB of line control reg */
 277         outb_p(0x30,port);      /* LS of divisor (48 -> 2400 bps) */
 278         outb_p(0x00,port+1);    /* MS of divisor */
 279         outb_p(0x03,port+3);    /* reset DLAB */
 280         outb_p(0x00,port+4);    /* reset DTR,RTS, OUT_2 */
 281         outb_p(0x00,port+1);    /* disable all intrs */
 282         (void)inb(port);        /* read data port to reset things (?) */
 283 }
 284 
 285 void serial_close(unsigned line, struct file * filp)
     /* [previous][next][first][last][top][bottom][index][help] */
 286 {
 287         struct serial_struct * info;
 288         int irq;
 289 
 290         if (line >= NR_SERIALS)
 291                 return;
 292         info = serial_table + line;
 293         if (!info->port)
 294                 return;
 295         outb(0x00,info->port+4);        /* reset DTR, RTS, */
 296         irq = info->irq;
 297         if (irq == 2)
 298                 irq = 9;
 299         free_irq(irq);
 300 }
 301 
 302 static void startup(unsigned short port)
     /* [previous][next][first][last][top][bottom][index][help] */
 303 {
 304         int i;
 305 
 306         outb_p(0x03,port+3);    /* reset DLAB */
 307         outb_p(0x0b,port+4);    /* set DTR,RTS, OUT_2 */
 308         outb_p(0x0f,port+1);    /* enable all intrs */
 309         inb_p(port+2);
 310         inb_p(port+6);
 311         inb_p(port+2);
 312         inb_p(port+5);
 313         for (i = 0; i < 16 ; i++) {
 314                 inb_p(port+0);
 315                 if (!(inb_p(port+5) & 1))
 316                         break;
 317         }
 318         inb_p(port+2);
 319         inb_p(port+5);
 320 }
 321 
 322 void change_speed(unsigned int line)
     /* [previous][next][first][last][top][bottom][index][help] */
 323 {
 324         struct serial_struct * info;
 325         unsigned short port,quot;
 326         unsigned cflag,cval;
 327         static unsigned short quotient[] = {
 328                 0, 2304, 1536, 1047, 857,
 329                 768, 576, 384, 192, 96,
 330                 64, 48, 24, 12, 6, 3
 331         };
 332 
 333         if (line >= NR_SERIALS)
 334                 return;
 335         info = serial_table + line;
 336         cflag = info->tty->termios.c_cflag;
 337         if (!(port = info->port))
 338                 return;
 339         quot = quotient[cflag & CBAUD];
 340         if (!quot)
 341                 outb(0x00,port+4);
 342         else if (!inb(port+4))
 343                 startup(port);
 344 /* byte size and parity */
 345         cval = cflag & (CSIZE | CSTOPB);
 346         cval >>= 4;
 347         if (cflag & PARENB)
 348                 cval |= 8;
 349         if (!(cflag & PARODD))
 350                 cval |= 16;
 351         cli();
 352         outb_p(cval | 0x80,port+3);     /* set DLAB */
 353         outb_p(quot & 0xff,port);       /* LS of divisor */
 354         outb_p(quot >> 8,port+1);       /* MS of divisor */
 355         outb(cval,port+3);              /* reset DLAB */
 356         sti();
 357 }
 358 
 359 static void (*serial_handler[NR_SERIALS])(int) = {
 360         com1_IRQ,com2_IRQ,com3_IRQ,com4_IRQ
 361 };
 362 
 363 /*
 364  * this routine enables interrupts on 'line', and disables them for any
 365  * other serial line that shared the same IRQ. Braindamaged AT hardware.
 366  */
 367 int serial_open(unsigned line, struct file * filp)
     /* [previous][next][first][last][top][bottom][index][help] */
 368 {
 369         struct serial_struct * info;
 370         int irq,retval;
 371         unsigned short port;
 372         struct sigaction sa;
 373 
 374         sa.sa_handler = serial_handler[line];
 375         sa.sa_flags = SA_INTERRUPT;
 376         sa.sa_mask = 0;
 377         sa.sa_restorer = NULL;
 378         if (line >= NR_SERIALS)
 379                 return -ENODEV;
 380         info = serial_table + line;
 381         if (!(port = info->port))
 382                 return -ENODEV;
 383         irq = info->irq;
 384         if (irq == 2)
 385                 irq = 9;
 386         if (retval = irqaction(irq,&sa))
 387                 return retval;
 388         startup(port);
 389         return 0;
 390 }
 391 
 392 int get_serial_info(unsigned int line, struct serial_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
 393 {
 394         if (line >= NR_SERIALS)
 395                 return -ENODEV;
 396         if (!info)
 397                 return -EFAULT;
 398         memcpy_tofs(info,serial_table+line,sizeof(*info));
 399         return 0;
 400 }
 401 
 402 int set_serial_info(unsigned int line, struct serial_struct * info)
     /* [previous][next][first][last][top][bottom][index][help] */
 403 {
 404         struct serial_struct tmp;
 405         unsigned new_port;
 406         unsigned irq,new_irq;
 407         int retval;
 408         void (*handler)(int) = serial_handler[line];
 409 
 410         if (!suser())
 411                 return -EPERM;
 412         if (line >= NR_SERIALS)
 413                 return -ENODEV;
 414         if (!info)
 415                 return -EFAULT;
 416         memcpy_fromfs(&tmp,info,sizeof(tmp));
 417         info = serial_table + line;
 418         if (!(new_port = tmp.port))
 419                 new_port = info->port;
 420         if (!(new_irq = tmp.irq))
 421                 new_irq = info->irq;
 422         if (new_irq > 15 || new_port > 0xffff)
 423                 return -EINVAL;
 424         if (new_irq == 2)
 425                 new_irq = 9;
 426         irq = info->irq;
 427         if (irq == 2)
 428                 irq = 9;
 429         if (irq != new_irq) {
 430                 retval = request_irq(new_irq,handler);
 431                 if (retval)
 432                         return retval;
 433                 info->irq = new_irq;
 434                 free_irq(irq);
 435         }
 436         cli();
 437         if (new_port != info->port) {
 438                 outb(0x00,info->port+4);        /* reset DTR, RTS, */
 439                 info->port = new_port;
 440                 init(info);
 441                 startup(new_port);
 442         }
 443         sti();
 444         return 0;
 445 }
 446 
 447 long rs_init(long kmem_start)
     /* [previous][next][first][last][top][bottom][index][help] */
 448 {
 449         int i;
 450         struct serial_struct * info;
 451 
 452 /* SERx_TIMER timers are used for receiving: timeout is always 0 (immediate) */
 453         timer_table[SER1_TIMER].fn = com1_timer;
 454         timer_table[SER1_TIMER].expires = 0;
 455         timer_table[SER2_TIMER].fn = com2_timer;
 456         timer_table[SER2_TIMER].expires = 0;
 457         timer_table[SER3_TIMER].fn = com3_timer;
 458         timer_table[SER3_TIMER].expires = 0;
 459         timer_table[SER4_TIMER].fn = com4_timer;
 460         timer_table[SER4_TIMER].expires = 0;
 461 /* SERx_TIMEOUT timers are used for writing: prevent serial lockups */
 462         timer_table[SER1_TIMEOUT].fn = com1_timeout;
 463         timer_table[SER1_TIMEOUT].expires = 0;
 464         timer_table[SER2_TIMEOUT].fn = com2_timeout;
 465         timer_table[SER2_TIMEOUT].expires = 0;
 466         timer_table[SER3_TIMEOUT].fn = com3_timeout;
 467         timer_table[SER3_TIMEOUT].expires = 0;
 468         timer_table[SER4_TIMEOUT].fn = com4_timeout;
 469         timer_table[SER4_TIMEOUT].expires = 0;
 470         for (i = 0, info = serial_table; i < NR_SERIALS; i++,info++) {
 471                 info->tty = (tty_table+64) + i;
 472                 init(info);
 473                 if (info->type == PORT_UNKNOWN)
 474                         continue;
 475                 printk("serial port at 0x%04x (irq = %d)",info->port,info->irq);
 476                 switch (info->type) {
 477                         case PORT_8250:
 478                                 printk(" is a 8250\n");
 479                                 break;
 480                         case PORT_16450:
 481                                 printk(" is a 16450\n");
 482                                 break;
 483                         case PORT_16550:
 484                                 printk(" is a 16550\n");
 485                                 break;
 486                         case PORT_16550A:
 487                                 printk(" is a 16550A\n");
 488                                 break;
 489                         default:
 490                                 printk("\n");
 491                                 break;
 492                 }
 493         }
 494         return kmem_start;
 495 }
 496 
 497 /*
 498  * This routine gets called when tty_write has put something into
 499  * the write_queue. It must check wheter the queue is empty, and
 500  * set the interrupt register accordingly
 501  *
 502  *      void _rs_write(struct tty_struct * tty);
 503  */
 504 void rs_write(struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
 505 {
 506         int line = tty - tty_table - 64;
 507 
 508         do_rs_write(serial_table+line);
 509 }

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