1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * HID driver for OneXPlayer gamepad configuration devices. 4 * 5 * Copyright (c) 2026 Valve Corporation 6 */ 7 8 #include <linux/array_size.h> 9 #include <linux/cleanup.h> 10 #include <linux/delay.h> 11 #include <linux/dev_printk.h> 12 #include <linux/device.h> 13 #include <linux/dmi.h> 14 #include <linux/hid.h> 15 #include <linux/jiffies.h> 16 #include <linux/kstrtox.h> 17 #include <linux/led-class-multicolor.h> 18 #include <linux/mutex.h> 19 #include <linux/sysfs.h> 20 #include <linux/types.h> 21 #include <linux/workqueue.h> 22 23 #include "hid-ids.h" 24 25 #define OXP_PACKET_SIZE 64 26 27 #define GEN1_MESSAGE_ID 0xff 28 #define GEN2_MESSAGE_ID 0x3f 29 30 #define GEN1_USAGE_PAGE 0xff01 31 #define GEN2_USAGE_PAGE 0xff00 32 33 enum oxp_function_index { 34 OXP_FID_GEN1_RGB_SET = 0x07, 35 OXP_FID_GEN1_RGB_REPLY = 0x0f, 36 OXP_FID_GEN2_TOGGLE_MODE = 0xb2, 37 OXP_FID_GEN2_STATUS_EVENT = 0xb8, 38 }; 39 40 static struct oxp_hid_cfg { 41 struct delayed_work oxp_rgb_queue; 42 struct delayed_work oxp_mcu_init; 43 struct led_classdev_mc *led_mc; 44 struct hid_device *hdev; 45 struct mutex cfg_mutex; /*ensure single synchronous output report*/ 46 u8 rgb_brightness; 47 u8 gamepad_mode; 48 u8 rgb_effect; 49 u8 rgb_speed; 50 u8 rgb_en; 51 } drvdata; 52 53 enum oxp_gamepad_mode_index { 54 OXP_GP_MODE_XINPUT = 0x00, 55 OXP_GP_MODE_DEBUG = 0x03, 56 }; 57 58 static const char *const oxp_gamepad_mode_text[] = { 59 [OXP_GP_MODE_XINPUT] = "xinput", 60 [OXP_GP_MODE_DEBUG] = "debug", 61 }; 62 63 enum oxp_feature_en_index { 64 OXP_FEAT_DISABLED, 65 OXP_FEAT_ENABLED, 66 }; 67 68 static const char *const oxp_feature_en_text[] = { 69 [OXP_FEAT_DISABLED] = "false", 70 [OXP_FEAT_ENABLED] = "true", 71 }; 72 73 enum oxp_rgb_effect_index { 74 OXP_UNKNOWN, 75 OXP_EFFECT_AURORA, 76 OXP_EFFECT_BIRTHDAY, 77 OXP_EFFECT_FLOWING, 78 OXP_EFFECT_CHROMA_1, 79 OXP_EFFECT_NEON, 80 OXP_EFFECT_CHROMA_2, 81 OXP_EFFECT_DREAMY, 82 OXP_EFFECT_WARM, 83 OXP_EFFECT_CYBERPUNK, 84 OXP_EFFECT_SEA, 85 OXP_EFFECT_SUNSET, 86 OXP_EFFECT_COLORFUL, 87 OXP_EFFECT_MONSTER, 88 OXP_EFFECT_GREEN, 89 OXP_EFFECT_BLUE, 90 OXP_EFFECT_YELLOW, 91 OXP_EFFECT_TEAL, 92 OXP_EFFECT_PURPLE, 93 OXP_EFFECT_FOGGY, 94 OXP_EFFECT_MONO_LIST, /* placeholder for effect_index_show */ 95 }; 96 97 /* These belong to rgb_effect_index, but we want to hide them from 98 * rgb_effect_text 99 */ 100 101 #define OXP_GET_PROPERTY 0xfc 102 #define OXP_SET_PROPERTY 0xfd 103 #define OXP_EFFECT_MONO_TRUE 0xfe /* actual index for monocolor */ 104 105 static const char *const oxp_rgb_effect_text[] = { 106 [OXP_UNKNOWN] = "unknown", 107 [OXP_EFFECT_AURORA] = "aurora", 108 [OXP_EFFECT_BIRTHDAY] = "birthday_cake", 109 [OXP_EFFECT_FLOWING] = "flowing_light", 110 [OXP_EFFECT_CHROMA_1] = "chroma_popping", 111 [OXP_EFFECT_NEON] = "neon", 112 [OXP_EFFECT_CHROMA_2] = "chroma_breathing", 113 [OXP_EFFECT_DREAMY] = "dreamy", 114 [OXP_EFFECT_WARM] = "warm_sun", 115 [OXP_EFFECT_CYBERPUNK] = "cyberpunk", 116 [OXP_EFFECT_SEA] = "sea_foam", 117 [OXP_EFFECT_SUNSET] = "sunset_afterglow", 118 [OXP_EFFECT_COLORFUL] = "colorful", 119 [OXP_EFFECT_MONSTER] = "monster_woke", 120 [OXP_EFFECT_GREEN] = "green_breathing", 121 [OXP_EFFECT_BLUE] = "blue_breathing", 122 [OXP_EFFECT_YELLOW] = "yellow_breathing", 123 [OXP_EFFECT_TEAL] = "teal_breathing", 124 [OXP_EFFECT_PURPLE] = "purple_breathing", 125 [OXP_EFFECT_FOGGY] = "foggy_haze", 126 [OXP_EFFECT_MONO_LIST] = "monocolor", 127 }; 128 129 struct oxp_gen_1_rgb_report { 130 u8 report_id; 131 u8 message_id; 132 u8 padding_2[2]; 133 u8 effect; 134 u8 enabled; 135 u8 speed; 136 u8 brightness; 137 u8 red; 138 u8 green; 139 u8 blue; 140 } __packed; 141 142 struct oxp_gen_2_rgb_report { 143 u8 report_id; 144 u8 header_id; 145 u8 padding_2; 146 u8 message_id; 147 u8 padding_4[2]; 148 u8 enabled; 149 u8 speed; 150 u8 brightness; 151 u8 red; 152 u8 green; 153 u8 blue; 154 u8 padding_12[3]; 155 u8 effect; 156 } __packed; 157 158 static u16 get_usage_page(struct hid_device *hdev) 159 { 160 return hdev->collection[0].usage >> 16; 161 } 162 163 static int oxp_hid_raw_event_gen_1(struct hid_device *hdev, 164 struct hid_report *report, u8 *data, 165 int size) 166 { 167 struct led_classdev_mc *led_mc = drvdata.led_mc; 168 struct oxp_gen_1_rgb_report *rgb_rep; 169 170 if (data[1] != OXP_FID_GEN1_RGB_REPLY) 171 return 0; 172 173 rgb_rep = (struct oxp_gen_1_rgb_report *)data; 174 /* Ensure we save monocolor as the list value */ 175 drvdata.rgb_effect = rgb_rep->effect == OXP_EFFECT_MONO_TRUE ? 176 OXP_EFFECT_MONO_LIST : 177 rgb_rep->effect; 178 drvdata.rgb_speed = rgb_rep->speed; 179 drvdata.rgb_en = rgb_rep->enabled == 0 ? OXP_FEAT_DISABLED : 180 OXP_FEAT_ENABLED; 181 drvdata.rgb_brightness = rgb_rep->brightness; 182 led_mc->led_cdev.brightness = rgb_rep->brightness / 4 * 183 led_mc->led_cdev.max_brightness; 184 /* If monocolor had less than 100% brightness on the previous boot, 185 * there will be no reliable way to determine the real intensity. 186 * Since intensity scaling is used with a hardware brightness set at max, 187 * our brightness will always look like 100%. Use the last set value to 188 * prevent successive boots from lowering the brightness further. 189 * Brightness will be "wrong" but the effect will remain the same visually. 190 */ 191 led_mc->subled_info[0].intensity = rgb_rep->red; 192 led_mc->subled_info[1].intensity = rgb_rep->green; 193 led_mc->subled_info[2].intensity = rgb_rep->blue; 194 195 return 0; 196 } 197 198 static int oxp_gen_2_property_out(enum oxp_function_index fid, u8 *data, u8 data_size); 199 200 static void oxp_mcu_init_fn(struct work_struct *work) 201 { 202 u8 gp_mode_data[3] = { OXP_GP_MODE_DEBUG, 0x01, 0x02 }; 203 int ret; 204 205 /* Cycle the gamepad mode */ 206 ret = oxp_gen_2_property_out(OXP_FID_GEN2_TOGGLE_MODE, gp_mode_data, 3); 207 if (ret) 208 dev_err(&drvdata.hdev->dev, 209 "Error: Failed to set gamepad mode: %i\n", ret); 210 211 /* Remainder only applies for xinput mode */ 212 if (drvdata.gamepad_mode == OXP_GP_MODE_DEBUG) 213 return; 214 215 gp_mode_data[0] = OXP_GP_MODE_XINPUT; 216 ret = oxp_gen_2_property_out(OXP_FID_GEN2_TOGGLE_MODE, gp_mode_data, 3); 217 if (ret) 218 dev_err(&drvdata.hdev->dev, 219 "Error: Failed to set gamepad mode: %i\n", ret); 220 } 221 222 static int oxp_hid_raw_event_gen_2(struct hid_device *hdev, 223 struct hid_report *report, u8 *data, 224 int size) 225 { 226 struct led_classdev_mc *led_mc = drvdata.led_mc; 227 struct oxp_gen_2_rgb_report *rgb_rep; 228 229 if (data[0] != OXP_FID_GEN2_STATUS_EVENT) 230 return 0; 231 232 /* Sent ~6s after resume event, indicating the MCU has fully reset. 233 * Re-apply our settings after this has been received. 234 */ 235 if (data[3] == OXP_EFFECT_MONO_TRUE) { 236 mod_delayed_work(system_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(50)); 237 return 0; 238 } 239 240 if (data[3] != OXP_GET_PROPERTY) 241 return 0; 242 243 rgb_rep = (struct oxp_gen_2_rgb_report *)data; 244 /* Ensure we save monocolor as the list value */ 245 drvdata.rgb_effect = rgb_rep->effect == OXP_EFFECT_MONO_TRUE ? 246 OXP_EFFECT_MONO_LIST : 247 rgb_rep->effect; 248 drvdata.rgb_speed = rgb_rep->speed; 249 drvdata.rgb_en = rgb_rep->enabled == 0 ? OXP_FEAT_DISABLED : 250 OXP_FEAT_ENABLED; 251 drvdata.rgb_brightness = rgb_rep->brightness; 252 led_mc->led_cdev.brightness = rgb_rep->brightness / 4 * 253 led_mc->led_cdev.max_brightness; 254 /* If monocolor had less than 100% brightness on the previous boot, 255 * there will be no reliable way to determine the real intensity. 256 * Since intensity scaling is used with a hardware brightness set at max, 257 * our brightness will always look like 100%. Use the last set value to 258 * prevent successive boots from lowering the brightness further. 259 * Brightness will be "wrong" but the effect will remain the same visually. 260 */ 261 led_mc->subled_info[0].intensity = rgb_rep->red; 262 led_mc->subled_info[1].intensity = rgb_rep->green; 263 led_mc->subled_info[2].intensity = rgb_rep->blue; 264 265 return 0; 266 } 267 268 static int oxp_hid_raw_event(struct hid_device *hdev, struct hid_report *report, 269 u8 *data, int size) 270 { 271 u16 up = get_usage_page(hdev); 272 273 dev_dbg(&hdev->dev, "raw event data: [%*ph]\n", OXP_PACKET_SIZE, data); 274 275 switch (up) { 276 case GEN1_USAGE_PAGE: 277 return oxp_hid_raw_event_gen_1(hdev, report, data, size); 278 case GEN2_USAGE_PAGE: 279 return oxp_hid_raw_event_gen_2(hdev, report, data, size); 280 default: 281 break; 282 } 283 284 return 0; 285 } 286 287 static int mcu_property_out(u8 *header, size_t header_size, u8 *data, 288 size_t data_size, u8 *footer, size_t footer_size) 289 { 290 unsigned char *dmabuf __free(kfree) = kzalloc(OXP_PACKET_SIZE, GFP_KERNEL); 291 int ret; 292 293 if (!dmabuf) 294 return -ENOMEM; 295 296 if (header_size + data_size + footer_size > OXP_PACKET_SIZE) 297 return -EINVAL; 298 299 guard(mutex)(&drvdata.cfg_mutex); 300 memcpy(dmabuf, header, header_size); 301 memcpy(dmabuf + header_size, data, data_size); 302 if (footer_size) 303 memcpy(dmabuf + OXP_PACKET_SIZE - footer_size, footer, footer_size); 304 305 dev_dbg(&drvdata.hdev->dev, "raw data: [%*ph]\n", OXP_PACKET_SIZE, dmabuf); 306 307 ret = hid_hw_output_report(drvdata.hdev, dmabuf, OXP_PACKET_SIZE); 308 if (ret < 0) 309 return ret; 310 311 /* MCU takes 200ms to be ready for another command. */ 312 msleep(200); 313 return ret == OXP_PACKET_SIZE ? 0 : -EIO; 314 } 315 316 static int oxp_gen_1_property_out(enum oxp_function_index fid, u8 *data, 317 u8 data_size) 318 { 319 u8 header[] = { fid, GEN1_MESSAGE_ID }; 320 size_t header_size = ARRAY_SIZE(header); 321 322 return mcu_property_out(header, header_size, data, data_size, NULL, 0); 323 } 324 325 static int oxp_gen_2_property_out(enum oxp_function_index fid, u8 *data, 326 u8 data_size) 327 { 328 u8 header[] = { fid, GEN2_MESSAGE_ID, 0x01 }; 329 u8 footer[] = { GEN2_MESSAGE_ID, fid }; 330 size_t header_size = ARRAY_SIZE(header); 331 size_t footer_size = ARRAY_SIZE(footer); 332 333 return mcu_property_out(header, header_size, data, data_size, footer, 334 footer_size); 335 } 336 337 static ssize_t gamepad_mode_store(struct device *dev, 338 struct device_attribute *attr, const char *buf, 339 size_t count) 340 { 341 u16 up = get_usage_page(drvdata.hdev); 342 u8 data[3] = { 0x00, 0x01, 0x02 }; 343 int ret = -EINVAL; 344 int i; 345 346 if (up != GEN2_USAGE_PAGE) 347 return ret; 348 349 for (i = 0; i < ARRAY_SIZE(oxp_gamepad_mode_text); i++) { 350 if (oxp_gamepad_mode_text[i] && sysfs_streq(buf, oxp_gamepad_mode_text[i])) { 351 ret = i; 352 break; 353 } 354 } 355 if (ret < 0) 356 return ret; 357 358 data[0] = ret; 359 360 ret = oxp_gen_2_property_out(OXP_FID_GEN2_TOGGLE_MODE, data, 3); 361 if (ret) 362 return ret; 363 364 drvdata.gamepad_mode = data[0]; 365 366 return count; 367 } 368 369 static ssize_t gamepad_mode_show(struct device *dev, 370 struct device_attribute *attr, char *buf) 371 { 372 return sysfs_emit(buf, "%s\n", oxp_gamepad_mode_text[drvdata.gamepad_mode]); 373 } 374 static DEVICE_ATTR_RW(gamepad_mode); 375 376 static ssize_t gamepad_mode_index_show(struct device *dev, 377 struct device_attribute *attr, 378 char *buf) 379 { 380 ssize_t count = 0; 381 unsigned int i; 382 383 for (i = 0; i < ARRAY_SIZE(oxp_gamepad_mode_text); i++) { 384 if (!oxp_gamepad_mode_text[i] || 385 oxp_gamepad_mode_text[i][0] == '\0') 386 continue; 387 388 count += sysfs_emit_at(buf, count, "%s ", oxp_gamepad_mode_text[i]); 389 } 390 391 if (count) 392 buf[count - 1] = '\n'; 393 394 return count; 395 } 396 static DEVICE_ATTR_RO(gamepad_mode_index); 397 398 static struct attribute *oxp_cfg_attrs[] = { 399 &dev_attr_gamepad_mode.attr, 400 &dev_attr_gamepad_mode_index.attr, 401 NULL, 402 }; 403 404 static const struct attribute_group oxp_cfg_attrs_group = { 405 .attrs = oxp_cfg_attrs, 406 }; 407 408 static int oxp_rgb_status_store(u8 enabled, u8 speed, u8 brightness) 409 { 410 u16 up = get_usage_page(drvdata.hdev); 411 u8 *data; 412 413 /* Always default to max brightness and use intensity scaling when in 414 * monocolor mode. 415 */ 416 switch (up) { 417 case GEN1_USAGE_PAGE: 418 data = (u8[4]) { OXP_SET_PROPERTY, enabled, speed, brightness }; 419 if (drvdata.rgb_effect == OXP_EFFECT_MONO_LIST) 420 data[3] = 0x04; 421 return oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, 4); 422 case GEN2_USAGE_PAGE: 423 data = (u8[6]) { OXP_SET_PROPERTY, 0x00, 0x02, enabled, speed, brightness }; 424 if (drvdata.rgb_effect == OXP_EFFECT_MONO_LIST) 425 data[5] = 0x04; 426 return oxp_gen_2_property_out(OXP_FID_GEN2_STATUS_EVENT, data, 6); 427 default: 428 return -ENODEV; 429 } 430 } 431 432 static ssize_t oxp_rgb_status_show(void) 433 { 434 u16 up = get_usage_page(drvdata.hdev); 435 u8 *data; 436 437 switch (up) { 438 case GEN1_USAGE_PAGE: 439 data = (u8[1]) { OXP_GET_PROPERTY }; 440 return oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, 1); 441 case GEN2_USAGE_PAGE: 442 data = (u8[3]) { OXP_GET_PROPERTY, 0x00, 0x02 }; 443 return oxp_gen_2_property_out(OXP_FID_GEN2_STATUS_EVENT, data, 3); 444 default: 445 return -ENODEV; 446 } 447 } 448 449 static int oxp_rgb_color_set(void) 450 { 451 u8 max_br = drvdata.led_mc->led_cdev.max_brightness; 452 u8 br = drvdata.led_mc->led_cdev.brightness; 453 u16 up = get_usage_page(drvdata.hdev); 454 u8 green, red, blue; 455 size_t size; 456 u8 *data; 457 int i; 458 459 red = br * drvdata.led_mc->subled_info[0].intensity / max_br; 460 green = br * drvdata.led_mc->subled_info[1].intensity / max_br; 461 blue = br * drvdata.led_mc->subled_info[2].intensity / max_br; 462 463 switch (up) { 464 case GEN1_USAGE_PAGE: 465 size = 55; 466 data = (u8[55]) { OXP_EFFECT_MONO_TRUE }; 467 468 for (i = 0; i < (size - 1) / 3; i++) { 469 data[3 * i + 1] = red; 470 data[3 * i + 2] = green; 471 data[3 * i + 3] = blue; 472 } 473 return oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, size); 474 case GEN2_USAGE_PAGE: 475 size = 57; 476 data = (u8[57]) { OXP_EFFECT_MONO_TRUE, 0x00, 0x02 }; 477 478 for (i = 1; i < size / 3; i++) { 479 data[3 * i] = red; 480 data[3 * i + 1] = green; 481 data[3 * i + 2] = blue; 482 } 483 return oxp_gen_2_property_out(OXP_FID_GEN2_STATUS_EVENT, data, size); 484 default: 485 return -ENODEV; 486 } 487 } 488 489 static int oxp_rgb_effect_set(u8 effect) 490 { 491 u16 up = get_usage_page(drvdata.hdev); 492 u8 *data; 493 int ret; 494 495 switch (effect) { 496 case OXP_EFFECT_AURORA: 497 case OXP_EFFECT_BIRTHDAY: 498 case OXP_EFFECT_FLOWING: 499 case OXP_EFFECT_CHROMA_1: 500 case OXP_EFFECT_NEON: 501 case OXP_EFFECT_CHROMA_2: 502 case OXP_EFFECT_DREAMY: 503 case OXP_EFFECT_WARM: 504 case OXP_EFFECT_CYBERPUNK: 505 case OXP_EFFECT_SEA: 506 case OXP_EFFECT_SUNSET: 507 case OXP_EFFECT_COLORFUL: 508 case OXP_EFFECT_MONSTER: 509 case OXP_EFFECT_GREEN: 510 case OXP_EFFECT_BLUE: 511 case OXP_EFFECT_YELLOW: 512 case OXP_EFFECT_TEAL: 513 case OXP_EFFECT_PURPLE: 514 case OXP_EFFECT_FOGGY: 515 switch (up) { 516 case GEN1_USAGE_PAGE: 517 data = (u8[1]) { effect }; 518 ret = oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, 1); 519 break; 520 case GEN2_USAGE_PAGE: 521 data = (u8[3]) { effect, 0x00, 0x02 }; 522 ret = oxp_gen_2_property_out(OXP_FID_GEN2_STATUS_EVENT, data, 3); 523 break; 524 default: 525 ret = -ENODEV; 526 } 527 break; 528 case OXP_EFFECT_MONO_LIST: 529 ret = oxp_rgb_color_set(); 530 break; 531 default: 532 return -EINVAL; 533 } 534 535 if (ret) 536 return ret; 537 538 drvdata.rgb_effect = effect; 539 540 return 0; 541 } 542 543 static ssize_t enabled_store(struct device *dev, struct device_attribute *attr, 544 const char *buf, size_t count) 545 { 546 int ret; 547 u8 val; 548 549 ret = sysfs_match_string(oxp_feature_en_text, buf); 550 if (ret < 0) 551 return ret; 552 val = ret; 553 554 ret = oxp_rgb_status_store(val, drvdata.rgb_speed, 555 drvdata.rgb_brightness); 556 if (ret) 557 return ret; 558 559 drvdata.rgb_en = val; 560 return count; 561 } 562 563 static ssize_t enabled_show(struct device *dev, struct device_attribute *attr, 564 char *buf) 565 { 566 int ret; 567 568 ret = oxp_rgb_status_show(); 569 if (ret) 570 return ret; 571 572 if (drvdata.rgb_en >= ARRAY_SIZE(oxp_feature_en_text)) 573 return -EINVAL; 574 575 return sysfs_emit(buf, "%s\n", oxp_feature_en_text[drvdata.rgb_en]); 576 } 577 static DEVICE_ATTR_RW(enabled); 578 579 static ssize_t enabled_index_show(struct device *dev, 580 struct device_attribute *attr, char *buf) 581 { 582 size_t count = 0; 583 unsigned int i; 584 585 for (i = 0; i < ARRAY_SIZE(oxp_feature_en_text); i++) 586 count += sysfs_emit_at(buf, count, "%s ", oxp_feature_en_text[i]); 587 588 if (count) 589 buf[count - 1] = '\n'; 590 591 return count; 592 } 593 static DEVICE_ATTR_RO(enabled_index); 594 595 static ssize_t effect_store(struct device *dev, struct device_attribute *attr, 596 const char *buf, size_t count) 597 { 598 int ret; 599 u8 val; 600 601 ret = sysfs_match_string(oxp_rgb_effect_text, buf); 602 if (ret < 0) 603 return ret; 604 605 val = ret; 606 607 ret = oxp_rgb_status_store(drvdata.rgb_en, drvdata.rgb_speed, 608 drvdata.rgb_brightness); 609 if (ret) 610 return ret; 611 612 ret = oxp_rgb_effect_set(val); 613 if (ret) 614 return ret; 615 616 return count; 617 } 618 619 static ssize_t effect_show(struct device *dev, struct device_attribute *attr, 620 char *buf) 621 { 622 int ret; 623 624 ret = oxp_rgb_status_show(); 625 if (ret) 626 return ret; 627 628 if (drvdata.rgb_effect >= ARRAY_SIZE(oxp_rgb_effect_text)) 629 return -EINVAL; 630 631 return sysfs_emit(buf, "%s\n", oxp_rgb_effect_text[drvdata.rgb_effect]); 632 } 633 634 static DEVICE_ATTR_RW(effect); 635 636 static ssize_t effect_index_show(struct device *dev, 637 struct device_attribute *attr, char *buf) 638 { 639 size_t count = 0; 640 unsigned int i; 641 642 for (i = 1; i < ARRAY_SIZE(oxp_rgb_effect_text); i++) 643 count += sysfs_emit_at(buf, count, "%s ", oxp_rgb_effect_text[i]); 644 645 if (count) 646 buf[count - 1] = '\n'; 647 648 return count; 649 } 650 static DEVICE_ATTR_RO(effect_index); 651 652 static ssize_t speed_store(struct device *dev, struct device_attribute *attr, 653 const char *buf, size_t count) 654 { 655 int ret; 656 u8 val; 657 658 ret = kstrtou8(buf, 10, &val); 659 if (ret) 660 return ret; 661 662 if (val > 9) 663 return -EINVAL; 664 665 ret = oxp_rgb_status_store(drvdata.rgb_en, val, drvdata.rgb_brightness); 666 if (ret) 667 return ret; 668 669 drvdata.rgb_speed = val; 670 return count; 671 } 672 673 static ssize_t speed_show(struct device *dev, struct device_attribute *attr, 674 char *buf) 675 { 676 int ret; 677 678 ret = oxp_rgb_status_show(); 679 if (ret) 680 return ret; 681 682 if (drvdata.rgb_speed > 9) 683 return -EINVAL; 684 685 return sysfs_emit(buf, "%hhu\n", drvdata.rgb_speed); 686 } 687 static DEVICE_ATTR_RW(speed); 688 689 static ssize_t speed_range_show(struct device *dev, 690 struct device_attribute *attr, char *buf) 691 { 692 return sysfs_emit(buf, "0-9\n"); 693 } 694 static DEVICE_ATTR_RO(speed_range); 695 696 static void oxp_rgb_queue_fn(struct work_struct *work) 697 { 698 unsigned int max_brightness = drvdata.led_mc->led_cdev.max_brightness; 699 unsigned int brightness = drvdata.led_mc->led_cdev.brightness; 700 u8 val = 4 * brightness / max_brightness; 701 int ret; 702 703 if (drvdata.rgb_brightness != val) { 704 ret = oxp_rgb_status_store(drvdata.rgb_en, drvdata.rgb_speed, val); 705 if (ret) 706 dev_err(drvdata.led_mc->led_cdev.dev, 707 "Error: Failed to write RGB Status: %i\n", ret); 708 709 drvdata.rgb_brightness = val; 710 } 711 712 if (drvdata.rgb_effect != OXP_EFFECT_MONO_LIST) 713 return; 714 715 ret = oxp_rgb_effect_set(drvdata.rgb_effect); 716 if (ret) 717 dev_err(drvdata.led_mc->led_cdev.dev, "Error: Failed to write RGB color: %i\n", 718 ret); 719 } 720 721 static void oxp_rgb_brightness_set(struct led_classdev *led_cdev, 722 enum led_brightness brightness) 723 { 724 led_cdev->brightness = brightness; 725 mod_delayed_work(system_wq, &drvdata.oxp_rgb_queue, msecs_to_jiffies(50)); 726 } 727 728 static struct attribute *oxp_rgb_attrs[] = { 729 &dev_attr_effect.attr, 730 &dev_attr_effect_index.attr, 731 &dev_attr_enabled.attr, 732 &dev_attr_enabled_index.attr, 733 &dev_attr_speed.attr, 734 &dev_attr_speed_range.attr, 735 NULL, 736 }; 737 738 static const struct attribute_group oxp_rgb_attr_group = { 739 .attrs = oxp_rgb_attrs, 740 }; 741 742 static struct mc_subled oxp_rgb_subled_info[] = { 743 { 744 .color_index = LED_COLOR_ID_RED, 745 .intensity = 0x24, 746 .channel = 0x1, 747 }, 748 { 749 .color_index = LED_COLOR_ID_GREEN, 750 .intensity = 0x22, 751 .channel = 0x2, 752 }, 753 { 754 .color_index = LED_COLOR_ID_BLUE, 755 .intensity = 0x99, 756 .channel = 0x3, 757 }, 758 }; 759 760 static struct led_classdev_mc oxp_cdev_rgb = { 761 .led_cdev = { 762 .name = "oxp:rgb:joystick_rings", 763 .color = LED_COLOR_ID_RGB, 764 .brightness = 0x64, 765 .max_brightness = 0x64, 766 .brightness_set = oxp_rgb_brightness_set, 767 }, 768 .num_colors = ARRAY_SIZE(oxp_rgb_subled_info), 769 .subled_info = oxp_rgb_subled_info, 770 }; 771 772 struct quirk_entry { 773 bool hybrid_mcu; 774 }; 775 776 static struct quirk_entry quirk_hybrid_mcu = { 777 .hybrid_mcu = true, 778 }; 779 780 static const struct dmi_system_id oxp_hybrid_mcu_list[] = { 781 { 782 .ident = "OneXPlayer Apex", 783 .matches = { 784 DMI_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), 785 DMI_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER APEX"), 786 }, 787 .driver_data = &quirk_hybrid_mcu, 788 }, 789 { 790 .ident = "OneXPlayer G1 AMD", 791 .matches = { 792 DMI_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), 793 DMI_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER G1 A"), 794 }, 795 .driver_data = &quirk_hybrid_mcu, 796 }, 797 { 798 .ident = "OneXPlayer G1 Intel", 799 .matches = { 800 DMI_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), 801 DMI_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER G1 i"), 802 }, 803 .driver_data = &quirk_hybrid_mcu, 804 }, 805 {}, 806 }; 807 808 static bool oxp_hybrid_mcu_device(void) 809 { 810 const struct dmi_system_id *dmi_id; 811 struct quirk_entry *quirks; 812 813 dmi_id = dmi_first_match(oxp_hybrid_mcu_list); 814 if (!dmi_id) 815 return false; 816 817 quirks = dmi_id->driver_data; 818 819 return quirks->hybrid_mcu; 820 } 821 822 static int oxp_cfg_probe(struct hid_device *hdev, u16 up) 823 { 824 int ret; 825 826 hid_set_drvdata(hdev, &drvdata); 827 mutex_init(&drvdata.cfg_mutex); 828 drvdata.hdev = hdev; 829 830 if (up == GEN2_USAGE_PAGE && oxp_hybrid_mcu_device()) 831 goto skip_rgb; 832 833 drvdata.led_mc = &oxp_cdev_rgb; 834 835 INIT_DELAYED_WORK(&drvdata.oxp_rgb_queue, oxp_rgb_queue_fn); 836 ret = devm_led_classdev_multicolor_register(&hdev->dev, &oxp_cdev_rgb); 837 if (ret) 838 return dev_err_probe(&hdev->dev, ret, 839 "Failed to create RGB device\n"); 840 841 ret = devm_device_add_group(drvdata.led_mc->led_cdev.dev, 842 &oxp_rgb_attr_group); 843 if (ret) 844 return dev_err_probe(drvdata.led_mc->led_cdev.dev, ret, 845 "Failed to create RGB configuration attributes\n"); 846 847 ret = oxp_rgb_status_show(); 848 if (ret) 849 dev_warn(drvdata.led_mc->led_cdev.dev, 850 "Failed to query RGB initial state: %i\n", ret); 851 852 /* Below features are only implemented in gen 2 */ 853 if (up != GEN2_USAGE_PAGE) 854 return 0; 855 856 skip_rgb: 857 drvdata.gamepad_mode = OXP_GP_MODE_XINPUT; 858 859 INIT_DELAYED_WORK(&drvdata.oxp_mcu_init, oxp_mcu_init_fn); 860 mod_delayed_work(system_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(50)); 861 862 ret = devm_device_add_group(&hdev->dev, &oxp_cfg_attrs_group); 863 if (ret) 864 return dev_err_probe(&hdev->dev, ret, 865 "Failed to attach configuration attributes\n"); 866 867 return 0; 868 } 869 870 static int oxp_hid_probe(struct hid_device *hdev, 871 const struct hid_device_id *id) 872 { 873 int ret; 874 u16 up; 875 876 ret = hid_parse(hdev); 877 if (ret) 878 return dev_err_probe(&hdev->dev, ret, "Failed to parse HID device\n"); 879 880 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 881 if (ret) 882 return dev_err_probe(&hdev->dev, ret, "Failed to start HID device\n"); 883 884 ret = hid_hw_open(hdev); 885 if (ret) { 886 hid_hw_stop(hdev); 887 return dev_err_probe(&hdev->dev, ret, "Failed to open HID device\n"); 888 } 889 890 up = get_usage_page(hdev); 891 dev_dbg(&hdev->dev, "Got usage page %04x\n", up); 892 893 switch (up) { 894 case GEN1_USAGE_PAGE: 895 case GEN2_USAGE_PAGE: 896 ret = oxp_cfg_probe(hdev, up); 897 if (ret) { 898 hid_hw_close(hdev); 899 hid_hw_stop(hdev); 900 } 901 902 return ret; 903 default: 904 return 0; 905 } 906 } 907 908 static void oxp_hid_remove(struct hid_device *hdev) 909 { 910 cancel_delayed_work(&drvdata.oxp_rgb_queue); 911 cancel_delayed_work(&drvdata.oxp_mcu_init); 912 hid_hw_close(hdev); 913 hid_hw_stop(hdev); 914 } 915 916 static const struct hid_device_id oxp_devices[] = { 917 { HID_USB_DEVICE(USB_VENDOR_ID_CRSC, USB_DEVICE_ID_ONEXPLAYER_GEN1) }, 918 { HID_USB_DEVICE(USB_VENDOR_ID_WCH, USB_DEVICE_ID_ONEXPLAYER_GEN2) }, 919 {} 920 }; 921 922 MODULE_DEVICE_TABLE(hid, oxp_devices); 923 static struct hid_driver hid_oxp = { 924 .name = "hid-oxp", 925 .id_table = oxp_devices, 926 .probe = oxp_hid_probe, 927 .remove = oxp_hid_remove, 928 .raw_event = oxp_hid_raw_event, 929 }; 930 module_hid_driver(hid_oxp); 931 932 MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>"); 933 MODULE_DESCRIPTION("Driver for OneXPlayer HID Interfaces"); 934 MODULE_LICENSE("GPL"); 935