in hid-nintendo.c [2107:2277]
static int nintendo_hid_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
int ret;
struct joycon_ctlr *ctlr;
hid_dbg(hdev, "probe - start\n");
ctlr = devm_kzalloc(&hdev->dev, sizeof(*ctlr), GFP_KERNEL);
if (!ctlr) {
ret = -ENOMEM;
goto err;
}
ctlr->hdev = hdev;
ctlr->ctlr_state = JOYCON_CTLR_STATE_INIT;
ctlr->rumble_queue_head = JC_RUMBLE_QUEUE_SIZE - 1;
ctlr->rumble_queue_tail = 0;
hid_set_drvdata(hdev, ctlr);
mutex_init(&ctlr->output_mutex);
init_waitqueue_head(&ctlr->wait);
spin_lock_init(&ctlr->lock);
ctlr->rumble_queue = alloc_workqueue("hid-nintendo-rumble_wq",
WQ_FREEZABLE | WQ_MEM_RECLAIM, 0);
INIT_WORK(&ctlr->rumble_worker, joycon_rumble_worker);
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "HID parse failed\n");
goto err_wq;
}
/*
* Patch the hw version of pro controller/joycons, so applications can
* distinguish between the default HID mappings and the mappings defined
* by the Linux game controller spec. This is important for the SDL2
* library, which has a game controller database, which uses device ids
* in combination with version as a key.
*/
hdev->version |= 0x8000;
ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
if (ret) {
hid_err(hdev, "HW start failed\n");
goto err_wq;
}
ret = hid_hw_open(hdev);
if (ret) {
hid_err(hdev, "cannot start hardware I/O\n");
goto err_stop;
}
hid_device_io_start(hdev);
/* Initialize the controller */
mutex_lock(&ctlr->output_mutex);
/* if handshake command fails, assume ble pro controller */
if ((jc_type_is_procon(ctlr) || jc_type_is_chrggrip(ctlr)) &&
!joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE, HZ)) {
hid_dbg(hdev, "detected USB controller\n");
/* set baudrate for improved latency */
ret = joycon_send_usb(ctlr, JC_USB_CMD_BAUDRATE_3M, HZ);
if (ret) {
hid_err(hdev, "Failed to set baudrate; ret=%d\n", ret);
goto err_mutex;
}
/* handshake */
ret = joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE, HZ);
if (ret) {
hid_err(hdev, "Failed handshake; ret=%d\n", ret);
goto err_mutex;
}
/*
* Set no timeout (to keep controller in USB mode).
* This doesn't send a response, so ignore the timeout.
*/
joycon_send_usb(ctlr, JC_USB_CMD_NO_TIMEOUT, HZ/10);
} else if (jc_type_is_chrggrip(ctlr)) {
hid_err(hdev, "Failed charging grip handshake\n");
ret = -ETIMEDOUT;
goto err_mutex;
}
/* get controller calibration data, and parse it */
ret = joycon_request_calibration(ctlr);
if (ret) {
/*
* We can function with default calibration, but it may be
* inaccurate. Provide a warning, and continue on.
*/
hid_warn(hdev, "Analog stick positions may be inaccurate\n");
}
/* get IMU calibration data, and parse it */
ret = joycon_request_imu_calibration(ctlr);
if (ret) {
/*
* We can function with default calibration, but it may be
* inaccurate. Provide a warning, and continue on.
*/
hid_warn(hdev, "Unable to read IMU calibration data\n");
}
/* Set the reporting mode to 0x30, which is the full report mode */
ret = joycon_set_report_mode(ctlr);
if (ret) {
hid_err(hdev, "Failed to set report mode; ret=%d\n", ret);
goto err_mutex;
}
/* Enable rumble */
ret = joycon_enable_rumble(ctlr);
if (ret) {
hid_err(hdev, "Failed to enable rumble; ret=%d\n", ret);
goto err_mutex;
}
/* Enable the IMU */
ret = joycon_enable_imu(ctlr);
if (ret) {
hid_err(hdev, "Failed to enable the IMU; ret=%d\n", ret);
goto err_mutex;
}
ret = joycon_read_info(ctlr);
if (ret) {
hid_err(hdev, "Failed to retrieve controller info; ret=%d\n",
ret);
goto err_mutex;
}
mutex_unlock(&ctlr->output_mutex);
/* Initialize the leds */
ret = joycon_leds_create(ctlr);
if (ret) {
hid_err(hdev, "Failed to create leds; ret=%d\n", ret);
goto err_close;
}
/* Initialize the battery power supply */
ret = joycon_power_supply_create(ctlr);
if (ret) {
hid_err(hdev, "Failed to create power_supply; ret=%d\n", ret);
goto err_close;
}
ret = joycon_input_create(ctlr);
if (ret) {
hid_err(hdev, "Failed to create input device; ret=%d\n", ret);
goto err_close;
}
ctlr->ctlr_state = JOYCON_CTLR_STATE_READ;
hid_dbg(hdev, "probe - success\n");
return 0;
err_mutex:
mutex_unlock(&ctlr->output_mutex);
err_close:
hid_hw_close(hdev);
err_stop:
hid_hw_stop(hdev);
err_wq:
destroy_workqueue(ctlr->rumble_queue);
err:
hid_err(hdev, "probe - fail = %d\n", ret);
return ret;
}