1191 lines · 31.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Surface2.0/SUR40/PixelSense input driver * * Copyright (c) 2014 by Florian 'floe' Echtler <floe@butterbrot.org> * * Derived from the USB Skeleton driver 1.1, * Copyright (c) 2003 Greg Kroah-Hartman (greg@kroah.com) * * and from the Apple USB BCM5974 multitouch driver, * Copyright (c) 2008 Henrik Rydberg (rydberg@euromail.se) * * and from the generic hid-multitouch driver, * Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr> * * and from the v4l2-pci-skeleton driver, * Copyright (c) Copyright 2014 Cisco Systems, Inc. */ #include <linux/kernel.h>#include <linux/errno.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/completion.h>#include <linux/uaccess.h>#include <linux/usb.h>#include <linux/printk.h>#include <linux/input.h>#include <linux/input/mt.h>#include <linux/usb/input.h>#include <linux/videodev2.h>#include <media/v4l2-device.h>#include <media/v4l2-dev.h>#include <media/v4l2-ioctl.h>#include <media/v4l2-ctrls.h>#include <media/videobuf2-v4l2.h>#include <media/videobuf2-dma-sg.h> /* read 512 bytes from endpoint 0x86 -> get header + blobs */struct sur40_header { 	__le16 type;       /* always 0x0001 */	__le16 count;      /* count of blobs (if 0: continue prev. packet) */ 	__le32 packet_id;  /* unique ID for all packets in one frame */ 	__le32 timestamp;  /* milliseconds (inc. by 16 or 17 each frame) */	__le32 unknown;    /* "epoch?" always 02/03 00 00 00 */ } __packed; struct sur40_blob { 	__le16 blob_id; 	u8 action;         /* 0x02 = enter/exit, 0x03 = update (?) */	u8 type;           /* bitmask (0x01 blob,  0x02 touch, 0x04 tag) */ 	__le16 bb_pos_x;   /* upper left corner of bounding box */	__le16 bb_pos_y; 	__le16 bb_size_x;  /* size of bounding box */	__le16 bb_size_y; 	__le16 pos_x;      /* finger tip position */	__le16 pos_y; 	__le16 ctr_x;      /* centroid position */	__le16 ctr_y; 	__le16 axis_x;     /* somehow related to major/minor axis, mostly: */	__le16 axis_y;     /* axis_x == bb_size_y && axis_y == bb_size_x */ 	__le32 angle;      /* orientation in radians relative to x axis -	                      actually an IEEE754 float, don't use in kernel */ 	__le32 area;       /* size in pixels/pressure (?) */ 	u8 padding[24]; 	__le32 tag_id;     /* valid when type == 0x04 (SUR40_TAG) */	__le32 unknown; } __packed; /* combined header/blob data */struct sur40_data {	struct sur40_header header;	struct sur40_blob   blobs[];} __packed; /* read 512 bytes from endpoint 0x82 -> get header below * continue reading 16k blocks until header.size bytes read */struct sur40_image_header {	__le32 magic;     /* "SUBF" */	__le32 packet_id;	__le32 size;      /* always 0x0007e900 = 960x540 */	__le32 timestamp; /* milliseconds (increases by 16 or 17 each frame) */	__le32 unknown;   /* "epoch?" always 02/03 00 00 00 */} __packed; /* version information */#define DRIVER_SHORT   "sur40"#define DRIVER_LONG    "Samsung SUR40"#define DRIVER_AUTHOR  "Florian 'floe' Echtler <floe@butterbrot.org>"#define DRIVER_DESC    "Surface2.0/SUR40/PixelSense input driver" /* vendor and device IDs */#define ID_MICROSOFT 0x045e#define ID_SUR40     0x0775 /* sensor resolution */#define SENSOR_RES_X 1920#define SENSOR_RES_Y 1080 /* touch data endpoint */#define TOUCH_ENDPOINT 0x86 /* video data endpoint */#define VIDEO_ENDPOINT 0x82 /* video header fields */#define VIDEO_HEADER_MAGIC 0x46425553#define VIDEO_PACKET_SIZE  16384 /* polling interval (ms) */#define POLL_INTERVAL 1 /* maximum number of contacts */#define MAX_CONTACTS 52 /* control commands */#define SUR40_GET_VERSION 0xb0 /* 12 bytes string    */#define SUR40_ACCEL_CAPS  0xb3 /*  5 bytes           */#define SUR40_SENSOR_CAPS 0xc1 /* 24 bytes           */ #define SUR40_POKE        0xc5 /* poke register byte */#define SUR40_PEEK        0xc4 /* 48 bytes registers */ #define SUR40_GET_STATE   0xc5 /*  4 bytes state (?) */#define SUR40_GET_SENSORS 0xb1 /*  8 bytes sensors   */ #define SUR40_BLOB	0x01#define SUR40_TOUCH	0x02#define SUR40_TAG	0x04 /* video controls */#define SUR40_BRIGHTNESS_MAX 0xff#define SUR40_BRIGHTNESS_MIN 0x00#define SUR40_BRIGHTNESS_DEF 0xff #define SUR40_CONTRAST_MAX 0x0f#define SUR40_CONTRAST_MIN 0x00#define SUR40_CONTRAST_DEF 0x0a #define SUR40_GAIN_MAX 0x09#define SUR40_GAIN_MIN 0x00#define SUR40_GAIN_DEF 0x08 #define SUR40_BACKLIGHT_MAX 0x01#define SUR40_BACKLIGHT_MIN 0x00#define SUR40_BACKLIGHT_DEF 0x01 #define sur40_str(s) #s#define SUR40_PARAM_RANGE(lo, hi) " (range " sur40_str(lo) "-" sur40_str(hi) ")" /* module parameters */static uint brightness = SUR40_BRIGHTNESS_DEF;module_param(brightness, uint, 0644);MODULE_PARM_DESC(brightness, "set initial brightness"	SUR40_PARAM_RANGE(SUR40_BRIGHTNESS_MIN, SUR40_BRIGHTNESS_MAX));static uint contrast = SUR40_CONTRAST_DEF;module_param(contrast, uint, 0644);MODULE_PARM_DESC(contrast, "set initial contrast"	SUR40_PARAM_RANGE(SUR40_CONTRAST_MIN, SUR40_CONTRAST_MAX));static uint gain = SUR40_GAIN_DEF;module_param(gain, uint, 0644);MODULE_PARM_DESC(gain, "set initial gain"	SUR40_PARAM_RANGE(SUR40_GAIN_MIN, SUR40_GAIN_MAX)); static const struct v4l2_pix_format sur40_pix_format[] = {	{		.pixelformat = V4L2_TCH_FMT_TU08,		.width  = SENSOR_RES_X / 2,		.height = SENSOR_RES_Y / 2,		.field = V4L2_FIELD_NONE,		.colorspace = V4L2_COLORSPACE_RAW,		.bytesperline = SENSOR_RES_X / 2,		.sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2),	},	{		.pixelformat = V4L2_PIX_FMT_GREY,		.width  = SENSOR_RES_X / 2,		.height = SENSOR_RES_Y / 2,		.field = V4L2_FIELD_NONE,		.colorspace = V4L2_COLORSPACE_RAW,		.bytesperline = SENSOR_RES_X / 2,		.sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2),	}}; /* master device state */struct sur40_state { 	struct usb_device *usbdev;	struct device *dev;	struct input_dev *input; 	struct v4l2_device v4l2;	struct video_device vdev;	struct mutex lock;	struct v4l2_pix_format pix_fmt;	struct v4l2_ctrl_handler hdl; 	struct vb2_queue queue;	struct list_head buf_list;	spinlock_t qlock;	int sequence; 	struct sur40_data *bulk_in_buffer;	size_t bulk_in_size;	u8 bulk_in_epaddr;	u8 vsvideo; 	char phys[64];}; struct sur40_buffer {	struct vb2_v4l2_buffer vb;	struct list_head list;}; /* forward declarations */static const struct video_device sur40_video_device;static const struct vb2_queue sur40_queue;static void sur40_process_video(struct sur40_state *sur40);static int sur40_s_ctrl(struct v4l2_ctrl *ctrl); static const struct v4l2_ctrl_ops sur40_ctrl_ops = {	.s_ctrl = sur40_s_ctrl,}; /* * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT * here by mistake which is very likely to have corrupted the firmware EEPROM * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug. * Should you ever run into a similar problem, the background story to this * incident and instructions on how to fix the corrupted EEPROM are available * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html*/ /* command wrapper */static int sur40_command(struct sur40_state *dev,			 u8 command, u16 index, void *buffer, u16 size){	return usb_control_msg(dev->usbdev, usb_rcvctrlpipe(dev->usbdev, 0),			       command,			       USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,			       0x00, index, buffer, size, 1000);} /* poke a byte in the panel register space */static int sur40_poke(struct sur40_state *dev, u8 offset, u8 value){	int result;	u8 index = 0x96; // 0xae for permanent write 	result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),		SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,		0x32, index, NULL, 0, 1000);	if (result < 0)		goto error;	msleep(5); 	result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),		SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,		0x72, offset, NULL, 0, 1000);	if (result < 0)		goto error;	msleep(5); 	result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),		SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,		0xb2, value, NULL, 0, 1000);	if (result < 0)		goto error;	msleep(5); error:	return result;} static int sur40_set_preprocessor(struct sur40_state *dev, u8 value){	u8 setting_07[2] = { 0x01, 0x00 };	u8 setting_17[2] = { 0x85, 0x80 };	int result; 	if (value > 1)		return -ERANGE; 	result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),		SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,		0x07, setting_07[value], NULL, 0, 1000);	if (result < 0)		goto error;	msleep(5); 	result = usb_control_msg(dev->usbdev, usb_sndctrlpipe(dev->usbdev, 0),		SUR40_POKE, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,		0x17, setting_17[value], NULL, 0, 1000);	if (result < 0)		goto error;	msleep(5); error:	return result;} static void sur40_set_vsvideo(struct sur40_state *handle, u8 value){	int i; 	for (i = 0; i < 4; i++)		sur40_poke(handle, 0x1c+i, value);	handle->vsvideo = value;} static void sur40_set_irlevel(struct sur40_state *handle, u8 value){	int i; 	for (i = 0; i < 8; i++)		sur40_poke(handle, 0x08+(2*i), value);} /* Initialization routine, called from sur40_open */static int sur40_init(struct sur40_state *dev){	int result;	u8 *buffer; 	buffer = kmalloc(24, GFP_KERNEL);	if (!buffer) {		result = -ENOMEM;		goto error;	} 	/* stupidly replay the original MS driver init sequence */	result = sur40_command(dev, SUR40_GET_VERSION, 0x00, buffer, 12);	if (result < 0)		goto error; 	result = sur40_command(dev, SUR40_GET_VERSION, 0x01, buffer, 12);	if (result < 0)		goto error; 	result = sur40_command(dev, SUR40_GET_VERSION, 0x02, buffer, 12);	if (result < 0)		goto error; 	result = sur40_command(dev, SUR40_SENSOR_CAPS, 0x00, buffer, 24);	if (result < 0)		goto error; 	result = sur40_command(dev, SUR40_ACCEL_CAPS, 0x00, buffer, 5);	if (result < 0)		goto error; 	result = sur40_command(dev, SUR40_GET_VERSION, 0x03, buffer, 12);	if (result < 0)		goto error; 	result = 0; 	/*	 * Discard the result buffer - no known data inside except	 * some version strings, maybe extract these sometime...	 */error:	kfree(buffer);	return result;} /* * Callback routines from input_dev */ /* Enable the device, polling will now start. */static int sur40_open(struct input_dev *input){	struct sur40_state *sur40 = input_get_drvdata(input); 	dev_dbg(sur40->dev, "open\n");	return sur40_init(sur40);} /* Disable device, polling has stopped. */static void sur40_close(struct input_dev *input){	struct sur40_state *sur40 = input_get_drvdata(input); 	dev_dbg(sur40->dev, "close\n");	/*	 * There is no known way to stop the device, so we simply	 * stop polling.	 */} /* * This function is called when a whole contact has been processed, * so that it can assign it to a slot and store the data there. */static void sur40_report_blob(struct sur40_blob *blob, struct input_dev *input){	int wide, major, minor;	int bb_size_x, bb_size_y, pos_x, pos_y, ctr_x, ctr_y, slotnum; 	if (blob->type != SUR40_TOUCH)		return; 	slotnum = input_mt_get_slot_by_key(input, le16_to_cpu(blob->blob_id));	if (slotnum < 0 || slotnum >= MAX_CONTACTS)		return; 	bb_size_x = le16_to_cpu(blob->bb_size_x);	bb_size_y = le16_to_cpu(blob->bb_size_y); 	pos_x = le16_to_cpu(blob->pos_x);	pos_y = le16_to_cpu(blob->pos_y); 	ctr_x = le16_to_cpu(blob->ctr_x);	ctr_y = le16_to_cpu(blob->ctr_y); 	input_mt_slot(input, slotnum);	input_mt_report_slot_state(input, MT_TOOL_FINGER, 1);	wide = (bb_size_x > bb_size_y);	major = max(bb_size_x, bb_size_y);	minor = min(bb_size_x, bb_size_y); 	input_report_abs(input, ABS_MT_POSITION_X, pos_x);	input_report_abs(input, ABS_MT_POSITION_Y, pos_y);	input_report_abs(input, ABS_MT_TOOL_X, ctr_x);	input_report_abs(input, ABS_MT_TOOL_Y, ctr_y); 	/* TODO: use a better orientation measure */	input_report_abs(input, ABS_MT_ORIENTATION, wide);	input_report_abs(input, ABS_MT_TOUCH_MAJOR, major);	input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);} /* core function: poll for new input data */static void sur40_poll(struct input_dev *input){	struct sur40_state *sur40 = input_get_drvdata(input);	int result, bulk_read, need_blobs, packet_blobs, i;	struct sur40_header *header = &sur40->bulk_in_buffer->header;	struct sur40_blob *inblob = &sur40->bulk_in_buffer->blobs[0]; 	dev_dbg(sur40->dev, "poll\n"); 	need_blobs = -1; 	do { 		/* perform a blocking bulk read to get data from the device */		result = usb_bulk_msg(sur40->usbdev,			usb_rcvbulkpipe(sur40->usbdev, sur40->bulk_in_epaddr),			sur40->bulk_in_buffer, sur40->bulk_in_size,			&bulk_read, 1000); 		dev_dbg(sur40->dev, "received %d bytes\n", bulk_read); 		if (result < 0) {			dev_err(sur40->dev, "error in usb_bulk_read\n");			return;		} 		result = bulk_read - sizeof(struct sur40_header); 		if (result % sizeof(struct sur40_blob) != 0) {			dev_err(sur40->dev, "transfer size mismatch\n");			return;		} 		/* first packet? */		if (need_blobs == -1) {			need_blobs = le16_to_cpu(header->count);			dev_dbg(sur40->dev, "need %d blobs\n", need_blobs);			/* packet_id = le32_to_cpu(header->packet_id); */		} 		/*		 * Sanity check. when video data is also being retrieved, the		 * packet ID will usually increase in the middle of a series		 * instead of at the end. However, the data is still consistent,		 * so the packet ID is probably just valid for the first packet		 * in a series.

Lines 1–500 of 1191.