hid-lg4ff.c (b3e5ced63e051e8f911b795ac5b06229a5328f7b) hid-lg4ff.c (2a552c30b2e44578b8d063e55b717ba94df84667)
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 */

--- 58 unchanged lines hidden (view full) ---

67
68#define LG4FF_FFEX_REV_MAJ 0x21
69#define LG4FF_FFEX_REV_MIN 0x00
70
71static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
72static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range);
73
74struct lg4ff_device_entry {
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 */

--- 58 unchanged lines hidden (view full) ---

67
68#define LG4FF_FFEX_REV_MAJ 0x21
69#define LG4FF_FFEX_REV_MIN 0x00
70
71static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
72static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range);
73
74struct lg4ff_device_entry {
75 __u32 product_id;
76 __u16 range;
77 __u16 min_range;
78 __u16 max_range;
75 u32 product_id;
76 u16 range;
77 u16 min_range;
78 u16 max_range;
79#ifdef CONFIG_LEDS_CLASS
79#ifdef CONFIG_LEDS_CLASS
80 __u8 led_state;
80 u8 led_state;
81 struct led_classdev *led[5];
82#endif
83 u32 alternate_modes;
84 const char *real_tag;
85 const char *real_name;
86 u16 real_product_id;
87 struct list_head list;
88 void (*set_range)(struct hid_device *hid, u16 range);
89};
90
91static const signed short lg4ff_wheel_effects[] = {
92 FF_CONSTANT,
93 FF_AUTOCENTER,
94 -1
95};
96
97struct lg4ff_wheel {
81 struct led_classdev *led[5];
82#endif
83 u32 alternate_modes;
84 const char *real_tag;
85 const char *real_name;
86 u16 real_product_id;
87 struct list_head list;
88 void (*set_range)(struct hid_device *hid, u16 range);
89};
90
91static const signed short lg4ff_wheel_effects[] = {
92 FF_CONSTANT,
93 FF_AUTOCENTER,
94 -1
95};
96
97struct lg4ff_wheel {
98 const __u32 product_id;
98 const u32 product_id;
99 const signed short *ff_effects;
99 const signed short *ff_effects;
100 const __u16 min_range;
101 const __u16 max_range;
100 const u16 min_range;
101 const u16 max_range;
102 void (*set_range)(struct hid_device *hid, u16 range);
103};
104
105struct lg4ff_compat_mode_switch {
102 void (*set_range)(struct hid_device *hid, u16 range);
103};
104
105struct lg4ff_compat_mode_switch {
106 const __u8 cmd_count; /* Number of commands to send */
107 const __u8 cmd[];
106 const u8 cmd_count; /* Number of commands to send */
107 const u8 cmd[];
108};
109
110struct lg4ff_wheel_ident_info {
111 const u16 mask;
112 const u16 result;
113 const u16 real_product_id;
114};
115

--- 124 unchanged lines hidden (view full) ---

240
241/* EXT_CMD16 - Understood by G25 and G27 */
242static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext16_g25 = {
243 1,
244 {0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}
245};
246
247/* Recalculates X axis value accordingly to currently selected range */
108};
109
110struct lg4ff_wheel_ident_info {
111 const u16 mask;
112 const u16 result;
113 const u16 real_product_id;
114};
115

--- 124 unchanged lines hidden (view full) ---

