mirror of https://github.com/nirenjan/libx52.git
240 lines
7.3 KiB
C
240 lines
7.3 KiB
C
/*
|
|
* 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 = data[3];
|
|
stick_axis = (stick_axis << 8) | data[2];
|
|
stick_axis = (stick_axis << 8) | data[1];
|
|
stick_axis = (stick_axis << 8) | 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, 0, 0);
|
|
input_set_abs_params(idev, ABS_Y, 0, 1023, 0, 0);
|
|
input_set_abs_params(idev, ABS_RZ, 0, 1023, 0, 0);
|
|
|
|
input_set_abs_params(idev, ABS_THROTTLE, 0, 255, 0, 0);
|
|
input_set_abs_params(idev, ABS_RX, 0, 255, 0, 0);
|
|
input_set_abs_params(idev, ABS_RY, 0, 255, 0, 0);
|
|
input_set_abs_params(idev, ABS_Z, 0, 255, 0, 0);
|
|
|
|
/* Mouse stick */
|
|
input_set_abs_params(idev, ABS_TILT_X, 0, 15, 0, 0);
|
|
input_set_abs_params(idev, ABS_TILT_Y, 0, 15, 0, 0);
|
|
|
|
/* 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);
|
|
}
|
|
|