| /* See COPYRIGHT for copyright information. */ | 
 |  | 
 | #include <arch/x86.h> | 
 | #include <arch/arch.h> | 
 | #include <arch/console.h> | 
 | #include <arch/kbdreg.h> | 
 | #include <atomic.h> | 
 | #include <string.h> | 
 | #include <assert.h> | 
 | #include <stdio.h> | 
 | #include <sys/queue.h> | 
 | #include <arch/topology.h> | 
 |  | 
 | #include <ros/memlayout.h> | 
 |  | 
 | /***** Serial I/O code *****/ | 
 |  | 
 | #define COM1			0x3F8	/* irq 4 */ | 
 | #define COM2			0x2F8	/* irq 3 */ | 
 | #define COM3			0x3E8	/* irq 4 */ | 
 | #define COM4			0x2E8	/* irq 3 */ | 
 |  | 
 | #define	COM_RX			0		// In:	Receive buffer (DLAB=0) | 
 | #define COM_DLL			0		// Out: Divisor Latch Low (DLAB=1) | 
 | #define COM_DLM			1		// Out: Divisor Latch High (DLAB=1) | 
 | #define COM_IER			1		// Out: Interrupt Enable Register | 
 | #define	COM_IER_RDI		0x01	//   Enable receiver data interrupt | 
 | #define COM_IIR			2		// In:	Interrupt ID Register | 
 | #define COM_FCR			2		// Out: FIFO Control Register | 
 | #define COM_LCR			3		// Out: Line Control Register | 
 | #define	COM_LCR_DLAB	0x80	//   Divisor latch access bit | 
 | #define	COM_LCR_WLEN8	0x03	//   Wordlength: 8 bits | 
 | #define COM_MCR			4		// Out: Modem Control Register | 
 | #define	COM_MCR_RTS		0x02	// RTS complement | 
 | #define	COM_MCR_DTR		0x01	// DTR complement | 
 | #define	COM_MCR_OUT2	0x08	// Out2 complement | 
 | #define	COM_MCR_GLB_IRQ	0x08	/* global irq controlled via MCR */ | 
 | #define COM_LSR			5		// In:	Line Status Register | 
 | #define COM_LSR_DATA	0x01	//   Data available | 
 | #define COM_LSR_READY	0x20	//   Ready to send | 
 | #define COM_SCRATCH		7		/* Scratch register */ | 
 |  | 
 | /* List of all initialized console devices */ | 
 | struct cons_dev_slist cdev_list = SLIST_HEAD_INITIALIZER(cdev_list); | 
 | /* need to statically allocate these, since cons_init is called so damn early */ | 
 | struct cons_dev com1, com2, com3, com4, kb; | 
 |  | 
 | static int __serial_get_char(int com, uint8_t *data) | 
 | { | 
 | 	if (!(inb(com + COM_LSR) & COM_LSR_DATA)) | 
 | 		return -1; | 
 | 	*data = inb(com + COM_RX); | 
 | 	/* serial input sends \r a lot, but we interpret them as \n later on.  this | 
 | 	 * will help userspace too, which isn't expecting the \rs.  the right answer | 
 | 	 * might involve telling userspace what sort of console this is. */ | 
 | 	if (*data == '\r') | 
 | 		*data = '\n'; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int serial_get_char(struct cons_dev *cdev, uint8_t *data) | 
 | { | 
 | 	return __serial_get_char(cdev->val, data); | 
 | } | 
 |  | 
 | static void __serial_put_char(int com, uint8_t c) | 
 | { | 
 | 	while (!(inb(com + COM_LSR) & COM_LSR_READY)) | 
 | 		cpu_relax(); | 
 | 	outb(com, c); | 
 | } | 
 |  | 
 | /* Writes c (or some variant of) to the serial cdev */ | 
 | static void serial_put_char(struct cons_dev *cdev, uint8_t c) | 
 | { | 
 | 	assert(cdev->type == CONS_SER_DEV); | 
 | 	/* We do some funky editing of a few chars, to suit what minicom seems to | 
 | 	 * expect (at least for brho). */ | 
 | 	switch (c & 0xff) { | 
 | 		case '\b': | 
 | 		case 0x7f: | 
 | 		#ifdef CONFIG_PRINTK_NO_BACKSPACE | 
 | 			__serial_put_char(cdev->val, (uint8_t)('^')); | 
 | 			__serial_put_char(cdev->val, (uint8_t)('H')); | 
 | 		#else | 
 | 			__serial_put_char(cdev->val, '\b'); | 
 | 			__serial_put_char(cdev->val, (uint8_t)(' ')); | 
 | 			__serial_put_char(cdev->val, '\b'); | 
 | 		#endif /* CONFIG_PRINTK_NO_BACKSPACE */ | 
 | 			break; | 
 | 		case '\n': | 
 | 			__serial_put_char(cdev->val, (uint8_t)('\n')); | 
 | 			__serial_put_char(cdev->val, (uint8_t)('\r')); | 
 | 			break; | 
 | 		case '\r': | 
 | 			__serial_put_char(cdev->val, (uint8_t)('\r')); | 
 | 			break; | 
 | 		default: | 
 | 			__serial_put_char(cdev->val, (uint8_t)(c & 0xff)); | 
 | 			break; | 
 | 	} | 
 | } | 
 |  | 
 | /* Writes c to every initialized serial port */ | 
 | static void serial_spam_char(int c) | 
 | { | 
 | 	struct cons_dev *i; | 
 | 	SLIST_FOREACH(i, &cdev_list, next) { | 
 | 		if (i->type == CONS_SER_DEV) | 
 | 			serial_put_char(i, c); | 
 | 	} | 
 | } | 
 |  | 
 | /* http://en.wikibooks.org/wiki/Serial_Programming/8250_UART_Programming \ | 
 |  * #Software_Identification_of_the_UART | 
 |  * | 
 |  * We return 0 for unknown (probably not there), and the char * o/w */ | 
 | static char *__serial_detect_type(int com) | 
 | { | 
 | 	uint8_t val; | 
 | 	char *model = 0; | 
 | 	/* First, check that the port actually exists.  I haven't seen any | 
 | 	 * documentation of the LSR 0xff check, but it seems to work on qemu and | 
 | 	 * hardware (brho's nehalem).  Perhaps 0xff is the default state for | 
 | 	 * 'unmapped' ports. */ | 
 | 	/* Serial port doesn't exist if COM_LSR returns 0xff */ | 
 | 	if (inb(com + COM_LSR) == 0xff) | 
 | 		return model; | 
 | 	/* Try to set FIFO, then based on the bits enabled, we can tell what model | 
 | 	 * it is */ | 
 | 	outb(com + COM_FCR, 0xe7); | 
 | 	val = inb(com + COM_IIR); | 
 | 	if (val & (1 << 6)) { | 
 | 		if (val & (1 << 7)) { | 
 | 			if (val & (1 << 5)) | 
 | 				model = "UART 16750"; | 
 | 			else | 
 | 				model = "UART 16550A"; | 
 | 		} else { | 
 | 			model = "UART 16550"; | 
 | 		} | 
 | 	} else { | 
 | 		/* no FIFO at all.  the 8250 had a buggy scratch register. */ | 
 | 		outb(com + COM_SCRATCH, 0x2a); | 
 | 		val = inb(com + COM_SCRATCH); | 
 | 		if (val == 0x2a) | 
 | 			model = "UART 16450"; | 
 | 		else | 
 | 			model = "UART 8250"; | 
 | 	} | 
 | 	return model; | 
 | } | 
 |  | 
 | /* Helper: attempts to initialize the serial device cdev with COM com.  If it | 
 |  * succeeds, the cdev will be on the cdev_list. */ | 
 | static void serial_com_init(struct cons_dev *cdev, int com) | 
 | { | 
 | 	cdev->model = __serial_detect_type(com); | 
 | 	/* Bail if detection failed */ | 
 | 	if (!cdev->model) | 
 | 		return; | 
 | 	/* Set up the struct */ | 
 | 	cdev->type = CONS_SER_DEV; | 
 | 	cdev->val = com; | 
 | 	switch (com) { | 
 | 		case (COM1): | 
 | 		case (COM3): | 
 | 			cdev->irq = 4; | 
 | 			break; | 
 | 		case (COM2): | 
 | 		case (COM4): | 
 | 			cdev->irq = 3; | 
 | 			break; | 
 | 		default: | 
 | 			/* not that printing is the safest thing right now... */ | 
 | 			panic("Unknown COM %d", com); | 
 | 	} | 
 | 	cdev->getc = serial_get_char; | 
 | 	/* Turn off the FIFO (not sure this is needed) */ | 
 | 	outb(com + COM_FCR, 0); | 
 | 	/* Set speed; requires DLAB latch */ | 
 | 	outb(com + COM_LCR, COM_LCR_DLAB); | 
 | 	/* Setting speed to 115200 (setting the divider to 1) */ | 
 | 	outb(com + COM_DLL, 1); | 
 | 	outb(com + COM_DLM, 0); | 
 | 	/* 8 data bits, 1 stop bit, parity off; turn off DLAB latch */ | 
 | 	outb(com + COM_LCR, COM_LCR_WLEN8 & ~COM_LCR_DLAB); | 
 | 	/* This should turn on hardware flow control and make sure the global irq | 
 | 	 * bit is on.  This bit is definitely used some hardware's 16550As, though | 
 | 	 * not for qemu.  Also, on both qemu and hardware, this whole line is a | 
 | 	 * noop, since the COM_MCR is already 0x0b, so we're just making sure the | 
 | 	 * three bits are still turned on (and leaving other bits unchanged) */ | 
 | 	outb(com + COM_MCR, inb(com + COM_MCR) | COM_MCR_RTS | COM_MCR_DTR | | 
 | 	                                         COM_MCR_GLB_IRQ); | 
 | 	/* Enable rx interrupts */ | 
 | 	outb(com + COM_IER, COM_IER_RDI); | 
 | 	/* Clear any preexisting overrun indications and interrupts */ | 
 | 	inb(com + COM_IIR); | 
 | 	inb(com + COM_RX); | 
 | 	/* Put us on the list of initialized cdevs (now that it is init'd) */ | 
 | 	SLIST_INSERT_HEAD(&cdev_list, cdev, next); | 
 | } | 
 |  | 
 | static void serial_init(void) | 
 | { | 
 | 	/* attempt to init all four COMs */ | 
 | 	serial_com_init(&com1, COM1); | 
 | 	serial_com_init(&com2, COM2); | 
 | 	serial_com_init(&com3, COM3); | 
 | 	serial_com_init(&com4, COM4); | 
 | } | 
 |  | 
 | /***** Parallel port output code *****/ | 
 | // For information on PC parallel port programming, see the class References | 
 | // page. | 
 |  | 
 | // Stupid I/O delay routine necessitated by historical PC design flaws | 
 | static void | 
 | delay(void) | 
 | { | 
 | 	inb(0x84); | 
 | 	inb(0x84); | 
 | 	inb(0x84); | 
 | 	inb(0x84); | 
 | } | 
 |  | 
 | static void | 
 | lpt_putc(int c) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; !(inb(0x378+1) & 0x80) && i < 12800; i++) | 
 | 		delay(); | 
 | 	outb(0x378+0, c); | 
 | 	outb(0x378+2, 0x08|0x04|0x01); | 
 | 	outb(0x378+2, 0x08); | 
 | } | 
 |  | 
 | /***** Text-mode CGA/VGA display output with scrolling *****/ | 
 | #define MONO_BASE	0x3B4 | 
 | #define MONO_BUF	0xB0000 | 
 | #define CGA_BASE	0x3D4 | 
 | #define CGA_BUF		0xB8000 | 
 |  | 
 | #define CRT_ROWS	25 | 
 | #define CRT_COLS	80 | 
 | #define CRT_SIZE	(CRT_ROWS * CRT_COLS) | 
 |  | 
 | #define MAX_SCROLL_LENGTH	20 | 
 | #define SCROLLING_CRT_SIZE	(MAX_SCROLL_LENGTH * CRT_SIZE) | 
 |  | 
 | static spinlock_t console_lock = SPINLOCK_INITIALIZER_IRQSAVE; | 
 |  | 
 | static unsigned addr_6845; | 
 | static uint16_t *crt_buf; | 
 | static uint16_t crt_pos; | 
 |  | 
 | static uint16_t scrolling_crt_buf[SCROLLING_CRT_SIZE]; | 
 | static uint16_t scrolling_crt_pos; | 
 | static uint8_t	current_crt_buf; | 
 |  | 
 | void | 
 | cga_init(void) | 
 | { | 
 | 	volatile uint16_t *cp; | 
 | 	uint16_t was; | 
 | 	unsigned pos; | 
 |  | 
 | 	cp = (uint16_t *)(KERNBASE + CGA_BUF); | 
 | 	was = *cp; | 
 | 	*cp = (uint16_t) 0xA55A; | 
 | 	if (*cp != 0xA55A) { | 
 | 		cp = (uint16_t *)(KERNBASE + MONO_BUF); | 
 | 		addr_6845 = MONO_BASE; | 
 | 	} else { | 
 | 		*cp = was; | 
 | 		addr_6845 = CGA_BASE; | 
 | 	} | 
 |  | 
 | 	/* Extract cursor location */ | 
 | 	outb(addr_6845, 14); | 
 | 	pos = inb(addr_6845 + 1) << 8; | 
 | 	outb(addr_6845, 15); | 
 | 	pos |= inb(addr_6845 + 1); | 
 |  | 
 | 	crt_buf = (uint16_t*)cp; | 
 | 	crt_pos = pos; | 
 | 	scrolling_crt_pos = 0; | 
 | 	current_crt_buf = 0; | 
 |  | 
 | } | 
 |  | 
 | static void set_screen(uint8_t screen_num) | 
 | { | 
 | 	uint16_t leftovers = (scrolling_crt_pos % CRT_COLS); | 
 | 	leftovers = (leftovers) ? CRT_COLS - leftovers : 0; | 
 |  | 
 | 	int offset = scrolling_crt_pos + leftovers - (screen_num + 1)*CRT_SIZE; | 
 | 	offset = (offset > 0) ? offset : 0; | 
 |  | 
 | 	memcpy(crt_buf, scrolling_crt_buf + offset, CRT_SIZE * sizeof(uint16_t)); | 
 | } | 
 |  | 
 | static void scroll_screen_up(void) | 
 | { | 
 | 	if(current_crt_buf <  (scrolling_crt_pos / CRT_SIZE)) | 
 | 		current_crt_buf++; | 
 | 	set_screen(current_crt_buf); | 
 | } | 
 |  | 
 | static void scroll_screen_down(void) | 
 | { | 
 | 	if(current_crt_buf > 0) | 
 | 		current_crt_buf--; | 
 | 	set_screen(current_crt_buf); | 
 | } | 
 |  | 
 | static void reset_screen(void) | 
 | { | 
 | 	current_crt_buf = 0; | 
 | 	set_screen(current_crt_buf); | 
 | } | 
 |  | 
 | void | 
 | cga_putc(int c) | 
 | { | 
 | 	// if no attribute given, then use black on white | 
 | 	if (!(c & ~0xFF)) | 
 | 		c |= 0x0700; | 
 |  | 
 | 	switch (c & 0xff) { | 
 | 	case '\b': | 
 | 	case 0x7f: | 
 | 	#ifdef CONFIG_PRINTK_NO_BACKSPACE | 
 | 		cga_putc('^'); | 
 | 		cga_putc('H'); | 
 | 	#else | 
 | 		if (crt_pos > 0) { | 
 | 			crt_pos--; | 
 | 			scrolling_crt_pos--; | 
 | 			crt_buf[crt_pos] = (c & ~0xff) | ' '; | 
 | 			scrolling_crt_buf[scrolling_crt_pos] = crt_buf[crt_pos]; | 
 | 		} | 
 | 	#endif /* CONFIG_PRINTK_NO_BACKSPACE */ | 
 | 		break; | 
 | 	case '\n': | 
 | 		crt_pos += CRT_COLS; | 
 | 		scrolling_crt_pos += CRT_COLS; | 
 | 		/* fallthru */ | 
 | 	case '\r': | 
 | 		crt_pos -= (crt_pos % CRT_COLS); | 
 | 		scrolling_crt_pos -= (scrolling_crt_pos % CRT_COLS); | 
 | 		break; | 
 | 	case '\t': | 
 | 		cga_putc(' '); | 
 | 		cga_putc(' '); | 
 | 		cga_putc(' '); | 
 | 		cga_putc(' '); | 
 | 		cga_putc(' '); | 
 | 		break; | 
 | 	default: | 
 | 		crt_buf[crt_pos++] = c;		/* write the character */ | 
 | 		scrolling_crt_buf[scrolling_crt_pos++] = c; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	// The purpose of this is to allow the screen to appear as if it is scrolling as | 
 | 	// more lines are added beyond the size of the monitor.  The top line is dropped | 
 | 	// and everything is shifted up by one. | 
 | 	if (crt_pos >= CRT_SIZE) { | 
 | 		int i; | 
 |  | 
 | 		memcpy(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t)); | 
 | 		for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++) | 
 | 			crt_buf[i] = 0x0700 | ' '; | 
 | 		crt_pos -= CRT_COLS; | 
 | 	} | 
 | 	// Do the same for the scrolling crt buffer when it hits its capacity | 
 | 	if (scrolling_crt_pos >= SCROLLING_CRT_SIZE) { | 
 | 		int i; | 
 |  | 
 | 		memcpy(scrolling_crt_buf, scrolling_crt_buf + CRT_COLS, | 
 | 		       (SCROLLING_CRT_SIZE - CRT_COLS) * sizeof(uint16_t)); | 
 | 		for (i = SCROLLING_CRT_SIZE - CRT_COLS; i < SCROLLING_CRT_SIZE; i++) | 
 | 			scrolling_crt_buf[i] = 0x0700 | ' '; | 
 | 		scrolling_crt_pos -= CRT_COLS; | 
 | 	} | 
 |  | 
 |  | 
 | 	/* move that little blinky thing */ | 
 | 	outb(addr_6845, 14); | 
 | 	outb(addr_6845 + 1, crt_pos >> 8); | 
 | 	outb(addr_6845, 15); | 
 | 	outb(addr_6845 + 1, crt_pos); | 
 | } | 
 |  | 
 |  | 
 | /***** Keyboard input code *****/ | 
 |  | 
 | #define NO		0 | 
 |  | 
 | #define SHIFT	(1<<0) | 
 | #define CTL		(1<<1) | 
 | #define ALT		(1<<2) | 
 |  | 
 | #define CAPSLOCK	(1<<3) | 
 | #define NUMLOCK		(1<<4) | 
 | #define SCROLLLOCK	(1<<5) | 
 |  | 
 | #define E0ESC		(1<<6) | 
 |  | 
 | static uint8_t shiftcode[256] = | 
 | { | 
 | 	[0x1D] CTL, | 
 | 	[0x2A] SHIFT, | 
 | 	[0x36] SHIFT, | 
 | 	[0x38] ALT, | 
 | 	[0x9D] CTL, | 
 | 	[0xB8] ALT | 
 | }; | 
 |  | 
 | static uint8_t togglecode[256] = | 
 | { | 
 | 	[0x3A] CAPSLOCK, | 
 | 	[0x45] NUMLOCK, | 
 | 	[0x46] SCROLLLOCK | 
 | }; | 
 |  | 
 | static uint8_t normalmap[256] = | 
 | { | 
 | 	NO,   0x1B, '1',  '2',  '3',  '4',  '5',  '6',	// 0x00 | 
 | 	'7',  '8',  '9',  '0',  '-',  '=',  '\b', '\t', | 
 | 	'q',  'w',  'e',  'r',  't',  'y',  'u',  'i',	// 0x10 | 
 | 	'o',  'p',  '[',  ']',  '\n', NO,   'a',  's', | 
 | 	'd',  'f',  'g',  'h',  'j',  'k',  'l',  ';',	// 0x20 | 
 | 	'\'', '`',  NO,   '\\', 'z',  'x',  'c',  'v', | 
 | 	'b',  'n',  'm',  ',',  '.',  '/',  NO,   '*',	// 0x30 | 
 | 	NO,   ' ',  NO,   NO,   NO,   NO,   NO,   NO, | 
 | 	NO,   NO,   NO,   NO,   NO,   NO,   NO,   '7',	// 0x40 | 
 | 	'8',  '9',  '-',  '4',  '5',  '6',  '+',  '1', | 
 | 	'2',  '3',  '0',  '.',  NO,   NO,   NO,   NO,	// 0x50 | 
 | 	[0xC7] KEY_HOME,	[0x9C] '\n' /*KP_Enter*/, | 
 | 	[0xB5] '/' /*KP_Div*/,	[0xC8] KEY_UP, | 
 | 	[0xC9] KEY_PGUP,	[0xCB] KEY_LF, | 
 | 	[0xCD] KEY_RT,		[0xCF] KEY_END, | 
 | 	[0xD0] KEY_DN,		[0xD1] KEY_PGDN, | 
 | 	[0xD2] KEY_INS,		[0xD3] KEY_DEL | 
 | }; | 
 |  | 
 | static uint8_t shiftmap[256] = | 
 | { | 
 | 	NO,   033,  '!',  '@',  '#',  '$',  '%',  '^',	// 0x00 | 
 | 	'&',  '*',  '(',  ')',  '_',  '+',  '\b', '\t', | 
 | 	'Q',  'W',  'E',  'R',  'T',  'Y',  'U',  'I',	// 0x10 | 
 | 	'O',  'P',  '{',  '}',  '\n', NO,   'A',  'S', | 
 | 	'D',  'F',  'G',  'H',  'J',  'K',  'L',  ':',	// 0x20 | 
 | 	'"',  '~',  NO,   '|',  'Z',  'X',  'C',  'V', | 
 | 	'B',  'N',  'M',  '<',  '>',  '?',  NO,   '*',	// 0x30 | 
 | 	NO,   ' ',  NO,   NO,   NO,   NO,   NO,   NO, | 
 | 	NO,   NO,   NO,   NO,   NO,   NO,   NO,   '7',	// 0x40 | 
 | 	'8',  '9',  '-',  '4',  '5',  '6',  '+',  '1', | 
 | 	'2',  '3',  '0',  '.',  NO,   NO,   NO,   NO,	// 0x50 | 
 | 	[0xC7] KEY_HOME,	[0x9C] '\n' /*KP_Enter*/, | 
 | 	[0xB5] '/' /*KP_Div*/,	[0xC8] KEY_UP, | 
 | 	[0xC9] KEY_PGUP,	[0xCB] KEY_LF, | 
 | 	[0xCD] KEY_RT,		[0xCF] KEY_END, | 
 | 	[0xD0] KEY_DN,		[0xD1] KEY_PGDN, | 
 | 	[0xD2] KEY_INS,		[0xD3] KEY_DEL | 
 | }; | 
 |  | 
 | #define C(x) (x - '@') | 
 |  | 
 | static uint8_t ctlmap[256] = | 
 | { | 
 | 	NO,      NO,      NO,      NO,      NO,      NO,      NO,      NO, | 
 | 	NO,      NO,      NO,      NO,      NO,      NO,      NO,      NO, | 
 | 	C('Q'),  C('W'),  C('E'),  C('R'),  C('T'),  C('Y'),  C('U'),  C('I'), | 
 | 	C('O'),  C('P'),  NO,      NO,      '\r',    NO,      C('A'),  C('S'), | 
 | 	C('D'),  C('F'),  C('G'),  C('H'),  C('J'),  C('K'),  C('L'),  NO, | 
 | 	NO,      NO,      NO,      C('\\'), C('Z'),  C('X'),  C('C'),  C('V'), | 
 | 	C('B'),  C('N'),  C('M'),  NO,      NO,      C('/'),  NO,      NO, | 
 | 	[0x97] KEY_HOME, | 
 | 	[0xB5] C('/'),		[0xC8] KEY_UP, | 
 | 	[0xC9] KEY_PGUP,	[0xCB] KEY_LF, | 
 | 	[0xCD] KEY_RT,		[0xCF] KEY_END, | 
 | 	[0xD0] KEY_DN,		[0xD1] KEY_PGDN, | 
 | 	[0xD2] KEY_INS,		[0xD3] KEY_DEL | 
 | }; | 
 |  | 
 | static uint8_t *charcode[4] = { | 
 | 	normalmap, | 
 | 	shiftmap, | 
 | 	ctlmap, | 
 | 	ctlmap | 
 | }; | 
 |  | 
 | /* | 
 |  * Get data from the keyboard.  If we finish a character, return it.  Else 0. | 
 |  * Return -1 if no data. | 
 |  */ | 
 | static uint32_t shift; | 
 | static bool crt_scrolled = FALSE; | 
 |  | 
 | /* TODO: i'm concerned about the (lack of) locking when scrolling the screen. */ | 
 | static int | 
 | kbd_proc_data(void) | 
 | { | 
 | #ifdef CONFIG_X86_DISABLE_KEYBOARD | 
 | 	/* on some machines with usb keyboards, any keyboard input triggers SMM | 
 | 	 * interference on all of the cores. */ | 
 | 	return -1; | 
 | #endif /* CONFIG_X86_DISABLE_KEYBOARD */ | 
 |  | 
 | 	int c; | 
 | 	uint8_t data; | 
 |  | 
 | #ifdef CONFIG_KB_CORE0_ONLY | 
 | 	/* Ghetto hack to avoid crashing brho's buggy nehalem. */ | 
 | 	uint32_t eax, ebx, ecx, edx, family, model, stepping; | 
 | 	cpuid(0x1, 0x0, &eax, &ebx, &ecx, &edx); | 
 | 	family = ((eax & 0x0FF00000) >> 20) + ((eax & 0x00000F00) >> 8); | 
 | 	model = ((eax & 0x000F0000) >> 12) + ((eax & 0x000000F0) >> 4); | 
 | 	stepping = eax & 0x0000000F; | 
 | 	if (family == 6 && model == 26 && stepping == 4) | 
 | 		if (core_id()) | 
 | 			return -1; | 
 | #endif /* CONFIG_KB_CORE0_ONLY */ | 
 |  | 
 | 	if ((inb(KBSTATP) & KBS_DIB) == 0) | 
 | 		return -1; | 
 |  | 
 | 	data = inb(KBDATAP); | 
 |  | 
 | 	if (data == 0xE0) { | 
 | 		// E0 escape character | 
 | 		shift |= E0ESC; | 
 | 		return 0; | 
 | 	} else if (data & 0x80) { | 
 | 		/* TODO: need a better check for bad key releases */ | 
 | 		if (data == 0xff) | 
 | 			return -1; | 
 | 		// Key released | 
 | 		data = (shift & E0ESC ? data : data & 0x7F); | 
 | 		shift &= ~(shiftcode[data] | E0ESC); | 
 | 		return 0; | 
 | 	} else if (shift & E0ESC) { | 
 | 		// Last character was an E0 escape; or with 0x80 | 
 | 		data |= 0x80; | 
 | 		shift &= ~E0ESC; | 
 | 	} | 
 |  | 
 | 	shift |= shiftcode[data]; | 
 | 	shift ^= togglecode[data]; | 
 |  | 
 | 	c = charcode[shift & (CTL | SHIFT)][data]; | 
 |  | 
 | 	//Scrolling screen functionality | 
 | 	if((shift & SHIFT) && ((c == KEY_UP) || (c == KEY_PGUP))) { | 
 | 		crt_scrolled = TRUE; | 
 | 		scroll_screen_up(); | 
 | 		return 0; | 
 | 	} | 
 | 	else if((shift & SHIFT) && ((c == KEY_DN) || (c == KEY_PGDN))) { | 
 | 		crt_scrolled = TRUE; | 
 | 		scroll_screen_down(); | 
 | 		return 0; | 
 | 	} | 
 | 	else if((shift & SHIFT) && c == KEY_RT) { | 
 | 		crt_scrolled = FALSE; | 
 | 		reset_screen(); | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	// On keypress other than SHIFT, reset if we were scrolled | 
 | 	if(crt_scrolled && (!(shift & SHIFT))) { | 
 | 		crt_scrolled = FALSE; | 
 | 		reset_screen(); | 
 | 	} | 
 |  | 
 | 	//Force character to capital if caps lock on | 
 | 	if (shift & CAPSLOCK) { | 
 | 		if ('a' <= c && c <= 'z') | 
 | 			c += 'A' - 'a'; | 
 | 		else if ('A' <= c && c <= 'Z') | 
 | 			c += 'a' - 'A'; | 
 | 	} | 
 |  | 
 | 	// Process special keys | 
 | 	// Ctrl-Alt-Del: reboot | 
 | 	if (!(~shift & (CTL | ALT)) && c == KEY_DEL) { | 
 | 		cprintf("Rebooting!\n"); | 
 | 		outb(0x92, 0x3); // courtesy of Chris Frost | 
 | 	} | 
 |  | 
 | 	return c; | 
 | } | 
 |  | 
 | static int kb_get_char(struct cons_dev *cdev, uint8_t *data) | 
 | { | 
 |  	int kb_d; | 
 | 	/* kbd_proc_data returns 0 if we should keep asking.  It return -1 when | 
 | 	 * there is no data, and anything else is a char */ | 
 | 	while ((kb_d = kbd_proc_data()) == 0) | 
 | 		cpu_relax(); | 
 | 	if (kb_d == -1) | 
 | 		return -1; | 
 | 	*data = (uint8_t)kb_d; | 
 | 	return 0; | 
 | } | 
 |  | 
 | void kbd_init(void) | 
 | { | 
 | 	/* init and post the kb cons_dev */ | 
 | 	kb.type = CONS_KB_DEV; | 
 | 	kb.val = 0; | 
 | 	kb.irq = 1;		/* default PC keyboard IRQ */ | 
 | 	kb.model = "PC Keyboard"; | 
 | 	kb.getc = kb_get_char; | 
 | 	SLIST_INSERT_HEAD(&cdev_list, &kb, next); | 
 | } | 
 |  | 
 | /***** General device-independent console code *****/ | 
 |  | 
 | /* Initialize the console devices */ | 
 | void cons_init(void) | 
 | { | 
 | 	cga_init(); | 
 | 	kbd_init(); | 
 | 	serial_init(); | 
 | } | 
 |  | 
 | /* Returns 0 on success, with the char in *data */ | 
 | int cons_get_char(struct cons_dev *cdev, uint8_t *data) | 
 | { | 
 | 	return cdev->getc(cdev, data); | 
 | } | 
 |  | 
 | /* Returns any available character, or 0 for none (legacy helper) */ | 
 | int cons_get_any_char(void) | 
 | { | 
 | 	uint8_t c; | 
 | 	struct cons_dev *i; | 
 | 	/* First to succeed gets returned */ | 
 | 	SLIST_FOREACH(i, &cdev_list, next) { | 
 | 		if (!cons_get_char(i, &c)) | 
 | 			return c; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* output a character to all console outputs (monitor and all serials) */ | 
 | void cons_putc(int c) | 
 | { | 
 | 	void logbuf(int c); | 
 |  | 
 | 	spin_lock_irqsave(&console_lock); | 
 |  | 
 | 	#ifndef CONFIG_SERIAL_IO | 
 | 		serial_spam_char(c); | 
 | 	#endif | 
 | 	//lpt_putc(c); 	/* very slow on the nehalem */ | 
 | 	cga_putc(c); | 
 | 	logbuf(c); | 
 |  | 
 | 	spin_unlock_irqsave(&console_lock); | 
 | } | 
 |  | 
 | // `High'-level console I/O.  Used by readline and cprintf. | 
 |  | 
 | void | 
 | cputchar(int c) | 
 | { | 
 | 	cons_putc(c); | 
 | } | 
 |  | 
 | void | 
 | cputbuf(const char*buf, int len) | 
 | { | 
 | 	int i; | 
 | 	for(i = 0; i < len; i++) | 
 | 		cons_putc(buf[i]); | 
 | } | 
 |  | 
 | int | 
 | getchar(void) | 
 | { | 
 | 	int c; | 
 |  | 
 | 	while ((c = cons_get_any_char()) == 0) | 
 | 		/* do nothing */; | 
 | 	return c; | 
 | } | 
 |  | 
 | int | 
 | iscons(int fdnum) | 
 | { | 
 | 	// used by readline | 
 | 	return 1; | 
 | } | 
 |  | 
 | /* TODO: remove us (and serial IO) */ | 
 | void serial_send_byte(uint8_t b) | 
 | { | 
 | } | 
 |  | 
 | int serial_read_byte(void) | 
 | { | 
 | 	return -1; | 
 | } |