in hid-nintendo.c [1214:1377]
static void joycon_parse_report(struct joycon_ctlr *ctlr,
struct joycon_input_report *rep)
{
struct input_dev *dev = ctlr->input;
unsigned long flags;
u8 tmp;
u32 btns;
unsigned long msecs = jiffies_to_msecs(jiffies);
spin_lock_irqsave(&ctlr->lock, flags);
if (IS_ENABLED(CONFIG_NINTENDO_FF) && rep->vibrator_report &&
(msecs - ctlr->rumble_msecs) >= JC_RUMBLE_PERIOD_MS &&
(ctlr->rumble_queue_head != ctlr->rumble_queue_tail ||
ctlr->rumble_zero_countdown > 0)) {
/*
* When this value reaches 0, we know we've sent multiple
* packets to the controller instructing it to disable rumble.
* We can safely stop sending periodic rumble packets until the
* next ff effect.
*/
if (ctlr->rumble_zero_countdown > 0)
ctlr->rumble_zero_countdown--;
queue_work(ctlr->rumble_queue, &ctlr->rumble_worker);
}
/* Parse the battery status */
tmp = rep->bat_con;
ctlr->host_powered = tmp & BIT(0);
ctlr->battery_charging = tmp & BIT(4);
tmp = tmp >> 5;
switch (tmp) {
case 0: /* empty */
ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
break;
case 1: /* low */
ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
break;
case 2: /* medium */
ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
break;
case 3: /* high */
ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
break;
case 4: /* full */
ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
break;
default:
ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
hid_warn(ctlr->hdev, "Invalid battery status\n");
break;
}
spin_unlock_irqrestore(&ctlr->lock, flags);
/* Parse the buttons and sticks */
btns = hid_field_extract(ctlr->hdev, rep->button_status, 0, 24);
if (jc_type_has_left(ctlr)) {
u16 raw_x;
u16 raw_y;
s32 x;
s32 y;
/* get raw stick values */
raw_x = hid_field_extract(ctlr->hdev, rep->left_stick, 0, 12);
raw_y = hid_field_extract(ctlr->hdev,
rep->left_stick + 1, 4, 12);
/* map the stick values */
x = joycon_map_stick_val(&ctlr->left_stick_cal_x, raw_x);
y = -joycon_map_stick_val(&ctlr->left_stick_cal_y, raw_y);
/* report sticks */
input_report_abs(dev, ABS_X, x);
input_report_abs(dev, ABS_Y, y);
/* report buttons */
input_report_key(dev, BTN_TL, btns & JC_BTN_L);
input_report_key(dev, BTN_TL2, btns & JC_BTN_ZL);
input_report_key(dev, BTN_SELECT, btns & JC_BTN_MINUS);
input_report_key(dev, BTN_THUMBL, btns & JC_BTN_LSTICK);
input_report_key(dev, BTN_Z, btns & JC_BTN_CAP);
if (jc_type_is_joycon(ctlr)) {
/* Report the S buttons as the non-existent triggers */
input_report_key(dev, BTN_TR, btns & JC_BTN_SL_L);
input_report_key(dev, BTN_TR2, btns & JC_BTN_SR_L);
/* Report d-pad as digital buttons for the joy-cons */
input_report_key(dev, BTN_DPAD_DOWN,
btns & JC_BTN_DOWN);
input_report_key(dev, BTN_DPAD_UP, btns & JC_BTN_UP);
input_report_key(dev, BTN_DPAD_RIGHT,
btns & JC_BTN_RIGHT);
input_report_key(dev, BTN_DPAD_LEFT,
btns & JC_BTN_LEFT);
} else {
int hatx = 0;
int haty = 0;
/* d-pad x */
if (btns & JC_BTN_LEFT)
hatx = -1;
else if (btns & JC_BTN_RIGHT)
hatx = 1;
input_report_abs(dev, ABS_HAT0X, hatx);
/* d-pad y */
if (btns & JC_BTN_UP)
haty = -1;
else if (btns & JC_BTN_DOWN)
haty = 1;
input_report_abs(dev, ABS_HAT0Y, haty);
}
}
if (jc_type_has_right(ctlr)) {
u16 raw_x;
u16 raw_y;
s32 x;
s32 y;
/* get raw stick values */
raw_x = hid_field_extract(ctlr->hdev, rep->right_stick, 0, 12);
raw_y = hid_field_extract(ctlr->hdev,
rep->right_stick + 1, 4, 12);
/* map stick values */
x = joycon_map_stick_val(&ctlr->right_stick_cal_x, raw_x);
y = -joycon_map_stick_val(&ctlr->right_stick_cal_y, raw_y);
/* report sticks */
input_report_abs(dev, ABS_RX, x);
input_report_abs(dev, ABS_RY, y);
/* report buttons */
input_report_key(dev, BTN_TR, btns & JC_BTN_R);
input_report_key(dev, BTN_TR2, btns & JC_BTN_ZR);
if (jc_type_is_joycon(ctlr)) {
/* Report the S buttons as the non-existent triggers */
input_report_key(dev, BTN_TL, btns & JC_BTN_SL_R);
input_report_key(dev, BTN_TL2, btns & JC_BTN_SR_R);
}
input_report_key(dev, BTN_START, btns & JC_BTN_PLUS);
input_report_key(dev, BTN_THUMBR, btns & JC_BTN_RSTICK);
input_report_key(dev, BTN_MODE, btns & JC_BTN_HOME);
input_report_key(dev, BTN_WEST, btns & JC_BTN_Y);
input_report_key(dev, BTN_NORTH, btns & JC_BTN_X);
input_report_key(dev, BTN_EAST, btns & JC_BTN_A);
input_report_key(dev, BTN_SOUTH, btns & JC_BTN_B);
}
input_sync(dev);
/*
* Immediately after receiving a report is the most reliable time to
* send a subcommand to the controller. Wake any subcommand senders
* waiting for a report.
*/
if (unlikely(mutex_is_locked(&ctlr->output_mutex))) {
spin_lock_irqsave(&ctlr->lock, flags);
ctlr->received_input_report = true;
spin_unlock_irqrestore(&ctlr->lock, flags);
wake_up(&ctlr->wait);
}
/* parse IMU data if present */
if (rep->id == JC_INPUT_IMU_DATA)
joycon_parse_imu_report(ctlr, rep);
}