Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 884316de authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull HID updates from Jiri Kosina:

 - new hid-alps driver for ALPS Touchpad-Stick device, from Masaki Ota

 - much improved and generalized HID led handling, and merge of
   specialized hid-thingm driver into this generic hid-led one, from
   Heiner Kallweit

 - i2c-hid power management improvements from Fu Zhonghui and Guohua
   Zhong

 - uhid initialization race fix from Roderick Colenbrander

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (21 commits)
  HID: add usb device id for Apple Magic Keyboard
  HID: hid-led: fix Delcom support on big endian systems
  HID: hid-led: add support for Greynut Luxafor
  HID: hid-led: add support for Delcom Visual Signal Indicator G2
  HID: hid-led: remove report id from struct hidled_config
  HID: alps: a few cleanups
  HID: remove ThingM blink(1) driver
  HID: hid-led: add support for ThingM blink(1)
  HID: hid-led: add support for reading from LED devices
  HID: hid-led: add support for devices with multiple independent LEDs
  HID: i2c-hid: set power sleep before shutdown
  HID: alps: match alps devices in core
  HID: thingm: simplify debug output code
  HID: alps: pass correct sizes to hid_hw_raw_request()
  HID: alps: struct u1_dev *priv is internal to the driver
  HID: add Alps I2C HID Touchpad-Stick support
  HID: led: fix config
  usb: misc: remove outdated USB LED driver
  HID: migrate USB LED driver from usb misc to hid
  HID: i2c_hid: enable i2c-hid devices to suspend/resume asynchronously
  ...
parents 69c42894 8c2f421c
Loading
Loading
Loading
Loading
+139 −0
Original line number Diff line number Diff line
ALPS HID Touchpad Protocol
----------------------

Introduction
------------
Currently ALPS HID driver supports U1 Touchpad device.

U1 devuce basic information.
Vender ID	0x044E
Product ID	0x120B
Version ID	0x0121


HID Descriptor
------------
Byte	Field			Value	Notes
0	wHIDDescLength		001E	Length of HID Descriptor : 30 bytes
2	bcdVersion		0100	Compliant with Version 1.00
4	wReportDescLength	00B2	Report Descriptor is 178 Bytes (0x00B2)
6	wReportDescRegister	0002	Identifier to read Report Descriptor
8	wInputRegister		0003	Identifier to read Input Report
10	wMaxInputLength		0053	Input Report is 80 Bytes + 2
12	wOutputRegister		0000	Identifier to read Output Report
14	wMaxOutputLength	0000	No Output Reports
16	wCommandRegister	0005	Identifier for Command Register
18	wDataRegister		0006	Identifier for Data Register
20	wVendorID		044E	Vendor ID 0x044E
22	wProductID		120B	Product ID 0x120B
24	wVersionID		0121	Version 01.21
26	RESERVED		0000	RESERVED


Report ID
------------
ReportID-1	(Input Reports)	(HIDUsage-Mouse) for TP&SP
ReportID-2	(Input Reports)	(HIDUsage-keyboard) for TP
ReportID-3	(Input Reports)	(Vendor Usage: Max 10 finger data) for TP
ReportID-4	(Input Reports)	(Vendor Usage: ON bit data) for GP
ReportID-5	(Feature Reports)	Feature Reports
ReportID-6	(Input Reports)	(Vendor Usage: StickPointer data) for SP
ReportID-7	(Feature Reports)	Flash update (Bootloader)


Data pattern
------------
Case1	ReportID_1	TP/SP	Relative/Relative
Case2	ReportID_3	TP	Absolute
	ReportID_6	SP	Absolute


Command Read/Write
------------------
To read/write to RAM, need to send a commands to the device.
The command format is as below.

DataByte(SET_REPORT)
Byte1	Command Byte
Byte2	Address - Byte 0 (LSB)
Byte3	Address - Byte 1
Byte4	Address - Byte 2
Byte5	Address - Byte 3 (MSB)
Byte6	Value Byte
Byte7	Checksum

