Commit c62f87e2 authored by Jiri Kosina's avatar Jiri Kosina
Browse files

Merge branch 'for-6.17/uclogic' into for-linus

- support for XP-PEN Artist 22R Pro (Joshua Goins)
parents ddb7a62a 96718545
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1409,6 +1409,7 @@
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S	0x0909
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_SW	0x0933
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06	0x0078
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_22R_PRO	0x091b
#define USB_DEVICE_ID_UGEE_TABLET_G5		0x0074
#define USB_DEVICE_ID_UGEE_TABLET_EX07S		0x0071
#define USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720	0x0055
+62 −4
Original line number Diff line number Diff line
@@ -62,6 +62,30 @@ static const __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
	return rdesc;
}

/* Buttons considered valid tablet pad inputs. */
static const unsigned int uclogic_extra_input_mapping[] = {
	BTN_0,
	BTN_1,
	BTN_2,
	BTN_3,
	BTN_4,
	BTN_5,
	BTN_6,
	BTN_7,
	BTN_8,
	BTN_RIGHT,
	BTN_MIDDLE,
	BTN_SIDE,
	BTN_EXTRA,
	BTN_FORWARD,
	BTN_BACK,
	BTN_B,
	BTN_A,
	BTN_BASE,
	BTN_BASE2,
	BTN_X
};

static int uclogic_input_mapping(struct hid_device *hdev,
				 struct hid_input *hi,
				 struct hid_field *field,
@@ -72,9 +96,27 @@ static int uclogic_input_mapping(struct hid_device *hdev,
	struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
	struct uclogic_params *params = &drvdata->params;

	if (field->application == HID_GD_KEYPAD) {
		/*
		 * Remap input buttons to sensible ones that are not invalid.
		 * This only affects previous behavior for devices with more than ten or so buttons.
		 */
		const int key = (usage->hid & HID_USAGE) - 1;

		if (key < ARRAY_SIZE(uclogic_extra_input_mapping)) {
			hid_map_usage(hi,
				      usage,
				      bit,
				      max,
				      EV_KEY,
				      uclogic_extra_input_mapping[key]);
			return 1;
		}
	} else if (field->application == HID_DG_PEN) {
		/* Discard invalid pen usages */
	if (params->pen.usage_invalid && (field->application == HID_DG_PEN))
		if (params->pen.usage_invalid)
			return -1;
	}

	/* Let hid-core decide what to do */
	return 0;
@@ -407,8 +449,22 @@ static int uclogic_raw_event_frame(

	/* If need to, and can, transform the bitmap dial reports */
	if (frame->bitmap_dial_byte > 0 && frame->bitmap_dial_byte < size) {
		if (data[frame->bitmap_dial_byte] == 2)
		switch (data[frame->bitmap_dial_byte]) {
		case 2:
			data[frame->bitmap_dial_byte] = -1;
			break;

		/* Everything below here is for tablets that shove multiple dials into 1 byte */
		case 16:
			data[frame->bitmap_dial_byte] = 0;
			data[frame->bitmap_second_dial_destination_byte] = 1;
			break;

		case 32:
			data[frame->bitmap_dial_byte] = 0;
			data[frame->bitmap_second_dial_destination_byte] = -1;
			break;
		}
	}

	return 0;
@@ -546,6 +602,8 @@ static const struct hid_device_id uclogic_devices[] = {
		.driver_data = UCLOGIC_MOUSE_FRAME_QUIRK | UCLOGIC_BATTERY_QUIRK },
	{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
				USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06) },
	{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
				USB_DEVICE_ID_UGEE_XPPEN_TABLET_22R_PRO) },
	{ }
};
MODULE_DEVICE_TABLE(hid, uclogic_devices);
+133 −1
Original line number Diff line number Diff line
@@ -103,6 +103,8 @@ static void uclogic_params_frame_hid_dbg(
		frame->touch_flip_at);
	hid_dbg(hdev, "\t\t.bitmap_dial_byte = %u\n",
		frame->bitmap_dial_byte);
	hid_dbg(hdev, "\t\t.bitmap_second_dial_destination_byte = %u\n",
			frame->bitmap_second_dial_destination_byte);
}

/**
@@ -1341,7 +1343,7 @@ static int uclogic_params_ugee_v2_init_event_hooks(struct hid_device *hdev,
						   struct uclogic_params *p)
{
	struct uclogic_raw_event_hook *event_hook;
	__u8 reconnect_event[] = {
	static const __u8 reconnect_event[] = {
		/* Event received on wireless tablet reconnection */
		0x02, 0xF8, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
	};
