Handle both known versions of KEY_MAX when retrieving the button map;
avoid segfaulting in any case!

--- joystick-20051019.orig/utils/jstest.c
+++ joystick-20051019/utils/jstest.c
@@ -49,6 +49,8 @@
 #include <linux/input.h>
 #include <linux/joystick.h>
 
+#include "axbtnmap.h"
+
 char *axis_names[ABS_MAX + 1] = {
 "X", "Y", "Z", "Rx", "Ry", "Rz", "Throttle", "Rudder", 
 "Wheel", "Gas", "Brake", "?", "?", "?", "?", "?",
@@ -74,8 +76,9 @@
 	unsigned char buttons = 2;
 	int version = 0x000800;
 	char name[NAME_LENGTH] = "Unknown";
-	uint16_t btnmap[KEY_MAX - BTN_MISC + 1];
-	uint8_t axmap[ABS_MAX + 1];
+	uint16_t btnmap[BTNMAP_SIZE];
+	uint8_t axmap[AXMAP_SIZE];
+	int btnmapok = 1;
 
 	if (argc < 2 || argc > 3 || !strcmp("--help", argv[1])) {
 		puts("");
@@ -99,15 +102,23 @@
 	ioctl(fd, JSIOCGAXES, &axes);
 	ioctl(fd, JSIOCGBUTTONS, &buttons);
 	ioctl(fd, JSIOCGNAME(NAME_LENGTH), name);
-	ioctl(fd, JSIOCGAXMAP, axmap);
-	ioctl(fd, JSIOCGBTNMAP, btnmap);
 
+	getaxmap(fd, axmap);
+	getbtnmap(fd, btnmap);
 
 	printf("Driver version is %d.%d.%d.\n",
 		version >> 16, (version >> 8) & 0xff, version & 0xff);
 
-	if (buttons > 0 && btnmap[0] < BTN_MISC) {
+	/* Determine whether the button map is usable. */
+	for (i = 0; btnmapok && i < buttons; i++) {
+		if (btnmap[i] < BTN_MISC || btnmap[i] > KEY_MAX) {
+			btnmapok = 0;
+			break;
+		}
+	}
+	if (!btnmapok) {
 		/* btnmap out of range for names. Don't print any. */
+		puts("jstest is not fully compatible with your kernel. Unable to retrieve button map!");
 		printf("Joystick (%s) has %d axes ", name, axes);
 		printf("and %d buttons.\n", buttons);
 	} else {
@@ -117,8 +128,9 @@
 		puts(")");
 
 		printf("and %d buttons (", buttons);
-		for (i = 0; i < buttons; i++)
+		for (i = 0; i < buttons; i++) {
 			printf("%s%s", i > 0 ? ", " : "", button_names[btnmap[i] - BTN_MISC]);
+		}
 		puts(").");
 	}
 
--- joystick-20051019.orig/utils/Makefile
+++ joystick-20051019/utils/Makefile
@@ -50,9 +50,17 @@
 ffmvforce: ffmvforce.o
 	$(CC) $^ -o $@ $(LDFLAGS) -g -lm `sdl-config --libs`
 
-jscal: jscal.o
+axbtnmap.o: axbtnmap.c axbtnmap.h
+
+jscal.o: jscal.c axbtnmap.h
+
+jscal: jscal.o axbtnmap.o
 	$(CC) $(CFLAGS) $(CPPFLAGS) -lm $^ -o $@
 
+jstest.o: jstest.c axbtnmap.h
+
+jstest: jstest.o axbtnmap.o
+
 gencodes: gencodes.c scancodes.h
 	$(CC) $(CFLAGS) $(CPPFLAGS) gencodes.c -o gencodes
 
--- /dev/null
+++ joystick-20051019/utils/axbtnmap.c
@@ -0,0 +1,89 @@
+/*
+ * Axis and button map support functions.
+ * Copyright © 2009 Stephen Kitt <steve@sk2.org>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <errno.h>
+#include <stdint.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <linux/input.h>
+#include <linux/joystick.h>
+
+#include "axbtnmap.h"
+
+/* The following values come from include/joystick.h in the kernel source. */
+#define JSIOCSBTNMAP_LARGE _IOW('j', 0x33, __u16[KEY_MAX_LARGE - BTN_MISC + 1])
+#define JSIOCSBTNMAP_SMALL _IOW('j', 0x33, __u16[KEY_MAX_SMALL - BTN_MISC + 1])
+#define JSIOCGBTNMAP_LARGE _IOR('j', 0x34, __u16[KEY_MAX_LARGE - BTN_MISC + 1])
+#define JSIOCGBTNMAP_SMALL _IOR('j', 0x34, __u16[KEY_MAX_SMALL - BTN_MISC + 1])
+
+int determine_ioctl(int fd, int *ioctls, int *ioctl_used, void *argp)
+{
+	int i, retval = 0;
+
+	/* Try each ioctl in turn. */
+	for (i = 0; ioctls[i]; i++) {
+		if ((retval = ioctl(fd, ioctls[i], argp)) >= 0) {
+			/* The ioctl did something. */
+			*ioctl_used = ioctls[i];
+			return retval;
+		} else if (errno != -EINVAL) {
+			/* Some other error occurred. */
+			return retval;
+		}
+	}
+	return retval;
+}
+
+int getbtnmap(int fd, uint16_t *btnmap)
+{
+	static int jsiocgbtnmap = 0;
+	int ioctls[] = { JSIOCGBTNMAP, JSIOCGBTNMAP_LARGE, JSIOCGBTNMAP_SMALL, 0 };
+
+	if (jsiocgbtnmap != 0) {
+		/* We already know which ioctl to use. */
+		return ioctl(fd, jsiocgbtnmap, btnmap);
+	} else {
+		return determine_ioctl(fd, ioctls, &jsiocgbtnmap, btnmap);
+	}
+}
+
+int setbtnmap(int fd, uint16_t *btnmap)
+{
+	static int jsiocsbtnmap = 0;
+	int ioctls[] = { JSIOCSBTNMAP, JSIOCSBTNMAP_LARGE, JSIOCSBTNMAP_SMALL, 0 };
+
+	if (jsiocsbtnmap != 0) {
+		/* We already know which ioctl to use. */
+		return ioctl(fd, jsiocsbtnmap, btnmap);
+	} else {
+		return determine_ioctl(fd, ioctls, &jsiocsbtnmap, btnmap);
+	}
+}
+
+int getaxmap(int fd, uint8_t *axmap)
+{
+	return ioctl(fd, JSIOCGAXMAP, axmap);
+}
+
+int setaxmap(int fd, uint8_t *axmap)
+{
+	return ioctl(fd, JSIOCSAXMAP, axmap);
+}
--- /dev/null
+++ joystick-20051019/utils/axbtnmap.h
@@ -0,0 +1,65 @@
+/*
+ * Axis and button map support functions.
+ * Copyright © 2009 Stephen Kitt <steve@sk2.org>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __AXBTNMAP_H__
+#define __AXBTNMAP_H__
+
+#include <stdint.h>
+#include <linux/input.h>
+
+/* The following values come from include/input.h in the kernel
+   source; the small variant is used up to version 2.6.27, the large
+   one from 2.6.28 onwards. We need to handle both values because the
+   kernel doesn't; it only expects one of the values, and we need to
+   determine which one at run-time. */
+#define KEY_MAX_LARGE 0x2FF
+#define KEY_MAX_SMALL 0x1FF
+
+/* Axis map size. */
+#define AXMAP_SIZE (ABS_MAX + 1)
+
+/* Button map size. */
+#define BTNMAP_SIZE (KEY_MAX_LARGE - BTN_MISC + 1)
+
+/* Retrieves the current axis map in the given array, which must
+   contain at least AXMAP_SIZE elements. Returns the result of the
+   ioctl(): negative in case of an error, 0 otherwise for kernels up
+   to 2.6.30, the length of the array actually copied for later
+   kernels. */
+int getaxmap(int fd, uint8_t *axmap);
+
+/* Uses the given array as the axis map. The array must contain at
+   least AXMAP_SIZE elements. Returns the result of the ioctl():
+   negative in case of an error, 0 otherwise. */
+int setaxmap(int fd, uint8_t *axmap);
+
+/* Retrieves the current button map in the given array, which must
+   contain at least BTNMAP_SIZE elements. Returns the result of the
+   ioctl(): negative in case of an error, 0 otherwise for kernels up
+   to 2.6.30, the length of the array actually copied for later
+   kernels. */
+int getbtnmap(int fd, uint16_t *btnmap);
+
+/* Uses the given array as the button map. The array must contain at
+   least BTNMAP_SIZE elements. Returns the result of the ioctl():
+   negative in case of an error, 0 otherwise. */
+int setbtnmap(int fd, uint16_t *btnmap);
+
+#endif
+
