root/kernel/chr_drv/serial.c

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

DEFINITIONS

This source file includes following definitions.
  1. modem_status_intr
  2. send_intr
  3. receive_intr
  4. line_status_intr
  5. check_tty
  6. do_IRQ3
  7. do_IRQ4
  8. com1_timer
  9. com2_timer
  10. com3_timer
  11. com4_timer
  12. do_rs_write
  13. com1_timeout
  14. com2_timeout
  15. com3_timeout
  16. com4_timeout
  17. init
  18. serial_open
  19. rs_init
  20. rs_write

   1 /*
   2  *  linux/kernel/serial.c
   3  *
   4  *  (C) 1991  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  *      void rs_init(void);
  13  * and all interrupts pertaining to serial IO.
  14  */
  15 
  16 #include <signal.h>
  17  
  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 
  25 #define WAKEUP_CHARS (3*TTY_BUF_SIZE/4)
  26 
  27 extern void IRQ3_interrupt(void);
  28 extern void IRQ4_interrupt(void);
  29 
  30 static void modem_status_intr(unsigned line, unsigned port, struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
  31 {
  32         unsigned char status = inb(port+6);
  33 
  34         if ((status & 0x88) == 0x08 && tty->pgrp > 0)
  35                 kill_pg(tty->pgrp,SIGHUP,1);
  36 }
  37 
  38 /*
  39  * There are several races here: we avoid most of them by disabling timer_active
  40  * for the crucial part of the process.. That's a good idea anyway.
  41  *
  42  * The problem is that we have to output characters /both/ from interrupts
  43  * and from the normal write: the latter to be sure the interrupts start up
  44  * again. With serial lines, the interrupts can happen so often that the
  45  * races actually are noticeable.
  46  */
  47 static void send_intr(unsigned line, unsigned port, struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
  48 {
  49         int c;
  50 
  51 #define TIMER ((SER1_TIMEOUT-1)+line)
  52         timer_active &= ~(1 << TIMER);
  53         if ((c = GETCH(tty->write_q)) < 0)
  54                 return;
  55         outb(c,port);
  56         timer_table[TIMER].expires = jiffies + 10;
  57         timer_active |= 1 << TIMER;
  58         if (LEFT(tty->write_q) > WAKEUP_CHARS)
  59                 wake_up(&tty->write_q->proc_list);
  60 #undef TIMER
  61 }
  62 
  63 static void receive_intr(unsigned line, unsigned port, struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
  64 {
  65         if (FULL(tty->read_q))
  66                 return;
  67         PUTCH(inb(port),tty->read_q);
  68         timer_active |= (1<<(SER1_TIMER-1))<<line;
  69 }
  70 
  71 static void line_status_intr(unsigned line, unsigned port, struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
  72 {
  73         unsigned char status = inb(port+5);
  74 
  75 /*      printk("line status: %02x\n",status); */
  76 }
  77 
  78 static void (*jmp_table[4])(unsigned,unsigned,struct tty_struct *) = {
  79         modem_status_intr,
  80         send_intr,
  81         receive_intr,
  82         line_status_intr
  83 };
  84 
  85 static void check_tty(unsigned line,struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
  86 {
  87         unsigned short port;
  88         unsigned char ident;
  89 
  90         if (!(port = tty->read_q->data))
  91                 return;
  92         while (1) {
  93                 ident = inb(port+2);
  94                 if (ident & 1)
  95                         return;
  96                 ident >>= 1;
  97                 if (ident > 3)
  98                         return;
  99                 jmp_table[ident](line,port,tty);
 100         }
 101 }
 102 
 103 /*
 104  * IRQ3 normally handles com2 and com4
 105  */
 106 void do_IRQ3(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 107 {
 108         check_tty(2,tty_table+65);
 109         check_tty(4,tty_table+67);
 110 }
 111 
 112 /*
 113  * IRQ4 normally handles com1 and com2
 114  */
 115 void do_IRQ4(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 116 {
 117         check_tty(1,tty_table+64);
 118         check_tty(3,tty_table+66);      
 119 }
 120 
 121 static void com1_timer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 122 {
 123         TTY_READ_FLUSH(tty_table+64);
 124 }
 125 
 126 static void com2_timer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 127 {
 128         TTY_READ_FLUSH(tty_table+65);
 129 }
 130 
 131 static void com3_timer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 132 {
 133         TTY_READ_FLUSH(tty_table+66);
 134 }
 135 
 136 static void com4_timer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 137 {
 138         TTY_READ_FLUSH(tty_table+67);
 139 }
 140 
 141 /*
 142  * Again, we disable interrupts to be sure there aren't any races:
 143  * see send_intr for details.
 144  */
 145 static inline void do_rs_write(unsigned line, struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
 146 {
 147         int port;
 148 
 149 #define TIMER ((SER1_TIMEOUT-1)+line)
 150         if (!tty || !tty->write_q || EMPTY(tty->write_q))
 151                 return;
 152         if (!(port = tty->write_q->data))
 153                 return;
 154         cli();
 155         if (inb_p(port+5) & 0x20)
 156                 send_intr(line,port,tty);
 157         else {
 158                 timer_table[TIMER].expires = jiffies + 10;
 159                 timer_active |= 1 << TIMER;
 160         }
 161         sti();
 162 #undef TIMER
 163 }
 164 
 165 static void com1_timeout(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 166 {
 167         do_rs_write(1,tty_table+64);
 168 }
 169 
 170 static void com2_timeout(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 171 {
 172         do_rs_write(2,tty_table+65);
 173 }
 174 
 175 static void com3_timeout(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 176 {
 177         do_rs_write(3,tty_table+66);
 178 }
 179 
 180 static void com4_timeout(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 181 {
 182         do_rs_write(4,tty_table+67);
 183 }
 184 
 185 static void init(int port)
     /* [previous][next][first][last][top][bottom][index][help] */
 186 {
 187         outb_p(0x80,port+3);    /* set DLAB of line control reg */
 188         outb_p(0x30,port);      /* LS of divisor (48 -> 2400 bps */
 189         outb_p(0x00,port+1);    /* MS of divisor */
 190         outb_p(0x03,port+3);    /* reset DLAB */
 191         outb_p(0x00,port+4);    /* reset DTR,RTS, OUT_2 */
 192         outb_p(0x0f,port+1);    /* enable all intrs */
 193         (void)inb(port);        /* read data port to reset things (?) */
 194 }
 195 
 196 /*
 197  * this routine enables interrupts on 'line', and disables them on
 198  * 'line ^ 2', as they share the same IRQ. Braindamaged AT hardware.
 199  */
 200 void serial_open(unsigned line)
     /* [previous][next][first][last][top][bottom][index][help] */
 201 {
 202         unsigned short port;
 203         unsigned short port2;
 204 
 205         if (line>3)
 206                 return;
 207         port = tty_table[64+line].read_q->data;
 208         if (!port)
 209                 return;
 210         port2 = tty_table[64+(line ^ 2)].read_q->data;
 211         cli();
 212         if (port2)
 213                 outb_p(0x00,port2+4);
 214         outb_p(0x03,port+3);    /* reset DLAB */
 215         outb_p(0x0f,port+4);    /* set DTR,RTS, OUT_2 */
 216         outb_p(0x0f,port+1);    /* enable all intrs */
 217         inb_p(port+5);
 218         inb_p(port+0);
 219         inb_p(port+6);
 220         inb(port+2);
 221         sti();
 222 }
 223 
 224 void rs_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 225 {
 226 /* SERx_TIMER timers are used for receiving: timeout is always 0 (immediate) */
 227         timer_table[SER1_TIMER].fn = com1_timer;
 228         timer_table[SER1_TIMER].expires = 0;
 229         timer_table[SER2_TIMER].fn = com2_timer;
 230         timer_table[SER2_TIMER].expires = 0;
 231         timer_table[SER3_TIMER].fn = com3_timer;
 232         timer_table[SER3_TIMER].expires = 0;
 233         timer_table[SER4_TIMER].fn = com4_timer;
 234         timer_table[SER4_TIMER].expires = 0;
 235 /* SERx_TIMEOUT timers are used for writing: prevent serial lockups */
 236         timer_table[SER1_TIMEOUT].fn = com1_timeout;
 237         timer_table[SER1_TIMEOUT].expires = 0;
 238         timer_table[SER2_TIMEOUT].fn = com2_timeout;
 239         timer_table[SER2_TIMEOUT].expires = 0;
 240         timer_table[SER3_TIMEOUT].fn = com3_timeout;
 241         timer_table[SER3_TIMEOUT].expires = 0;
 242         timer_table[SER4_TIMEOUT].fn = com4_timeout;
 243         timer_table[SER4_TIMEOUT].expires = 0;
 244         set_intr_gate(0x23,IRQ3_interrupt);
 245         set_intr_gate(0x24,IRQ4_interrupt);
 246         init(tty_table[64].read_q->data);
 247         init(tty_table[65].read_q->data);
 248         init(tty_table[66].read_q->data);
 249         init(tty_table[67].read_q->data);
 250         outb(inb_p(0x21)&0xE7,0x21);
 251 }
 252 
 253 /*
 254  * This routine gets called when tty_write has put something into
 255  * the write_queue. It must check wheter the queue is empty, and
 256  * set the interrupt register accordingly
 257  *
 258  *      void _rs_write(struct tty_struct * tty);
 259  */
 260 void rs_write(struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
 261 {
 262         int line = tty - tty_table - 63;
 263 
 264         do_rs_write(line,tty);
 265 }

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