/* * ----------------------------------------------------------------------- * * Copyright 2009 Intel Corporation; author: H. Peter Anvin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, Inc., 53 Temple Place Ste 330, * Boston MA 02111-1307, USA; either version 2 of the License, or * (at your option) any later version; incorporated herein by reference. * * ----------------------------------------------------------------------- * * serirq.c * * Serial port IRQ code * * We don't know what IRQ, if any, we have, so map all of them... */ #include #include #include #include "bios.h" static char serial_buf[serial_buf_size]; static unsigned short SerialIRQPort; /* Serial port w IRQ service */ char *SerialHead = serial_buf; /* Head of serial port rx buffer */ char *SerialTail = serial_buf; /* Tail of serial port rx buffer */ static unsigned char IRQMask[2]; /* PIC IRQ mask status */ static unsigned int oldirq[16]; typedef void (*irqhandler_t)(void); void sirq_cleanup(void); static void irq_common(unsigned short old_irq) { char *dst; irqhandler_t next; char val; dst = SerialHead; next = (irqhandler_t)oldirq[old_irq]; /* LSR */ val = inb(SerialPort + 5); /* Received data */ while (val & 1) { /* RDR */ *dst++ = inb(SerialPort); /* LSR */ val = inb(SerialPort + 5); if ((val & FlowIgnore) == FlowIgnore) { /* Wrap around if necessary */ dst = (char *)((unsigned long)dst & (serial_buf_size - 1)); /* Would this cause overflow? */ if (dst != SerialTail) SerialHead = dst; } } /* Chain to next handler */ next(); } #define SERIAL_IRQ_HANDLER(n) \ static void serstub_irq##n(void) \ { \ irq_common(n); \ } SERIAL_IRQ_HANDLER(0); SERIAL_IRQ_HANDLER(1); SERIAL_IRQ_HANDLER(2); SERIAL_IRQ_HANDLER(3); SERIAL_IRQ_HANDLER(4); SERIAL_IRQ_HANDLER(5); SERIAL_IRQ_HANDLER(6); SERIAL_IRQ_HANDLER(7); SERIAL_IRQ_HANDLER(8); SERIAL_IRQ_HANDLER(9); SERIAL_IRQ_HANDLER(10); SERIAL_IRQ_HANDLER(11); SERIAL_IRQ_HANDLER(12); SERIAL_IRQ_HANDLER(13); SERIAL_IRQ_HANDLER(14); SERIAL_IRQ_HANDLER(15); static inline void save_irq_vectors(uint32_t *src, uint32_t *dst) { int i; for (i = 0; i < 8; i++) *dst++ = *src++; } static inline void install_irq_vectors(uint32_t *dst, int first) { if (first) { *dst++ = (uint32_t)serstub_irq0; *dst++ = (uint32_t)serstub_irq1; *dst++ = (uint32_t)serstub_irq2; *dst++ = (uint32_t)serstub_irq3; *dst++ = (uint32_t)serstub_irq4; *dst++ = (uint32_t)serstub_irq5; *dst++ = (uint32_t)serstub_irq6; *dst++ = (uint32_t)serstub_irq7; } else { *dst++ = (uint32_t)serstub_irq8; *dst++ = (uint32_t)serstub_irq9; *dst++ = (uint32_t)serstub_irq10; *dst++ = (uint32_t)serstub_irq11; *dst++ = (uint32_t)serstub_irq12; *dst++ = (uint32_t)serstub_irq13; *dst++ = (uint32_t)serstub_irq14; *dst++ = (uint32_t)serstub_irq15; } } __export void sirq_install(void) { char val, val2; sirq_cleanup(); save_irq_vectors((uint32_t *)(4 * 0x8), oldirq); save_irq_vectors((uint32_t *)(4 * 0x70), &oldirq[8]); install_irq_vectors((uint32_t *)(4 * 0x8), 1); install_irq_vectors((uint32_t *)(4 * 0x70), 0); SerialIRQPort = SerialPort; /* Clear DLAB (should already be...) */ outb(0x3, SerialIRQPort + 5); io_delay(); /* Enable receive interrupt */ outb(0x1, SerialIRQPort + 1); io_delay(); /* * Enable all the interrupt lines at the PIC. Some BIOSes only * enable the timer interrupts and other interrupts actively * in use by the BIOS. */ /* Secondary PIC mask register */ val = inb(0xA1); val2 = inb(0x21); IRQMask[0] = val; IRQMask[1] = val2; io_delay(); /* Remove all interrupt masks */ outb(0x21, 0); outb(0xA1, 0); } __export void sirq_cleanup_nowipe(void) { uint32_t *dst; int i; if (!SerialIRQPort) return; /* Clear DLAB */ outb(0x3, SerialIRQPort + 5); io_delay(); /* Clear IER */ outb(0x0, SerialIRQPort + 1); io_delay(); /* Restore PIC masks */ outb(IRQMask[0], 0x21); outb(IRQMask[1], 0xA1); /* Restore the original interrupt vectors */ dst = (uint32_t *)(4 * 0x8); for (i = 0; i < 8; i++) *dst++ = oldirq[i]; dst = (uint32_t *)(4 * 0x70); for (i = 8; i < 16; i++) *dst++ = oldirq[i]; /* No active interrupt system */ SerialIRQPort = 0; } void sirq_cleanup(void) { sirq_cleanup_nowipe(); memset(SerialHead, 0x0, serial_buf_size); }