162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * kinect sensor device camera, gspca driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2011  Antonio Ospite <ospite@studenti.unina.it>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Based on the OpenKinect project and libfreenect
862306a36Sopenharmony_ci * http://openkinect.org/wiki/Init_Analysis
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Special thanks to Steven Toth and kernellabs.com for sponsoring a Kinect
1162306a36Sopenharmony_ci * sensor device which I tested the driver on.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define MODULE_NAME "kinect"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "gspca.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define CTRL_TIMEOUT 500
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciMODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>");
2362306a36Sopenharmony_ciMODULE_DESCRIPTION("GSPCA/Kinect Sensor Device USB Camera Driver");
2462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic bool depth_mode;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct pkt_hdr {
2962306a36Sopenharmony_ci	uint8_t magic[2];
3062306a36Sopenharmony_ci	uint8_t pad;
3162306a36Sopenharmony_ci	uint8_t flag;
3262306a36Sopenharmony_ci	uint8_t unk1;
3362306a36Sopenharmony_ci	uint8_t seq;
3462306a36Sopenharmony_ci	uint8_t unk2;
3562306a36Sopenharmony_ci	uint8_t unk3;
3662306a36Sopenharmony_ci	uint32_t timestamp;
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistruct cam_hdr {
4062306a36Sopenharmony_ci	uint8_t magic[2];
4162306a36Sopenharmony_ci	__le16 len;
4262306a36Sopenharmony_ci	__le16 cmd;
4362306a36Sopenharmony_ci	__le16 tag;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* specific webcam descriptor */
4762306a36Sopenharmony_cistruct sd {
4862306a36Sopenharmony_ci	struct gspca_dev gspca_dev; /* !! must be the first item */
4962306a36Sopenharmony_ci	uint16_t cam_tag;           /* a sequence number for packets */
5062306a36Sopenharmony_ci	uint8_t stream_flag;        /* to identify different stream types */
5162306a36Sopenharmony_ci	uint8_t obuf[0x400];        /* output buffer for control commands */
5262306a36Sopenharmony_ci	uint8_t ibuf[0x200];        /* input buffer for control commands */
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define MODE_640x480   0x0001
5662306a36Sopenharmony_ci#define MODE_640x488   0x0002
5762306a36Sopenharmony_ci#define MODE_1280x1024 0x0004
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define FORMAT_BAYER   0x0010
6062306a36Sopenharmony_ci#define FORMAT_UYVY    0x0020
6162306a36Sopenharmony_ci#define FORMAT_Y10B    0x0040
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define FPS_HIGH       0x0100
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic const struct v4l2_pix_format depth_camera_mode[] = {
6662306a36Sopenharmony_ci	{640, 480, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE,
6762306a36Sopenharmony_ci	 .bytesperline = 640 * 10 / 8,
6862306a36Sopenharmony_ci	 .sizeimage =  640 * 480 * 10 / 8,
6962306a36Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SRGB,
7062306a36Sopenharmony_ci	 .priv = MODE_640x488 | FORMAT_Y10B},
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic const struct v4l2_pix_format video_camera_mode[] = {
7462306a36Sopenharmony_ci	{640, 480, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
7562306a36Sopenharmony_ci	 .bytesperline = 640,
7662306a36Sopenharmony_ci	 .sizeimage = 640 * 480,
7762306a36Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SRGB,
7862306a36Sopenharmony_ci	 .priv = MODE_640x480 | FORMAT_BAYER | FPS_HIGH},
7962306a36Sopenharmony_ci	{640, 480, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
8062306a36Sopenharmony_ci	 .bytesperline = 640 * 2,
8162306a36Sopenharmony_ci	 .sizeimage = 640 * 480 * 2,
8262306a36Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SRGB,
8362306a36Sopenharmony_ci	 .priv = MODE_640x480 | FORMAT_UYVY},
8462306a36Sopenharmony_ci	{1280, 1024, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE,
8562306a36Sopenharmony_ci	 .bytesperline = 1280,
8662306a36Sopenharmony_ci	 .sizeimage = 1280 * 1024,
8762306a36Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SRGB,
8862306a36Sopenharmony_ci	 .priv = MODE_1280x1024 | FORMAT_BAYER},
8962306a36Sopenharmony_ci	{640, 488, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE,
9062306a36Sopenharmony_ci	 .bytesperline = 640 * 10 / 8,
9162306a36Sopenharmony_ci	 .sizeimage =  640 * 488 * 10 / 8,
9262306a36Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SRGB,
9362306a36Sopenharmony_ci	 .priv = MODE_640x488 | FORMAT_Y10B | FPS_HIGH},
9462306a36Sopenharmony_ci	{1280, 1024, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE,
9562306a36Sopenharmony_ci	 .bytesperline = 1280 * 10 / 8,
9662306a36Sopenharmony_ci	 .sizeimage =  1280 * 1024 * 10 / 8,
9762306a36Sopenharmony_ci	 .colorspace = V4L2_COLORSPACE_SRGB,
9862306a36Sopenharmony_ci	 .priv = MODE_1280x1024 | FORMAT_Y10B},
9962306a36Sopenharmony_ci};
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic int kinect_write(struct usb_device *udev, uint8_t *data,
10262306a36Sopenharmony_ci			uint16_t wLength)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	return usb_control_msg(udev,
10562306a36Sopenharmony_ci			      usb_sndctrlpipe(udev, 0),
10662306a36Sopenharmony_ci			      0x00,
10762306a36Sopenharmony_ci			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
10862306a36Sopenharmony_ci			      0, 0, data, wLength, CTRL_TIMEOUT);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic int kinect_read(struct usb_device *udev, uint8_t *data, uint16_t wLength)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	return usb_control_msg(udev,
11462306a36Sopenharmony_ci			      usb_rcvctrlpipe(udev, 0),
11562306a36Sopenharmony_ci			      0x00,
11662306a36Sopenharmony_ci			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
11762306a36Sopenharmony_ci			      0, 0, data, wLength, CTRL_TIMEOUT);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf,
12162306a36Sopenharmony_ci		unsigned int cmd_len, void *replybuf, unsigned int reply_len)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
12462306a36Sopenharmony_ci	struct usb_device *udev = gspca_dev->dev;
12562306a36Sopenharmony_ci	int res, actual_len;
12662306a36Sopenharmony_ci	uint8_t *obuf = sd->obuf;
12762306a36Sopenharmony_ci	uint8_t *ibuf = sd->ibuf;
12862306a36Sopenharmony_ci	struct cam_hdr *chdr = (void *)obuf;
12962306a36Sopenharmony_ci	struct cam_hdr *rhdr = (void *)ibuf;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (cmd_len & 1 || cmd_len > (0x400 - sizeof(*chdr))) {
13262306a36Sopenharmony_ci		pr_err("send_cmd: Invalid command length (0x%x)\n", cmd_len);
13362306a36Sopenharmony_ci		return -1;
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	chdr->magic[0] = 0x47;
13762306a36Sopenharmony_ci	chdr->magic[1] = 0x4d;
13862306a36Sopenharmony_ci	chdr->cmd = cpu_to_le16(cmd);
13962306a36Sopenharmony_ci	chdr->tag = cpu_to_le16(sd->cam_tag);
14062306a36Sopenharmony_ci	chdr->len = cpu_to_le16(cmd_len / 2);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	memcpy(obuf+sizeof(*chdr), cmdbuf, cmd_len);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	res = kinect_write(udev, obuf, cmd_len + sizeof(*chdr));
14562306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_USBO, "Control cmd=%04x tag=%04x len=%04x: %d\n",
14662306a36Sopenharmony_ci		  cmd,
14762306a36Sopenharmony_ci		  sd->cam_tag, cmd_len, res);
14862306a36Sopenharmony_ci	if (res < 0) {
14962306a36Sopenharmony_ci		pr_err("send_cmd: Output control transfer failed (%d)\n", res);
15062306a36Sopenharmony_ci		return res;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	do {
15462306a36Sopenharmony_ci		actual_len = kinect_read(udev, ibuf, 0x200);
15562306a36Sopenharmony_ci	} while (actual_len == 0);
15662306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_USBO, "Control reply: %d\n", actual_len);
15762306a36Sopenharmony_ci	if (actual_len < (int)sizeof(*rhdr)) {
15862306a36Sopenharmony_ci		pr_err("send_cmd: Input control transfer failed (%d)\n",
15962306a36Sopenharmony_ci		       actual_len);
16062306a36Sopenharmony_ci		return actual_len < 0 ? actual_len : -EREMOTEIO;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci	actual_len -= sizeof(*rhdr);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (rhdr->magic[0] != 0x52 || rhdr->magic[1] != 0x42) {
16562306a36Sopenharmony_ci		pr_err("send_cmd: Bad magic %02x %02x\n",
16662306a36Sopenharmony_ci		       rhdr->magic[0], rhdr->magic[1]);
16762306a36Sopenharmony_ci		return -1;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci	if (rhdr->cmd != chdr->cmd) {
17062306a36Sopenharmony_ci		pr_err("send_cmd: Bad cmd %02x != %02x\n",
17162306a36Sopenharmony_ci		       rhdr->cmd, chdr->cmd);
17262306a36Sopenharmony_ci		return -1;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci	if (rhdr->tag != chdr->tag) {
17562306a36Sopenharmony_ci		pr_err("send_cmd: Bad tag %04x != %04x\n",
17662306a36Sopenharmony_ci		       rhdr->tag, chdr->tag);
17762306a36Sopenharmony_ci		return -1;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci	if (le16_to_cpu(rhdr->len) != (actual_len/2)) {
18062306a36Sopenharmony_ci		pr_err("send_cmd: Bad len %04x != %04x\n",
18162306a36Sopenharmony_ci		       le16_to_cpu(rhdr->len), (int)(actual_len/2));
18262306a36Sopenharmony_ci		return -1;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (actual_len > reply_len) {
18662306a36Sopenharmony_ci		pr_warn("send_cmd: Data buffer is %d bytes long, but got %d bytes\n",
18762306a36Sopenharmony_ci			reply_len, actual_len);
18862306a36Sopenharmony_ci		memcpy(replybuf, ibuf+sizeof(*rhdr), reply_len);
18962306a36Sopenharmony_ci	} else {
19062306a36Sopenharmony_ci		memcpy(replybuf, ibuf+sizeof(*rhdr), actual_len);
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	sd->cam_tag++;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return actual_len;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic int write_register(struct gspca_dev *gspca_dev, uint16_t reg,
19962306a36Sopenharmony_ci			uint16_t data)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	uint16_t reply[2];
20262306a36Sopenharmony_ci	__le16 cmd[2];
20362306a36Sopenharmony_ci	int res;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	cmd[0] = cpu_to_le16(reg);
20662306a36Sopenharmony_ci	cmd[1] = cpu_to_le16(data);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_USBO, "Write Reg 0x%04x <= 0x%02x\n", reg, data);
20962306a36Sopenharmony_ci	res = send_cmd(gspca_dev, 0x03, cmd, 4, reply, 4);
21062306a36Sopenharmony_ci	if (res < 0)
21162306a36Sopenharmony_ci		return res;
21262306a36Sopenharmony_ci	if (res != 2) {
21362306a36Sopenharmony_ci		pr_warn("send_cmd returned %d [%04x %04x], 0000 expected\n",
21462306a36Sopenharmony_ci			res, reply[0], reply[1]);
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci	return 0;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci/* this function is called at probe time */
22062306a36Sopenharmony_cistatic int sd_config_video(struct gspca_dev *gspca_dev,
22162306a36Sopenharmony_ci		     const struct usb_device_id *id)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
22462306a36Sopenharmony_ci	struct cam *cam;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	sd->cam_tag = 0;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	sd->stream_flag = 0x80;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	cam = &gspca_dev->cam;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	cam->cam_mode = video_camera_mode;
23362306a36Sopenharmony_ci	cam->nmodes = ARRAY_SIZE(video_camera_mode);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	gspca_dev->xfer_ep = 0x81;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci#if 0
23862306a36Sopenharmony_ci	/* Setting those values is not needed for video stream */
23962306a36Sopenharmony_ci	cam->npkt = 15;
24062306a36Sopenharmony_ci	gspca_dev->pkt_size = 960 * 2;
24162306a36Sopenharmony_ci#endif
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return 0;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic int sd_config_depth(struct gspca_dev *gspca_dev,
24762306a36Sopenharmony_ci		     const struct usb_device_id *id)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
25062306a36Sopenharmony_ci	struct cam *cam;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	sd->cam_tag = 0;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	sd->stream_flag = 0x70;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	cam = &gspca_dev->cam;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	cam->cam_mode = depth_camera_mode;
25962306a36Sopenharmony_ci	cam->nmodes = ARRAY_SIZE(depth_camera_mode);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	gspca_dev->xfer_ep = 0x82;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	return 0;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/* this function is called at probe and resume time */
26762306a36Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "Kinect Camera device.\n");
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	return 0;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic int sd_start_video(struct gspca_dev *gspca_dev)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	int mode;
27762306a36Sopenharmony_ci	uint8_t fmt_reg, fmt_val;
27862306a36Sopenharmony_ci	uint8_t res_reg, res_val;
27962306a36Sopenharmony_ci	uint8_t fps_reg, fps_val;
28062306a36Sopenharmony_ci	uint8_t mode_val;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (mode & FORMAT_Y10B) {
28562306a36Sopenharmony_ci		fmt_reg = 0x19;
28662306a36Sopenharmony_ci		res_reg = 0x1a;
28762306a36Sopenharmony_ci		fps_reg = 0x1b;
28862306a36Sopenharmony_ci		mode_val = 0x03;
28962306a36Sopenharmony_ci	} else {
29062306a36Sopenharmony_ci		fmt_reg = 0x0c;
29162306a36Sopenharmony_ci		res_reg = 0x0d;
29262306a36Sopenharmony_ci		fps_reg = 0x0e;
29362306a36Sopenharmony_ci		mode_val = 0x01;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/* format */
29762306a36Sopenharmony_ci	if (mode & FORMAT_UYVY)
29862306a36Sopenharmony_ci		fmt_val = 0x05;
29962306a36Sopenharmony_ci	else
30062306a36Sopenharmony_ci		fmt_val = 0x00;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (mode & MODE_1280x1024)
30362306a36Sopenharmony_ci		res_val = 0x02;
30462306a36Sopenharmony_ci	else
30562306a36Sopenharmony_ci		res_val = 0x01;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (mode & FPS_HIGH)
30862306a36Sopenharmony_ci		fps_val = 0x1e;
30962306a36Sopenharmony_ci	else
31062306a36Sopenharmony_ci		fps_val = 0x0f;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	/* turn off IR-reset function */
31462306a36Sopenharmony_ci	write_register(gspca_dev, 0x105, 0x00);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* Reset video stream */
31762306a36Sopenharmony_ci	write_register(gspca_dev, 0x05, 0x00);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	/* Due to some ridiculous condition in the firmware, we have to start
32062306a36Sopenharmony_ci	 * and stop the depth stream before the camera will hand us 1280x1024
32162306a36Sopenharmony_ci	 * IR.  This is a stupid workaround, but we've yet to find a better
32262306a36Sopenharmony_ci	 * solution.
32362306a36Sopenharmony_ci	 *
32462306a36Sopenharmony_ci	 * Thanks to Drew Fisher for figuring this out.
32562306a36Sopenharmony_ci	 */
32662306a36Sopenharmony_ci	if (mode & (FORMAT_Y10B | MODE_1280x1024)) {
32762306a36Sopenharmony_ci		write_register(gspca_dev, 0x13, 0x01);
32862306a36Sopenharmony_ci		write_register(gspca_dev, 0x14, 0x1e);
32962306a36Sopenharmony_ci		write_register(gspca_dev, 0x06, 0x02);
33062306a36Sopenharmony_ci		write_register(gspca_dev, 0x06, 0x00);
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	write_register(gspca_dev, fmt_reg, fmt_val);
33462306a36Sopenharmony_ci	write_register(gspca_dev, res_reg, res_val);
33562306a36Sopenharmony_ci	write_register(gspca_dev, fps_reg, fps_val);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Start video stream */
33862306a36Sopenharmony_ci	write_register(gspca_dev, 0x05, mode_val);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/* disable Hflip */
34162306a36Sopenharmony_ci	write_register(gspca_dev, 0x47, 0x00);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return 0;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic int sd_start_depth(struct gspca_dev *gspca_dev)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	/* turn off IR-reset function */
34962306a36Sopenharmony_ci	write_register(gspca_dev, 0x105, 0x00);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/* reset depth stream */
35262306a36Sopenharmony_ci	write_register(gspca_dev, 0x06, 0x00);
35362306a36Sopenharmony_ci	/* Depth Stream Format 0x03: 11 bit stream | 0x02: 10 bit */
35462306a36Sopenharmony_ci	write_register(gspca_dev, 0x12, 0x02);
35562306a36Sopenharmony_ci	/* Depth Stream Resolution 1: standard (640x480) */
35662306a36Sopenharmony_ci	write_register(gspca_dev, 0x13, 0x01);
35762306a36Sopenharmony_ci	/* Depth Framerate / 0x1e (30): 30 fps */
35862306a36Sopenharmony_ci	write_register(gspca_dev, 0x14, 0x1e);
35962306a36Sopenharmony_ci	/* Depth Stream Control  / 2: Open Depth Stream */
36062306a36Sopenharmony_ci	write_register(gspca_dev, 0x06, 0x02);
36162306a36Sopenharmony_ci	/* disable depth hflip / LSB = 0: Smoothing Disabled */
36262306a36Sopenharmony_ci	write_register(gspca_dev, 0x17, 0x00);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	return 0;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic void sd_stopN_video(struct gspca_dev *gspca_dev)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	/* reset video stream */
37062306a36Sopenharmony_ci	write_register(gspca_dev, 0x05, 0x00);
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic void sd_stopN_depth(struct gspca_dev *gspca_dev)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	/* reset depth stream */
37662306a36Sopenharmony_ci	write_register(gspca_dev, 0x06, 0x00);
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	struct pkt_hdr *hdr = (void *)__data;
38462306a36Sopenharmony_ci	uint8_t *data = __data + sizeof(*hdr);
38562306a36Sopenharmony_ci	int datalen = len - sizeof(*hdr);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	uint8_t sof = sd->stream_flag | 1;
38862306a36Sopenharmony_ci	uint8_t mof = sd->stream_flag | 2;
38962306a36Sopenharmony_ci	uint8_t eof = sd->stream_flag | 5;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (len < 12)
39262306a36Sopenharmony_ci		return;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (hdr->magic[0] != 'R' || hdr->magic[1] != 'B') {
39562306a36Sopenharmony_ci		pr_warn("[Stream %02x] Invalid magic %02x%02x\n",
39662306a36Sopenharmony_ci			sd->stream_flag, hdr->magic[0], hdr->magic[1]);
39762306a36Sopenharmony_ci		return;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	if (hdr->flag == sof)
40162306a36Sopenharmony_ci		gspca_frame_add(gspca_dev, FIRST_PACKET, data, datalen);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	else if (hdr->flag == mof)
40462306a36Sopenharmony_ci		gspca_frame_add(gspca_dev, INTER_PACKET, data, datalen);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	else if (hdr->flag == eof)
40762306a36Sopenharmony_ci		gspca_frame_add(gspca_dev, LAST_PACKET, data, datalen);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	else
41062306a36Sopenharmony_ci		pr_warn("Packet type not recognized...\n");
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci/* sub-driver description */
41462306a36Sopenharmony_cistatic const struct sd_desc sd_desc_video = {
41562306a36Sopenharmony_ci	.name      = MODULE_NAME,
41662306a36Sopenharmony_ci	.config    = sd_config_video,
41762306a36Sopenharmony_ci	.init      = sd_init,
41862306a36Sopenharmony_ci	.start     = sd_start_video,
41962306a36Sopenharmony_ci	.stopN     = sd_stopN_video,
42062306a36Sopenharmony_ci	.pkt_scan  = sd_pkt_scan,
42162306a36Sopenharmony_ci	/*
42262306a36Sopenharmony_ci	.get_streamparm = sd_get_streamparm,
42362306a36Sopenharmony_ci	.set_streamparm = sd_set_streamparm,
42462306a36Sopenharmony_ci	*/
42562306a36Sopenharmony_ci};
42662306a36Sopenharmony_cistatic const struct sd_desc sd_desc_depth = {
42762306a36Sopenharmony_ci	.name      = MODULE_NAME,
42862306a36Sopenharmony_ci	.config    = sd_config_depth,
42962306a36Sopenharmony_ci	.init      = sd_init,
43062306a36Sopenharmony_ci	.start     = sd_start_depth,
43162306a36Sopenharmony_ci	.stopN     = sd_stopN_depth,
43262306a36Sopenharmony_ci	.pkt_scan  = sd_pkt_scan,
43362306a36Sopenharmony_ci	/*
43462306a36Sopenharmony_ci	.get_streamparm = sd_get_streamparm,
43562306a36Sopenharmony_ci	.set_streamparm = sd_set_streamparm,
43662306a36Sopenharmony_ci	*/
43762306a36Sopenharmony_ci};
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci/* -- module initialisation -- */
44062306a36Sopenharmony_cistatic const struct usb_device_id device_table[] = {
44162306a36Sopenharmony_ci	{USB_DEVICE(0x045e, 0x02ae)},
44262306a36Sopenharmony_ci	{USB_DEVICE(0x045e, 0x02bf)},
44362306a36Sopenharmony_ci	{}
44462306a36Sopenharmony_ci};
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci/* -- device connect -- */
44962306a36Sopenharmony_cistatic int sd_probe(struct usb_interface *intf, const struct usb_device_id *id)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	if (depth_mode)
45262306a36Sopenharmony_ci		return gspca_dev_probe(intf, id, &sd_desc_depth,
45362306a36Sopenharmony_ci				       sizeof(struct sd), THIS_MODULE);
45462306a36Sopenharmony_ci	else
45562306a36Sopenharmony_ci		return gspca_dev_probe(intf, id, &sd_desc_video,
45662306a36Sopenharmony_ci				       sizeof(struct sd), THIS_MODULE);
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic struct usb_driver sd_driver = {
46062306a36Sopenharmony_ci	.name       = MODULE_NAME,
46162306a36Sopenharmony_ci	.id_table   = device_table,
46262306a36Sopenharmony_ci	.probe      = sd_probe,
46362306a36Sopenharmony_ci	.disconnect = gspca_disconnect,
46462306a36Sopenharmony_ci#ifdef CONFIG_PM
46562306a36Sopenharmony_ci	.suspend    = gspca_suspend,
46662306a36Sopenharmony_ci	.resume     = gspca_resume,
46762306a36Sopenharmony_ci	.reset_resume = gspca_resume,
46862306a36Sopenharmony_ci#endif
46962306a36Sopenharmony_ci};
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_cimodule_usb_driver(sd_driver);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_cimodule_param(depth_mode, bool, 0644);
47462306a36Sopenharmony_ciMODULE_PARM_DESC(depth_mode, "0=video 1=depth");
475