Command Byte is read=0xD1/write=0xD2 .
Address is read/write RAM address.
Value Byte is writing data when you send the write commands.
When you read RAM, there is no meaning.

DataByte(GET_REPORT)
Byte1	Response Byte
Byte2	Address - Byte 0 (LSB)
Byte3	Address - Byte 1
Byte4	Address - Byte 2
Byte5	Address - Byte 3 (MSB)
Byte6	Value Byte
Byte7	Checksum

Read value is stored in Value Byte.


Packet Format
Touchpad data byte
------------------
	b7	b6	b5	b4	b3	b2	b1	b0
1	0	0	SW6	SW5	SW4	SW3	SW2	SW1
2	0	0	0	Fcv	Fn3	Fn2	Fn1	Fn0
3	Xa0_7	Xa0_6	Xa0_5	Xa0_4	Xa0_3	Xa0_2	Xa0_1	Xa0_0
4	Xa0_15	Xa0_14	Xa0_13	Xa0_12	Xa0_11	Xa0_10	Xa0_9	Xa0_8
5	Ya0_7	Ya0_6	Ya0_5	Ya0_4	Ya0_3	Ya0_2	Ya0_1	Ya0_0
6	Ya0_15	Ya0_14	Ya0_13	Ya0_12	Ya0_11	Ya0_10	Ya0_9	Ya0_8
7	LFB0	Zs0_6	Zs0_5	Zs0_4	Zs0_3	Zs0_2	Zs0_1	Zs0_0

8	Xa1_7	Xa1_6	Xa1_5	Xa1_4	Xa1_3	Xa1_2	Xa1_1	Xa1_0
9	Xa1_15	Xa1_14	Xa1_13	Xa1_12	Xa1_11	Xa1_10	Xa1_9	Xa1_8
10	Ya1_7	Ya1_6	Ya1_5	Ya1_4	Ya1_3	Ya1_2	Ya1_1	Ya1_0
11	Ya1_15	Ya1_14	Ya1_13	Ya1_12	Ya1_11	Ya1_10	Ya1_9	Ya1_8
12	LFB1	Zs1_6	Zs1_5	Zs1_4	Zs1_3	Zs1_2	Zs1_1	Zs1_0

13	Xa2_7	Xa2_6	Xa2_5	Xa2_4	Xa2_3	Xa2_2	Xa2_1	Xa2_0
14	Xa2_15	Xa2_14	Xa2_13	Xa2_12	Xa2_11	Xa2_10	Xa2_9	Xa2_8
15	Ya2_7	Ya2_6	Ya2_5	Ya2_4	Ya2_3	Ya2_2	Ya2_1	Ya2_0
16	Ya2_15	Ya2_14	Ya2_13	Ya2_12	Ya2_11	Ya2_10	Ya2_9	Ya2_8
17	LFB2	Zs2_6	Zs2_5	Zs2_4	Zs2_3	Zs2_2	Zs2_1	Zs2_0

18	Xa3_7	Xa3_6	Xa3_5	Xa3_4	Xa3_3	Xa3_2	Xa3_1	Xa3_0
19	Xa3_15	Xa3_14	Xa3_13	Xa3_12	Xa3_11	Xa3_10	Xa3_9	Xa3_8
20	Ya3_7	Ya3_6	Ya3_5	Ya3_4	Ya3_3	Ya3_2	Ya3_1	Ya3_0
21	Ya3_15	Ya3_14	Ya3_13	Ya3_12	Ya3_11	Ya3_10	Ya3_9	Ya3_8
22	LFB3	Zs3_6	Zs3_5	Zs3_4	Zs3_3	Zs3_2	Zs3_1	Zs3_0

