1 /* 2 * ----------------------------------------------------------------------- 3 * 4 * Copyright 2009 Intel Corporation; author: H. Peter Anvin 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, Inc., 53 Temple Place Ste 330, 9 * Boston MA 02111-1307, USA; either version 2 of the License, or 10 * (at your option) any later version; incorporated herein by reference. 11 * 12 * ----------------------------------------------------------------------- 13 * 14 * serirq.c 15 * 16 * Serial port IRQ code 17 * 18 * We don't know what IRQ, if any, we have, so map all of them... 19 */ 20 #include <sys/io.h> 21 #include <string.h> 22 23 #include <fs.h> 24 #include "bios.h" 25 26 static char serial_buf[serial_buf_size]; 27 28 static unsigned short SerialIRQPort; /* Serial port w IRQ service */ 29 char *SerialHead = serial_buf; /* Head of serial port rx buffer */ 30 char *SerialTail = serial_buf; /* Tail of serial port rx buffer */ 31 32 static unsigned char IRQMask[2]; /* PIC IRQ mask status */ 33 34 static unsigned int oldirq[16]; 35 36 typedef void (*irqhandler_t)(void); 37 38 void sirq_cleanup(void); 39 40 static void irq_common(unsigned short old_irq) 41 { 42 char *dst; 43 irqhandler_t next; 44 char val; 45 46 dst = SerialHead; 47 next = (irqhandler_t)oldirq[old_irq]; 48 49 /* LSR */ 50 val = inb(SerialPort + 5); 51 52 /* Received data */ 53 while (val & 1) { 54 /* RDR */ 55 *dst++ = inb(SerialPort); 56 /* LSR */ 57 val = inb(SerialPort + 5); 58 if ((val & FlowIgnore) == FlowIgnore) { 59 /* Wrap around if necessary */ 60 dst = (char *)((unsigned long)dst & (serial_buf_size - 1)); 61 62 /* Would this cause overflow? */ 63 if (dst != SerialTail) 64 SerialHead = dst; 65 } 66 } 67 68 /* Chain to next handler */ 69 next(); 70 } 71 72 #define SERIAL_IRQ_HANDLER(n) \ 73 static void serstub_irq##n(void) \ 74 { \ 75 irq_common(n); \ 76 } 77 78 SERIAL_IRQ_HANDLER(0); 79 SERIAL_IRQ_HANDLER(1); 80 SERIAL_IRQ_HANDLER(2); 81 SERIAL_IRQ_HANDLER(3); 82 SERIAL_IRQ_HANDLER(4); 83 SERIAL_IRQ_HANDLER(5); 84 SERIAL_IRQ_HANDLER(6); 85 SERIAL_IRQ_HANDLER(7); 86 SERIAL_IRQ_HANDLER(8); 87 SERIAL_IRQ_HANDLER(9); 88 SERIAL_IRQ_HANDLER(10); 89 SERIAL_IRQ_HANDLER(11); 90 SERIAL_IRQ_HANDLER(12); 91 SERIAL_IRQ_HANDLER(13); 92 SERIAL_IRQ_HANDLER(14); 93 SERIAL_IRQ_HANDLER(15); 94 95 static inline void save_irq_vectors(uint32_t *src, uint32_t *dst) 96 { 97 int i; 98 99 for (i = 0; i < 8; i++) 100 *dst++ = *src++; 101 } 102 103 static inline void install_irq_vectors(uint32_t *dst, int first) 104 { 105 if (first) { 106 *dst++ = (uint32_t)serstub_irq0; 107 *dst++ = (uint32_t)serstub_irq1; 108 *dst++ = (uint32_t)serstub_irq2; 109 *dst++ = (uint32_t)serstub_irq3; 110 *dst++ = (uint32_t)serstub_irq4; 111 *dst++ = (uint32_t)serstub_irq5; 112 *dst++ = (uint32_t)serstub_irq6; 113 *dst++ = (uint32_t)serstub_irq7; 114 } else { 115 *dst++ = (uint32_t)serstub_irq8; 116 *dst++ = (uint32_t)serstub_irq9; 117 *dst++ = (uint32_t)serstub_irq10; 118 *dst++ = (uint32_t)serstub_irq11; 119 *dst++ = (uint32_t)serstub_irq12; 120 *dst++ = (uint32_t)serstub_irq13; 121 *dst++ = (uint32_t)serstub_irq14; 122 *dst++ = (uint32_t)serstub_irq15; 123 } 124 } 125 126 __export void sirq_install(void) 127 { 128 char val, val2; 129 130 sirq_cleanup(); 131 132 save_irq_vectors((uint32_t *)(4 * 0x8), oldirq); 133 save_irq_vectors((uint32_t *)(4 * 0x70), &oldirq[8]); 134 135 install_irq_vectors((uint32_t *)(4 * 0x8), 1); 136 install_irq_vectors((uint32_t *)(4 * 0x70), 0); 137 138 SerialIRQPort = SerialPort; 139 140 /* Clear DLAB (should already be...) */ 141 outb(0x3, SerialIRQPort + 5); 142 io_delay(); 143 144 /* Enable receive interrupt */ 145 outb(0x1, SerialIRQPort + 1); 146 io_delay(); 147 148 /* 149 * Enable all the interrupt lines at the PIC. Some BIOSes only 150 * enable the timer interrupts and other interrupts actively 151 * in use by the BIOS. 152 */ 153 154 /* Secondary PIC mask register */ 155 val = inb(0xA1); 156 val2 = inb(0x21); 157 IRQMask[0] = val; 158 IRQMask[1] = val2; 159 160 io_delay(); 161 162 /* Remove all interrupt masks */ 163 outb(0x21, 0); 164 outb(0xA1, 0); 165 } 166 167 __export void sirq_cleanup_nowipe(void) 168 { 169 uint32_t *dst; 170 int i; 171 172 if (!SerialIRQPort) 173 return; 174 175 /* Clear DLAB */ 176 outb(0x3, SerialIRQPort + 5); 177 io_delay(); 178 179 /* Clear IER */ 180 outb(0x0, SerialIRQPort + 1); 181 io_delay(); 182 183 /* Restore PIC masks */ 184 outb(IRQMask[0], 0x21); 185 outb(IRQMask[1], 0xA1); 186 187 /* Restore the original interrupt vectors */ 188 dst = (uint32_t *)(4 * 0x8); 189 for (i = 0; i < 8; i++) 190 *dst++ = oldirq[i]; 191 192 dst = (uint32_t *)(4 * 0x70); 193 for (i = 8; i < 16; i++) 194 *dst++ = oldirq[i]; 195 196 /* No active interrupt system */ 197 SerialIRQPort = 0; 198 } 199 200 void sirq_cleanup(void) 201 { 202 sirq_cleanup_nowipe(); 203 memcpy(SerialHead, 0x0, serial_buf_size); 204 } 205