1500 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Force feedback support for Logitech Gaming Wheels4 *5 * Including G27, G25, DFP, DFGT, FFEX, Momo, Momo2 &6 * Speed Force Wireless (WiiWheel)7 *8 * Copyright (c) 2010 Simon Wood <simon@mungewell.org>9 */10 11/*12 */13 14 15#include <linux/input.h>16#include <linux/usb.h>17#include <linux/hid.h>18 19#include "usbhid/usbhid.h"20#include "hid-lg.h"21#include "hid-lg4ff.h"22#include "hid-ids.h"23 24#define LG4FF_MMODE_IS_MULTIMODE 025#define LG4FF_MMODE_SWITCHED 126#define LG4FF_MMODE_NOT_MULTIMODE 227 28#define LG4FF_MODE_NATIVE_IDX 029#define LG4FF_MODE_DFEX_IDX 130#define LG4FF_MODE_DFP_IDX 231#define LG4FF_MODE_G25_IDX 332#define LG4FF_MODE_DFGT_IDX 433#define LG4FF_MODE_G27_IDX 534#define LG4FF_MODE_G29_IDX 635#define LG4FF_MODE_MAX_IDX 736 37#define LG4FF_MODE_NATIVE BIT(LG4FF_MODE_NATIVE_IDX)38#define LG4FF_MODE_DFEX BIT(LG4FF_MODE_DFEX_IDX)39#define LG4FF_MODE_DFP BIT(LG4FF_MODE_DFP_IDX)40#define LG4FF_MODE_G25 BIT(LG4FF_MODE_G25_IDX)41#define LG4FF_MODE_DFGT BIT(LG4FF_MODE_DFGT_IDX)42#define LG4FF_MODE_G27 BIT(LG4FF_MODE_G27_IDX)43#define LG4FF_MODE_G29 BIT(LG4FF_MODE_G29_IDX)44 45#define LG4FF_DFEX_TAG "DF-EX"46#define LG4FF_DFEX_NAME "Driving Force / Formula EX"47#define LG4FF_DFP_TAG "DFP"48#define LG4FF_DFP_NAME "Driving Force Pro"49#define LG4FF_G25_TAG "G25"50#define LG4FF_G25_NAME "G25 Racing Wheel"51#define LG4FF_G27_TAG "G27"52#define LG4FF_G27_NAME "G27 Racing Wheel"53#define LG4FF_G29_TAG "G29"54#define LG4FF_G29_NAME "G29 Racing Wheel"55#define LG4FF_DFGT_TAG "DFGT"56#define LG4FF_DFGT_NAME "Driving Force GT"57 58#define LG4FF_FFEX_REV_MAJ 0x2159#define LG4FF_FFEX_REV_MIN 0x0060 61static void lg4ff_set_range_dfp(struct hid_device *hid, u16 range);62static void lg4ff_set_range_g25(struct hid_device *hid, u16 range);63 64struct lg4ff_wheel_data {65 const u32 product_id;66 u16 combine;67 u16 range;68 const u16 min_range;69 const u16 max_range;70#ifdef CONFIG_LEDS_CLASS71 u8 led_state;72 struct led_classdev *led[5];73#endif74 const u32 alternate_modes;75 const char * const real_tag;76 const char * const real_name;77 const u16 real_product_id;78 79 void (*set_range)(struct hid_device *hid, u16 range);80};81 82struct lg4ff_device_entry {83 spinlock_t report_lock; /* Protect output HID report */84 struct hid_report *report;85 struct lg4ff_wheel_data wdata;86};87 88static const signed short lg4ff_wheel_effects[] = {89 FF_CONSTANT,90 FF_AUTOCENTER,91 -192};93 94static const signed short no_wheel_effects[] = {95 -196};97 98struct lg4ff_wheel {99 const u32 product_id;100 const signed short *ff_effects;101 const u16 min_range;102 const u16 max_range;103 void (*set_range)(struct hid_device *hid, u16 range);104};105 106struct lg4ff_compat_mode_switch {107 const u8 cmd_count; /* Number of commands to send */108 const u8 cmd[];109};110 111struct lg4ff_wheel_ident_info {112 const u32 modes;113 const u16 mask;114 const u16 result;115 const u16 real_product_id;116};117 118struct lg4ff_multimode_wheel {119 const u16 product_id;120 const u32 alternate_modes;121 const char *real_tag;122 const char *real_name;123};124 125struct lg4ff_alternate_mode {126 const u16 product_id;127 const char *tag;128 const char *name;129};130 131static const struct lg4ff_wheel lg4ff_devices[] = {132 {USB_DEVICE_ID_LOGITECH_WINGMAN_FG, no_wheel_effects, 40, 180, NULL},133 {USB_DEVICE_ID_LOGITECH_WINGMAN_FFG, lg4ff_wheel_effects, 40, 180, NULL},134 {USB_DEVICE_ID_LOGITECH_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},135 {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL, lg4ff_wheel_effects, 40, 270, NULL},136 {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_dfp},137 {USB_DEVICE_ID_LOGITECH_G25_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},138 {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},139 {USB_DEVICE_ID_LOGITECH_G27_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},140 {USB_DEVICE_ID_LOGITECH_G29_WHEEL, lg4ff_wheel_effects, 40, 900, lg4ff_set_range_g25},141 {USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2, lg4ff_wheel_effects, 40, 270, NULL},142 {USB_DEVICE_ID_LOGITECH_WII_WHEEL, lg4ff_wheel_effects, 40, 270, NULL}143};144 145static const struct lg4ff_multimode_wheel lg4ff_multimode_wheels[] = {146 {USB_DEVICE_ID_LOGITECH_DFP_WHEEL,147 LG4FF_MODE_NATIVE | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,148 LG4FF_DFP_TAG, LG4FF_DFP_NAME},149 {USB_DEVICE_ID_LOGITECH_G25_WHEEL,150 LG4FF_MODE_NATIVE | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,151 LG4FF_G25_TAG, LG4FF_G25_NAME},152 {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL,153 LG4FF_MODE_NATIVE | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,154 LG4FF_DFGT_TAG, LG4FF_DFGT_NAME},155 {USB_DEVICE_ID_LOGITECH_G27_WHEEL,156 LG4FF_MODE_NATIVE | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,157 LG4FF_G27_TAG, LG4FF_G27_NAME},158 {USB_DEVICE_ID_LOGITECH_G29_WHEEL,159 LG4FF_MODE_NATIVE | LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,160 LG4FF_G29_TAG, LG4FF_G29_NAME},161};162 163static const struct lg4ff_alternate_mode lg4ff_alternate_modes[] = {164 [LG4FF_MODE_NATIVE_IDX] = {0, "native", ""},165 [LG4FF_MODE_DFEX_IDX] = {USB_DEVICE_ID_LOGITECH_WHEEL, LG4FF_DFEX_TAG, LG4FF_DFEX_NAME},166 [LG4FF_MODE_DFP_IDX] = {USB_DEVICE_ID_LOGITECH_DFP_WHEEL, LG4FF_DFP_TAG, LG4FF_DFP_NAME},167 [LG4FF_MODE_G25_IDX] = {USB_DEVICE_ID_LOGITECH_G25_WHEEL, LG4FF_G25_TAG, LG4FF_G25_NAME},168 [LG4FF_MODE_DFGT_IDX] = {USB_DEVICE_ID_LOGITECH_DFGT_WHEEL, LG4FF_DFGT_TAG, LG4FF_DFGT_NAME},169 [LG4FF_MODE_G27_IDX] = {USB_DEVICE_ID_LOGITECH_G27_WHEEL, LG4FF_G27_TAG, LG4FF_G27_NAME},170 [LG4FF_MODE_G29_IDX] = {USB_DEVICE_ID_LOGITECH_G29_WHEEL, LG4FF_G29_TAG, LG4FF_G29_NAME},171};172 173/* Multimode wheel identificators */174static const struct lg4ff_wheel_ident_info lg4ff_dfp_ident_info = {175 LG4FF_MODE_DFP | LG4FF_MODE_DFEX,176 0xf000,177 0x1000,178 USB_DEVICE_ID_LOGITECH_DFP_WHEEL179};180 181static const struct lg4ff_wheel_ident_info lg4ff_g25_ident_info = {182 LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,183 0xff00,184 0x1200,185 USB_DEVICE_ID_LOGITECH_G25_WHEEL186};187 188static const struct lg4ff_wheel_ident_info lg4ff_g27_ident_info = {189 LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,190 0xfff0,191 0x1230,192 USB_DEVICE_ID_LOGITECH_G27_WHEEL193};194 195static const struct lg4ff_wheel_ident_info lg4ff_dfgt_ident_info = {196 LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,197 0xff00,198 0x1300,199 USB_DEVICE_ID_LOGITECH_DFGT_WHEEL200};201 202static const struct lg4ff_wheel_ident_info lg4ff_g29_ident_info = {203 LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,204 0xfff8,205 0x1350,206 USB_DEVICE_ID_LOGITECH_G29_WHEEL207};208 209static const struct lg4ff_wheel_ident_info lg4ff_g29_ident_info2 = {210 LG4FF_MODE_G29 | LG4FF_MODE_G27 | LG4FF_MODE_G25 | LG4FF_MODE_DFGT | LG4FF_MODE_DFP | LG4FF_MODE_DFEX,211 0xff00,212 0x8900,213 USB_DEVICE_ID_LOGITECH_G29_WHEEL214};215 216/* Multimode wheel identification checklists */217static const struct lg4ff_wheel_ident_info *lg4ff_main_checklist[] = {218 &lg4ff_g29_ident_info,219 &lg4ff_g29_ident_info2,220 &lg4ff_dfgt_ident_info,221 &lg4ff_g27_ident_info,222 &lg4ff_g25_ident_info,223 &lg4ff_dfp_ident_info224};225 226/* Compatibility mode switching commands */227/* EXT_CMD9 - Understood by G27 and DFGT */228static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfex = {229 2,230 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */231 0xf8, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00} /* Switch mode to DF-EX with detach */232};233 234static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfp = {235 2,236 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */237 0xf8, 0x09, 0x01, 0x01, 0x00, 0x00, 0x00} /* Switch mode to DFP with detach */238};239 240static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g25 = {241 2,242 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */243 0xf8, 0x09, 0x02, 0x01, 0x00, 0x00, 0x00} /* Switch mode to G25 with detach */244};245 246static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfgt = {247 2,248 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */249 0xf8, 0x09, 0x03, 0x01, 0x00, 0x00, 0x00} /* Switch mode to DFGT with detach */250};251 252static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g27 = {253 2,254 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */255 0xf8, 0x09, 0x04, 0x01, 0x00, 0x00, 0x00} /* Switch mode to G27 with detach */256};257 258static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g29 = {259 2,260 {0xf8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, /* Revert mode upon USB reset */261 0xf8, 0x09, 0x05, 0x01, 0x01, 0x00, 0x00} /* Switch mode to G29 with detach */262};263 264/* EXT_CMD1 - Understood by DFP, G25, G27 and DFGT */265static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext01_dfp = {266 1,267 {0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00}268};269 270/* EXT_CMD16 - Understood by G25 and G27 */271static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext16_g25 = {272 1,273 {0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}274};275 276/* Recalculates X axis value accordingly to currently selected range */277static s32 lg4ff_adjust_dfp_x_axis(s32 value, u16 range)278{279 u16 max_range;280 s32 new_value;281 282 if (range == 900)283 return value;284 else if (range == 200)285 return value;286 else if (range < 200)287 max_range = 200;288 else289 max_range = 900;290 291 new_value = 8192 + mult_frac(value - 8192, max_range, range);292 if (new_value < 0)293 return 0;294 else if (new_value > 16383)295 return 16383;296 else297 return new_value;298}299 300int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,301 struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data)302{303 struct lg4ff_device_entry *entry = drv_data->device_props;304 s32 new_value = 0;305 306 if (!entry) {307 hid_err(hid, "Device properties not found");308 return 0;309 }310 311 switch (entry->wdata.product_id) {312 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:313 switch (usage->code) {314 case ABS_X:315 new_value = lg4ff_adjust_dfp_x_axis(value, entry->wdata.range);316 input_event(field->hidinput->input, usage->type, usage->code, new_value);317 return 1;318 default:319 return 0;320 }321 default:322 return 0;323 }324}325 326int lg4ff_raw_event(struct hid_device *hdev, struct hid_report *report,327 u8 *rd, int size, struct lg_drv_data *drv_data)328{329 int offset;330 struct lg4ff_device_entry *entry = drv_data->device_props;331 332 if (!entry)333 return 0;334 335 /* adjust HID report present combined pedals data */336 if (entry->wdata.combine) {337 switch (entry->wdata.product_id) {338 case USB_DEVICE_ID_LOGITECH_WHEEL:339 rd[5] = rd[3];340 rd[6] = 0x7F;341 return 1;342 case USB_DEVICE_ID_LOGITECH_WINGMAN_FG:343 case USB_DEVICE_ID_LOGITECH_WINGMAN_FFG:344 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:345 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:346 rd[4] = rd[3];347 rd[5] = 0x7F;348 return 1;349 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:350 rd[5] = rd[4];351 rd[6] = 0x7F;352 return 1;353 case USB_DEVICE_ID_LOGITECH_G25_WHEEL:354 case USB_DEVICE_ID_LOGITECH_G27_WHEEL:355 offset = 5;356 break;357 case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:358 case USB_DEVICE_ID_LOGITECH_G29_WHEEL:359 offset = 6;360 break;361 case USB_DEVICE_ID_LOGITECH_WII_WHEEL:362 offset = 3;363 break;364 default:365 return 0;366 }367 368 /* Compute a combined axis when wheel does not supply it */369 rd[offset] = (0xFF + rd[offset] - rd[offset+1]) >> 1;370 rd[offset+1] = 0x7F;371 return 1;372 }373 374 return 0;375}376 377static void lg4ff_init_wheel_data(struct lg4ff_wheel_data * const wdata, const struct lg4ff_wheel *wheel,378 const struct lg4ff_multimode_wheel *mmode_wheel,379 const u16 real_product_id)380{381 u32 alternate_modes = 0;382 const char *real_tag = NULL;383 const char *real_name = NULL;384 385 if (mmode_wheel) {386 alternate_modes = mmode_wheel->alternate_modes;387 real_tag = mmode_wheel->real_tag;388 real_name = mmode_wheel->real_name;389 }390 391 {392 struct lg4ff_wheel_data t_wdata = { .product_id = wheel->product_id,393 .real_product_id = real_product_id,394 .combine = 0,395 .min_range = wheel->min_range,396 .max_range = wheel->max_range,397 .set_range = wheel->set_range,398 .alternate_modes = alternate_modes,399 .real_tag = real_tag,400 .real_name = real_name };401 402 memcpy(wdata, &t_wdata, sizeof(t_wdata));403 }404}405 406static int lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)407{408 struct hid_device *hid = input_get_drvdata(dev);409 struct lg4ff_device_entry *entry;410 struct lg_drv_data *drv_data;411 unsigned long flags;412 s32 *value;413 int x;414 415 drv_data = hid_get_drvdata(hid);416 if (!drv_data) {417 hid_err(hid, "Private driver data not found!\n");418 return -EINVAL;419 }420 421 entry = drv_data->device_props;422 if (!entry) {423 hid_err(hid, "Device properties not found!\n");424 return -EINVAL;425 }426 value = entry->report->field[0]->value;427 428#define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0)429 430 switch (effect->type) {431 case FF_CONSTANT:432 x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */433 CLAMP(x);434 435 spin_lock_irqsave(&entry->report_lock, flags);436 if (x == 0x80) {437 /* De-activate force in slot-1*/438 value[0] = 0x13;439 value[1] = 0x00;440 value[2] = 0x00;441 value[3] = 0x00;442 value[4] = 0x00;443 value[5] = 0x00;444 value[6] = 0x00;445 446 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);447 spin_unlock_irqrestore(&entry->report_lock, flags);448 return 0;449 }450 451 value[0] = 0x11; /* Slot 1 */452 value[1] = 0x08;453 value[2] = x;454 value[3] = 0x80;455 value[4] = 0x00;456 value[5] = 0x00;457 value[6] = 0x00;458 459 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);460 spin_unlock_irqrestore(&entry->report_lock, flags);461 break;462 }463 return 0;464}465 466/* Sends default autocentering command compatible with467 * all wheels except Formula Force EX */468static void lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)469{470 struct hid_device *hid = input_get_drvdata(dev);471 s32 *value;472 u32 expand_a, expand_b;473 struct lg4ff_device_entry *entry;474 struct lg_drv_data *drv_data;475 unsigned long flags;476 477 drv_data = hid_get_drvdata(hid);478 if (!drv_data) {479 hid_err(hid, "Private driver data not found!\n");480 return;481 }482 483 entry = drv_data->device_props;484 if (!entry) {485 hid_err(hid, "Device properties not found!\n");486 return;487 }488 value = entry->report->field[0]->value;489 490 /* De-activate Auto-Center */491 spin_lock_irqsave(&entry->report_lock, flags);492 if (magnitude == 0) {493 value[0] = 0xf5;494 value[1] = 0x00;495 value[2] = 0x00;496 value[3] = 0x00;497 value[4] = 0x00;498 value[5] = 0x00;499 value[6] = 0x00;500 501 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);502 spin_unlock_irqrestore(&entry->report_lock, flags);503 return;504 }505 506 if (magnitude <= 0xaaaa) {507 expand_a = 0x0c * magnitude;508 expand_b = 0x80 * magnitude;509 } else {510 expand_a = (0x0c * 0xaaaa) + 0x06 * (magnitude - 0xaaaa);511 expand_b = (0x80 * 0xaaaa) + 0xff * (magnitude - 0xaaaa);512 }513 514 /* Adjust for non-MOMO wheels */515 switch (entry->wdata.product_id) {516 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL:517 case USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2:518 break;519 default:520 expand_a = expand_a >> 1;521 break;522 }523 524 value[0] = 0xfe;525 value[1] = 0x0d;526 value[2] = expand_a / 0xaaaa;527 value[3] = expand_a / 0xaaaa;528 value[4] = expand_b / 0xaaaa;529 value[5] = 0x00;530 value[6] = 0x00;531 532 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);533 534 /* Activate Auto-Center */535 value[0] = 0x14;536 value[1] = 0x00;537 value[2] = 0x00;538 value[3] = 0x00;539 value[4] = 0x00;540 value[5] = 0x00;541 value[6] = 0x00;542 543 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);544 spin_unlock_irqrestore(&entry->report_lock, flags);545}546 547/* Sends autocentering command compatible with Formula Force EX */548static void lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)549{550 struct hid_device *hid = input_get_drvdata(dev);551 struct lg4ff_device_entry *entry;552 struct lg_drv_data *drv_data;553 unsigned long flags;554 s32 *value;555 magnitude = magnitude * 90 / 65535;556 557 drv_data = hid_get_drvdata(hid);558 if (!drv_data) {559 hid_err(hid, "Private driver data not found!\n");560 return;561 }562 563 entry = drv_data->device_props;564 if (!entry) {565 hid_err(hid, "Device properties not found!\n");566 return;567 }568 value = entry->report->field[0]->value;569 570 spin_lock_irqsave(&entry->report_lock, flags);571 value[0] = 0xfe;572 value[1] = 0x03;573 value[2] = magnitude >> 14;574 value[3] = magnitude >> 14;575 value[4] = magnitude;576 value[5] = 0x00;577 value[6] = 0x00;578 579 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);580 spin_unlock_irqrestore(&entry->report_lock, flags);581}582 583/* Sends command to set range compatible with G25/G27/Driving Force GT */584static void lg4ff_set_range_g25(struct hid_device *hid, u16 range)585{586 struct lg4ff_device_entry *entry;587 struct lg_drv_data *drv_data;588 unsigned long flags;589 s32 *value;590 591 drv_data = hid_get_drvdata(hid);592 if (!drv_data) {593 hid_err(hid, "Private driver data not found!\n");594 return;595 }596 597 entry = drv_data->device_props;598 if (!entry) {599 hid_err(hid, "Device properties not found!\n");600 return;601 }602 value = entry->report->field[0]->value;603 dbg_hid("G25/G27/DFGT: setting range to %u\n", range);604 605 spin_lock_irqsave(&entry->report_lock, flags);606 value[0] = 0xf8;607 value[1] = 0x81;608 value[2] = range & 0x00ff;609 value[3] = (range & 0xff00) >> 8;610 value[4] = 0x00;611 value[5] = 0x00;612 value[6] = 0x00;613 614 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);615 spin_unlock_irqrestore(&entry->report_lock, flags);616}617 618/* Sends commands to set range compatible with Driving Force Pro wheel */619static void lg4ff_set_range_dfp(struct hid_device *hid, u16 range)620{621 struct lg4ff_device_entry *entry;622 struct lg_drv_data *drv_data;623 unsigned long flags;624 int start_left, start_right, full_range;625 s32 *value;626 627 drv_data = hid_get_drvdata(hid);628 if (!drv_data) {629 hid_err(hid, "Private driver data not found!\n");630 return;631 }632 633 entry = drv_data->device_props;634 if (!entry) {635 hid_err(hid, "Device properties not found!\n");636 return;637 }638 value = entry->report->field[0]->value;639 dbg_hid("Driving Force Pro: setting range to %u\n", range);640 641 /* Prepare "coarse" limit command */642 spin_lock_irqsave(&entry->report_lock, flags);643 value[0] = 0xf8;644 value[1] = 0x00; /* Set later */645 value[2] = 0x00;646 value[3] = 0x00;647 value[4] = 0x00;648 value[5] = 0x00;649 value[6] = 0x00;650 651 if (range > 200) {652 value[1] = 0x03;653 full_range = 900;654 } else {655 value[1] = 0x02;656 full_range = 200;657 }658 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);659 660 /* Prepare "fine" limit command */661 value[0] = 0x81;662 value[1] = 0x0b;663 value[2] = 0x00;664 value[3] = 0x00;665 value[4] = 0x00;666 value[5] = 0x00;667 value[6] = 0x00;668 669 if (range == 200 || range == 900) { /* Do not apply any fine limit */670 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);671 spin_unlock_irqrestore(&entry->report_lock, flags);672 return;673 }674 675 /* Construct fine limit command */676 start_left = (((full_range - range + 1) * 2047) / full_range);677 start_right = 0xfff - start_left;678 679 value[2] = start_left >> 4;680 value[3] = start_right >> 4;681 value[4] = 0xff;682 value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);683 value[6] = 0xff;684 685 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);686 spin_unlock_irqrestore(&entry->report_lock, flags);687}688 689static const struct lg4ff_compat_mode_switch *lg4ff_get_mode_switch_command(const u16 real_product_id, const u16 target_product_id)690{691 switch (real_product_id) {692 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:693 switch (target_product_id) {694 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:695 return &lg4ff_mode_switch_ext01_dfp;696 /* DFP can only be switched to its native mode */697 default:698 return NULL;699 }700 break;701 case USB_DEVICE_ID_LOGITECH_G25_WHEEL:702 switch (target_product_id) {703 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:704 return &lg4ff_mode_switch_ext01_dfp;705 case USB_DEVICE_ID_LOGITECH_G25_WHEEL:706 return &lg4ff_mode_switch_ext16_g25;707 /* G25 can only be switched to DFP mode or its native mode */708 default:709 return NULL;710 }711 break;712 case USB_DEVICE_ID_LOGITECH_G27_WHEEL:713 switch (target_product_id) {714 case USB_DEVICE_ID_LOGITECH_WHEEL:715 return &lg4ff_mode_switch_ext09_dfex;716 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:717 return &lg4ff_mode_switch_ext09_dfp;718 case USB_DEVICE_ID_LOGITECH_G25_WHEEL:719 return &lg4ff_mode_switch_ext09_g25;720 case USB_DEVICE_ID_LOGITECH_G27_WHEEL:721 return &lg4ff_mode_switch_ext09_g27;722 /* G27 can only be switched to DF-EX, DFP, G25 or its native mode */723 default:724 return NULL;725 }726 break;727 case USB_DEVICE_ID_LOGITECH_G29_WHEEL:728 switch (target_product_id) {729 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:730 return &lg4ff_mode_switch_ext09_dfp;731 case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:732 return &lg4ff_mode_switch_ext09_dfgt;733 case USB_DEVICE_ID_LOGITECH_G25_WHEEL:734 return &lg4ff_mode_switch_ext09_g25;735 case USB_DEVICE_ID_LOGITECH_G27_WHEEL:736 return &lg4ff_mode_switch_ext09_g27;737 case USB_DEVICE_ID_LOGITECH_G29_WHEEL:738 return &lg4ff_mode_switch_ext09_g29;739 /* G29 can only be switched to DF-EX, DFP, DFGT, G25, G27 or its native mode */740 default:741 return NULL;742 }743 break;744 case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:745 switch (target_product_id) {746 case USB_DEVICE_ID_LOGITECH_WHEEL:747 return &lg4ff_mode_switch_ext09_dfex;748 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:749 return &lg4ff_mode_switch_ext09_dfp;750 case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL:751 return &lg4ff_mode_switch_ext09_dfgt;752 /* DFGT can only be switched to DF-EX, DFP or its native mode */753 default:754 return NULL;755 }756 break;757 /* No other wheels have multiple modes */758 default:759 return NULL;760 }761}762 763static int lg4ff_switch_compatibility_mode(struct hid_device *hid, const struct lg4ff_compat_mode_switch *s)764{765 struct lg4ff_device_entry *entry;766 struct lg_drv_data *drv_data;767 unsigned long flags;768 s32 *value;769 u8 i;770 771 drv_data = hid_get_drvdata(hid);772 if (!drv_data) {773 hid_err(hid, "Private driver data not found!\n");774 return -EINVAL;775 }776 777 entry = drv_data->device_props;778 if (!entry) {779 hid_err(hid, "Device properties not found!\n");780 return -EINVAL;781 }782 value = entry->report->field[0]->value;783 784 spin_lock_irqsave(&entry->report_lock, flags);785 for (i = 0; i < s->cmd_count; i++) {786 u8 j;787 788 for (j = 0; j < 7; j++)789 value[j] = s->cmd[j + (7*i)];790 791 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);792 }793 spin_unlock_irqrestore(&entry->report_lock, flags);794 hid_hw_wait(hid);795 return 0;796}797 798static ssize_t lg4ff_alternate_modes_show(struct device *dev, struct device_attribute *attr, char *buf)799{800 struct hid_device *hid = to_hid_device(dev);801 struct lg4ff_device_entry *entry;802 struct lg_drv_data *drv_data;803 ssize_t count = 0;804 int i;805 806 drv_data = hid_get_drvdata(hid);807 if (!drv_data) {808 hid_err(hid, "Private driver data not found!\n");809 return 0;810 }811 812 entry = drv_data->device_props;813 if (!entry) {814 hid_err(hid, "Device properties not found!\n");815 return 0;816 }817 818 if (!entry->wdata.real_name) {819 hid_err(hid, "NULL pointer to string\n");820 return 0;821 }822 823 for (i = 0; i < LG4FF_MODE_MAX_IDX; i++) {824 if (entry->wdata.alternate_modes & BIT(i)) {825 /* Print tag and full name */826 count += scnprintf(buf + count, PAGE_SIZE - count, "%s: %s",827 lg4ff_alternate_modes[i].tag,828 !lg4ff_alternate_modes[i].product_id ? entry->wdata.real_name : lg4ff_alternate_modes[i].name);829 if (count >= PAGE_SIZE - 1)830 return count;831 832 /* Mark the currently active mode with an asterisk */833 if (lg4ff_alternate_modes[i].product_id == entry->wdata.product_id ||834 (lg4ff_alternate_modes[i].product_id == 0 && entry->wdata.product_id == entry->wdata.real_product_id))835 count += scnprintf(buf + count, PAGE_SIZE - count, " *\n");836 else837 count += scnprintf(buf + count, PAGE_SIZE - count, "\n");838 839 if (count >= PAGE_SIZE - 1)840 return count;841 }842 }843 844 return count;845}846 847static ssize_t lg4ff_alternate_modes_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)848{849 struct hid_device *hid = to_hid_device(dev);850 struct lg4ff_device_entry *entry;851 struct lg_drv_data *drv_data;852 const struct lg4ff_compat_mode_switch *s;853 u16 target_product_id = 0;854 int i, ret;855 char *lbuf;856 857 drv_data = hid_get_drvdata(hid);858 if (!drv_data) {859 hid_err(hid, "Private driver data not found!\n");860 return -EINVAL;861 }862 863 entry = drv_data->device_props;864 if (!entry) {865 hid_err(hid, "Device properties not found!\n");866 return -EINVAL;867 }868 869 /* Allow \n at the end of the input parameter */870 lbuf = kasprintf(GFP_KERNEL, "%s", buf);871 if (!lbuf)872 return -ENOMEM;873 874 i = strlen(lbuf);875 876 if (i == 0) {877 kfree(lbuf);878 return -EINVAL;879 }880 881 if (lbuf[i-1] == '\n') {882 if (i == 1) {883 kfree(lbuf);884 return -EINVAL;885 }886 lbuf[i-1] = '\0';887 }888 889 for (i = 0; i < LG4FF_MODE_MAX_IDX; i++) {890 const u16 mode_product_id = lg4ff_alternate_modes[i].product_id;891 const char *tag = lg4ff_alternate_modes[i].tag;892 893 if (entry->wdata.alternate_modes & BIT(i)) {894 if (!strcmp(tag, lbuf)) {895 if (!mode_product_id)896 target_product_id = entry->wdata.real_product_id;897 else898 target_product_id = mode_product_id;899 break;900 }901 }902 }903 904 if (i == LG4FF_MODE_MAX_IDX) {905 hid_info(hid, "Requested mode \"%s\" is not supported by the device\n", lbuf);906 kfree(lbuf);907 return -EINVAL;908 }909 kfree(lbuf); /* Not needed anymore */910 911 if (target_product_id == entry->wdata.product_id) /* Nothing to do */912 return count;913 914 /* Automatic switching has to be disabled for the switch to DF-EX mode to work correctly */915 if (target_product_id == USB_DEVICE_ID_LOGITECH_WHEEL && !lg4ff_no_autoswitch) {916 hid_info(hid, "\"%s\" cannot be switched to \"DF-EX\" mode. Load the \"hid_logitech\" module with \"lg4ff_no_autoswitch=1\" parameter set and try again\n",917 entry->wdata.real_name);918 return -EINVAL;919 }920 921 /* Take care of hardware limitations */922 if ((entry->wdata.real_product_id == USB_DEVICE_ID_LOGITECH_DFP_WHEEL || entry->wdata.real_product_id == USB_DEVICE_ID_LOGITECH_G25_WHEEL) &&923 entry->wdata.product_id > target_product_id) {924 hid_info(hid, "\"%s\" cannot be switched back into \"%s\" mode\n", entry->wdata.real_name, lg4ff_alternate_modes[i].name);925 return -EINVAL;926 }927 928 s = lg4ff_get_mode_switch_command(entry->wdata.real_product_id, target_product_id);929 if (!s) {930 hid_err(hid, "Invalid target product ID %X\n", target_product_id);931 return -EINVAL;932 }933 934 ret = lg4ff_switch_compatibility_mode(hid, s);935 return (ret == 0 ? count : ret);936}937static DEVICE_ATTR(alternate_modes, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_alternate_modes_show, lg4ff_alternate_modes_store);938 939static ssize_t lg4ff_combine_show(struct device *dev, struct device_attribute *attr,940 char *buf)941{942 struct hid_device *hid = to_hid_device(dev);943 struct lg4ff_device_entry *entry;944 struct lg_drv_data *drv_data;945 size_t count;946 947 drv_data = hid_get_drvdata(hid);948 if (!drv_data) {949 hid_err(hid, "Private driver data not found!\n");950 return 0;951 }952 953 entry = drv_data->device_props;954 if (!entry) {955 hid_err(hid, "Device properties not found!\n");956 return 0;957 }958 959 count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.combine);960 return count;961}962 963static ssize_t lg4ff_combine_store(struct device *dev, struct device_attribute *attr,964 const char *buf, size_t count)965{966 struct hid_device *hid = to_hid_device(dev);967 struct lg4ff_device_entry *entry;968 struct lg_drv_data *drv_data;969 u16 combine = simple_strtoul(buf, NULL, 10);970 971 drv_data = hid_get_drvdata(hid);972 if (!drv_data) {973 hid_err(hid, "Private driver data not found!\n");974 return -EINVAL;975 }976 977 entry = drv_data->device_props;978 if (!entry) {979 hid_err(hid, "Device properties not found!\n");980 return -EINVAL;981 }982 983 if (combine > 1)984 combine = 1;985 986 entry->wdata.combine = combine;987 return count;988}989static DEVICE_ATTR(combine_pedals, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_combine_show, lg4ff_combine_store);990 991/* Export the currently set range of the wheel */992static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr,993 char *buf)994{995 struct hid_device *hid = to_hid_device(dev);996 struct lg4ff_device_entry *entry;997 struct lg_drv_data *drv_data;998 size_t count;999 1000 drv_data = hid_get_drvdata(hid);1001 if (!drv_data) {1002 hid_err(hid, "Private driver data not found!\n");1003 return 0;1004 }1005 1006 entry = drv_data->device_props;1007 if (!entry) {1008 hid_err(hid, "Device properties not found!\n");1009 return 0;1010 }1011 1012 count = scnprintf(buf, PAGE_SIZE, "%u\n", entry->wdata.range);1013 return count;1014}1015 1016/* Set range to user specified value, call appropriate function1017 * according to the type of the wheel */1018static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr,1019 const char *buf, size_t count)1020{1021 struct hid_device *hid = to_hid_device(dev);1022 struct lg4ff_device_entry *entry;1023 struct lg_drv_data *drv_data;1024 u16 range = simple_strtoul(buf, NULL, 10);1025 1026 drv_data = hid_get_drvdata(hid);1027 if (!drv_data) {1028 hid_err(hid, "Private driver data not found!\n");1029 return -EINVAL;1030 }1031 1032 entry = drv_data->device_props;1033 if (!entry) {1034 hid_err(hid, "Device properties not found!\n");1035 return -EINVAL;1036 }1037 1038 if (range == 0)1039 range = entry->wdata.max_range;1040 1041 /* Check if the wheel supports range setting1042 * and that the range is within limits for the wheel */1043 if (entry->wdata.set_range && range >= entry->wdata.min_range && range <= entry->wdata.max_range) {1044 entry->wdata.set_range(hid, range);1045 entry->wdata.range = range;1046 }1047 1048 return count;1049}1050static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, lg4ff_range_show, lg4ff_range_store);1051 1052static ssize_t lg4ff_real_id_show(struct device *dev, struct device_attribute *attr, char *buf)1053{1054 struct hid_device *hid = to_hid_device(dev);1055 struct lg4ff_device_entry *entry;1056 struct lg_drv_data *drv_data;1057 size_t count;1058 1059 drv_data = hid_get_drvdata(hid);1060 if (!drv_data) {1061 hid_err(hid, "Private driver data not found!\n");1062 return 0;1063 }1064 1065 entry = drv_data->device_props;1066 if (!entry) {1067 hid_err(hid, "Device properties not found!\n");1068 return 0;1069 }1070 1071 if (!entry->wdata.real_tag || !entry->wdata.real_name) {1072 hid_err(hid, "NULL pointer to string\n");1073 return 0;1074 }1075 1076 count = scnprintf(buf, PAGE_SIZE, "%s: %s\n", entry->wdata.real_tag, entry->wdata.real_name);1077 return count;1078}1079 1080static ssize_t lg4ff_real_id_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)1081{1082 /* Real ID is a read-only value */1083 return -EPERM;1084}1085static DEVICE_ATTR(real_id, S_IRUGO, lg4ff_real_id_show, lg4ff_real_id_store);1086 1087#ifdef CONFIG_LEDS_CLASS1088static void lg4ff_set_leds(struct hid_device *hid, u8 leds)1089{1090 struct lg_drv_data *drv_data;1091 struct lg4ff_device_entry *entry;1092 unsigned long flags;1093 s32 *value;1094 1095 drv_data = hid_get_drvdata(hid);1096 if (!drv_data) {1097 hid_err(hid, "Private driver data not found!\n");1098 return;1099 }1100 1101 entry = drv_data->device_props;1102 if (!entry) {1103 hid_err(hid, "Device properties not found!\n");1104 return;1105 }1106 value = entry->report->field[0]->value;1107 1108 spin_lock_irqsave(&entry->report_lock, flags);1109 value[0] = 0xf8;1110 value[1] = 0x12;1111 value[2] = leds;1112 value[3] = 0x00;1113 value[4] = 0x00;1114 value[5] = 0x00;1115 value[6] = 0x00;1116 hid_hw_request(hid, entry->report, HID_REQ_SET_REPORT);1117 spin_unlock_irqrestore(&entry->report_lock, flags);1118}1119 1120static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,1121 enum led_brightness value)1122{1123 struct device *dev = led_cdev->dev->parent;1124 struct hid_device *hid = to_hid_device(dev);1125 struct lg_drv_data *drv_data = hid_get_drvdata(hid);1126 struct lg4ff_device_entry *entry;1127 int i, state = 0;1128 1129 if (!drv_data) {1130 hid_err(hid, "Device data not found.");1131 return;1132 }1133 1134 entry = drv_data->device_props;1135 1136 if (!entry) {1137 hid_err(hid, "Device properties not found.");1138 return;1139 }1140 1141 for (i = 0; i < 5; i++) {1142 if (led_cdev != entry->wdata.led[i])1143 continue;1144 state = (entry->wdata.led_state >> i) & 1;1145 if (value == LED_OFF && state) {1146 entry->wdata.led_state &= ~(1 << i);1147 lg4ff_set_leds(hid, entry->wdata.led_state);1148 } else if (value != LED_OFF && !state) {1149 entry->wdata.led_state |= 1 << i;1150 lg4ff_set_leds(hid, entry->wdata.led_state);1151 }1152 break;1153 }1154}1155 1156static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cdev)1157{1158 struct device *dev = led_cdev->dev->parent;1159 struct hid_device *hid = to_hid_device(dev);1160 struct lg_drv_data *drv_data = hid_get_drvdata(hid);1161 struct lg4ff_device_entry *entry;1162 int i, value = 0;1163 1164 if (!drv_data) {1165 hid_err(hid, "Device data not found.");1166 return LED_OFF;1167 }1168 1169 entry = drv_data->device_props;1170 1171 if (!entry) {1172 hid_err(hid, "Device properties not found.");1173 return LED_OFF;1174 }1175 1176 for (i = 0; i < 5; i++)1177 if (led_cdev == entry->wdata.led[i]) {1178 value = (entry->wdata.led_state >> i) & 1;1179 break;1180 }1181 1182 return value ? LED_FULL : LED_OFF;1183}1184#endif1185 1186static u16 lg4ff_identify_multimode_wheel(struct hid_device *hid, const u16 reported_product_id, const u16 bcdDevice)1187{1188 u32 current_mode;1189 int i;1190 1191 /* identify current mode from USB PID */1192 for (i = 1; i < ARRAY_SIZE(lg4ff_alternate_modes); i++) {1193 dbg_hid("Testing whether PID is %X\n", lg4ff_alternate_modes[i].product_id);1194 if (reported_product_id == lg4ff_alternate_modes[i].product_id)1195 break;1196 }1197 1198 if (i == ARRAY_SIZE(lg4ff_alternate_modes))1199 return 0;1200 1201 current_mode = BIT(i);1202 1203 for (i = 0; i < ARRAY_SIZE(lg4ff_main_checklist); i++) {1204 const u16 mask = lg4ff_main_checklist[i]->mask;1205 const u16 result = lg4ff_main_checklist[i]->result;1206 const u16 real_product_id = lg4ff_main_checklist[i]->real_product_id;1207 1208 if ((current_mode & lg4ff_main_checklist[i]->modes) && \1209 (bcdDevice & mask) == result) {1210 dbg_hid("Found wheel with real PID %X whose reported PID is %X\n", real_product_id, reported_product_id);1211 return real_product_id;1212 }1213 }1214 1215 /* No match found. This is either Driving Force or an unknown1216 * wheel model, do not touch it */1217 dbg_hid("Wheel with bcdDevice %X was not recognized as multimode wheel, leaving in its current mode\n", bcdDevice);1218 return 0;1219}1220 1221static int lg4ff_handle_multimode_wheel(struct hid_device *hid, u16 *real_product_id, const u16 bcdDevice)1222{1223 const u16 reported_product_id = hid->product;1224 int ret;1225 1226 *real_product_id = lg4ff_identify_multimode_wheel(hid, reported_product_id, bcdDevice);1227 /* Probed wheel is not a multimode wheel */1228 if (!*real_product_id) {1229 *real_product_id = reported_product_id;1230 dbg_hid("Wheel is not a multimode wheel\n");1231 return LG4FF_MMODE_NOT_MULTIMODE;1232 }1233 1234 /* Switch from "Driving Force" mode to native mode automatically.1235 * Otherwise keep the wheel in its current mode */1236 if (reported_product_id == USB_DEVICE_ID_LOGITECH_WHEEL &&1237 reported_product_id != *real_product_id &&1238 !lg4ff_no_autoswitch) {1239 const struct lg4ff_compat_mode_switch *s = lg4ff_get_mode_switch_command(*real_product_id, *real_product_id);1240 1241 if (!s) {1242 hid_err(hid, "Invalid product id %X\n", *real_product_id);1243 return LG4FF_MMODE_NOT_MULTIMODE;1244 }1245 1246 ret = lg4ff_switch_compatibility_mode(hid, s);1247 if (ret) {1248 /* Wheel could not have been switched to native mode,1249 * leave it in "Driving Force" mode and continue */1250 hid_err(hid, "Unable to switch wheel mode, errno %d\n", ret);1251 return LG4FF_MMODE_IS_MULTIMODE;1252 }1253 return LG4FF_MMODE_SWITCHED;1254 }1255 1256 return LG4FF_MMODE_IS_MULTIMODE;1257}1258 1259 1260int lg4ff_init(struct hid_device *hid)1261{1262 struct hid_input *hidinput;1263 struct input_dev *dev;1264 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;1265 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);1266 const struct usb_device_descriptor *udesc = &(hid_to_usb_dev(hid)->descriptor);1267 const u16 bcdDevice = le16_to_cpu(udesc->bcdDevice);1268 const struct lg4ff_multimode_wheel *mmode_wheel = NULL;1269 struct lg4ff_device_entry *entry;1270 struct lg_drv_data *drv_data;1271 int error, i, j;1272 int mmode_ret, mmode_idx = -1;1273 u16 real_product_id;1274 1275 if (list_empty(&hid->inputs)) {1276 hid_err(hid, "no inputs found\n");1277 return -ENODEV;1278 }1279 hidinput = list_entry(hid->inputs.next, struct hid_input, list);1280 dev = hidinput->input;1281 1282 /* Check that the report looks ok */1283 if (!hid_validate_values(hid, HID_OUTPUT_REPORT, 0, 0, 7))1284 return -1;1285 1286 drv_data = hid_get_drvdata(hid);1287 if (!drv_data) {1288 hid_err(hid, "Cannot add device, private driver data not allocated\n");1289 return -1;1290 }1291 entry = kzalloc(sizeof(*entry), GFP_KERNEL);1292 if (!entry)1293 return -ENOMEM;1294 spin_lock_init(&entry->report_lock);1295 entry->report = report;1296 drv_data->device_props = entry;1297 1298 /* Check if a multimode wheel has been connected and1299 * handle it appropriately */1300 mmode_ret = lg4ff_handle_multimode_wheel(hid, &real_product_id, bcdDevice);1301 1302 /* Wheel has been told to switch to native mode. There is no point in going on1303 * with the initialization as the wheel will do a USB reset when it switches mode1304 */1305 if (mmode_ret == LG4FF_MMODE_SWITCHED)1306 return 0;1307 else if (mmode_ret < 0) {1308 hid_err(hid, "Unable to switch device mode during initialization, errno %d\n", mmode_ret);1309 error = mmode_ret;1310 goto err_init;1311 }1312 1313 /* Check what wheel has been connected */1314 for (i = 0; i < ARRAY_SIZE(lg4ff_devices); i++) {1315 if (hid->product == lg4ff_devices[i].product_id) {1316 dbg_hid("Found compatible device, product ID %04X\n", lg4ff_devices[i].product_id);1317 break;1318 }1319 }1320 1321 if (i == ARRAY_SIZE(lg4ff_devices)) {1322 hid_err(hid, "This device is flagged to be handled by the lg4ff module but this module does not know how to handle it. "1323 "Please report this as a bug to LKML, Simon Wood <simon@mungewell.org> or "1324 "Michal Maly <madcatxster@devoid-pointer.net>\n");1325 error = -1;1326 goto err_init;1327 }1328 1329 if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {1330 for (mmode_idx = 0; mmode_idx < ARRAY_SIZE(lg4ff_multimode_wheels); mmode_idx++) {1331 if (real_product_id == lg4ff_multimode_wheels[mmode_idx].product_id)1332 break;1333 }1334 1335 if (mmode_idx == ARRAY_SIZE(lg4ff_multimode_wheels)) {1336 hid_err(hid, "Device product ID %X is not listed as a multimode wheel", real_product_id);1337 error = -1;1338 goto err_init;1339 }1340 }1341 1342 /* Set supported force feedback capabilities */1343 for (j = 0; lg4ff_devices[i].ff_effects[j] >= 0; j++)1344 set_bit(lg4ff_devices[i].ff_effects[j], dev->ffbit);1345 1346 error = input_ff_create_memless(dev, NULL, lg4ff_play);1347 1348 if (error)1349 goto err_init;1350 1351 /* Initialize device properties */1352 if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {1353 BUG_ON(mmode_idx == -1);1354 mmode_wheel = &lg4ff_multimode_wheels[mmode_idx];1355 }1356 lg4ff_init_wheel_data(&entry->wdata, &lg4ff_devices[i], mmode_wheel, real_product_id);1357 1358 /* Check if autocentering is available and1359 * set the centering force to zero by default */1360 if (test_bit(FF_AUTOCENTER, dev->ffbit)) {1361 /* Formula Force EX expects different autocentering command */1362 if ((bcdDevice >> 8) == LG4FF_FFEX_REV_MAJ &&1363 (bcdDevice & 0xff) == LG4FF_FFEX_REV_MIN)1364 dev->ff->set_autocenter = lg4ff_set_autocenter_ffex;1365 else1366 dev->ff->set_autocenter = lg4ff_set_autocenter_default;1367 1368 dev->ff->set_autocenter(dev, 0);1369 }1370 1371 /* Create sysfs interface */1372 error = device_create_file(&hid->dev, &dev_attr_combine_pedals);1373 if (error)1374 hid_warn(hid, "Unable to create sysfs interface for \"combine\", errno %d\n", error);1375 error = device_create_file(&hid->dev, &dev_attr_range);1376 if (error)1377 hid_warn(hid, "Unable to create sysfs interface for \"range\", errno %d\n", error);1378 if (mmode_ret == LG4FF_MMODE_IS_MULTIMODE) {1379 error = device_create_file(&hid->dev, &dev_attr_real_id);1380 if (error)1381 hid_warn(hid, "Unable to create sysfs interface for \"real_id\", errno %d\n", error);1382 error = device_create_file(&hid->dev, &dev_attr_alternate_modes);1383 if (error)1384 hid_warn(hid, "Unable to create sysfs interface for \"alternate_modes\", errno %d\n", error);1385 }1386 dbg_hid("sysfs interface created\n");1387 1388 /* Set the maximum range to start with */1389 entry->wdata.range = entry->wdata.max_range;1390 if (entry->wdata.set_range)1391 entry->wdata.set_range(hid, entry->wdata.range);1392 1393#ifdef CONFIG_LEDS_CLASS1394 /* register led subsystem - G27/G29 only */1395 entry->wdata.led_state = 0;1396 for (j = 0; j < 5; j++)1397 entry->wdata.led[j] = NULL;1398 1399 if (lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G27_WHEEL ||1400 lg4ff_devices[i].product_id == USB_DEVICE_ID_LOGITECH_G29_WHEEL) {1401 struct led_classdev *led;1402 size_t name_sz;1403 char *name;1404 1405 lg4ff_set_leds(hid, 0);1406 1407 name_sz = strlen(dev_name(&hid->dev)) + 8;1408 1409 for (j = 0; j < 5; j++) {1410 led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);1411 if (!led) {1412 hid_err(hid, "can't allocate memory for LED %d\n", j);1413 goto err_leds;1414 }1415 1416 name = (void *)(&led[1]);1417 snprintf(name, name_sz, "%s::RPM%d", dev_name(&hid->dev), j+1);1418 led->name = name;1419 led->brightness = 0;1420 led->max_brightness = 1;1421 led->brightness_get = lg4ff_led_get_brightness;1422 led->brightness_set = lg4ff_led_set_brightness;1423 1424 entry->wdata.led[j] = led;1425 error = led_classdev_register(&hid->dev, led);1426 1427 if (error) {1428 hid_err(hid, "failed to register LED %d. Aborting.\n", j);1429err_leds:1430 /* Deregister LEDs (if any) */1431 for (j = 0; j < 5; j++) {1432 led = entry->wdata.led[j];1433 entry->wdata.led[j] = NULL;1434 if (!led)1435 continue;1436 led_classdev_unregister(led);1437 kfree(led);1438 }1439 goto out; /* Let the driver continue without LEDs */1440 }1441 }1442 }1443out:1444#endif1445 hid_info(hid, "Force feedback support for Logitech Gaming Wheels\n");1446 return 0;1447 1448err_init:1449 drv_data->device_props = NULL;1450 kfree(entry);1451 return error;1452}1453 1454int lg4ff_deinit(struct hid_device *hid)1455{1456 struct lg4ff_device_entry *entry;1457 struct lg_drv_data *drv_data;1458 1459 drv_data = hid_get_drvdata(hid);1460 if (!drv_data) {1461 hid_err(hid, "Error while deinitializing device, no private driver data.\n");1462 return -1;1463 }1464 entry = drv_data->device_props;1465 if (!entry)1466 goto out; /* Nothing more to do */1467 1468 /* Multimode devices will have at least the "MODE_NATIVE" bit set */1469 if (entry->wdata.alternate_modes) {1470 device_remove_file(&hid->dev, &dev_attr_real_id);1471 device_remove_file(&hid->dev, &dev_attr_alternate_modes);1472 }1473 1474 device_remove_file(&hid->dev, &dev_attr_combine_pedals);1475 device_remove_file(&hid->dev, &dev_attr_range);1476#ifdef CONFIG_LEDS_CLASS1477 {1478 int j;1479 struct led_classdev *led;1480 1481 /* Deregister LEDs (if any) */1482 for (j = 0; j < 5; j++) {1483 1484 led = entry->wdata.led[j];1485 entry->wdata.led[j] = NULL;1486 if (!led)1487 continue;1488 led_classdev_unregister(led);1489 kfree(led);1490 }1491 }1492#endif1493 drv_data->device_props = NULL;1494 1495 kfree(entry);1496out:1497 dbg_hid("Device successfully unregistered\n");1498 return 0;1499}1500