23	Xa4_7	Xa4_6	Xa4_5	Xa4_4	Xa4_3	Xa4_2	Xa4_1	Xa4_0
24	Xa4_15	Xa4_14	Xa4_13	Xa4_12	Xa4_11	Xa4_10	Xa4_9	Xa4_8
25	Ya4_7	Ya4_6	Ya4_5	Ya4_4	Ya4_3	Ya4_2	Ya4_1	Ya4_0
26	Ya4_15	Ya4_14	Ya4_13	Ya4_12	Ya4_11	Ya4_10	Ya4_9	Ya4_8
27	LFB4	Zs4_6	Zs4_5	Zs4_4	Zs4_3	Zs4_2	Zs4_1	Zs4_0


SW1-SW6:	SW ON/OFF status
Xan_15-0(16bit):X Absolute data of the "n"th finger
Yan_15-0(16bit):Y Absolute data of the "n"th finger
Zsn_6-0(7bit):	Operation area of the "n"th finger


StickPointer data byte
------------------
	b7	b6	b5	b4	b3	b2	b1	b0
Byte1	1	1	1	0	1	SW3	SW2	SW1
Byte2	X7	X6	X5	X4	X3	X2	X1	X0
Byte3	X15	X14	X13	X12	X11	X10	X9	X8
Byte4	Y7	Y6	Y5	Y4	Y3	Y2	Y1	Y0
Byte5	Y15	Y14	Y13	Y12	Y11	Y10	Y9	Y8
Byte6	Z7	Z6	Z5	Z4	Z3	Z2	Z1	Z0
Byte7	T&P	Z14	Z13	Z12	Z11	Z10	Z9	Z8

SW1-SW3:	SW ON/OFF status
Xn_15-0(16bit):X Absolute data
Yn_15-0(16bit):Y Absolute data
Zn_14-0(15bit):Z
+0 −5
Original line number Diff line number Diff line
@@ -11440,11 +11440,6 @@ F: Documentation/thermal/cpu-cooling-api.txt
F:	drivers/thermal/cpu_cooling.c
F:	include/linux/cpu_cooling.h

THINGM BLINK(1) USB RGB LED DRIVER
M:	Vivien Didelot <vivien.didelot@savoirfairelinux.com>
S:	Maintained
F:	drivers/hid/hid-thingm.c

THINKPAD ACPI EXTRAS DRIVER
M:	Henrique de Moraes Holschuh <ibm-acpi@hmh.eng.br>
L:	ibm-acpi-devel@lists.sourceforge.net
+27 −4
Original line number Diff line number Diff line
@@ -388,6 +388,21 @@ config HID_LCPOWER
	---help---
	Support for LC-Power RC1000MCE RF remote control.

config HID_LED
	tristate "Simple RGB LED support"
	depends on HID
	depends on LEDS_CLASS
	---help---
	Support for simple RGB LED devices. Currently supported are:
	- Riso Kagaku Webmail Notifier
	- Dream Cheeky Webmail Notifier and Friends Alert
	- ThingM blink(1)
	- Delcom Visual Signal Indicator Generation 2
	- Greynut Luxafor

	To compile this driver as a module, choose M here: the
	module will be called hid-led.

config HID_LENOVO
	tristate "Lenovo / Thinkpad devices"
	depends on HID
@@ -819,11 +834,11 @@ config HID_THINGM
	tristate "ThingM blink(1) USB RGB LED"
	depends on HID
	depends on LEDS_CLASS
	select HID_LED
	---help---
	Support for the ThingM blink(1) USB RGB LED. This driver registers a
	Linux LED class instance, plus additional sysfs attributes to control
	RGB colors, fade time and playing. The device is exposed through hidraw
	to access other functions.
	Support for the ThingM blink(1) USB RGB LED. This driver has been
	merged into the generic hid led driver. Config symbol HID_THINGM
	just selects HID_LED and will be removed soon.

config HID_THRUSTMASTER
	tristate "ThrustMaster devices support"
@@ -936,6 +951,14 @@ config HID_SENSOR_CUSTOM_SENSOR
	  standard sensors.
	  Select this config option for custom/generic sensor support.

