diff -urN kernel26/arch/arm/mach-pxa/h4000/Kconfig kernel26_h4000/arch/arm/mach-pxa/h4000/Kconfig
--- kernel26/arch/arm/mach-pxa/h4000/Kconfig	2005-01-23 18:12:33.000000000 -0700
+++ kernel26_h4000/arch/arm/mach-pxa/h4000/Kconfig	2005-02-01 20:00:22.000000000 -0700
@@ -14,3 +14,8 @@
 config IPAQ_H4000_BUTTONS
         tristate "Buttons support"
         depends on IPAQ_HANDHELD && MACH_H4000 && IPAQ_ASIC3
+        
+config IPAQ_H4000_KBD
+        tristate "Keyboard support for h4300"
+        depends on IPAQ_HANDHELD && MACH_H4000 && IPAQ_ASIC3
+
diff -urN kernel26/arch/arm/mach-pxa/h4000/Makefile kernel26_h4000/arch/arm/mach-pxa/h4000/Makefile
--- kernel26/arch/arm/mach-pxa/h4000/Makefile	2005-01-07 20:54:27.000000000 -0700
+++ kernel26_h4000/arch/arm/mach-pxa/h4000/Makefile	2005-02-01 19:59:29.000000000 -0700
@@ -6,4 +6,5 @@
 obj-$(CONFIG_IPAQ_H4000_LCD)    += h4000_lcd.o
 obj-$(CONFIG_IPAQ_H4000_TS)     += h4000_ts.o
 obj-$(CONFIG_IPAQ_H4000_BUTTONS)        += h4000_buttons.o
+obj-$(CONFIG_IPAQ_H4000_KBD)    += h4000_kbd.o
 
