--- a/rules.mk
+++ b/rules.mk
@@ -48,10 +48,11 @@ discover_objs = discover/event.o discove
 	discover/parser-utils.o
 
 # client objs
-ui_common_objs = ui/common/discover-client.o ui/common/loader.o \
-	ui/common/ui-system.o ui/common/timer.o ui/common/url.o
-ncurses_objs = ui/ncurses/nc-scr.o ui/ncurses/nc-menu.o \
-	 ui/ncurses/nc-ked.o ui/ncurses/nc-cui.o
+ui_common_objs = ui/common/discover-client.o ui/common/joystick.o \
+	ui/common/loader.o ui/common/ui-system.o ui/common/timer.o \
+	ui/common/url.o
+ncurses_objs = ui/ncurses/nc-scr.o ui/ncurses/nc-menu.o ui/ncurses/nc-ked.o \
+	ui/ncurses/nc-cui.o
 twin_objs = ui/twin/pb-twin.o
 
 # Makefiles
--- /dev/null
+++ b/ui/common/joystick.c
@@ -0,0 +1,106 @@
+/*
+ *  Copyright (C) 2009 Sony Computer Entertainment Inc.
+ *  Copyright 2009 Sony Corp.
+ *
+ *  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; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "log/log.h"
+#include "talloc/talloc.h"
+#include "joystick.h"
+
+/**
+ * pjs_process_event - Read joystick event and map to UI key code.
+ *
+ * Returns a map routine UI key code or zero.
+ */
+
+int pjs_process_event(const struct pjs *pjs)
+{
+	int result;
+	struct js_event e;
+
+	assert(pjs->fd);
+
+	result = read(pjs->fd, &e, sizeof(e));
+
+	if (result != sizeof(e)) {
+		pb_log("%s: read failed: %s\n", __func__, strerror(errno));
+		return 0;
+	}
+
+	return pjs->map(&e);
+}
+
+/**
+ * pjs_destructor - The talloc destructor for a joystick handler.
+ */
+
+static int pjs_destructor(void *arg)
+{
+	struct pjs *pjs = pjs_from_arg(arg);
+
+	close(pjs->fd);
+	pjs->fd = 0;
+
+	return 0;
+}
+
+/**
+ * pjs_init - Initialize the joystick event handler.
+ */
+
+struct pjs *pjs_init(void *ctx, int (*map)(const struct js_event *))
+{
+	static const char dev_name[] = "/dev/input/js0";
+	struct pjs *pjs;
+
+	pjs = talloc_zero(ctx, struct pjs);
+
+	if (!pjs)
+		return NULL;
+
+	pjs->map = map;
+	pjs->fd = open(dev_name, O_RDONLY | O_NONBLOCK);
+
+	if (pjs->fd < 0) {
+		pb_log("%s: open %s failed: %s\n", __func__, dev_name,
+			strerror(errno));
+		goto out_err;
+	}
+
+	talloc_set_destructor(pjs, pjs_destructor);
+
+	pb_log("%s: using %s\n", __func__, dev_name);
+
+	return pjs;
+
+out_err:
+	close(pjs->fd);
+	pjs->fd = 0;
+	talloc_free(pjs);
+	return NULL;
+}
--- /dev/null
+++ b/ui/common/joystick.h
@@ -0,0 +1,47 @@
+/*
+ *  Copyright (C) 2009 Sony Computer Entertainment Inc.
+ *  Copyright 2009 Sony Corp.
+ *
+ *  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; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if !defined(_PB_JOYSTICK_H)
+#define _PB_JOYSTICK_H
+
+#include <linux/joystick.h>
+
+/**
+ * struct pjs - Petitboot joystick event handler.
+ * @map: Routine to map from a Linux struct js_event to a ui key code.
+ */
+
+struct pjs {
+	int fd;
+	int (*map)(const struct js_event *e);
+};
+
+struct pjs *pjs_init(void *ctx, int (*map)(const struct js_event *));
+int pjs_process_event(const struct pjs *pjs);
+
+static inline struct pjs *pjs_from_arg(void *arg)
+{
+	return arg;
+}
+
+static inline int pjs_get_fd(const struct pjs *pjs)
+{
+	return pjs->fd;
+}
+
+#endif
--- a/ui/ncurses/nc-cui.c
+++ b/ui/ncurses/nc-cui.c
@@ -230,6 +230,10 @@ struct nc_scr *cui_set_current(struct cu
 	return old;
 }
 