@@ -1529,6 +1531,126 @@ static int uclogic_params_ugee_v2_init(struct uclogic_params *params,
	return rc;
}

/*
 * uclogic_params_init_ugee_xppen_pro_22r() - Initializes a UGEE XP-Pen Pro 22R tablet device.
 *
 * @hdev:	The HID device of the tablet interface to initialize and get
 *		parameters from. Cannot be NULL.
 * @params:	Parameters to fill in (to be cleaned with
 *		uclogic_params_cleanup()). Not modified in case of error.
 *		Cannot be NULL.
 *
 * Returns:
 *	Zero, if successful. A negative errno code on error.
 */
static int uclogic_params_init_ugee_xppen_pro_22r(struct uclogic_params *params,
						  struct hid_device *hdev,
						  const u8 rdesc_frame_arr[],
						  const size_t rdesc_frame_size)
{
	int rc = 0;
	struct usb_interface *iface;
	__u8 bInterfaceNumber;
	const int str_desc_len = 12;
	u8 *str_desc = NULL;
	__u8 *rdesc_pen = NULL;
	s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM];
	enum uclogic_params_frame_type frame_type;
	/* The resulting parameters (noop) */
	struct uclogic_params p = {0, };

	if (!hdev || !params) {
		rc = -EINVAL;
		goto cleanup;
	}

	iface = to_usb_interface(hdev->dev.parent);
	bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;

	/* Ignore non-pen interfaces */
	if (bInterfaceNumber != 2) {
		rc = -EINVAL;
		uclogic_params_init_invalid(&p);
		goto cleanup;
	}

	/*
	 * Initialize the interface by sending magic data.
	 * This magic data is the same as other UGEE v2 tablets.
	 */
	rc = uclogic_probe_interface(hdev,
				     uclogic_ugee_v2_probe_arr,
				     uclogic_ugee_v2_probe_size,
				     uclogic_ugee_v2_probe_endpoint);
	if (rc) {
		uclogic_params_init_invalid(&p);
		goto cleanup;
	}

	/**
	 * Read the string descriptor containing pen and frame parameters.
	 * These are slightly different than typical UGEE v2 devices.
	 */
	rc = uclogic_params_get_str_desc(&str_desc, hdev, 100, str_desc_len);
	if (rc != str_desc_len) {
		rc = (rc < 0) ? rc : -EINVAL;
		hid_err(hdev, "failed retrieving pen and frame parameters: %d\n", rc);
		uclogic_params_init_invalid(&p);
		goto cleanup;
	}

	rc = uclogic_params_parse_ugee_v2_desc(str_desc, str_desc_len,
					       desc_params,
					       ARRAY_SIZE(desc_params),
					       &frame_type);
	if (rc)
		goto cleanup;

	// str_desc doesn't report the correct amount of buttons, so manually fix it
	desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = 20;

	kfree(str_desc);
	str_desc = NULL;

	/* Initialize the pen interface */
	rdesc_pen = uclogic_rdesc_template_apply(
				uclogic_rdesc_ugee_v2_pen_template_arr,
				uclogic_rdesc_ugee_v2_pen_template_size,
				desc_params, ARRAY_SIZE(desc_params));
	if (!rdesc_pen) {
		rc = -ENOMEM;
		goto cleanup;
	}

	p.pen.desc_ptr = rdesc_pen;
	p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size;
	p.pen.id = 0x02;
	p.pen.subreport_list[0].value = 0xf0;
	p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID;

	/* Initialize the frame interface */
	rc = uclogic_params_frame_init_with_desc(
		&p.frame_list[0],
		rdesc_frame_arr,
		rdesc_frame_size,
		UCLOGIC_RDESC_V1_FRAME_ID);
	if (rc < 0) {
		hid_err(hdev, "initializing frame params failed: %d\n", rc);
		goto cleanup;
	}

	p.frame_list[0].bitmap_dial_byte = 7;
	p.frame_list[0].bitmap_second_dial_destination_byte = 8;

	/* Output parameters */
	memcpy(params, &p, sizeof(*params));
	memset(&p, 0, sizeof(p));
cleanup:
	kfree(str_desc);
	uclogic_params_cleanup(&p);
	return rc;
}

