Skip to content

Commit c39e3d5

Browse files
bentissJiri Kosina
authored andcommitted
HID: logitech-hidpp: late bind the input device on wireless connection
Now that the receiver forwards the connect/disconnect events, we can know when the device is available to communicate with us. When it is ready, we can for instance retrieve its full name, which guarantee that we always have the same name for the DJ device (the DJ name is somewhat shorter than the HID++ name). This mechanism is mandatory for the touchpads line, which has the min/max information stored in the device. This information can only be retrieved when the device is connected. So we can not populate the input device until we are sure that the device is connected. This patch creates a new input device for such devices. However, this input is not bound to hid directly, so the various drivers which wants to use it are required to process completely the incoming reports in .raw_event(). Note that the patch in itself just adds the bits for the next ones, and this feature is disabled by default. Signed-off-by: Benjamin Tissoires <[email protected]> Tested-by: Andrew de los Reyes <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent 6a9ddc8 commit c39e3d5

File tree

1 file changed

+147
-8
lines changed

1 file changed

+147
-8
lines changed

drivers/hid/hid-logitech-hidpp.c

Lines changed: 147 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ MODULE_AUTHOR("Nestor Lopez Casado <[email protected]>");
3636

3737
#define HIDPP_QUIRK_CLASS_WTP BIT(0)
3838

39+
/* bits 1..20 are reserved for classes */
40+
#define HIDPP_QUIRK_DELAYED_INIT BIT(21)
41+
3942
/*
4043
* There are two hidpp protocols in use, the first version hidpp10 is known
4144
* as register access protocol or RAP, the second version hidpp20 is known as
@@ -91,6 +94,11 @@ struct hidpp_device {
9194

9295
void *private_data;
9396

97+
struct work_struct work;
98+
struct kfifo delayed_work_fifo;
99+
atomic_t connected;
100+
struct input_dev *delayed_input;
101+
94102
unsigned long quirks;
95103
};
96104

@@ -110,6 +118,8 @@ struct hidpp_device {
110118
#define HIDPP_ERROR_INVALID_PARAM_VALUE 0x0b
111119
#define HIDPP_ERROR_WRONG_PIN_CODE 0x0c
112120

121+
static void hidpp_connect_event(struct hidpp_device *hidpp_dev);
122+
113123
static int __hidpp_send_report(struct hid_device *hdev,
114124
struct hidpp_report *hidpp_report)
115125
{
@@ -230,6 +240,13 @@ static int hidpp_send_rap_command_sync(struct hidpp_device *hidpp_dev,
230240
return ret;
231241
}
232242

243+
static void delayed_work_cb(struct work_struct *work)
244+
{
245+
struct hidpp_device *hidpp = container_of(work, struct hidpp_device,
246+
work);
247+
hidpp_connect_event(hidpp);
248+
}
249+
233250
static inline bool hidpp_match_answer(struct hidpp_report *question,
234251
struct hidpp_report *answer)
235252
{
@@ -245,6 +262,12 @@ static inline bool hidpp_match_error(struct hidpp_report *question,
245262
(answer->fap.params[0] == question->fap.funcindex_clientid);
246263
}
247264

265+
static inline bool hidpp_report_is_connect_event(struct hidpp_report *report)
266+
{
267+
return (report->report_id == REPORT_ID_HIDPP_SHORT) &&
268+
(report->rap.sub_id == 0x41);
269+
}
270+
248271
/* -------------------------------------------------------------------------- */
249272
/* HIDP++ 1.0 commands */
250273
/* -------------------------------------------------------------------------- */
@@ -535,12 +558,10 @@ static int wtp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
535558
return -1;
536559
}
537560

538-
static void wtp_input_configured(struct hid_device *hdev,
539-
struct hid_input *hidinput)
561+
static void wtp_populate_input(struct hidpp_device *hidpp,
562+
struct input_dev *input_dev, bool origin_is_hid_core)
540563
{
541-
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
542564
struct wtp_data *wd = hidpp->private_data;
543-
struct input_dev *input_dev = hidinput->input;
544565

545566
__set_bit(EV_ABS, input_dev->evbit);
546567
__set_bit(EV_KEY, input_dev->evbit);
@@ -716,13 +737,20 @@ static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi,
716737
return 0;
717738
}
718739

740+
static void hidpp_populate_input(struct hidpp_device *hidpp,
741+
struct input_dev *input, bool origin_is_hid_core)
742+
{
743+
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
744+
wtp_populate_input(hidpp, input, origin_is_hid_core);
745+
}
746+
719747
static void hidpp_input_configured(struct hid_device *hdev,
720748
struct hid_input *hidinput)
721749
{
722750
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
751+
struct input_dev *input = hidinput->input;
723752

724-
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
725-
wtp_input_configured(hdev, hidinput);
753+
hidpp_populate_input(hidpp, input, true);
726754
}
727755

728756
static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
@@ -756,6 +784,15 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data,
756784
}
757785
}
758786

787+
if (unlikely(hidpp_report_is_connect_event(report))) {
788+
atomic_set(&hidpp->connected,
789+
!(report->rap.params[0] & (1 << 6)));
790+
if ((hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) &&
791+
(schedule_work(&hidpp->work) == 0))
792+
dbg_hid("%s: connect event already queued\n", __func__);
793+
return 1;
794+
}
795+
759796
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)
760797
return wtp_raw_event(hidpp->hid_dev, data, size);
761798