config HID_ALPS
	tristate "Alps HID device support"
	depends on HID
	---help---
	Support for Alps I2C HID touchpads and StickPointer.
	Say Y here if you have a Alps touchpads over i2c-hid or usbhid
	and want support for its special functionalities.

endmenu

endif # HID
+2 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ hid-wiimote-y := hid-wiimote-core.o hid-wiimote-modules.o
hid-wiimote-$(CONFIG_DEBUG_FS)	+= hid-wiimote-debug.o

obj-$(CONFIG_HID_A4TECH)	+= hid-a4tech.o
obj-$(CONFIG_HID_ALPS)		+= hid-alps.o
obj-$(CONFIG_HID_ACRUX)		+= hid-axff.o
obj-$(CONFIG_HID_APPLE)		+= hid-apple.o
obj-$(CONFIG_HID_APPLEIR)	+= hid-appleir.o
@@ -90,12 +91,12 @@ obj-$(CONFIG_HID_SPEEDLINK) += hid-speedlink.o
obj-$(CONFIG_HID_STEELSERIES)	+= hid-steelseries.o
obj-$(CONFIG_HID_SUNPLUS)	+= hid-sunplus.o
obj-$(CONFIG_HID_GREENASIA)	+= hid-gaff.o
obj-$(CONFIG_HID_THINGM)	+= hid-thingm.o
obj-$(CONFIG_HID_THRUSTMASTER)	+= hid-tmff.o
obj-$(CONFIG_HID_TIVO)		+= hid-tivo.o
obj-$(CONFIG_HID_TOPSEED)	+= hid-topseed.o
obj-$(CONFIG_HID_TWINHAN)	+= hid-twinhan.o
obj-$(CONFIG_HID_UCLOGIC)	+= hid-uclogic.o
obj-$(CONFIG_HID_LED)		+= hid-led.o
obj-$(CONFIG_HID_XINMO)		+= hid-xinmo.o
obj-$(CONFIG_HID_ZEROPLUS)	+= hid-zpff.o
obj-$(CONFIG_HID_ZYDACRON)	+= hid-zydacron.o

drivers/hid/hid-alps.c

0 → 100644
+506 −0
Original line number Diff line number Diff line
/*
 *  Copyright (c) 2016 Masaki Ota <masaki.ota@jp.alps.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 */

#include <linux/kernel.h>
#include <linux/hid.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/module.h>
#include <asm/unaligned.h>
#include "hid-ids.h"

/* ALPS Device Product ID */
#define HID_PRODUCT_ID_T3_BTNLESS	0xD0C0
#define HID_PRODUCT_ID_COSMO		0x1202
#define HID_PRODUCT_ID_U1_PTP_1		0x1207
#define HID_PRODUCT_ID_U1			0x1209
#define HID_PRODUCT_ID_U1_PTP_2		0x120A
#define HID_PRODUCT_ID_U1_DUAL		0x120B
#define HID_PRODUCT_ID_T4_BTNLESS	0x120C

#define DEV_SINGLEPOINT				0x01
#define DEV_DUALPOINT				0x02

#define U1_MOUSE_REPORT_ID			0x01 /* Mouse data ReportID */
#define U1_ABSOLUTE_REPORT_ID		0x03 /* Absolute data ReportID */
#define U1_FEATURE_REPORT_ID		0x05 /* Feature ReportID */
#define U1_SP_ABSOLUTE_REPORT_ID	0x06 /* Feature ReportID */

#define U1_FEATURE_REPORT_LEN		0x08 /* Feature Report Length */
#define U1_FEATURE_REPORT_LEN_ALL	0x0A
#define U1_CMD_REGISTER_READ		0xD1
#define U1_CMD_REGISTER_WRITE		0xD2

#define	U1_DEVTYPE_SP_SUPPORT		0x10 /* SP Support */
#define	U1_DISABLE_DEV				0x01
#define U1_TP_ABS_MODE				0x02
#define	U1_SP_ABS_MODE				0x80

