On Wed, Feb 12, 2014 at 5:47 AM, David Herrmann <dh.herrm...@gmail.com> wrote: > Hi > > On Mon, Feb 10, 2014 at 6:58 PM, Benjamin Tissoires > <benjamin.tissoi...@redhat.com> wrote: >> We can not directly change the underlying struct hid_ll_driver here >> as we did for hdev->hid_output_raw_report. >> So allocate a struct ll_driver, and replace the old one when removing >> the device. >> To get a fully functional driver, we must also split the function >> sixaxis_usb_output_raw_report() in 2, one regarding output_report, and >> the other for raw_request. > > Sorry, i cannot follow here. Could you explain why this is needed? And
Well, as mentioned in the original comments, the sixaxis has two problem: - when you send an output report, you should call hid_hw_raw_request instead (though there is an interrupt output endpoint) - when you send a raw_request, you should drop the reportID in the buffer you are sending, but keep it in the SET_REPORT call... (quite annoying). Splitting this in two (it was all implemented in hid_output_report) allows to transparently use the hid_hw_call, not matter who calls what. > I also don't think you're supposed to change the ll_driver unlocked. > The real ll_driver might access it in any way. I don't think we do it > right now, but it might happen. Yeah, I am not a big fan of this either. My other concern regarding that is the dependency against usb, while it could be a uHID device. > > Why can't we introduce QUIRK flags that make HID core drop the > report_id for outgoing messages? I did not want to come with because it would make the bright new ll_transport interface ugly, but this may be the only way to have something generic and drop the usb dependency and the races that may appears (plus the ugly alloc/free). Cheers, Benjamin > > Thanks > David > >> Signed-off-by: Benjamin Tissoires <benjamin.tissoi...@redhat.com> >> --- >> drivers/hid/hid-sony.c | 75 >> ++++++++++++++++++++++++++++++++++---------------- >> 1 file changed, 52 insertions(+), 23 deletions(-) >> >> diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c >> index d980943..dbbcd0c8 100644 >> --- a/drivers/hid/hid-sony.c >> +++ b/drivers/hid/hid-sony.c >> @@ -507,6 +507,8 @@ struct sony_sc { >> struct work_struct state_worker; >> struct power_supply battery; >> >> + struct hid_ll_driver *prev_ll_driver; >> + >> #ifdef CONFIG_SONY_FF >> __u8 left; >> __u8 right; >> @@ -760,38 +762,52 @@ static int sony_mapping(struct hid_device *hdev, >> struct hid_input *hi, >> >> /* >> * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP >> - * like it should according to usbhid/hid-core.c::usbhid_output_raw_report() >> + * like it should according to usbhid/hid-core.c::usbhid_output_report() >> * so we need to override that forcing HID Output Reports on the Control EP. >> - * >> - * There is also another issue about HID Output Reports via USB, the Sixaxis >> - * does not want the report_id as part of the data packet, so we have to >> - * discard buf[0] when sending the actual control message, even for numbered >> - * reports, humpf! >> */ >> -static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf, >> - size_t count, unsigned char report_type) >> +static int sixaxis_usb_output_report(struct hid_device *hid, __u8 *buf, >> + size_t count) >> +{ >> + return hid_hw_raw_request(hid, buf[0], buf, count, HID_OUTPUT_REPORT, >> + HID_REQ_SET_REPORT); >> +} >> + >> +/* >> + * There is also another issue about the SET_REPORT command via USB, >> + * the Sixaxis does not want the report_id as part of the data packet, so >> + * we have to discard buf[0] when sending the actual control message, even >> + * for numbered reports, humpf! >> + */ >> +static int sixaxis_usb_raw_request(struct hid_device *hid, >> + unsigned char reportnum, __u8 *buf, >> + size_t len, unsigned char rtype, int >> reqtype) >> { >> struct usb_interface *intf = to_usb_interface(hid->dev.parent); >> struct usb_device *dev = interface_to_usbdev(intf); >> struct usb_host_interface *interface = intf->cur_altsetting; >> - int report_id = buf[0]; >> int ret; >> + u8 usb_dir; >> + unsigned int usb_pipe; >> >> - if (report_type == HID_OUTPUT_REPORT) { >> + if (reqtype == HID_REQ_SET_REPORT) { >> /* Don't send the Report ID */ >> buf++; >> - count--; >> + len--; >> + usb_dir = USB_DIR_OUT; >> + usb_pipe = usb_sndctrlpipe(dev, 0); >> + } else { >> + usb_dir = USB_DIR_IN; >> + usb_pipe = usb_rcvctrlpipe(dev, 0); >> } >> >> - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), >> - HID_REQ_SET_REPORT, >> - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, >> - ((report_type + 1) << 8) | report_id, >> - interface->desc.bInterfaceNumber, buf, count, >> + ret = usb_control_msg(dev, usb_pipe, reqtype, >> + usb_dir | USB_TYPE_CLASS | USB_RECIP_INTERFACE, >> + ((rtype + 1) << 8) | reportnum, >> + interface->desc.bInterfaceNumber, buf, len, >> USB_CTRL_SET_TIMEOUT); >> >> - /* Count also the Report ID, in case of an Output report. */ >> - if (ret > 0 && report_type == HID_OUTPUT_REPORT) >> + /* Count also the Report ID. */ >> + if (ret > 0 && reqtype == HID_REQ_SET_REPORT) >> ret++; >> >> return ret; >> @@ -1047,7 +1063,7 @@ static void sixaxis_state_worker(struct work_struct >> *work) >> buf[10] |= sc->led_state[2] << 3; >> buf[10] |= sc->led_state[3] << 4; >> >> - hid_output_raw_report(sc->hdev, buf, sizeof(buf), HID_OUTPUT_REPORT); >> + hid_hw_output_report(sc->hdev, buf, sizeof(buf)); >> } >> >> static void dualshock4_state_worker(struct work_struct *work) >> @@ -1254,6 +1270,7 @@ static int sony_probe(struct hid_device *hdev, const >> struct hid_device_id *id) >> unsigned long quirks = id->driver_data; >> struct sony_sc *sc; >> unsigned int connect_mask = HID_CONNECT_DEFAULT; >> + struct hid_ll_driver *ll_driver; >> >> sc = devm_kzalloc(&hdev->dev, sizeof(*sc), GFP_KERNEL); >> if (sc == NULL) { >> @@ -1285,13 +1302,20 @@ static int sony_probe(struct hid_device *hdev, const >> struct hid_device_id *id) >> } >> >> if (sc->quirks & SIXAXIS_CONTROLLER_USB) { >> - hdev->hid_output_raw_report = sixaxis_usb_output_raw_report; >> + ll_driver = devm_kzalloc(&hdev->dev, sizeof(*ll_driver), >> + GFP_KERNEL); >> + if (ll_driver == NULL) >> + return -ENOMEM; >> + sc->prev_ll_driver = hdev->ll_driver; >> + *ll_driver = *hdev->ll_driver; >> + hdev->ll_driver = ll_driver; >> + ll_driver->output_report = sixaxis_usb_output_report; >> + ll_driver->raw_request = sixaxis_usb_raw_request; >> ret = sixaxis_set_operational_usb(hdev); >> INIT_WORK(&sc->state_worker, sixaxis_state_worker); >> - } >> - else if (sc->quirks & SIXAXIS_CONTROLLER_BT) >> + } else if (sc->quirks & SIXAXIS_CONTROLLER_BT) { >> ret = sixaxis_set_operational_bt(hdev); >> - else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { >> + } else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) { >> /* Report 5 (31 bytes) is used to send data to the >> controller via USB */ >> ret = sony_set_output_report(sc, 0x05, 248); >> if (ret < 0) >> @@ -1339,6 +1363,8 @@ static int sony_probe(struct hid_device *hdev, const >> struct hid_device_id *id) >> err_close: >> hid_hw_close(hdev); >> err_stop: >> + if (sc->prev_ll_driver) >> + hdev->ll_driver = sc->prev_ll_driver; >> if (sc->quirks & SONY_LED_SUPPORT) >> sony_leds_remove(hdev); >> if (sc->quirks & SONY_BATTERY_SUPPORT) >> @@ -1351,6 +1377,9 @@ static void sony_remove(struct hid_device *hdev) >> { >> struct sony_sc *sc = hid_get_drvdata(hdev); >> >> + if (sc->prev_ll_driver) >> + hdev->ll_driver = sc->prev_ll_driver; >> + >> if (sc->quirks & SONY_LED_SUPPORT) >> sony_leds_remove(hdev); >> >> -- >> 1.8.3.1 >> -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/