Merge branch 'experimental'

This brings in changes to make the kernel module act like an input
device. However, this version does have one flaw, in that the axes
input for all the centered axes does not work. This is most likely
due to a bug in the invocation of input_set_abs_params. Only the
throttle and slider axes work, as does the hat. The other (minor)
issue is that the hat values show up as -32767 and +32767 instead
of -1 and +1 like the usbhid driver.
pull/3/head
Nirenjan Krishnan 2012-10-11 21:10:26 -07:00
commit 827c13bf85
4 changed files with 323 additions and 14 deletions

View File

@ -1,5 +1,5 @@
obj-m := saitek_x52.o
saitek_x52-objs := x52joy.o x52joy_commands.o
saitek_x52-objs := x52joy.o x52joy_commands.o x52joy_input.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

View File

@ -19,6 +19,8 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/input.h>
#include <linux/usb/input.h>
#include "x52joy_commands.h"
#include "x52joy_common.h"
@ -27,16 +29,6 @@
#define DRIVER_DESC "Saitek X52Pro HOTAS Driver"
#define DRIVER_VERSION "1.0"
#define X52TYPE_X52 1
#define X52TYPE_X52PRO 2
#define X52TYPE_UNKNOWN 0
#define VENDOR_ID_SAITEK 0x06a3
#define PRODUCT_ID_X52_PRO 0x0762
#define X52FLAGS_SUPPORTS_MFD (1 << 0)
#define X52FLAGS_SUPPORTS_LED (1 << 1)
static const struct x52_device {
u16 idVendor;
u16 idProduct;
@ -393,6 +385,8 @@ static int x52_probe(struct usb_interface *intf,
{
struct usb_device *udev = interface_to_usbdev(intf);
struct x52_joy *joy = NULL;
struct input_dev *idev = NULL;
struct usb_endpoint_descriptor *ep_irq_in;
int retval = -ENOMEM;
int i;
@ -405,12 +399,61 @@ static int x52_probe(struct usb_interface *intf,
joy = kzalloc(sizeof(*joy), GFP_KERNEL);
if (joy == NULL) {
dev_err(&intf->dev, "Out of memory\n");
dev_err(&intf->dev, "Out of memory: Cannot create joystick\n");
goto error;
}
joy->type = x52_devices[i].type;
idev = input_allocate_device();
if (idev == NULL) {
dev_err(&intf->dev, "Out of memory: Cannot create input\n");
goto error;
}
joy->idata = usb_alloc_coherent(udev, X52_PACKET_LEN, GFP_KERNEL,
&joy->idata_dma);
if (!joy->idata) {
dev_err(&intf->dev, "Out of memory: Cannot alloc coherent buffer\n");
goto error;
}
joy->irq_in = usb_alloc_urb(0, GFP_KERNEL);
if (!joy->irq_in) {
dev_err(&intf->dev, "Out of memory: Cannot alloc IRQ URB\n");
goto error1;
}
joy->udev = usb_get_dev(udev);
joy->idev = idev;
usb_make_path(udev, joy->phys, sizeof(joy->phys));
strlcat(joy->phys, "/input0", sizeof(joy->phys));
idev->name = x52_devices[i].name;
idev->phys = joy->phys;
usb_to_input_id(udev, &idev->id);
idev->dev.parent = &intf->dev;
input_set_drvdata(idev, joy);
idev->open = x52_open;
idev->close = x52_close;
x52_setup_input(idev);
ep_irq_in = &intf->cur_altsetting->endpoint[0].desc;
usb_fill_int_urb(joy->irq_in, udev,
usb_rcvintpipe(udev, ep_irq_in->bEndpointAddress),
joy->idata, X52_PACKET_LEN, x52_irq_handler,
joy, ep_irq_in->bInterval);
joy->irq_in->transfer_dma = joy->idata_dma;
joy->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
retval = input_register_device(joy->idev);
if (retval) {
goto error2;
}
/* Set the feature bits */
joy->feat_mfd = !!(x52_devices[i].flags & X52FLAGS_SUPPORTS_MFD);
joy->feat_led = !!(x52_devices[i].flags & X52FLAGS_SUPPORTS_LED);
@ -457,7 +500,12 @@ static int x52_probe(struct usb_interface *intf,
dev_info(&intf->dev, "X52 device now attached\n");
return 0;
error2:
usb_free_urb(joy->irq_in);
error1:
usb_free_coherent(udev, X52_PACKET_LEN, joy->idata, joy->idata_dma);
error:
input_free_device(idev);
kfree(joy);
return retval;
}
@ -469,6 +517,11 @@ static void x52_disconnect(struct usb_interface *intf)
joy = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL);
usb_kill_urb(joy->irq_in);
usb_free_urb(joy->irq_in);
usb_free_coherent(joy->udev, X52_PACKET_LEN, joy->idata, joy->idata_dma);
input_free_device(joy->idev);
if (joy->feat_mfd) {
device_remove_file(&intf->dev, &dev_attr_mfd_line1);
device_remove_file(&intf->dev, &dev_attr_mfd_line2);
@ -513,7 +566,7 @@ static void x52_disconnect(struct usb_interface *intf)
}
static struct usb_driver x52_driver = {
.name = "X52",
.name = "saitek_x52",
.probe = x52_probe,
.disconnect = x52_disconnect,
.id_table = id_table,

View File

@ -13,6 +13,7 @@
#define X52JOY_COMMON_H
#include <linux/usb.h>
#include <linux/input.h>
#include "x52joy_map.h"
@ -20,7 +21,9 @@ struct x52_joy {
struct usb_device *udev;
struct urb *irq_in;
unsigned char *idata;
struct input_dev *dev;
dma_addr_t idata_dma;
struct input_dev *idev;
char phys[64];
u32 led_status;
struct x52_mfd_line line[X52_MFD_LINES];
@ -49,6 +52,18 @@ struct x52_joy {
u8 :5;
};
#define X52_PACKET_LEN 16
#define X52TYPE_X52 1
#define X52TYPE_X52PRO 2
#define X52TYPE_UNKNOWN 0
#define VENDOR_ID_SAITEK 0x06a3
#define PRODUCT_ID_X52_PRO 0x0762
#define X52FLAGS_SUPPORTS_MFD (1 << 0)
#define X52FLAGS_SUPPORTS_LED (1 << 1)
int set_text(struct x52_joy *joy, u8 line_no);
int set_brightness(struct x52_joy *joy, u8 target);
int set_led(struct x52_joy *joy, u8 target);
@ -56,4 +71,9 @@ int set_date(struct x52_joy *joy);
int set_shift(struct x52_joy *joy);
int set_blink(struct x52_joy *joy);
void x52_irq_handler(struct urb *urb);
void x52_setup_input(struct input_dev *idev);
int x52_open (struct input_dev *idev);
void x52_close (struct input_dev *idev);
#endif /* !defined X52JOY_COMMON_H */

View File

@ -0,0 +1,236 @@
/*
* Saitek X52 Pro HOTAS driver
*
* 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 <linux/input.h>
#include <linux/usb/input.h>
#include "x52joy_commands.h"
#include "x52joy_common.h"
#define X52PRO_X_SHIFT 0
#define X52PRO_X_MASK 0x3ff
#define X52PRO_Y_SHIFT 10
#define X52PRO_Y_MASK 0x3ff
#define X52PRO_RZ_SHIFT 20
#define X52PRO_RZ_MASK 0x3ff
static void x52pro_decode_urb(struct x52_joy *joy, unsigned char *data)
{
struct input_dev *idev = joy->idev;
u32 stick_axis = le32_to_cpu(&data[0]);
input_report_key(idev, BTN_TRIGGER_HAPPY1, data[8] & 0x01);
input_report_key(idev, BTN_TRIGGER_HAPPY2, data[8] & 0x02);
input_report_key(idev, BTN_TRIGGER_HAPPY3, data[8] & 0x04);
input_report_key(idev, BTN_TRIGGER_HAPPY4, data[8] & 0x08);
input_report_key(idev, BTN_TRIGGER_HAPPY5, data[8] & 0x10);
input_report_key(idev, BTN_TRIGGER_HAPPY6, data[8] & 0x20);
input_report_key(idev, BTN_TRIGGER_HAPPY7, data[8] & 0x40);
input_report_key(idev, BTN_TRIGGER_HAPPY8, data[8] & 0x80);
input_report_key(idev, BTN_TRIGGER_HAPPY9, data[9] & 0x01);
input_report_key(idev, BTN_TRIGGER_HAPPY10, data[9] & 0x02);
input_report_key(idev, BTN_TRIGGER_HAPPY11, data[9] & 0x04);
input_report_key(idev, BTN_TRIGGER_HAPPY12, data[9] & 0x08);
input_report_key(idev, BTN_TRIGGER_HAPPY13, data[9] & 0x10);
input_report_key(idev, BTN_TRIGGER_HAPPY14, data[9] & 0x20);
input_report_key(idev, BTN_TRIGGER_HAPPY15, data[9] & 0x40);
input_report_key(idev, BTN_TRIGGER_HAPPY16, data[9] & 0x80);
input_report_key(idev, BTN_TRIGGER_HAPPY17, data[10] & 0x01);
input_report_key(idev, BTN_TRIGGER_HAPPY18, data[10] & 0x02);
input_report_key(idev, BTN_TRIGGER_HAPPY19, data[10] & 0x04);
input_report_key(idev, BTN_TRIGGER_HAPPY20, data[10] & 0x08);
input_report_key(idev, BTN_TRIGGER_HAPPY21, data[10] & 0x10);
input_report_key(idev, BTN_TRIGGER_HAPPY22, data[10] & 0x20);
input_report_key(idev, BTN_TRIGGER_HAPPY23, data[10] & 0x40);
input_report_key(idev, BTN_TRIGGER_HAPPY24, data[10] & 0x80);
input_report_key(idev, BTN_TRIGGER_HAPPY25, data[11] & 0x01);
input_report_key(idev, BTN_TRIGGER_HAPPY26, data[11] & 0x02);
input_report_key(idev, BTN_TRIGGER_HAPPY27, data[11] & 0x04);
input_report_key(idev, BTN_TRIGGER_HAPPY28, data[11] & 0x08);
input_report_key(idev, BTN_TRIGGER_HAPPY29, data[11] & 0x10);
input_report_key(idev, BTN_TRIGGER_HAPPY30, data[11] & 0x20);
input_report_key(idev, BTN_TRIGGER_HAPPY31, data[11] & 0x40);
input_report_key(idev, BTN_TRIGGER_HAPPY32, data[11] & 0x80);
input_report_key(idev, BTN_TRIGGER_HAPPY33, data[12] & 0x01);
input_report_key(idev, BTN_TRIGGER_HAPPY34, data[12] & 0x02);
input_report_key(idev, BTN_TRIGGER_HAPPY35, data[12] & 0x04);
input_report_key(idev, BTN_TRIGGER_HAPPY36, data[12] & 0x08);
input_report_key(idev, BTN_TRIGGER_HAPPY37, data[12] & 0x10);
input_report_key(idev, BTN_TRIGGER_HAPPY38, data[12] & 0x20);
input_report_key(idev, BTN_TRIGGER_HAPPY39, data[12] & 0x40);
input_report_abs(idev, ABS_X,
(stick_axis >> X52PRO_X_SHIFT) & X52PRO_X_MASK);
input_report_abs(idev, ABS_Y,
(stick_axis >> X52PRO_Y_SHIFT) & X52PRO_Y_MASK);
input_report_abs(idev, ABS_RZ,
(stick_axis >> X52PRO_RZ_SHIFT) & X52PRO_RZ_MASK);
input_report_abs(idev, ABS_THROTTLE, data[4]);
input_report_abs(idev, ABS_RX, data[5]);
input_report_abs(idev, ABS_RY, data[6]);
input_report_abs(idev, ABS_Z, data[7]);
input_report_abs(idev, ABS_TILT_X, data[14] & 0xF);
input_report_abs(idev, ABS_TILT_Y, data[14] >> 4);
switch(data[13]) {
case 0x00:
input_report_abs(idev, ABS_HAT0X, 0);
input_report_abs(idev, ABS_HAT0Y, 0);
break;
case 0x10:
input_report_abs(idev, ABS_HAT0X, 0);
input_report_abs(idev, ABS_HAT0Y, -1);
break;
case 0x20:
input_report_abs(idev, ABS_HAT0X, 1);
input_report_abs(idev, ABS_HAT0Y, -1);
break;
case 0x30:
input_report_abs(idev, ABS_HAT0X, 1);
input_report_abs(idev, ABS_HAT0Y, 0);
break;
case 0x40:
input_report_abs(idev, ABS_HAT0X, 1);
input_report_abs(idev, ABS_HAT0Y, 1);
break;
case 0x50:
input_report_abs(idev, ABS_HAT0X, 0);
input_report_abs(idev, ABS_HAT0Y, 1);
break;
case 0x60:
input_report_abs(idev, ABS_HAT0X, -1);
input_report_abs(idev, ABS_HAT0Y, 1);
break;
case 0x70:
input_report_abs(idev, ABS_HAT0X, -1);
input_report_abs(idev, ABS_HAT0Y, 0);
break;
case 0x80:
input_report_abs(idev, ABS_HAT0X, -1);
input_report_abs(idev, ABS_HAT0Y, -1);
break;
}
input_sync(idev);
}
void x52_irq_handler(struct urb *urb)
{
struct x52_joy *joy = urb->context;
int retval, status;
status = urb->status;
switch (status) {
case 0:
/* success */
break;
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* This URB is terminated, clean up */
dbg("%s - URB shutting down with status: %d",
__func__, status);
return;
default:
dbg("%s - nonzero URB status received: %d",
__func__, status);
goto error;
}
switch (joy->type) {
case X52TYPE_X52PRO:
x52pro_decode_urb(joy, joy->idata);
break;
case X52TYPE_X52:
break;
default:
break;
}
error:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
err ("%s - usb_submit_urb failed with result %d",
__func__, retval);
}
void x52_setup_input(struct input_dev *idev)
{
int i;
if (!idev) {
return;
}
/*
* Enable event inputs.
*
* EV_KEY for buttons (and keyboard events in the future)
* EV_ABS for the axis
* EV_REL for future mouse support by the mouse stick
*/
idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
/* For now, map the buttons directly */
for (i = BTN_TRIGGER_HAPPY1; i <= BTN_TRIGGER_HAPPY39; i++) {
__set_bit(i, idev->keybit);
}
/* Map the axes */
input_set_abs_params(idev, ABS_X, 0, 1023, 16, 512);
input_set_abs_params(idev, ABS_Y, 0, 1023, 16, 512);
input_set_abs_params(idev, ABS_RZ, 0, 1023, 16, 512);
input_set_abs_params(idev, ABS_THROTTLE, 0, 255, 0, 0);
input_set_abs_params(idev, ABS_RX, 0, 255, 16, 128);
input_set_abs_params(idev, ABS_RY, 0, 255, 16, 128);
input_set_abs_params(idev, ABS_Z, 0, 255, 0, 0);
/* Mouse stick */
input_set_abs_params(idev, ABS_TILT_X, 0, 15, 0, 8);
input_set_abs_params(idev, ABS_TILT_Y, 0, 15, 0, 8);
/* Hat switch */
input_set_abs_params(idev, ABS_HAT0X, -1, 1, 0, 0);
input_set_abs_params(idev, ABS_HAT0Y, -1, 1, 0, 0);
}
int x52_open (struct input_dev *idev)
{
struct x52_joy *joy = input_get_drvdata(idev);
joy->irq_in->dev = joy->udev;
if (usb_submit_urb(joy->irq_in, GFP_KERNEL)) {
return -EIO;
}
return 0;
}
void x52_close (struct input_dev *idev)
{
struct x52_joy *joy = input_get_drvdata(idev);
usb_kill_urb(joy->irq_in);
}