Merge branch 'for-6.7/logitech' into for-linus

- big cleanup of logitech-hidpp probe code (Hans de Goede)
This commit is contained in:
Jiri Kosina 2023-11-01 00:12:42 +01:00
commit 54021f902b

View file

@ -69,12 +69,11 @@ MODULE_PARM_DESC(disable_tap_to_click,
#define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22) #define HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS BIT(22)
#define HIDPP_QUIRK_DELAYED_INIT BIT(23) #define HIDPP_QUIRK_DELAYED_INIT BIT(23)
#define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24) #define HIDPP_QUIRK_FORCE_OUTPUT_REPORTS BIT(24)
#define HIDPP_QUIRK_UNIFYING BIT(25) #define HIDPP_QUIRK_HIDPP_WHEELS BIT(25)
#define HIDPP_QUIRK_HIDPP_WHEELS BIT(26) #define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS BIT(26)
#define HIDPP_QUIRK_HIDPP_EXTRA_MOUSE_BTNS BIT(27) #define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS BIT(27)
#define HIDPP_QUIRK_HIDPP_CONSUMER_VENDOR_KEYS BIT(28) #define HIDPP_QUIRK_HI_RES_SCROLL_1P0 BIT(28)
#define HIDPP_QUIRK_HI_RES_SCROLL_1P0 BIT(29) #define HIDPP_QUIRK_WIRELESS_STATUS BIT(29)
#define HIDPP_QUIRK_WIRELESS_STATUS BIT(30)
/* These are just aliases for now */ /* These are just aliases for now */
#define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS #define HIDPP_QUIRK_KBD_SCROLL_WHEEL HIDPP_QUIRK_HIDPP_WHEELS
@ -194,7 +193,6 @@ struct hidpp_device {
struct work_struct work; struct work_struct work;
struct kfifo delayed_work_fifo; struct kfifo delayed_work_fifo;
atomic_t connected;
struct input_dev *delayed_input; struct input_dev *delayed_input;
unsigned long quirks; unsigned long quirks;
@ -235,8 +233,6 @@ struct hidpp_device {
#define HIDPP20_ERROR_UNSUPPORTED 0x09 #define HIDPP20_ERROR_UNSUPPORTED 0x09
#define HIDPP20_ERROR 0xff #define HIDPP20_ERROR 0xff
static void hidpp_connect_event(struct hidpp_device *hidpp_dev);
static int __hidpp_send_report(struct hid_device *hdev, static int __hidpp_send_report(struct hid_device *hdev,
struct hidpp_report *hidpp_report) struct hidpp_report *hidpp_report)
{ {
@ -450,13 +446,6 @@ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
return ret; return ret;
} }
static void delayed_work_cb(struct work_struct *work)
{
struct hidpp_device *hidpp = container_of(work, struct hidpp_device,
work);
hidpp_connect_event(hidpp);
}
static inline bool hidpp_match_answer(struct hidpp_report *question, static inline bool hidpp_match_answer(struct hidpp_report *question,
struct hidpp_report *answer) struct hidpp_report *answer)
{ {
@ -1835,15 +1824,14 @@ static int hidpp_battery_get_property(struct power_supply *psy,
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
#define HIDPP_PAGE_WIRELESS_DEVICE_STATUS 0x1d4b #define HIDPP_PAGE_WIRELESS_DEVICE_STATUS 0x1d4b
static int hidpp_set_wireless_feature_index(struct hidpp_device *hidpp) static int hidpp_get_wireless_feature_index(struct hidpp_device *hidpp, u8 *feature_index)
{ {
u8 feature_type; u8 feature_type;
int ret; int ret;
ret = hidpp_root_get_feature(hidpp, ret = hidpp_root_get_feature(hidpp,
HIDPP_PAGE_WIRELESS_DEVICE_STATUS, HIDPP_PAGE_WIRELESS_DEVICE_STATUS,
&hidpp->wireless_feature_index, feature_index, &feature_type);
&feature_type);
return ret; return ret;
} }
@ -3142,7 +3130,7 @@ static int wtp_allocate(struct hid_device *hdev, const struct hid_device_id *id)
return 0; return 0;
}; };
static int wtp_connect(struct hid_device *hdev, bool connected) static int wtp_connect(struct hid_device *hdev)
{ {
struct hidpp_device *hidpp = hid_get_drvdata(hdev); struct hidpp_device *hidpp = hid_get_drvdata(hdev);
struct wtp_data *wd = hidpp->private_data; struct wtp_data *wd = hidpp->private_data;
@ -3204,7 +3192,7 @@ static const u8 m560_config_parameter[] = {0x00, 0xaf, 0x03};
#define M560_SUB_ID 0x0a #define M560_SUB_ID 0x0a
#define M560_BUTTON_MODE_REGISTER 0x35 #define M560_BUTTON_MODE_REGISTER 0x35
static int m560_send_config_command(struct hid_device *hdev, bool connected) static int m560_send_config_command(struct hid_device *hdev)
{ {
struct hidpp_report response; struct hidpp_report response;
struct hidpp_device *hidpp_dev; struct hidpp_device *hidpp_dev;
@ -3399,7 +3387,7 @@ static int k400_allocate(struct hid_device *hdev)
return 0; return 0;
}; };
static int k400_connect(struct hid_device *hdev, bool connected) static int k400_connect(struct hid_device *hdev)
{ {
struct hidpp_device *hidpp = hid_get_drvdata(hdev); struct hidpp_device *hidpp = hid_get_drvdata(hdev);
@ -3894,8 +3882,6 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
} }
if (unlikely(hidpp_report_is_connect_event(hidpp, report))) { if (unlikely(hidpp_report_is_connect_event(hidpp, report))) {
atomic_set(&hidpp->connected,
!(report->rap.params[0] & (1 << 6)));
if (schedule_work(&hidpp->work) == 0) if (schedule_work(&hidpp->work) == 0)
dbg_hid("%s: connect event already queued\n", __func__); dbg_hid("%s: connect event already queued\n", __func__);
return 1; return 1;
@ -4131,24 +4117,22 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
return ret; return ret;
} }
static void hidpp_overwrite_name(struct hid_device *hdev) /* Get name + serial for USB and Bluetooth HID++ devices */
static void hidpp_non_unifying_init(struct hidpp_device *hidpp)
{ {
struct hidpp_device *hidpp = hid_get_drvdata(hdev); struct hid_device *hdev = hidpp->hid_dev;
char *name; char *name;
if (hidpp->protocol_major < 2) /* Bluetooth devices already have their serialnr set */
return; if (hid_is_usb(hdev))
hidpp_serial_init(hidpp);
name = hidpp_get_device_name(hidpp); name = hidpp_get_device_name(hidpp);
if (name) {
if (!name) {
hid_err(hdev, "unable to retrieve the name of the device");
} else {
dbg_hid("HID++: Got name: %s\n", name); dbg_hid("HID++: Got name: %s\n", name);
snprintf(hdev->name, sizeof(hdev->name), "%s", name); snprintf(hdev->name, sizeof(hdev->name), "%s", name);
kfree(name);
} }
kfree(name);
} }
static int hidpp_input_open(struct input_dev *dev) static int hidpp_input_open(struct input_dev *dev)
@ -4189,15 +4173,18 @@ static struct input_dev *hidpp_allocate_input(struct hid_device *hdev)
return input_dev; return input_dev;
} }
static void hidpp_connect_event(struct hidpp_device *hidpp) static void hidpp_connect_event(struct work_struct *work)
{ {
struct hidpp_device *hidpp = container_of(work, struct hidpp_device, work);
struct hid_device *hdev = hidpp->hid_dev; struct hid_device *hdev = hidpp->hid_dev;
int ret = 0;
bool connected = atomic_read(&hidpp->connected);
struct input_dev *input; struct input_dev *input;
char *name, *devm_name; char *name, *devm_name;
int ret;
if (!connected) { /* Get device version to check if it is connected */
ret = hidpp_root_get_protocol_version(hidpp);
if (ret) {
hid_info(hidpp->hid_dev, "Disconnected\n");
if (hidpp->battery.ps) { if (hidpp->battery.ps) {
hidpp->battery.online = false; hidpp->battery.online = false;
hidpp->battery.status = POWER_SUPPLY_STATUS_UNKNOWN; hidpp->battery.status = POWER_SUPPLY_STATUS_UNKNOWN;
@ -4208,15 +4195,15 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
} }
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) { if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
ret = wtp_connect(hdev, connected); ret = wtp_connect(hdev);
if (ret) if (ret)
return; return;
} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) { } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) {
ret = m560_send_config_command(hdev, connected); ret = m560_send_config_command(hdev);
if (ret) if (ret)
return; return;
} else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) { } else if (hidpp->quirks & HIDPP_QUIRK_CLASS_K400) {
ret = k400_connect(hdev, connected); ret = k400_connect(hdev);
if (ret) if (ret)
return; return;
} }
@ -4239,14 +4226,11 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
return; return;
} }
/* the device is already connected, we can ask for its name and if (hidpp->protocol_major >= 2) {
* protocol */ u8 feature_index;
if (!hidpp->protocol_major) {
ret = hidpp_root_get_protocol_version(hidpp); if (!hidpp_get_wireless_feature_index(hidpp, &feature_index))
if (ret) { hidpp->wireless_feature_index = feature_index;
hid_err(hdev, "Can not get the protocol version.\n");
return;
}
} }
if (hidpp->name == hdev->name && hidpp->protocol_major >= 2) { if (hidpp->name == hdev->name && hidpp->protocol_major >= 2) {
@ -4391,10 +4375,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
{ {
struct hidpp_device *hidpp; struct hidpp_device *hidpp;
int ret; int ret;
bool connected;
unsigned int connect_mask = HID_CONNECT_DEFAULT; unsigned int connect_mask = HID_CONNECT_DEFAULT;
struct hidpp_ff_private_data data;
bool will_restart = false;
/* report_fixup needs drvdata to be set before we call hid_parse */ /* report_fixup needs drvdata to be set before we call hid_parse */
hidpp = devm_kzalloc(&hdev->dev, sizeof(*hidpp), GFP_KERNEL); hidpp = devm_kzalloc(&hdev->dev, sizeof(*hidpp), GFP_KERNEL);
@ -4423,9 +4404,6 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
return hid_hw_start(hdev, HID_CONNECT_DEFAULT); return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
} }
if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE)
hidpp->quirks |= HIDPP_QUIRK_UNIFYING;
if (id->group == HID_GROUP_LOGITECH_27MHZ_DEVICE && if (id->group == HID_GROUP_LOGITECH_27MHZ_DEVICE &&
hidpp_application_equals(hdev, HID_GD_MOUSE)) hidpp_application_equals(hdev, HID_GD_MOUSE))
hidpp->quirks |= HIDPP_QUIRK_HIDPP_WHEELS | hidpp->quirks |= HIDPP_QUIRK_HIDPP_WHEELS |
@ -4445,11 +4423,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret; return ret;
} }
if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT || INIT_WORK(&hidpp->work, hidpp_connect_event);
hidpp->quirks & HIDPP_QUIRK_UNIFYING)
will_restart = true;
INIT_WORK(&hidpp->work, delayed_work_cb);
mutex_init(&hidpp->send_mutex); mutex_init(&hidpp->send_mutex);
init_waitqueue_head(&hidpp->wait); init_waitqueue_head(&hidpp->wait);
@ -4460,10 +4434,12 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
hdev->name); hdev->name);
/* /*
* Plain USB connections need to actually call start and open * First call hid_hw_start(hdev, 0) to allow IO without connecting any
* on the transport driver to allow incoming data. * hid subdrivers (hid-input, hidraw). This allows retrieving the dev's
* name and serial number and store these in hdev->name and hdev->uniq,
* before the hid-input and hidraw drivers expose these to userspace.
*/ */
ret = hid_hw_start(hdev, will_restart ? 0 : connect_mask); ret = hid_hw_start(hdev, 0);
if (ret) { if (ret) {
hid_err(hdev, "hw start failed\n"); hid_err(hdev, "hw start failed\n");
goto hid_hw_start_fail; goto hid_hw_start_fail;
@ -4479,70 +4455,46 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
/* Allow incoming packets */ /* Allow incoming packets */
hid_device_io_start(hdev); hid_device_io_start(hdev);
if (hidpp->quirks & HIDPP_QUIRK_UNIFYING) /* Get name + serial, store in hdev->name + hdev->uniq */
if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE)
hidpp_unifying_init(hidpp); hidpp_unifying_init(hidpp);
else if (hid_is_usb(hidpp->hid_dev)) else
hidpp_serial_init(hidpp); hidpp_non_unifying_init(hidpp);
connected = hidpp_root_get_protocol_version(hidpp) == 0; if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
atomic_set(&hidpp->connected, connected); connect_mask &= ~HID_CONNECT_HIDINPUT;
if (!(hidpp->quirks & HIDPP_QUIRK_UNIFYING)) {
if (!connected) {
ret = -ENODEV;
hid_err(hdev, "Device not connected");
goto hid_hw_init_fail;
}
hidpp_overwrite_name(hdev); /* Now export the actual inputs and hidraw nodes to the world */
} hid_device_io_stop(hdev);
ret = hid_connect(hdev, connect_mask);
if (connected && hidpp->protocol_major >= 2) { if (ret) {
ret = hidpp_set_wireless_feature_index(hidpp); hid_err(hdev, "%s:hid_connect returned error %d\n", __func__, ret);
if (ret == -ENOENT) goto hid_hw_init_fail;
hidpp->wireless_feature_index = 0;
else if (ret)
goto hid_hw_init_fail;
ret = 0;
}
if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
ret = wtp_get_config(hidpp);
if (ret)
goto hid_hw_init_fail;
} else if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_G920)) {
ret = g920_get_config(hidpp, &data);
if (ret)
goto hid_hw_init_fail;
} }
/* Check for connected devices now that incoming packets will not be disabled again */
hid_device_io_start(hdev);
schedule_work(&hidpp->work); schedule_work(&hidpp->work);
flush_work(&hidpp->work); flush_work(&hidpp->work);
if (will_restart) {
/* Reset the HID node state */
hid_device_io_stop(hdev);
hid_hw_close(hdev);
hid_hw_stop(hdev);
if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
connect_mask &= ~HID_CONNECT_HIDINPUT;
/* Now export the actual inputs and hidraw nodes to the world */
ret = hid_hw_start(hdev, connect_mask);
if (ret) {
hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
goto hid_hw_start_fail;
}
}
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) { if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
ret = hidpp_ff_init(hidpp, &data); struct hidpp_ff_private_data data;
ret = g920_get_config(hidpp, &data);
if (!ret)
ret = hidpp_ff_init(hidpp, &data);
if (ret) if (ret)
hid_warn(hidpp->hid_dev, hid_warn(hidpp->hid_dev,
"Unable to initialize force feedback support, errno %d\n", "Unable to initialize force feedback support, errno %d\n",
ret); ret);
} }
/*
* This relies on logi_dj_ll_close() being a no-op so that DJ connection
* events will still be received.
*/
hid_hw_close(hdev);
return ret; return ret;
hid_hw_init_fail: hid_hw_init_fail: