diff options
Diffstat (limited to 'drivers/media/usb/gspca/kinect.c')
-rw-r--r-- | drivers/media/usb/gspca/kinect.c | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/drivers/media/usb/gspca/kinect.c b/drivers/media/usb/gspca/kinect.c new file mode 100644 index 000000000000..40ad6687ee5d --- /dev/null +++ b/drivers/media/usb/gspca/kinect.c | |||
@@ -0,0 +1,408 @@ | |||
1 | /* | ||
2 | * kinect sensor device camera, gspca driver | ||
3 | * | ||
4 | * Copyright (C) 2011 Antonio Ospite <ospite@studenti.unina.it> | ||
5 | * | ||
6 | * Based on the OpenKinect project and libfreenect | ||
7 | * http://openkinect.org/wiki/Init_Analysis | ||
8 | * | ||
9 | * Special thanks to Steven Toth and kernellabs.com for sponsoring a Kinect | ||
10 | * sensor device which I tested the driver on. | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
25 | */ | ||
26 | |||
27 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
28 | |||
29 | #define MODULE_NAME "kinect" | ||
30 | |||
31 | #include "gspca.h" | ||
32 | |||
33 | #define CTRL_TIMEOUT 500 | ||
34 | |||
35 | MODULE_AUTHOR("Antonio Ospite <ospite@studenti.unina.it>"); | ||
36 | MODULE_DESCRIPTION("GSPCA/Kinect Sensor Device USB Camera Driver"); | ||
37 | MODULE_LICENSE("GPL"); | ||
38 | |||
39 | struct pkt_hdr { | ||
40 | uint8_t magic[2]; | ||
41 | uint8_t pad; | ||
42 | uint8_t flag; | ||
43 | uint8_t unk1; | ||
44 | uint8_t seq; | ||
45 | uint8_t unk2; | ||
46 | uint8_t unk3; | ||
47 | uint32_t timestamp; | ||
48 | }; | ||
49 | |||
50 | struct cam_hdr { | ||
51 | uint8_t magic[2]; | ||
52 | uint16_t len; | ||
53 | uint16_t cmd; | ||
54 | uint16_t tag; | ||
55 | }; | ||
56 | |||
57 | /* specific webcam descriptor */ | ||
58 | struct sd { | ||
59 | struct gspca_dev gspca_dev; /* !! must be the first item */ | ||
60 | uint16_t cam_tag; /* a sequence number for packets */ | ||
61 | uint8_t stream_flag; /* to identify different stream types */ | ||
62 | uint8_t obuf[0x400]; /* output buffer for control commands */ | ||
63 | uint8_t ibuf[0x200]; /* input buffer for control commands */ | ||
64 | }; | ||
65 | |||
66 | #define MODE_640x480 0x0001 | ||
67 | #define MODE_640x488 0x0002 | ||
68 | #define MODE_1280x1024 0x0004 | ||
69 | |||
70 | #define FORMAT_BAYER 0x0010 | ||
71 | #define FORMAT_UYVY 0x0020 | ||
72 | #define FORMAT_Y10B 0x0040 | ||
73 | |||
74 | #define FPS_HIGH 0x0100 | ||
75 | |||
76 | static const struct v4l2_pix_format video_camera_mode[] = { | ||
77 | {640, 480, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, | ||
78 | .bytesperline = 640, | ||
79 | .sizeimage = 640 * 480, | ||
80 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
81 | .priv = MODE_640x480 | FORMAT_BAYER | FPS_HIGH}, | ||
82 | {640, 480, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, | ||
83 | .bytesperline = 640 * 2, | ||
84 | .sizeimage = 640 * 480 * 2, | ||
85 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
86 | .priv = MODE_640x480 | FORMAT_UYVY}, | ||
87 | {1280, 1024, V4L2_PIX_FMT_SGRBG8, V4L2_FIELD_NONE, | ||
88 | .bytesperline = 1280, | ||
89 | .sizeimage = 1280 * 1024, | ||
90 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
91 | .priv = MODE_1280x1024 | FORMAT_BAYER}, | ||
92 | {640, 488, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE, | ||
93 | .bytesperline = 640 * 10 / 8, | ||
94 | .sizeimage = 640 * 488 * 10 / 8, | ||
95 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
96 | .priv = MODE_640x488 | FORMAT_Y10B | FPS_HIGH}, | ||
97 | {1280, 1024, V4L2_PIX_FMT_Y10BPACK, V4L2_FIELD_NONE, | ||
98 | .bytesperline = 1280 * 10 / 8, | ||
99 | .sizeimage = 1280 * 1024 * 10 / 8, | ||
100 | .colorspace = V4L2_COLORSPACE_SRGB, | ||
101 | .priv = MODE_1280x1024 | FORMAT_Y10B}, | ||
102 | }; | ||
103 | |||
104 | static int kinect_write(struct usb_device *udev, uint8_t *data, | ||
105 | uint16_t wLength) | ||
106 | { | ||
107 | return usb_control_msg(udev, | ||
108 | usb_sndctrlpipe(udev, 0), | ||
109 | 0x00, | ||
110 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
111 | 0, 0, data, wLength, CTRL_TIMEOUT); | ||
112 | } | ||
113 | |||
114 | static int kinect_read(struct usb_device *udev, uint8_t *data, uint16_t wLength) | ||
115 | { | ||
116 | return usb_control_msg(udev, | ||
117 | usb_rcvctrlpipe(udev, 0), | ||
118 | 0x00, | ||
119 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
120 | 0, 0, data, wLength, CTRL_TIMEOUT); | ||
121 | } | ||
122 | |||
123 | static int send_cmd(struct gspca_dev *gspca_dev, uint16_t cmd, void *cmdbuf, | ||
124 | unsigned int cmd_len, void *replybuf, unsigned int reply_len) | ||
125 | { | ||
126 | struct sd *sd = (struct sd *) gspca_dev; | ||
127 | struct usb_device *udev = gspca_dev->dev; | ||
128 | int res, actual_len; | ||
129 | uint8_t *obuf = sd->obuf; | ||
130 | uint8_t *ibuf = sd->ibuf; | ||
131 | struct cam_hdr *chdr = (void *)obuf; | ||
132 | struct cam_hdr *rhdr = (void *)ibuf; | ||
133 | |||
134 | if (cmd_len & 1 || cmd_len > (0x400 - sizeof(*chdr))) { | ||
135 | pr_err("send_cmd: Invalid command length (0x%x)\n", cmd_len); | ||
136 | return -1; | ||
137 | } | ||
138 | |||
139 | chdr->magic[0] = 0x47; | ||
140 | chdr->magic[1] = 0x4d; | ||
141 | chdr->cmd = cpu_to_le16(cmd); | ||
142 | chdr->tag = cpu_to_le16(sd->cam_tag); | ||
143 | chdr->len = cpu_to_le16(cmd_len / 2); | ||
144 | |||
145 | memcpy(obuf+sizeof(*chdr), cmdbuf, cmd_len); | ||
146 | |||
147 | res = kinect_write(udev, obuf, cmd_len + sizeof(*chdr)); | ||
148 | PDEBUG(D_USBO, "Control cmd=%04x tag=%04x len=%04x: %d", cmd, | ||
149 | sd->cam_tag, cmd_len, res); | ||
150 | if (res < 0) { | ||
151 | pr_err("send_cmd: Output control transfer failed (%d)\n", res); | ||
152 | return res; | ||
153 | } | ||
154 | |||
155 | do { | ||
156 | actual_len = kinect_read(udev, ibuf, 0x200); | ||
157 | } while (actual_len == 0); | ||
158 | PDEBUG(D_USBO, "Control reply: %d", res); | ||
159 | if (actual_len < sizeof(*rhdr)) { | ||
160 | pr_err("send_cmd: Input control transfer failed (%d)\n", res); | ||
161 | return res; | ||
162 | } | ||
163 | actual_len -= sizeof(*rhdr); | ||
164 | |||
165 | if (rhdr->magic[0] != 0x52 || rhdr->magic[1] != 0x42) { | ||
166 | pr_err("send_cmd: Bad magic %02x %02x\n", | ||
167 | rhdr->magic[0], rhdr->magic[1]); | ||
168 | return -1; | ||
169 | } | ||
170 | if (rhdr->cmd != chdr->cmd) { | ||
171 | pr_err("send_cmd: Bad cmd %02x != %02x\n", | ||
172 | rhdr->cmd, chdr->cmd); | ||
173 | return -1; | ||
174 | } | ||
175 | if (rhdr->tag != chdr->tag) { | ||
176 | pr_err("send_cmd: Bad tag %04x != %04x\n", | ||
177 | rhdr->tag, chdr->tag); | ||
178 | return -1; | ||
179 | } | ||
180 | if (cpu_to_le16(rhdr->len) != (actual_len/2)) { | ||
181 | pr_err("send_cmd: Bad len %04x != %04x\n", | ||
182 | cpu_to_le16(rhdr->len), (int)(actual_len/2)); | ||
183 | return -1; | ||
184 | } | ||
185 | |||
186 | if (actual_len > reply_len) { | ||
187 | pr_warn("send_cmd: Data buffer is %d bytes long, but got %d bytes\n", | ||
188 | reply_len, actual_len); | ||
189 | memcpy(replybuf, ibuf+sizeof(*rhdr), reply_len); | ||
190 | } else { | ||
191 | memcpy(replybuf, ibuf+sizeof(*rhdr), actual_len); | ||
192 | } | ||
193 | |||
194 | sd->cam_tag++; | ||
195 | |||
196 | return actual_len; | ||
197 | } | ||
198 | |||
199 | static int write_register(struct gspca_dev *gspca_dev, uint16_t reg, | ||
200 | uint16_t data) | ||
201 | { | ||
202 | uint16_t reply[2]; | ||
203 | uint16_t cmd[2]; | ||
204 | int res; | ||
205 | |||
206 | cmd[0] = cpu_to_le16(reg); | ||
207 | cmd[1] = cpu_to_le16(data); | ||
208 | |||
209 | PDEBUG(D_USBO, "Write Reg 0x%04x <= 0x%02x", reg, data); | ||
210 | res = send_cmd(gspca_dev, 0x03, cmd, 4, reply, 4); | ||
211 | if (res < 0) | ||
212 | return res; | ||
213 | if (res != 2) { | ||
214 | pr_warn("send_cmd returned %d [%04x %04x], 0000 expected\n", | ||
215 | res, reply[0], reply[1]); | ||
216 | } | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | /* this function is called at probe time */ | ||
221 | static int sd_config(struct gspca_dev *gspca_dev, | ||
222 | const struct usb_device_id *id) | ||
223 | { | ||
224 | struct sd *sd = (struct sd *) gspca_dev; | ||
225 | struct cam *cam; | ||
226 | |||
227 | sd->cam_tag = 0; | ||
228 | |||
229 | /* Only video stream is supported for now, | ||
230 | * which has stream flag = 0x80 */ | ||
231 | sd->stream_flag = 0x80; | ||
232 | |||
233 | cam = &gspca_dev->cam; | ||
234 | |||
235 | cam->cam_mode = video_camera_mode; | ||
236 | cam->nmodes = ARRAY_SIZE(video_camera_mode); | ||
237 | |||
238 | #if 0 | ||
239 | /* Setting those values is not needed for video stream */ | ||
240 | cam->npkt = 15; | ||
241 | gspca_dev->pkt_size = 960 * 2; | ||
242 | #endif | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | /* this function is called at probe and resume time */ | ||
248 | static int sd_init(struct gspca_dev *gspca_dev) | ||
249 | { | ||
250 | PDEBUG(D_PROBE, "Kinect Camera device."); | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static int sd_start(struct gspca_dev *gspca_dev) | ||
256 | { | ||
257 | int mode; | ||
258 | uint8_t fmt_reg, fmt_val; | ||
259 | uint8_t res_reg, res_val; | ||
260 | uint8_t fps_reg, fps_val; | ||
261 | uint8_t mode_val; | ||
262 | |||
263 | mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; | ||
264 | |||
265 | if (mode & FORMAT_Y10B) { | ||
266 | fmt_reg = 0x19; | ||
267 | res_reg = 0x1a; | ||
268 | fps_reg = 0x1b; | ||
269 | mode_val = 0x03; | ||
270 | } else { | ||
271 | fmt_reg = 0x0c; | ||
272 | res_reg = 0x0d; | ||
273 | fps_reg = 0x0e; | ||
274 | mode_val = 0x01; | ||
275 | } | ||
276 | |||
277 | /* format */ | ||
278 | if (mode & FORMAT_UYVY) | ||
279 | fmt_val = 0x05; | ||
280 | else | ||
281 | fmt_val = 0x00; | ||
282 | |||
283 | if (mode & MODE_1280x1024) | ||
284 | res_val = 0x02; | ||
285 | else | ||
286 | res_val = 0x01; | ||
287 | |||
288 | if (mode & FPS_HIGH) | ||
289 | fps_val = 0x1e; | ||
290 | else | ||
291 | fps_val = 0x0f; | ||
292 | |||
293 | |||
294 | /* turn off IR-reset function */ | ||
295 | write_register(gspca_dev, 0x105, 0x00); | ||
296 | |||
297 | /* Reset video stream */ | ||
298 | write_register(gspca_dev, 0x05, 0x00); | ||
299 | |||
300 | /* Due to some ridiculous condition in the firmware, we have to start | ||
301 | * and stop the depth stream before the camera will hand us 1280x1024 | ||
302 | * IR. This is a stupid workaround, but we've yet to find a better | ||
303 | * solution. | ||
304 | * | ||
305 | * Thanks to Drew Fisher for figuring this out. | ||
306 | */ | ||
307 | if (mode & (FORMAT_Y10B | MODE_1280x1024)) { | ||
308 | write_register(gspca_dev, 0x13, 0x01); | ||
309 | write_register(gspca_dev, 0x14, 0x1e); | ||
310 | write_register(gspca_dev, 0x06, 0x02); | ||
311 | write_register(gspca_dev, 0x06, 0x00); | ||
312 | } | ||
313 | |||
314 | write_register(gspca_dev, fmt_reg, fmt_val); | ||
315 | write_register(gspca_dev, res_reg, res_val); | ||
316 | write_register(gspca_dev, fps_reg, fps_val); | ||
317 | |||
318 | /* Start video stream */ | ||
319 | write_register(gspca_dev, 0x05, mode_val); | ||
320 | |||
321 | /* disable Hflip */ | ||
322 | write_register(gspca_dev, 0x47, 0x00); | ||
323 | |||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | static void sd_stopN(struct gspca_dev *gspca_dev) | ||
328 | { | ||
329 | /* reset video stream */ | ||
330 | write_register(gspca_dev, 0x05, 0x00); | ||
331 | } | ||
332 | |||
333 | static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *__data, int len) | ||
334 | { | ||
335 | struct sd *sd = (struct sd *) gspca_dev; | ||
336 | |||
337 | struct pkt_hdr *hdr = (void *)__data; | ||
338 | uint8_t *data = __data + sizeof(*hdr); | ||
339 | int datalen = len - sizeof(*hdr); | ||
340 | |||
341 | uint8_t sof = sd->stream_flag | 1; | ||
342 | uint8_t mof = sd->stream_flag | 2; | ||
343 | uint8_t eof = sd->stream_flag | 5; | ||
344 | |||
345 | if (len < 12) | ||
346 | return; | ||
347 | |||
348 | if (hdr->magic[0] != 'R' || hdr->magic[1] != 'B') { | ||
349 | pr_warn("[Stream %02x] Invalid magic %02x%02x\n", | ||
350 | sd->stream_flag, hdr->magic[0], hdr->magic[1]); | ||
351 | return; | ||
352 | } | ||
353 | |||
354 | if (hdr->flag == sof) | ||
355 | gspca_frame_add(gspca_dev, FIRST_PACKET, data, datalen); | ||
356 | |||
357 | else if (hdr->flag == mof) | ||
358 | gspca_frame_add(gspca_dev, INTER_PACKET, data, datalen); | ||
359 | |||
360 | else if (hdr->flag == eof) | ||
361 | gspca_frame_add(gspca_dev, LAST_PACKET, data, datalen); | ||
362 | |||
363 | else | ||
364 | pr_warn("Packet type not recognized...\n"); | ||
365 | } | ||
366 | |||
367 | /* sub-driver description */ | ||
368 | static const struct sd_desc sd_desc = { | ||
369 | .name = MODULE_NAME, | ||
370 | .config = sd_config, | ||
371 | .init = sd_init, | ||
372 | .start = sd_start, | ||
373 | .stopN = sd_stopN, | ||
374 | .pkt_scan = sd_pkt_scan, | ||
375 | /* | ||
376 | .get_streamparm = sd_get_streamparm, | ||
377 | .set_streamparm = sd_set_streamparm, | ||
378 | */ | ||
379 | }; | ||
380 | |||
381 | /* -- module initialisation -- */ | ||
382 | static const struct usb_device_id device_table[] = { | ||
383 | {USB_DEVICE(0x045e, 0x02ae)}, | ||
384 | {} | ||
385 | }; | ||
386 | |||
387 | MODULE_DEVICE_TABLE(usb, device_table); | ||
388 | |||
389 | /* -- device connect -- */ | ||
390 | static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) | ||
391 | { | ||
392 | return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), | ||
393 | THIS_MODULE); | ||
394 | } | ||
395 | |||
396 | static struct usb_driver sd_driver = { | ||
397 | .name = MODULE_NAME, | ||
398 | .id_table = device_table, | ||
399 | .probe = sd_probe, | ||
400 | .disconnect = gspca_disconnect, | ||
401 | #ifdef CONFIG_PM | ||
402 | .suspend = gspca_suspend, | ||
403 | .resume = gspca_resume, | ||
404 | .reset_resume = gspca_resume, | ||
405 | #endif | ||
406 | }; | ||
407 | |||
408 | module_usb_driver(sd_driver); | ||