Re: Driver for Logitech M560

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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.
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

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.

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 */
 #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};
+
+struct m560_private_data {
+	u8 prev_data[30]; // FIXME: select a right size
+	int btn_middle:1;
+	int btn_forward:1;
+	int btn_backward:1;
+};
+
+/* 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
+
+/*
+ * 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;
+}
+
+/*
+ * 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,
+		m560_config_command[0],
+		m560_config_command+1,
+		sizeof(m560_config_command)-1,
+		&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;
+
+	return 0;
+};
+
+static inline void set_btn_bit(u8 *data, int bit)
+{
+	int bytenr = bit / 8;
+	int bitmask = 1 << (bit & 0x07);
+
+	data[bytenr] |= bitmask;
+}
+
+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)
+		return 1;
+
+	/* check if the report is the ack of the config_command */
+	if (data[0] == 0x11 && data[2] == 0x0a &&
+	    size >= (3+sizeof(m560_config_command)) &&
+	    !memcmp(data+3, m560_config_command,
+		sizeof(m560_config_command))) {
+			return true;
+	}
+
+	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;
+
+		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;
+		}
+
+		/* 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];
+
+	} 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;
+
+		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);
+	}
+
+	/* 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);
+
+	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;
+	}
 
 	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);
+
 }
 
 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)},



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




[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux