in hid-playstation.c [1010:1186]
static int dualsense_parse_report(struct ps_device *ps_dev, struct hid_report *report,
u8 *data, int size)
{
struct hid_device *hdev = ps_dev->hdev;
struct dualsense *ds = container_of(ps_dev, struct dualsense, base);
struct dualsense_input_report *ds_report;
uint8_t battery_data, battery_capacity, charging_status, value;
int battery_status;
uint32_t sensor_timestamp;
bool btn_mic_state;
unsigned long flags;
int i;
/*
* DualSense in USB uses the full HID report for reportID 1, but
* Bluetooth uses a minimal HID report for reportID 1 and reports
* the full report using reportID 49.
*/
if (hdev->bus == BUS_USB && report->id == DS_INPUT_REPORT_USB &&
size == DS_INPUT_REPORT_USB_SIZE) {
ds_report = (struct dualsense_input_report *)&data[1];
} else if (hdev->bus == BUS_BLUETOOTH && report->id == DS_INPUT_REPORT_BT &&
size == DS_INPUT_REPORT_BT_SIZE) {
/* Last 4 bytes of input report contain crc32 */
uint32_t report_crc = get_unaligned_le32(&data[size - 4]);
if (!ps_check_crc32(PS_INPUT_CRC32_SEED, data, size - 4, report_crc)) {
hid_err(hdev, "DualSense input CRC's check failed\n");
return -EILSEQ;
}
ds_report = (struct dualsense_input_report *)&data[2];
} else {
hid_err(hdev, "Unhandled reportID=%d\n", report->id);
return -1;
}
input_report_abs(ds->gamepad, ABS_X, ds_report->x);
input_report_abs(ds->gamepad, ABS_Y, ds_report->y);
input_report_abs(ds->gamepad, ABS_RX, ds_report->rx);
input_report_abs(ds->gamepad, ABS_RY, ds_report->ry);
input_report_abs(ds->gamepad, ABS_Z, ds_report->z);
input_report_abs(ds->gamepad, ABS_RZ, ds_report->rz);
value = ds_report->buttons[0] & DS_BUTTONS0_HAT_SWITCH;
if (value >= ARRAY_SIZE(ps_gamepad_hat_mapping))
value = 8; /* center */
input_report_abs(ds->gamepad, ABS_HAT0X, ps_gamepad_hat_mapping[value].x);
input_report_abs(ds->gamepad, ABS_HAT0Y, ps_gamepad_hat_mapping[value].y);
input_report_key(ds->gamepad, BTN_WEST, ds_report->buttons[0] & DS_BUTTONS0_SQUARE);
input_report_key(ds->gamepad, BTN_SOUTH, ds_report->buttons[0] & DS_BUTTONS0_CROSS);
input_report_key(ds->gamepad, BTN_EAST, ds_report->buttons[0] & DS_BUTTONS0_CIRCLE);
input_report_key(ds->gamepad, BTN_NORTH, ds_report->buttons[0] & DS_BUTTONS0_TRIANGLE);
input_report_key(ds->gamepad, BTN_TL, ds_report->buttons[1] & DS_BUTTONS1_L1);
input_report_key(ds->gamepad, BTN_TR, ds_report->buttons[1] & DS_BUTTONS1_R1);
input_report_key(ds->gamepad, BTN_TL2, ds_report->buttons[1] & DS_BUTTONS1_L2);
input_report_key(ds->gamepad, BTN_TR2, ds_report->buttons[1] & DS_BUTTONS1_R2);
input_report_key(ds->gamepad, BTN_SELECT, ds_report->buttons[1] & DS_BUTTONS1_CREATE);
input_report_key(ds->gamepad, BTN_START, ds_report->buttons[1] & DS_BUTTONS1_OPTIONS);
input_report_key(ds->gamepad, BTN_THUMBL, ds_report->buttons[1] & DS_BUTTONS1_L3);
input_report_key(ds->gamepad, BTN_THUMBR, ds_report->buttons[1] & DS_BUTTONS1_R3);
input_report_key(ds->gamepad, BTN_MODE, ds_report->buttons[2] & DS_BUTTONS2_PS_HOME);
input_sync(ds->gamepad);
/*
* The DualSense has an internal microphone, which can be muted through a mute button
* on the device. The driver is expected to read the button state and program the device
* to mute/unmute audio at the hardware level.
*/
btn_mic_state = !!(ds_report->buttons[2] & DS_BUTTONS2_MIC_MUTE);
if (btn_mic_state && !ds->last_btn_mic_state) {
spin_lock_irqsave(&ps_dev->lock, flags);
ds->update_mic_mute = true;
ds->mic_muted = !ds->mic_muted; /* toggle */
spin_unlock_irqrestore(&ps_dev->lock, flags);
/* Schedule updating of microphone state at hardware level. */
schedule_work(&ds->output_worker);
}
ds->last_btn_mic_state = btn_mic_state;
/* Parse and calibrate gyroscope data. */
for (i = 0; i < ARRAY_SIZE(ds_report->gyro); i++) {
int raw_data = (short)le16_to_cpu(ds_report->gyro[i]);
int calib_data = mult_frac(ds->gyro_calib_data[i].sens_numer,
raw_data - ds->gyro_calib_data[i].bias,
ds->gyro_calib_data[i].sens_denom);
input_report_abs(ds->sensors, ds->gyro_calib_data[i].abs_code, calib_data);
}
/* Parse and calibrate accelerometer data. */
for (i = 0; i < ARRAY_SIZE(ds_report->accel); i++) {
int raw_data = (short)le16_to_cpu(ds_report->accel[i]);
int calib_data = mult_frac(ds->accel_calib_data[i].sens_numer,
raw_data - ds->accel_calib_data[i].bias,
ds->accel_calib_data[i].sens_denom);
input_report_abs(ds->sensors, ds->accel_calib_data[i].abs_code, calib_data);
}
/* Convert timestamp (in 0.33us unit) to timestamp_us */
sensor_timestamp = le32_to_cpu(ds_report->sensor_timestamp);
if (!ds->sensor_timestamp_initialized) {
ds->sensor_timestamp_us = DIV_ROUND_CLOSEST(sensor_timestamp, 3);
ds->sensor_timestamp_initialized = true;
} else {
uint32_t delta;
if (ds->prev_sensor_timestamp > sensor_timestamp)
delta = (U32_MAX - ds->prev_sensor_timestamp + sensor_timestamp + 1);
else
delta = sensor_timestamp - ds->prev_sensor_timestamp;
ds->sensor_timestamp_us += DIV_ROUND_CLOSEST(delta, 3);
}
ds->prev_sensor_timestamp = sensor_timestamp;
input_event(ds->sensors, EV_MSC, MSC_TIMESTAMP, ds->sensor_timestamp_us);
input_sync(ds->sensors);
for (i = 0; i < ARRAY_SIZE(ds_report->points); i++) {
struct dualsense_touch_point *point = &ds_report->points[i];
bool active = (point->contact & DS_TOUCH_POINT_INACTIVE) ? false : true;
input_mt_slot(ds->touchpad, i);
input_mt_report_slot_state(ds->touchpad, MT_TOOL_FINGER, active);
if (active) {
int x = (point->x_hi << 8) | point->x_lo;
int y = (point->y_hi << 4) | point->y_lo;
input_report_abs(ds->touchpad, ABS_MT_POSITION_X, x);
input_report_abs(ds->touchpad, ABS_MT_POSITION_Y, y);
}
}
input_mt_sync_frame(ds->touchpad);
input_report_key(ds->touchpad, BTN_LEFT, ds_report->buttons[2] & DS_BUTTONS2_TOUCHPAD);
input_sync(ds->touchpad);
battery_data = ds_report->status & DS_STATUS_BATTERY_CAPACITY;
charging_status = (ds_report->status & DS_STATUS_CHARGING) >> DS_STATUS_CHARGING_SHIFT;
switch (charging_status) {
case 0x0:
/*
* Each unit of battery data corresponds to 10%
* 0 = 0-9%, 1 = 10-19%, .. and 10 = 100%
*/
battery_capacity = min(battery_data * 10 + 5, 100);
battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
break;
case 0x1:
battery_capacity = min(battery_data * 10 + 5, 100);
battery_status = POWER_SUPPLY_STATUS_CHARGING;
break;
case 0x2:
battery_capacity = 100;
battery_status = POWER_SUPPLY_STATUS_FULL;
break;
case 0xa: /* voltage or temperature out of range */
case 0xb: /* temperature error */
battery_capacity = 0;
battery_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
case 0xf: /* charging error */
default:
battery_capacity = 0;
battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
}
spin_lock_irqsave(&ps_dev->lock, flags);
ps_dev->battery_capacity = battery_capacity;
ps_dev->battery_status = battery_status;
spin_unlock_irqrestore(&ps_dev->lock, flags);
return 0;
}