int uclogic_params_init()

in hid-uclogic-params.c [841:1140]


int uclogic_params_init(struct uclogic_params *params,
			struct hid_device *hdev)
{
	int rc;
	struct usb_device *udev;
	__u8  bNumInterfaces;
	struct usb_interface *iface;
	__u8 bInterfaceNumber;
	bool found;
	/* The resulting parameters (noop) */
	struct uclogic_params p = {0, };

	/* Check arguments */
	if (params == NULL || hdev == NULL || !hid_is_usb(hdev)) {
		rc = -EINVAL;
		goto cleanup;
	}

	udev = hid_to_usb_dev(hdev);
	bNumInterfaces = udev->config->desc.bNumInterfaces;
	iface = to_usb_interface(hdev->dev.parent);
	bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;

	/*
	 * Set replacement report descriptor if the original matches the
	 * specified size. Otherwise keep interface unchanged.
	 */
#define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \
	uclogic_params_init_with_opt_desc(                  \
		&p, hdev,                                   \
		UCLOGIC_RDESC_##_orig_desc_token##_SIZE,    \
		uclogic_rdesc_##_new_desc_token##_arr,      \
		uclogic_rdesc_##_new_desc_token##_size)

#define VID_PID(_vid, _pid) \
	(((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX))

	/*
	 * Handle specific interfaces for specific tablets.
	 *
	 * Observe the following logic:
	 *
	 * If the interface is recognized as producing certain useful input:
	 *	Mark interface as valid.
	 *	Output interface parameters.
	 * Else, if the interface is recognized as *not* producing any useful
	 * input:
	 *	Mark interface as invalid.
	 * Else:
	 *	Mark interface as valid.
	 *	Output noop parameters.
	 *
	 * Rule of thumb: it is better to disable a broken interface than let
	 *		  it spew garbage input.
	 */

	switch (VID_PID(hdev->vendor, hdev->product)) {
	case VID_PID(USB_VENDOR_ID_UCLOGIC,
		     USB_DEVICE_ID_UCLOGIC_TABLET_PF1209):
		rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed);
		if (rc != 0)
			goto cleanup;
		break;
	case VID_PID(USB_VENDOR_ID_UCLOGIC,
		     USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U):
		rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed);
		if (rc != 0)
			goto cleanup;
		break;
	case VID_PID(USB_VENDOR_ID_UCLOGIC,
		     USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U):
		if (hdev->dev_rsize == UCLOGIC_RDESC_WP5540U_V2_ORIG_SIZE) {
			if (bInterfaceNumber == 0) {
				/* Try to probe v1 pen parameters */
				rc = uclogic_params_pen_init_v1(&p.pen,
								&found, hdev);
				if (rc != 0) {
					hid_err(hdev,
						"pen probing failed: %d\n",
						rc);
					goto cleanup;
				}
				if (!found) {
					hid_warn(hdev,
						 "pen parameters not found");
				}
			} else {
				uclogic_params_init_invalid(&p);
			}
		} else {
			rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed);
			if (rc != 0)
				goto cleanup;
		}
		break;
	case VID_PID(USB_VENDOR_ID_UCLOGIC,
		     USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U):
		rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed);
		if (rc != 0)
			goto cleanup;
		break;
	case VID_PID(USB_VENDOR_ID_UCLOGIC,
		     USB_DEVICE_ID_UCLOGIC_TABLET_WP1062):
		rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed);
		if (rc != 0)
			goto cleanup;
		break;
	case VID_PID(USB_VENDOR_ID_UCLOGIC,
		     USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850):
		switch (bInterfaceNumber) {
		case 0:
			rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0);
			if (rc != 0)
				goto cleanup;
			break;
		case 1:
			rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1);
			if (rc != 0)
				goto cleanup;
			break;
		case 2:
			rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2);
			if (rc != 0)
				goto cleanup;
			break;
		}
		break;
	case VID_PID(USB_VENDOR_ID_UCLOGIC,
		     USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60):
		/*
		 * If it is not a three-interface version, which is known to
		 * respond to initialization.
		 */
		if (bNumInterfaces != 3) {
			switch (bInterfaceNumber) {
			case 0:
				rc = WITH_OPT_DESC(TWHA60_ORIG0,
							twha60_fixed0);
				if (rc != 0)
					goto cleanup;
				break;
			case 1:
				rc = WITH_OPT_DESC(TWHA60_ORIG1,
							twha60_fixed1);
				if (rc != 0)
					goto cleanup;
				break;
			}
			break;
		}
		fallthrough;
	case VID_PID(USB_VENDOR_ID_HUION,
		     USB_DEVICE_ID_HUION_TABLET):
	case VID_PID(USB_VENDOR_ID_HUION,
		     USB_DEVICE_ID_HUION_HS64):
	case VID_PID(USB_VENDOR_ID_UCLOGIC,
		     USB_DEVICE_ID_HUION_TABLET):
	case VID_PID(USB_VENDOR_ID_UCLOGIC,
		     USB_DEVICE_ID_YIYNOVA_TABLET):
	case VID_PID(USB_VENDOR_ID_UCLOGIC,
		     USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81):
	case VID_PID(USB_VENDOR_ID_UCLOGIC,
		     USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3):
	case VID_PID(USB_VENDOR_ID_UCLOGIC,
		     USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45):
	case VID_PID(USB_VENDOR_ID_UCLOGIC,
		     USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_47):
		rc = uclogic_params_huion_init(&p, hdev);
		if (rc != 0)
			goto cleanup;
		break;
	case VID_PID(USB_VENDOR_ID_UGTIZER,
		     USB_DEVICE_ID_UGTIZER_TABLET_GP0610):
	case VID_PID(USB_VENDOR_ID_UGTIZER,
		     USB_DEVICE_ID_UGTIZER_TABLET_GT5040):
	case VID_PID(USB_VENDOR_ID_UGEE,
		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540):
	case VID_PID(USB_VENDOR_ID_UGEE,
		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640):
	case VID_PID(USB_VENDOR_ID_UGEE,
		     USB_DEVICE_ID_UGEE_TABLET_RAINBOW_CV720):
		/* If this is the pen interface */
		if (bInterfaceNumber == 1) {
			/* Probe v1 pen parameters */
			rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
			if (rc != 0) {
				hid_err(hdev, "pen probing failed: %d\n", rc);
				goto cleanup;
			}
			if (!found) {
				hid_warn(hdev, "pen parameters not found");
				uclogic_params_init_invalid(&p);
			}
		} else {
			/* TODO: Consider marking the interface invalid */
			uclogic_params_init_with_pen_unused(&p);
		}
		break;
	case VID_PID(USB_VENDOR_ID_UGEE,
		     USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01):
		/* If this is the pen and frame interface */
		if (bInterfaceNumber == 1) {
			/* Probe v1 pen parameters */
			rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
			if (rc != 0) {
				hid_err(hdev, "pen probing failed: %d\n", rc);
				goto cleanup;
			}
			/* Initialize frame parameters */
			rc = uclogic_params_frame_init_with_desc(
				&p.frame,
				uclogic_rdesc_xppen_deco01_frame_arr,
				uclogic_rdesc_xppen_deco01_frame_size,
				0);
			if (rc != 0)
				goto cleanup;
		} else {
			/* TODO: Consider marking the interface invalid */
			uclogic_params_init_with_pen_unused(&p);
		}
		break;
	case VID_PID(USB_VENDOR_ID_TRUST,
		     USB_DEVICE_ID_TRUST_PANORA_TABLET):
	case VID_PID(USB_VENDOR_ID_UGEE,
		     USB_DEVICE_ID_UGEE_TABLET_G5):
		/* Ignore non-pen interfaces */
		if (bInterfaceNumber != 1) {
			uclogic_params_init_invalid(&p);
			break;
		}

		rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
		if (rc != 0) {
			hid_err(hdev, "pen probing failed: %d\n", rc);
			goto cleanup;
		} else if (found) {
			rc = uclogic_params_frame_init_with_desc(
				&p.frame,
				uclogic_rdesc_ugee_g5_frame_arr,
				uclogic_rdesc_ugee_g5_frame_size,
				UCLOGIC_RDESC_UGEE_G5_FRAME_ID);
			if (rc != 0) {
				hid_err(hdev,
					"failed creating buttonpad parameters: %d\n",
					rc);
				goto cleanup;
			}
			p.frame.re_lsb =
				UCLOGIC_RDESC_UGEE_G5_FRAME_RE_LSB;
			p.frame.dev_id_byte =
				UCLOGIC_RDESC_UGEE_G5_FRAME_DEV_ID_BYTE;
		} else {
			hid_warn(hdev, "pen parameters not found");
			uclogic_params_init_invalid(&p);
		}

		break;
	case VID_PID(USB_VENDOR_ID_UGEE,
		     USB_DEVICE_ID_UGEE_TABLET_EX07S):
		/* Ignore non-pen interfaces */
		if (bInterfaceNumber != 1) {
			uclogic_params_init_invalid(&p);
			break;
		}

		rc = uclogic_params_pen_init_v1(&p.pen, &found, hdev);
		if (rc != 0) {
			hid_err(hdev, "pen probing failed: %d\n", rc);
			goto cleanup;
		} else if (found) {
			rc = uclogic_params_frame_init_with_desc(
				&p.frame,
				uclogic_rdesc_ugee_ex07_buttonpad_arr,
				uclogic_rdesc_ugee_ex07_buttonpad_size,
				0);
			if (rc != 0) {
				hid_err(hdev,
					"failed creating buttonpad parameters: %d\n",
					rc);
				goto cleanup;
			}
		} else {
			hid_warn(hdev, "pen parameters not found");
			uclogic_params_init_invalid(&p);
		}

		break;
	}

#undef VID_PID
#undef WITH_OPT_DESC

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