From 42642d5905cca4d28a6e21feeca5a81d6749b3da Mon Sep 17 00:00:00 2001 From: Nirenjan Krishnan Date: Sun, 30 Sep 2012 12:38:29 -0700 Subject: [PATCH] Add initial version of X52pro joystick driver This is pretty much bare bones code to get a basic sysfs filesystem in place. It has only code to create sysfs files for the 3 MFD lines. Right now, this needs to be enabled by unplugging the joystick, insmod'ing the x52joy.ko module, rmmod'ing the usbhid module and replugging the joystick. We don't yet have a /dev/input/ interface and there's no interrupt message handling yet. --- .gitignore | 13 +++ driver/Makefile | 9 ++ driver/x52joy.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 247 insertions(+) create mode 100644 .gitignore create mode 100644 driver/Makefile create mode 100644 driver/x52joy.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dae5ed4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Compiled object files +*.ko +*.o +*.mod.* + +# Module files +modules.order +Module.symvers +.*.cmd +.tmp_versions + +# Vim swap files +.*.swp diff --git a/driver/Makefile b/driver/Makefile new file mode 100644 index 0000000..2c692ef --- /dev/null +++ b/driver/Makefile @@ -0,0 +1,9 @@ +obj-m := x52joy.o +KDIR := /lib/modules/$(shell uname -r)/build +PWD := $(shell pwd) + +all: + $(MAKE) -C $(KDIR) M=$(PWD) modules + +clean: + $(MAKE) -C $(KDIR) M=$(PWD) clean diff --git a/driver/x52joy.c b/driver/x52joy.c new file mode 100644 index 0000000..a4fa7aa --- /dev/null +++ b/driver/x52joy.c @@ -0,0 +1,225 @@ +/* + * Saitek X52 Pro HOTAS driver - 1.0 + * + * Copyright (C) 2012 Nirenjan Krishnan (nirenjan@nirenjan.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, version 2. + * + */ + +#ifdef CONFIG_USB_DEBUG + #define DEBUG 1 +#endif + +#include +#include +#include +#include +#include +#include + +#define DRIVER_AUTHOR "Nirenjan Krishnan, nirenjan@gmail.com" +#define DRIVER_DESC "Saitek X52Pro HOTAS Driver" + +#define VENDOR_ID_SAITEK 0x06a3 +#define PRODUCT_ID_X52_PRO 0x0762 + +/* list of devices that work with this driver */ +static struct usb_device_id id_table[] = { + { USB_DEVICE(VENDOR_ID_SAITEK, PRODUCT_ID_X52_PRO) }, + /* + * Future versions of this driver may support the original + * X52 HOTAS joystick, but for now, only the X52 Pro is + * supported. + */ + { }, +}; +MODULE_DEVICE_TABLE (usb, id_table); + +/* + * The X52 MFD supports the following: + * - 3 lines of 16 characters each + * - Clock with HH:MM + * - Date with YYMMDD (IIRC) + */ +#define DRIVER_LINE_SIZE 256 /* Support upto 256 bytes in the driver */ +#define X52_MFD_LINE_SIZE 17 /* Add one for null byte */ + +struct x52_mfd_line { + u8 text[DRIVER_LINE_SIZE]; + u16 length; + u16 current_pos; +}; + +struct x52_joy { + struct usb_device *udev; + u32 led_status; + struct x52_mfd_line line[3]; + u8 time_hour; + u8 time_min; + u16 time_offs2; + u16 time_offs3; + u8 date_year; + u8 date_month; + u8 date_day; + + u8 mode_h24:1; + u8 feat_mfd:1; + u8 feat_led:1; + u8 debug:1; +}; + +/********************************************************************** + * MFD Line manipulation functions + *********************************************************************/ +static void set_text(u8 line_no) +{ + /* TODO */ + return; +} + +static ssize_t show_text_line(struct device *dev, char *buf, u8 line) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct x52_joy *joy = usb_get_intfdata(intf); + + if (joy->feat_mfd) { + return sprintf(buf, "%s\n", joy->line[line].text); + } else { + sprintf(buf, "\n"); + return -EOPNOTSUPP; + } + return 0; +} + +static ssize_t set_text_line(struct device *dev, const char *buf,\ + size_t count, u8 line) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct x52_joy *joy = usb_get_intfdata(intf); + u16 i; + u16 length; + + if (!joy->feat_mfd) { + return -EOPNOTSUPP; + } + + /* Copy the string from src to dst, upto 16 characters max */ + for (i = 0, length = 0; i < DRIVER_LINE_SIZE - 1 && i < count; i++, length++) { + joy->line[line].text[i] = buf[i]; + } + joy->line[line].length = length; + joy->line[line].current_pos = 0; + + /* Append empty bytes until the destination is full */ + for ( ; i < DRIVER_LINE_SIZE; i++) { + /* The X52 pro MFD uses space characters as empty characters */ + joy->line[line].text[i] = 32; + } + set_text(line); + return count; +} + + +#define show_set_text(no) \ +static ssize_t show_text_line##no(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_text_line(dev, buf, no); \ +} \ +static ssize_t set_text_line##no(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + return set_text_line(dev, buf, count, no); \ +} \ +static DEVICE_ATTR(line##no, S_IWUGO | S_IRUGO, show_text_line##no, set_text_line##no); + +show_set_text(1); +show_set_text(2); +show_set_text(3); + +/********************************************************************** + * X52 driver functions + *********************************************************************/ +static int x52_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct x52_joy *joy = NULL; + int retval = -ENOMEM; + + joy = kmalloc(sizeof(*joy), GFP_KERNEL); + if (joy == NULL) { + dev_err(&intf->dev, "Out of memory\n"); + goto error; + } + memset(joy, 0, sizeof(*joy)); + + joy->udev = usb_get_dev(dev); + + /* Set the feature bits */ + joy->feat_mfd = 1; + joy->feat_led = 1; + + usb_set_intfdata(intf, joy); + + device_create_file(&intf->dev, &dev_attr_line1); + device_create_file(&intf->dev, &dev_attr_line2); + device_create_file(&intf->dev, &dev_attr_line3); + + dev_info(&intf->dev, "X52 device now attached\n"); + return 0; + +error: + kfree(joy); + return retval; +} + +static void x52_disconnect(struct usb_interface *intf) +{ + struct x52_joy *joy; + + joy = usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); + + device_remove_file(&intf->dev, &dev_attr_line1); + device_remove_file(&intf->dev, &dev_attr_line2); + device_remove_file(&intf->dev, &dev_attr_line3); + + usb_put_dev(joy->udev); + kfree(joy); + + dev_info(&intf->dev, "X52 device now disconnected\n"); +} + +static struct usb_driver x52_driver = { + .name = "X52", + .probe = x52_probe, + .disconnect = x52_disconnect, + .id_table = id_table, +}; + +static int __init x52_init(void) +{ + int retval = 0; + + retval = usb_register(&x52_driver); + if (retval) { + err("usb_register failed (errno=%d)\n", retval); + } + + return retval; +} + +static void __exit x52_exit(void) +{ + usb_deregister(&x52_driver); +} + +module_init (x52_init); +module_exit (x52_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL v2"); +