Skip to content

Commit 83d640c

Browse files
DanielOgorchockJiri Kosina
authored andcommitted
HID: nintendo: add support for reading user calibration
If the controller's SPI flash contains user stick calibration(s), they should be prioritized over the factory calibrations. The user calibrations have 2 magic bytes preceding them. If the bytes are the correct magic values, the user calibration is used. Signed-off-by: Daniel J. Ogorchock <djogorchock@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
1 parent 294a828 commit 83d640c

1 file changed

Lines changed: 148 additions & 58 deletions

File tree

drivers/hid/hid-nintendo.c

Lines changed: 148 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
*/
2424

2525
#include "hid-ids.h"
26+
#include <asm/unaligned.h>
2627
#include <linux/delay.h>
2728
#include <linux/device.h>
2829
#include <linux/hid.h>
@@ -96,11 +97,23 @@ static const u8 JC_USB_RESET = 0x06;
9697
static const u8 JC_USB_PRE_HANDSHAKE = 0x91;
9798
static const u8 JC_USB_SEND_UART = 0x92;
9899

99-
/* SPI storage addresses of factory calibration data */
100-
static const u16 JC_CAL_DATA_START = 0x603d;
101-
static const u16 JC_CAL_DATA_END = 0x604e;
102-
#define JC_CAL_DATA_SIZE (JC_CAL_DATA_END - JC_CAL_DATA_START + 1)
100+
/* Magic value denoting presence of user calibration */
101+
static const u16 JC_CAL_USR_MAGIC_0 = 0xB2;
102+
static const u16 JC_CAL_USR_MAGIC_1 = 0xA1;
103+
static const u8 JC_CAL_USR_MAGIC_SIZE = 2;
104+
105+
/* SPI storage addresses of user calibration data */
106+
static const u16 JC_CAL_USR_LEFT_MAGIC_ADDR = 0x8010;
107+
static const u16 JC_CAL_USR_LEFT_DATA_ADDR = 0x8012;
108+
static const u16 JC_CAL_USR_LEFT_DATA_END = 0x801A;
109+
static const u16 JC_CAL_USR_RIGHT_MAGIC_ADDR = 0x801B;
110+
static const u16 JC_CAL_USR_RIGHT_DATA_ADDR = 0x801D;
111+
#define JC_CAL_STICK_DATA_SIZE \
112+
(JC_CAL_USR_LEFT_DATA_END - JC_CAL_USR_LEFT_DATA_ADDR + 1)
103113

114+
/* SPI storage addresses of factory calibration data */
115+
static const u16 JC_CAL_FCT_DATA_LEFT_ADDR = 0x603d;
116+
static const u16 JC_CAL_FCT_DATA_RIGHT_ADDR = 0x6046;
104117

105118
/* The raw analog joystick values will be mapped in terms of this magnitude */
106119
static const u16 JC_MAX_STICK_MAG = 32767;
@@ -531,38 +544,140 @@ static int joycon_set_player_leds(struct joycon_ctlr *ctlr, u8 flash, u8 on)
531544
return joycon_send_subcmd(ctlr, req, 1, HZ/4);
532545
}
533546