#define ADDRESS_U1_DEV_CTRL_1	0x00800040
#define ADDRESS_U1_DEVICE_TYP	0x00800043
#define ADDRESS_U1_NUM_SENS_X	0x00800047
#define ADDRESS_U1_NUM_SENS_Y	0x00800048
#define ADDRESS_U1_PITCH_SENS_X	0x00800049
#define ADDRESS_U1_PITCH_SENS_Y	0x0080004A
#define ADDRESS_U1_RESO_DWN_ABS 0x0080004E
#define ADDRESS_U1_PAD_BTN		0x00800052
#define ADDRESS_U1_SP_BTN		0x0080009F

#define MAX_TOUCHES	5

/**
 * struct u1_data
 *
 * @input: pointer to the kernel input device
 * @input2: pointer to the kernel input2 device
 * @hdev: pointer to the struct hid_device
 *
 * @dev_ctrl: device control parameter
 * @dev_type: device type
 * @sen_line_num_x: number of sensor line of X
 * @sen_line_num_y: number of sensor line of Y
 * @pitch_x: sensor pitch of X
 * @pitch_y: sensor pitch of Y
 * @resolution: resolution
 * @btn_info: button information
 * @x_active_len_mm: active area length of X (mm)
 * @y_active_len_mm: active area length of Y (mm)
 * @x_max: maximum x coordinate value
 * @y_max: maximum y coordinate value
 * @btn_cnt: number of buttons
 * @sp_btn_cnt: number of stick buttons
 */
struct u1_dev {
	struct input_dev *input;
	struct input_dev *input2;
	struct hid_device *hdev;

	u8	dev_ctrl;
	u8	dev_type;
	u8	sen_line_num_x;
	u8	sen_line_num_y;
	u8	pitch_x;
	u8	pitch_y;
	u8	resolution;
	u8	btn_info;
	u8	sp_btn_info;
	u32	x_active_len_mm;
	u32	y_active_len_mm;
	u32	x_max;
	u32	y_max;
	u32	btn_cnt;
	u32	sp_btn_cnt;
};

static int u1_read_write_register(struct hid_device *hdev, u32 address,
	u8 *read_val, u8 write_val, bool read_flag)
{
	int ret, i;
	u8 check_sum;
	u8 *input;
	u8 *readbuf;

	input = kzalloc(U1_FEATURE_REPORT_LEN, GFP_KERNEL);
	if (!input)
		return -ENOMEM;

	input[0] = U1_FEATURE_REPORT_ID;
	if (read_flag) {
		input[1] = U1_CMD_REGISTER_READ;
		input[6] = 0x00;
	} else {
		input[1] = U1_CMD_REGISTER_WRITE;
		input[6] = write_val;
	}

	put_unaligned_le32(address, input + 2);

	/* Calculate the checksum */
	check_sum = U1_FEATURE_REPORT_LEN_ALL;
	for (i = 0; i < U1_FEATURE_REPORT_LEN - 1; i++)
		check_sum += input[i];

	input[7] = check_sum;
	ret = hid_hw_raw_request(hdev, U1_FEATURE_REPORT_ID, input,
			U1_FEATURE_REPORT_LEN,
			HID_FEATURE_REPORT, HID_REQ_SET_REPORT);

	if (ret < 0) {
		dev_err(&hdev->dev, "failed to read command (%d)\n", ret);
		goto exit;
	}

	if (read_flag) {
		readbuf = kzalloc(U1_FEATURE_REPORT_LEN, GFP_KERNEL);
		if (!readbuf) {
			kfree(input);
			return -ENOMEM;
		}

		ret = hid_hw_raw_request(hdev, U1_FEATURE_REPORT_ID, readbuf,
				U1_FEATURE_REPORT_LEN,
				HID_FEATURE_REPORT, HID_REQ_GET_REPORT);

		if (ret < 0) {
			dev_err(&hdev->dev, "failed read register (%d)\n", ret);
			goto exit;
		}

		*read_val = readbuf[6];

		kfree(readbuf);
	}

	ret = 0;

exit:
	kfree(input);
	return ret;
}