/**
 * uclogic_params_init() - initialize a tablet interface and discover its
 * parameters.
@@ -1845,6 +1967,16 @@ int uclogic_params_init(struct uclogic_params *params,
			uclogic_params_init_invalid(&p);
		}

		break;
	case VID_PID(USB_VENDOR_ID_UGEE,
			USB_DEVICE_ID_UGEE_XPPEN_TABLET_22R_PRO):
		rc = uclogic_params_init_ugee_xppen_pro_22r(&p,
			hdev,
			uclogic_rdesc_xppen_artist_22r_pro_frame_arr,
			uclogic_rdesc_xppen_artist_22r_pro_frame_size);
		if (rc != 0)
			goto cleanup;

		break;
	}

+5 −0
Original line number Diff line number Diff line
@@ -175,6 +175,11 @@ struct uclogic_params_frame {
	 * counterclockwise, as opposed to the normal 1 and -1.
	 */
	unsigned int bitmap_dial_byte;
	/*
	 * Destination offset for the second bitmap dial byte, if the tablet
	 * supports a second dial at all.
	 */
	unsigned int bitmap_second_dial_destination_byte;
};

/*
+44 −0
Original line number Diff line number Diff line
@@ -1193,6 +1193,50 @@ const __u8 uclogic_rdesc_xppen_deco01_frame_arr[] = {
const size_t uclogic_rdesc_xppen_deco01_frame_size =
			sizeof(uclogic_rdesc_xppen_deco01_frame_arr);

/* Fixed report descriptor for XP-Pen Arist 22R Pro frame */
const __u8 uclogic_rdesc_xppen_artist_22r_pro_frame_arr[] = {
	0x05, 0x01,         /*  Usage Page (Desktop),                       */
	0x09, 0x07,         /*  Usage (Keypad),                             */
	0xA1, 0x01,         /*  Collection (Application),                   */
	0x85, UCLOGIC_RDESC_V1_FRAME_ID,
	/*      Report ID (Virtual report),             */
	0x05, 0x0D,         /*     Usage Page (Digitizer),                  */
	0x09, 0x39,         /*      Usage (Tablet Function Keys),           */
	0xA0,               /*      Collection (Physical),                  */
	0x14,               /*          Logical Minimum (0),                */
	0x25, 0x01,         /*          Logical Maximum (1),                */
	0x75, 0x01,         /*          Report Size (1),                    */
	0x95, 0x08,         /*          Report Count (8),                   */
	0x81, 0x01,         /*          Input (Constant),                   */
	0x05, 0x09,         /*          Usage Page (Button),                */
	0x19, 0x01,         /*          Usage Minimum (01h),                */
	0x29, 0x14,         /*          Usage Maximum (14h),                */
	0x95, 0x14,         /*          Report Count (20),                  */
	0x81, 0x02,         /*          Input (Variable),                   */
	0x95, 0x14,         /*          Report Count (20),                  */
	0x81, 0x01,         /*          Input (Constant),                   */
	0x05, 0x01,         /*          Usage Page (Desktop),               */
	0x09, 0x38,         /*          Usage (Wheel),                      */
	0x75, 0x08,         /*          Report Size (8),                    */
	0x95, 0x01,         /*          Report Count (1),                   */
	0x15, 0xFF,         /*          Logical Minimum (-1),               */
	0x25, 0x08,         /*          Logical Maximum (8),                */
	0x81, 0x06,         /*          Input (Variable, Relative),         */
	0x05, 0x0C,         /*          Usage Page (Consumer Devices),      */
	0x0A, 0x38, 0x02,   /*          Usage (AC PAN),                     */
	0x95, 0x01,         /*          Report Count (1),                   */
	0x81, 0x06,         /*          Input (Variable, Relative),         */
	0x26, 0xFF, 0x00,   /*          Logical Maximum (255),              */
	0x75, 0x08,         /*          Report Size (8),                    */
	0x95, 0x01,         /*          Report Count (1),                   */
	0x81, 0x02,         /*          Input (Variable),                   */
	0xC0,               /*      End Collection                          */
	0xC0,               /*  End Collection                              */
};

const size_t uclogic_rdesc_xppen_artist_22r_pro_frame_size =
				sizeof(uclogic_rdesc_xppen_artist_22r_pro_frame_arr);

/**
 * uclogic_rdesc_template_apply() - apply report descriptor parameters to a
 * report descriptor template, creating a report descriptor. Copies the
Loading