@@ -814,11 +851,99 @@ static void hidpp_overwrite_name(struct hid_device *hdev, bool use_unifying)
814851
kfree(name);
815852
}
816853

854+
static int hidpp_input_open(struct input_dev *dev)
855+
{
856+
struct hid_device *hid = input_get_drvdata(dev);
857+
858+
return hid_hw_open(hid);
859+
}
860+
861+
static void hidpp_input_close(struct input_dev *dev)
862+
{
863+
struct hid_device *hid = input_get_drvdata(dev);
864+
865+
hid_hw_close(hid);
866+
}
867+
868+
static struct input_dev *hidpp_allocate_input(struct hid_device *hdev)
869+
{
870+
struct input_dev *input_dev = devm_input_allocate_device(&hdev->dev);
871+
872+
if (!input_dev)
873+
return NULL;
874+
875+
input_set_drvdata(input_dev, hdev);
876+
input_dev->open = hidpp_input_open;
877+
input_dev->close = hidpp_input_close;
878+
879+
input_dev->name = hdev->name;
880+
input_dev->phys = hdev->phys;
881+
input_dev->uniq = hdev->uniq;
882+
input_dev->id.bustype = hdev->bus;
883+
input_dev->id.vendor = hdev->vendor;
884+
input_dev->id.product = hdev->product;
885+
input_dev->id.version = hdev->version;
886+
input_dev->dev.parent = &hdev->dev;
887+
888+
return input_dev;
889+
}
890+
891+
static void hidpp_connect_event(struct hidpp_device *hidpp)
892+
{
893+
struct hid_device *hdev = hidpp->hid_dev;
894+
int ret = 0;
895+
bool connected = atomic_read(&hidpp->connected);
896+
struct input_dev *input;
897+
char *name, *devm_name;
898+
u8 name_length;
899+
900+
if (!connected || hidpp->delayed_input)
901+
return;
902+
903+
if (!hidpp->protocol_major) {
904+
ret = !hidpp_is_connected(hidpp);
905+
if (ret) {
906+
hid_err(hdev, "Can not get the protocol version.\n");
907+
return;
908+
}
909+
}
910+
911+
/* the device is already connected, we can ask for its name and
912+
* protocol */
913+
hid_info(hdev, "HID++ %u.%u device connected.\n",
914+
hidpp->protocol_major, hidpp->protocol_minor);
915+
916+
input = hidpp_allocate_input(hdev);
917+
if (!input) {
918+
hid_err(hdev, "cannot allocate new input device: %d\n", ret);
919+
return;
920+
}
921+
922+
name = hidpp_get_device_name(hidpp, &name_length);
923+
if (!name) {
924+
hid_err(hdev, "unable to retrieve the name of the device");
925+
} else {
926+
devm_name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s", name);
927+
if (devm_name)
928+
input->name = devm_name;
929+
kfree(name);
930+
}
931+
932+
hidpp_populate_input(hidpp, input, false);
933+
934+
ret = input_register_device(input);
935+
if (ret)
936+
input_free_device(input);
937+
938+
hidpp->delayed_input = input;
939+
}
940+
817941
static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
818942
{
819943
struct hidpp_device *hidpp;
820944
int ret;
821945
bool connected;
946+
unsigned int connect_mask = HID_CONNECT_DEFAULT;
822947

823948
hidpp = devm_kzalloc(&hdev->dev, sizeof(struct hidpp_device),
824949
GFP_KERNEL);
@@ -836,6 +961,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
836961
return ret;
837962
}
838963

964+
INIT_WORK(&hidpp->work, delayed_work_cb);
839965
mutex_init(&hidpp->send_mutex);
840966
init_waitqueue_head(&hidpp->wait);
841967

@@ -860,8 +986,9 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
860986
}
861987

862988
hidpp_overwrite_name(hdev, id->group == HID_GROUP_LOGITECH_DJ_DEVICE);
989+
atomic_set(&hidpp->connected, connected);
863990

864-
if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) {
991+
if (connected && (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP)) {
865992
ret = wtp_get_config(hidpp);
866993
if (ret)
867994
goto hid_parse_fail;
@@ -870,16 +997,27 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
870997
/* Block incoming packets */
871998
hid_device_io_stop(hdev);
872999

873-
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
1000+
if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
1001+
connect_mask &= ~HID_CONNECT_HIDINPUT;
1002+
1003+
ret = hid_hw_start(hdev, connect_mask);
8741004
if (ret) {
8751005
hid_err(hdev, "%s:hid_hw_start returned error\n", __func__);
8761006
goto hid_hw_start_fail;
8771007
}
8781008

1009+
if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT) {
1010+
/* Allow incoming packets */
1011+
hid_device_io_start(hdev);
1012+
1013+
hidpp_connect_event(hidpp);
1014+
}
1015+
8791016
return ret;
8801017

8811018
hid_hw_start_fail:
8821019
hid_parse_fail:
1020+
cancel_work_sync(&hidpp->work);
8831021
mutex_destroy(&hidpp->send_mutex);
8841022
hid_set_drvdata(hdev, NULL);
8851023
return ret;
@@ -889,6 +1027,7 @@ static void hidpp_remove(struct hid_device *hdev)
8891027
{
8901028
struct hidpp_device *hidpp = hid_get_drvdata(hdev);
8911029

1030+
cancel_work_sync(&hidpp->work);
8921031
mutex_destroy(&hidpp->send_mutex);
8931032
hid_hw_stop(hdev);
8941033
}

0 commit comments

Comments
 (0)