linux/drivers/hid/hid-lg3ff.c
<<
>>
Prefs
   1/*
   2 *  Force feedback support for Logitech Flight System G940
   3 *
   4 *  Copyright (c) 2009 Gary Stein <LordCnidarian@gmail.com>
   5 */
   6
   7/*
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 *
  18 * You should have received a copy of the GNU General Public License
  19 * along with this program; if not, write to the Free Software
  20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  21 */
  22
  23
  24#include <linux/input.h>
  25#include <linux/usb.h>
  26#include <linux/hid.h>
  27
  28#include "usbhid/usbhid.h"
  29#include "hid-lg.h"
  30
  31/*
  32 * G940 Theory of Operation (from experimentation)
  33 *
  34 * There are 63 fields (only 3 of them currently used)
  35 * 0 - seems to be command field
  36 * 1 - 30 deal with the x axis
  37 * 31 -60 deal with the y axis
  38 *
  39 * Field 1 is x axis constant force
  40 * Field 31 is y axis constant force
  41 *
  42 * other interesting fields 1,2,3,4 on x axis
  43 * (same for 31,32,33,34 on y axis)
  44 *
  45 * 0 0 127 127 makes the joystick autocenter hard
  46 *
  47 * 127 0 127 127 makes the joystick loose on the right,
  48 * but stops all movemnt left
  49 *
  50 * -127 0 -127 -127 makes the joystick loose on the left,
  51 * but stops all movement right
  52 *
  53 * 0 0 -127 -127 makes the joystick rattle very hard
  54 *
  55 * I'm sure these are effects that I don't know enough about them
  56 */
  57
  58struct lg3ff_device {
  59        struct hid_report *report;
  60};
  61
  62static int hid_lg3ff_play(struct input_dev *dev, void *data,
  63                         struct ff_effect *effect)
  64{
  65        struct hid_device *hid = input_get_drvdata(dev);
  66        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
  67        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
  68        int x, y;
  69
  70/*
  71 * Maxusage should always be 63 (maximum fields)
  72 * likely a better way to ensure this data is clean
  73 */
  74        memset(report->field[0]->value, 0, sizeof(__s32)*report->field[0]->maxusage);
  75
  76        switch (effect->type) {
  77        case FF_CONSTANT:
  78/*
  79 * Already clamped in ff_memless
  80 * 0 is center (different then other logitech)
  81 */
  82                x = effect->u.ramp.start_level;
  83                y = effect->u.ramp.end_level;
  84
  85                /* send command byte */
  86                report->field[0]->value[0] = 0x51;
  87
  88/*
  89 * Sign backwards from other Force3d pro
  90 * which get recast here in two's complement 8 bits
  91 */
  92                report->field[0]->value[1] = (unsigned char)(-x);
  93                report->field[0]->value[31] = (unsigned char)(-y);
  94
  95                usbhid_submit_report(hid, report, USB_DIR_OUT);
  96                break;
  97        }
  98        return 0;
  99}
 100static void hid_lg3ff_set_autocenter(struct input_dev *dev, u16 magnitude)
 101{
 102        struct hid_device *hid = input_get_drvdata(dev);
 103        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 104        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
 105
 106/*
 107 * Auto Centering probed from device
 108 * NOTE: deadman's switch on G940 must be covered
 109 * for effects to work
 110 */
 111        report->field[0]->value[0] = 0x51;
 112        report->field[0]->value[1] = 0x00;
 113        report->field[0]->value[2] = 0x00;
 114        report->field[0]->value[3] = 0x7F;
 115        report->field[0]->value[4] = 0x7F;
 116        report->field[0]->value[31] = 0x00;
 117        report->field[0]->value[32] = 0x00;
 118        report->field[0]->value[33] = 0x7F;
 119        report->field[0]->value[34] = 0x7F;
 120
 121        usbhid_submit_report(hid, report, USB_DIR_OUT);
 122}
 123
 124
 125static const signed short ff3_joystick_ac[] = {
 126        FF_CONSTANT,
 127        FF_AUTOCENTER,
 128        -1
 129};
 130
 131int lg3ff_init(struct hid_device *hid)
 132{
 133        struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
 134        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 135        struct input_dev *dev = hidinput->input;
 136        struct hid_report *report;
 137        struct hid_field *field;
 138        const signed short *ff_bits = ff3_joystick_ac;
 139        int error;
 140        int i;
 141
 142        /* Find the report to use */
 143        if (list_empty(report_list)) {
 144                hid_err(hid, "No output report found\n");
 145                return -1;
 146        }
 147
 148        /* Check that the report looks ok */
 149        report = list_entry(report_list->next, struct hid_report, list);
 150        if (!report) {
 151                hid_err(hid, "NULL output report\n");
 152                return -1;
 153        }
 154
 155        field = report->field[0];
 156        if (!field) {
 157                hid_err(hid, "NULL field\n");
 158                return -1;
 159        }
 160
 161        /* Assume single fixed device G940 */
 162        for (i = 0; ff_bits[i] >= 0; i++)
 163                set_bit(ff_bits[i], dev->ffbit);
 164
 165        error = input_ff_create_memless(dev, NULL, hid_lg3ff_play);
 166        if (error)
 167                return error;
 168
 169        if (test_bit(FF_AUTOCENTER, dev->ffbit))
 170                dev->ff->set_autocenter = hid_lg3ff_set_autocenter;
 171
 172        hid_info(hid, "Force feedback for Logitech Flight System G940 by Gary Stein <LordCnidarian@gmail.com>\n");
 173        return 0;
 174}
 175
 176