static int alps_raw_event(struct hid_device *hdev,
		struct hid_report *report, u8 *data, int size)
{
	unsigned int x, y, z;
	int i;
	short sp_x, sp_y;
	struct u1_dev *hdata = hid_get_drvdata(hdev);

	switch (data[0]) {
	case U1_MOUSE_REPORT_ID:
		break;
	case U1_FEATURE_REPORT_ID:
		break;
	case U1_ABSOLUTE_REPORT_ID:
		for (i = 0; i < MAX_TOUCHES; i++) {
			u8 *contact = &data[i * 5];

			x = get_unaligned_le16(contact + 3);
			y = get_unaligned_le16(contact + 5);
			z = contact[7] & 0x7F;

			input_mt_slot(hdata->input, i);

			if (z != 0) {
				input_mt_report_slot_state(hdata->input,
					MT_TOOL_FINGER, 1);
			} else {
				input_mt_report_slot_state(hdata->input,
					MT_TOOL_FINGER, 0);
				break;
			}

			input_report_abs(hdata->input, ABS_MT_POSITION_X, x);
			input_report_abs(hdata->input, ABS_MT_POSITION_Y, y);
			input_report_abs(hdata->input, ABS_MT_PRESSURE, z);

		}

		input_mt_sync_frame(hdata->input);

		input_report_key(hdata->input, BTN_LEFT,
			data[1] & 0x1);
		input_report_key(hdata->input, BTN_RIGHT,
			(data[1] & 0x2));
		input_report_key(hdata->input, BTN_MIDDLE,
			(data[1] & 0x4));

		input_sync(hdata->input);

		return 1;

	case U1_SP_ABSOLUTE_REPORT_ID:
		sp_x = get_unaligned_le16(data+2);
		sp_y = get_unaligned_le16(data+4);

		sp_x = sp_x / 8;
		sp_y = sp_y / 8;

		input_report_rel(hdata->input2, REL_X, sp_x);
		input_report_rel(hdata->input2, REL_Y, sp_y);

		input_report_key(hdata->input2, BTN_LEFT,
			data[1] & 0x1);
		input_report_key(hdata->input2, BTN_RIGHT,
			(data[1] & 0x2));
		input_report_key(hdata->input2, BTN_MIDDLE,
			(data[1] & 0x4));

		input_sync(hdata->input2);

		return 1;
	}

	return 0;
}

#ifdef CONFIG_PM
static int alps_post_reset(struct hid_device *hdev)
{
	return u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1,
				NULL, U1_TP_ABS_MODE, false);
}

static int alps_post_resume(struct hid_device *hdev)
{
	return u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1,
				NULL, U1_TP_ABS_MODE, false);
}
#endif /* CONFIG_PM */

