···11+What: /sys/class/leds/go:rgb:joystick_rings/effect22+Date: April 202633+Contact: linux-input@vger.kernel.org44+Description: This controls the display effect of the RGB interface.55+66+ Values are monocolor, breathe, chroma, or rainbow.77+88+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.99+1010+What: /sys/class/leds/go:rgb:joystick_rings/effect_index1111+Date: April 20261212+Contact: linux-input@vger.kernel.org1313+Description: This displays the available options for the effect attribute.1414+1515+ Values are monocolor, breathe, chroma, or rainbow.1616+1717+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.1818+1919+What: /sys/class/leds/go:rgb:joystick_rings/enabled2020+Date: April 20262121+Contact: linux-input@vger.kernel.org2222+Description: This controls enabling or disabling the RGB interface.2323+2424+ Values are true or false.2525+2626+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.2727+2828+What: /sys/class/leds/go:rgb:joystick_rings/enabled_index2929+Date: April 20263030+Contact: linux-input@vger.kernel.org3131+Description: This displays the available options for the enabled attribute.3232+3333+ Values are true or false.3434+3535+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.3636+3737+What: /sys/class/leds/go:rgb:joystick_rings/mode3838+Date: April 20263939+Contact: linux-input@vger.kernel.org4040+Description: This controls the operating mode of the RGB interface.4141+4242+ Values are dynamic or custom. Custom allows setting the RGB effect and color.4343+ Dynamic is a Windows mode for syncing Lenovo RGB interfaces not currently4444+ supported under Linux.4545+4646+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.4747+4848+What: /sys/class/leds/go:rgb:joystick_rings/mode_index4949+Date: April 20265050+Contact: linux-input@vger.kernel.org5151+Description: This displays the available options for the mode attribute.5252+5353+ Values are dynamic or custom.5454+5555+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.5656+5757+What: /sys/class/leds/go:rgb:joystick_rings/profile5858+Date: April 20265959+Contact: linux-input@vger.kernel.org6060+Description: This controls selecting the configured RGB profile.6161+6262+ Values are 1-3.6363+6464+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.6565+6666+What: /sys/class/leds/go:rgb:joystick_rings/profile_range6767+Date: April 20266868+Contact: linux-input@vger.kernel.org6969+Description: This displays the available options for the profile attribute.7070+7171+ Values are 1-3.7272+7373+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.7474+7575+What: /sys/class/leds/go:rgb:joystick_rings/speed7676+Date: April 20267777+Contact: linux-input@vger.kernel.org7878+Description: This controls the change rate for the breathe, chroma, and rainbow effects.7979+8080+ Values are 0-100.8181+8282+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.8383+8484+What: /sys/class/leds/go:rgb:joystick_rings/speed_range8585+Date: April 20268686+Contact: linux-input@vger.kernel.org8787+Description: This displays the available options for the speed attribute.8888+8989+ Values are 0-100.9090+9191+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.9292+9393+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/firmware_version9494+Date: April 20269595+Contact: linux-input@vger.kernel.org9696+Description: This displays the firmware version of the internal MCU.9797+9898+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.9999+100100+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/fps_mode_dpi101101+Date: April 2026102102+Contact: linux-input@vger.kernel.org103103+Description: This displays the DPI of the right handle when the FPS mode switch is on.104104+105105+ Values are 500, 800, 1200, and 1800.106106+107107+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.108108+109109+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/fps_mode_dpi_index110110+Date: April 2026111111+Contact: linux-input@vger.kernel.org112112+Description: This displays the available options for the fps_mode_dpi attribute.113113+114114+ Values are 500, 800, 1200, and 1800.115115+116116+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.117117+118118+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/hardware_generation119119+Date: April 2026120120+Contact: linux-input@vger.kernel.org121121+Description: This displays the hardware generation of the internal MCU.122122+123123+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.124124+125125+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/hardware_version126126+Date: April 2026127127+Contact: linux-input@vger.kernel.org128128+Description: This displays the hardware version of the internal MCU.129129+130130+131131+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.132132+133133+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/auto_sleep_time134134+Date: April 2026135135+Contact: linux-input@vger.kernel.org136136+Description: This controls the sleep timer due to inactivity for the left removable controller.137137+138138+ Values are 0-255.139139+140140+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.141141+142142+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/auto_sleep_time_range143143+Date: April 2026144144+Contact: linux-input@vger.kernel.org145145+Description: This displays the available options for the left_handle/auto_sleep_time attribute.146146+147147+ Values are 0-255.148148+149149+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.150150+151151+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_gyro152152+Date: April 2026153153+Contact: linux-input@vger.kernel.org154154+Description: This initiates or halts calibration of the left removable controller's IMU.155155+156156+ Values are start, stop.157157+158158+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.159159+160160+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_gyro_index161161+Date: April 2026162162+Contact: linux-input@vger.kernel.org163163+Description: This displays the available options for the left_handle/calibrate_gyro attribute.164164+165165+ Values are start, stop.166166+167167+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.168168+169169+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_gyro_status170170+Date: April 2026171171+Contact: linux-input@vger.kernel.org172172+Description: This displays the result of the last attempted calibration of the left removable controller's IMU.173173+174174+ Values are unknown, success, failure.175175+176176+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.177177+178178+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_joystick179179+Date: April 2026180180+Contact: linux-input@vger.kernel.org181181+Description: This initiates or halts calibration of the left removable controller's joystick.182182+183183+ Values are start, stop.184184+185185+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.186186+187187+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_joystick_index188188+Date: April 2026189189+Contact: linux-input@vger.kernel.org190190+Description: This displays the available options for the left_handle/calibrate_jotstick attribute.191191+192192+ Values are start, stop.193193+194194+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.195195+196196+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_joystick_status197197+Date: April 2026198198+Contact: linux-input@vger.kernel.org199199+Description: This displays the result of the last attempted calibration of the left removable controller's joystick.200200+201201+ Values are unknown, success, failure.202202+203203+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.204204+205205+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_tirgger206206+Date: April 2026207207+Contact: linux-input@vger.kernel.org208208+Description: This initiates or halts calibration of the left removable controller's trigger.209209+210210+ Values are start, stop.211211+212212+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.213213+214214+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_gyro_trigger215215+Date: April 2026216216+Contact: linux-input@vger.kernel.org217217+Description: This displays the available options for the left_handle/calibrate_trigger attribute.218218+219219+ Values are start, stop.220220+221221+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.222222+223223+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/calibrate_trigger_status224224+Date: April 2026225225+Contact: linux-input@vger.kernel.org226226+Description: This displays the result of the last attempted calibration of the left removable controller's trigger.227227+228228+ Values are unknown, success, failure.229229+230230+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.231231+232232+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/firmware_version233233+Date: April 2026234234+Contact: linux-input@vger.kernel.org235235+Description: This displays the left removable controller's firmware version.236236+237237+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.238238+239239+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/hardware_generation240240+Date: April 2026241241+Contact: linux-input@vger.kernel.org242242+Description: This displays the hardware generation of the left removable controller.243243+244244+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.245245+246246+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/hardware_version247247+Date: April 2026248248+Contact: linux-input@vger.kernel.org249249+Description: This displays the hardware version of the left removable controller.250250+251251+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.252252+253253+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/imu_bypass_enabled254254+Date: April 2026255255+Contact: linux-input@vger.kernel.org256256+Description: This controls enabling or disabling the IMU bypass function of the left removable controller.257257+258258+ Values are true or false.259259+260260+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.261261+262262+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/imu_bypass_enabled_index263263+Date: April 2026264264+Contact: linux-input@vger.kernel.org265265+Description: This displays the available options for the left_handle/imu_bypass_enabled attribute.266266+267267+ Values are true or false.268268+269269+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/imu_enabled270270+Date: April 2026271271+Contact: linux-input@vger.kernel.org272272+Description: This controls enabling or disabling the IMU of the left removable controller.273273+274274+ Values are true or false.275275+276276+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.277277+278278+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/imu_enabled_index279279+Date: April 2026280280+Contact: linux-input@vger.kernel.org281281+Description: This displays the available options for the left_handle/imu_enabled attribute.282282+283283+ Values are true or false.284284+285285+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.286286+287287+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/product_version288288+Date: April 2026289289+Contact: linux-input@vger.kernel.org290290+Description: This displays the product version of the left removable controller.291291+292292+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.293293+294294+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/protocol_version295295+Date: April 2026296296+Contact: linux-input@vger.kernel.org297297+Description: This displays the protocol version of the left removable controller.298298+299299+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.300300+301301+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/reset302302+Date: April 2026303303+Contact: linux-input@vger.kernel.org304304+Description: Resets the left removable controller to factory defaults.305305+306306+ Writing 1 to this path initiates.307307+308308+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.309309+310310+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/rumble_mode311311+Date: April 2026312312+Contact: linux-input@vger.kernel.org313313+Description: This controls setting the response behavior for rumble events for the left removable controller.314314+315315+ Values are fps, racing, standarg, spg, rpg.316316+317317+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.318318+319319+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/rumble_mode_index320320+Date: April 2026321321+Contact: linux-input@vger.kernel.org322322+Description: This displays the available options for the left_handle/rumble_mode attribute.323323+324324+ Values are fps, racing, standarg, spg, rpg.325325+326326+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.327327+328328+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/rumble_notification329329+Date: April 2026330330+Contact: linux-input@vger.kernel.org331331+Description: This controls enabling haptic rumble events for the left removable controller.332332+333333+ Values are true, false.334334+335335+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.336336+337337+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/rumble_notification_index338338+Date: April 2026339339+Contact: linux-input@vger.kernel.org340340+Description: This displays the available options for the left_handle/rumble_notification attribute.341341+342342+ Values are true, false.343343+344344+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.345345+346346+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/mode347347+Date: April 2026348348+Contact: linux-input@vger.kernel.org349349+Description: This controls the operating mode of the built-in controller.350350+351351+ Values are xinput or dinput.352352+353353+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.354354+355355+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/left_handle/mode_index356356+Date: April 2026357357+Contact: linux-input@vger.kernel.org358358+Description: This displays the available options for the mode attribute.359359+360360+ Values are xinput or dinput.361361+362362+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.363363+364364+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/os_mode365365+Date: April 2026366366+Contact: linux-input@vger.kernel.org367367+Description: This controls the behavior of built in chord combinations.368368+369369+ Values are windows or linux.370370+371371+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.372372+373373+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/os_mode_index374374+Date: April 2026375375+Contact: linux-input@vger.kernel.org376376+Description: This displays the available options for the os_mode attribute.377377+378378+ Values are windows or linux.379379+380380+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.381381+382382+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/product_version383383+Date: April 2026384384+Contact: linux-input@vger.kernel.org385385+Description: This displays the product version of the internal MCU.386386+387387+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.388388+389389+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/protocol_version390390+Date: April 2026391391+Contact: linux-input@vger.kernel.org392392+Description: This displays the protocol version of the internal MCU.393393+394394+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.395395+396396+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/reset_mcu397397+Date: April 2026398398+Contact: linux-input@vger.kernel.org399399+Description: Resets the internal MCU to factory defaults.400400+401401+ Writing 1 to this path initiates.402402+403403+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.404404+405405+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/auto_sleep_time406406+Date: April 2026407407+Contact: linux-input@vger.kernel.org408408+Description: This controls the sleep timer due to inactivity for the right removable controller.409409+410410+ Values are 0-255.411411+412412+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.413413+414414+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/auto_sleep_time_range415415+Date: April 2026416416+Contact: linux-input@vger.kernel.org417417+Description: This displays the available options for the right_handle/auto_sleep_time attribute.418418+419419+ Values are 0-255.420420+421421+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.422422+423423+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_gyro424424+Date: April 2026425425+Contact: linux-input@vger.kernel.org426426+Description: This initiates or halts calibration of the right removable controller's IMU.427427+428428+ Values are start, stop.429429+430430+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.431431+432432+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_gyro_index433433+Date: April 2026434434+Contact: linux-input@vger.kernel.org435435+Description: This displays the available options for the right_handle/calibrate_gyro attribute.436436+437437+ Values are start, stop.438438+439439+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.440440+441441+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_gyro_status442442+Date: April 2026443443+Contact: linux-input@vger.kernel.org444444+Description: This displays the result of the last attempted calibration of the right removable controller's IMU.445445+446446+ Values are unknown, success, failure.447447+448448+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.449449+450450+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_joystick451451+Date: April 2026452452+Contact: linux-input@vger.kernel.org453453+Description: This initiates or halts calibration of the right removable controller's joystick.454454+455455+ Values are start, stop.456456+457457+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.458458+459459+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_joystick_index460460+Date: April 2026461461+Contact: linux-input@vger.kernel.org462462+Description: This displays the available options for the right_handle/calibrate_jotstick attribute.463463+464464+ Values are start, stop.465465+466466+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.467467+468468+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_joystick_status469469+Date: April 2026470470+Contact: linux-input@vger.kernel.org471471+Description: This displays the result of the last attempted calibration of the right removable controller's joystick.472472+473473+ Values are unknown, success, failure.474474+475475+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.476476+477477+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_tirgger478478+Date: April 2026479479+Contact: linux-input@vger.kernel.org480480+Description: This initiates or halts calibration of the right removable controller's trigger.481481+482482+ Values are start, stop.483483+484484+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.485485+486486+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_gyro_trigger487487+Date: April 2026488488+Contact: linux-input@vger.kernel.org489489+Description: This displays the available options for the right_handle/calibrate_trigger attribute.490490+491491+ Values are start, stop.492492+493493+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.494494+495495+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/calibrate_trigger_status496496+Date: April 2026497497+Contact: linux-input@vger.kernel.org498498+Description: This displays the result of the last attempted calibration of the right removable controller's trigger.499499+500500+ Values are unknown, success, failure.501501+502502+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.503503+504504+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/firmware_version505505+Date: April 2026506506+Contact: linux-input@vger.kernel.org507507+Description: This displays the right removable controller's firmware version.508508+509509+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.510510+511511+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/hardware_generation512512+Date: April 2026513513+Contact: linux-input@vger.kernel.org514514+Description: This displays the hardware generation of the right removable controller.515515+516516+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.517517+518518+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/hardware_version519519+Date: April 2026520520+Contact: linux-input@vger.kernel.org521521+Description: This displays the hardware version of the right removable controller.522522+523523+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.524524+525525+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/imu_bypass_enabled526526+Date: April 2026527527+Contact: linux-input@vger.kernel.org528528+Description: This controls enabling or disabling the IMU bypass function of the right removable controller.529529+530530+ Values are true or false.531531+532532+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.533533+534534+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/imu_bypass_enabled_index535535+Date: April 2026536536+Contact: linux-input@vger.kernel.org537537+Description: This displays the available options for the right_handle/imu_bypass_enabled attribute.538538+539539+ Values are true or false.540540+541541+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/imu_enabled542542+Date: April 2026543543+Contact: linux-input@vger.kernel.org544544+Description: This controls enabling or disabling the IMU of the right removable controller.545545+546546+ Values are true or false.547547+548548+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.549549+550550+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/imu_enabled_index551551+Date: April 2026552552+Contact: linux-input@vger.kernel.org553553+Description: This displays the available options for the right_handle/imu_enabled attribute.554554+555555+ Values are true or false.556556+557557+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.558558+559559+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/product_version560560+Date: April 2026561561+Contact: linux-input@vger.kernel.org562562+Description: This displays the product version of the right removable controller.563563+564564+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.565565+566566+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/protocol_version567567+Date: April 2026568568+Contact: linux-input@vger.kernel.org569569+Description: This displays the protocol version of the right removable controller.570570+571571+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.572572+573573+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/reset574574+Date: April 2026575575+Contact: linux-input@vger.kernel.org576576+Description: Resets the right removable controller to factory defaults.577577+578578+ Writing 1 to this path initiates.579579+580580+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.581581+582582+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/rumble_mode583583+Date: April 2026584584+Contact: linux-input@vger.kernel.org585585+Description: This controls setting the response behavior for rumble events for the right removable controller.586586+587587+ Values are fps, racing, standarg, spg, rpg.588588+589589+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.590590+591591+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/rumble_mode_index592592+Date: April 2026593593+Contact: linux-input@vger.kernel.org594594+Description: This displays the available options for the right_handle/rumble_mode attribute.595595+596596+ Values are fps, racing, standarg, spg, rpg.597597+598598+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.599599+600600+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/rumble_notification601601+Date: April 2026602602+Contact: linux-input@vger.kernel.org603603+Description: This controls enabling haptic rumble events for the right removable controller.604604+605605+ Values are true, false.606606+607607+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.608608+609609+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/right_handle/rumble_notification_index610610+Date: April 2026611611+Contact: linux-input@vger.kernel.org612612+Description: This displays the available options for the right_handle/rumble_notification attribute.613613+614614+ Values are true, false.615615+616616+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.617617+618618+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/rumble_intensity619619+Date: April 2026620620+Contact: linux-input@vger.kernel.org621621+Description: This controls setting the rumble intensity for both removable controllers.622622+623623+ Values are off, low, medium, high.624624+625625+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.626626+627627+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/rumble_intensity_index628628+Date: April 2026629629+Contact: linux-input@vger.kernel.org630630+Description: This displays the available options for the rumble_intensity attribute.631631+632632+ Values are off, low, medium, high.633633+634634+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.635635+636636+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/enabled637637+Date: April 2026638638+Contact: linux-input@vger.kernel.org639639+Description: This controls enabling or disabling the touchpad.640640+641641+ Values are true, false.642642+643643+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.644644+645645+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/enabled_index646646+Date: April 2026647647+Contact: linux-input@vger.kernel.org648648+Description: This displays the available options for the touchpad/enabled attribute.649649+650650+ Values are true, false.651651+652652+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.653653+654654+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/vibration_enabled655655+Date: April 2026656656+Contact: linux-input@vger.kernel.org657657+Description: This controls enabling haptic rumble events for the touchpad.658658+659659+ Values are true, false.660660+661661+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.662662+663663+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/vibration_enabled_index664664+Date: April 2026665665+Contact: linux-input@vger.kernel.org666666+Description: This displays the available options for the touchpad/vibration_enabled attribute.667667+668668+ Values are true, false.669669+670670+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.671671+672672+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/vibration_intensity673673+Date: April 2026674674+Contact: linux-input@vger.kernel.org675675+Description: This controls setting the intensity of the touchpad haptics.676676+677677+ Values are off, low, medium, high.678678+679679+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.680680+681681+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/vibration_intensity_index682682+Date: April 2026683683+Contact: linux-input@vger.kernel.org684684+Description: This displays the available options for the touchpad/vibration_intensity attribute.685685+686686+ Values are off, low, medium, high.687687+688688+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.689689+690690+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/tx_dongle/firmware_version691691+Date: April 2026692692+Contact: linux-input@vger.kernel.org693693+Description: This displays the firmware version of the internal wireless transmission dongle.694694+695695+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.696696+697697+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/tx_dongle/hardware_generation698698+Date: April 2026699699+Contact: linux-input@vger.kernel.org700700+Description: This displays the hardware generation of the internal wireless transmission dongle.701701+702702+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.703703+704704+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/tx_dongle/hardware_version705705+Date: April 2026706706+Contact: linux-input@vger.kernel.org707707+Description: This displays the hardware version of the internal wireless transmission dongle.708708+709709+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.710710+711711+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/tx_dongle/product_version712712+Date: April 2026713713+Contact: linux-input@vger.kernel.org714714+Description: This displays the product version of the internal wireless transmission dongle.715715+716716+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.717717+718718+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/tx_dongle/protocol_version719719+Date: April 2026720720+Contact: linux-input@vger.kernel.org721721+Description: This displays the protocol version of the internal wireless transmission dongle.722722+723723+ Applies to Lenovo Legion Go and Go 2 line of handheld devices.724724+
···11+What: /sys/class/leds/go_s:rgb:joystick_rings/effect22+Date: April 202633+Contact: linux-input@vger.kernel.org44+Description: This controls the display effect of the RGB interface.55+66+ Values are monocolor, breathe, chroma, or rainbow.77+88+ Applies to Lenovo Legion Go S line of handheld devices.99+1010+What: /sys/class/leds/go_s:rgb:joystick_rings/effect_index1111+Date: April 20261212+Contact: linux-input@vger.kernel.org1313+Description: This displays the available options for the effect attribute.1414+1515+ Values are monocolor, breathe, chroma, or rainbow.1616+1717+ Applies to Lenovo Legion Go S line of handheld devices.1818+1919+What: /sys/class/leds/go_s:rgb:joystick_rings/enabled2020+Date: April 20262121+Contact: linux-input@vger.kernel.org2222+Description: This controls enabling or disabling the RGB interface.2323+2424+ Values are true or false.2525+2626+ Applies to Lenovo Legion Go S line of handheld devices.2727+2828+What: /sys/class/leds/go_s:rgb:joystick_rings/enabled_index2929+Date: April 20263030+Contact: linux-input@vger.kernel.org3131+Description: This displays the available options for the enabled attribute.3232+3333+ Values are true or false.3434+3535+ Applies to Lenovo Legion Go S line of handheld devices.3636+3737+What: /sys/class/leds/go_s:rgb:joystick_rings/mode3838+Date: April 20263939+Contact: linux-input@vger.kernel.org4040+Description: This controls the operating mode of the RGB interface.4141+4242+ Values are dynamic or custom. Custom allows setting the RGB effect and color.4343+ Dynamic is a Windows mode for syncing Lenovo RGB interfaces not currently4444+ supported under Linux.4545+4646+ Applies to Lenovo Legion Go S line of handheld devices.4747+4848+What: /sys/class/leds/go_s:rgb:joystick_rings/mode_index4949+Date: April 20265050+Contact: linux-input@vger.kernel.org5151+Description: This displays the available options for the mode attribute.5252+5353+ Values are dynamic or custom.5454+5555+ Applies to Lenovo Legion Go S line of handheld devices.5656+5757+What: /sys/class/leds/go_s:rgb:joystick_rings/profile5858+Date: April 20265959+Contact: linux-input@vger.kernel.org6060+Description: This controls selecting the configured RGB profile.6161+6262+ Values are 1-3.6363+6464+ Applies to Lenovo Legion Go S line of handheld devices.6565+6666+What: /sys/class/leds/go_s:rgb:joystick_rings/profile_range6767+Date: April 20266868+Contact: linux-input@vger.kernel.org6969+Description: This displays the available options for the profile attribute.7070+7171+ Values are 1-3.7272+7373+ Applies to Lenovo Legion Go S line of handheld devices.7474+7575+What: /sys/class/leds/go_s:rgb:joystick_rings/speed7676+Date: April 20267777+Contact: linux-input@vger.kernel.org7878+Description: This controls the change rate for the breathe, chroma, and rainbow effects.7979+8080+ Values are 0-100.8181+8282+ Applies to Lenovo Legion Go S line of handheld devices.8383+8484+What: /sys/class/leds/go_s:rgb:joystick_rings/speed_range8585+Date: April 20268686+Contact: linux-input@vger.kernel.org8787+Description: This displays the available options for the speed attribute.8888+8989+ Values are 0-100.9090+9191+ Applies to Lenovo Legion Go S line of handheld devices.9292+9393+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/gamepad/auto_sleep_time9494+Date: April 20269595+Contact: linux-input@vger.kernel.org9696+Description: This controls the sleep timer due to inactivity for the built-in controller.9797+9898+ Values are 0-255.9999+100100+ Applies to Lenovo Legion Go S line of handheld devices.101101+102102+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/gamepad/auto_sleep_time_range103103+Date: April 2026104104+Contact: linux-input@vger.kernel.org105105+Description: This displays the available options for the gamepad/auto_sleep_time attribute.106106+107107+ Values are 0-255.108108+109109+ Applies to Lenovo Legion Go S line of handheld devices.110110+111111+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/gamepad/dpad_mode112112+Date: April 2026113113+Contact: linux-input@vger.kernel.org114114+Description: This controls the operating mode of the built-in controllers D-pad.115115+116116+ Values are 4-way or 8-way.117117+118118+ Applies to Lenovo Legion Go S line of handheld devices.119119+120120+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/gamepad/dpad_mode_index121121+Date: April 2026122122+Contact: linux-input@vger.kernel.org123123+Description: This displays the available options for the gamepad/dpad_mode attribute.124124+125125+ Values are 4-way or 8-way.126126+127127+ Applies to Lenovo Legion Go S line of handheld devices.128128+129129+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/gamepad/mode130130+Date: April 2026131131+Contact: linux-input@vger.kernel.org132132+Description: This controls the operating mode of the built-in controller.133133+134134+ Values are xinput or dinput.135135+136136+ Applies to Lenovo Legion Go S line of handheld devices.137137+138138+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/gamepad/mode_index139139+Date: April 2026140140+Contact: linux-input@vger.kernel.org141141+Description: This displays the available options for the gamepad/mode attribute.142142+143143+ Values are xinput or dinput.144144+145145+ Applies to Lenovo Legion Go S line of handheld devices.146146+147147+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/gamepad/poll_rate148148+Date: April 2026149149+Contact: linux-input@vger.kernel.org150150+Description: This controls the poll rate in Hz of the built-in controller.151151+152152+ Values are 125, 250, 500, or 1000.153153+154154+ Applies to Lenovo Legion Go S line of handheld devices.155155+156156+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/gamepad/poll_rate_index157157+Date: April 2026158158+Contact: linux-input@vger.kernel.org159159+Description: This displays the available options for the gamepad/poll_rate attribute.160160+161161+ Values are 125, 250, 500, or 1000.162162+163163+ Applies to Lenovo Legion Go S line of handheld devices.164164+165165+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/imu/bypass_enabled166166+Date: April 2026167167+Contact: linux-input@vger.kernel.org168168+Description: This controls enabling or disabling the IMU bypass function. When enabled the IMU data is directly reported to the OS through169169+an HIDRAW interface.170170+171171+ Values are true or false.172172+173173+ Applies to Lenovo Legion Go S line of handheld devices.174174+175175+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/imu/bypass_enabled_index176176+Date: April 2026177177+Contact: linux-input@vger.kernel.org178178+Description: This displays the available options for the imu/bypass_enabled attribute.179179+180180+ Values are true or false.181181+182182+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/imu/manufacturer183183+Date: April 2026184184+Contact: linux-input@vger.kernel.org185185+Description: This displays the manufacturer of the intertial measurment unit.186186+187187+ Values are Bosch or ST.188188+189189+ Applies to Lenovo Legion Go S line of handheld devices.190190+191191+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/imu/sensor_enabled192192+Date: April 2026193193+Contact: linux-input@vger.kernel.org194194+Description: This controls enabling or disabling the IMU.195195+196196+ Values are true, false, or wake-2s.197197+198198+ Applies to Lenovo Legion Go S line of handheld devices.199199+200200+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/imu/sensor_enabled_index201201+Date: April 2026202202+Contact: linux-input@vger.kernel.org203203+Description: This displays the available options for the imu/sensor_enabled attribute.204204+205205+ Values are true, false, or wake-2s.206206+207207+ Applies to Lenovo Legion Go S line of handheld devices.208208+209209+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/mcu_id210210+Date: April 2026211211+Contact: linux-input@vger.kernel.org212212+Description: This displays the MCU Identification Number213213+214214+ Applies to Lenovo Legion Go S line of handheld devices.215215+216216+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/mouse/step217217+Date: April 2026218218+Contact: linux-input@vger.kernel.org219219+Description: This controls which value is used for the mouse sensitivity.220220+221221+ Values are 1-127.222222+223223+ Applies to Lenovo Legion Go S line of handheld devices.224224+225225+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/mouse/step_range226226+Date: April 2026227227+Contact: linux-input@vger.kernel.org228228+Description: This displays the available options for the mouse/step attribute.229229+230230+ Values are 1-127.231231+232232+ Applies to Lenovo Legion Go S line of handheld devices.233233+234234+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/os_mode235235+Date: April 2026236236+Contact: linux-input@vger.kernel.org237237+Description: This controls which value is used for the touchpads operating mode.238238+239239+ Values are windows or linux.240240+241241+ Applies to Lenovo Legion Go S line of handheld devices.242242+243243+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/os_mode_index244244+Date: April 2026245245+Contact: linux-input@vger.kernel.org246246+Description: This displays the available options for the os_mode attribute.247247+248248+ Values are windows or linux.249249+250250+ Applies to Lenovo Legion Go S line of handheld devices.251251+252252+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/enabled253253+Date: April 2026254254+Contact: linux-input@vger.kernel.org255255+Description: This controls enabling or disabling the built-in touchpad.256256+257257+ Values are true or false.258258+259259+ Applies to Lenovo Legion Go S line of handheld devices.260260+261261+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/enabled_index262262+Date: April 2026263263+Contact: linux-input@vger.kernel.org264264+Description: This displays the available options for the touchpad/enabled attribute.265265+266266+ Values are true or false.267267+268268+ Applies to Lenovo Legion Go S line of handheld devices.269269+270270+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/linux_mode271271+Date: April 2026272272+Contact: linux-input@vger.kernel.org273273+Description: This controls behavior of the touchpad events when os_mode is set to linux.274274+275275+ Values are absolute or relative.276276+277277+ Applies to Lenovo Legion Go S line of handheld devices.278278+279279+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/linux_mode_index280280+Date: April 2026281281+Contact: linux-input@vger.kernel.org282282+Description: This displays the available options for the touchpad/linux_mode attribute.283283+284284+ Values are absolute or relative.285285+286286+ Applies to Lenovo Legion Go S line of handheld devices.287287+288288+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/windows_mode289289+Date: April 2026290290+Contact: linux-input@vger.kernel.org291291+Description: This controls behavior of the touchpad events when os_mode is set to windows.292292+293293+ Values are absolute or relative.294294+295295+ Applies to Lenovo Legion Go S line of handheld devices.296296+297297+What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/touchpad/windows_mode_index298298+Date: April 2026299299+Contact: linux-input@vger.kernel.org300300+Description: This displays the available options for the touchpad/windows_mode attribute.301301+302302+ Values are absolute or relative.303303+304304+ Applies to Lenovo Legion Go S line of handheld devices.
+11
MAINTAINERS
···1446914469S: Maintained1447014470F: drivers/platform/x86/lenovo/wmi-hotkey-utilities.c14471144711447214472+LENOVO HID drivers1447314473+M: Derek J. Clark <derekjohn.clark@gmail.com>1447414474+M: Mark Pearson <mpearson-lenovo@squebb.ca>1447514475+L: linux-input@vger.kernel.org1447614476+S: Maintained1447714477+F: Documentation/ABI/testing/sysfs-driver-hid-lenovo-go1447814478+F: Documentation/ABI/testing/sysfs-driver-hid-lenovo-go-s1447914479+F: drivers/hid/hid-lenovo-go-s.c1448014480+F: drivers/hid/hid-lenovo-go.c1448114481+F: drivers/hid/hid-lenovo.c1448214482+1447214483LETSKETCH HID TABLET DRIVER1447314484M: Hans de Goede <hansg@kernel.org>1447414485L: linux-input@vger.kernel.org
+23-2
drivers/hid/Kconfig
···601601602602config HID_LENOVO603603 tristate "Lenovo / Thinkpad devices"604604- select NEW_LEDS605605- select LEDS_CLASS604604+ depends on LEDS_CLASS606605 help607606 Support for IBM/Lenovo devices that are not fully compliant with HID standard.608607···612613 configuration)613614 - ThinkPad Compact Bluetooth Keyboard with TrackPoint (supports Fn keys)614615 - ThinkPad Compact USB Keyboard with TrackPoint (supports Fn keys)616616+617617+config HID_LENOVO_GO618618+ tristate "HID Driver for Lenovo Legion Go Series Controllers"619619+ depends on USB_HID620620+ depends on LEDS_CLASS_MULTICOLOR621621+ help622622+ Support for Lenovo Legion Go devices with detachable controllers.623623+624624+ Say Y here to include configuration interface support for the Lenovo Legion Go625625+ and Legion Go 2 Handheld Console Controllers. Say M here to compile this626626+ driver as a module. The module will be called hid-lenovo-go.627627+628628+config HID_LENOVO_GO_S629629+ tristate "HID Driver for Lenovo Legion Go S Controller"630630+ depends on USB_HID631631+ depends on LEDS_CLASS_MULTICOLOR632632+ help633633+ Support for Lenovo Legion Go S Handheld Console Controller.634634+635635+ Say Y here to include configuration interface support for the Lenovo Legion Go636636+ S. Say M here to compile this driver as a module. The module will be called637637+ hid-lenovo-go-s.615638616639config HID_LETSKETCH617640 tristate "Letsketch WP9620N tablets"
···11+// SPDX-License-Identifier: GPL-2.0-or-later22+/*33+ * HID driver for Lenovo Legion Go S devices.44+ *55+ * Copyright (c) 2026 Derek J. Clark <derekjohn.clark@gmail.com>66+ * Copyright (c) 2026 Valve Corporation77+ */88+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt99+1010+#include <linux/array_size.h>1111+#include <linux/cleanup.h>1212+#include <linux/completion.h>1313+#include <linux/delay.h>1414+#include <linux/dev_printk.h>1515+#include <linux/device.h>1616+#include <linux/hid.h>1717+#include <linux/jiffies.h>1818+#include <linux/kstrtox.h>1919+#include <linux/led-class-multicolor.h>2020+#include <linux/mutex.h>2121+#include <linux/printk.h>2222+#include <linux/string.h>2323+#include <linux/sysfs.h>2424+#include <linux/types.h>2525+#include <linux/unaligned.h>2626+#include <linux/usb.h>2727+#include <linux/workqueue.h>2828+#include <linux/workqueue_types.h>2929+3030+#include "hid-ids.h"3131+3232+#define GO_S_CFG_INTF_IN 0x843333+#define GO_S_PACKET_SIZE 643434+3535+static struct hid_gos_cfg {3636+ struct delayed_work gos_cfg_setup;3737+ struct completion send_cmd_complete;3838+ struct led_classdev *led_cdev;3939+ struct hid_device *hdev;4040+ struct mutex cfg_mutex; /*ensure single synchronous output report*/4141+ u8 gp_auto_sleep_time;4242+ u8 gp_dpad_mode;4343+ u8 gp_mode;4444+ u8 gp_poll_rate;4545+ u8 imu_bypass_en;4646+ u8 imu_manufacturer;4747+ u8 imu_sensor_en;4848+ u8 mcu_id[12];4949+ u8 mouse_step;5050+ u8 os_mode;5151+ u8 rgb_effect;5252+ u8 rgb_en;5353+ u8 rgb_mode;5454+ u8 rgb_profile;5555+ u8 rgb_speed;5656+ u8 tp_en;5757+ u8 tp_linux_mode;5858+ u8 tp_windows_mode;5959+ u8 tp_version;6060+ u8 tp_manufacturer;6161+} drvdata;6262+6363+struct gos_cfg_attr {6464+ u8 index;6565+};6666+6767+struct command_report {6868+ u8 cmd;6969+ u8 sub_cmd;7070+ u8 data[63];7171+} __packed;7272+7373+struct version_report {7474+ u8 cmd;7575+ u32 version;7676+ u8 reserved[59];7777+} __packed;7878+7979+enum mcu_command_index {8080+ GET_VERSION = 0x01,8181+ GET_MCU_ID,8282+ GET_GAMEPAD_CFG,8383+ SET_GAMEPAD_CFG,8484+ GET_TP_PARAM,8585+ SET_TP_PARAM,8686+ GET_RGB_CFG = 0x0f,8787+ SET_RGB_CFG,8888+ GET_PL_TEST = 0xdf,8989+};9090+9191+enum feature_enabled_index {9292+ FEATURE_DISABLED,9393+ FEATURE_ENABLED,9494+};9595+9696+static const char *const feature_enabled_text[] = {9797+ [FEATURE_DISABLED] = "false",9898+ [FEATURE_ENABLED] = "true",9999+};100100+101101+enum feature_status_index {102102+ FEATURE_NONE = 0x00,103103+ FEATURE_GAMEPAD_MODE = 0x01,104104+ FEATURE_AUTO_SLEEP_TIME = 0x04,105105+ FEATURE_IMU_BYPASS,106106+ FEATURE_RGB_ENABLE,107107+ FEATURE_IMU_ENABLE,108108+ FEATURE_TOUCHPAD_ENABLE,109109+ FEATURE_OS_MODE = 0x0A,110110+ FEATURE_POLL_RATE = 0x10,111111+ FEATURE_DPAD_MODE,112112+ FEATURE_MOUSE_WHEEL_STEP,113113+};114114+115115+enum gamepad_mode_index {116116+ XINPUT,117117+ DINPUT,118118+};119119+120120+static const char *const gamepad_mode_text[] = {121121+ [XINPUT] = "xinput",122122+ [DINPUT] = "dinput",123123+};124124+125125+enum os_type_index {126126+ WINDOWS,127127+ LINUX,128128+};129129+130130+static const char *const os_type_text[] = {131131+ [WINDOWS] = "windows",132132+ [LINUX] = "linux",133133+};134134+135135+enum poll_rate_index {136136+ HZ125,137137+ HZ250,138138+ HZ500,139139+ HZ1000,140140+};141141+142142+static const char *const poll_rate_text[] = {143143+ [HZ125] = "125",144144+ [HZ250] = "250",145145+ [HZ500] = "500",146146+ [HZ1000] = "1000",147147+};148148+149149+enum dpad_mode_index {150150+ DIR8,151151+ DIR4,152152+};153153+154154+static const char *const dpad_mode_text[] = {155155+ [DIR8] = "8-way",156156+ [DIR4] = "4-way",157157+};158158+159159+enum touchpad_mode_index {160160+ TP_REL,161161+ TP_ABS,162162+};163163+164164+static const char *const touchpad_mode_text[] = {165165+ [TP_REL] = "relative",166166+ [TP_ABS] = "absolute",167167+};168168+169169+enum touchpad_config_index {170170+ CFG_WINDOWS_MODE = 0x03,171171+ CFG_LINUX_MODE,172172+173173+};174174+175175+enum rgb_mode_index {176176+ RGB_MODE_DYNAMIC,177177+ RGB_MODE_CUSTOM,178178+};179179+180180+static const char *const rgb_mode_text[] = {181181+ [RGB_MODE_DYNAMIC] = "dynamic",182182+ [RGB_MODE_CUSTOM] = "custom",183183+};184184+185185+enum rgb_effect_index {186186+ RGB_EFFECT_MONO,187187+ RGB_EFFECT_BREATHE,188188+ RGB_EFFECT_CHROMA,189189+ RGB_EFFECT_RAINBOW,190190+};191191+192192+static const char *const rgb_effect_text[] = {193193+ [RGB_EFFECT_MONO] = "monocolor",194194+ [RGB_EFFECT_BREATHE] = "breathe",195195+ [RGB_EFFECT_CHROMA] = "chroma",196196+ [RGB_EFFECT_RAINBOW] = "rainbow",197197+};198198+199199+enum rgb_config_index {200200+ LIGHT_MODE_SEL = 0x01,201201+ LIGHT_PROFILE_SEL,202202+ USR_LIGHT_PROFILE_1,203203+ USR_LIGHT_PROFILE_2,204204+ USR_LIGHT_PROFILE_3,205205+};206206+207207+enum test_command_index {208208+ TEST_TP_MFR = 0x02,209209+ TEST_IMU_MFR,210210+ TEST_TP_VER,211211+};212212+213213+enum tp_mfr_index {214214+ TP_NONE,215215+ TP_BETTERLIFE,216216+ TP_SIPO,217217+};218218+219219+static const char *const touchpad_manufacturer_text[] = {220220+ [TP_NONE] = "none",221221+ [TP_BETTERLIFE] = "BetterLife",222222+ [TP_SIPO] = "SIPO",223223+};224224+225225+enum imu_mfr_index {226226+ IMU_NONE,227227+ IMU_BOSCH,228228+ IMU_ST,229229+};230230+231231+static const char *const imu_manufacturer_text[] = {232232+ [IMU_NONE] = "none",233233+ [IMU_BOSCH] = "Bosch",234234+ [IMU_ST] = "ST",235235+};236236+237237+static int hid_gos_version_event(u8 *data)238238+{239239+ struct version_report *ver_rep = (struct version_report *)data;240240+241241+ drvdata.hdev->firmware_version = get_unaligned_le32(&ver_rep->version);242242+ return 0;243243+}244244+245245+static int hid_gos_mcu_id_event(struct command_report *cmd_rep)246246+{247247+ drvdata.mcu_id[0] = cmd_rep->sub_cmd;248248+ memcpy(&drvdata.mcu_id[1], cmd_rep->data, 11);249249+250250+ return 0;251251+}252252+253253+static int hid_gos_gamepad_cfg_event(struct command_report *cmd_rep)254254+{255255+ int ret = 0;256256+257257+ switch (cmd_rep->sub_cmd) {258258+ case FEATURE_GAMEPAD_MODE:259259+ drvdata.gp_mode = cmd_rep->data[0];260260+ break;261261+ case FEATURE_AUTO_SLEEP_TIME:262262+ drvdata.gp_auto_sleep_time = cmd_rep->data[0];263263+ break;264264+ case FEATURE_IMU_BYPASS:265265+ drvdata.imu_bypass_en = cmd_rep->data[0];266266+ break;267267+ case FEATURE_RGB_ENABLE:268268+ drvdata.rgb_en = cmd_rep->data[0];269269+ break;270270+ case FEATURE_IMU_ENABLE:271271+ drvdata.imu_sensor_en = cmd_rep->data[0];272272+ break;273273+ case FEATURE_TOUCHPAD_ENABLE:274274+ drvdata.tp_en = cmd_rep->data[0];275275+ break;276276+ case FEATURE_OS_MODE:277277+ drvdata.os_mode = cmd_rep->data[0];278278+ break;279279+ case FEATURE_POLL_RATE:280280+ drvdata.gp_poll_rate = cmd_rep->data[0];281281+ break;282282+ case FEATURE_DPAD_MODE:283283+ drvdata.gp_dpad_mode = cmd_rep->data[0];284284+ break;285285+ case FEATURE_MOUSE_WHEEL_STEP:286286+ drvdata.mouse_step = cmd_rep->data[0];287287+ break;288288+ default:289289+ ret = -EINVAL;290290+ break;291291+ }292292+293293+ return ret;294294+}295295+296296+static int hid_gos_touchpad_event(struct command_report *cmd_rep)297297+{298298+ int ret = 0;299299+300300+ switch (cmd_rep->sub_cmd) {301301+ case CFG_LINUX_MODE:302302+ drvdata.tp_linux_mode = cmd_rep->data[0];303303+ break;304304+ case CFG_WINDOWS_MODE:305305+ drvdata.tp_windows_mode = cmd_rep->data[0];306306+ break;307307+ default:308308+ ret = -EINVAL;309309+ break;310310+ }311311+312312+ return ret;313313+}314314+315315+static int hid_gos_pl_test_event(struct command_report *cmd_rep)316316+{317317+ int ret = 0;318318+319319+ switch (cmd_rep->sub_cmd) {320320+ case TEST_TP_MFR:321321+ drvdata.tp_manufacturer = cmd_rep->data[0];322322+ ret = 0;323323+ break;324324+ case TEST_IMU_MFR:325325+ drvdata.imu_manufacturer = cmd_rep->data[0];326326+ ret = 0;327327+ break;328328+ case TEST_TP_VER:329329+ drvdata.tp_version = cmd_rep->data[0];330330+ ret = 0;331331+ break;332332+ default:333333+ ret = -EINVAL;334334+ break;335335+ }336336+ return ret;337337+}338338+339339+static int hid_gos_light_event(struct command_report *cmd_rep)340340+{341341+ struct led_classdev_mc *mc_cdev;342342+ int ret = 0;343343+344344+ switch (cmd_rep->sub_cmd) {345345+ case LIGHT_MODE_SEL:346346+ drvdata.rgb_mode = cmd_rep->data[0];347347+ ret = 0;348348+ break;349349+ case LIGHT_PROFILE_SEL:350350+ drvdata.rgb_profile = cmd_rep->data[0];351351+ ret = 0;352352+ break;353353+ case USR_LIGHT_PROFILE_1:354354+ case USR_LIGHT_PROFILE_2:355355+ case USR_LIGHT_PROFILE_3:356356+ mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);357357+ drvdata.rgb_effect = cmd_rep->data[0];358358+ mc_cdev->subled_info[0].intensity = cmd_rep->data[1];359359+ mc_cdev->subled_info[1].intensity = cmd_rep->data[2];360360+ mc_cdev->subled_info[2].intensity = cmd_rep->data[3];361361+ drvdata.led_cdev->brightness = cmd_rep->data[4];362362+ drvdata.rgb_speed = cmd_rep->data[5];363363+ ret = 0;364364+ break;365365+ default:366366+ ret = -EINVAL;367367+ break;368368+ }369369+ return ret;370370+}371371+372372+static int hid_gos_set_event_return(struct command_report *cmd_rep)373373+{374374+ if (cmd_rep->data[0] != 0)375375+ return -EIO;376376+377377+ return 0;378378+}379379+380380+static int get_endpoint_address(struct hid_device *hdev)381381+{382382+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);383383+ struct usb_host_endpoint *ep;384384+385385+ if (intf) {386386+ ep = intf->cur_altsetting->endpoint;387387+ if (ep)388388+ return ep->desc.bEndpointAddress;389389+ }390390+391391+ return -ENODEV;392392+}393393+394394+static int hid_gos_raw_event(struct hid_device *hdev, struct hid_report *report,395395+ u8 *data, int size)396396+{397397+ struct command_report *cmd_rep;398398+ int ep, ret;399399+400400+ ep = get_endpoint_address(hdev);401401+ if (ep != GO_S_CFG_INTF_IN)402402+ return 0;403403+404404+ if (size != GO_S_PACKET_SIZE)405405+ return -EINVAL;406406+407407+ cmd_rep = (struct command_report *)data;408408+409409+ switch (cmd_rep->cmd) {410410+ case GET_VERSION:411411+ ret = hid_gos_version_event(data);412412+ break;413413+ case GET_MCU_ID:414414+ ret = hid_gos_mcu_id_event(cmd_rep);415415+ break;416416+ case GET_GAMEPAD_CFG:417417+ ret = hid_gos_gamepad_cfg_event(cmd_rep);418418+ break;419419+ case GET_TP_PARAM:420420+ ret = hid_gos_touchpad_event(cmd_rep);421421+ break;422422+ case GET_PL_TEST:423423+ ret = hid_gos_pl_test_event(cmd_rep);424424+ break;425425+ case GET_RGB_CFG:426426+ ret = hid_gos_light_event(cmd_rep);427427+ break;428428+ case SET_GAMEPAD_CFG:429429+ case SET_RGB_CFG:430430+ case SET_TP_PARAM:431431+ ret = hid_gos_set_event_return(cmd_rep);432432+ break;433433+ default:434434+ ret = -EINVAL;435435+ break;436436+ }437437+ dev_dbg(&hdev->dev, "Rx data as raw input report: [%*ph]\n",438438+ GO_S_PACKET_SIZE, data);439439+440440+ complete(&drvdata.send_cmd_complete);441441+ return ret;442442+}443443+444444+static int mcu_property_out(struct hid_device *hdev, u8 command, u8 index,445445+ u8 *data, size_t len)446446+{447447+ unsigned char *dmabuf __free(kfree) = NULL;448448+ u8 header[] = { command, index };449449+ size_t header_size = ARRAY_SIZE(header);450450+ int timeout, ret;451451+452452+ if (header_size + len > GO_S_PACKET_SIZE)453453+ return -EINVAL;454454+455455+ guard(mutex)(&drvdata.cfg_mutex);456456+ /* We can't use a devm_alloc reusable buffer without side effects during suspend */457457+ dmabuf = kzalloc(GO_S_PACKET_SIZE, GFP_KERNEL);458458+ if (!dmabuf)459459+ return -ENOMEM;460460+461461+ memcpy(dmabuf, header, header_size);462462+ memcpy(dmabuf + header_size, data, len);463463+464464+ dev_dbg(&hdev->dev, "Send data as raw output report: [%*ph]\n",465465+ GO_S_PACKET_SIZE, dmabuf);466466+467467+ ret = hid_hw_output_report(hdev, dmabuf, GO_S_PACKET_SIZE);468468+ if (ret < 0)469469+ return ret;470470+471471+ ret = ret == GO_S_PACKET_SIZE ? 0 : -EINVAL;472472+ if (ret)473473+ return ret;474474+475475+ /* PL_TEST commands can take longer because they go out to another device */476476+ timeout = (command == GET_PL_TEST) ? 200 : 5;477477+ ret = wait_for_completion_interruptible_timeout(&drvdata.send_cmd_complete,478478+ msecs_to_jiffies(timeout));479479+480480+ if (ret == 0) /* timeout occurred */481481+ ret = -EBUSY;482482+483483+ reinit_completion(&drvdata.send_cmd_complete);484484+ return 0;485485+}486486+487487+static ssize_t gamepad_property_store(struct device *dev,488488+ struct device_attribute *attr,489489+ const char *buf, size_t count,490490+ enum feature_status_index index)491491+{492492+ size_t size = 1;493493+ u8 val = 0;494494+ int ret;495495+496496+ switch (index) {497497+ case FEATURE_GAMEPAD_MODE:498498+ ret = sysfs_match_string(gamepad_mode_text, buf);499499+ if (ret < 0)500500+ return ret;501501+ val = ret;502502+ break;503503+ case FEATURE_AUTO_SLEEP_TIME:504504+ ret = kstrtou8(buf, 10, &val);505505+ if (ret)506506+ return ret;507507+ break;508508+ case FEATURE_IMU_ENABLE:509509+ ret = sysfs_match_string(feature_enabled_text, buf);510510+ if (ret < 0)511511+ return ret;512512+ val = ret;513513+ break;514514+ case FEATURE_IMU_BYPASS:515515+ ret = sysfs_match_string(feature_enabled_text, buf);516516+ if (ret < 0)517517+ return ret;518518+ val = ret;519519+ break;520520+ case FEATURE_RGB_ENABLE:521521+ ret = sysfs_match_string(feature_enabled_text, buf);522522+ if (ret < 0)523523+ return ret;524524+ val = ret;525525+ break;526526+ case FEATURE_TOUCHPAD_ENABLE:527527+ ret = sysfs_match_string(feature_enabled_text, buf);528528+ if (ret < 0)529529+ return ret;530530+ val = ret;531531+ break;532532+ case FEATURE_OS_MODE:533533+ ret = sysfs_match_string(os_type_text, buf);534534+ if (ret < 0)535535+ return ret;536536+ val = ret;537537+ break;538538+ case FEATURE_POLL_RATE:539539+ ret = sysfs_match_string(poll_rate_text, buf);540540+ if (ret < 0)541541+ return ret;542542+ val = ret;543543+ break;544544+ case FEATURE_DPAD_MODE:545545+ ret = sysfs_match_string(dpad_mode_text, buf);546546+ if (ret < 0)547547+ return ret;548548+ val = ret;549549+ break;550550+ case FEATURE_MOUSE_WHEEL_STEP:551551+ ret = kstrtou8(buf, 10, &val);552552+ if (ret)553553+ return ret;554554+ if (val < 1 || val > 127)555555+ return -EINVAL;556556+ break;557557+ default:558558+ return -EINVAL;559559+ }560560+561561+ if (!val)562562+ size = 0;563563+564564+ ret = mcu_property_out(drvdata.hdev, SET_GAMEPAD_CFG, index, &val,565565+ size);566566+ if (ret < 0)567567+ return ret;568568+569569+ return count;570570+}571571+572572+static ssize_t gamepad_property_show(struct device *dev,573573+ struct device_attribute *attr, char *buf,574574+ enum feature_status_index index)575575+{576576+ ssize_t count = 0;577577+ u8 i;578578+579579+ count = mcu_property_out(drvdata.hdev, GET_GAMEPAD_CFG, index, NULL, 0);580580+ if (count < 0)581581+ return count;582582+583583+ switch (index) {584584+ case FEATURE_GAMEPAD_MODE:585585+ i = drvdata.gp_mode;586586+ if (i >= ARRAY_SIZE(gamepad_mode_text))587587+ return -EINVAL;588588+ count = sysfs_emit(buf, "%s\n", gamepad_mode_text[i]);589589+ break;590590+ case FEATURE_AUTO_SLEEP_TIME:591591+ count = sysfs_emit(buf, "%u\n", drvdata.gp_auto_sleep_time);592592+ break;593593+ case FEATURE_IMU_ENABLE:594594+ i = drvdata.imu_sensor_en;595595+ if (i >= ARRAY_SIZE(feature_enabled_text))596596+ return -EINVAL;597597+ count = sysfs_emit(buf, "%s\n", feature_enabled_text[i]);598598+ break;599599+ case FEATURE_IMU_BYPASS:600600+ i = drvdata.imu_bypass_en;601601+ if (i >= ARRAY_SIZE(feature_enabled_text))602602+ return -EINVAL;603603+ count = sysfs_emit(buf, "%s\n", feature_enabled_text[i]);604604+ break;605605+ case FEATURE_RGB_ENABLE:606606+ i = drvdata.rgb_en;607607+ if (i >= ARRAY_SIZE(feature_enabled_text))608608+ return -EINVAL;609609+ count = sysfs_emit(buf, "%s\n", feature_enabled_text[i]);610610+ break;611611+ case FEATURE_TOUCHPAD_ENABLE:612612+ i = drvdata.tp_en;613613+ if (i >= ARRAY_SIZE(feature_enabled_text))614614+ return -EINVAL;615615+ count = sysfs_emit(buf, "%s\n", feature_enabled_text[i]);616616+ break;617617+ case FEATURE_OS_MODE:618618+ i = drvdata.os_mode;619619+ if (i >= ARRAY_SIZE(os_type_text))620620+ return -EINVAL;621621+ count = sysfs_emit(buf, "%s\n", os_type_text[i]);622622+ break;623623+ case FEATURE_POLL_RATE:624624+ i = drvdata.gp_poll_rate;625625+ if (i >= ARRAY_SIZE(poll_rate_text))626626+ return -EINVAL;627627+ count = sysfs_emit(buf, "%s\n", poll_rate_text[i]);628628+ break;629629+ case FEATURE_DPAD_MODE:630630+ i = drvdata.gp_dpad_mode;631631+ if (i >= ARRAY_SIZE(dpad_mode_text))632632+ return -EINVAL;633633+ count = sysfs_emit(buf, "%s\n", dpad_mode_text[i]);634634+ break;635635+ case FEATURE_MOUSE_WHEEL_STEP:636636+ i = drvdata.mouse_step;637637+ if (i < 1 || i > 127)638638+ return -EINVAL;639639+ count = sysfs_emit(buf, "%u\n", i);640640+ break;641641+ default:642642+ return -EINVAL;643643+ }644644+645645+ return count;646646+}647647+648648+static ssize_t gamepad_property_options(struct device *dev,649649+ struct device_attribute *attr,650650+ char *buf,651651+ enum feature_status_index index)652652+{653653+ size_t count = 0;654654+ unsigned int i;655655+656656+ switch (index) {657657+ case FEATURE_GAMEPAD_MODE:658658+ for (i = 0; i < ARRAY_SIZE(gamepad_mode_text); i++) {659659+ count += sysfs_emit_at(buf, count, "%s ",660660+ gamepad_mode_text[i]);661661+ }662662+ break;663663+ case FEATURE_AUTO_SLEEP_TIME:664664+ return sysfs_emit(buf, "0-255\n");665665+ case FEATURE_IMU_ENABLE:666666+ for (i = 0; i < ARRAY_SIZE(feature_enabled_text); i++) {667667+ count += sysfs_emit_at(buf, count, "%s ",668668+ feature_enabled_text[i]);669669+ }670670+ break;671671+ case FEATURE_IMU_BYPASS:672672+ case FEATURE_RGB_ENABLE:673673+ case FEATURE_TOUCHPAD_ENABLE:674674+ for (i = 0; i < ARRAY_SIZE(feature_enabled_text); i++) {675675+ count += sysfs_emit_at(buf, count, "%s ",676676+ feature_enabled_text[i]);677677+ }678678+ break;679679+ case FEATURE_OS_MODE:680680+ for (i = 0; i < ARRAY_SIZE(os_type_text); i++) {681681+ count += sysfs_emit_at(buf, count, "%s ",682682+ os_type_text[i]);683683+ }684684+ break;685685+ case FEATURE_POLL_RATE:686686+ for (i = 0; i < ARRAY_SIZE(poll_rate_text); i++) {687687+ count += sysfs_emit_at(buf, count, "%s ",688688+ poll_rate_text[i]);689689+ }690690+ break;691691+ case FEATURE_DPAD_MODE:692692+ for (i = 0; i < ARRAY_SIZE(dpad_mode_text); i++) {693693+ count += sysfs_emit_at(buf, count, "%s ",694694+ dpad_mode_text[i]);695695+ }696696+ break;697697+ case FEATURE_MOUSE_WHEEL_STEP:698698+ return sysfs_emit(buf, "1-127\n");699699+ default:700700+ return count;701701+ }702702+703703+ if (count)704704+ buf[count - 1] = '\n';705705+706706+ return count;707707+}708708+709709+static ssize_t touchpad_property_store(struct device *dev,710710+ struct device_attribute *attr,711711+ const char *buf, size_t count,712712+ enum touchpad_config_index index)713713+{714714+ size_t size = 1;715715+ u8 val = 0;716716+ int ret;717717+718718+ switch (index) {719719+ case CFG_WINDOWS_MODE:720720+ ret = sysfs_match_string(touchpad_mode_text, buf);721721+ if (ret < 0)722722+ return ret;723723+ val = ret;724724+ break;725725+ case CFG_LINUX_MODE:726726+ ret = sysfs_match_string(touchpad_mode_text, buf);727727+ if (ret < 0)728728+ return ret;729729+ val = ret;730730+ break;731731+ default:732732+ return -EINVAL;733733+ }734734+ if (!val)735735+ size = 0;736736+737737+ ret = mcu_property_out(drvdata.hdev, SET_TP_PARAM, index, &val, size);738738+ if (ret < 0)739739+ return ret;740740+741741+ return count;742742+}743743+744744+static ssize_t touchpad_property_show(struct device *dev,745745+ struct device_attribute *attr, char *buf,746746+ enum touchpad_config_index index)747747+{748748+ int ret = 0;749749+ u8 i;750750+751751+ ret = mcu_property_out(drvdata.hdev, GET_TP_PARAM, index, NULL, 0);752752+ if (ret < 0)753753+ return ret;754754+755755+ switch (index) {756756+ case CFG_WINDOWS_MODE:757757+ i = drvdata.tp_windows_mode;758758+ break;759759+ case CFG_LINUX_MODE:760760+ i = drvdata.tp_linux_mode;761761+ break;762762+ default:763763+ return -EINVAL;764764+ }765765+766766+ if (i >= ARRAY_SIZE(touchpad_mode_text))767767+ return -EINVAL;768768+769769+ return sysfs_emit(buf, "%s\n", touchpad_mode_text[i]);770770+}771771+772772+static ssize_t touchpad_property_options(struct device *dev,773773+ struct device_attribute *attr,774774+ char *buf,775775+ enum touchpad_config_index index)776776+{777777+ size_t count = 0;778778+ unsigned int i;779779+780780+ switch (index) {781781+ case CFG_WINDOWS_MODE:782782+ case CFG_LINUX_MODE:783783+ for (i = 0; i < ARRAY_SIZE(touchpad_mode_text); i++) {784784+ count += sysfs_emit_at(buf, count, "%s ",785785+ touchpad_mode_text[i]);786786+ }787787+ break;788788+ default:789789+ return count;790790+ }791791+792792+ if (count)793793+ buf[count - 1] = '\n';794794+795795+ return count;796796+}797797+798798+static ssize_t test_property_show(struct device *dev,799799+ struct device_attribute *attr, char *buf,800800+ enum test_command_index index)801801+{802802+ size_t count = 0;803803+ u8 i;804804+805805+ switch (index) {806806+ case TEST_TP_MFR:807807+ i = drvdata.tp_manufacturer;808808+ if (i >= ARRAY_SIZE(touchpad_manufacturer_text))809809+ return -EINVAL;810810+ count = sysfs_emit(buf, "%s\n", touchpad_manufacturer_text[i]);811811+ break;812812+ case TEST_IMU_MFR:813813+ i = drvdata.imu_manufacturer;814814+ if (i >= ARRAY_SIZE(imu_manufacturer_text))815815+ return -EINVAL;816816+ count = sysfs_emit(buf, "%s\n", imu_manufacturer_text[i]);817817+ break;818818+ case TEST_TP_VER:819819+ count = sysfs_emit(buf, "%u\n", drvdata.tp_version);820820+ break;821821+ default:822822+ count = -EINVAL;823823+ break;824824+ }825825+826826+ return count;827827+}828828+829829+static ssize_t mcu_id_show(struct device *dev, struct device_attribute *attr,830830+ char *buf)831831+{832832+ return sysfs_emit(buf, "%*phN\n", 12, &drvdata.mcu_id);833833+}834834+835835+static int rgb_cfg_call(struct hid_device *hdev, enum mcu_command_index cmd,836836+ enum rgb_config_index index, u8 *val, size_t size)837837+{838838+ if (cmd != SET_RGB_CFG && cmd != GET_RGB_CFG)839839+ return -EINVAL;840840+841841+ if (index < LIGHT_MODE_SEL || index > USR_LIGHT_PROFILE_3)842842+ return -EINVAL;843843+844844+ return mcu_property_out(hdev, cmd, index, val, size);845845+}846846+847847+static int rgb_attr_show(void)848848+{849849+ enum rgb_config_index index;850850+851851+ index = drvdata.rgb_profile + 2;852852+853853+ return rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, index, NULL, 0);854854+};855855+856856+static ssize_t rgb_effect_store(struct device *dev,857857+ struct device_attribute *attr, const char *buf,858858+ size_t count)859859+{860860+ struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);861861+ enum rgb_config_index index;862862+ u8 effect;863863+ int ret;864864+865865+ ret = sysfs_match_string(rgb_effect_text, buf);866866+ if (ret < 0)867867+ return ret;868868+869869+ effect = ret;870870+ index = drvdata.rgb_profile + 2;871871+ u8 rgb_profile[6] = { effect,872872+ mc_cdev->subled_info[0].intensity,873873+ mc_cdev->subled_info[1].intensity,874874+ mc_cdev->subled_info[2].intensity,875875+ drvdata.led_cdev->brightness,876876+ drvdata.rgb_speed };877877+878878+ ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6);879879+ if (ret)880880+ return ret;881881+882882+ drvdata.rgb_effect = effect;883883+ return count;884884+};885885+886886+static ssize_t rgb_effect_show(struct device *dev,887887+ struct device_attribute *attr, char *buf)888888+{889889+ int ret;890890+891891+ ret = rgb_attr_show();892892+ if (ret)893893+ return ret;894894+895895+ if (drvdata.rgb_effect >= ARRAY_SIZE(rgb_effect_text))896896+ return -EINVAL;897897+898898+ return sysfs_emit(buf, "%s\n", rgb_effect_text[drvdata.rgb_effect]);899899+}900900+901901+static ssize_t rgb_effect_index_show(struct device *dev,902902+ struct device_attribute *attr, char *buf)903903+{904904+ ssize_t count = 0;905905+ unsigned int i;906906+907907+ for (i = 0; i < ARRAY_SIZE(rgb_effect_text); i++)908908+ count += sysfs_emit_at(buf, count, "%s ", rgb_effect_text[i]);909909+910910+ if (count)911911+ buf[count - 1] = '\n';912912+913913+ return count;914914+}915915+916916+static ssize_t rgb_speed_store(struct device *dev,917917+ struct device_attribute *attr, const char *buf,918918+ size_t count)919919+{920920+ struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);921921+ enum rgb_config_index index;922922+ int val = 0;923923+ int ret;924924+925925+ ret = kstrtoint(buf, 10, &val);926926+ if (ret)927927+ return ret;928928+929929+ if (val < 0 || val > 100)930930+ return -EINVAL;931931+932932+ index = drvdata.rgb_profile + 2;933933+ u8 rgb_profile[6] = { drvdata.rgb_effect,934934+ mc_cdev->subled_info[0].intensity,935935+ mc_cdev->subled_info[1].intensity,936936+ mc_cdev->subled_info[2].intensity,937937+ drvdata.led_cdev->brightness,938938+ val };939939+940940+ ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6);941941+ if (ret)942942+ return ret;943943+944944+ drvdata.rgb_speed = val;945945+946946+ return count;947947+};948948+949949+static ssize_t rgb_speed_show(struct device *dev, struct device_attribute *attr,950950+ char *buf)951951+{952952+ int ret;953953+954954+ ret = rgb_attr_show();955955+ if (ret)956956+ return ret;957957+958958+ if (drvdata.rgb_speed > 100)959959+ return -EINVAL;960960+961961+ return sysfs_emit(buf, "%hhu\n", drvdata.rgb_speed);962962+}963963+964964+static ssize_t rgb_speed_range_show(struct device *dev,965965+ struct device_attribute *attr, char *buf)966966+{967967+ return sysfs_emit(buf, "0-100\n");968968+}969969+970970+static ssize_t rgb_mode_store(struct device *dev, struct device_attribute *attr,971971+ const char *buf, size_t count)972972+{973973+ int ret;974974+ u8 val;975975+976976+ ret = sysfs_match_string(rgb_mode_text, buf);977977+ if (ret <= 0)978978+ return ret;979979+980980+ val = ret;981981+982982+ ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, LIGHT_MODE_SEL, &val,983983+ 1);984984+ if (ret)985985+ return ret;986986+987987+ drvdata.rgb_mode = val;988988+989989+ return count;990990+};991991+992992+static ssize_t rgb_mode_show(struct device *dev, struct device_attribute *attr,993993+ char *buf)994994+{995995+ int ret;996996+997997+ ret = rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, LIGHT_MODE_SEL, NULL, 0);998998+ if (ret)999999+ return ret;10001000+10011001+ if (drvdata.rgb_mode >= ARRAY_SIZE(rgb_mode_text))10021002+ return -EINVAL;10031003+10041004+ return sysfs_emit(buf, "%s\n", rgb_mode_text[drvdata.rgb_mode]);10051005+};10061006+10071007+static ssize_t rgb_mode_index_show(struct device *dev,10081008+ struct device_attribute *attr, char *buf)10091009+{10101010+ ssize_t count = 0;10111011+ unsigned int i;10121012+10131013+ for (i = 1; i < ARRAY_SIZE(rgb_mode_text); i++)10141014+ count += sysfs_emit_at(buf, count, "%s ", rgb_mode_text[i]);10151015+10161016+ if (count)10171017+ buf[count - 1] = '\n';10181018+10191019+ return count;10201020+}10211021+10221022+static ssize_t rgb_profile_store(struct device *dev,10231023+ struct device_attribute *attr, const char *buf,10241024+ size_t count)10251025+{10261026+ size_t size = 1;10271027+ int ret;10281028+ u8 val;10291029+10301030+ ret = kstrtou8(buf, 10, &val);10311031+ if (ret < 0)10321032+ return ret;10331033+10341034+ if (val < 1 || val > 3)10351035+ return -EINVAL;10361036+10371037+ ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, LIGHT_PROFILE_SEL, &val, size);10381038+ if (ret)10391039+ return ret;10401040+10411041+ drvdata.rgb_profile = val;10421042+10431043+ return count;10441044+};10451045+10461046+static ssize_t rgb_profile_show(struct device *dev,10471047+ struct device_attribute *attr, char *buf)10481048+{10491049+ int ret;10501050+10511051+ ret = rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, LIGHT_PROFILE_SEL, NULL, 0);10521052+ if (ret)10531053+ return ret;10541054+10551055+ if (drvdata.rgb_profile < 1 || drvdata.rgb_profile > 3)10561056+ return -EINVAL;10571057+10581058+ return sysfs_emit(buf, "%hhu\n", drvdata.rgb_profile);10591059+};10601060+10611061+static ssize_t rgb_profile_range_show(struct device *dev,10621062+ struct device_attribute *attr, char *buf)10631063+{10641064+ return sysfs_emit(buf, "1-3\n");10651065+}10661066+10671067+static void hid_gos_brightness_set(struct led_classdev *led_cdev,10681068+ enum led_brightness brightness)10691069+{10701070+ struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);10711071+ enum rgb_config_index index;10721072+ int ret;10731073+10741074+ if (brightness > led_cdev->max_brightness) {10751075+ dev_err(led_cdev->dev, "Invalid argument\n");10761076+ return;10771077+ }10781078+10791079+ index = drvdata.rgb_profile + 2;10801080+ u8 rgb_profile[6] = { drvdata.rgb_effect,10811081+ mc_cdev->subled_info[0].intensity,10821082+ mc_cdev->subled_info[1].intensity,10831083+ mc_cdev->subled_info[2].intensity,10841084+ brightness,10851085+ drvdata.rgb_speed };10861086+10871087+ ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6);10881088+ switch (ret) {10891089+ case 0:10901090+ led_cdev->brightness = brightness;10911091+ break;10921092+ case -ENODEV: /* during switch to IAP -ENODEV is expected */10931093+ case -ENOSYS: /* during rmmod -ENOSYS is expected */10941094+ dev_dbg(led_cdev->dev, "Failed to write RGB profile: %i\n",10951095+ ret);10961096+ break;10971097+ default:10981098+ dev_err(led_cdev->dev, "Failed to write RGB profile: %i\n",10991099+ ret);11001100+ }11011101+}11021102+11031103+#define LEGOS_DEVICE_ATTR_RW(_name, _attrname, _rtype, _group) \11041104+ static ssize_t _name##_store(struct device *dev, \11051105+ struct device_attribute *attr, \11061106+ const char *buf, size_t count) \11071107+ { \11081108+ return _group##_property_store(dev, attr, buf, count, \11091109+ _name.index); \11101110+ } \11111111+ static ssize_t _name##_show(struct device *dev, \11121112+ struct device_attribute *attr, char *buf) \11131113+ { \11141114+ return _group##_property_show(dev, attr, buf, _name.index); \11151115+ } \11161116+ static ssize_t _name##_##_rtype##_show( \11171117+ struct device *dev, struct device_attribute *attr, char *buf) \11181118+ { \11191119+ return _group##_property_options(dev, attr, buf, _name.index); \11201120+ } \11211121+ static DEVICE_ATTR_RW_NAMED(_name, _attrname)11221122+11231123+#define LEGOS_DEVICE_ATTR_RO(_name, _attrname, _group) \11241124+ static ssize_t _name##_show(struct device *dev, \11251125+ struct device_attribute *attr, char *buf) \11261126+ { \11271127+ return _group##_property_show(dev, attr, buf, _name.index); \11281128+ } \11291129+ static DEVICE_ATTR_RO_NAMED(_name, _attrname)11301130+11311131+/* Gamepad */11321132+static struct gos_cfg_attr auto_sleep_time = { FEATURE_AUTO_SLEEP_TIME };11331133+LEGOS_DEVICE_ATTR_RW(auto_sleep_time, "auto_sleep_time", range, gamepad);11341134+static DEVICE_ATTR_RO(auto_sleep_time_range);11351135+11361136+static struct gos_cfg_attr dpad_mode = { FEATURE_DPAD_MODE };11371137+LEGOS_DEVICE_ATTR_RW(dpad_mode, "dpad_mode", index, gamepad);11381138+static DEVICE_ATTR_RO(dpad_mode_index);11391139+11401140+static struct gos_cfg_attr gamepad_mode = { FEATURE_GAMEPAD_MODE };11411141+LEGOS_DEVICE_ATTR_RW(gamepad_mode, "mode", index, gamepad);11421142+static DEVICE_ATTR_RO_NAMED(gamepad_mode_index, "mode_index");11431143+11441144+static struct gos_cfg_attr gamepad_poll_rate = { FEATURE_POLL_RATE };11451145+LEGOS_DEVICE_ATTR_RW(gamepad_poll_rate, "poll_rate", index, gamepad);11461146+static DEVICE_ATTR_RO_NAMED(gamepad_poll_rate_index, "poll_rate_index");11471147+11481148+static struct attribute *legos_gamepad_attrs[] = {11491149+ &dev_attr_auto_sleep_time.attr,11501150+ &dev_attr_auto_sleep_time_range.attr,11511151+ &dev_attr_dpad_mode.attr,11521152+ &dev_attr_dpad_mode_index.attr,11531153+ &dev_attr_gamepad_mode.attr,11541154+ &dev_attr_gamepad_mode_index.attr,11551155+ &dev_attr_gamepad_poll_rate.attr,11561156+ &dev_attr_gamepad_poll_rate_index.attr,11571157+ NULL,11581158+};11591159+11601160+static const struct attribute_group gamepad_attr_group = {11611161+ .name = "gamepad",11621162+ .attrs = legos_gamepad_attrs,11631163+};11641164+11651165+/* IMU */11661166+static struct gos_cfg_attr imu_bypass_enabled = { FEATURE_IMU_BYPASS };11671167+LEGOS_DEVICE_ATTR_RW(imu_bypass_enabled, "bypass_enabled", index, gamepad);11681168+static DEVICE_ATTR_RO_NAMED(imu_bypass_enabled_index, "bypass_enabled_index");11691169+11701170+static struct gos_cfg_attr imu_manufacturer = { TEST_IMU_MFR };11711171+LEGOS_DEVICE_ATTR_RO(imu_manufacturer, "manufacturer", test);11721172+11731173+static struct gos_cfg_attr imu_sensor_enabled = { FEATURE_IMU_ENABLE };11741174+LEGOS_DEVICE_ATTR_RW(imu_sensor_enabled, "sensor_enabled", index, gamepad);11751175+static DEVICE_ATTR_RO_NAMED(imu_sensor_enabled_index, "sensor_enabled_index");11761176+11771177+static struct attribute *legos_imu_attrs[] = {11781178+ &dev_attr_imu_bypass_enabled.attr,11791179+ &dev_attr_imu_bypass_enabled_index.attr,11801180+ &dev_attr_imu_manufacturer.attr,11811181+ &dev_attr_imu_sensor_enabled.attr,11821182+ &dev_attr_imu_sensor_enabled_index.attr,11831183+ NULL,11841184+};11851185+11861186+static const struct attribute_group imu_attr_group = {11871187+ .name = "imu",11881188+ .attrs = legos_imu_attrs,11891189+};11901190+11911191+/* MCU */11921192+static DEVICE_ATTR_RO(mcu_id);11931193+11941194+static struct gos_cfg_attr os_mode = { FEATURE_OS_MODE };11951195+LEGOS_DEVICE_ATTR_RW(os_mode, "os_mode", index, gamepad);11961196+static DEVICE_ATTR_RO(os_mode_index);11971197+11981198+static struct attribute *legos_mcu_attrs[] = {11991199+ &dev_attr_mcu_id.attr,12001200+ &dev_attr_os_mode.attr,12011201+ &dev_attr_os_mode_index.attr,12021202+ NULL,12031203+};12041204+12051205+static const struct attribute_group mcu_attr_group = {12061206+ .attrs = legos_mcu_attrs,12071207+};12081208+12091209+/* Mouse */12101210+static struct gos_cfg_attr mouse_wheel_step = { FEATURE_MOUSE_WHEEL_STEP };12111211+LEGOS_DEVICE_ATTR_RW(mouse_wheel_step, "step", range, gamepad);12121212+static DEVICE_ATTR_RO_NAMED(mouse_wheel_step_range, "step_range");12131213+12141214+static struct attribute *legos_mouse_attrs[] = {12151215+ &dev_attr_mouse_wheel_step.attr,12161216+ &dev_attr_mouse_wheel_step_range.attr,12171217+ NULL,12181218+};12191219+12201220+static const struct attribute_group mouse_attr_group = {12211221+ .name = "mouse",12221222+ .attrs = legos_mouse_attrs,12231223+};12241224+12251225+/* Touchpad */12261226+static struct gos_cfg_attr touchpad_enabled = { FEATURE_TOUCHPAD_ENABLE };12271227+LEGOS_DEVICE_ATTR_RW(touchpad_enabled, "enabled", index, gamepad);12281228+static DEVICE_ATTR_RO_NAMED(touchpad_enabled_index, "enabled_index");12291229+12301230+static struct gos_cfg_attr touchpad_linux_mode = { CFG_LINUX_MODE };12311231+LEGOS_DEVICE_ATTR_RW(touchpad_linux_mode, "linux_mode", index, touchpad);12321232+static DEVICE_ATTR_RO_NAMED(touchpad_linux_mode_index, "linux_mode_index");12331233+12341234+static struct gos_cfg_attr touchpad_manufacturer = { TEST_TP_MFR };12351235+LEGOS_DEVICE_ATTR_RO(touchpad_manufacturer, "manufacturer", test);12361236+12371237+static struct gos_cfg_attr touchpad_version = { TEST_TP_VER };12381238+LEGOS_DEVICE_ATTR_RO(touchpad_version, "version", test);12391239+12401240+static struct gos_cfg_attr touchpad_windows_mode = { CFG_WINDOWS_MODE };12411241+LEGOS_DEVICE_ATTR_RW(touchpad_windows_mode, "windows_mode", index, touchpad);12421242+static DEVICE_ATTR_RO_NAMED(touchpad_windows_mode_index, "windows_mode_index");12431243+12441244+static struct attribute *legos_touchpad_attrs[] = {12451245+ &dev_attr_touchpad_enabled.attr,12461246+ &dev_attr_touchpad_enabled_index.attr,12471247+ &dev_attr_touchpad_linux_mode.attr,12481248+ &dev_attr_touchpad_linux_mode_index.attr,12491249+ &dev_attr_touchpad_manufacturer.attr,12501250+ &dev_attr_touchpad_version.attr,12511251+ &dev_attr_touchpad_windows_mode.attr,12521252+ &dev_attr_touchpad_windows_mode_index.attr,12531253+ NULL,12541254+};12551255+12561256+static const struct attribute_group touchpad_attr_group = {12571257+ .name = "touchpad",12581258+ .attrs = legos_touchpad_attrs,12591259+};12601260+12611261+static const struct attribute_group *top_level_attr_groups[] = {12621262+ &gamepad_attr_group,12631263+ &imu_attr_group,12641264+ &mcu_attr_group,12651265+ &mouse_attr_group,12661266+ &touchpad_attr_group,12671267+ NULL,12681268+};12691269+12701270+/* RGB */12711271+static struct gos_cfg_attr rgb_enabled = { FEATURE_RGB_ENABLE };12721272+LEGOS_DEVICE_ATTR_RW(rgb_enabled, "enabled", index, gamepad);12731273+static DEVICE_ATTR_RO_NAMED(rgb_enabled_index, "enabled_index");12741274+12751275+static DEVICE_ATTR_RW_NAMED(rgb_effect, "effect");12761276+static DEVICE_ATTR_RO_NAMED(rgb_effect_index, "effect_index");12771277+static DEVICE_ATTR_RW_NAMED(rgb_mode, "mode");12781278+static DEVICE_ATTR_RO_NAMED(rgb_mode_index, "mode_index");12791279+static DEVICE_ATTR_RW_NAMED(rgb_profile, "profile");12801280+static DEVICE_ATTR_RO_NAMED(rgb_profile_range, "profile_range");12811281+static DEVICE_ATTR_RW_NAMED(rgb_speed, "speed");12821282+static DEVICE_ATTR_RO_NAMED(rgb_speed_range, "speed_range");12831283+12841284+static struct attribute *gos_rgb_attrs[] = {12851285+ &dev_attr_rgb_enabled.attr,12861286+ &dev_attr_rgb_enabled_index.attr,12871287+ &dev_attr_rgb_effect.attr,12881288+ &dev_attr_rgb_effect_index.attr,12891289+ &dev_attr_rgb_mode.attr,12901290+ &dev_attr_rgb_mode_index.attr,12911291+ &dev_attr_rgb_profile.attr,12921292+ &dev_attr_rgb_profile_range.attr,12931293+ &dev_attr_rgb_speed.attr,12941294+ &dev_attr_rgb_speed_range.attr,12951295+ NULL,12961296+};12971297+12981298+static struct attribute_group rgb_attr_group = {12991299+ .attrs = gos_rgb_attrs,13001300+};13011301+13021302+static struct mc_subled gos_rgb_subled_info[] = {13031303+ {13041304+ .color_index = LED_COLOR_ID_RED,13051305+ .brightness = 0x50,13061306+ .intensity = 0x24,13071307+ .channel = 0x1,13081308+ },13091309+ {13101310+ .color_index = LED_COLOR_ID_GREEN,13111311+ .brightness = 0x50,13121312+ .intensity = 0x22,13131313+ .channel = 0x2,13141314+ },13151315+ {13161316+ .color_index = LED_COLOR_ID_BLUE,13171317+ .brightness = 0x50,13181318+ .intensity = 0x99,13191319+ .channel = 0x3,13201320+ },13211321+};13221322+13231323+static struct led_classdev_mc gos_cdev_rgb = {13241324+ .led_cdev = {13251325+ .name = "go_s:rgb:joystick_rings",13261326+ .brightness = 0x50,13271327+ .max_brightness = 0x64,13281328+ .brightness_set = hid_gos_brightness_set,13291329+ },13301330+ .num_colors = ARRAY_SIZE(gos_rgb_subled_info),13311331+ .subled_info = gos_rgb_subled_info,13321332+};13331333+13341334+static void cfg_setup(struct work_struct *work)13351335+{13361336+ int ret;13371337+13381338+ /* MCU */13391339+ ret = mcu_property_out(drvdata.hdev, GET_MCU_ID, FEATURE_NONE, NULL, 0);13401340+ if (ret) {13411341+ dev_err(&drvdata.hdev->dev, "Failed to retrieve MCU ID: %i\n",13421342+ ret);13431343+ return;13441344+ }13451345+13461346+ ret = mcu_property_out(drvdata.hdev, GET_VERSION, FEATURE_NONE, NULL, 0);13471347+ if (ret) {13481348+ dev_err(&drvdata.hdev->dev, "Failed to retrieve MCU Version: %i\n", ret);13491349+ return;13501350+ }13511351+13521352+ ret = mcu_property_out(drvdata.hdev, GET_PL_TEST, TEST_TP_MFR, NULL, 0);13531353+ if (ret) {13541354+ dev_err(&drvdata.hdev->dev,13551355+ "Failed to retrieve Touchpad Manufacturer: %i\n", ret);13561356+ return;13571357+ }13581358+13591359+ ret = mcu_property_out(drvdata.hdev, GET_PL_TEST, TEST_TP_VER, NULL, 0);13601360+ if (ret) {13611361+ dev_err(&drvdata.hdev->dev,13621362+ "Failed to retrieve Touchpad Firmware Version: %i\n", ret);13631363+ return;13641364+ }13651365+13661366+ ret = mcu_property_out(drvdata.hdev, GET_PL_TEST, TEST_IMU_MFR, NULL, 0);13671367+ if (ret) {13681368+ dev_err(&drvdata.hdev->dev,13691369+ "Failed to retrieve IMU Manufacturer: %i\n", ret);13701370+ return;13711371+ }13721372+}13731373+13741374+static int hid_gos_cfg_probe(struct hid_device *hdev,13751375+ const struct hid_device_id *_id)13761376+{13771377+ int ret;13781378+13791379+ hid_set_drvdata(hdev, &drvdata);13801380+ drvdata.hdev = hdev;13811381+ mutex_init(&drvdata.cfg_mutex);13821382+13831383+ ret = sysfs_create_groups(&hdev->dev.kobj, top_level_attr_groups);13841384+ if (ret) {13851385+ dev_err_probe(&hdev->dev, ret,13861386+ "Failed to create gamepad configuration attributes\n");13871387+ return ret;13881388+ }13891389+13901390+ ret = devm_led_classdev_multicolor_register(&hdev->dev, &gos_cdev_rgb);13911391+ if (ret) {13921392+ dev_err_probe(&hdev->dev, ret, "Failed to create RGB device\n");13931393+ return ret;13941394+ }13951395+13961396+ ret = devm_device_add_group(gos_cdev_rgb.led_cdev.dev, &rgb_attr_group);13971397+ if (ret) {13981398+ dev_err_probe(&hdev->dev, ret,13991399+ "Failed to create RGB configuration attributes\n");14001400+ return ret;14011401+ }14021402+14031403+ drvdata.led_cdev = &gos_cdev_rgb.led_cdev;14041404+14051405+ init_completion(&drvdata.send_cmd_complete);14061406+14071407+ /* Executing calls prior to returning from probe will lock the MCU. Schedule14081408+ * initial data call after probe has completed and MCU can accept calls.14091409+ */14101410+ INIT_DELAYED_WORK(&drvdata.gos_cfg_setup, &cfg_setup);14111411+ ret = schedule_delayed_work(&drvdata.gos_cfg_setup, msecs_to_jiffies(2));14121412+ if (!ret) {14131413+ dev_err(&hdev->dev, "Failed to schedule startup delayed work\n");14141414+ return -ENODEV;14151415+ }14161416+14171417+ return 0;14181418+}14191419+14201420+static void hid_gos_cfg_remove(struct hid_device *hdev)14211421+{14221422+ guard(mutex)(&drvdata.cfg_mutex);14231423+ cancel_delayed_work_sync(&drvdata.gos_cfg_setup);14241424+ sysfs_remove_groups(&hdev->dev.kobj, top_level_attr_groups);14251425+ hid_hw_close(hdev);14261426+ hid_hw_stop(hdev);14271427+ hid_set_drvdata(hdev, NULL);14281428+}14291429+14301430+static int hid_gos_probe(struct hid_device *hdev,14311431+ const struct hid_device_id *id)14321432+{14331433+ int ret, ep;14341434+14351435+ ret = hid_parse(hdev);14361436+ if (ret) {14371437+ hid_err(hdev, "Parse failed\n");14381438+ return ret;14391439+ }14401440+14411441+ ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);14421442+ if (ret) {14431443+ hid_err(hdev, "Failed to start HID device\n");14441444+ return ret;14451445+ }14461446+14471447+ ret = hid_hw_open(hdev);14481448+ if (ret) {14491449+ hid_err(hdev, "Failed to open HID device\n");14501450+ hid_hw_stop(hdev);14511451+ return ret;14521452+ }14531453+14541454+ ep = get_endpoint_address(hdev);14551455+ if (ep != GO_S_CFG_INTF_IN) {14561456+ dev_dbg(&hdev->dev, "Started interface %x as generic HID device.\n", ep);14571457+ return 0;14581458+ }14591459+14601460+ ret = hid_gos_cfg_probe(hdev, id);14611461+ if (ret)14621462+ dev_err_probe(&hdev->dev, ret, "Failed to start configuration interface");14631463+14641464+ dev_dbg(&hdev->dev, "Started interface %x as Go S configuration interface\n", ep);14651465+ return ret;14661466+}14671467+14681468+static void hid_gos_remove(struct hid_device *hdev)14691469+{14701470+ int ep = get_endpoint_address(hdev);14711471+14721472+ switch (ep) {14731473+ case GO_S_CFG_INTF_IN:14741474+ hid_gos_cfg_remove(hdev);14751475+ break;14761476+ default:14771477+ hid_hw_close(hdev);14781478+ hid_hw_stop(hdev);14791479+14801480+ break;14811481+ }14821482+}14831483+14841484+static const struct hid_device_id hid_gos_devices[] = {14851485+ { HID_USB_DEVICE(USB_VENDOR_ID_QHE,14861486+ USB_DEVICE_ID_LENOVO_LEGION_GO_S_XINPUT) },14871487+ { HID_USB_DEVICE(USB_VENDOR_ID_QHE,14881488+ USB_DEVICE_ID_LENOVO_LEGION_GO_S_DINPUT) },14891489+ {}14901490+};14911491+14921492+MODULE_DEVICE_TABLE(hid, hid_gos_devices);14931493+static struct hid_driver hid_lenovo_go_s = {14941494+ .name = "hid-lenovo-go-s",14951495+ .id_table = hid_gos_devices,14961496+ .probe = hid_gos_probe,14971497+ .remove = hid_gos_remove,14981498+ .raw_event = hid_gos_raw_event,14991499+};15001500+module_hid_driver(hid_lenovo_go_s);15011501+15021502+MODULE_AUTHOR("Derek J. Clark");15031503+MODULE_DESCRIPTION("HID Driver for Lenovo Legion Go S Series gamepad.");15041504+MODULE_LICENSE("GPL");
+2500
drivers/hid/hid-lenovo-go.c
···11+// SPDX-License-Identifier: GPL-2.0-or-later22+/*33+ * HID driver for Lenovo Legion Go series gamepads.44+ *55+ * Copyright (c) 2026 Derek J. Clark <derekjohn.clark@gmail.com>66+ * Copyright (c) 2026 Valve Corporation77+ */88+99+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt1010+1111+#include <linux/array_size.h>1212+#include <linux/cleanup.h>1313+#include <linux/completion.h>1414+#include <linux/delay.h>1515+#include <linux/dev_printk.h>1616+#include <linux/device.h>1717+#include <linux/device/devres.h>1818+#include <linux/hid.h>1919+#include <linux/jiffies.h>2020+#include <linux/kstrtox.h>2121+#include <linux/led-class-multicolor.h>2222+#include <linux/mutex.h>2323+#include <linux/printk.h>2424+#include <linux/sysfs.h>2525+#include <linux/types.h>2626+#include <linux/unaligned.h>2727+#include <linux/usb.h>2828+#include <linux/workqueue.h>2929+#include <linux/workqueue_types.h>3030+3131+#include "hid-ids.h"3232+3333+#define GO_GP_INTF_IN 0x833434+#define GO_OUTPUT_REPORT_ID 0x053535+#define GO_GP_RESET_SUCCESS 0x013636+#define GO_PACKET_SIZE 643737+3838+static struct hid_go_cfg {3939+ struct delayed_work go_cfg_setup;4040+ struct completion send_cmd_complete;4141+ struct led_classdev *led_cdev;4242+ struct hid_device *hdev;4343+ struct mutex cfg_mutex; /*ensure single synchronous output report*/4444+ u8 fps_mode;4545+ u8 gp_left_auto_sleep_time;4646+ u8 gp_left_gyro_cal_status;4747+ u8 gp_left_joy_cal_status;4848+ u8 gp_left_notify_en;4949+ u8 gp_left_rumble_mode;5050+ u8 gp_left_trigg_cal_status;5151+ u32 gp_left_version_firmware;5252+ u8 gp_left_version_gen;5353+ u32 gp_left_version_hardware;5454+ u32 gp_left_version_product;5555+ u32 gp_left_version_protocol;5656+ u8 gp_mode;5757+ u8 gp_right_auto_sleep_time;5858+ u8 gp_right_gyro_cal_status;5959+ u8 gp_right_joy_cal_status;6060+ u8 gp_right_notify_en;6161+ u8 gp_right_rumble_mode;6262+ u8 gp_right_trigg_cal_status;6363+ u32 gp_right_version_firmware;6464+ u8 gp_right_version_gen;6565+ u32 gp_right_version_hardware;6666+ u32 gp_right_version_product;6767+ u32 gp_right_version_protocol;6868+ u8 gp_rumble_intensity;6969+ u8 imu_left_bypass_en;7070+ u8 imu_left_sensor_en;7171+ u8 imu_right_bypass_en;7272+ u8 imu_right_sensor_en;7373+ u32 mcu_version_firmware;7474+ u8 mcu_version_gen;7575+ u32 mcu_version_hardware;7676+ u32 mcu_version_product;7777+ u32 mcu_version_protocol;7878+ u32 mouse_dpi;7979+ u8 os_mode;8080+ u8 rgb_effect;8181+ u8 rgb_en;8282+ u8 rgb_mode;8383+ u8 rgb_profile;8484+ u8 rgb_speed;8585+ u8 tp_en;8686+ u8 tp_vibration_en;8787+ u8 tp_vibration_intensity;8888+ u32 tx_dongle_version_firmware;8989+ u8 tx_dongle_version_gen;9090+ u32 tx_dongle_version_hardware;9191+ u32 tx_dongle_version_product;9292+ u32 tx_dongle_version_protocol;9393+} drvdata;9494+9595+struct go_cfg_attr {9696+ u8 index;9797+};9898+9999+struct command_report {100100+ u8 report_id;101101+ u8 id;102102+ u8 cmd;103103+ u8 sub_cmd;104104+ u8 device_type;105105+ u8 data[59];106106+} __packed;107107+108108+enum command_id {109109+ MCU_CONFIG_DATA = 0x00,110110+ OS_MODE_DATA = 0x06,111111+ GAMEPAD_DATA = 0x3c,112112+};113113+114114+enum mcu_command_index {115115+ GET_VERSION_DATA = 0x02,116116+ GET_FEATURE_STATUS,117117+ SET_FEATURE_STATUS,118118+ GET_MOTOR_CFG,119119+ SET_MOTOR_CFG,120120+ GET_DPI_CFG,121121+ SET_DPI_CFG,122122+ SET_TRIGGER_CFG = 0x0a,123123+ SET_JOYSTICK_CFG = 0x0c,124124+ SET_GYRO_CFG = 0x0e,125125+ GET_RGB_CFG,126126+ SET_RGB_CFG,127127+ GET_DEVICE_STATUS = 0xa0,128128+129129+};130130+131131+enum dev_type {132132+ UNSPECIFIED,133133+ USB_MCU,134134+ TX_DONGLE,135135+ LEFT_CONTROLLER,136136+ RIGHT_CONTROLLER,137137+};138138+139139+enum enabled_status_index {140140+ FEATURE_UNKNOWN,141141+ FEATURE_ENABLED,142142+ FEATURE_DISABLED,143143+};144144+145145+static const char *const enabled_status_text[] = {146146+ [FEATURE_UNKNOWN] = "unknown",147147+ [FEATURE_ENABLED] = "true",148148+ [FEATURE_DISABLED] = "false",149149+};150150+151151+enum version_data_index {152152+ PRODUCT_VERSION = 0x02,153153+ PROTOCOL_VERSION,154154+ FIRMWARE_VERSION,155155+ HARDWARE_VERSION,156156+ HARDWARE_GENERATION,157157+};158158+159159+enum feature_status_index {160160+ FEATURE_RESET_GAMEPAD = 0x02,161161+ FEATURE_IMU_BYPASS,162162+ FEATURE_IMU_ENABLE = 0x05,163163+ FEATURE_TOUCHPAD_ENABLE = 0x07,164164+ FEATURE_LIGHT_ENABLE,165165+ FEATURE_AUTO_SLEEP_TIME,166166+ FEATURE_FPS_SWITCH_STATUS = 0x0b,167167+ FEATURE_GAMEPAD_MODE = 0x0e,168168+};169169+170170+#define FEATURE_OS_MODE 0x69171171+172172+enum fps_switch_status_index {173173+ FPS_STATUS_UNKNOWN,174174+ GAMEPAD,175175+ FPS,176176+};177177+178178+static const char *const fps_switch_text[] = {179179+ [FPS_STATUS_UNKNOWN] = "unknown",180180+ [GAMEPAD] = "gamepad",181181+ [FPS] = "fps",182182+};183183+184184+enum gamepad_mode_index {185185+ GAMEPAD_MODE_UNKNOWN,186186+ XINPUT,187187+ DINPUT,188188+};189189+190190+static const char *const gamepad_mode_text[] = {191191+ [GAMEPAD_MODE_UNKNOWN] = "unknown",192192+ [XINPUT] = "xinput",193193+ [DINPUT] = "dinput",194194+};195195+196196+enum motor_cfg_index {197197+ MOTOR_CFG_ALL = 0x01,198198+ MOTOR_INTENSITY,199199+ VIBRATION_NOTIFY_ENABLE,200200+ RUMBLE_MODE,201201+ TP_VIBRATION_ENABLE,202202+ TP_VIBRATION_INTENSITY,203203+};204204+205205+enum intensity_index {206206+ INTENSITY_UNKNOWN,207207+ INTENSITY_OFF,208208+ INTENSITY_LOW,209209+ INTENSITY_MEDIUM,210210+ INTENSITY_HIGH,211211+};212212+213213+static const char *const intensity_text[] = {214214+ [INTENSITY_UNKNOWN] = "unknown",215215+ [INTENSITY_OFF] = "off",216216+ [INTENSITY_LOW] = "low",217217+ [INTENSITY_MEDIUM] = "medium",218218+ [INTENSITY_HIGH] = "high",219219+};220220+221221+enum rumble_mode_index {222222+ RUMBLE_MODE_UNKNOWN,223223+ RUMBLE_MODE_FPS,224224+ RUMBLE_MODE_RACE,225225+ RUMBLE_MODE_AVERAGE,226226+ RUMBLE_MODE_SPG,227227+ RUMBLE_MODE_RPG,228228+};229229+230230+static const char *const rumble_mode_text[] = {231231+ [RUMBLE_MODE_UNKNOWN] = "unknown",232232+ [RUMBLE_MODE_FPS] = "fps",233233+ [RUMBLE_MODE_RACE] = "racing",234234+ [RUMBLE_MODE_AVERAGE] = "standard",235235+ [RUMBLE_MODE_SPG] = "spg",236236+ [RUMBLE_MODE_RPG] = "rpg",237237+};238238+239239+#define FPS_MODE_DPI 0x02240240+#define TRIGGER_CALIBRATE 0x04241241+#define JOYSTICK_CALIBRATE 0x04242242+#define GYRO_CALIBRATE 0x06243243+244244+enum cal_device_type {245245+ CALDEV_GYROSCOPE = 0x01,246246+ CALDEV_JOYSTICK,247247+ CALDEV_TRIGGER,248248+ CALDEV_JOY_TRIGGER,249249+};250250+251251+enum cal_enable {252252+ CAL_UNKNOWN,253253+ CAL_START,254254+ CAL_STOP,255255+};256256+257257+static const char *const cal_enabled_text[] = {258258+ [CAL_UNKNOWN] = "unknown",259259+ [CAL_START] = "start",260260+ [CAL_STOP] = "stop",261261+};262262+263263+enum cal_status_index {264264+ CAL_STAT_UNKNOWN,265265+ CAL_STAT_SUCCESS,266266+ CAL_STAT_FAILURE,267267+};268268+269269+static const char *const cal_status_text[] = {270270+ [CAL_STAT_UNKNOWN] = "unknown",271271+ [CAL_STAT_SUCCESS] = "success",272272+ [CAL_STAT_FAILURE] = "failure",273273+};274274+275275+enum rgb_config_index {276276+ LIGHT_CFG_ALL = 0x01,277277+ LIGHT_MODE_SEL,278278+ LIGHT_PROFILE_SEL,279279+ USR_LIGHT_PROFILE_1,280280+ USR_LIGHT_PROFILE_2,281281+ USR_LIGHT_PROFILE_3,282282+};283283+284284+enum rgb_mode_index {285285+ RGB_MODE_UNKNOWN,286286+ RGB_MODE_DYNAMIC,287287+ RGB_MODE_CUSTOM,288288+};289289+290290+static const char *const rgb_mode_text[] = {291291+ [RGB_MODE_UNKNOWN] = "unknown",292292+ [RGB_MODE_DYNAMIC] = "dynamic",293293+ [RGB_MODE_CUSTOM] = "custom",294294+};295295+296296+enum rgb_effect_index {297297+ RGB_EFFECT_MONO,298298+ RGB_EFFECT_BREATHE,299299+ RGB_EFFECT_CHROMA,300300+ RGB_EFFECT_RAINBOW,301301+};302302+303303+static const char *const rgb_effect_text[] = {304304+ [RGB_EFFECT_MONO] = "monocolor",305305+ [RGB_EFFECT_BREATHE] = "breathe",306306+ [RGB_EFFECT_CHROMA] = "chroma",307307+ [RGB_EFFECT_RAINBOW] = "rainbow",308308+};309309+310310+enum device_status_index {311311+ GET_CAL_STATUS = 0x02,312312+ GET_UPGRADE_STATUS,313313+ GET_MACRO_REC_STATUS,314314+ GET_HOTKEY_TRIGG_STATUS,315315+};316316+317317+enum os_mode_cfg_index {318318+ SET_OS_MODE = 0x09,319319+ GET_OS_MODE,320320+};321321+322322+enum os_mode_type_index {323323+ OS_UNKNOWN,324324+ WINDOWS,325325+ LINUX,326326+};327327+328328+static const char *const os_mode_text[] = {329329+ [OS_UNKNOWN] = "unknown",330330+ [WINDOWS] = "windows",331331+ [LINUX] = "linux",332332+};333333+334334+static int hid_go_version_event(struct command_report *cmd_rep)335335+{336336+ switch (cmd_rep->sub_cmd) {337337+ case PRODUCT_VERSION:338338+ switch (cmd_rep->device_type) {339339+ case USB_MCU:340340+ drvdata.mcu_version_product =341341+ get_unaligned_be32(cmd_rep->data);342342+ return 0;343343+ case TX_DONGLE:344344+ drvdata.tx_dongle_version_product =345345+ get_unaligned_be32(cmd_rep->data);346346+ return 0;347347+ case LEFT_CONTROLLER:348348+ drvdata.gp_left_version_product =349349+ get_unaligned_be32(cmd_rep->data);350350+ return 0;351351+ case RIGHT_CONTROLLER:352352+ drvdata.gp_right_version_product =353353+ get_unaligned_be32(cmd_rep->data);354354+ return 0;355355+ default:356356+ return -EINVAL;357357+ }358358+ case PROTOCOL_VERSION:359359+ switch (cmd_rep->device_type) {360360+ case USB_MCU:361361+ drvdata.mcu_version_protocol =362362+ get_unaligned_be32(cmd_rep->data);363363+ return 0;364364+ case TX_DONGLE:365365+ drvdata.tx_dongle_version_protocol =366366+ get_unaligned_be32(cmd_rep->data);367367+ return 0;368368+ case LEFT_CONTROLLER:369369+ drvdata.gp_left_version_protocol =370370+ get_unaligned_be32(cmd_rep->data);371371+ return 0;372372+ case RIGHT_CONTROLLER:373373+ drvdata.gp_right_version_protocol =374374+ get_unaligned_be32(cmd_rep->data);375375+ return 0;376376+ default:377377+ return -EINVAL;378378+ }379379+ case FIRMWARE_VERSION:380380+ switch (cmd_rep->device_type) {381381+ case USB_MCU:382382+ drvdata.mcu_version_firmware =383383+ get_unaligned_be32(cmd_rep->data);384384+ return 0;385385+ case TX_DONGLE:386386+ drvdata.tx_dongle_version_firmware =387387+ get_unaligned_be32(cmd_rep->data);388388+ return 0;389389+ case LEFT_CONTROLLER:390390+ drvdata.gp_left_version_firmware =391391+ get_unaligned_be32(cmd_rep->data);392392+ return 0;393393+ case RIGHT_CONTROLLER:394394+ drvdata.gp_right_version_firmware =395395+ get_unaligned_be32(cmd_rep->data);396396+ return 0;397397+ default:398398+ return -EINVAL;399399+ }400400+ case HARDWARE_VERSION:401401+ switch (cmd_rep->device_type) {402402+ case USB_MCU:403403+ drvdata.mcu_version_hardware =404404+ get_unaligned_be32(cmd_rep->data);405405+ return 0;406406+ case TX_DONGLE:407407+ drvdata.tx_dongle_version_hardware =408408+ get_unaligned_be32(cmd_rep->data);409409+ return 0;410410+ case LEFT_CONTROLLER:411411+ drvdata.gp_left_version_hardware =412412+ get_unaligned_be32(cmd_rep->data);413413+ return 0;414414+ case RIGHT_CONTROLLER:415415+ drvdata.gp_right_version_hardware =416416+ get_unaligned_be32(cmd_rep->data);417417+ return 0;418418+ default:419419+ return -EINVAL;420420+ }421421+ case HARDWARE_GENERATION:422422+ switch (cmd_rep->device_type) {423423+ case USB_MCU:424424+ drvdata.mcu_version_gen = cmd_rep->data[0];425425+ return 0;426426+ case TX_DONGLE:427427+ drvdata.tx_dongle_version_gen = cmd_rep->data[0];428428+ return 0;429429+ case LEFT_CONTROLLER:430430+ drvdata.gp_left_version_gen = cmd_rep->data[0];431431+ return 0;432432+ case RIGHT_CONTROLLER:433433+ drvdata.gp_right_version_gen = cmd_rep->data[0];434434+ return 0;435435+ default:436436+ return -EINVAL;437437+ }438438+ default:439439+ return -EINVAL;440440+ }441441+}442442+443443+static int hid_go_feature_status_event(struct command_report *cmd_rep)444444+{445445+ switch (cmd_rep->sub_cmd) {446446+ case FEATURE_RESET_GAMEPAD:447447+ return 0;448448+ case FEATURE_IMU_ENABLE:449449+ switch (cmd_rep->device_type) {450450+ case LEFT_CONTROLLER:451451+ drvdata.imu_left_sensor_en = cmd_rep->data[0];452452+ return 0;453453+ case RIGHT_CONTROLLER:454454+ drvdata.imu_right_sensor_en = cmd_rep->data[0];455455+ return 0;456456+ default:457457+ return -EINVAL;458458+ }459459+ case FEATURE_IMU_BYPASS:460460+ switch (cmd_rep->device_type) {461461+ case LEFT_CONTROLLER:462462+ drvdata.imu_left_bypass_en = cmd_rep->data[0];463463+ return 0;464464+ case RIGHT_CONTROLLER:465465+ drvdata.imu_right_bypass_en = cmd_rep->data[0];466466+ return 0;467467+ default:468468+ return -EINVAL;469469+ }470470+ break;471471+ case FEATURE_LIGHT_ENABLE:472472+ drvdata.rgb_en = cmd_rep->data[0];473473+ return 0;474474+ case FEATURE_AUTO_SLEEP_TIME:475475+ switch (cmd_rep->device_type) {476476+ case LEFT_CONTROLLER:477477+ drvdata.gp_left_auto_sleep_time = cmd_rep->data[0];478478+ return 0;479479+ case RIGHT_CONTROLLER:480480+ drvdata.gp_right_auto_sleep_time = cmd_rep->data[0];481481+ return 0;482482+ default:483483+ return -EINVAL;484484+ }485485+ break;486486+ case FEATURE_TOUCHPAD_ENABLE:487487+ drvdata.tp_en = cmd_rep->data[0];488488+ return 0;489489+ case FEATURE_GAMEPAD_MODE:490490+ drvdata.gp_mode = cmd_rep->data[0];491491+ return 0;492492+ case FEATURE_FPS_SWITCH_STATUS:493493+ drvdata.fps_mode = cmd_rep->data[0];494494+ return 0;495495+ default:496496+ return -EINVAL;497497+ }498498+}499499+500500+static int hid_go_motor_event(struct command_report *cmd_rep)501501+{502502+ switch (cmd_rep->sub_cmd) {503503+ case MOTOR_CFG_ALL:504504+ return -EINVAL;505505+ case MOTOR_INTENSITY:506506+ drvdata.gp_rumble_intensity = cmd_rep->data[0];507507+ return 0;508508+ case VIBRATION_NOTIFY_ENABLE:509509+ switch (cmd_rep->device_type) {510510+ case LEFT_CONTROLLER:511511+ drvdata.gp_left_notify_en = cmd_rep->data[0];512512+ return 0;513513+ case RIGHT_CONTROLLER:514514+ drvdata.gp_right_notify_en = cmd_rep->data[0];515515+ return 0;516516+ default:517517+ return -EINVAL;518518+ }519519+ break;520520+ case RUMBLE_MODE:521521+ switch (cmd_rep->device_type) {522522+ case LEFT_CONTROLLER:523523+ drvdata.gp_left_rumble_mode = cmd_rep->data[0];524524+ return 0;525525+ case RIGHT_CONTROLLER:526526+ drvdata.gp_right_rumble_mode = cmd_rep->data[0];527527+ return 0;528528+ default:529529+ return -EINVAL;530530+ }531531+ case TP_VIBRATION_ENABLE:532532+ drvdata.tp_vibration_en = cmd_rep->data[0];533533+ return 0;534534+ case TP_VIBRATION_INTENSITY:535535+ drvdata.tp_vibration_intensity = cmd_rep->data[0];536536+ return 0;537537+ }538538+ return -EINVAL;539539+}540540+541541+static int hid_go_fps_dpi_event(struct command_report *cmd_rep)542542+{543543+ if (cmd_rep->sub_cmd != FPS_MODE_DPI)544544+ return -EINVAL;545545+546546+ drvdata.mouse_dpi = get_unaligned_le32(cmd_rep->data);547547+548548+ return 0;549549+}550550+551551+static int hid_go_light_event(struct command_report *cmd_rep)552552+{553553+ struct led_classdev_mc *mc_cdev;554554+555555+ switch (cmd_rep->sub_cmd) {556556+ case LIGHT_MODE_SEL:557557+ drvdata.rgb_mode = cmd_rep->data[0];558558+ return 0;559559+ case LIGHT_PROFILE_SEL:560560+ drvdata.rgb_profile = cmd_rep->data[0];561561+ return 0;562562+ case USR_LIGHT_PROFILE_1:563563+ case USR_LIGHT_PROFILE_2:564564+ case USR_LIGHT_PROFILE_3:565565+ mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);566566+ drvdata.rgb_effect = cmd_rep->data[0];567567+ mc_cdev->subled_info[0].intensity = cmd_rep->data[1];568568+ mc_cdev->subled_info[1].intensity = cmd_rep->data[2];569569+ mc_cdev->subled_info[2].intensity = cmd_rep->data[3];570570+ drvdata.led_cdev->brightness = cmd_rep->data[4];571571+ drvdata.rgb_speed = 100 - cmd_rep->data[5];572572+ return 0;573573+ default:574574+ return -EINVAL;575575+ }576576+}577577+578578+static int hid_go_device_status_event(struct command_report *cmd_rep)579579+{580580+ switch (cmd_rep->device_type) {581581+ case LEFT_CONTROLLER:582582+ switch (cmd_rep->data[0]) {583583+ case CALDEV_GYROSCOPE:584584+ drvdata.gp_left_gyro_cal_status = cmd_rep->data[1];585585+ return 0;586586+ case CALDEV_JOYSTICK:587587+ drvdata.gp_left_joy_cal_status = cmd_rep->data[1];588588+ return 0;589589+ case CALDEV_TRIGGER:590590+ drvdata.gp_left_trigg_cal_status = cmd_rep->data[1];591591+ return 0;592592+ default:593593+ return -EINVAL;594594+ }595595+ break;596596+ case RIGHT_CONTROLLER:597597+ switch (cmd_rep->data[0]) {598598+ case CALDEV_GYROSCOPE:599599+ drvdata.gp_right_gyro_cal_status = cmd_rep->data[1];600600+ return 0;601601+ case CALDEV_JOYSTICK:602602+ drvdata.gp_right_joy_cal_status = cmd_rep->data[1];603603+ return 0;604604+ case CALDEV_TRIGGER:605605+ drvdata.gp_right_trigg_cal_status = cmd_rep->data[1];606606+ return 0;607607+ default:608608+ return -EINVAL;609609+ }610610+ break;611611+ default:612612+ return -EINVAL;613613+ }614614+}615615+616616+static int hid_go_os_mode_cfg_event(struct command_report *cmd_rep)617617+{618618+ switch (cmd_rep->sub_cmd) {619619+ case SET_OS_MODE:620620+ if (cmd_rep->data[0] != 1)621621+ return -EIO;622622+ return 0;623623+ case GET_OS_MODE:624624+ drvdata.os_mode = cmd_rep->data[0];625625+ return 0;626626+ default:627627+ return -EINVAL;628628+ }629629+}630630+631631+static int hid_go_set_event_return(struct command_report *cmd_rep)632632+{633633+ if (cmd_rep->data[0] != 0)634634+ return -EIO;635635+636636+ return 0;637637+}638638+639639+static int get_endpoint_address(struct hid_device *hdev)640640+{641641+ struct usb_interface *intf = to_usb_interface(hdev->dev.parent);642642+ struct usb_host_endpoint *ep;643643+644644+ if (!intf)645645+ return -ENODEV;646646+647647+ ep = intf->cur_altsetting->endpoint;648648+ if (!ep)649649+ return -ENODEV;650650+651651+ return ep->desc.bEndpointAddress;652652+}653653+654654+static int hid_go_raw_event(struct hid_device *hdev, struct hid_report *report,655655+ u8 *data, int size)656656+{657657+ struct command_report *cmd_rep;658658+ int ep, ret;659659+660660+ if (size != GO_PACKET_SIZE)661661+ goto passthrough;662662+663663+ ep = get_endpoint_address(hdev);664664+ if (ep != GO_GP_INTF_IN)665665+ goto passthrough;666666+667667+ cmd_rep = (struct command_report *)data;668668+669669+ switch (cmd_rep->id) {670670+ case MCU_CONFIG_DATA:671671+ switch (cmd_rep->cmd) {672672+ case GET_VERSION_DATA:673673+ ret = hid_go_version_event(cmd_rep);674674+ break;675675+ case GET_FEATURE_STATUS:676676+ ret = hid_go_feature_status_event(cmd_rep);677677+ break;678678+ case GET_MOTOR_CFG:679679+ ret = hid_go_motor_event(cmd_rep);680680+ break;681681+ case GET_DPI_CFG:682682+ ret = hid_go_fps_dpi_event(cmd_rep);683683+ break;684684+ case GET_RGB_CFG:685685+ ret = hid_go_light_event(cmd_rep);686686+ break;687687+ case GET_DEVICE_STATUS:688688+ ret = hid_go_device_status_event(cmd_rep);689689+ break;690690+ case SET_FEATURE_STATUS:691691+ case SET_MOTOR_CFG:692692+ case SET_DPI_CFG:693693+ case SET_RGB_CFG:694694+ case SET_TRIGGER_CFG:695695+ case SET_JOYSTICK_CFG:696696+ case SET_GYRO_CFG:697697+ ret = hid_go_set_event_return(cmd_rep);698698+ break;699699+ default:700700+ ret = -EINVAL;701701+ break;702702+ }703703+ break;704704+ case OS_MODE_DATA:705705+ ret = hid_go_os_mode_cfg_event(cmd_rep);706706+ break;707707+ default:708708+ goto passthrough;709709+ }710710+ dev_dbg(&hdev->dev, "Rx data as raw input report: [%*ph]\n",711711+ GO_PACKET_SIZE, data);712712+713713+ complete(&drvdata.send_cmd_complete);714714+ return ret;715715+716716+passthrough:717717+ /* Forward other HID reports so they generate events */718718+ hid_input_report(hdev, HID_INPUT_REPORT, data, size, 1);719719+ return 0;720720+}721721+722722+static int mcu_property_out(struct hid_device *hdev, u8 id, u8 command,723723+ u8 index, enum dev_type device, u8 *data, size_t len)724724+{725725+ unsigned char *dmabuf __free(kfree) = NULL;726726+ u8 header[] = { GO_OUTPUT_REPORT_ID, id, command, index, device };727727+ size_t header_size = ARRAY_SIZE(header);728728+ int timeout = 50;729729+ int ret;730730+731731+ if (header_size + len > GO_PACKET_SIZE)732732+ return -EINVAL;733733+734734+ guard(mutex)(&drvdata.cfg_mutex);735735+ /* We can't use a devm_alloc reusable buffer without side effects during suspend */736736+ dmabuf = kzalloc(GO_PACKET_SIZE, GFP_KERNEL);737737+ if (!dmabuf)738738+ return -ENOMEM;739739+740740+ memcpy(dmabuf, header, header_size);741741+ memcpy(dmabuf + header_size, data, len);742742+743743+ dev_dbg(&hdev->dev, "Send data as raw output report: [%*ph]\n",744744+ GO_PACKET_SIZE, dmabuf);745745+746746+ ret = hid_hw_output_report(hdev, dmabuf, GO_PACKET_SIZE);747747+ if (ret < 0)748748+ return ret;749749+750750+ ret = ret == GO_PACKET_SIZE ? 0 : -EINVAL;751751+ if (ret)752752+ return ret;753753+754754+ ret = wait_for_completion_interruptible_timeout(&drvdata.send_cmd_complete,755755+ msecs_to_jiffies(timeout));756756+757757+ if (ret == 0) /* timeout occurred */758758+ ret = -EBUSY;759759+760760+ reinit_completion(&drvdata.send_cmd_complete);761761+ return 0;762762+}763763+764764+static ssize_t version_show(struct device *dev, struct device_attribute *attr,765765+ char *buf, enum version_data_index index,766766+ enum dev_type device_type)767767+{768768+ ssize_t count = 0;769769+ int ret;770770+771771+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,772772+ index, device_type, NULL, 0);773773+ if (ret)774774+ return ret;775775+776776+ switch (index) {777777+ case PRODUCT_VERSION:778778+ switch (device_type) {779779+ case USB_MCU:780780+ count = sysfs_emit(buf, "%x\n",781781+ drvdata.mcu_version_product);782782+ break;783783+ case TX_DONGLE:784784+ count = sysfs_emit(buf, "%x\n",785785+ drvdata.tx_dongle_version_product);786786+ break;787787+ case LEFT_CONTROLLER:788788+ count = sysfs_emit(buf, "%x\n",789789+ drvdata.gp_left_version_product);790790+ break;791791+ case RIGHT_CONTROLLER:792792+ count = sysfs_emit(buf, "%x\n",793793+ drvdata.gp_right_version_product);794794+ break;795795+ default:796796+ return -EINVAL;797797+ }798798+ break;799799+ case PROTOCOL_VERSION:800800+ switch (device_type) {801801+ case USB_MCU:802802+ count = sysfs_emit(buf, "%x\n",803803+ drvdata.mcu_version_protocol);804804+ break;805805+ case TX_DONGLE:806806+ count = sysfs_emit(buf, "%x\n",807807+ drvdata.tx_dongle_version_protocol);808808+ break;809809+ case LEFT_CONTROLLER:810810+ count = sysfs_emit(buf, "%x\n",811811+ drvdata.gp_left_version_protocol);812812+ break;813813+ case RIGHT_CONTROLLER:814814+ count = sysfs_emit(buf, "%x\n",815815+ drvdata.gp_right_version_protocol);816816+ break;817817+ default:818818+ return -EINVAL;819819+ }820820+ break;821821+ case FIRMWARE_VERSION:822822+ switch (device_type) {823823+ case USB_MCU:824824+ count = sysfs_emit(buf, "%x\n",825825+ drvdata.mcu_version_firmware);826826+ break;827827+ case TX_DONGLE:828828+ count = sysfs_emit(buf, "%x\n",829829+ drvdata.tx_dongle_version_firmware);830830+ break;831831+ case LEFT_CONTROLLER:832832+ count = sysfs_emit(buf, "%x\n",833833+ drvdata.gp_left_version_firmware);834834+ break;835835+ case RIGHT_CONTROLLER:836836+ count = sysfs_emit(buf, "%x\n",837837+ drvdata.gp_right_version_firmware);838838+ break;839839+ default:840840+ return -EINVAL;841841+ }842842+ break;843843+ case HARDWARE_VERSION:844844+ switch (device_type) {845845+ case USB_MCU:846846+ count = sysfs_emit(buf, "%x\n",847847+ drvdata.mcu_version_hardware);848848+ break;849849+ case TX_DONGLE:850850+ count = sysfs_emit(buf, "%x\n",851851+ drvdata.tx_dongle_version_hardware);852852+ break;853853+ case LEFT_CONTROLLER:854854+ count = sysfs_emit(buf, "%x\n",855855+ drvdata.gp_left_version_hardware);856856+ break;857857+ case RIGHT_CONTROLLER:858858+ count = sysfs_emit(buf, "%x\n",859859+ drvdata.gp_right_version_hardware);860860+ break;861861+ default:862862+ return -EINVAL;863863+ }864864+ break;865865+ case HARDWARE_GENERATION:866866+ switch (device_type) {867867+ case USB_MCU:868868+ count = sysfs_emit(buf, "%x\n",869869+ drvdata.mcu_version_gen);870870+ break;871871+ case TX_DONGLE:872872+ count = sysfs_emit(buf, "%x\n",873873+ drvdata.tx_dongle_version_gen);874874+ break;875875+ case LEFT_CONTROLLER:876876+ count = sysfs_emit(buf, "%x\n",877877+ drvdata.gp_left_version_gen);878878+ break;879879+ case RIGHT_CONTROLLER:880880+ count = sysfs_emit(buf, "%x\n",881881+ drvdata.gp_right_version_gen);882882+ break;883883+ default:884884+ return -EINVAL;885885+ }886886+ break;887887+ }888888+889889+ return count;890890+}891891+892892+static ssize_t feature_status_store(struct device *dev,893893+ struct device_attribute *attr,894894+ const char *buf, size_t count,895895+ enum feature_status_index index,896896+ enum dev_type device_type)897897+{898898+ size_t size = 1;899899+ u8 val = 0;900900+ int ret;901901+902902+ switch (index) {903903+ case FEATURE_IMU_ENABLE:904904+ case FEATURE_IMU_BYPASS:905905+ case FEATURE_LIGHT_ENABLE:906906+ case FEATURE_TOUCHPAD_ENABLE:907907+ ret = sysfs_match_string(enabled_status_text, buf);908908+ val = ret;909909+ break;910910+ case FEATURE_AUTO_SLEEP_TIME:911911+ ret = kstrtou8(buf, 10, &val);912912+ break;913913+ case FEATURE_RESET_GAMEPAD:914914+ ret = kstrtou8(buf, 10, &val);915915+ if (val != GO_GP_RESET_SUCCESS)916916+ return -EINVAL;917917+ break;918918+ case FEATURE_FPS_SWITCH_STATUS:919919+ ret = sysfs_match_string(fps_switch_text, buf);920920+ val = ret;921921+ break;922922+ case FEATURE_GAMEPAD_MODE:923923+ ret = sysfs_match_string(gamepad_mode_text, buf);924924+ val = ret;925925+ break;926926+ default:927927+ return -EINVAL;928928+ }929929+930930+ if (ret < 0)931931+ return ret;932932+933933+ if (!val)934934+ size = 0;935935+936936+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA,937937+ SET_FEATURE_STATUS, index, device_type, &val,938938+ size);939939+ if (ret < 0)940940+ return ret;941941+942942+ return count;943943+}944944+945945+static ssize_t feature_status_show(struct device *dev,946946+ struct device_attribute *attr, char *buf,947947+ enum feature_status_index index,948948+ enum dev_type device_type)949949+{950950+ ssize_t count = 0;951951+ int ret;952952+ u8 i;953953+954954+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA,955955+ GET_FEATURE_STATUS, index, device_type, NULL, 0);956956+ if (ret)957957+ return ret;958958+959959+ switch (index) {960960+ case FEATURE_IMU_ENABLE:961961+ switch (device_type) {962962+ case LEFT_CONTROLLER:963963+ i = drvdata.imu_left_sensor_en;964964+ break;965965+ case RIGHT_CONTROLLER:966966+ i = drvdata.imu_right_sensor_en;967967+ break;968968+ default:969969+ return -EINVAL;970970+ }971971+ if (i >= ARRAY_SIZE(enabled_status_text))972972+ return -EINVAL;973973+974974+ count = sysfs_emit(buf, "%s\n", enabled_status_text[i]);975975+ break;976976+ case FEATURE_IMU_BYPASS:977977+ switch (device_type) {978978+ case LEFT_CONTROLLER:979979+ i = drvdata.imu_left_bypass_en;980980+ break;981981+ case RIGHT_CONTROLLER:982982+ i = drvdata.imu_right_bypass_en;983983+ break;984984+ default:985985+ return -EINVAL;986986+ }987987+ if (i >= ARRAY_SIZE(enabled_status_text))988988+ return -EINVAL;989989+990990+ count = sysfs_emit(buf, "%s\n", enabled_status_text[i]);991991+ break;992992+ case FEATURE_LIGHT_ENABLE:993993+ i = drvdata.rgb_en;994994+ if (i >= ARRAY_SIZE(enabled_status_text))995995+ return -EINVAL;996996+997997+ count = sysfs_emit(buf, "%s\n", enabled_status_text[i]);998998+ break;999999+ case FEATURE_TOUCHPAD_ENABLE:10001000+ i = drvdata.tp_en;10011001+ if (i >= ARRAY_SIZE(enabled_status_text))10021002+ return -EINVAL;10031003+10041004+ count = sysfs_emit(buf, "%s\n", enabled_status_text[i]);10051005+ break;10061006+ case FEATURE_AUTO_SLEEP_TIME:10071007+ switch (device_type) {10081008+ case LEFT_CONTROLLER:10091009+ i = drvdata.gp_left_auto_sleep_time;10101010+ break;10111011+ case RIGHT_CONTROLLER:10121012+ i = drvdata.gp_right_auto_sleep_time;10131013+ break;10141014+ default:10151015+ return -EINVAL;10161016+ }10171017+ count = sysfs_emit(buf, "%u\n", i);10181018+ break;10191019+ case FEATURE_FPS_SWITCH_STATUS:10201020+ i = drvdata.fps_mode;10211021+ if (i >= ARRAY_SIZE(fps_switch_text))10221022+ return -EINVAL;10231023+10241024+ count = sysfs_emit(buf, "%s\n", fps_switch_text[i]);10251025+ break;10261026+ case FEATURE_GAMEPAD_MODE:10271027+ i = drvdata.gp_mode;10281028+ if (i >= ARRAY_SIZE(gamepad_mode_text))10291029+ return -EINVAL;10301030+10311031+ count = sysfs_emit(buf, "%s\n", gamepad_mode_text[i]);10321032+ break;10331033+ default:10341034+ return -EINVAL;10351035+ }10361036+10371037+ return count;10381038+}10391039+10401040+static ssize_t feature_status_options(struct device *dev,10411041+ struct device_attribute *attr, char *buf,10421042+ enum feature_status_index index)10431043+{10441044+ ssize_t count = 0;10451045+ unsigned int i;10461046+10471047+ switch (index) {10481048+ case FEATURE_IMU_ENABLE:10491049+ case FEATURE_IMU_BYPASS:10501050+ case FEATURE_LIGHT_ENABLE:10511051+ case FEATURE_TOUCHPAD_ENABLE:10521052+ for (i = 1; i < ARRAY_SIZE(enabled_status_text); i++) {10531053+ count += sysfs_emit_at(buf, count, "%s ",10541054+ enabled_status_text[i]);10551055+ }10561056+ break;10571057+ case FEATURE_AUTO_SLEEP_TIME:10581058+ return sysfs_emit(buf, "0-255\n");10591059+ case FEATURE_FPS_SWITCH_STATUS:10601060+ for (i = 1; i < ARRAY_SIZE(fps_switch_text); i++) {10611061+ count += sysfs_emit_at(buf, count, "%s ",10621062+ fps_switch_text[i]);10631063+ }10641064+ break;10651065+ case FEATURE_GAMEPAD_MODE:10661066+ for (i = 1; i < ARRAY_SIZE(gamepad_mode_text); i++) {10671067+ count += sysfs_emit_at(buf, count, "%s ",10681068+ gamepad_mode_text[i]);10691069+ }10701070+ break;10711071+ default:10721072+ return -EINVAL;10731073+ }10741074+10751075+ if (count)10761076+ buf[count - 1] = '\n';10771077+10781078+ return count;10791079+}10801080+10811081+static ssize_t motor_config_store(struct device *dev,10821082+ struct device_attribute *attr,10831083+ const char *buf, size_t count,10841084+ enum motor_cfg_index index,10851085+ enum dev_type device_type)10861086+{10871087+ size_t size = 1;10881088+ u8 val = 0;10891089+ int ret;10901090+10911091+ switch (index) {10921092+ case MOTOR_CFG_ALL:10931093+ return -EINVAL;10941094+ case MOTOR_INTENSITY:10951095+ ret = sysfs_match_string(intensity_text, buf);10961096+ val = ret;10971097+ break;10981098+ case VIBRATION_NOTIFY_ENABLE:10991099+ ret = sysfs_match_string(enabled_status_text, buf);11001100+ val = ret;11011101+ break;11021102+ case RUMBLE_MODE:11031103+ ret = sysfs_match_string(rumble_mode_text, buf);11041104+ val = ret;11051105+ break;11061106+ case TP_VIBRATION_ENABLE:11071107+ ret = sysfs_match_string(enabled_status_text, buf);11081108+ val = ret;11091109+ break;11101110+ case TP_VIBRATION_INTENSITY:11111111+ ret = sysfs_match_string(intensity_text, buf);11121112+ val = ret;11131113+ break;11141114+ }11151115+11161116+ if (ret < 0)11171117+ return ret;11181118+11191119+ if (!val)11201120+ size = 0;11211121+11221122+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, SET_MOTOR_CFG,11231123+ index, device_type, &val, size);11241124+ if (ret < 0)11251125+ return ret;11261126+11271127+ return count;11281128+}11291129+11301130+static ssize_t motor_config_show(struct device *dev,11311131+ struct device_attribute *attr, char *buf,11321132+ enum motor_cfg_index index,11331133+ enum dev_type device_type)11341134+{11351135+ ssize_t count = 0;11361136+ int ret;11371137+ u8 i;11381138+11391139+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_MOTOR_CFG,11401140+ index, device_type, NULL, 0);11411141+ if (ret)11421142+ return ret;11431143+11441144+ switch (index) {11451145+ case MOTOR_CFG_ALL:11461146+ return -EINVAL;11471147+ case MOTOR_INTENSITY:11481148+ i = drvdata.gp_rumble_intensity;11491149+ if (i >= ARRAY_SIZE(intensity_text))11501150+ return -EINVAL;11511151+11521152+ count = sysfs_emit(buf, "%s\n", intensity_text[i]);11531153+ break;11541154+ case VIBRATION_NOTIFY_ENABLE:11551155+ switch (device_type) {11561156+ case LEFT_CONTROLLER:11571157+ i = drvdata.gp_left_notify_en;11581158+ break;11591159+ case RIGHT_CONTROLLER:11601160+ i = drvdata.gp_right_notify_en;11611161+ break;11621162+ default:11631163+ return -EINVAL;11641164+ }11651165+ if (i >= ARRAY_SIZE(enabled_status_text))11661166+ return -EINVAL;11671167+11681168+ count = sysfs_emit(buf, "%s\n", enabled_status_text[i]);11691169+ break;11701170+ case RUMBLE_MODE:11711171+ switch (device_type) {11721172+ case LEFT_CONTROLLER:11731173+ i = drvdata.gp_left_rumble_mode;11741174+ break;11751175+ case RIGHT_CONTROLLER:11761176+ i = drvdata.gp_right_rumble_mode;11771177+ break;11781178+ default:11791179+ return -EINVAL;11801180+ }11811181+ if (i >= ARRAY_SIZE(rumble_mode_text))11821182+ return -EINVAL;11831183+11841184+ count = sysfs_emit(buf, "%s\n", rumble_mode_text[i]);11851185+ break;11861186+ case TP_VIBRATION_ENABLE:11871187+ i = drvdata.tp_vibration_en;11881188+ if (i >= ARRAY_SIZE(enabled_status_text))11891189+ return -EINVAL;11901190+11911191+ count = sysfs_emit(buf, "%s\n", enabled_status_text[i]);11921192+ break;11931193+ case TP_VIBRATION_INTENSITY:11941194+ i = drvdata.tp_vibration_intensity;11951195+ if (i >= ARRAY_SIZE(intensity_text))11961196+ return -EINVAL;11971197+11981198+ count = sysfs_emit(buf, "%s\n", intensity_text[i]);11991199+ break;12001200+ }12011201+12021202+ return count;12031203+}12041204+12051205+static ssize_t motor_config_options(struct device *dev,12061206+ struct device_attribute *attr, char *buf,12071207+ enum motor_cfg_index index)12081208+{12091209+ ssize_t count = 0;12101210+ unsigned int i;12111211+12121212+ switch (index) {12131213+ case MOTOR_CFG_ALL:12141214+ break;12151215+ case RUMBLE_MODE:12161216+ for (i = 1; i < ARRAY_SIZE(rumble_mode_text); i++) {12171217+ count += sysfs_emit_at(buf, count, "%s ",12181218+ rumble_mode_text[i]);12191219+ }12201220+ break;12211221+ case MOTOR_INTENSITY:12221222+ case TP_VIBRATION_INTENSITY:12231223+ for (i = 1; i < ARRAY_SIZE(intensity_text); i++) {12241224+ count += sysfs_emit_at(buf, count, "%s ",12251225+ intensity_text[i]);12261226+ }12271227+ break;12281228+ case VIBRATION_NOTIFY_ENABLE:12291229+ case TP_VIBRATION_ENABLE:12301230+ for (i = 1; i < ARRAY_SIZE(enabled_status_text); i++) {12311231+ count += sysfs_emit_at(buf, count, "%s ",12321232+ enabled_status_text[i]);12331233+ }12341234+ break;12351235+ }12361236+12371237+ if (count)12381238+ buf[count - 1] = '\n';12391239+12401240+ return count;12411241+}12421242+12431243+static ssize_t fps_mode_dpi_store(struct device *dev,12441244+ struct device_attribute *attr,12451245+ const char *buf, size_t count)12461246+12471247+{12481248+ size_t size = 4;12491249+ u32 value;12501250+ u8 val[4];12511251+ int ret;12521252+12531253+ ret = kstrtou32(buf, 10, &value);12541254+ if (ret)12551255+ return ret;12561256+12571257+ if (value != 500 && value != 800 && value != 1200 && value != 1800)12581258+ return -EINVAL;12591259+12601260+ put_unaligned_le32(value, val);12611261+12621262+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, SET_DPI_CFG,12631263+ FPS_MODE_DPI, UNSPECIFIED, val, size);12641264+ if (ret < 0)12651265+ return ret;12661266+12671267+ return count;12681268+}12691269+12701270+static ssize_t fps_mode_dpi_show(struct device *dev,12711271+ struct device_attribute *attr, char *buf)12721272+{12731273+ int ret;12741274+12751275+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_DPI_CFG,12761276+ FPS_MODE_DPI, UNSPECIFIED, NULL, 0);12771277+ if (ret < 0)12781278+ return ret;12791279+12801280+ return sysfs_emit(buf, "%u\n", drvdata.mouse_dpi);12811281+}12821282+12831283+static ssize_t fps_mode_dpi_index_show(struct device *dev,12841284+ struct device_attribute *attr, char *buf)12851285+{12861286+ return sysfs_emit(buf, "500 800 1200 1800\n");12871287+}12881288+12891289+static ssize_t device_status_show(struct device *dev,12901290+ struct device_attribute *attr, char *buf,12911291+ enum device_status_index index,12921292+ enum dev_type device_type,12931293+ enum cal_device_type cal_type)12941294+{12951295+ u8 i;12961296+12971297+ switch (index) {12981298+ case GET_CAL_STATUS:12991299+ switch (device_type) {13001300+ case LEFT_CONTROLLER:13011301+ switch (cal_type) {13021302+ case CALDEV_GYROSCOPE:13031303+ i = drvdata.gp_left_gyro_cal_status;13041304+ break;13051305+ case CALDEV_JOYSTICK:13061306+ i = drvdata.gp_left_joy_cal_status;13071307+ break;13081308+ case CALDEV_TRIGGER:13091309+ i = drvdata.gp_left_trigg_cal_status;13101310+ break;13111311+ default:13121312+ return -EINVAL;13131313+ }13141314+ break;13151315+ case RIGHT_CONTROLLER:13161316+ switch (cal_type) {13171317+ case CALDEV_GYROSCOPE:13181318+ i = drvdata.gp_right_gyro_cal_status;13191319+ break;13201320+ case CALDEV_JOYSTICK:13211321+ i = drvdata.gp_right_joy_cal_status;13221322+ break;13231323+ case CALDEV_TRIGGER:13241324+ i = drvdata.gp_right_trigg_cal_status;13251325+ break;13261326+ default:13271327+ return -EINVAL;13281328+ }13291329+ break;13301330+ default:13311331+ return -EINVAL;13321332+ }13331333+ break;13341334+ default:13351335+ return -EINVAL;13361336+ }13371337+13381338+ if (i >= ARRAY_SIZE(cal_status_text))13391339+ return -EINVAL;13401340+13411341+ return sysfs_emit(buf, "%s\n", cal_status_text[i]);13421342+}13431343+13441344+static ssize_t calibrate_config_store(struct device *dev,13451345+ struct device_attribute *attr,13461346+ const char *buf, u8 cmd, u8 sub_cmd,13471347+ size_t count, enum dev_type device_type)13481348+{13491349+ size_t size = 1;13501350+ u8 val = 0;13511351+ int ret;13521352+13531353+ ret = sysfs_match_string(cal_enabled_text, buf);13541354+ if (ret < 0)13551355+ return ret;13561356+13571357+ val = ret;13581358+ if (!val)13591359+ size = 0;13601360+13611361+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, cmd, sub_cmd,13621362+ device_type, &val, size);13631363+ if (ret < 0)13641364+ return ret;13651365+13661366+ return count;13671367+}13681368+13691369+static ssize_t calibrate_config_options(struct device *dev,13701370+ struct device_attribute *attr,13711371+ char *buf)13721372+{13731373+ ssize_t count = 0;13741374+ unsigned int i;13751375+13761376+ for (i = 1; i < ARRAY_SIZE(cal_enabled_text); i++)13771377+ count += sysfs_emit_at(buf, count, "%s ", cal_enabled_text[i]);13781378+13791379+ buf[count - 1] = '\n';13801380+13811381+ return count;13821382+}13831383+13841384+static ssize_t os_mode_store(struct device *dev, struct device_attribute *attr,13851385+ const char *buf, size_t count)13861386+{13871387+ size_t size = 1;13881388+ int ret;13891389+ u8 val;13901390+13911391+ ret = sysfs_match_string(os_mode_text, buf);13921392+ if (ret <= 0)13931393+ return ret;13941394+13951395+ val = ret;13961396+ ret = mcu_property_out(drvdata.hdev, OS_MODE_DATA, FEATURE_OS_MODE,13971397+ SET_OS_MODE, USB_MCU, &val, size);13981398+ if (ret < 0)13991399+ return ret;14001400+14011401+ drvdata.os_mode = val;14021402+14031403+ return count;14041404+}14051405+14061406+static ssize_t os_mode_show(struct device *dev, struct device_attribute *attr,14071407+ char *buf)14081408+{14091409+ ssize_t count = 0;14101410+ int ret;14111411+ u8 i;14121412+14131413+ ret = mcu_property_out(drvdata.hdev, OS_MODE_DATA, FEATURE_OS_MODE,14141414+ GET_OS_MODE, USB_MCU, NULL, 0);14151415+ if (ret)14161416+ return ret;14171417+14181418+ i = drvdata.os_mode;14191419+ if (i >= ARRAY_SIZE(os_mode_text))14201420+ return -EINVAL;14211421+14221422+ count = sysfs_emit(buf, "%s\n", os_mode_text[i]);14231423+14241424+ return count;14251425+}14261426+14271427+static ssize_t os_mode_index_show(struct device *dev,14281428+ struct device_attribute *attr, char *buf)14291429+{14301430+ ssize_t count = 0;14311431+ unsigned int i;14321432+14331433+ for (i = 1; i < ARRAY_SIZE(os_mode_text); i++)14341434+ count += sysfs_emit_at(buf, count, "%s ", os_mode_text[i]);14351435+14361436+ if (count)14371437+ buf[count - 1] = '\n';14381438+14391439+ return count;14401440+}14411441+14421442+static int rgb_cfg_call(struct hid_device *hdev, enum mcu_command_index cmd,14431443+ enum rgb_config_index index, u8 *val, size_t size)14441444+{14451445+ if (cmd != SET_RGB_CFG && cmd != GET_RGB_CFG)14461446+ return -EINVAL;14471447+14481448+ if (index < LIGHT_CFG_ALL || index > USR_LIGHT_PROFILE_3)14491449+ return -EINVAL;14501450+14511451+ return mcu_property_out(hdev, MCU_CONFIG_DATA, cmd, index, UNSPECIFIED,14521452+ val, size);14531453+}14541454+14551455+static int rgb_attr_show(void)14561456+{14571457+ enum rgb_config_index index;14581458+14591459+ index = drvdata.rgb_profile + 3;14601460+14611461+ return rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, index, NULL, 0);14621462+}14631463+14641464+static ssize_t rgb_effect_store(struct device *dev,14651465+ struct device_attribute *attr, const char *buf,14661466+ size_t count)14671467+{14681468+ struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);14691469+ enum rgb_config_index index;14701470+ u8 effect;14711471+ int ret;14721472+14731473+ ret = sysfs_match_string(rgb_effect_text, buf);14741474+ if (ret < 0)14751475+ return ret;14761476+14771477+ effect = ret;14781478+ index = drvdata.rgb_profile + 3;14791479+ u8 rgb_profile[6] = { effect,14801480+ mc_cdev->subled_info[0].intensity,14811481+ mc_cdev->subled_info[1].intensity,14821482+ mc_cdev->subled_info[2].intensity,14831483+ drvdata.led_cdev->brightness,14841484+ drvdata.rgb_speed };14851485+14861486+ ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6);14871487+ if (ret)14881488+ return ret;14891489+14901490+ drvdata.rgb_effect = effect;14911491+ return count;14921492+}14931493+14941494+static ssize_t rgb_effect_show(struct device *dev,14951495+ struct device_attribute *attr, char *buf)14961496+{14971497+ int ret;14981498+14991499+ ret = rgb_attr_show();15001500+ if (ret)15011501+ return ret;15021502+15031503+ if (drvdata.rgb_effect >= ARRAY_SIZE(rgb_effect_text))15041504+ return -EINVAL;15051505+15061506+ return sysfs_emit(buf, "%s\n", rgb_effect_text[drvdata.rgb_effect]);15071507+}15081508+15091509+static ssize_t rgb_effect_index_show(struct device *dev,15101510+ struct device_attribute *attr, char *buf)15111511+{15121512+ ssize_t count = 0;15131513+ unsigned int i;15141514+15151515+ for (i = 0; i < ARRAY_SIZE(rgb_effect_text); i++)15161516+ count += sysfs_emit_at(buf, count, "%s ", rgb_effect_text[i]);15171517+15181518+ if (count)15191519+ buf[count - 1] = '\n';15201520+15211521+ return count;15221522+}15231523+15241524+static ssize_t rgb_speed_store(struct device *dev,15251525+ struct device_attribute *attr, const char *buf,15261526+ size_t count)15271527+{15281528+ struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);15291529+ enum rgb_config_index index;15301530+ int val = 0;15311531+ int ret;15321532+15331533+ ret = kstrtoint(buf, 10, &val);15341534+ if (ret)15351535+ return ret;15361536+15371537+ if (val < 0 || val > 100)15381538+ return -EINVAL;15391539+15401540+ /* This is a delay setting, invert logic for consistency with other drivers */15411541+ val = 100 - val;15421542+15431543+ index = drvdata.rgb_profile + 3;15441544+ u8 rgb_profile[6] = { drvdata.rgb_effect,15451545+ mc_cdev->subled_info[0].intensity,15461546+ mc_cdev->subled_info[1].intensity,15471547+ mc_cdev->subled_info[2].intensity,15481548+ drvdata.led_cdev->brightness,15491549+ val };15501550+15511551+ ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6);15521552+ if (ret)15531553+ return ret;15541554+15551555+ drvdata.rgb_speed = val;15561556+15571557+ return count;15581558+}15591559+15601560+static ssize_t rgb_speed_show(struct device *dev, struct device_attribute *attr,15611561+ char *buf)15621562+{15631563+ int ret, val;15641564+15651565+ ret = rgb_attr_show();15661566+ if (ret)15671567+ return ret;15681568+15691569+ if (drvdata.rgb_speed > 100)15701570+ return -EINVAL;15711571+15721572+ val = drvdata.rgb_speed;15731573+15741574+ return sysfs_emit(buf, "%hhu\n", val);15751575+}15761576+15771577+static ssize_t rgb_speed_range_show(struct device *dev,15781578+ struct device_attribute *attr, char *buf)15791579+{15801580+ return sysfs_emit(buf, "0-100\n");15811581+}15821582+15831583+static ssize_t rgb_mode_store(struct device *dev, struct device_attribute *attr,15841584+ const char *buf, size_t count)15851585+{15861586+ int ret;15871587+ u8 val;15881588+15891589+ ret = sysfs_match_string(rgb_mode_text, buf);15901590+ if (ret <= 0)15911591+ return ret;15921592+15931593+ val = ret;15941594+15951595+ ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, LIGHT_MODE_SEL, &val, 1);15961596+ if (ret)15971597+ return ret;15981598+15991599+ drvdata.rgb_mode = val;16001600+16011601+ return count;16021602+}16031603+16041604+static ssize_t rgb_mode_show(struct device *dev, struct device_attribute *attr,16051605+ char *buf)16061606+{16071607+ int ret;16081608+16091609+ ret = rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, LIGHT_MODE_SEL, NULL, 0);16101610+ if (ret)16111611+ return ret;16121612+16131613+ if (drvdata.rgb_mode >= ARRAY_SIZE(rgb_mode_text))16141614+ return -EINVAL;16151615+16161616+ return sysfs_emit(buf, "%s\n", rgb_mode_text[drvdata.rgb_mode]);16171617+}16181618+16191619+static ssize_t rgb_mode_index_show(struct device *dev,16201620+ struct device_attribute *attr, char *buf)16211621+{16221622+ ssize_t count = 0;16231623+ unsigned int i;16241624+16251625+ for (i = 1; i < ARRAY_SIZE(rgb_mode_text); i++)16261626+ count += sysfs_emit_at(buf, count, "%s ", rgb_mode_text[i]);16271627+16281628+ if (count)16291629+ buf[count - 1] = '\n';16301630+16311631+ return count;16321632+}16331633+16341634+static ssize_t rgb_profile_store(struct device *dev,16351635+ struct device_attribute *attr, const char *buf,16361636+ size_t count)16371637+{16381638+ size_t size = 1;16391639+ int ret;16401640+ u8 val;16411641+16421642+ ret = kstrtou8(buf, 10, &val);16431643+ if (ret < 0)16441644+ return ret;16451645+16461646+ if (val < 1 || val > 3)16471647+ return -EINVAL;16481648+16491649+ ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, LIGHT_PROFILE_SEL, &val, size);16501650+ if (ret)16511651+ return ret;16521652+16531653+ drvdata.rgb_profile = val;16541654+16551655+ return count;16561656+}16571657+16581658+static ssize_t rgb_profile_show(struct device *dev,16591659+ struct device_attribute *attr, char *buf)16601660+{16611661+ int ret;16621662+16631663+ ret = rgb_cfg_call(drvdata.hdev, GET_RGB_CFG, LIGHT_PROFILE_SEL, NULL, 0);16641664+ if (ret)16651665+ return ret;16661666+16671667+ if (drvdata.rgb_profile < 1 || drvdata.rgb_profile > 3)16681668+ return -EINVAL;16691669+16701670+ return sysfs_emit(buf, "%hhu\n", drvdata.rgb_profile);16711671+}16721672+16731673+static ssize_t rgb_profile_range_show(struct device *dev,16741674+ struct device_attribute *attr, char *buf)16751675+{16761676+ return sysfs_emit(buf, "1-3\n");16771677+}16781678+16791679+static void hid_go_brightness_set(struct led_classdev *led_cdev,16801680+ enum led_brightness brightness)16811681+{16821682+ struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(drvdata.led_cdev);16831683+ enum rgb_config_index index;16841684+ int ret;16851685+16861686+ if (brightness > led_cdev->max_brightness) {16871687+ dev_err(led_cdev->dev, "Invalid argument\n");16881688+ return;16891689+ }16901690+16911691+ index = drvdata.rgb_profile + 3;16921692+ u8 rgb_profile[6] = { drvdata.rgb_effect,16931693+ mc_cdev->subled_info[0].intensity,16941694+ mc_cdev->subled_info[1].intensity,16951695+ mc_cdev->subled_info[2].intensity,16961696+ brightness,16971697+ drvdata.rgb_speed };16981698+16991699+ ret = rgb_cfg_call(drvdata.hdev, SET_RGB_CFG, index, rgb_profile, 6);17001700+ switch (ret) {17011701+ case 0:17021702+ led_cdev->brightness = brightness;17031703+ break;17041704+ case -ENODEV: /* during switch to IAP -ENODEV is expected */17051705+ case -ENOSYS: /* during rmmod -ENOSYS is expected */17061706+ dev_dbg(led_cdev->dev, "Failed to write RGB profile: %i\n", ret);17071707+ break;17081708+ default:17091709+ dev_err(led_cdev->dev, "Failed to write RGB profile: %i\n", ret);17101710+ }17111711+}17121712+17131713+#define LEGO_DEVICE_ATTR_RW(_name, _attrname, _dtype, _rtype, _group) \17141714+ static ssize_t _name##_store(struct device *dev, \17151715+ struct device_attribute *attr, \17161716+ const char *buf, size_t count) \17171717+ { \17181718+ return _group##_store(dev, attr, buf, count, _name.index, \17191719+ _dtype); \17201720+ } \17211721+ static ssize_t _name##_show(struct device *dev, \17221722+ struct device_attribute *attr, char *buf) \17231723+ { \17241724+ return _group##_show(dev, attr, buf, _name.index, _dtype); \17251725+ } \17261726+ static ssize_t _name##_##_rtype##_show( \17271727+ struct device *dev, struct device_attribute *attr, char *buf) \17281728+ { \17291729+ return _group##_options(dev, attr, buf, _name.index); \17301730+ } \17311731+ static DEVICE_ATTR_RW_NAMED(_name, _attrname)17321732+17331733+#define LEGO_DEVICE_ATTR_WO(_name, _attrname, _dtype, _group) \17341734+ static ssize_t _name##_store(struct device *dev, \17351735+ struct device_attribute *attr, \17361736+ const char *buf, size_t count) \17371737+ { \17381738+ return _group##_store(dev, attr, buf, count, _name.index, \17391739+ _dtype); \17401740+ } \17411741+ static DEVICE_ATTR_WO_NAMED(_name, _attrname)17421742+17431743+#define LEGO_DEVICE_ATTR_RO(_name, _attrname, _dtype, _group) \17441744+ static ssize_t _name##_show(struct device *dev, \17451745+ struct device_attribute *attr, char *buf) \17461746+ { \17471747+ return _group##_show(dev, attr, buf, _name.index, _dtype); \17481748+ } \17491749+ static DEVICE_ATTR_RO_NAMED(_name, _attrname)17501750+17511751+#define LEGO_CAL_DEVICE_ATTR(_name, _attrname, _scmd, _dtype, _rtype) \17521752+ static ssize_t _name##_store(struct device *dev, \17531753+ struct device_attribute *attr, \17541754+ const char *buf, size_t count) \17551755+ { \17561756+ return calibrate_config_store(dev, attr, buf, _name.index, \17571757+ _scmd, count, _dtype); \17581758+ } \17591759+ static ssize_t _name##_##_rtype##_show( \17601760+ struct device *dev, struct device_attribute *attr, char *buf) \17611761+ { \17621762+ return calibrate_config_options(dev, attr, buf); \17631763+ } \17641764+ static DEVICE_ATTR_WO_NAMED(_name, _attrname)17651765+17661766+#define LEGO_DEVICE_STATUS_ATTR(_name, _attrname, _scmd, _dtype) \17671767+ static ssize_t _name##_show(struct device *dev, \17681768+ struct device_attribute *attr, char *buf) \17691769+ { \17701770+ return device_status_show(dev, attr, buf, _name.index, _scmd, \17711771+ _dtype); \17721772+ } \17731773+ static DEVICE_ATTR_RO_NAMED(_name, _attrname)17741774+17751775+/* Gamepad - MCU */17761776+static struct go_cfg_attr version_product_mcu = { PRODUCT_VERSION };17771777+LEGO_DEVICE_ATTR_RO(version_product_mcu, "product_version", USB_MCU, version);17781778+17791779+static struct go_cfg_attr version_protocol_mcu = { PROTOCOL_VERSION };17801780+LEGO_DEVICE_ATTR_RO(version_protocol_mcu, "protocol_version", USB_MCU, version);17811781+17821782+static struct go_cfg_attr version_firmware_mcu = { FIRMWARE_VERSION };17831783+LEGO_DEVICE_ATTR_RO(version_firmware_mcu, "firmware_version", USB_MCU, version);17841784+17851785+static struct go_cfg_attr version_hardware_mcu = { HARDWARE_VERSION };17861786+LEGO_DEVICE_ATTR_RO(version_hardware_mcu, "hardware_version", USB_MCU, version);17871787+17881788+static struct go_cfg_attr version_gen_mcu = { HARDWARE_GENERATION };17891789+LEGO_DEVICE_ATTR_RO(version_gen_mcu, "hardware_generation", USB_MCU, version);17901790+17911791+static struct go_cfg_attr fps_switch_status = { FEATURE_FPS_SWITCH_STATUS };17921792+LEGO_DEVICE_ATTR_RO(fps_switch_status, "fps_switch_status", UNSPECIFIED,17931793+ feature_status);17941794+17951795+static struct go_cfg_attr gamepad_mode = { FEATURE_GAMEPAD_MODE };17961796+LEGO_DEVICE_ATTR_RW(gamepad_mode, "mode", UNSPECIFIED, index, feature_status);17971797+static DEVICE_ATTR_RO_NAMED(gamepad_mode_index, "mode_index");17981798+17991799+static struct go_cfg_attr reset_mcu = { FEATURE_RESET_GAMEPAD };18001800+LEGO_DEVICE_ATTR_WO(reset_mcu, "reset_mcu", USB_MCU, feature_status);18011801+18021802+static struct go_cfg_attr gamepad_rumble_intensity = { MOTOR_INTENSITY };18031803+LEGO_DEVICE_ATTR_RW(gamepad_rumble_intensity, "rumble_intensity", UNSPECIFIED,18041804+ index, motor_config);18051805+static DEVICE_ATTR_RO_NAMED(gamepad_rumble_intensity_index,18061806+ "rumble_intensity_index");18071807+18081808+static DEVICE_ATTR_RW(fps_mode_dpi);18091809+static DEVICE_ATTR_RO(fps_mode_dpi_index);18101810+18111811+static DEVICE_ATTR_RW(os_mode);18121812+static DEVICE_ATTR_RO(os_mode_index);18131813+18141814+static struct attribute *mcu_attrs[] = {18151815+ &dev_attr_fps_mode_dpi.attr,18161816+ &dev_attr_fps_mode_dpi_index.attr,18171817+ &dev_attr_fps_switch_status.attr,18181818+ &dev_attr_gamepad_mode.attr,18191819+ &dev_attr_gamepad_mode_index.attr,18201820+ &dev_attr_gamepad_rumble_intensity.attr,18211821+ &dev_attr_gamepad_rumble_intensity_index.attr,18221822+ &dev_attr_os_mode.attr,18231823+ &dev_attr_os_mode_index.attr,18241824+ &dev_attr_reset_mcu.attr,18251825+ &dev_attr_version_firmware_mcu.attr,18261826+ &dev_attr_version_gen_mcu.attr,18271827+ &dev_attr_version_hardware_mcu.attr,18281828+ &dev_attr_version_product_mcu.attr,18291829+ &dev_attr_version_protocol_mcu.attr,18301830+ NULL,18311831+};18321832+18331833+static const struct attribute_group mcu_attr_group = {18341834+ .attrs = mcu_attrs,18351835+};18361836+18371837+/* Gamepad - TX Dongle */18381838+static struct go_cfg_attr version_product_tx_dongle = { PRODUCT_VERSION };18391839+LEGO_DEVICE_ATTR_RO(version_product_tx_dongle, "product_version", TX_DONGLE, version);18401840+18411841+static struct go_cfg_attr version_protocol_tx_dongle = { PROTOCOL_VERSION };18421842+LEGO_DEVICE_ATTR_RO(version_protocol_tx_dongle, "protocol_version", TX_DONGLE, version);18431843+18441844+static struct go_cfg_attr version_firmware_tx_dongle = { FIRMWARE_VERSION };18451845+LEGO_DEVICE_ATTR_RO(version_firmware_tx_dongle, "firmware_version", TX_DONGLE, version);18461846+18471847+static struct go_cfg_attr version_hardware_tx_dongle = { HARDWARE_VERSION };18481848+LEGO_DEVICE_ATTR_RO(version_hardware_tx_dongle, "hardware_version", TX_DONGLE, version);18491849+18501850+static struct go_cfg_attr version_gen_tx_dongle = { HARDWARE_GENERATION };18511851+LEGO_DEVICE_ATTR_RO(version_gen_tx_dongle, "hardware_generation", TX_DONGLE, version);18521852+18531853+static struct go_cfg_attr reset_tx_dongle = { FEATURE_RESET_GAMEPAD };18541854+LEGO_DEVICE_ATTR_RO(reset_tx_dongle, "reset", TX_DONGLE, feature_status);18551855+18561856+static struct attribute *tx_dongle_attrs[] = {18571857+ &dev_attr_reset_tx_dongle.attr,18581858+ &dev_attr_version_hardware_tx_dongle.attr,18591859+ &dev_attr_version_firmware_tx_dongle.attr,18601860+ &dev_attr_version_gen_tx_dongle.attr,18611861+ &dev_attr_version_product_tx_dongle.attr,18621862+ &dev_attr_version_protocol_tx_dongle.attr,18631863+ NULL,18641864+};18651865+18661866+static const struct attribute_group tx_dongle_attr_group = {18671867+ .name = "tx_dongle",18681868+ .attrs = tx_dongle_attrs,18691869+};18701870+18711871+/* Gamepad - Left */18721872+static struct go_cfg_attr version_product_left = { PRODUCT_VERSION };18731873+LEGO_DEVICE_ATTR_RO(version_product_left, "product_version", LEFT_CONTROLLER, version);18741874+18751875+static struct go_cfg_attr version_protocol_left = { PROTOCOL_VERSION };18761876+LEGO_DEVICE_ATTR_RO(version_protocol_left, "protocol_version", LEFT_CONTROLLER, version);18771877+18781878+static struct go_cfg_attr version_firmware_left = { FIRMWARE_VERSION };18791879+LEGO_DEVICE_ATTR_RO(version_firmware_left, "firmware_version", LEFT_CONTROLLER, version);18801880+18811881+static struct go_cfg_attr version_hardware_left = { HARDWARE_VERSION };18821882+LEGO_DEVICE_ATTR_RO(version_hardware_left, "hardware_version", LEFT_CONTROLLER, version);18831883+18841884+static struct go_cfg_attr version_gen_left = { HARDWARE_GENERATION };18851885+LEGO_DEVICE_ATTR_RO(version_gen_left, "hardware_generation", LEFT_CONTROLLER, version);18861886+18871887+static struct go_cfg_attr auto_sleep_time_left = { FEATURE_AUTO_SLEEP_TIME };18881888+LEGO_DEVICE_ATTR_RW(auto_sleep_time_left, "auto_sleep_time", LEFT_CONTROLLER,18891889+ range, feature_status);18901890+static DEVICE_ATTR_RO_NAMED(auto_sleep_time_left_range,18911891+ "auto_sleep_time_range");18921892+18931893+static struct go_cfg_attr imu_bypass_left = { FEATURE_IMU_BYPASS };18941894+LEGO_DEVICE_ATTR_RW(imu_bypass_left, "imu_bypass_enabled", LEFT_CONTROLLER,18951895+ index, feature_status);18961896+static DEVICE_ATTR_RO_NAMED(imu_bypass_left_index, "imu_bypass_enabled_index");18971897+18981898+static struct go_cfg_attr imu_enabled_left = { FEATURE_IMU_ENABLE };18991899+LEGO_DEVICE_ATTR_RW(imu_enabled_left, "imu_enabled", LEFT_CONTROLLER, index,19001900+ feature_status);19011901+static DEVICE_ATTR_RO_NAMED(imu_enabled_left_index, "imu_enabled_index");19021902+19031903+static struct go_cfg_attr reset_left = { FEATURE_RESET_GAMEPAD };19041904+LEGO_DEVICE_ATTR_WO(reset_left, "reset", LEFT_CONTROLLER, feature_status);19051905+19061906+static struct go_cfg_attr rumble_mode_left = { RUMBLE_MODE };19071907+LEGO_DEVICE_ATTR_RW(rumble_mode_left, "rumble_mode", LEFT_CONTROLLER, index,19081908+ motor_config);19091909+static DEVICE_ATTR_RO_NAMED(rumble_mode_left_index, "rumble_mode_index");19101910+19111911+static struct go_cfg_attr rumble_notification_left = { VIBRATION_NOTIFY_ENABLE };19121912+LEGO_DEVICE_ATTR_RW(rumble_notification_left, "rumble_notification",19131913+ LEFT_CONTROLLER, index, motor_config);19141914+static DEVICE_ATTR_RO_NAMED(rumble_notification_left_index,19151915+ "rumble_notification_index");19161916+19171917+static struct go_cfg_attr cal_trigg_left = { TRIGGER_CALIBRATE };19181918+LEGO_CAL_DEVICE_ATTR(cal_trigg_left, "calibrate_trigger", SET_TRIGGER_CFG,19191919+ LEFT_CONTROLLER, index);19201920+static DEVICE_ATTR_RO_NAMED(cal_trigg_left_index, "calibrate_trigger_index");19211921+19221922+static struct go_cfg_attr cal_joy_left = { JOYSTICK_CALIBRATE };19231923+LEGO_CAL_DEVICE_ATTR(cal_joy_left, "calibrate_joystick", SET_JOYSTICK_CFG,19241924+ LEFT_CONTROLLER, index);19251925+static DEVICE_ATTR_RO_NAMED(cal_joy_left_index, "calibrate_joystick_index");19261926+19271927+static struct go_cfg_attr cal_gyro_left = { GYRO_CALIBRATE };19281928+LEGO_CAL_DEVICE_ATTR(cal_gyro_left, "calibrate_gyro", SET_GYRO_CFG,19291929+ LEFT_CONTROLLER, index);19301930+static DEVICE_ATTR_RO_NAMED(cal_gyro_left_index, "calibrate_gyro_index");19311931+19321932+static struct go_cfg_attr cal_trigg_left_status = { GET_CAL_STATUS };19331933+LEGO_DEVICE_STATUS_ATTR(cal_trigg_left_status, "calibrate_trigger_status",19341934+ LEFT_CONTROLLER, CALDEV_TRIGGER);19351935+19361936+static struct go_cfg_attr cal_joy_left_status = { GET_CAL_STATUS };19371937+LEGO_DEVICE_STATUS_ATTR(cal_joy_left_status, "calibrate_joystick_status",19381938+ LEFT_CONTROLLER, CALDEV_JOYSTICK);19391939+19401940+static struct go_cfg_attr cal_gyro_left_status = { GET_CAL_STATUS };19411941+LEGO_DEVICE_STATUS_ATTR(cal_gyro_left_status, "calibrate_gyro_status",19421942+ LEFT_CONTROLLER, CALDEV_GYROSCOPE);19431943+19441944+static struct attribute *left_gamepad_attrs[] = {19451945+ &dev_attr_auto_sleep_time_left.attr,19461946+ &dev_attr_auto_sleep_time_left_range.attr,19471947+ &dev_attr_cal_gyro_left.attr,19481948+ &dev_attr_cal_gyro_left_index.attr,19491949+ &dev_attr_cal_gyro_left_status.attr,19501950+ &dev_attr_cal_joy_left.attr,19511951+ &dev_attr_cal_joy_left_index.attr,19521952+ &dev_attr_cal_joy_left_status.attr,19531953+ &dev_attr_cal_trigg_left.attr,19541954+ &dev_attr_cal_trigg_left_index.attr,19551955+ &dev_attr_cal_trigg_left_status.attr,19561956+ &dev_attr_imu_bypass_left.attr,19571957+ &dev_attr_imu_bypass_left_index.attr,19581958+ &dev_attr_imu_enabled_left.attr,19591959+ &dev_attr_imu_enabled_left_index.attr,19601960+ &dev_attr_reset_left.attr,19611961+ &dev_attr_rumble_mode_left.attr,19621962+ &dev_attr_rumble_mode_left_index.attr,19631963+ &dev_attr_rumble_notification_left.attr,19641964+ &dev_attr_rumble_notification_left_index.attr,19651965+ &dev_attr_version_hardware_left.attr,19661966+ &dev_attr_version_firmware_left.attr,19671967+ &dev_attr_version_gen_left.attr,19681968+ &dev_attr_version_product_left.attr,19691969+ &dev_attr_version_protocol_left.attr,19701970+ NULL,19711971+};19721972+19731973+static const struct attribute_group left_gamepad_attr_group = {19741974+ .name = "left_handle",19751975+ .attrs = left_gamepad_attrs,19761976+};19771977+19781978+/* Gamepad - Right */19791979+static struct go_cfg_attr version_product_right = { PRODUCT_VERSION };19801980+LEGO_DEVICE_ATTR_RO(version_product_right, "product_version", RIGHT_CONTROLLER, version);19811981+19821982+static struct go_cfg_attr version_protocol_right = { PROTOCOL_VERSION };19831983+LEGO_DEVICE_ATTR_RO(version_protocol_right, "protocol_version", RIGHT_CONTROLLER, version);19841984+19851985+static struct go_cfg_attr version_firmware_right = { FIRMWARE_VERSION };19861986+LEGO_DEVICE_ATTR_RO(version_firmware_right, "firmware_version", RIGHT_CONTROLLER, version);19871987+19881988+static struct go_cfg_attr version_hardware_right = { HARDWARE_VERSION };19891989+LEGO_DEVICE_ATTR_RO(version_hardware_right, "hardware_version", RIGHT_CONTROLLER, version);19901990+19911991+static struct go_cfg_attr version_gen_right = { HARDWARE_GENERATION };19921992+LEGO_DEVICE_ATTR_RO(version_gen_right, "hardware_generation", RIGHT_CONTROLLER, version);19931993+19941994+static struct go_cfg_attr auto_sleep_time_right = { FEATURE_AUTO_SLEEP_TIME };19951995+LEGO_DEVICE_ATTR_RW(auto_sleep_time_right, "auto_sleep_time", RIGHT_CONTROLLER,19961996+ range, feature_status);19971997+static DEVICE_ATTR_RO_NAMED(auto_sleep_time_right_range,19981998+ "auto_sleep_time_range");19991999+20002000+static struct go_cfg_attr imu_bypass_right = { FEATURE_IMU_BYPASS };20012001+LEGO_DEVICE_ATTR_RW(imu_bypass_right, "imu_bypass_enabled", RIGHT_CONTROLLER,20022002+ index, feature_status);20032003+static DEVICE_ATTR_RO_NAMED(imu_bypass_right_index, "imu_bypass_enabled_index");20042004+20052005+static struct go_cfg_attr imu_enabled_right = { FEATURE_IMU_BYPASS };20062006+LEGO_DEVICE_ATTR_RW(imu_enabled_right, "imu_enabled", RIGHT_CONTROLLER, index,20072007+ feature_status);20082008+static DEVICE_ATTR_RO_NAMED(imu_enabled_right_index, "imu_enabled_index");20092009+20102010+static struct go_cfg_attr reset_right = { FEATURE_RESET_GAMEPAD };20112011+LEGO_DEVICE_ATTR_WO(reset_right, "reset", LEFT_CONTROLLER, feature_status);20122012+20132013+static struct go_cfg_attr rumble_mode_right = { RUMBLE_MODE };20142014+LEGO_DEVICE_ATTR_RW(rumble_mode_right, "rumble_mode", RIGHT_CONTROLLER, index,20152015+ motor_config);20162016+static DEVICE_ATTR_RO_NAMED(rumble_mode_right_index, "rumble_mode_index");20172017+20182018+static struct go_cfg_attr rumble_notification_right = { VIBRATION_NOTIFY_ENABLE };20192019+LEGO_DEVICE_ATTR_RW(rumble_notification_right, "rumble_notification",20202020+ RIGHT_CONTROLLER, index, motor_config);20212021+static DEVICE_ATTR_RO_NAMED(rumble_notification_right_index,20222022+ "rumble_notification_index");20232023+20242024+static struct go_cfg_attr cal_trigg_right = { TRIGGER_CALIBRATE };20252025+LEGO_CAL_DEVICE_ATTR(cal_trigg_right, "calibrate_trigger", SET_TRIGGER_CFG,20262026+ RIGHT_CONTROLLER, index);20272027+static DEVICE_ATTR_RO_NAMED(cal_trigg_right_index, "calibrate_trigger_index");20282028+20292029+static struct go_cfg_attr cal_joy_right = { JOYSTICK_CALIBRATE };20302030+LEGO_CAL_DEVICE_ATTR(cal_joy_right, "calibrate_joystick", SET_JOYSTICK_CFG,20312031+ RIGHT_CONTROLLER, index);20322032+static DEVICE_ATTR_RO_NAMED(cal_joy_right_index, "calibrate_joystick_index");20332033+20342034+static struct go_cfg_attr cal_gyro_right = { GYRO_CALIBRATE };20352035+LEGO_CAL_DEVICE_ATTR(cal_gyro_right, "calibrate_gyro", SET_GYRO_CFG,20362036+ RIGHT_CONTROLLER, index);20372037+static DEVICE_ATTR_RO_NAMED(cal_gyro_right_index, "calibrate_gyro_index");20382038+20392039+static struct go_cfg_attr cal_trigg_right_status = { GET_CAL_STATUS };20402040+LEGO_DEVICE_STATUS_ATTR(cal_trigg_right_status, "calibrate_trigger_status",20412041+ RIGHT_CONTROLLER, CALDEV_TRIGGER);20422042+20432043+static struct go_cfg_attr cal_joy_right_status = { GET_CAL_STATUS };20442044+LEGO_DEVICE_STATUS_ATTR(cal_joy_right_status, "calibrate_joystick_status",20452045+ RIGHT_CONTROLLER, CALDEV_JOYSTICK);20462046+20472047+static struct go_cfg_attr cal_gyro_right_status = { GET_CAL_STATUS };20482048+LEGO_DEVICE_STATUS_ATTR(cal_gyro_right_status, "calibrate_gyro_status",20492049+ RIGHT_CONTROLLER, CALDEV_GYROSCOPE);20502050+20512051+static struct attribute *right_gamepad_attrs[] = {20522052+ &dev_attr_auto_sleep_time_right.attr,20532053+ &dev_attr_auto_sleep_time_right_range.attr,20542054+ &dev_attr_cal_gyro_right.attr,20552055+ &dev_attr_cal_gyro_right_index.attr,20562056+ &dev_attr_cal_gyro_right_status.attr,20572057+ &dev_attr_cal_joy_right.attr,20582058+ &dev_attr_cal_joy_right_index.attr,20592059+ &dev_attr_cal_joy_right_status.attr,20602060+ &dev_attr_cal_trigg_right.attr,20612061+ &dev_attr_cal_trigg_right_index.attr,20622062+ &dev_attr_cal_trigg_right_status.attr,20632063+ &dev_attr_imu_bypass_right.attr,20642064+ &dev_attr_imu_bypass_right_index.attr,20652065+ &dev_attr_imu_enabled_right.attr,20662066+ &dev_attr_imu_enabled_right_index.attr,20672067+ &dev_attr_reset_right.attr,20682068+ &dev_attr_rumble_mode_right.attr,20692069+ &dev_attr_rumble_mode_right_index.attr,20702070+ &dev_attr_rumble_notification_right.attr,20712071+ &dev_attr_rumble_notification_right_index.attr,20722072+ &dev_attr_version_hardware_right.attr,20732073+ &dev_attr_version_firmware_right.attr,20742074+ &dev_attr_version_gen_right.attr,20752075+ &dev_attr_version_product_right.attr,20762076+ &dev_attr_version_protocol_right.attr,20772077+ NULL,20782078+};20792079+20802080+static const struct attribute_group right_gamepad_attr_group = {20812081+ .name = "right_handle",20822082+ .attrs = right_gamepad_attrs,20832083+};20842084+20852085+/* Touchpad */20862086+static struct go_cfg_attr touchpad_enabled = { FEATURE_TOUCHPAD_ENABLE };20872087+LEGO_DEVICE_ATTR_RW(touchpad_enabled, "enabled", UNSPECIFIED, index,20882088+ feature_status);20892089+static DEVICE_ATTR_RO_NAMED(touchpad_enabled_index, "enabled_index");20902090+20912091+static struct go_cfg_attr touchpad_vibration_enabled = { TP_VIBRATION_ENABLE };20922092+LEGO_DEVICE_ATTR_RW(touchpad_vibration_enabled, "vibration_enabled", UNSPECIFIED,20932093+ index, motor_config);20942094+static DEVICE_ATTR_RO_NAMED(touchpad_vibration_enabled_index,20952095+ "vibration_enabled_index");20962096+20972097+static struct go_cfg_attr touchpad_vibration_intensity = { TP_VIBRATION_INTENSITY };20982098+LEGO_DEVICE_ATTR_RW(touchpad_vibration_intensity, "vibration_intensity",20992099+ UNSPECIFIED, index, motor_config);21002100+static DEVICE_ATTR_RO_NAMED(touchpad_vibration_intensity_index,21012101+ "vibration_intensity_index");21022102+21032103+static struct attribute *touchpad_attrs[] = {21042104+ &dev_attr_touchpad_enabled.attr,21052105+ &dev_attr_touchpad_enabled_index.attr,21062106+ &dev_attr_touchpad_vibration_enabled.attr,21072107+ &dev_attr_touchpad_vibration_enabled_index.attr,21082108+ &dev_attr_touchpad_vibration_intensity.attr,21092109+ &dev_attr_touchpad_vibration_intensity_index.attr,21102110+ NULL,21112111+};21122112+21132113+static const struct attribute_group touchpad_attr_group = {21142114+ .name = "touchpad",21152115+ .attrs = touchpad_attrs,21162116+};21172117+21182118+static const struct attribute_group *top_level_attr_groups[] = {21192119+ &mcu_attr_group, &tx_dongle_attr_group,21202120+ &left_gamepad_attr_group, &right_gamepad_attr_group,21212121+ &touchpad_attr_group, NULL,21222122+};21232123+21242124+/* RGB */21252125+static struct go_cfg_attr rgb_enabled = { FEATURE_LIGHT_ENABLE };21262126+21272127+LEGO_DEVICE_ATTR_RW(rgb_enabled, "enabled", UNSPECIFIED, index, feature_status);21282128+static DEVICE_ATTR_RO_NAMED(rgb_effect_index, "effect_index");21292129+static DEVICE_ATTR_RO_NAMED(rgb_enabled_index, "enabled_index");21302130+static DEVICE_ATTR_RO_NAMED(rgb_mode_index, "mode_index");21312131+static DEVICE_ATTR_RO_NAMED(rgb_profile_range, "profile_range");21322132+static DEVICE_ATTR_RO_NAMED(rgb_speed_range, "speed_range");21332133+static DEVICE_ATTR_RW_NAMED(rgb_effect, "effect");21342134+static DEVICE_ATTR_RW_NAMED(rgb_mode, "mode");21352135+static DEVICE_ATTR_RW_NAMED(rgb_profile, "profile");21362136+static DEVICE_ATTR_RW_NAMED(rgb_speed, "speed");21372137+21382138+static struct attribute *go_rgb_attrs[] = {21392139+ &dev_attr_rgb_effect.attr,21402140+ &dev_attr_rgb_effect_index.attr,21412141+ &dev_attr_rgb_enabled.attr,21422142+ &dev_attr_rgb_enabled_index.attr,21432143+ &dev_attr_rgb_mode.attr,21442144+ &dev_attr_rgb_mode_index.attr,21452145+ &dev_attr_rgb_profile.attr,21462146+ &dev_attr_rgb_profile_range.attr,21472147+ &dev_attr_rgb_speed.attr,21482148+ &dev_attr_rgb_speed_range.attr,21492149+ NULL,21502150+};21512151+21522152+static struct attribute_group rgb_attr_group = {21532153+ .attrs = go_rgb_attrs,21542154+};21552155+21562156+static struct mc_subled go_rgb_subled_info[] = {21572157+ {21582158+ .color_index = LED_COLOR_ID_RED,21592159+ .brightness = 0x50,21602160+ .intensity = 0x24,21612161+ .channel = 0x1,21622162+ },21632163+ {21642164+ .color_index = LED_COLOR_ID_GREEN,21652165+ .brightness = 0x50,21662166+ .intensity = 0x22,21672167+ .channel = 0x2,21682168+ },21692169+ {21702170+ .color_index = LED_COLOR_ID_BLUE,21712171+ .brightness = 0x50,21722172+ .intensity = 0x99,21732173+ .channel = 0x3,21742174+ },21752175+};21762176+21772177+static struct led_classdev_mc go_cdev_rgb = {21782178+ .led_cdev = {21792179+ .name = "go:rgb:joystick_rings",21802180+ .color = LED_COLOR_ID_RGB,21812181+ .brightness = 0x50,21822182+ .max_brightness = 0x64,21832183+ .brightness_set = hid_go_brightness_set,21842184+ },21852185+ .num_colors = ARRAY_SIZE(go_rgb_subled_info),21862186+ .subled_info = go_rgb_subled_info,21872187+};21882188+21892189+static void cfg_setup(struct work_struct *work)21902190+{21912191+ int ret;21922192+21932193+ /* MCU Version Attrs */21942194+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,21952195+ PRODUCT_VERSION, USB_MCU, NULL, 0);21962196+ if (ret < 0) {21972197+ dev_err(&drvdata.hdev->dev,21982198+ "Failed to retrieve USB_MCU Product Version: %i\n", ret);21992199+ return;22002200+ }22012201+22022202+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,22032203+ PROTOCOL_VERSION, USB_MCU, NULL, 0);22042204+ if (ret < 0) {22052205+ dev_err(&drvdata.hdev->dev,22062206+ "Failed to retrieve USB_MCU Protocol Version: %i\n", ret);22072207+ return;22082208+ }22092209+22102210+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,22112211+ FIRMWARE_VERSION, USB_MCU, NULL, 0);22122212+ if (ret < 0) {22132213+ dev_err(&drvdata.hdev->dev,22142214+ "Failed to retrieve USB_MCU Firmware Version: %i\n", ret);22152215+ return;22162216+ }22172217+22182218+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,22192219+ HARDWARE_VERSION, USB_MCU, NULL, 0);22202220+ if (ret < 0) {22212221+ dev_err(&drvdata.hdev->dev,22222222+ "Failed to retrieve USB_MCU Hardware Version: %i\n", ret);22232223+ return;22242224+ }22252225+22262226+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,22272227+ HARDWARE_GENERATION, USB_MCU, NULL, 0);22282228+ if (ret < 0) {22292229+ dev_err(&drvdata.hdev->dev,22302230+ "Failed to retrieve USB_MCU Hardware Generation: %i\n", ret);22312231+ return;22322232+ }22332233+22342234+ /* TX Dongle Version Attrs */22352235+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,22362236+ PRODUCT_VERSION, TX_DONGLE, NULL, 0);22372237+ if (ret < 0) {22382238+ dev_err(&drvdata.hdev->dev,22392239+ "Failed to retrieve TX_DONGLE Product Version: %i\n", ret);22402240+ return;22412241+ }22422242+22432243+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,22442244+ PROTOCOL_VERSION, TX_DONGLE, NULL, 0);22452245+ if (ret < 0) {22462246+ dev_err(&drvdata.hdev->dev,22472247+ "Failed to retrieve TX_DONGLE Protocol Version: %i\n", ret);22482248+ return;22492249+ }22502250+22512251+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,22522252+ FIRMWARE_VERSION, TX_DONGLE, NULL, 0);22532253+ if (ret < 0) {22542254+ dev_err(&drvdata.hdev->dev,22552255+ "Failed to retrieve TX_DONGLE Firmware Version: %i\n", ret);22562256+ return;22572257+ }22582258+22592259+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,22602260+ HARDWARE_VERSION, TX_DONGLE, NULL, 0);22612261+ if (ret < 0) {22622262+ dev_err(&drvdata.hdev->dev,22632263+ "Failed to retrieve TX_DONGLE Hardware Version: %i\n", ret);22642264+ return;22652265+ }22662266+22672267+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,22682268+ HARDWARE_GENERATION, TX_DONGLE, NULL, 0);22692269+ if (ret < 0) {22702270+ dev_err(&drvdata.hdev->dev,22712271+ "Failed to retrieve TX_DONGLE Hardware Generation: %i\n", ret);22722272+ return;22732273+ }22742274+22752275+ /* Left Handle Version Attrs */22762276+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,22772277+ PRODUCT_VERSION, LEFT_CONTROLLER, NULL, 0);22782278+ if (ret < 0) {22792279+ dev_err(&drvdata.hdev->dev,22802280+ "Failed to retrieve LEFT_CONTROLLER Product Version: %i\n", ret);22812281+ return;22822282+ }22832283+22842284+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,22852285+ PROTOCOL_VERSION, LEFT_CONTROLLER, NULL, 0);22862286+ if (ret < 0) {22872287+ dev_err(&drvdata.hdev->dev,22882288+ "Failed to retrieve LEFT_CONTROLLER Protocol Version: %i\n", ret);22892289+ return;22902290+ }22912291+22922292+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,22932293+ FIRMWARE_VERSION, LEFT_CONTROLLER, NULL, 0);22942294+ if (ret < 0) {22952295+ dev_err(&drvdata.hdev->dev,22962296+ "Failed to retrieve LEFT_CONTROLLER Firmware Version: %i\n", ret);22972297+ return;22982298+ }22992299+23002300+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,23012301+ HARDWARE_VERSION, LEFT_CONTROLLER, NULL, 0);23022302+ if (ret < 0) {23032303+ dev_err(&drvdata.hdev->dev,23042304+ "Failed to retrieve LEFT_CONTROLLER Hardware Version: %i\n", ret);23052305+ return;23062306+ }23072307+23082308+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,23092309+ HARDWARE_GENERATION, LEFT_CONTROLLER, NULL, 0);23102310+ if (ret < 0) {23112311+ dev_err(&drvdata.hdev->dev,23122312+ "Failed to retrieve LEFT_CONTROLLER Hardware Generation: %i\n", ret);23132313+ return;23142314+ }23152315+23162316+ /* Right Handle Version Attrs */23172317+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,23182318+ PRODUCT_VERSION, RIGHT_CONTROLLER, NULL, 0);23192319+ if (ret < 0) {23202320+ dev_err(&drvdata.hdev->dev,23212321+ "Failed to retrieve RIGHT_CONTROLLER Product Version: %i\n", ret);23222322+ return;23232323+ }23242324+23252325+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,23262326+ PROTOCOL_VERSION, RIGHT_CONTROLLER, NULL, 0);23272327+ if (ret < 0) {23282328+ dev_err(&drvdata.hdev->dev,23292329+ "Failed to retrieve RIGHT_CONTROLLER Protocol Version: %i\n", ret);23302330+ return;23312331+ }23322332+23332333+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,23342334+ FIRMWARE_VERSION, RIGHT_CONTROLLER, NULL, 0);23352335+ if (ret < 0) {23362336+ dev_err(&drvdata.hdev->dev,23372337+ "Failed to retrieve RIGHT_CONTROLLER Firmware Version: %i\n", ret);23382338+ return;23392339+ }23402340+23412341+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,23422342+ HARDWARE_VERSION, RIGHT_CONTROLLER, NULL, 0);23432343+ if (ret < 0) {23442344+ dev_err(&drvdata.hdev->dev,23452345+ "Failed to retrieve RIGHT_CONTROLLER Hardware Version: %i\n", ret);23462346+ return;23472347+ }23482348+23492349+ ret = mcu_property_out(drvdata.hdev, MCU_CONFIG_DATA, GET_VERSION_DATA,23502350+ HARDWARE_GENERATION, RIGHT_CONTROLLER, NULL, 0);23512351+ if (ret < 0) {23522352+ dev_err(&drvdata.hdev->dev,23532353+ "Failed to retrieve RIGHT_CONTROLLER Hardware Generation: %i\n", ret);23542354+ return;23552355+ }23562356+}23572357+23582358+static int hid_go_cfg_probe(struct hid_device *hdev,23592359+ const struct hid_device_id *_id)23602360+{23612361+ unsigned char *buf;23622362+ int ret;23632363+23642364+ buf = devm_kzalloc(&hdev->dev, GO_PACKET_SIZE, GFP_KERNEL);23652365+ if (!buf)23662366+ return -ENOMEM;23672367+23682368+ hid_set_drvdata(hdev, &drvdata);23692369+ drvdata.hdev = hdev;23702370+ mutex_init(&drvdata.cfg_mutex);23712371+23722372+ ret = sysfs_create_groups(&hdev->dev.kobj, top_level_attr_groups);23732373+ if (ret) {23742374+ dev_err_probe(&hdev->dev, ret,23752375+ "Failed to create gamepad configuration attributes\n");23762376+ return ret;23772377+ }23782378+23792379+ ret = devm_led_classdev_multicolor_register(&hdev->dev, &go_cdev_rgb);23802380+ if (ret) {23812381+ dev_err_probe(&hdev->dev, ret, "Failed to create RGB device\n");23822382+ return ret;23832383+ }23842384+23852385+ ret = devm_device_add_group(go_cdev_rgb.led_cdev.dev, &rgb_attr_group);23862386+ if (ret) {23872387+ dev_err_probe(&hdev->dev, ret,23882388+ "Failed to create RGB configuration attributes\n");23892389+ return ret;23902390+ }23912391+23922392+ drvdata.led_cdev = &go_cdev_rgb.led_cdev;23932393+23942394+ init_completion(&drvdata.send_cmd_complete);23952395+23962396+ /* Executing calls prior to returning from probe will lock the MCU. Schedule23972397+ * initial data call after probe has completed and MCU can accept calls.23982398+ */23992399+ INIT_DELAYED_WORK(&drvdata.go_cfg_setup, &cfg_setup);24002400+ ret = schedule_delayed_work(&drvdata.go_cfg_setup, msecs_to_jiffies(2));24012401+ if (!ret) {24022402+ dev_err(&hdev->dev,24032403+ "Failed to schedule startup delayed work\n");24042404+ return -ENODEV;24052405+ }24062406+ return 0;24072407+}24082408+24092409+static void hid_go_cfg_remove(struct hid_device *hdev)24102410+{24112411+ guard(mutex)(&drvdata.cfg_mutex);24122412+ sysfs_remove_groups(&hdev->dev.kobj, top_level_attr_groups);24132413+ hid_hw_close(hdev);24142414+ hid_hw_stop(hdev);24152415+ hid_set_drvdata(hdev, NULL);24162416+}24172417+24182418+static int hid_go_probe(struct hid_device *hdev, const struct hid_device_id *id)24192419+{24202420+ int ret, ep;24212421+24222422+ hdev->quirks |= HID_QUIRK_INPUT_PER_APP | HID_QUIRK_MULTI_INPUT;24232423+24242424+ ret = hid_parse(hdev);24252425+ if (ret) {24262426+ hid_err(hdev, "Parse failed\n");24272427+ return ret;24282428+ }24292429+24302430+ ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);24312431+ if (ret) {24322432+ hid_err(hdev, "Failed to start HID device\n");24332433+ return ret;24342434+ }24352435+24362436+ ret = hid_hw_open(hdev);24372437+ if (ret) {24382438+ hid_err(hdev, "Failed to open HID device\n");24392439+ hid_hw_stop(hdev);24402440+ return ret;24412441+ }24422442+24432443+ ep = get_endpoint_address(hdev);24442444+ if (ep != GO_GP_INTF_IN) {24452445+ dev_dbg(&hdev->dev, "Started interface %x as generic HID device\n", ep);24462446+ return 0;24472447+ }24482448+24492449+ ret = hid_go_cfg_probe(hdev, id);24502450+ if (ret)24512451+ dev_err_probe(&hdev->dev, ret, "Failed to start configuration interface\n");24522452+24532453+ dev_dbg(&hdev->dev, "Started Legion Go HID Device: %x\n", ep);24542454+24552455+ return ret;24562456+}24572457+24582458+static void hid_go_remove(struct hid_device *hdev)24592459+{24602460+ int ep = get_endpoint_address(hdev);24612461+24622462+ if (ep <= 0)24632463+ return;24642464+24652465+ switch (ep) {24662466+ case GO_GP_INTF_IN:24672467+ hid_go_cfg_remove(hdev);24682468+ break;24692469+ default:24702470+ hid_hw_close(hdev);24712471+ hid_hw_stop(hdev);24722472+ break;24732473+ }24742474+}24752475+24762476+static const struct hid_device_id hid_go_devices[] = {24772477+ { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO,24782478+ USB_DEVICE_ID_LENOVO_LEGION_GO2_XINPUT) },24792479+ { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO,24802480+ USB_DEVICE_ID_LENOVO_LEGION_GO2_DINPUT) },24812481+ { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO,24822482+ USB_DEVICE_ID_LENOVO_LEGION_GO2_DUAL_DINPUT) },24832483+ { HID_USB_DEVICE(USB_VENDOR_ID_LENOVO,24842484+ USB_DEVICE_ID_LENOVO_LEGION_GO2_FPS) },24852485+ {}24862486+};24872487+MODULE_DEVICE_TABLE(hid, hid_go_devices);24882488+24892489+static struct hid_driver hid_lenovo_go = {24902490+ .name = "hid-lenovo-go",24912491+ .id_table = hid_go_devices,24922492+ .probe = hid_go_probe,24932493+ .remove = hid_go_remove,24942494+ .raw_event = hid_go_raw_event,24952495+};24962496+module_hid_driver(hid_lenovo_go);24972497+24982498+MODULE_AUTHOR("Derek J. Clark");24992499+MODULE_DESCRIPTION("HID Driver for Lenovo Legion Go Series Gamepads.");25002500+MODULE_LICENSE("GPL");
+46
include/linux/device.h
···190190 struct device_attribute dev_attr_##_name = __ATTR_RW_MODE(_name, 0600)191191192192/**193193+ * DEVICE_ATTR_RW_NAMED - Define a read-write device attribute with a sysfs name194194+ * that differs from the function name.195195+ * @_name: Attribute function preface196196+ * @_attrname: Attribute name as it wil be exposed in the sysfs.197197+ *198198+ * Like DEVICE_ATTR_RW(), but allows for reusing names under separate paths in199199+ * the same driver.200200+ */201201+#define DEVICE_ATTR_RW_NAMED(_name, _attrname) \202202+ struct device_attribute dev_attr_##_name = { \203203+ .attr = { .name = _attrname, .mode = 0644 }, \204204+ .show = _name##_show, \205205+ .store = _name##_store, \206206+ }207207+208208+/**193209 * DEVICE_ATTR_RO - Define a readable device attribute.194210 * @_name: Attribute name.195211 *···224208 struct device_attribute dev_attr_##_name = __ATTR_RO_MODE(_name, 0400)225209226210/**211211+ * DEVICE_ATTR_RO_NAMED - Define a read-only device attribute with a sysfs name212212+ * that differs from the function name.213213+ * @_name: Attribute function preface214214+ * @_attrname: Attribute name as it wil be exposed in the sysfs.215215+ *216216+ * Like DEVICE_ATTR_RO(), but allows for reusing names under separate paths in217217+ * the same driver.218218+ */219219+#define DEVICE_ATTR_RO_NAMED(_name, _attrname) \220220+ struct device_attribute dev_attr_##_name = { \221221+ .attr = { .name = _attrname, .mode = 0444 }, \222222+ .show = _name##_show, \223223+ }224224+225225+/**227226 * DEVICE_ATTR_WO - Define an admin-only writable device attribute.228227 * @_name: Attribute name.229228 *···246215 */247216#define DEVICE_ATTR_WO(_name) \248217 struct device_attribute dev_attr_##_name = __ATTR_WO(_name)218218+219219+/**220220+ * DEVICE_ATTR_WO_NAMED - Define a read-only device attribute with a sysfs name221221+ * that differs from the function name.222222+ * @_name: Attribute function preface223223+ * @_attrname: Attribute name as it wil be exposed in the sysfs.224224+ *225225+ * Like DEVICE_ATTR_WO(), but allows for reusing names under separate paths in226226+ * the same driver.227227+ */228228+#define DEVICE_ATTR_WO_NAMED(_name, _attrname) \229229+ struct device_attribute dev_attr_##_name = { \230230+ .attr = { .name = _attrname, .mode = 0200 }, \231231+ .store = _name##_store, \232232+ }249233250234/**251235 * DEVICE_ULONG_ATTR - Define a device attribute backed by an unsigned long.