linux/drivers/hid/hid-lg4ff.c
<<
>>
Prefs
   1/*
   2 *  Force feedback support for Logitech Speed Force Wireless
   3 *
   4 *  http://wiibrew.org/wiki/Logitech_USB_steering_wheel
   5 *
   6 *  Copyright (c) 2010 Simon Wood <simon@mungewell.org>
   7 */
   8
   9/*
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License as published by
  12 * the Free Software Foundation; either version 2 of the License, or
  13 * (at your option) any later version.
  14 *
  15 * This program is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 * GNU General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program; if not, write to the Free Software
  22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  23 */
  24
  25
  26#include <linux/input.h>
  27#include <linux/usb.h>
  28#include <linux/hid.h>
  29
  30#include "usbhid/usbhid.h"
  31#include "hid-lg.h"
  32#include "hid-ids.h"
  33
  34#define DFGT_REV_MAJ 0x13
  35#define DFGT_REV_MIN 0x22
  36#define DFP_REV_MAJ 0x11
  37#define DFP_REV_MIN 0x06
  38#define FFEX_REV_MAJ 0x21
  39#define FFEX_REV_MIN 0x00
  40#define G25_REV_MAJ 0x12
  41#define G25_REV_MIN 0x22
  42#define G27_REV_MAJ 0x12
  43#define G27_REV_MIN 0x38
  44
  45#define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
  46
  47static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
  48static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range);
  49static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf);
  50static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
  51
  52static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store);
  53
  54static bool list_inited;
  55
  56struct lg4ff_device_entry {
  57        char  *device_id;       /* Use name in respective kobject structure's address as the ID */
  58        __u16 range;
  59        __u16 min_range;
  60        __u16 max_range;
  61        __u8  leds;
  62        struct list_head list;
  63        void (*set_range)(struct hid_device *hid, u16 range);
  64};
  65
  66static struct lg4ff_device_entry device_list;
  67
  68static const signed short lg4ff_wheel_effects[] = {
  69        FF_CONSTANT,
  70        FF_AUTOCENTER,
  71        -1
  72};
  73
  74struct lg4ff_wheel {
  75        const __u32 product_id;
  76        const signed short *ff_effects;
  77        const __u16 min_range;
  78        const __u16 max_range;
  79        void (*set_range)(struct hid_device *hid, u16 range);
  80};
  81
  82static const struct lg4ff_wheel lg4ff_devices[] = {
  83        {USB_DEVICE_ID_LOGITECH_WHEEL,       lg4ff_wheel_effects, 40, 270, NULL},
  84        {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL,  lg4ff_wheel_effects, 40, 270, NULL},
  85        {USB_DEVICE_ID_LOGITECH_DFP_WHEEL,   lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_dfp},
  86        {USB_DEVICE_ID_LOGITECH_G25_WHEEL,   lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
  87        {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL,  lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
  88        {USB_DEVICE_ID_LOGITECH_G27_WHEEL,   lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
  89        {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2, lg4ff_wheel_effects, 40, 270, NULL},
  90        {USB_DEVICE_ID_LOGITECH_WII_WHEEL,   lg4ff_wheel_effects, 40, 270, NULL}
  91};
  92
  93struct lg4ff_native_cmd {
  94        const __u8 cmd_num;     /* Number of commands to send */
  95        const __u8 cmd[];
  96};
  97
  98struct lg4ff_usb_revision {
  99        const __u16 rev_maj;
 100        const __u16 rev_min;
 101        const struct lg4ff_native_cmd *command;
 102};
 103
 104static const struct lg4ff_native_cmd native_dfp = {
 105        1,
 106        {0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}
 107};
 108
 109static const struct lg4ff_native_cmd native_dfgt = {
 110        2,
 111        {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,      /* 1st command */
 112         0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00}      /* 2nd command */
 113};
 114
 115static const struct lg4ff_native_cmd native_g25 = {
 116        1,
 117        {0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}
 118};
 119
 120static const struct lg4ff_native_cmd native_g27 = {
 121        2,
 122        {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,      /* 1st command */
 123         0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00}      /* 2nd command */
 124};
 125
 126static const struct lg4ff_usb_revision lg4ff_revs[] = {
 127        {DFGT_REV_MAJ, DFGT_REV_MIN, &native_dfgt},     /* Driving Force GT */
 128        {DFP_REV_MAJ,  DFP_REV_MIN,  &native_dfp},      /* Driving Force Pro */
 129        {G25_REV_MAJ,  G25_REV_MIN,  &native_g25},      /* G25 */
 130        {G27_REV_MAJ,  G27_REV_MIN,  &native_g27},      /* G27 */
 131};
 132
 133static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
 134{
 135        struct hid_device *hid = input_get_drvdata(dev);
 136        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 137        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
 138        int x;
 139
 140#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
 141
 142        switch (effect->type) {
 143        case FF_CONSTANT:
 144                x = effect->u.ramp.start_level + 0x80;  /* 0x80 is no force */
 145                CLAMP(x);
 146                report->field[0]->value[0] = 0x11;      /* Slot 1 */
 147                report->field[0]->value[1] = 0x08;
 148                report->field[0]->value[2] = x;
 149                report->field[0]->value[3] = 0x80;
 150                report->field[0]->value[4] = 0x00;
 151                report->field[0]->value[5] = 0x00;
 152                report->field[0]->value[6] = 0x00;
 153
 154                usbhid_submit_report(hid, report, USB_DIR_OUT);
 155                break;
 156        }
 157        return 0;
 158}
 159
 160/* Sends default autocentering command compatible with
 161 * all wheels except Formula Force EX */
 162static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
 163{
 164        struct hid_device *hid = input_get_drvdata(dev);
 165        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 166        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
 167
 168        report->field[0]->value[0] = 0xfe;
 169        report->field[0]->value[1] = 0x0d;
 170        report->field[0]->value[2] = magnitude >> 13;
 171        report->field[0]->value[3] = magnitude >> 13;
 172        report->field[0]->value[4] = magnitude >> 8;
 173        report->field[0]->value[5] = 0x00;
 174        report->field[0]->value[6] = 0x00;
 175
 176        usbhid_submit_report(hid, report, USB_DIR_OUT);
 177}
 178
 179/* Sends autocentering command compatible with Formula Force EX */
 180static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
 181{
 182        struct hid_device *hid = input_get_drvdata(dev);
 183        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 184        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
 185        magnitude = magnitude * 90 / 65535;
 186        
 187
 188        report->field[0]->value[0] = 0xfe;
 189        report->field[0]->value[1] = 0x03;
 190        report->field[0]->value[2] = magnitude >> 14;
 191        report->field[0]->value[3] = magnitude >> 14;
 192        report->field[0]->value[4] = magnitude;
 193        report->field[0]->value[5] = 0x00;
 194        report->field[0]->value[6] = 0x00;
 195
 196        usbhid_submit_report(hid, report, USB_DIR_OUT);
 197}
 198
 199/* Sends command to set range compatible with G25/G27/Driving Force GT */
 200static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
 201{
 202        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 203        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
 204        dbg_hid("G25/G27/DFGT: setting range to %u\n", range);
 205
 206        report->field[0]->value[0] = 0xf8;
 207        report->field[0]->value[1] = 0x81;
 208        report->field[0]->value[2] = range & 0x00ff;
 209        report->field[0]->value[3] = (range & 0xff00) >> 8;
 210        report->field[0]->value[4] = 0x00;
 211        report->field[0]->value[5] = 0x00;
 212        report->field[0]->value[6] = 0x00;
 213
 214        usbhid_submit_report(hid, report, USB_DIR_OUT);
 215}
 216
 217/* Sends commands to set range compatible with Driving Force Pro wheel */
 218static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
 219{
 220        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 221        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
 222        int start_left, start_right, full_range;
 223        dbg_hid("Driving Force Pro: setting range to %u\n", range);
 224
 225        /* Prepare "coarse" limit command */
 226        report->field[0]->value[0] = 0xf8;
 227        report->field[0]->value[1] = 0x00;      /* Set later */
 228        report->field[0]->value[2] = 0x00;
 229        report->field[0]->value[3] = 0x00;
 230        report->field[0]->value[4] = 0x00;
 231        report->field[0]->value[5] = 0x00;
 232        report->field[0]->value[6] = 0x00;
 233
 234        if (range > 200) {
 235                report->field[0]->value[1] = 0x03;
 236                full_range = 900;
 237        } else {
 238                report->field[0]->value[1] = 0x02;
 239                full_range = 200;
 240        }
 241        usbhid_submit_report(hid, report, USB_DIR_OUT);
 242
 243        /* Prepare "fine" limit command */
 244        report->field[0]->value[0] = 0x81;
 245        report->field[0]->value[1] = 0x0b;
 246        report->field[0]->value[2] = 0x00;
 247        report->field[0]->value[3] = 0x00;
 248        report->field[0]->value[4] = 0x00;
 249        report->field[0]->value[5] = 0x00;
 250        report->field[0]->value[6] = 0x00;
 251
 252        if (range == 200 || range == 900) {     /* Do not apply any fine limit */
 253                usbhid_submit_report(hid, report, USB_DIR_OUT);
 254                return;
 255        }
 256
 257        /* Construct fine limit command */
 258        start_left = (((full_range - range + 1) * 2047) / full_range);
 259        start_right = 0xfff - start_left;
 260
 261        report->field[0]->value[2] = start_left >> 4;
 262        report->field[0]->value[3] = start_right >> 4;
 263        report->field[0]->value[4] = 0xff;
 264        report->field[0]->value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);
 265        report->field[0]->value[6] = 0xff;
 266
 267        usbhid_submit_report(hid, report, USB_DIR_OUT);
 268}
 269
 270static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_native_cmd *cmd)
 271{
 272        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 273        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
 274        __u8 i, j;
 275
 276        j = 0;
 277        while (j < 7*cmd->cmd_num) {
 278                for (i = 0; i < 7; i++)
 279                        report->field[0]->value[i] = cmd->cmd[j++];
 280
 281                usbhid_submit_report(hid, report, USB_DIR_OUT);
 282        }
 283}
 284
 285/* Read current range and display it in terminal */
 286static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf)
 287{
 288        struct lg4ff_device_entry *uninitialized_var(entry);
 289        struct list_head *h;
 290        struct hid_device *hid = to_hid_device(dev);
 291        size_t count;
 292
 293        list_for_each(h, &device_list.list) {
 294                entry = list_entry(h, struct lg4ff_device_entry, list);
 295                if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0)
 296                        break;
 297        }
 298        if (h == &device_list.list) {
 299                dbg_hid("Device not found!");
 300                return 0;
 301        }
 302
 303        count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->range);
 304        return count;
 305}
 306
 307/* Set range to user specified value, call appropriate function
 308 * according to the type of the wheel */
 309static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 310{
 311        struct lg4ff_device_entry *uninitialized_var(entry);
 312        struct list_head *h;
 313        struct hid_device *hid = to_hid_device(dev);
 314        __u16 range = simple_strtoul(buf, NULL, 10);
 315
 316        list_for_each(h, &device_list.list) {
 317                entry = list_entry(h, struct lg4ff_device_entry, list);
 318                if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0)
 319                        break;
 320        }
 321        if (h == &device_list.list) {
 322                dbg_hid("Device not found!");
 323                return count;
 324        }
 325
 326        if (range == 0)
 327                range = entry->max_range;
 328
 329        /* Check if the wheel supports range setting
 330         * and that the range is within limits for the wheel */
 331        if (entry->set_range != NULL && range >= entry->min_range && range <= entry->max_range) {
 332                entry->set_range(hid, range);
 333                entry->range = range;
 334        }
 335
 336        return count;
 337}
 338
 339int lg4ff_init(struct hid_device *hid)
 340{
 341        struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
 342        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 343        struct input_dev *dev = hidinput->input;
 344        struct hid_report *report;
 345        struct hid_field *field;
 346        struct lg4ff_device_entry *entry;
 347        struct usb_device_descriptor *udesc;
 348        int error, i, j;
 349        __u16 bcdDevice, rev_maj, rev_min;
 350
 351        /* Find the report to use */
 352        if (list_empty(report_list)) {
 353                hid_err(hid, "No output report found\n");
 354                return -1;
 355        }
 356
 357        /* Check that the report looks ok */
 358        report = list_entry(report_list->next, struct hid_report, list);
 359        if (!report) {
 360                hid_err(hid, "NULL output report\n");
 361                return -1;
 362        }
 363
 364        field = report->field[0];
 365        if (!field) {
 366                hid_err(hid, "NULL field\n");
 367                return -1;
 368        }
 369
 370        /* Check what wheel has been connected */
 371        for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) {
 372                if (hid->product == lg4ff_devices[i].product_id) {
 373                        dbg_hid("Found compatible device, product ID %04X\n", lg4ff_devices[i].product_id);
 374                        break;
 375                }
 376        }
 377
 378        if (i == ARRAY_SIZE(lg4ff_devices)) {
 379                hid_err(hid, "Device is not supported by lg4ff driver. If you think it should be, consider reporting a bug to"
 380                             "LKML, Simon Wood <simon@mungewell.org> or Michal Maly <madcatxster@gmail.com>\n");
 381                return -1;
 382        }
 383
 384        /* Attempt to switch wheel to native mode when applicable */
 385        udesc = &(hid_to_usb_dev(hid)->descriptor);
 386        if (!udesc) {
 387                hid_err(hid, "NULL USB device descriptor\n");
 388                return -1;
 389        }
 390        bcdDevice = le16_to_cpu(udesc->bcdDevice);
 391        rev_maj = bcdDevice >> 8;
 392        rev_min = bcdDevice & 0xff;
 393
 394        if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_WHEEL) {
 395                dbg_hid("Generic wheel detected, can it do native?\n");
 396                dbg_hid("USB revision: %2x.%02x\n", rev_maj, rev_min);
 397
 398                for (j = 0; j < ARRAY_SIZE(lg4ff_revs); j++) {
 399                        if (lg4ff_revs[j].rev_maj == rev_maj && lg4ff_revs[j].rev_min == rev_min) {
 400                                hid_lg4ff_switch_native(hid, lg4ff_revs[j].command);
 401                                hid_info(hid, "Switched to native mode\n");
 402                        }
 403                }
 404        }
 405
 406        /* Set supported force feedback capabilities */
 407        for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++)
 408                set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit);
 409
 410        error = input_ff_create_memless(dev, NULL, hid_lg4ff_play);
 411
 412        if (error)
 413                return error;
 414
 415        /* Check if autocentering is available and
 416         * set the centering force to zero by default */
 417        if (test_bit(FF_AUTOCENTER, dev->ffbit)) {
 418                if(rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN)  /* Formula Force EX expects different autocentering command */
 419                        dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex;
 420                else
 421                        dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default;
 422
 423                dev->ff->set_autocenter(dev, 0);
 424        }
 425
 426                /* Initialize device_list if this is the first device to handle by lg4ff */
 427        if (!list_inited) {
 428                INIT_LIST_HEAD(&device_list.list);
 429                list_inited = 1;
 430        }
 431
 432        /* Add the device to device_list */
 433        entry = (struct lg4ff_device_entry *)kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL);
 434        if (!entry) {
 435                hid_err(hid, "Cannot add device, insufficient memory.\n");
 436                return -ENOMEM;
 437        }
 438        entry->device_id = kstrdup((&hid->dev)->kobj.name, GFP_KERNEL);
 439        if (!entry->device_id) {
 440                hid_err(hid, "Cannot set device_id, insufficient memory.\n");
 441                kfree(entry);
 442                return -ENOMEM;
 443        }
 444        entry->min_range = lg4ff_devices[i].min_range;
 445        entry->max_range = lg4ff_devices[i].max_range;
 446        entry->set_range = lg4ff_devices[i].set_range;
 447        list_add(&entry->list, &device_list.list);
 448
 449        /* Create sysfs interface */
 450        error = device_create_file(&hid->dev, &dev_attr_range);
 451        if (error)
 452                return error;
 453        dbg_hid("sysfs interface created\n");
 454
 455        /* Set the maximum range to start with */
 456        entry->range = entry->max_range;
 457        if (entry->set_range != NULL)
 458                entry->set_range(hid, entry->range);
 459
 460        hid_info(hid, "Force feedback for Logitech Speed Force Wireless by Simon Wood <simon@mungewell.org>\n");
 461        return 0;
 462}
 463
 464int lg4ff_deinit(struct hid_device *hid)
 465{
 466        bool found = 0;
 467        struct lg4ff_device_entry *entry;
 468        struct list_head *h, *g;
 469        list_for_each_safe(h, g, &device_list.list) {
 470                entry = list_entry(h, struct lg4ff_device_entry, list);
 471                if (strcmp(entry->device_id, (&hid->dev)->kobj.name) == 0) {
 472                        list_del(h);
 473                        kfree(entry->device_id);
 474                        kfree(entry);
 475                        found = 1;
 476                        break;
 477                }
 478        }
 479
 480        if (!found) {
 481                dbg_hid("Device entry not found!\n");
 482                return -1;
 483        }
 484
 485        device_remove_file(&hid->dev, &dev_attr_range);
 486        dbg_hid("Device successfully unregistered\n");
 487        return 0;
 488}
 489