Loading drivers/hid/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -736,6 +736,7 @@ config HID_NINTENDO depends on HID depends on NEW_LEDS depends on LEDS_CLASS select POWER_SUPPLY help Adds support for the Nintendo Switch Joy-Cons and Pro Controller. All controllers support bluetooth, and the Pro Controller also supports Loading drivers/hid/hid-nintendo.c +134 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ * https://github.com/MTCKC/ProconXInput * hid-wiimote kernel hid driver * hid-logitech-hidpp driver * hid-sony driver * * This driver supports the Nintendo Switch Joy-Cons and Pro Controllers. The * Pro Controllers can either be used over USB or Bluetooth. Loading @@ -27,6 +28,7 @@ #include <linux/input.h> #include <linux/leds.h> #include <linux/module.h> #include <linux/power_supply.h> #include <linux/spinlock.h> /* Loading Loading @@ -199,6 +201,7 @@ struct joycon_ctlr { struct input_dev *input; struct led_classdev leds[JC_NUM_LEDS]; enum joycon_ctlr_state ctlr_state; spinlock_t lock; /* The following members are used for synchronous sends/receives */ enum joycon_msg_type msg_type; Loading @@ -216,6 +219,12 @@ struct joycon_ctlr { struct joycon_stick_cal right_stick_cal_x; struct joycon_stick_cal right_stick_cal_y; /* power supply data */ struct power_supply *battery; struct power_supply_desc battery_desc; u8 battery_capacity; bool battery_charging; bool host_powered; }; static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len) Loading Loading @@ -446,9 +455,41 @@ 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; u32 id = ctlr->hdev->product; /* Parse the battery status */ tmp = rep->bat_con; spin_lock_irqsave(&ctlr->lock, flags); 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 (id != USB_DEVICE_ID_NINTENDO_JOYCONR) { Loading Loading @@ -743,6 +784,91 @@ static int joycon_player_leds_create(struct joycon_ctlr *ctlr) return 0; } static int joycon_battery_get_property(struct power_supply *supply, enum power_supply_property prop, union power_supply_propval *val) { struct joycon_ctlr *ctlr = power_supply_get_drvdata(supply); unsigned long flags; int ret = 0; u8 capacity; bool charging; bool powered; spin_lock_irqsave(&ctlr->lock, flags); capacity = ctlr->battery_capacity; charging = ctlr->battery_charging; powered = ctlr->host_powered; spin_unlock_irqrestore(&ctlr->lock, flags); switch (prop) { case POWER_SUPPLY_PROP_PRESENT: val->intval = 1; break; case POWER_SUPPLY_PROP_SCOPE: val->intval = POWER_SUPPLY_SCOPE_DEVICE; break; case POWER_SUPPLY_PROP_CAPACITY_LEVEL: val->intval = capacity; break; case POWER_SUPPLY_PROP_STATUS: if (charging) val->intval = POWER_SUPPLY_STATUS_CHARGING; else if (capacity == POWER_SUPPLY_CAPACITY_LEVEL_FULL && powered) val->intval = POWER_SUPPLY_STATUS_FULL; else val->intval = POWER_SUPPLY_STATUS_DISCHARGING; break; default: ret = -EINVAL; break; } return ret; } static enum power_supply_property joycon_battery_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_STATUS, }; static int joycon_power_supply_create(struct joycon_ctlr *ctlr) { struct hid_device *hdev = ctlr->hdev; struct power_supply_config supply_config = { .drv_data = ctlr, }; const char * const name_fmt = "nintendo_switch_controller_battery_%s"; int ret = 0; /* Set initially to unknown before receiving first input report */ ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; /* Configure the battery's description */ ctlr->battery_desc.properties = joycon_battery_props; ctlr->battery_desc.num_properties = ARRAY_SIZE(joycon_battery_props); ctlr->battery_desc.get_property = joycon_battery_get_property; ctlr->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; ctlr->battery_desc.use_for_apm = 0; ctlr->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL, name_fmt, dev_name(&hdev->dev)); if (!ctlr->battery_desc.name) return -ENOMEM; ctlr->battery = devm_power_supply_register(&hdev->dev, &ctlr->battery_desc, &supply_config); if (IS_ERR(ctlr->battery)) { ret = PTR_ERR(ctlr->battery); hid_err(hdev, "Failed to register battery; ret=%d\n", ret); return ret; } return power_supply_powers(ctlr->battery, &hdev->dev); } /* Common handler for parsing inputs */ static int joycon_ctlr_read_handler(struct joycon_ctlr *ctlr, u8 *data, int size) Loading Loading @@ -834,6 +960,7 @@ static int nintendo_hid_probe(struct hid_device *hdev, hid_set_drvdata(hdev, ctlr); mutex_init(&ctlr->output_mutex); init_waitqueue_head(&ctlr->wait); spin_lock_init(&ctlr->lock); ret = hid_parse(hdev); if (ret) { Loading Loading @@ -906,6 +1033,13 @@ static int nintendo_hid_probe(struct hid_device *hdev, 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); Loading Loading
drivers/hid/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -736,6 +736,7 @@ config HID_NINTENDO depends on HID depends on NEW_LEDS depends on LEDS_CLASS select POWER_SUPPLY help Adds support for the Nintendo Switch Joy-Cons and Pro Controller. All controllers support bluetooth, and the Pro Controller also supports Loading
drivers/hid/hid-nintendo.c +134 −0 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ * https://github.com/MTCKC/ProconXInput * hid-wiimote kernel hid driver * hid-logitech-hidpp driver * hid-sony driver * * This driver supports the Nintendo Switch Joy-Cons and Pro Controllers. The * Pro Controllers can either be used over USB or Bluetooth. Loading @@ -27,6 +28,7 @@ #include <linux/input.h> #include <linux/leds.h> #include <linux/module.h> #include <linux/power_supply.h> #include <linux/spinlock.h> /* Loading Loading @@ -199,6 +201,7 @@ struct joycon_ctlr { struct input_dev *input; struct led_classdev leds[JC_NUM_LEDS]; enum joycon_ctlr_state ctlr_state; spinlock_t lock; /* The following members are used for synchronous sends/receives */ enum joycon_msg_type msg_type; Loading @@ -216,6 +219,12 @@ struct joycon_ctlr { struct joycon_stick_cal right_stick_cal_x; struct joycon_stick_cal right_stick_cal_y; /* power supply data */ struct power_supply *battery; struct power_supply_desc battery_desc; u8 battery_capacity; bool battery_charging; bool host_powered; }; static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len) Loading Loading @@ -446,9 +455,41 @@ 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; u32 id = ctlr->hdev->product; /* Parse the battery status */ tmp = rep->bat_con; spin_lock_irqsave(&ctlr->lock, flags); 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 (id != USB_DEVICE_ID_NINTENDO_JOYCONR) { Loading Loading @@ -743,6 +784,91 @@ static int joycon_player_leds_create(struct joycon_ctlr *ctlr) return 0; } static int joycon_battery_get_property(struct power_supply *supply, enum power_supply_property prop, union power_supply_propval *val) { struct joycon_ctlr *ctlr = power_supply_get_drvdata(supply); unsigned long flags; int ret = 0; u8 capacity; bool charging; bool powered; spin_lock_irqsave(&ctlr->lock, flags); capacity = ctlr->battery_capacity; charging = ctlr->battery_charging; powered = ctlr->host_powered; spin_unlock_irqrestore(&ctlr->lock, flags); switch (prop) { case POWER_SUPPLY_PROP_PRESENT: val->intval = 1; break; case POWER_SUPPLY_PROP_SCOPE: val->intval = POWER_SUPPLY_SCOPE_DEVICE; break; case POWER_SUPPLY_PROP_CAPACITY_LEVEL: val->intval = capacity; break; case POWER_SUPPLY_PROP_STATUS: if (charging) val->intval = POWER_SUPPLY_STATUS_CHARGING; else if (capacity == POWER_SUPPLY_CAPACITY_LEVEL_FULL && powered) val->intval = POWER_SUPPLY_STATUS_FULL; else val->intval = POWER_SUPPLY_STATUS_DISCHARGING; break; default: ret = -EINVAL; break; } return ret; } static enum power_supply_property joycon_battery_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_SCOPE, POWER_SUPPLY_PROP_STATUS, }; static int joycon_power_supply_create(struct joycon_ctlr *ctlr) { struct hid_device *hdev = ctlr->hdev; struct power_supply_config supply_config = { .drv_data = ctlr, }; const char * const name_fmt = "nintendo_switch_controller_battery_%s"; int ret = 0; /* Set initially to unknown before receiving first input report */ ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; /* Configure the battery's description */ ctlr->battery_desc.properties = joycon_battery_props; ctlr->battery_desc.num_properties = ARRAY_SIZE(joycon_battery_props); ctlr->battery_desc.get_property = joycon_battery_get_property; ctlr->battery_desc.type = POWER_SUPPLY_TYPE_BATTERY; ctlr->battery_desc.use_for_apm = 0; ctlr->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL, name_fmt, dev_name(&hdev->dev)); if (!ctlr->battery_desc.name) return -ENOMEM; ctlr->battery = devm_power_supply_register(&hdev->dev, &ctlr->battery_desc, &supply_config); if (IS_ERR(ctlr->battery)) { ret = PTR_ERR(ctlr->battery); hid_err(hdev, "Failed to register battery; ret=%d\n", ret); return ret; } return power_supply_powers(ctlr->battery, &hdev->dev); } /* Common handler for parsing inputs */ static int joycon_ctlr_read_handler(struct joycon_ctlr *ctlr, u8 *data, int size) Loading Loading @@ -834,6 +960,7 @@ static int nintendo_hid_probe(struct hid_device *hdev, hid_set_drvdata(hdev, ctlr); mutex_init(&ctlr->output_mutex); init_waitqueue_head(&ctlr->wait); spin_lock_init(&ctlr->lock); ret = hid_parse(hdev); if (ret) { Loading Loading @@ -906,6 +1033,13 @@ static int nintendo_hid_probe(struct hid_device *hdev, 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); Loading