linux/drivers/hid/hid-lg4ff.c
<<
>>
Prefs
   1/*
   2 *  Force feedback support for Logitech Gaming Wheels
   3 *
   4 *  Including G27, G25, DFP, DFGT, FFEX, Momo, Momo2 &
   5 *  Speed Force Wireless (WiiWheel)
   6 *
   7 *  Copyright (c) 2010 Simon Wood <simon@mungewell.org>
   8 */
   9
  10/*
  11 * This program is free software; you can redistribute it and/or modify
  12 * it under the terms of the GNU General Public License as published by
  13 * the Free Software Foundation; either version 2 of the License, or
  14 * (at your option) any later version.
  15 *
  16 * This program is distributed in the hope that it will be useful,
  17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19 * GNU General Public License for more details.
  20 *
  21 * You should have received a copy of the GNU General Public License
  22 * along with this program; if not, write to the Free Software
  23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  24 */
  25
  26
  27#include <linux/input.h>
  28#include <linux/usb.h>
  29#include <linux/hid.h>
  30
  31#include "usbhid/usbhid.h"
  32#include "hid-lg.h"
  33#include "hid-ids.h"
  34
  35#define DFGT_REV_MAJ 0x13
  36#define DFGT_REV_MIN 0x22
  37#define DFP_REV_MAJ 0x11
  38#define DFP_REV_MIN 0x06
  39#define FFEX_REV_MAJ 0x21
  40#define FFEX_REV_MIN 0x00
  41#define G25_REV_MAJ 0x12
  42#define G25_REV_MIN 0x22
  43#define G27_REV_MAJ 0x12
  44#define G27_REV_MIN 0x38
  45
  46#define DFP_X_MIN 0
  47#define DFP_X_MAX 16383
  48#define DFP_PEDAL_MIN 0
  49#define DFP_PEDAL_MAX 255
  50
  51#define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
  52
  53static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
  54static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range);
  55static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf);
  56static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
  57
  58static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store);
  59
  60struct lg4ff_device_entry {
  61        __u32 product_id;
  62        __u16 range;
  63        __u16 min_range;
  64        __u16 max_range;
  65#ifdef CONFIG_LEDS_CLASS
  66        __u8  led_state;
  67        struct led_classdev *led[5];
  68#endif
  69        struct list_head list;
  70        void (*set_range)(struct hid_device *hid, u16 range);
  71};
  72
  73static const signed short lg4ff_wheel_effects[] = {
  74        FF_CONSTANT,
  75        FF_AUTOCENTER,
  76        -1
  77};
  78
  79struct lg4ff_wheel {
  80        const __u32 product_id;
  81        const signed short *ff_effects;
  82        const __u16 min_range;
  83        const __u16 max_range;
  84        void (*set_range)(struct hid_device *hid, u16 range);
  85};
  86
  87static const struct lg4ff_wheel lg4ff_devices[] = {
  88        {USB_DEVICE_ID_LOGITECH_WHEEL,       lg4ff_wheel_effects, 40, 270, NULL},
  89        {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL,  lg4ff_wheel_effects, 40, 270, NULL},
  90        {USB_DEVICE_ID_LOGITECH_DFP_WHEEL,   lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_dfp},
  91        {USB_DEVICE_ID_LOGITECH_G25_WHEEL,   lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
  92        {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL,  lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
  93        {USB_DEVICE_ID_LOGITECH_G27_WHEEL,   lg4ff_wheel_effects, 40, 900, hid_lg4ff_set_range_g25},
  94        {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2, lg4ff_wheel_effects, 40, 270, NULL},
  95        {USB_DEVICE_ID_LOGITECH_WII_WHEEL,   lg4ff_wheel_effects, 40, 270, NULL}
  96};
  97
  98struct lg4ff_native_cmd {
  99        const __u8 cmd_num;     /* Number of commands to send */
 100        const __u8 cmd[];
 101};
 102
 103struct lg4ff_usb_revision {
 104        const __u16 rev_maj;
 105        const __u16 rev_min;
 106        const struct lg4ff_native_cmd *command;
 107};
 108
 109static const struct lg4ff_native_cmd native_dfp = {
 110        1,
 111        {0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}
 112};
 113
 114static const struct lg4ff_native_cmd native_dfgt = {
 115        2,
 116        {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,      /* 1st command */
 117         0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00}      /* 2nd command */
 118};
 119
 120static const struct lg4ff_native_cmd native_g25 = {
 121        1,
 122        {0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}
 123};
 124
 125static const struct lg4ff_native_cmd native_g27 = {
 126        2,
 127        {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,      /* 1st command */
 128         0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00}      /* 2nd command */
 129};
 130
 131static const struct lg4ff_usb_revision lg4ff_revs[] = {
 132        {DFGT_REV_MAJ, DFGT_REV_MIN, &native_dfgt},     /* Driving Force GT */
 133        {DFP_REV_MAJ,  DFP_REV_MIN,  &native_dfp},      /* Driving Force Pro */
 134        {G25_REV_MAJ,  G25_REV_MIN,  &native_g25},      /* G25 */
 135        {G27_REV_MAJ,  G27_REV_MIN,  &native_g27},      /* G27 */
 136};
 137
 138/* Recalculates X axis value accordingly to currently selected range */
 139static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range)
 140{
 141        __u16 max_range;
 142        __s32 new_value;
 143
 144        if (range == 900)
 145                return value;
 146        else if (range == 200)
 147                return value;
 148        else if (range < 200)
 149                max_range = 200;
 150        else
 151                max_range = 900;
 152
 153        new_value = 8192 + mult_frac(value - 8192, max_range, range);
 154        if (new_value < 0)
 155                return 0;
 156        else if (new_value > 16383)
 157                return 16383;
 158        else
 159                return new_value;
 160}
 161
 162int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
 163                             struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data)
 164{
 165        struct lg4ff_device_entry *entry = drv_data->device_props;
 166        __s32 new_value = 0;
 167
 168        if (!entry) {
 169                hid_err(hid, "Device properties not found");
 170                return 0;
 171        }
 172
 173        switch (entry->product_id) {
 174        case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
 175                switch (usage->code) {
 176                case ABS_X:
 177                        new_value = lg4ff_adjust_dfp_x_axis(value, entry->range);
 178                        input_event(field->hidinput->input, usage->type, usage->code, new_value);
 179                        return 1;
 180                default:
 181                        return 0;
 182                }
 183        default:
 184                return 0;
 185        }
 186}
 187
 188static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
 189{
 190        struct hid_device *hid = input_get_drvdata(dev);
 191        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 192        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
 193        __s32 *value = report->field[0]->value;
 194        int x;
 195
 196#define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0)
 197
 198        switch (effect->type) {
 199        case FF_CONSTANT:
 200                x = effect->u.ramp.start_level + 0x80;  /* 0x80 is no force */
 201                CLAMP(x);
 202                value[0] = 0x11;        /* Slot 1 */
 203                value[1] = 0x08;
 204                value[2] = x;
 205                value[3] = 0x80;
 206                value[4] = 0x00;
 207                value[5] = 0x00;
 208                value[6] = 0x00;
 209
 210                usbhid_submit_report(hid, report, USB_DIR_OUT);
 211                break;
 212        }
 213        return 0;
 214}
 215
 216/* Sends default autocentering command compatible with
 217 * all wheels except Formula Force EX */
 218static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
 219{
 220        struct hid_device *hid = input_get_drvdata(dev);
 221        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 222        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
 223        __s32 *value = report->field[0]->value;
 224
 225        value[0] = 0xfe;
 226        value[1] = 0x0d;
 227        value[2] = magnitude >> 13;
 228        value[3] = magnitude >> 13;
 229        value[4] = magnitude >> 8;
 230        value[5] = 0x00;
 231        value[6] = 0x00;
 232
 233        usbhid_submit_report(hid, report, USB_DIR_OUT);
 234}
 235
 236/* Sends autocentering command compatible with Formula Force EX */
 237static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
 238{
 239        struct hid_device *hid = input_get_drvdata(dev);
 240        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 241        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
 242        __s32 *value = report->field[0]->value;
 243        magnitude = magnitude * 90 / 65535;
 244
 245        value[0] = 0xfe;
 246        value[1] = 0x03;
 247        value[2] = magnitude >> 14;
 248        value[3] = magnitude >> 14;
 249        value[4] = magnitude;
 250        value[5] = 0x00;
 251        value[6] = 0x00;
 252
 253        usbhid_submit_report(hid, report, USB_DIR_OUT);
 254}
 255
 256/* Sends command to set range compatible with G25/G27/Driving Force GT */
 257static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
 258{
 259        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 260        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
 261        __s32 *value = report->field[0]->value;
 262
 263        dbg_hid("G25/G27/DFGT: setting range to %u\n", range);
 264
 265        value[0] = 0xf8;
 266        value[1] = 0x81;
 267        value[2] = range & 0x00ff;
 268        value[3] = (range & 0xff00) >> 8;
 269        value[4] = 0x00;
 270        value[5] = 0x00;
 271        value[6] = 0x00;
 272
 273        usbhid_submit_report(hid, report, USB_DIR_OUT);
 274}
 275
 276/* Sends commands to set range compatible with Driving Force Pro wheel */
 277static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
 278{
 279        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 280        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
 281        int start_left, start_right, full_range;
 282        __s32 *value = report->field[0]->value;
 283
 284        dbg_hid("Driving Force Pro: setting range to %u\n", range);
 285
 286        /* Prepare "coarse" limit command */
 287        value[0] = 0xf8;
 288        value[1] = 0x00;        /* Set later */
 289        value[2] = 0x00;
 290        value[3] = 0x00;
 291        value[4] = 0x00;
 292        value[5] = 0x00;
 293        value[6] = 0x00;
 294
 295        if (range > 200) {
 296                report->field[0]->value[1] = 0x03;
 297                full_range = 900;
 298        } else {
 299                report->field[0]->value[1] = 0x02;
 300                full_range = 200;
 301        }
 302        usbhid_submit_report(hid, report, USB_DIR_OUT);
 303
 304        /* Prepare "fine" limit command */
 305        value[0] = 0x81;
 306        value[1] = 0x0b;
 307        value[2] = 0x00;
 308        value[3] = 0x00;
 309        value[4] = 0x00;
 310        value[5] = 0x00;
 311        value[6] = 0x00;
 312
 313        if (range == 200 || range == 900) {     /* Do not apply any fine limit */
 314                usbhid_submit_report(hid, report, USB_DIR_OUT);
 315                return;
 316        }
 317
 318        /* Construct fine limit command */
 319        start_left = (((full_range - range + 1) * 2047) / full_range);
 320        start_right = 0xfff - start_left;
 321
 322        value[2] = start_left >> 4;
 323        value[3] = start_right >> 4;
 324        value[4] = 0xff;
 325        value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);
 326        value[6] = 0xff;
 327
 328        usbhid_submit_report(hid, report, USB_DIR_OUT);
 329}
 330
 331static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_native_cmd *cmd)
 332{
 333        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 334        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
 335        __u8 i, j;
 336
 337        j = 0;
 338        while (j < 7*cmd->cmd_num) {
 339                for (i = 0; i < 7; i++)
 340                        report->field[0]->value[i] = cmd->cmd[j++];
 341
 342                usbhid_submit_report(hid, report, USB_DIR_OUT);
 343        }
 344}
 345
 346/* Read current range and display it in terminal */
 347static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf)
 348{
 349        struct hid_device *hid = to_hid_device(dev);
 350        struct lg4ff_device_entry *entry;
 351        struct lg_drv_data *drv_data;
 352        size_t count;
 353
 354        drv_data = hid_get_drvdata(hid);
 355        if (!drv_data) {
 356                hid_err(hid, "Private driver data not found!\n");
 357                return 0;
 358        }
 359
 360        entry = drv_data->device_props;
 361        if (!entry) {
 362                hid_err(hid, "Device properties not found!\n");
 363                return 0;
 364        }
 365
 366        count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->range);
 367        return count;
 368}
 369
 370/* Set range to user specified value, call appropriate function
 371 * according to the type of the wheel */
 372static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 373{
 374        struct hid_device *hid = to_hid_device(dev);
 375        struct lg4ff_device_entry *entry;
 376        struct lg_drv_data *drv_data;
 377        __u16 range = simple_strtoul(buf, NULL, 10);
 378
 379        drv_data = hid_get_drvdata(hid);
 380        if (!drv_data) {
 381                hid_err(hid, "Private driver data not found!\n");
 382                return 0;
 383        }
 384
 385        entry = drv_data->device_props;
 386        if (!entry) {
 387                hid_err(hid, "Device properties not found!\n");
 388                return 0;
 389        }
 390
 391        if (range == 0)
 392                range = entry->max_range;
 393
 394        /* Check if the wheel supports range setting
 395         * and that the range is within limits for the wheel */
 396        if (entry->set_range != NULL && range >= entry->min_range && range <= entry->max_range) {
 397                entry->set_range(hid, range);
 398                entry->range = range;
 399        }
 400
 401        return count;
 402}
 403
 404#ifdef CONFIG_LEDS_CLASS
 405static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
 406{
 407        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 408        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
 409        __s32 *value = report->field[0]->value;
 410
 411        value[0] = 0xf8;
 412        value[1] = 0x12;
 413        value[2] = leds;
 414        value[3] = 0x00;
 415        value[4] = 0x00;
 416        value[5] = 0x00;
 417        value[6] = 0x00;
 418        usbhid_submit_report(hid, report, USB_DIR_OUT);
 419}
 420
 421static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
 422                        enum led_brightness value)
 423{
 424        struct device *dev = led_cdev->dev->parent;
 425        struct hid_device *hid = container_of(dev, struct hid_device, dev);
 426        struct lg_drv_data *drv_data = hid_get_drvdata(hid);
 427        struct lg4ff_device_entry *entry;
 428        int i, state = 0;
 429
 430        if (!drv_data) {
 431                hid_err(hid, "Device data not found.");
 432                return;
 433        }
 434
 435        entry = (struct lg4ff_device_entry *)drv_data->device_props;
 436
 437        if (!entry) {
 438                hid_err(hid, "Device properties not found.");
 439                return;
 440        }
 441
 442        for (i = 0; i < 5; i++) {
 443                if (led_cdev != entry->led[i])
 444                        continue;
 445                state = (entry->led_state >> i) & 1;
 446                if (value == LED_OFF && state) {
 447                        entry->led_state &= ~(1 << i);
 448                        lg4ff_set_leds(hid, entry->led_state);
 449                } else if (value != LED_OFF && !state) {
 450                        entry->led_state |= 1 << i;
 451                        lg4ff_set_leds(hid, entry->led_state);
 452                }
 453                break;
 454        }
 455}
 456
 457static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cdev)
 458{
 459        struct device *dev = led_cdev->dev->parent;
 460        struct hid_device *hid = container_of(dev, struct hid_device, dev);
 461        struct lg_drv_data *drv_data = hid_get_drvdata(hid);
 462        struct lg4ff_device_entry *entry;
 463        int i, value = 0;
 464
 465        if (!drv_data) {
 466                hid_err(hid, "Device data not found.");
 467                return LED_OFF;
 468        }
 469
 470        entry = (struct lg4ff_device_entry *)drv_data->device_props;
 471
 472        if (!entry) {
 473                hid_err(hid, "Device properties not found.");
 474                return LED_OFF;
 475        }
 476
 477        for (i = 0; i < 5; i++)
 478                if (led_cdev == entry->led[i]) {
 479                        value = (entry->led_state >> i) & 1;
 480                        break;
 481                }
 482
 483        return value ? LED_FULL : LED_OFF;
 484}
 485#endif
 486
 487int lg4ff_init(struct hid_device *hid)
 488{
 489        struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
 490        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
 491        struct input_dev *dev = hidinput->input;
 492        struct hid_report *report;
 493        struct hid_field *field;
 494        struct lg4ff_device_entry *entry;
 495        struct lg_drv_data *drv_data;
 496        struct usb_device_descriptor *udesc;
 497        int error, i, j;
 498        __u16 bcdDevice, rev_maj, rev_min;
 499
 500        /* Find the report to use */
 501        if (list_empty(report_list)) {
 502                hid_err(hid, "No output report found\n");
 503                return -1;
 504        }
 505
 506        /* Check that the report looks ok */
 507        report = list_entry(report_list->next, struct hid_report, list);
 508        if (!report) {
 509                hid_err(hid, "NULL output report\n");
 510                return -1;
 511        }
 512
 513        field = report->field[0];
 514        if (!field) {
 515                hid_err(hid, "NULL field\n");
 516                return -1;
 517        }
 518
 519        /* Check what wheel has been connected */
 520        for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) {
 521                if (hid->product == lg4ff_devices[i].product_id) {
 522                        dbg_hid("Found compatible device, product ID %04X\n", lg4ff_devices[i].product_id);
 523                        break;
 524                }
 525        }
 526
 527        if (i == ARRAY_SIZE(lg4ff_devices)) {
 528                hid_err(hid, "Device is not supported by lg4ff driver. If you think it should be, consider reporting a bug to"
 529                             "LKML, Simon Wood <simon@mungewell.org> or Michal Maly <madcatxster@gmail.com>\n");
 530                return -1;
 531        }
 532
 533        /* Attempt to switch wheel to native mode when applicable */
 534        udesc = &(hid_to_usb_dev(hid)->descriptor);
 535        if (!udesc) {
 536                hid_err(hid, "NULL USB device descriptor\n");
 537                return -1;
 538        }
 539        bcdDevice = le16_to_cpu(udesc->bcdDevice);
 540        rev_maj = bcdDevice >> 8;
 541        rev_min = bcdDevice & 0xff;
 542
 543        if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_WHEEL) {
 544                dbg_hid("Generic wheel detected, can it do native?\n");
 545                dbg_hid("USB revision: %2x.%02x\n", rev_maj, rev_min);
 546
 547                for (j = 0; j < ARRAY_SIZE(lg4ff_revs); j++) {
 548                        if (lg4ff_revs[j].rev_maj == rev_maj && lg4ff_revs[j].rev_min == rev_min) {
 549                                hid_lg4ff_switch_native(hid, lg4ff_revs[j].command);
 550                                hid_info(hid, "Switched to native mode\n");
 551                        }
 552                }
 553        }
 554
 555        /* Set supported force feedback capabilities */
 556        for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++)
 557                set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit);
 558
 559        error = input_ff_create_memless(dev, NULL, hid_lg4ff_play);
 560
 561        if (error)
 562                return error;
 563
 564        /* Check if autocentering is available and
 565         * set the centering force to zero by default */
 566        if (test_bit(FF_AUTOCENTER, dev->ffbit)) {
 567                if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */
 568                        dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex;
 569                else
 570                        dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default;
 571
 572                dev->ff->set_autocenter(dev, 0);
 573        }
 574
 575        /* Get private driver data */
 576        drv_data = hid_get_drvdata(hid);
 577        if (!drv_data) {
 578                hid_err(hid, "Cannot add device, private driver data not allocated\n");
 579                return -1;
 580        }
 581
 582        /* Initialize device properties */
 583        entry = kzalloc(sizeof(struct lg4ff_device_entry), GFP_KERNEL);
 584        if (!entry) {
 585                hid_err(hid, "Cannot add device, insufficient memory to allocate device properties.\n");
 586                return -ENOMEM;
 587        }
 588        drv_data->device_props = entry;
 589
 590        entry->product_id = lg4ff_devices[i].product_id;
 591        entry->min_range = lg4ff_devices[i].min_range;
 592        entry->max_range = lg4ff_devices[i].max_range;
 593        entry->set_range = lg4ff_devices[i].set_range;
 594
 595        /* Create sysfs interface */
 596        error = device_create_file(&hid->dev, &dev_attr_range);
 597        if (error)
 598                return error;
 599        dbg_hid("sysfs interface created\n");
 600
 601        /* Set default axes parameters */
 602        switch (lg4ff_devices[i].product_id) {
 603        case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
 604                dbg_hid("Setting axes parameters for Driving Force Pro\n");
 605                input_set_abs_params(dev, ABS_X, DFP_X_MIN, DFP_X_MAX, 0, 0);
 606                input_set_abs_params(dev, ABS_Y, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0);
 607                input_set_abs_params(dev, ABS_RZ, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0);
 608                break;
 609        default:
 610                break;
 611        }
 612
 613        /* Set the maximum range to start with */
 614        entry->range = entry->max_range;
 615        if (entry->set_range != NULL)
 616                entry->set_range(hid, entry->range);
 617
 618#ifdef CONFIG_LEDS_CLASS
 619        /* register led subsystem - G27 only */
 620        entry->led_state = 0;
 621        for (j = 0; j < 5; j++)
 622                entry->led[j] = NULL;
 623
 624        if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL) {
 625                struct led_classdev *led;
 626                size_t name_sz;
 627                char *name;
 628
 629                lg4ff_set_leds(hid, 0);
 630
 631                name_sz = strlen(dev_name(&hid->dev)) + 8;
 632
 633                for (j = 0; j < 5; j++) {
 634                        led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
 635                        if (!led) {
 636                                hid_err(hid, "can't allocate memory for LED %d\n", j);
 637                                goto err;
 638                        }
 639
 640                        name = (void *)(&led[1]);
 641                        snprintf(name, name_sz, "%s::RPM%d", dev_name(&hid->dev), j+1);
 642                        led->name = name;
 643                        led->brightness = 0;
 644                        led->max_brightness = 1;
 645                        led->brightness_get = lg4ff_led_get_brightness;
 646                        led->brightness_set = lg4ff_led_set_brightness;
 647
 648                        entry->led[j] = led;
 649                        error = led_classdev_register(&hid->dev, led);
 650
 651                        if (error) {
 652                                hid_err(hid, "failed to register LED %d. Aborting.\n", j);
 653err:
 654                                /* Deregister LEDs (if any) */
 655                                for (j = 0; j < 5; j++) {
 656                                        led = entry->led[j];
 657                                        entry->led[j] = NULL;
 658                                        if (!led)
 659                                                continue;
 660                                        led_classdev_unregister(led);
 661                                        kfree(led);
 662                                }
 663                                goto out;       /* Let the driver continue without LEDs */
 664                        }
 665                }
 666        }
 667out:
 668#endif
 669        hid_info(hid, "Force feedback support for Logitech Gaming Wheels\n");
 670        return 0;
 671}
 672
 673
 674
 675int lg4ff_deinit(struct hid_device *hid)
 676{
 677        struct lg4ff_device_entry *entry;
 678        struct lg_drv_data *drv_data;
 679
 680        device_remove_file(&hid->dev, &dev_attr_range);
 681
 682        drv_data = hid_get_drvdata(hid);
 683        if (!drv_data) {
 684                hid_err(hid, "Error while deinitializing device, no private driver data.\n");
 685                return -1;
 686        }
 687        entry = drv_data->device_props;
 688        if (!entry) {
 689                hid_err(hid, "Error while deinitializing device, no device properties data.\n");
 690                return -1;
 691        }
 692
 693#ifdef CONFIG_LEDS_CLASS
 694        {
 695                int j;
 696                struct led_classdev *led;
 697
 698                /* Deregister LEDs (if any) */
 699                for (j = 0; j < 5; j++) {
 700
 701                        led = entry->led[j];
 702                        entry->led[j] = NULL;
 703                        if (!led)
 704                                continue;
 705                        led_classdev_unregister(led);
 706                        kfree(led);
 707                }
 708        }
 709#endif
 710
 711        /* Deallocate memory */
 712        kfree(entry);
 713
 714        dbg_hid("Device successfully unregistered\n");
 715        return 0;
 716}
 717
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.