From: Vitaly Kuznetsov <[email protected]> Sent: Monday, November 11, 2024
5:01 AM
>
> Michael Kelley <[email protected]> writes:
>
> > From: Michael Kelley Sent: Wednesday, November 6, 2024 10:36 AM
> >> From: Vitaly Kuznetsov <[email protected]> Sent: Tuesday, November 5,
> >> 2024 9:45 AM
[snip]
> >>
> >> The alternative is to keep the "struct hid_driver mousevsc_hid_driver;"
> >> line
> >> and to populate it with a name, id_table, and an HID probe function
> >> specific
> >> to the Hyper-V mouse. Then instead of the incorrect assignment to
> >> hid_dev->driver, add a
> >>
> >> module_hid_driver(mousevsc_hid_driver);
> >>
> >> statement, which registers the driver. The new HID probe function does
> >> the hid_parse() and hid_hw_start() which have been removed from
> >> mousevsc_probe() as your patch does. With this arrangement, the
> >> hid_hw_start() can be done with the desired HID_CONNECT_*
> >> options so that /dev/hidraw0 won't appear. It's only a few lines
> >> of code.
> >>
> >> I can try to code up this approach if it is preferred. But I'm likely tied
> >> up with some personal things for the next few days, so might not get
> >> to it for a little while. Feel free to try it yourself if you want.
> >
> > Here's what I had in mind. It appears to work and preserves the
> > custom aspects of the current code in mousevsc_probe(). Turns
> > out I can't use module_hid_driver() because it conflicts with the
> > existing module_init() and module_exit() use, so I've directly
> > coded hid_register/unregister_driver().
> >
> > diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c
> > index f33485d83d24..98a7fa09c4ee 100644
> > --- a/drivers/hid/hid-hyperv.c
> > +++ b/drivers/hid/hid-hyperv.c
> > @@ -422,6 +422,25 @@ static int mousevsc_hid_raw_request(struct hid_device
> > *hid,
> > return 0;
> > }
> >
> > +static int mousevsc_hid_probe(struct hid_device *hid_dev, const struct
> > hid_device_id *id)
> > +{
> > + int ret;
> > +
> > + ret = hid_parse(hid_dev);
> > + if (ret) {
> > + hid_err(hid_dev, "parse failed\n");
> > + return ret;
> > + }
> > +
> > + ret = hid_hw_start(hid_dev, HID_CONNECT_HIDINPUT | HID_CONNECT_HIDDEV);
> > + if (ret) {
> > + hid_err(hid_dev, "hw start failed\n");
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > static const struct hid_ll_driver mousevsc_ll_driver = {
> > .parse = mousevsc_hid_parse,
> > .open = mousevsc_hid_open,
> > @@ -431,7 +450,16 @@ static const struct hid_ll_driver mousevsc_ll_driver =
> > {
> > .raw_request = mousevsc_hid_raw_request,
> > };
> >
> > -static struct hid_driver mousevsc_hid_driver;
> > +static const struct hid_device_id mousevsc_devices[] = {
> > + { HID_DEVICE(BUS_VIRTUAL, HID_GROUP_ANY, 0x045E, 0x0621) },
> > + { }
> > +};
> > +
> > +static struct hid_driver mousevsc_hid_driver = {
> > + .name = "hid-hyperv",
> > + .id_table = mousevsc_devices,
> > + .probe = mousevsc_hid_probe,
> > +};
> >
> > static int mousevsc_probe(struct hv_device *device,
> > const struct hv_vmbus_device_id *dev_id)
> > @@ -473,7 +501,6 @@ static int mousevsc_probe(struct hv_device *device,
> > }
> >
> > hid_dev->ll_driver = &mousevsc_ll_driver;
> > - hid_dev->driver = &mousevsc_hid_driver;
> > hid_dev->bus = BUS_VIRTUAL;
> > hid_dev->vendor = input_dev->hid_dev_info.vendor;
> > hid_dev->product = input_dev->hid_dev_info.product;
> > @@ -488,20 +515,6 @@ static int mousevsc_probe(struct hv_device *device,
> > if (ret)
> > goto probe_err2;
> >
> > -
> > - ret = hid_parse(hid_dev);
> > - if (ret) {
> > - hid_err(hid_dev, "parse failed\n");
> > - goto probe_err2;
> > - }
> > -
> > - ret = hid_hw_start(hid_dev, HID_CONNECT_HIDINPUT | HID_CONNECT_HIDDEV);
> > -
> > - if (ret) {
> > - hid_err(hid_dev, "hw start failed\n");
> > - goto probe_err2;
> > - }
> > -
> > device_init_wakeup(&device->device, true);
> >
> > input_dev->connected = true;
> > @@ -579,11 +592,21 @@ static struct hv_driver mousevsc_drv = {
> >
> > static int __init mousevsc_init(void)
> > {
> > - return vmbus_driver_register(&mousevsc_drv);
> > + int ret;
> > +
> > + ret = vmbus_driver_register(&mousevsc_drv);
> > + if (ret)
> > + return ret;
> > +
> > + ret = hid_register_driver(&mousevsc_hid_driver);
> > + if (ret)
> > + vmbus_driver_unregister(&mousevsc_drv);
> > + return ret;
> > }
> >
> > static void __exit mousevsc_exit(void)
> > {
> > + hid_unregister_driver(&mousevsc_hid_driver);
> > vmbus_driver_unregister(&mousevsc_drv);
>
> This works for me with one minor issue. If we do hid_unregister_driver()
> first, the device gets pickup up by hid-generic during unload:
>
> [ 174.988727] input: Microsoft Vmbus HID-compliant Mouse as
> /devices/0006:045E:0621.0001/input/input4
> [ 174.995261] hid-generic 0006:045E:0621.0001: input,hidraw0: VIRTUAL HID
> v0.01
> Mouse [Microsoft Vmbus HID-compliant Mouse] on
> [ 174.999222] hv_vmbus: unregistering driver hid_hyperv
>
> so I think hid_unregister_driver() and vmbus_driver_unregister() calls
> must be swapped. It also seems logical to invert the order in
> mousevsc_init(): do hid_register_driver() first and then call
> vmbus_driver_register() to avoid the race where a mousevsc device shows
> up but the HID driver for it is not yet registered.
>
Yes, that makes perfect sense.
Michael