240
241/* EXT_CMD16 - Understood by G25 and G27 */
242static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext16_g25 = {
243 1,
244 {0xf8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00}
245};
246
247/* Recalculates X axis value accordingly to currently selected range */
248static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range)
248static s32 lg4ff_adjust_dfp_x_axis(s32 value, u16 range)
249{
249{
250 __u16 max_range;
251 __s32 new_value;
250 u16 max_range;
251 s32 new_value;
252
253 if (range == 900)
254 return value;
255 else if (range == 200)
256 return value;
257 else if (range < 200)
258 max_range = 200;
259 else

--- 4 unchanged lines hidden (view full) ---

264 return 0;
265 else if (new_value > 16383)
266 return 16383;
267 else
268 return new_value;
269}
270
271int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
252
253 if (range == 900)
254 return value;
255 else if (range == 200)
256 return value;
257 else if (range < 200)
258 max_range = 200;
259 else

--- 4 unchanged lines hidden (view full) ---

264 return 0;
265 else if (new_value > 16383)
266 return 16383;
267 else
268 return new_value;
269}
270
271int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
272 struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data)
272 struct hid_usage *usage, s32 value, struct lg_drv_data *drv_data)
273{
274 struct lg4ff_device_entry *entry = drv_data->device_props;
273{
274 struct lg4ff_device_entry *entry = drv_data->device_props;
275 __s32 new_value = 0;
275 s32 new_value = 0;
276
277 if (!entry) {
278 hid_err(hid, "Device properties not found");
279 return 0;
280 }
281
282 switch (entry->product_id) {
283 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:

--- 10 unchanged lines hidden (view full) ---

294 }
295}
296
297static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
298{
299 struct hid_device *hid = input_get_drvdata(dev);
300 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
301 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
276
277 if (!entry) {
278 hid_err(hid, "Device properties not found");
279 return 0;
280 }
281
282 switch (entry->product_id) {
283 case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:

--- 10 unchanged lines hidden (view full) ---

294 }
295}
296
297static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
298{
299 struct hid_device *hid = input_get_drvdata(dev);
300 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
301 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
302 __s32 *value = report->field[0]->value;
302 s32 *value = report->field[0]->value;
303 int x;
304
305#define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0)
306
307 switch (effect->type) {
308 case FF_CONSTANT:
309 x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */
310 CLAMP(x);

--- 28 unchanged lines hidden (view full) ---

339
340/* Sends default autocentering command compatible with
341 * all wheels except Formula Force EX */
342static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
343{
344 struct hid_device *hid = input_get_drvdata(dev);
345 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
346 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
303 int x;
304
305#define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0)
306
307 switch (effect->type) {
308 case FF_CONSTANT:
309 x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */
310 CLAMP(x);

--- 28 unchanged lines hidden (view full) ---

339
340/* Sends default autocentering command compatible with
341 * all wheels except Formula Force EX */
342static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitude)
343{
344 struct hid_device *hid = input_get_drvdata(dev);
345 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
346 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
347 __s32 *value = report->field[0]->value;
348 __u32 expand_a, expand_b;
347 s32 *value = report->field[0]->value;
348 u32 expand_a, expand_b;
349 struct lg4ff_device_entry *entry;
350 struct lg_drv_data *drv_data;
351
352 drv_data = hid_get_drvdata(hid);
353 if (!drv_data) {
354 hid_err(hid, "Private driver data not found!\n");
355 return;
356 }

--- 59 unchanged lines hidden (view full) ---

416}
417
418/* Sends autocentering command compatible with Formula Force EX */
419static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
420{
421 struct hid_device *hid = input_get_drvdata(dev);
422 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
423 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
349 struct lg4ff_device_entry *entry;
350 struct lg_drv_data *drv_data;
351
352 drv_data = hid_get_drvdata(hid);
353 if (!drv_data) {
354 hid_err(hid, "Private driver data not found!\n");
355 return;
356 }

--- 59 unchanged lines hidden (view full) ---

416}
417
418/* Sends autocentering command compatible with Formula Force EX */
419static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
420{
421 struct hid_device *hid = input_get_drvdata(dev);
422 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
423 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
424 __s32 *value = report->field[0]->value;
424 s32 *value = report->field[0]->value;
425 magnitude = magnitude * 90 / 65535;
426
427 value[0] = 0xfe;
428 value[1] = 0x03;
429 value[2] = magnitude >> 14;
430 value[3] = magnitude >> 14;
431 value[4] = magnitude;
432 value[5] = 0x00;
433 value[6] = 0x00;
434
435 hid_hw_request(hid, report, HID_REQ_SET_REPORT);
436}
437
438/* Sends command to set range compatible with G25/G27/Driving Force GT */
439static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
440{
441 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
442 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
425 magnitude = magnitude * 90 / 65535;
426
427 value[0] = 0xfe;
428 value[1] = 0x03;
429 value[2] = magnitude >> 14;
430 value[3] = magnitude >> 14;
431 value[4] = magnitude;
432 value[5] = 0x00;
433 value[6] = 0x00;
434
435 hid_hw_request(hid, report, HID_REQ_SET_REPORT);
436}
437
438/* Sends command to set range compatible with G25/G27/Driving Force GT */
439static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
440{
441 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
442 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
443 __s32 *value = report->field[0]->value;
443 s32 *value = report->field[0]->value;
444
445 dbg_hid("G25/G27/DFGT: setting range to %u\n", range);
446
447 value[0] = 0xf8;
448 value[1] = 0x81;
449 value[2] = range & 0x00ff;
450 value[3] = (range & 0xff00) >> 8;
451 value[4] = 0x00;
452 value[5] = 0x00;
453 value[6] = 0x00;
454
455 hid_hw_request(hid, report, HID_REQ_SET_REPORT);
456}
457
458/* Sends commands to set range compatible with Driving Force Pro wheel */
444
445 dbg_hid("G25/G27/DFGT: setting range to %u\n", range);
446
447 value[0] = 0xf8;
448 value[1] = 0x81;
449 value[2] = range & 0x00ff;
450 value[3] = (range & 0xff00) >> 8;
451 value[4] = 0x00;
452 value[5] = 0x00;
453 value[6] = 0x00;
454
455 hid_hw_request(hid, report, HID_REQ_SET_REPORT);
456}
457
458/* Sends commands to set range compatible with Driving Force Pro wheel */
459static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
459static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range)
460{
461 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
462 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
463 int start_left, start_right, full_range;
460{
461 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
462 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
463 int start_left, start_right, full_range;
464 __s32 *value = report->field[0]->value;
464 s32 *value = report->field[0]->value;
465
466 dbg_hid("Driving Force Pro: setting range to %u\n", range);
467
468 /* Prepare "coarse" limit command */
469 value[0] = 0xf8;
470 value[1] = 0x00; /* Set later */
471 value[2] = 0x00;
472 value[3] = 0x00;

--- 93 unchanged lines hidden (view full) ---

566 return NULL;
567 }
568}
569
570static int lg4ff_switch_compatibility_mode(struct hid_device *hid, const struct lg4ff_compat_mode_switch *s)
571{
572 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
573 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
465
466 dbg_hid("Driving Force Pro: setting range to %u\n", range);
467
468 /* Prepare "coarse" limit command */
469 value[0] = 0xf8;
470 value[1] = 0x00; /* Set later */
471 value[2] = 0x00;
472 value[3] = 0x00;

--- 93 unchanged lines hidden (view full) ---

566 return NULL;
567 }
568}
569
570static int lg4ff_switch_compatibility_mode(struct hid_device *hid, const struct lg4ff_compat_mode_switch *s)
571{
572 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
573 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
574 __s32 *value = report->field[0]->value;
574 s32 *value = report->field[0]->value;
575 u8 i;
576
577 for (i = 0; i < s->cmd_count; i++) {
578 u8 j;
579
580 for (j = 0; j < 7; j++)
581 value[j] = s->cmd[j + (7*i)];
582

--- 166 unchanged lines hidden (view full) ---

749/* Set range to user specified value, call appropriate function
750 * according to the type of the wheel */
751static ssize_t range_store(struct device *dev, struct device_attribute *attr,
752 const char *buf, size_t count)
753{
754 struct hid_device *hid = to_hid_device(dev);
755 struct lg4ff_device_entry *entry;
756 struct lg_drv_data *drv_data;
575 u8 i;
576
577 for (i = 0; i < s->cmd_count; i++) {
578 u8 j;
579
580 for (j = 0; j < 7; j++)
581 value[j] = s->cmd[j + (7*i)];
582

--- 166 unchanged lines hidden (view full) ---

749/* Set range to user specified value, call appropriate function
750 * according to the type of the wheel */
751static ssize_t range_store(struct device *dev, struct device_attribute *attr,
752 const char *buf, size_t count)
753{
754 struct hid_device *hid = to_hid_device(dev);
755 struct lg4ff_device_entry *entry;
756 struct lg_drv_data *drv_data;
757 __u16 range = simple_strtoul(buf, NULL, 10);
757 u16 range = simple_strtoul(buf, NULL, 10);
758
759 drv_data = hid_get_drvdata(hid);
760 if (!drv_data) {
761 hid_err(hid, "Private driver data not found!\n");
762 return -EINVAL;
763 }
764
765 entry = drv_data->device_props;

--- 47 unchanged lines hidden (view full) ---

813static ssize_t lg4ff_real_id_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
814{
815 /* Real ID is a read-only value */
816 return -EPERM;
817}
818static DEVICE_ATTR(real_id, S_IRUGO, lg4ff_real_id_show, lg4ff_real_id_store);
819
820#ifdef CONFIG_LEDS_CLASS
758
759 drv_data = hid_get_drvdata(hid);
760 if (!drv_data) {
761 hid_err(hid, "Private driver data not found!\n");
762 return -EINVAL;
763 }
764
765 entry = drv_data->device_props;

--- 47 unchanged lines hidden (view full) ---

813static ssize_t lg4ff_real_id_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
814{
815 /* Real ID is a read-only value */
816 return -EPERM;
817}
818static DEVICE_ATTR(real_id, S_IRUGO, lg4ff_real_id_show, lg4ff_real_id_store);
819
820#ifdef CONFIG_LEDS_CLASS
821static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
821static void lg4ff_set_leds(struct hid_device *hid, u8 leds)
822{
823 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
824 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
822{
823 struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
824 struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
825 __s32 *value = report->field[0]->value;
825 s32 *value = report->field[0]->value;
826
827 value[0] = 0xf8;
828 value[1] = 0x12;
829 value[2] = leds;
830 value[3] = 0x00;
831 value[4] = 0x00;
832 value[5] = 0x00;
833 value[6] = 0x00;

--- 378 unchanged lines hidden ---
826
827 value[0] = 0xf8;
828 value[1] = 0x12;
829 value[2] = leds;
830 value[3] = 0x00;
831 value[4] = 0x00;
832 value[5] = 0x00;
833 value[6] = 0x00;

--- 378 unchanged lines hidden ---