static int alps_input_configured(struct hid_device *hdev, struct hid_input *hi)
{
	struct u1_dev *data = hid_get_drvdata(hdev);
	struct input_dev *input = hi->input, *input2;
	struct u1_dev devInfo;
	int ret;
	int res_x, res_y, i;

	data->input = input;

	hid_dbg(hdev, "Opening low level driver\n");
	ret = hid_hw_open(hdev);
	if (ret)
		return ret;

	/* Allow incoming hid reports */
	hid_device_io_start(hdev);

	/* Device initialization */
	ret = u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1,
			&devInfo.dev_ctrl, 0, true);
	if (ret < 0) {
		dev_err(&hdev->dev, "failed U1_DEV_CTRL_1 (%d)\n", ret);
		goto exit;
	}

	devInfo.dev_ctrl &= ~U1_DISABLE_DEV;
	devInfo.dev_ctrl |= U1_TP_ABS_MODE;
	ret = u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1,
			NULL, devInfo.dev_ctrl, false);
	if (ret < 0) {
		dev_err(&hdev->dev, "failed to change TP mode (%d)\n", ret);
		goto exit;
	}

	ret = u1_read_write_register(hdev, ADDRESS_U1_NUM_SENS_X,
			&devInfo.sen_line_num_x, 0, true);
	if (ret < 0) {
		dev_err(&hdev->dev, "failed U1_NUM_SENS_X (%d)\n", ret);
		goto exit;
	}

	ret = u1_read_write_register(hdev, ADDRESS_U1_NUM_SENS_Y,
			&devInfo.sen_line_num_y, 0, true);
		if (ret < 0) {
		dev_err(&hdev->dev, "failed U1_NUM_SENS_Y (%d)\n", ret);
		goto exit;
	}

	ret = u1_read_write_register(hdev, ADDRESS_U1_PITCH_SENS_X,
			&devInfo.pitch_x, 0, true);
	if (ret < 0) {
		dev_err(&hdev->dev, "failed U1_PITCH_SENS_X (%d)\n", ret);
		goto exit;
	}

	ret = u1_read_write_register(hdev, ADDRESS_U1_PITCH_SENS_Y,
			&devInfo.pitch_y, 0, true);
	if (ret < 0) {
		dev_err(&hdev->dev, "failed U1_PITCH_SENS_Y (%d)\n", ret);
		goto exit;
	}

	ret = u1_read_write_register(hdev, ADDRESS_U1_RESO_DWN_ABS,
		&devInfo.resolution, 0, true);
	if (ret < 0) {
		dev_err(&hdev->dev, "failed U1_RESO_DWN_ABS (%d)\n", ret);
		goto exit;
	}

	ret = u1_read_write_register(hdev, ADDRESS_U1_PAD_BTN,
			&devInfo.btn_info, 0, true);
	if (ret < 0) {
		dev_err(&hdev->dev, "failed U1_PAD_BTN (%d)\n", ret);
		goto exit;
	}

	/* Check StickPointer device */
	ret = u1_read_write_register(hdev, ADDRESS_U1_DEVICE_TYP,
			&devInfo.dev_type, 0, true);
	if (ret < 0) {
		dev_err(&hdev->dev, "failed U1_DEVICE_TYP (%d)\n", ret);
		goto exit;
	}

	devInfo.x_active_len_mm =
		(devInfo.pitch_x * (devInfo.sen_line_num_x - 1)) / 10;
	devInfo.y_active_len_mm =
		(devInfo.pitch_y * (devInfo.sen_line_num_y - 1)) / 10;

	devInfo.x_max =
		(devInfo.resolution << 2) * (devInfo.sen_line_num_x - 1);
	devInfo.y_max =
		(devInfo.resolution << 2) * (devInfo.sen_line_num_y - 1);

	__set_bit(EV_ABS, input->evbit);
	input_set_abs_params(input, ABS_MT_POSITION_X, 1, devInfo.x_max, 0, 0);
	input_set_abs_params(input, ABS_MT_POSITION_Y, 1, devInfo.y_max, 0, 0);

	if (devInfo.x_active_len_mm && devInfo.y_active_len_mm) {
		res_x = (devInfo.x_max - 1) / devInfo.x_active_len_mm;
		res_y = (devInfo.y_max - 1) / devInfo.y_active_len_mm;

		input_abs_set_res(input, ABS_MT_POSITION_X, res_x);
		input_abs_set_res(input, ABS_MT_POSITION_Y, res_y);
	}

	input_set_abs_params(input, ABS_MT_PRESSURE, 0, 64, 0, 0);

	input_mt_init_slots(input, MAX_TOUCHES, INPUT_MT_POINTER);

	__set_bit(EV_KEY, input->evbit);
	if ((devInfo.btn_info & 0x0F) == (devInfo.btn_info & 0xF0) >> 4) {
		devInfo.btn_cnt = (devInfo.btn_info & 0x0F);
	} else {
		/* Button pad */
		devInfo.btn_cnt = 1;
		__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
	}

	for (i = 0; i < devInfo.btn_cnt; i++)
		__set_bit(BTN_LEFT + i, input->keybit);


	/* Stick device initialization */
	if (devInfo.dev_type & U1_DEVTYPE_SP_SUPPORT) {

		input2 = input_allocate_device();
		if (!input2) {
			input_free_device(input2);
			goto exit;
		}

		data->input2 = input2;

		devInfo.dev_ctrl |= U1_SP_ABS_MODE;
		ret = u1_read_write_register(hdev, ADDRESS_U1_DEV_CTRL_1,
			NULL, devInfo.dev_ctrl, false);
		if (ret < 0) {
			dev_err(&hdev->dev, "failed SP mode (%d)\n", ret);
			input_free_device(input2);
			goto exit;
		}

		ret = u1_read_write_register(hdev, ADDRESS_U1_SP_BTN,
			&devInfo.sp_btn_info, 0, true);
		if (ret < 0) {
			dev_err(&hdev->dev, "failed U1_SP_BTN (%d)\n", ret);
			input_free_device(input2);
			goto exit;
		}

		input2->phys = input->phys;
		input2->name = "DualPoint Stick";
		input2->id.bustype = BUS_I2C;
		input2->id.vendor  = input->id.vendor;
		input2->id.product = input->id.product;
		input2->id.version = input->id.version;
		input2->dev.parent = input->dev.parent;

		__set_bit(EV_KEY, input2->evbit);
		devInfo.sp_btn_cnt = (devInfo.sp_btn_info & 0x0F);
		for (i = 0; i < devInfo.sp_btn_cnt; i++)
			__set_bit(BTN_LEFT + i, input2->keybit);

		__set_bit(EV_REL, input2->evbit);
		__set_bit(REL_X, input2->relbit);
		__set_bit(REL_Y, input2->relbit);
		__set_bit(INPUT_PROP_POINTER, input2->propbit);
		__set_bit(INPUT_PROP_POINTING_STICK, input2->propbit);

		if (input_register_device(data->input2)) {
			input_free_device(input2);
			goto exit;
		}
	}

