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/hid.h>
  26
  27#include "hid-lg.h"
  28
  29/*
  30 * G940 Theory of Operation (from experimentation)
  31 *
  32 * There are 63 fields (only 3 of them currently used)
  33 * 0 - seems to be command field
  34 * 1 - 30 deal with the x axis
  35 * 31 -60 deal with the y axis
  36 *
  37 * Field 1 is x axis constant force
  38 * Field 31 is y axis constant force
  39 *
  40 * other interesting fields 1,2,3,4 on x axis
  41 * (same for 31,32,33,34 on y axis)
  42 *
  43 * 0 0 127 127 makes the joystick autocenter hard
  44 *
  45 * 127 0 127 127 makes the joystick loose on the right,
  46 * but stops all movemnt left
  47 *
  48 * -127 0 -127 -127 makes the joystick loose on the left,
  49 * but stops all movement right
  50 *
  51 * 0 0 -127 -127 makes the joystick rattle very hard
  52 *
  53 * I'm sure these are effects that I don't know enough about them
  54 */
  55
  56struct lg3ff_device {
  57        struct hid_report *report;
  58};
  59
  60static int hid_lg3ff_play(struct input_dev *dev, void *data,
  61                         struct ff_effect *effect)
  62{
  63        struct hid_device *hid = input_get_drvdata(dev);
  64        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
  65        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
  66        int x, y;
  67
  68/*
  69 * Available values in the field should always be 63, but we only use up to
  70 * 35. Instead, clear the entire area, however big it is.
  71 */
  72        memset(report->field[0]->value, 0,
  73               sizeof(__s32) * report->field[0]->report_count);
  74
  75        switch (effect->type) {
  76        case FF_CONSTANT:
  77/*
  78 * Already clamped in ff_memless
  79 * 0 is center (different then other logitech)
  80 */
  81                x = effect->u.ramp.start_level;
  82                y = effect->u.ramp.end_level;
  83
  84                /* send command byte */
  85                report->field[0]->value[0] = 0x51;
  86
  87/*
  88 * Sign backwards from other Force3d pro
  89 * which get recast here in two's complement 8 bits
  90 */
  91                report->field[0]->value[1] = (unsigned char)(-x);
  92                report->field[0]->value[31] = (unsigned char)(-y);
  93
  94                hid_hw_request(hid, report, HID_REQ_SET_REPORT);
  95                break;
  96        }
  97        return 0;
  98}
  99static void hid_lg3ff_set_autocenter(struct input_dev *dev, u16 magnitude)
 100{
 101        struct hid_device *hid = input_get_drvdata(dev);
 102        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 103        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
 104
 105/*
 106 * Auto Centering probed from device
 107 * NOTE: deadman's switch on G940 must be covered
 108 * for effects to work
 109 */
 110        report->field[0]->value[0] = 0x51;
 111        report->field[0]->value[1] = 0x00;
 112        report->field[0]->value[2] = 0x00;
 113        report->field[0]->value[3] = 0x7F;
 114        report->field[0]->value[4] = 0x7F;
 115        report->field[0]->value[31] = 0x00;
 116        report->field[0]->value[32] = 0x00;
 117        report->field[0]->value[33] = 0x7F;
 118        report->field[0]->value[34] = 0x7F;
 119
 120        hid_hw_request(hid, report, HID_REQ_SET_REPORT);
 121}
 122
 123
 124static const signed short ff3_joystick_ac[] = {
 125        FF_CONSTANT,
 126        FF_AUTOCENTER,
 127        -1
 128};
 129
 130int lg3ff_init(struct hid_device *hid)
 131{
 132        struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
 133        struct input_dev *dev = hidinput->input;
 134        const signed short *ff_bits = ff3_joystick_ac;
 135        int error;
 136        int i;
 137
 138        /* Check that the report looks ok */
 139        if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 35))
 140                return -ENODEV;
 141
 142        /* Assume single fixed device G940 */
 143        for (i = 0; ff_bits[i] >= 0; i++)
 144                set_bit(ff_bits[i], dev->ffbit);
 145
 146        error = input_ff_create_memless(dev, NULL, hid_lg3ff_play);
 147        if (error)
 148                return error;
 149
 150        if (test_bit(FF_AUTOCENTER, dev->ffbit))
 151                dev->ff->set_autocenter = hid_lg3ff_set_autocenter;
 152
 153        hid_info(hid, "Force feedback for Logitech Flight System G940 by Gary Stein <LordCnidarian@gmail.com>\n");
 154        return 0;
 155}
 156
 157