+/**
+ * cui_process_key - Process input on stdin.
+ */
+
 static int cui_process_key(void *arg)
 {
 	struct cui *cui = cui_from_arg(arg);
@@ -243,6 +247,24 @@ static int cui_process_key(void *arg)
 }
 
 /**
+ * cui_process_js - Process joystick events.
+ */
+
+static int cui_process_js(void *arg)
+{
+	struct cui *cui = cui_from_arg(arg);
+	int c;
+
+	c = pjs_process_event(cui->pjs);
+
+	if (c) {
+		ungetch(c);
+		cui_process_key(arg);
+	}
+
+	return 0;
+}
+/**
  * cui_client_process_socket - Process a socket event from the discover server.
  */
 
@@ -511,7 +533,8 @@ static struct discover_client_ops cui_cl
  */
 
 struct cui *cui_init(void* platform_info,
-	int (*on_kexec)(struct cui *, struct cui_opt_data *))
+	int (*on_kexec)(struct cui *, struct cui_opt_data *),
+	int (*js_map)(const struct js_event *e))
 {
 	struct cui *cui;
 	struct discover_client *client;
@@ -557,6 +580,15 @@ struct cui *cui_init(void* platform_info
 
 	waiter_register(STDIN_FILENO, WAIT_IN, cui_process_key, cui);
 
+	if (js_map) {
+
+		cui->pjs = pjs_init(cui, js_map);
+
+		if (cui->pjs)
+			waiter_register(pjs_get_fd(cui->pjs), WAIT_IN,
+				cui_process_js, cui);
+	}
+
 	return cui;
 
 fail_client_init:
--- a/ui/ncurses/nc-cui.h
+++ b/ui/ncurses/nc-cui.h
@@ -21,6 +21,7 @@
 
 #include <signal.h>
 
+#include "ui/common/joystick.h"
 #include "ui/common/timer.h"
 #include "nc-menu.h"
 #include "nc-ked.h"
@@ -53,13 +54,15 @@ struct cui {
 	struct nc_scr *current;
 	struct pmenu *main;
 	struct ui_timer timer;
+	struct pjs *pjs;
 	void *platform_info;
 	unsigned int default_item;
 	int (*on_kexec)(struct cui *cui, struct cui_opt_data *cod);
 };
 
 struct cui *cui_init(void* platform_info,
-	int (*on_kexec)(struct cui *, struct cui_opt_data *));
+	int (*on_kexec)(struct cui *, struct cui_opt_data *),
+	int (*js_map)(const struct js_event *e));
 struct nc_scr *cui_set_current(struct cui *cui, struct nc_scr *scr);
 int cui_run(struct cui *cui, struct pmenu *main, unsigned int default_item);
 int cui_ked_run(struct pmenu_item *item);
--- a/ui/ncurses/nc-ked.h
+++ b/ui/ncurses/nc-ked.h
@@ -20,6 +20,7 @@
 #define _PB_NC_KED_H
 
 #include <assert.h>
+#include <linux/input.h> /* This must be included before ncurses.h */
 #include <form.h>
 
 #include "pb-protocol/pb-protocol.h"
--- a/ui/ncurses/nc-menu.c
+++ b/ui/ncurses/nc-menu.c
@@ -204,7 +204,7 @@ static void pmenu_process_key(struct nc_
 		if (c == ERR)
 			return;
 
-		/* DBGS("%d (%o)\n", c, c); */
+		if (1) DBGS("%d (%o)\n", c, c);
 
 		if (menu->hot_key)
 			c = menu->hot_key(menu, item, c);
--- a/ui/ncurses/nc-menu.h
+++ b/ui/ncurses/nc-menu.h
@@ -20,6 +20,7 @@
 #define _PB_NC_MENU_H
 
 #include <assert.h>
+#include <linux/input.h> /* This must be included before ncurses.h */
 #include <menu.h>
 
 #include "log/log.h"
--- a/ui/ncurses/nc-scr.h
+++ b/ui/ncurses/nc-scr.h
@@ -19,6 +19,7 @@
 #if !defined(_PB_NC_SCR_H)
 #define _PB_NC_SCR_H
 
+#include <linux/input.h> /* This must be included before ncurses.h */
 #include <ncurses.h>
 
 #define DBG(fmt, args...) pb_log("DBG: " fmt, ## args)
--- a/ui/ncurses/pb-cui.c
+++ b/ui/ncurses/pb-cui.c
@@ -269,7 +269,7 @@ int main(int argc, char *argv[])
 		return EXIT_FAILURE;
 	}
 
-	pb.cui = cui_init(&pb, pb_kexec_cb);
+	pb.cui = cui_init(&pb, pb_kexec_cb, NULL);
 
 	if (!pb.cui)
 		return EXIT_FAILURE;
--- a/ui/ncurses/ps3-cui.c
+++ b/ui/ncurses/ps3-cui.c
@@ -157,6 +157,83 @@ static struct ps3_cui *ps3_from_item(str
 }
 
 /**
+ * ps3_sixaxis_map - Map a Linux joystick event to an ncurses key code.
+ *
+ */
+
+static int ps3_sixaxis_map(const struct js_event *e)
+{
+#if 0
+	static const int axis_map[] = {
+		0,		/*   0  Left thumb X	*/
+		0,		/*   1  Left thumb Y	*/
+		0,		/*   2  Right thumb X	*/
+		0,		/*   3  Right thumb Y	*/
+		0,		/*   4  nothing		*/
+		0,		/*   5  nothing		*/
+		0,		/*   6  nothing		*/
+		0,		/*   7  nothing		*/
+		0,		/*   8  Dpad Up		*/
+		0,		/*   9  Dpad Right	*/
+		0,		/*  10  Dpad Down	*/
+		0,		/*  11  Dpad Left	*/
+		0,		/*  12  L2		*/
+		0,		/*  13  R2		*/
+		0,		/*  14  L1		*/
+		0,		/*  15  R1		*/
+		0,		/*  16  Triangle	*/
+		0,		/*  17  Circle		*/
+		0,		/*  18  Cross		*/
+		0,		/*  19  Square		*/
+		0,		/*  20  nothing		*/
+		0,		/*  21  nothing		*/
+		0,		/*  22  nothing		*/
+		0,		/*  23  nothing		*/
+		0,		/*  24  nothing		*/
+		0,		/*  25  nothing		*/
+		0,		/*  26  nothing		*/
+		0,		/*  27  nothing		*/
+	};
+#endif
+	static const int button_map[] = {
+		0,		/*   0  Select		*/
+		0,		/*   1  L3		*/
+		0,		/*   2  R3		*/
+		0,		/*   3  Start		*/
+		KEY_UP,		/*   4  Dpad Up		*/
+		0,		/*   5  Dpad Right	*/
+		KEY_DOWN,	/*   6  Dpad Down	*/
+		0,		/*   7  Dpad Left	*/
+		KEY_UP,		/*   8  L2		*/
+		KEY_DOWN,	/*   9  R2		*/
+		KEY_HOME,	/*  10  L1		*/
+		KEY_END,	/*  11  R1		*/
+		0,		/*  12  Triangle	*/
+		0,		/*  13  Circle		*/
+		13,		/*  14  Cross		*/
+		0,		/*  15  Square		*/
+		0,		/*  16  PS Button	*/
+		0,		/*  17  nothing		*/
+		0,		/*  18  nothing		*/
+	};
+
+	if (!e->value)
+		return 0;
+
+	if (e->type == JS_EVENT_BUTTON
+		&& e->number < sizeof(button_map) / sizeof(button_map[0]))
+		return button_map[e->number];
+
+#if 0
+	if (e->type == JS_EVENT_AXIS
+		&& e->number < sizeof(axis_map) / sizeof(axis_map[0]))
+		return axis_map[e->number];
+#endif
+
+	return 0;
+}
+
+/**
  * ps3_set_mode - Set video mode helper.
  *
  * Runs ps3_set_video_mode().
@@ -550,7 +627,7 @@ int main(int argc, char *argv[])
 	if (!result && (ps3.values.video_mode != (uint16_t)mode))
 		ps3_set_video_mode(ps3.values.video_mode);
 
-	ps3.cui = cui_init(&ps3, ps3_kexec_cb);
+	ps3.cui = cui_init(&ps3, ps3_kexec_cb, ps3_sixaxis_map);
 
 	if (!ps3.cui)
 		return EXIT_FAILURE;