exit:
	hid_device_io_stop(hdev);
	hid_hw_close(hdev);
	return ret;
}

static int alps_input_mapping(struct hid_device *hdev,
		struct hid_input *hi, struct hid_field *field,
		struct hid_usage *usage, unsigned long **bit, int *max)
{
	return -1;
}

static int alps_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
	struct u1_dev *data = NULL;
	int ret;

	data = devm_kzalloc(&hdev->dev, sizeof(struct u1_dev), GFP_KERNEL);
	if (!data)
		return -ENOMEM;

	data->hdev = hdev;
	hid_set_drvdata(hdev, data);

	hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;

	ret = hid_parse(hdev);
	if (ret) {
		hid_err(hdev, "parse failed\n");
		return ret;
	}

	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
	if (ret) {
		hid_err(hdev, "hw start failed\n");
		return ret;
	}

	return 0;
}

static void alps_remove(struct hid_device *hdev)
{
	hid_hw_stop(hdev);
}

static const struct hid_device_id alps_id[] = {
	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY,
		USB_VENDOR_ID_ALPS_JP, HID_DEVICE_ID_ALPS_U1_DUAL) },
	{ }
};
MODULE_DEVICE_TABLE(hid, alps_id);

static struct hid_driver alps_driver = {
	.name = "hid-alps",
	.id_table		= alps_id,
	.probe			= alps_probe,
	.remove			= alps_remove,
	.raw_event		= alps_raw_event,
	.input_mapping		= alps_input_mapping,
	.input_configured	= alps_input_configured,
#ifdef CONFIG_PM
	.resume			= alps_post_resume,
	.reset_resume		= alps_post_reset,
#endif
};

module_hid_driver(alps_driver);

MODULE_AUTHOR("Masaki Ota <masaki.ota@jp.alps.com>");
MODULE_DESCRIPTION("ALPS HID driver");
MODULE_LICENSE("GPL");
Loading