On Fri, Apr 10, 2015 at 2:56 PM, Goffredo Baroncelli <kreijack@xxxxxxxxx> wrote: > Hi, > > after the Antonio's mail, I updated my work on the latest kernel (3.19.3); this work for me, but I am sure that I made some mistakes, so please consider this a beta. Thanks for the patch. I have some comments, please see inline. > I added a new device class (M560), and I put some hooks to process the raw data. > > I used the following "quirks": > - HIDPP_QUIRK_DELAYED_INIT > - HIDPP_QUIRK_MULTI_INPUT I don't think you really need HIDPP_QUIRK_MULTI_INPUT. If the mouse has only one input node (or you want to send only on the input node), then you don't need to keep the keyboard node. [10 min later] OK, after reviewing the code, I see why you kept HIDPP_QUIRK_MULTI_INPUT. I am not entirely sure what is the best solution: keeping it and rely on hid-input for the parsing/allocating of regular mouse events and having .raw_event() tampering with the raw report, or just trash hid-input and process entirely the incoming report... > > these work (when the mouse is connected, the configuration message is sent), but I am not sure if this is correct. > Anyway in the past I noticed some false spurious reconnections, and re-sending the configuration time to time made the mouse not-responsive for few tens of second (it seems a low value, but I noticed it); so I am not sure if I will leave the "reconfiguration" on reconnection. > > Comments are welcome. Please for the next submission do a proper one with a commit message and a signed-of-by line. This way, we can integrate it when the code looks clearer or someone else can take over the job if you decide not to port it upstream. > > BR > G.Baroncelli > > > diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c > index a93cefe..4d907d6 100644 > --- a/drivers/hid/hid-logitech-hidpp.c > +++ b/drivers/hid/hid-logitech-hidpp.c > @@ -35,6 +35,7 @@ MODULE_AUTHOR("Nestor Lopez Casado <nlopezcasad@xxxxxxxxxxxx>"); > #define HIDPP_REPORT_LONG_LENGTH 20 > > #define HIDPP_QUIRK_CLASS_WTP BIT(0) > +#define HIDPP_QUIRK_CLASS_M560 BIT(1) > > /* bits 1..20 are reserved for classes */ Technically speaking, this should be changed to "2..20" :) > #define HIDPP_QUIRK_DELAYED_INIT BIT(21) > @@ -925,6 +926,225 @@ static void wtp_connect(struct hid_device *hdev, bool connected) > } > > /* -------------------------------------------------------------------------- */ > +/* Logitech M560 devices */ > +/* -------------------------------------------------------------------------- */ > + > +/* > + * Logitech M560 protocol overview > + * > + * The Logitech M560 mouse, is designed for windows 8. When the middle and/or > + * the sides buttons are pressed, it sends some keyboard keys events > + * instead of buttons ones. > + * To complicate further the things, the middle button keys sequence > + * is different from the odd press and the even press. > + * > + * forward button -> Super_R > + * backward button -> Super_L+'d' (press only) > + * middle button -> 1st time: Alt_L+SuperL+XF86TouchpadOff (press only) > + * 2nd time: left-click (press only) > + * NB: press-only means that when the button is pressed, the > + * KeyPress/ButtonPress and KeyRelease/ButtonRelease events are generated > + * together sequentially; instead when the button is released, no event is > + * generated ! > + * > + * With the command > + * 10<xx>0a 3500af03 (where <xx> is the mouse id), > + * the mouse reacts differently: > + * - it never send a keyboard key event > + * - for the three mouse button it sends: > + * middle button press 11<xx>0a 3500af00... > + * side 1 button (forward) press 11<xx>0a 3500b000... > + * side 2 button (backward) press 11<xx>0a 3500ae00... > + * middle/side1/side2 button release 11<xx>0a 35000000... > + */ > +static u8 m560_config_command[] = {0x35, 0x00, 0xaf, 0x03}; please mark this one as const. > + > +struct m560_private_data { > + u8 prev_data[30]; // FIXME: select a right size Don't insert FIXME, fix it before the integration :) > + int btn_middle:1; > + int btn_forward:1; > + int btn_backward:1; using a int per button seems a lot of lost space. Maybe you can use a bitmask or at least bool (which remaps to an unsigned char IIRC). > +}; > + > +/* how the button are mapped in the report */ > +#define MOUSE_BTN_LEFT 0 > +#define MOUSE_BTN_RIGHT 1 > +#define MOUSE_BTN_MIDDLE 2 > +#define MOUSE_BTN_WHEEL_LEFT 3 > +#define MOUSE_BTN_WHEEL_RIGHT 4 > +#define MOUSE_BTN_FORWARD 5 > +#define MOUSE_BTN_BACKWARD 6 Please prefix those defines by M560. > + > +/* > + * m560_priv_data - helper to convert from hidpp_device to m560_private_data > + * > + * @hdev: hid device > + * > + * @return: return m560_private_data if available, NULL otherwise > + */ > +static inline struct m560_private_data *m560_priv_data(struct hid_device *hdev) > +{ > + struct hidpp_device *hidpp_dev = hid_get_drvdata(hdev); > + return hidpp_dev ? hidpp_dev->private_data : NULL; > +} Are you sure we really need this check for M560 mice? if you are calling this in the M560 protocol handling only and made sure in the probe that private_data gets allocated, no need to double check things. > + > +/* > + * m560_send_config_command - send the config_command to the mouse > + * > + * @dev: hid device where the mouse belongs > + * > + * @return: 0 OK > + */ > +static int m560_send_config_command(struct hid_device *hdev) { > + struct hidpp_report response; > + struct hidpp_device *hidpp_dev = hid_get_drvdata(hdev); > + int ret; > + > + ret = hidpp_send_rap_command_sync( > + hidpp_dev, > + REPORT_ID_HIDPP_SHORT, > + 0x0a, Please use a define for this sub id. BTW, are you sure the mouse is HID++ 1.0? If it is 2.0, you have to use the fap protocol, not the rap. > + m560_config_command[0], > + m560_config_command+1, > + sizeof(m560_config_command)-1, I would personally define m560_config_command[0] in a separate define (M560_BUTTON_MODE_REGISTER for instance) and keep the parameters to the right size right now. > + &response > + ); > + > + return ret; > +} > + > +static int m560_allocate(struct hid_device *hdev) > +{ > + struct hidpp_device *hidpp = hid_get_drvdata(hdev); > + struct m560_private_data *d; > + > + d = devm_kzalloc(&hdev->dev, sizeof(struct m560_private_data), > + GFP_KERNEL); > + if (!d) > + return -ENOMEM; > + > + hidpp->private_data = d; > + //d->hidpp_dev = hidpp; > + //d->hid_dev = hdev; No leftover debug comments please. > + > + return 0; > +}; > + > +static inline void set_btn_bit(u8 *data, int bit) > +{ > + int bytenr = bit / 8; > + int bitmask = 1 << (bit & 0x07); > + > + data[bytenr] |= bitmask; You seem to be using {set|get|clear}_btn_bit only with MOUSE_BTN_* which max is 6. You don't really need this helpers and just use plain bit operations. If you define MOUSE_BTN_* as BIT(0)..BIT(6), you don't need extra layer at all. > +} > + > +static inline int get_btn_bit(u8 *data, int bit) > +{ > + int bytenr = bit / 8; > + int bitmask = 1 << (bit & 0x07); > + > + return !!(data[bytenr] & bitmask); > +} > + > +static inline void clear_btn_bit(u8 *data, int bit) > +{ > + int bytenr = bit / 8; > + int bitmask = 1 << (bit & 0x07); > + > + data[bytenr] &= ~bitmask; > +} > + > +static int m560_raw_event(struct hid_device *hdev, u8 *data, int size) > +{ > + struct m560_private_data *mydata = m560_priv_data(hdev); > + > + /* check if the data is a mouse related report */ > + if (data[0] != 0x02 && data[2] != 0x0a) Please no magical numbers. You should have these defined somewhere (OK, I did not define 0x02 myself, but you should!) > + return 1; > + > + /* check if the report is the ack of the config_command */ > + if (data[0] == 0x11 && data[2] == 0x0a && 0x11 is REPORT_ID_HIDPP_LONG > + size >= (3+sizeof(m560_config_command)) && > + !memcmp(data+3, m560_config_command, > + sizeof(m560_config_command))) { > + return true; this whole test is a lot messy. There should be a way to clean it a little (maybe just define what you expect, and use one memcmp). > + } > + > + if (data[0] == 0x11 && data[2] == 0x0a && data[06] == 0x00) { > + /* > + * m560 mouse button report > + * > + * data[0] = 0x11 > + * data[1] = deviceid > + * data[2] = 0x0a > + * data[5] = button (0xaf->middle, 0xb0->forward, > + * 0xaf ->backward, 0x00->release all) > + * data[6] = 0x00 > + */ > + > + int btn, i, maxsize; > + > + /* check if the event is a button */ > + btn = data[5]; > + if (btn != 0x00 && btn != 0xb0 && btn != 0xae && btn != 0xaf) > + return true; I wouldn't be surprised if these buttons where just bitmasks. Do you still get the same values if you press 2/3 buttons at the same time? 0xa0 seems common to all these bytes (0b10100000). BIT(0) seems to be btn_middle BIT(4) seems to be btn_forward but btn_backward does not make any sense. Nestor, can you tell us what these magic packets are? > + > + if (btn == 0xaf) > + mydata->btn_middle = 1; > + else if (btn == 0xb0) > + mydata->btn_forward = 1; > + else if (btn == 0xae) > + mydata->btn_backward = 1; > + else if (btn == 0x00) { > + mydata->btn_backward = 0; > + mydata->btn_forward = 0; > + mydata->btn_middle = 0; > + } As mentioned above, this looks fragile if 2 buttons are pressed at the same time, the first one will be spuriously released. > + > + /* replace the report with the old one */ > + if (size > sizeof(mydata->prev_data)) > + maxsize = sizeof(mydata->prev_data); > + else > + maxsize = size; > + for (i = 0 ; i < maxsize ; i++) > + data[i] = mydata->prev_data[i]; I think here and later, you are making things too complicated. Can't you just emit the buttons when retrieving them with input_event(), and call input_sync() now and drop the whole rewriting of HID report which just add some overhead and complexity. > + > + } else if (data[0] == 0x02) { > + /* > + * standard mouse report > + * > + * data[0] = type (0x02) > + * data[1..2] = buttons > + * data[3..5] = xy > + * data[6] = wheel > + * data[7] = horizontal wheel > + */ > + > + /* horizontal wheel handling */ > + if (get_btn_bit(data+1,MOUSE_BTN_WHEEL_LEFT)) > + data[1+6] = -1; > + if (get_btn_bit(data+1,MOUSE_BTN_WHEEL_RIGHT)) > + data[1+6] = 1; Do we really need to clear those buttons? Xorg should be able to remap them to BTN_HORIZ_WHEEL or so. > + > + clear_btn_bit(data+1, MOUSE_BTN_WHEEL_LEFT); > + clear_btn_bit(data+1, MOUSE_BTN_WHEEL_RIGHT); > + > + /* copy the type and buttons status */ > + memcpy(mydata->prev_data, data, 3); This can be dropped if you directly call input_event in the previous case. > + } > + > + /* add the extra buttons */ > + if (mydata->btn_middle) > + set_btn_bit(data+1, MOUSE_BTN_MIDDLE); > + if (mydata->btn_forward) > + set_btn_bit(data+1, MOUSE_BTN_FORWARD); > + if (mydata->btn_backward) > + set_btn_bit(data+1, MOUSE_BTN_BACKWARD); This is somewhat disturbing. I think it is fine, but an other solution would be to simply ignore the mapping of those buttons in input_mapping. > + > + return 1; > +} > + > +/* -------------------------------------------------------------------------- */ > /* Generic HID++ devices */ > /* -------------------------------------------------------------------------- */ > > @@ -936,6 +1156,9 @@ static int hidpp_input_mapping(struct hid_device *hdev, struct hid_input *hi, > > if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) > return wtp_input_mapping(hdev, hi, field, usage, bit, max); > + if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560 && > + field->application != HID_GD_MOUSE) > + return -1; > > return 0; > } > @@ -998,6 +1221,8 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, > > if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) > return wtp_raw_event(hidpp->hid_dev, data, size); > + if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) > + return m560_raw_event(hidpp->hid_dev, data, size); > > return 0; > } > @@ -1026,6 +1251,8 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report, > > if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) > return wtp_raw_event(hdev, data, size); > + if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) > + return m560_raw_event(hidpp->hid_dev, data, size); > > return 0; > } > @@ -1100,6 +1327,8 @@ static void hidpp_connect_event(struct hidpp_device *hidpp) > > if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) > wtp_connect(hdev, connected); > + if ((hidpp->quirks & HIDPP_QUIRK_CLASS_M560) && connected) > + m560_send_config_command(hdev); > > if (!connected || hidpp->delayed_input) > return; > @@ -1164,6 +1393,11 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) > if (ret) > goto wtp_allocate_fail; > } > + if (hidpp->quirks & HIDPP_QUIRK_CLASS_M560) { > + ret = m560_allocate(hdev); > + if (ret) > + goto wtp_allocate_fail; This looks weird. we should probably rename the label to "class_allocate_fail" or something similar. > + } > > INIT_WORK(&hidpp->work, delayed_work_cb); > mutex_init(&hidpp->send_mutex); > @@ -1240,6 +1474,7 @@ static void hidpp_remove(struct hid_device *hdev) > cancel_work_sync(&hidpp->work); > mutex_destroy(&hidpp->send_mutex); > hid_hw_stop(hdev); > + Please no extra line out of context. > } > > static const struct hid_device_id hidpp_devices[] = { > @@ -1261,6 +1496,12 @@ static const struct hid_device_id hidpp_devices[] = { > USB_VENDOR_ID_LOGITECH, 0x4102), > .driver_data = HIDPP_QUIRK_DELAYED_INIT | HIDPP_QUIRK_MULTI_INPUT | > HIDPP_QUIRK_CLASS_WTP }, > + { /* Mouse logitech M560 */ > + HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, > + USB_VENDOR_ID_LOGITECH, 0x402d), > + .driver_data = HIDPP_QUIRK_CLASS_M560 | HIDPP_QUIRK_DELAYED_INIT | > + HIDPP_QUIRK_MULTI_INPUT > + }, > > { HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, > USB_VENDOR_ID_LOGITECH, HID_ANY_ID)}, > > One last comment. Please check the patch against ./script/checkpatch.pl. There are here and ther some whitespace problems that checkpatch will raise. Thanks for the submission. Cheers, Benjamin > > On 2015-04-09 19:35, Benjamin Tissoires wrote: >> Hi Antonio, >> >> On Thu, Apr 9, 2015 at 8:41 AM, Antonio Ospite <ao2@xxxxxx> wrote: >>> On Fri, 05 Sep 2014 19:47:44 +0200 >>> Goffredo Baroncelli <kreijack@xxxxxxxxx> wrote: >>> >>>> On 09/03/2014 11:36 PM, Benjamin Tissoires wrote: >>>>> Hi Goffredo, >>>>> >>>>> On Mon, Sep 1, 2014 at 3:20 PM, Goffredo Baroncelli <kreijack@xxxxxxxxx> wrote: >>>>>> Hi Benjamin, >>>>>> >>>>>> following the Nestor suggestion, I rewrote the driver for the >>>>>> mouse Logitech M560 on the basis of your work (branch "for-whot"). >>>>> >>>>> Just for the record. This branch is located here: >>>>> https://github.com/bentiss/hid-logitech-dj/tree/for-whot and I >>>>> *really* need to finish this work so that everything can go upstream >>>>> :( >>>> >>> >>> Hi Benjamin, any news about upstreaming this work? >> >> The hidpp / raw touchpad mode is already upstream since the v3.19 kernel. \o/ >> >> We just need to port Goffredo's changes now and push them too. >> There has been some differences since this branch was published. The >> main is that there is no more special subdrivers in several files, >> everything is handled in hidpp. I think the logic behind is somewhat >> the same so it should not be too much a problem to do the work. >> >> Cheers, >> Benjamin >> >>> >>>> :-) For me this is not a problem. I solved my issue (I made a dkms >>>> package for this mouse), but I want to share my effort.. >>>> >>> >>> Goffredo, is this the latest version of your dkms-enabled external >>> module? >>> https://github.com/kreijack/hid-logitech-dj/tree/m560-dkms >>> >>> Thanks, >>> Antonio >>> >>> -- >>> Antonio Ospite >>> http://ao2.it >>> >>> A: Because it messes up the order in which people normally read text. >>> See http://en.wikipedia.org/wiki/Posting_style >>> Q: Why is top-posting such a bad thing? >> > > > -- > gpg @keyserver.linux.it: Goffredo Baroncelli <kreijackATinwind.it> > Key fingerprint BBF5 1610 0B64 DAC6 5F7D 17B2 0EDA 9B37 8B82 E0B5 -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html