@@ -201,26 +201,47 @@ static const struct lg4ff_wheel_ident_checklist lg4ff_main_checklist = {
201
201
};
202
202
203
203
/* Compatibility mode switching commands */
204
- static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_dfp = {
205
- 1 ,
206
- {0xf8 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }
204
+ /* EXT_CMD9 - Understood by G27 and DFGT */
205
+ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfex = {
206
+ 2 ,
207
+ {0xf8 , 0x0a , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , /* Revert mode upon USB reset */
208
+ 0xf8 , 0x09 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 } /* Switch mode to DF-EX with detach */
207
209
};
208
210
209
- static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_dfgt = {
211
+ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfp = {
210
212
2 ,
211
- {0xf8 , 0x0a , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , /* 1st command */
212
- 0xf8 , 0x09 , 0x03 , 0x01 , 0x00 , 0x00 , 0x00 } /* 2nd command */
213
+ {0xf8 , 0x0a , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , /* Revert mode upon USB reset */
214
+ 0xf8 , 0x09 , 0x01 , 0x01 , 0x00 , 0x00 , 0x00 } /* Switch mode to DFP with detach */
213
215
};
214
216
215
- static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_g25 = {
216
- 1 ,
217
- {0xf8 , 0x10 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }
217
+ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g25 = {
218
+ 2 ,
219
+ {0xf8 , 0x0a , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , /* Revert mode upon USB reset */
220
+ 0xf8 , 0x09 , 0x02 , 0x01 , 0x00 , 0x00 , 0x00 } /* Switch mode to G25 with detach */
218
221
};
219
222
220
- static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_g27 = {
223
+ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_dfgt = {
221
224
2 ,
222
- {0xf8 , 0x0a , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , /* 1st command */
223
- 0xf8 , 0x09 , 0x04 , 0x01 , 0x00 , 0x00 , 0x00 } /* 2nd command */
225
+ {0xf8 , 0x0a , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , /* Revert mode upon USB reset */
226
+ 0xf8 , 0x09 , 0x03 , 0x01 , 0x00 , 0x00 , 0x00 } /* Switch mode to DFGT with detach */
227
+ };
228
+
229
+ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext09_g27 = {
230
+ 2 ,
231
+ {0xf8 , 0x0a , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , /* Revert mode upon USB reset */
232
+ 0xf8 , 0x09 , 0x04 , 0x01 , 0x00 , 0x00 , 0x00 } /* Switch mode to G27 with detach */
233
+ };
234
+
235
+ /* EXT_CMD1 - Understood by DFP, G25, G27 and DFGT */
236
+ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext01_dfp = {
237
+ 1 ,
238
+ {0xf8 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }
239
+ };
240
+
241
+ /* EXT_CMD16 - Understood by G25 and G27 */
242
+ static const struct lg4ff_compat_mode_switch lg4ff_mode_switch_ext16_g25 = {
243
+ 1 ,
244
+ {0xf8 , 0x10 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }
224
245
};
225
246
226
247
/* Recalculates X axis value accordingly to currently selected range */
@@ -489,6 +510,63 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
489
510
hid_hw_request (hid , report , HID_REQ_SET_REPORT );
490
511
}
491
512
513
+ static const struct lg4ff_compat_mode_switch * lg4ff_get_mode_switch_command (const u16 real_product_id , const u16 target_product_id )
514
+ {
515
+ switch (real_product_id ) {
516
+ case USB_DEVICE_ID_LOGITECH_DFP_WHEEL :
517
+ switch (target_product_id ) {
518
+ case USB_DEVICE_ID_LOGITECH_DFP_WHEEL :
519
+ return & lg4ff_mode_switch_ext01_dfp ;
520
+ /* DFP can only be switched to its native mode */
521
+ default :
522
+ return NULL ;
523
+ }
524
+ break ;
525
+ case USB_DEVICE_ID_LOGITECH_G25_WHEEL :
526
+ switch (target_product_id ) {
527
+ case USB_DEVICE_ID_LOGITECH_DFP_WHEEL :
528
+ return & lg4ff_mode_switch_ext01_dfp ;
529
+ case USB_DEVICE_ID_LOGITECH_G25_WHEEL :
530
+ return & lg4ff_mode_switch_ext16_g25 ;
531
+ /* G25 can only be switched to DFP mode or its native mode */
532
+ default :
533
+ return NULL ;
534
+ }
535
+ break ;
536
+ case USB_DEVICE_ID_LOGITECH_G27_WHEEL :
537
+ switch (target_product_id ) {
538
+ case USB_DEVICE_ID_LOGITECH_WHEEL :
539
+ return & lg4ff_mode_switch_ext09_dfex ;
540
+ case USB_DEVICE_ID_LOGITECH_DFP_WHEEL :
541
+ return & lg4ff_mode_switch_ext09_dfp ;
542
+ case USB_DEVICE_ID_LOGITECH_G25_WHEEL :
543
+ return & lg4ff_mode_switch_ext09_g25 ;
544
+ case USB_DEVICE_ID_LOGITECH_G27_WHEEL :
545
+ return & lg4ff_mode_switch_ext09_g27 ;
546
+ /* G27 can only be switched to DF-EX, DFP, G25 or its native mode */
547
+ default :
548
+ return NULL ;
549
+ }
550
+ break ;
551
+ case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL :
552
+ switch (target_product_id ) {
553
+ case USB_DEVICE_ID_LOGITECH_WHEEL :
554
+ return & lg4ff_mode_switch_ext09_dfex ;
555
+ case USB_DEVICE_ID_LOGITECH_DFP_WHEEL :
556
+ return & lg4ff_mode_switch_ext09_dfp ;
557
+ case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL :
558
+ return & lg4ff_mode_switch_ext09_dfgt ;
559
+ /* DFGT can only be switched to DF-EX, DFP or its native mode */
560
+ default :
561
+ return NULL ;
562
+ }
563
+ break ;
564
+ /* No other wheels have multiple modes */
565
+ default :
566
+ return NULL ;
567
+ }
568
+ }
569
+
492
570
static int lg4ff_switch_compatibility_mode (struct hid_device * hid , const struct lg4ff_compat_mode_switch * s )
493
571
{
494
572
struct usb_device * usbdev = hid_to_usb_dev (hid );
@@ -558,7 +636,87 @@ static ssize_t lg4ff_alternate_modes_show(struct device *dev, struct device_attr
558
636
559
637
static ssize_t lg4ff_alternate_modes_store (struct device * dev , struct device_attribute * attr , const char * buf , size_t count )
560
638
{
561
- return - ENOSYS ;
639
+ struct hid_device * hid = to_hid_device (dev );
640
+ struct lg4ff_device_entry * entry ;
641
+ struct lg_drv_data * drv_data ;
642
+ const struct lg4ff_compat_mode_switch * s ;
643
+ u16 target_product_id = 0 ;
644
+ int i , ret ;
645
+ char * lbuf ;
646
+
647
+ drv_data = hid_get_drvdata (hid );
648
+ if (!drv_data ) {
649
+ hid_err (hid , "Private driver data not found!\n" );
650
+ return - EINVAL ;
651
+ }
652
+
653
+ entry = drv_data -> device_props ;
654
+ if (!entry ) {
655
+ hid_err (hid , "Device properties not found!\n" );
656
+ return - EINVAL ;
657
+ }
658
+
659
+ /* Allow \n at the end of the input parameter */
660
+ lbuf = kasprintf (GFP_KERNEL , "%s" , buf );
661
+ if (!lbuf )
662
+ return - ENOMEM ;
663
+
664
+ i = strlen (lbuf );
665
+ if (lbuf [i - 1 ] == '\n' ) {
666
+ if (i == 1 ) {
667
+ kfree (lbuf );
668
+ return - EINVAL ;
669
+ }
670
+ lbuf [i - 1 ] = '\0' ;
671
+ }
672
+
673
+ for (i = 0 ; i < LG4FF_MODE_MAX_IDX ; i ++ ) {
674
+ const u16 mode_product_id = lg4ff_alternate_modes [i ].product_id ;
675
+ const char * tag = lg4ff_alternate_modes [i ].tag ;
676
+
677
+ if (entry -> alternate_modes & BIT (i )) {
678
+ if (!strcmp (tag , lbuf )) {
679
+ if (!mode_product_id )
680
+ target_product_id = entry -> real_product_id ;
681
+ else
682
+ target_product_id = mode_product_id ;
683
+ break ;
684
+ }
685
+ }
686
+ }
687
+
688
+ if (i == LG4FF_MODE_MAX_IDX ) {
689
+ hid_info (hid , "Requested mode \"%s\" is not supported by the device\n" , lbuf );
690
+ kfree (lbuf );
691
+ return - EINVAL ;
692
+ }
693
+ kfree (lbuf ); /* Not needed anymore */
694
+
695
+ if (target_product_id == entry -> product_id ) /* Nothing to do */
696
+ return count ;
697
+
698
+ /* Automatic switching has to be disabled for the switch to DF-EX mode to work correctly */
699
+ if (target_product_id == USB_DEVICE_ID_LOGITECH_WHEEL && !lg4ff_no_autoswitch ) {
700
+ 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" ,
701
+ entry -> real_name );
702
+ return - EINVAL ;
703
+ }
704
+
705
+ /* Take care of hardware limitations */
706
+ if ((entry -> real_product_id == USB_DEVICE_ID_LOGITECH_DFP_WHEEL || entry -> real_product_id == USB_DEVICE_ID_LOGITECH_G25_WHEEL ) &&
707
+ entry -> product_id > target_product_id ) {
708
+ hid_info (hid , "\"%s\" cannot be switched back into \"%s\" mode\n" , entry -> real_name , lg4ff_alternate_modes [i ].name );
709
+ return - EINVAL ;
710
+ }
711
+
712
+ s = lg4ff_get_mode_switch_command (entry -> real_product_id , target_product_id );
713
+ if (!s ) {
714
+ hid_err (hid , "Invalid target product ID %X\n" , target_product_id );
715
+ return - EINVAL ;
716
+ }
717
+
718
+ ret = lg4ff_switch_compatibility_mode (hid , s );
719
+ return (ret == 0 ? count : ret );
562
720
}
563
721
static DEVICE_ATTR (alternate_modes , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH , lg4ff_alternate_modes_show , lg4ff_alternate_modes_store ) ;
564
722
@@ -783,7 +941,8 @@ static u16 lg4ff_identify_multimode_wheel(struct hid_device *hid, const u16 repo
783
941
}
784
942
}
785
943
786
- /* No match found. This is an unknown wheel model, do not touch it */
944
+ /* No match found. This is either Driving Force or an unknown
945
+ * wheel model, do not touch it */
787
946
dbg_hid ("Wheel with bcdDevice %X was not recognized as multimode wheel, leaving in its current mode\n" , bcdDevice );
788
947
return 0 ;
789
948
}
@@ -806,22 +965,9 @@ static int lg4ff_handle_multimode_wheel(struct hid_device *hid, u16 *real_produc
806
965
if (reported_product_id == USB_DEVICE_ID_LOGITECH_WHEEL &&
807
966
reported_product_id != * real_product_id &&
808
967
!lg4ff_no_autoswitch ) {
809
- const struct lg4ff_compat_mode_switch * s ;
968
+ const struct lg4ff_compat_mode_switch * s = lg4ff_get_mode_switch_command ( * real_product_id , * real_product_id ) ;
810
969
811
- switch (* real_product_id ) {
812
- case USB_DEVICE_ID_LOGITECH_DFP_WHEEL :
813
- s = & lg4ff_mode_switch_dfp ;
814
- break ;
815
- case USB_DEVICE_ID_LOGITECH_G25_WHEEL :
816
- s = & lg4ff_mode_switch_g25 ;
817
- break ;
818
- case USB_DEVICE_ID_LOGITECH_G27_WHEEL :
819
- s = & lg4ff_mode_switch_g27 ;
820
- break ;
821
- case USB_DEVICE_ID_LOGITECH_DFGT_WHEEL :
822
- s = & lg4ff_mode_switch_dfgt ;
823
- break ;
824
- default :
970
+ if (!s ) {
825
971
hid_err (hid , "Invalid product id %X\n" , * real_product_id );
826
972
return LG4FF_MMODE_NOT_MULTIMODE ;
827
973
}
0 commit comments