534-
static const u16 DFLT_STICK_CAL_CEN = 2000;
535-
static const u16 DFLT_STICK_CAL_MAX = 3500;
536-
static const u16 DFLT_STICK_CAL_MIN = 500;
537-
static int joycon_request_calibration(struct joycon_ctlr *ctlr)
547+
static int joycon_request_spi_flash_read(struct joycon_ctlr *ctlr,
548+
u32 start_addr, u8 size, u8 **reply)
538549
{
539550
struct joycon_subcmd_request *req;
540-
u8 buffer[sizeof(*req) + 5] = { 0 };
541551
struct joycon_input_report *report;
542-
struct joycon_stick_cal *cal_x;
543-
struct joycon_stick_cal *cal_y;
552+
u8 buffer[sizeof(*req) + 5] = { 0 };
553+
u8 *data;
554+
int ret;
555+
556+
if (!reply)
557+
return -EINVAL;
558+
559+
req = (struct joycon_subcmd_request *)buffer;
560+
req->subcmd_id = JC_SUBCMD_SPI_FLASH_READ;
561+
data = req->data;
562+
put_unaligned_le32(start_addr, data);
563+
data[4] = size;
564+
565+
hid_dbg(ctlr->hdev, "requesting SPI flash data\n");
566+
ret = joycon_send_subcmd(ctlr, req, 5, HZ);
567+
if (ret) {
568+
hid_err(ctlr->hdev, "failed reading SPI flash; ret=%d\n", ret);
569+
} else {
570+
report = (struct joycon_input_report *)ctlr->input_buf;
571+
/* The read data starts at the 6th byte */
572+
*reply = &report->reply.data[5];
573+
}
574+
return ret;
575+
}
576+
577+
/*
578+
* User calibration's presence is denoted with a magic byte preceding it.
579+
* returns 0 if magic val is present, 1 if not present, < 0 on error
580+
*/
581+
static int joycon_check_for_cal_magic(struct joycon_ctlr *ctlr, u32 flash_addr)
582+
{
583+
int ret;
584+
u8 *reply;
585+
586+
ret = joycon_request_spi_flash_read(ctlr, flash_addr,
587+
JC_CAL_USR_MAGIC_SIZE, &reply);
588+
if (ret)
589+
return ret;
590+
591+
return reply[0] != JC_CAL_USR_MAGIC_0 || reply[1] != JC_CAL_USR_MAGIC_1;
592+
}
593+
594+
static int joycon_read_stick_calibration(struct joycon_ctlr *ctlr, u16 cal_addr,
595+
struct joycon_stick_cal *cal_x,
596+
struct joycon_stick_cal *cal_y,
597+
bool left_stick)
598+
{
544599
s32 x_max_above;
545600
s32 x_min_below;
546601
s32 y_max_above;
547602
s32 y_min_below;
548-
u8 *data;
549603
u8 *raw_cal;
550604
int ret;
551605

552-
req = (struct joycon_subcmd_request *)buffer;
553-
req->subcmd_id = JC_SUBCMD_SPI_FLASH_READ;
554-
data = req->data;
555-
data[0] = 0xFF & JC_CAL_DATA_START;
556-
data[1] = 0xFF & (JC_CAL_DATA_START >> 8);
557-
data[2] = 0xFF & (JC_CAL_DATA_START >> 16);
558-
data[3] = 0xFF & (JC_CAL_DATA_START >> 24);
559-
data[4] = JC_CAL_DATA_SIZE;
606+
ret = joycon_request_spi_flash_read(ctlr, cal_addr,
607+
JC_CAL_STICK_DATA_SIZE, &raw_cal);
608+
if (ret)
609+
return ret;
610+
611+
/* stick calibration parsing: note the order differs based on stick */
612+
if (left_stick) {
613+
x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0,
614+
12);
615+
y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4,
616+
12);
617+
cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0,
618+
12);
619+
cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4,
620+
12);
621+
x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0,
622+
12);
623+
y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4,
624+
12);
625+
} else {
626+
cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0,
627+
12);
628+
cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4,
629+
12);
630+
x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0,
631+
12);
632+
y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4,
633+
12);
634+
x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0,
635+
12);
636+
y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4,
637+
12);
638+
}
639+
640+
cal_x->max = cal_x->center + x_max_above;
641+
cal_x->min = cal_x->center - x_min_below;
642+
cal_y->max = cal_y->center + y_max_above;
643+
cal_y->min = cal_y->center - y_min_below;
644+
645+
return 0;
646+
}
647+
648+
static const u16 DFLT_STICK_CAL_CEN = 2000;
649+
static const u16 DFLT_STICK_CAL_MAX = 3500;
650+
static const u16 DFLT_STICK_CAL_MIN = 500;
651+
static int joycon_request_calibration(struct joycon_ctlr *ctlr)
652+
{
653+
u16 left_stick_addr = JC_CAL_FCT_DATA_LEFT_ADDR;
654+
u16 right_stick_addr = JC_CAL_FCT_DATA_RIGHT_ADDR;
655+
int ret;
560656

561657
hid_dbg(ctlr->hdev, "requesting cal data\n");
562-
ret = joycon_send_subcmd(ctlr, req, 5, HZ);
658+
659+
/* check if user stick calibrations are present */
660+
if (!joycon_check_for_cal_magic(ctlr, JC_CAL_USR_LEFT_MAGIC_ADDR)) {
661+
left_stick_addr = JC_CAL_USR_LEFT_DATA_ADDR;
662+
hid_info(ctlr->hdev, "using user cal for left stick\n");
663+
} else {
664+
hid_info(ctlr->hdev, "using factory cal for left stick\n");
665+
}
666+
if (!joycon_check_for_cal_magic(ctlr, JC_CAL_USR_RIGHT_MAGIC_ADDR)) {
667+
right_stick_addr = JC_CAL_USR_RIGHT_DATA_ADDR;
668+
hid_info(ctlr->hdev, "using user cal for right stick\n");
669+
} else {
670+
hid_info(ctlr->hdev, "using factory cal for right stick\n");
671+
}
672+
673+
/* read the left stick calibration data */
674+
ret = joycon_read_stick_calibration(ctlr, left_stick_addr,
675+
&ctlr->left_stick_cal_x,
676+
&ctlr->left_stick_cal_y,
677+
true);
563678
if (ret) {
564679
hid_warn(ctlr->hdev,
565-
"Failed to read stick cal, using defaults; ret=%d\n",
680+
"Failed to read left stick cal, using dflts; e=%d\n",
566681
ret);
567682

568683
ctlr->left_stick_cal_x.center = DFLT_STICK_CAL_CEN;
@@ -572,6 +687,17 @@ static int joycon_request_calibration(struct joycon_ctlr *ctlr)
572687
ctlr->left_stick_cal_y.center = DFLT_STICK_CAL_CEN;
573688
ctlr->left_stick_cal_y.max = DFLT_STICK_CAL_MAX;
574689
ctlr->left_stick_cal_y.min = DFLT_STICK_CAL_MIN;
690+
}
691+
692+
/* read the right stick calibration data */
693+
ret = joycon_read_stick_calibration(ctlr, right_stick_addr,
694+
&ctlr->right_stick_cal_x,
695+
&ctlr->right_stick_cal_y,
696+
false);
697+
if (ret) {
698+
hid_warn(ctlr->hdev,
699+
"Failed to read right stick cal, using dflts; e=%d\n",
700+
ret);
575701

576702
ctlr->right_stick_cal_x.center = DFLT_STICK_CAL_CEN;
577703
ctlr->right_stick_cal_x.max = DFLT_STICK_CAL_MAX;
@@ -580,44 +706,8 @@ static int joycon_request_calibration(struct joycon_ctlr *ctlr)
580706
ctlr->right_stick_cal_y.center = DFLT_STICK_CAL_CEN;
581707
ctlr->right_stick_cal_y.max = DFLT_STICK_CAL_MAX;
582708
ctlr->right_stick_cal_y.min = DFLT_STICK_CAL_MIN;
583-
584-
return ret;
585709
}
586710

587-
report = (struct joycon_input_report *)ctlr->input_buf;
588-
raw_cal = &report->reply.data[5];
589-
590-
/* left stick calibration parsing */
591-
cal_x = &ctlr->left_stick_cal_x;
592-
cal_y = &ctlr->left_stick_cal_y;
593-
594-
x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0, 12);
595-
y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4, 12);
596-
cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0, 12);
597-
cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4, 12);
598-
x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0, 12);
599-
y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4, 12);
600-
cal_x->max = cal_x->center + x_max_above;
601-
cal_x->min = cal_x->center - x_min_below;
602-
cal_y->max = cal_y->center + y_max_above;
603-
cal_y->min = cal_y->center - y_min_below;
604-
605-
/* right stick calibration parsing */
606-
raw_cal += 9;
607-
cal_x = &ctlr->right_stick_cal_x;
608-
cal_y = &ctlr->right_stick_cal_y;
609-
610-
cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0, 12);
611-
cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4, 12);
612-
x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0, 12);
613-
y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4, 12);
614-
x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0, 12);
615-
y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4, 12);
616-
cal_x->max = cal_x->center + x_max_above;
617-
cal_x->min = cal_x->center - x_min_below;
618-
cal_y->max = cal_y->center + y_max_above;
619-
cal_y->min = cal_y->center - y_min_below;
620-
621711
hid_dbg(ctlr->hdev, "calibration:\n"
622712
"l_x_c=%d l_x_max=%d l_x_min=%d\n"
623713
"l_y_c=%d l_y_max=%d l_y_min=%d\n"

0 commit comments

Comments
 (0)