diff -urN kernel26/arch/arm/mach-pxa/h4000/h4000_kbd.c kernel26_h4000/arch/arm/mach-pxa/h4000/h4000_kbd.c
--- kernel26/arch/arm/mach-pxa/h4000/h4000_kbd.c	1969-12-31 17:00:00.000000000 -0700
+++ kernel26_h4000/arch/arm/mach-pxa/h4000/h4000_kbd.c	2005-02-01 19:58:18.000000000 -0700
@@ -0,0 +1,250 @@
+/* h4000_kbd.c 
+ *
+ * License:     GPL-2
+ * Test implementation by:
+ *   Shawn Anderson sa@xmission.com
+ */
+
+#include <asm/irq.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <asm/arch/h4000-gpio.h>
+#include <asm/mach/irq.h>
+#include <asm/arch/irqs.h>
+
+#define KEY_FUNC 0x32
+
+#define asic3_p2v(x) (0xf2000000+((x) & 0x01ffffff)+(((x) & 0x1c000000) >> 1))
+#define ASIC3_REG(r) (*((volatile u16 *) (asic3_p2v(r))))
+#define H4000_IRQ(x)      (IRQ_BOARD_START + (x))
+#define H4000_KBD_IRQ H4000_IRQ(0)
+#define H4000_REC_BTN_IRQ H4000_IRQ(1)
+
+
+static struct input_dev h4000kbd_dev;
+
+static char *h4000kbd_name = "h4300 ipaq keyboard";
+static char *h4000kbd_phys = "h4000kbd/input0";
+
+static unsigned int h4000kbd_keycode[0x80] = {
+/*0*/   0, 0, 0, 0, 0, 0, 0, 0,
+	0,         0,              KEY_CALENDAR,  KEY_CONTACTS,
+	0,         0,              KEY_MEMO,      KEY_SYSRQ,
+/*1*/ 	KEY_R,     KEY_RIGHT,      KEY_W,         KEY_E,
+	KEY_SELECT,KEY_U,          KEY_I,         KEY_P,
+	KEY_F,     KEY_Y,          KEY_Q,         KEY_D,
+	KEY_DOWN,  KEY_K,          KEY_O,         KEY_BACKSPACE,
+/*2*/ 	KEY_X,     KEY_H,          KEY_A,         KEY_S,
+	KEY_T,     KEY_J,          KEY_L,         KEY_ENTER, 
+	KEY_C,     KEY_B,          KEY_LEFTSHIFT, KEY_Z,
+	KEY_G,     KEY_M,          KEY_BACKSLASH/*!*/, KEY_APOSTROPHE,
+/*3*/	KEY_ESC,   KEY_SPACE,      0/*KEY_FUNC*/, KEY_TAB,
+	KEY_V,     KEY_N,          KEY_COMMA,     KEY_QUESTION,
+	KEY_LEFT,  KEY_UP,         0, 0,
+	KEY_LEFTCTRL,  KEY_DOT,    0, 0,
+/*4*/   0, 0, 0, 0, 0, 0, 0, 0,
+	0,         0,              KEY_PROG1,     KEY_PROG2,
+	0,         0,              KEY_PROG3,     KEY_PROG4,
+/*5*/   0/*%*/,    KEY_RIGHT,      0/*@*/,        0/*$*/,
+	KEY_SELECT,KEY_1,          KEY_2,         KEY_SLASH,
+	KEY_EQUAL, KEY_MINUS,      0/*~*/,        0,/*:*/
+	KEY_DOWN,  KEY_5,          KEY_3,         KEY_DELETE,
+/*6*/   KEY_SEMICOLON/*;*/,    KEY_KPPLUS,     0/*{*/,        0/*}*/,
+	0/*_*/,    KEY_4,          KEY_6,         KEY_ENTER, 
+	KEY_LEFT,  KEY_RIGHT,      KEY_CAPSLOCK,  0/*&*/,
+	KEY_UP,    KEY_8,          KEY_9,         0/*"*/,
+/*7*/   KEY_ESC,   KEY_COMPOSE,    0/*KEY_FUNC*/, KEY_TAB,
+	KEY_DOWN,  KEY_7,          KEY_0,         0/*#*/,
+	KEY_LEFT,  KEY_UP,         0, 0,
+	KEY_MENU,  KEY_KPASTERISK, 0, 0
+};
+
+static int asic3_spi_process_byte( unsigned char data )
+{
+#define ASIC3_SPI_Control (*((volatile u16*) (asic3_p2v(0x0c000400))))
+#define ASIC3_SPI_Data    (*((volatile u16*) (asic3_p2v(0x0c000408))))
+#define SPI_CONTROL_SEL   (1 << 6)
+#define SPI_CONTROL_SPIE  (1 << 5)
+#define SPI_CONTROL_SPE   (1 << 4)
+	unsigned long flags;
+	unsigned int  timeout;
+	int           result = 0;
+
+	local_irq_save(flags);
+	ASIC3_SPI_Control |= SPI_CONTROL_SPIE;          /* Enable interrupts */
+	ASIC3_SPI_Data     = data;                              /* Send data */
+	ASIC3_SPI_Control |= SPI_CONTROL_SPE;          /* Start the transfer */
+
+	for (timeout = 255; timeout > 0; timeout--) {
+		if ( !(ASIC3_SPI_Control & SPI_CONTROL_SPE )) {
+			udelay(20);    /* wait for a while, or we'll miss it */
+			result = ASIC3_SPI_Data;
+			break;
+		}
+	}
+	ASIC3_SPI_Control &= ~SPI_CONTROL_SPIE;        /* Disable interrupts */
+	local_irq_restore(flags);
+	return result & 0xff;
+}
+
+static void h4000_mask_irq(unsigned int irq)
+{
+	if (irq == H4000_KBD_IRQ)
+		ASIC3_REG(0x0c000100) |= 0x2;
+	else if (irq == H4000_REC_BTN_IRQ)
+		ASIC3_REG(0x0c000300) |= 0x4;
+}
+
+static void h4000_unmask_irq(unsigned int irq)
+{
+	if (irq == H4000_KBD_IRQ) {
+		ASIC3_REG(0x0c000124) &= ~(0x2);
+		ASIC3_REG(0x0c000100) &= ~(0x2);
+	}
+	else if (irq == H4000_REC_BTN_IRQ) {
+		ASIC3_REG(0x0c000324) &= ~(0x4);
+		ASIC3_REG(0x0c000300) &= ~(0x4);
+	}
+}
+
+static struct irqchip h4000_irq_chip = {
+	.ack        = h4000_mask_irq,
+	.mask       = h4000_mask_irq,
+	.unmask     = h4000_unmask_irq,
+};
+
+static irqreturn_t h4000_kbd(int irq, void *dev_id, struct pt_regs *regs)
+{
+#define FN_PRESS (1<<0)
+#define FN_REL   (1<<1)
+#define KEY_REL  (1<<2)
+	int pressed;
+	unsigned char scancode = 0;
+	static unsigned char fn_flags = 0;
+
+	scancode = asic3_spi_process_byte(0);
+
+	if (scancode & 0x80) {
+		pressed = 0;
+		scancode &= ~0x80;
+	} else
+		pressed = 1;
+
+	if (scancode < 0x80) {
+		if (scancode == KEY_FUNC) {
+			if (pressed)
+				fn_flags = FN_PRESS;
+			else if (fn_flags & KEY_REL)
+				fn_flags = 0;
+			else
+				fn_flags |= FN_REL;
+		}
+		if (scancode != KEY_FUNC && fn_flags) {
+			scancode = h4000kbd_keycode[scancode+0x40];
+			if (!pressed) {
+				if (fn_flags & FN_REL)
+					fn_flags = 0;
+				else
+					fn_flags |= KEY_REL;
+			}
+		} else
+			scancode = h4000kbd_keycode[scancode]; 
+
+		input_regs(&h4000kbd_dev, regs);
+		input_report_key(&h4000kbd_dev, scancode, pressed);
+		input_sync(&h4000kbd_dev);
+	} else
+		printk("%s: error code 0x%x\n", __FUNCTION__, scancode);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t h4000_rec_button(int irq, void *dev_id, struct pt_regs *regs)
+{
+	input_regs(&h4000kbd_dev, regs);
+	input_report_key(&h4000kbd_dev, KEY_RECORD, 1);
+	input_report_key(&h4000kbd_dev, KEY_RECORD, 0);
+	input_sync(&h4000kbd_dev);
+	return IRQ_HANDLED;
+}
+
+static void h4000_irq_handler(unsigned int irq, struct irqdesc *desc,
+		struct pt_regs *regs)
+{
+	// H4000_KBD_IRQ, H4000_REC_BTN_IRQ, H4000_JACK_IN_IRQ 
+	if (ASIC3_REG(0x0c000124) & 0x2)
+		irq = H4000_KBD_IRQ;
+	else if (ASIC3_REG(0x0c000324) & 0x4)
+		irq = H4000_REC_BTN_IRQ;
+
+	desc = irq_desc + irq;
+	desc->handle(irq, desc, regs);
+}
+
+static int __init h4000kbd_init(void)
+{
+	int i;
+	unsigned int irq = 0;
+	// if (!is_h4300) return -EIO
+	init_input_dev(&h4000kbd_dev);
+
+	h4000kbd_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+	h4000kbd_dev.keycode = h4000kbd_keycode;
+	h4000kbd_dev.keycodesize = sizeof(unsigned char);
+	h4000kbd_dev.keycodemax = ARRAY_SIZE(h4000kbd_keycode);
+
+	for (i = 0; i < h4000kbd_dev.keycodemax; i++)
+		if (h4000kbd_keycode[i])
+			set_bit(h4000kbd_keycode[i], h4000kbd_dev.keybit);
+
+	set_bit(KEY_RECORD, h4000kbd_dev.keybit);
+
+	set_irq_type(IRQ_GPIO(GPIO_NR_H4000_ASIC3_IRQ), IRQT_RISING);
+	set_irq_chained_handler(IRQ_GPIO(GPIO_NR_H4000_ASIC3_IRQ),
+			h4000_irq_handler);
+
+	for(irq = H4000_IRQ(0); irq <= H4000_IRQ(1); irq++) {
+		set_irq_chip(   irq, &h4000_irq_chip);
+		set_irq_handler(irq, do_level_IRQ);
+		set_irq_flags(  irq, IRQF_VALID | IRQF_PROBE);
+	}
+
+	if (request_irq(H4000_KBD_IRQ,
+			&h4000_kbd, SA_INTERRUPT, "keyboard", NULL)) {
+		printk("request_irq failed for H4000_KBD_IRQ\n");
+		return -1;
+	}
+	
+	if (request_irq(H4000_REC_BTN_IRQ,
+			&h4000_rec_button,SA_INTERRUPT,"record button", NULL)) {
+		printk("request_irq failed for H4000_REC_BTN_IRQ\n");
+		return -1;
+	}
+
+
+	h4000kbd_dev.name       = h4000kbd_name;
+	h4000kbd_dev.phys       = h4000kbd_phys;
+	h4000kbd_dev.id.bustype = BUS_HOST;
+	h4000kbd_dev.id.vendor  = 0x1;
+	h4000kbd_dev.id.product = 0x1;
+	h4000kbd_dev.id.version = 0x1;
+
+	input_register_device(&h4000kbd_dev);
+
+	printk(KERN_INFO "input: %s\n", h4000kbd_name);
+
+	return 0;
+}
+static void __exit h4000kbd_exit(void)
+{
+	input_unregister_device(&h4000kbd_dev);
+	free_irq(H4000_KBD_IRQ, &h4000_kbd);
+	free_irq(H4000_REC_BTN_IRQ, &h4000_rec_button);
+}
+
+module_init(h4000kbd_init);
+module_exit(h4000kbd_exit);
+
+/* vim: set ts=8 tw=80 shiftwidth=8 noet: */

