1 | /* $NetBSD: pseye.c,v 1.23 2016/07/07 06:55:42 msaitoh Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2008 Jared D. McNeill <jmcneill@invisible.ca> |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
17 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
18 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
19 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
20 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
26 | * POSSIBILITY OF SUCH DAMAGE. |
27 | */ |
28 | |
29 | /* |
30 | * Sony PlayStation Eye Driver |
31 | * |
32 | * The only documentation we have for this part is based on a series |
33 | * of forum postings by Jim Paris on ps2dev.org. Many thanks for |
34 | * figuring this one out. |
35 | * |
36 | * URL: http://forums.ps2dev.org/viewtopic.php?t=9238 |
37 | */ |
38 | |
39 | #include <sys/cdefs.h> |
40 | __KERNEL_RCSID(0, "$NetBSD: pseye.c,v 1.23 2016/07/07 06:55:42 msaitoh Exp $" ); |
41 | |
42 | #include <sys/param.h> |
43 | #include <sys/systm.h> |
44 | #include <sys/device.h> |
45 | #include <sys/fcntl.h> |
46 | #include <sys/conf.h> |
47 | #include <sys/poll.h> |
48 | #include <sys/bus.h> |
49 | #include <sys/mutex.h> |
50 | #include <sys/kthread.h> |
51 | #include <sys/condvar.h> |
52 | #include <sys/module.h> |
53 | |
54 | #include <dev/usb/usb.h> |
55 | #include <dev/usb/usbdi.h> |
56 | #include <dev/usb/usbdivar.h> |
57 | #include <dev/usb/usbdi_util.h> |
58 | #include <dev/usb/usbdevs.h> |
59 | #include <dev/usb/uvideoreg.h> |
60 | |
61 | #include <dev/video_if.h> |
62 | |
63 | #define PRI_PSEYE PRI_BIO |
64 | |
65 | /* Bulk-in buffer length -- make room for payload + UVC headers */ |
66 | #define PSEYE_BULKIN_BUFLEN ((640 * 480 * 2) + 4096) |
67 | #define PSEYE_BULKIN_BLKLEN 2048 |
68 | |
69 | /* SCCB/sensor interface */ |
70 | #define PSEYE_SCCB_ADDRESS 0xf1 |
71 | #define PSEYE_SCCB_SUBADDR 0xf2 |
72 | #define PSEYE_SCCB_WRITE 0xf3 |
73 | #define PSEYE_SCCB_READ 0xf4 |
74 | #define PSEYE_SCCB_OPERATION 0xf5 |
75 | #define PSEYE_SCCB_STATUS 0xf6 |
76 | |
77 | #define PSEYE_SCCB_OP_WRITE_3 0x37 |
78 | #define PSEYE_SCCB_OP_WRITE_2 0x33 |
79 | #define PSEYE_SCCB_OP_READ_2 0xf9 |
80 | |
81 | struct pseye_softc { |
82 | device_t sc_dev; |
83 | |
84 | struct usbd_device * sc_udev; |
85 | struct usbd_interface * sc_iface; |
86 | |
87 | device_t sc_videodev; |
88 | char sc_running; |
89 | |
90 | kcondvar_t sc_cv; |
91 | kmutex_t sc_mtx; |
92 | |
93 | struct usbd_pipe * sc_bulkin_pipe; |
94 | struct usbd_xfer * sc_bulkin_xfer; |
95 | int sc_bulkin; |
96 | uint8_t *sc_bulkin_buffer; |
97 | int sc_bulkin_bufferlen; |
98 | |
99 | char sc_dying; |
100 | |
101 | char sc_businfo[32]; |
102 | }; |
103 | |
104 | static int pseye_match(device_t, cfdata_t, void *); |
105 | static void pseye_attach(device_t, device_t, void *); |
106 | static int pseye_detach(device_t, int); |
107 | static void pseye_childdet(device_t, device_t); |
108 | static int pseye_activate(device_t, enum devact); |
109 | |
110 | static void pseye_init(struct pseye_softc *); |
111 | static void pseye_sccb_init(struct pseye_softc *); |
112 | static void pseye_stop(struct pseye_softc *); |
113 | static void pseye_start(struct pseye_softc *); |
114 | static void pseye_led(struct pseye_softc *, bool); |
115 | static uint8_t pseye_getreg(struct pseye_softc *, uint16_t); |
116 | static void pseye_setreg(struct pseye_softc *, uint16_t, uint8_t); |
117 | static void pseye_setregv(struct pseye_softc *, uint16_t, uint8_t); |
118 | static void pseye_sccb_setreg(struct pseye_softc *, uint8_t, uint8_t); |
119 | static bool pseye_sccb_status(struct pseye_softc *); |
120 | |
121 | static int pseye_init_pipes(struct pseye_softc *); |
122 | static int pseye_close_pipes(struct pseye_softc *); |
123 | |
124 | static usbd_status pseye_get_frame(struct pseye_softc *, uint32_t *); |
125 | static void pseye_submit_payload(struct pseye_softc *, uint32_t); |
126 | |
127 | /* video(9) API */ |
128 | static int pseye_open(void *, int); |
129 | static void pseye_close(void *); |
130 | static const char * pseye_get_devname(void *); |
131 | static const char * pseye_get_businfo(void *); |
132 | static int pseye_enum_format(void *, uint32_t, |
133 | struct video_format *); |
134 | static int pseye_get_format(void *, struct video_format *); |
135 | static int pseye_set_format(void *, struct video_format *); |
136 | static int pseye_try_format(void *, struct video_format *); |
137 | static int pseye_start_transfer(void *); |
138 | static int pseye_stop_transfer(void *); |
139 | |
140 | CFATTACH_DECL2_NEW(pseye, sizeof(struct pseye_softc), |
141 | pseye_match, pseye_attach, pseye_detach, pseye_activate, |
142 | NULL, pseye_childdet); |
143 | |
144 | static const struct video_hw_if pseye_hw_if = { |
145 | .open = pseye_open, |
146 | .close = pseye_close, |
147 | .get_devname = pseye_get_devname, |
148 | .get_businfo = pseye_get_businfo, |
149 | .enum_format = pseye_enum_format, |
150 | .get_format = pseye_get_format, |
151 | .set_format = pseye_set_format, |
152 | .try_format = pseye_try_format, |
153 | .start_transfer = pseye_start_transfer, |
154 | .stop_transfer = pseye_stop_transfer, |
155 | .control_iter_init = NULL, |
156 | .control_iter_next = NULL, |
157 | .get_control_desc_group = NULL, |
158 | .get_control_group = NULL, |
159 | .set_control_group = NULL, |
160 | }; |
161 | |
162 | static int |
163 | pseye_match(device_t parent, cfdata_t match, void *opaque) |
164 | { |
165 | struct usbif_attach_arg *uiaa = opaque; |
166 | |
167 | if (uiaa->uiaa_class != UICLASS_VENDOR) |
168 | return UMATCH_NONE; |
169 | |
170 | if (uiaa->uiaa_vendor == USB_VENDOR_OMNIVISION2) { |
171 | switch (uiaa->uiaa_product) { |
172 | case USB_PRODUCT_OMNIVISION2_PSEYE: |
173 | if (uiaa->uiaa_ifaceno != 0) |
174 | return UMATCH_NONE; |
175 | return UMATCH_VENDOR_PRODUCT; |
176 | } |
177 | } |
178 | |
179 | return UMATCH_NONE; |
180 | } |
181 | |
182 | static void |
183 | pseye_attach(device_t parent, device_t self, void *opaque) |
184 | { |
185 | struct pseye_softc *sc = device_private(self); |
186 | struct usbif_attach_arg *uiaa = opaque; |
187 | struct usbd_device *dev = uiaa->uiaa_device; |
188 | usb_interface_descriptor_t *id = NULL; |
189 | usb_endpoint_descriptor_t *ed = NULL, *ed_bulkin = NULL; |
190 | char *devinfop; |
191 | int i; |
192 | |
193 | aprint_naive("\n" ); |
194 | aprint_normal("\n" ); |
195 | |
196 | devinfop = usbd_devinfo_alloc(dev, 0); |
197 | aprint_normal_dev(self, "%s\n" , devinfop); |
198 | usbd_devinfo_free(devinfop); |
199 | |
200 | sc->sc_dev = self; |
201 | sc->sc_udev = dev; |
202 | sc->sc_iface = uiaa->uiaa_iface; |
203 | snprintf(sc->sc_businfo, sizeof(sc->sc_businfo), "usb:%08x" , |
204 | sc->sc_udev->ud_cookie.cookie); |
205 | sc->sc_bulkin_bufferlen = PSEYE_BULKIN_BUFLEN; |
206 | |
207 | sc->sc_dying = sc->sc_running = 0; |
208 | cv_init(&sc->sc_cv, device_xname(self)); |
209 | mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_NONE); |
210 | |
211 | id = usbd_get_interface_descriptor(sc->sc_iface); |
212 | if (id == NULL) { |
213 | aprint_error_dev(self, "failed to get interface descriptor\n" ); |
214 | sc->sc_dying = 1; |
215 | return; |
216 | } |
217 | |
218 | for (i = 0; i < id->bNumEndpoints; i++) { |
219 | ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i); |
220 | if (ed == NULL) { |
221 | aprint_error_dev(self, "couldn't get ep %d\n" , i); |
222 | sc->sc_dying = 1; |
223 | return; |
224 | } |
225 | |
226 | if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && |
227 | UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) { |
228 | ed_bulkin = ed; |
229 | break; |
230 | } |
231 | } |
232 | |
233 | if (ed_bulkin == NULL) { |
234 | aprint_error_dev(self, "no bulk-in endpoint found\n" ); |
235 | sc->sc_dying = 1; |
236 | return; |
237 | } |
238 | |
239 | sc->sc_bulkin = ed_bulkin->bEndpointAddress; |
240 | |
241 | int error = pseye_init_pipes(sc); |
242 | if (error) { |
243 | aprint_error_dev(self, "couldn't open pipes\n" ); |
244 | return; |
245 | } |
246 | |
247 | error = usbd_create_xfer(sc->sc_bulkin_pipe, sc->sc_bulkin_bufferlen, |
248 | USBD_SHORT_XFER_OK, 0, &sc->sc_bulkin_xfer); |
249 | if (error) { |
250 | aprint_error_dev(self, "couldn't create transfer\n" ); |
251 | pseye_close_pipes(sc); |
252 | return; |
253 | } |
254 | |
255 | sc->sc_bulkin_buffer = usbd_get_buffer(sc->sc_bulkin_xfer); |
256 | |
257 | pseye_init(sc); |
258 | |
259 | if (!pmf_device_register(self, NULL, NULL)) |
260 | aprint_error_dev(self, "couldn't establish power handler\n" ); |
261 | |
262 | sc->sc_videodev = video_attach_mi(&pseye_hw_if, self); |
263 | if (sc->sc_videodev == NULL) { |
264 | aprint_error_dev(self, "couldn't attach video layer\n" ); |
265 | sc->sc_dying = 1; |
266 | return; |
267 | } |
268 | |
269 | usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, self); |
270 | |
271 | } |
272 | |
273 | static int |
274 | pseye_detach(device_t self, int flags) |
275 | { |
276 | struct pseye_softc *sc = device_private(self); |
277 | |
278 | sc->sc_dying = 1; |
279 | |
280 | pmf_device_deregister(self); |
281 | |
282 | if (sc->sc_videodev != NULL) { |
283 | config_detach(sc->sc_videodev, flags); |
284 | sc->sc_videodev = NULL; |
285 | } |
286 | |
287 | if (sc->sc_bulkin_pipe != NULL) { |
288 | usbd_abort_pipe(sc->sc_bulkin_pipe); |
289 | } |
290 | |
291 | if (sc->sc_bulkin_xfer != NULL) { |
292 | usbd_destroy_xfer(sc->sc_bulkin_xfer); |
293 | sc->sc_bulkin_xfer = NULL; |
294 | } |
295 | |
296 | if (sc->sc_bulkin_pipe != NULL) { |
297 | usbd_close_pipe(sc->sc_bulkin_pipe); |
298 | sc->sc_bulkin_pipe = NULL; |
299 | } |
300 | |
301 | mutex_enter(&sc->sc_mtx); |
302 | if (sc->sc_running) { |
303 | sc->sc_running = 0; |
304 | cv_wait_sig(&sc->sc_cv, &sc->sc_mtx); |
305 | } |
306 | mutex_exit(&sc->sc_mtx); |
307 | |
308 | cv_destroy(&sc->sc_cv); |
309 | mutex_destroy(&sc->sc_mtx); |
310 | |
311 | usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); |
312 | |
313 | return 0; |
314 | } |
315 | |
316 | int |
317 | pseye_activate(device_t self, enum devact act) |
318 | { |
319 | struct pseye_softc *sc = device_private(self); |
320 | |
321 | switch (act) { |
322 | case DVACT_DEACTIVATE: |
323 | sc->sc_dying = 1; |
324 | return 0; |
325 | default: |
326 | return EOPNOTSUPP; |
327 | } |
328 | } |
329 | |
330 | static void |
331 | pseye_childdet(device_t self, device_t child) |
332 | { |
333 | struct pseye_softc *sc = device_private(self); |
334 | |
335 | if (sc->sc_videodev) { |
336 | KASSERT(sc->sc_videodev == child); |
337 | sc->sc_videodev = NULL; |
338 | } |
339 | } |
340 | |
341 | /* |
342 | * Device access |
343 | */ |
344 | |
345 | static void |
346 | pseye_init(struct pseye_softc *sc) |
347 | { |
348 | pseye_sccb_init(sc); |
349 | |
350 | pseye_setregv(sc, 0xc2, 0x0c); |
351 | pseye_setregv(sc, 0x88, 0xf8); |
352 | pseye_setregv(sc, 0xc3, 0x69); |
353 | pseye_setregv(sc, 0x89, 0xff); |
354 | pseye_setregv(sc, 0x76, 0x03); |
355 | pseye_setregv(sc, 0x92, 0x01); |
356 | pseye_setregv(sc, 0x93, 0x18); |
357 | pseye_setregv(sc, 0x94, 0x10); |
358 | pseye_setregv(sc, 0x95, 0x10); |
359 | pseye_setregv(sc, 0xe2, 0x00); |
360 | pseye_setregv(sc, 0xe7, 0x3e); |
361 | |
362 | pseye_setregv(sc, 0x96, 0x00); |
363 | |
364 | pseye_setreg(sc, 0x97, 0x20); |
365 | pseye_setreg(sc, 0x97, 0x20); |
366 | pseye_setreg(sc, 0x97, 0x20); |
367 | pseye_setreg(sc, 0x97, 0x0a); |
368 | pseye_setreg(sc, 0x97, 0x3f); |
369 | pseye_setreg(sc, 0x97, 0x4a); |
370 | pseye_setreg(sc, 0x97, 0x20); |
371 | pseye_setreg(sc, 0x97, 0x15); |
372 | pseye_setreg(sc, 0x97, 0x0b); |
373 | |
374 | pseye_setregv(sc, 0x8e, 0x40); |
375 | pseye_setregv(sc, 0x1f, 0x81); |
376 | pseye_setregv(sc, 0x34, 0x05); |
377 | pseye_setregv(sc, 0xe3, 0x04); |
378 | pseye_setregv(sc, 0x88, 0x00); |
379 | pseye_setregv(sc, 0x89, 0x00); |
380 | pseye_setregv(sc, 0x76, 0x00); |
381 | pseye_setregv(sc, 0xe7, 0x2e); |
382 | pseye_setregv(sc, 0x31, 0xf9); |
383 | pseye_setregv(sc, 0x25, 0x42); |
384 | pseye_setregv(sc, 0x21, 0xf0); |
385 | |
386 | pseye_setreg(sc, 0x1c, 0x00); |
387 | pseye_setreg(sc, 0x1d, 0x40); |
388 | pseye_setreg(sc, 0x1d, 0x02); /* payload size 0x0200 * 4 == 2048 */ |
389 | pseye_setreg(sc, 0x1d, 0x00); |
390 | pseye_setreg(sc, 0x1d, 0x02); /* frame size 0x025800 * 4 == 614400 */ |
391 | pseye_setreg(sc, 0x1d, 0x58); |
392 | pseye_setreg(sc, 0x1d, 0x00); |
393 | |
394 | pseye_setreg(sc, 0x1c, 0x0a); |
395 | pseye_setreg(sc, 0x1d, 0x08); /* enable UVC header */ |
396 | pseye_setreg(sc, 0x1d, 0x0e); |
397 | |
398 | pseye_setregv(sc, 0x8d, 0x1c); |
399 | pseye_setregv(sc, 0x8e, 0x80); |
400 | pseye_setregv(sc, 0xe5, 0x04); |
401 | |
402 | pseye_sccb_setreg(sc, 0x12, 0x80); |
403 | pseye_sccb_setreg(sc, 0x11, 0x01); |
404 | pseye_sccb_setreg(sc, 0x11, 0x01); |
405 | pseye_sccb_setreg(sc, 0x11, 0x01); |
406 | pseye_sccb_setreg(sc, 0x11, 0x01); |
407 | pseye_sccb_setreg(sc, 0x11, 0x01); |
408 | pseye_sccb_setreg(sc, 0x11, 0x01); |
409 | pseye_sccb_setreg(sc, 0x11, 0x01); |
410 | pseye_sccb_setreg(sc, 0x11, 0x01); |
411 | pseye_sccb_setreg(sc, 0x11, 0x01); |
412 | pseye_sccb_setreg(sc, 0x11, 0x01); |
413 | pseye_sccb_setreg(sc, 0x11, 0x01); |
414 | |
415 | pseye_sccb_setreg(sc, 0x3d, 0x03); |
416 | pseye_sccb_setreg(sc, 0x17, 0x26); |
417 | pseye_sccb_setreg(sc, 0x18, 0xa0); |
418 | pseye_sccb_setreg(sc, 0x19, 0x07); |
419 | pseye_sccb_setreg(sc, 0x1a, 0xf0); |
420 | pseye_sccb_setreg(sc, 0x32, 0x00); |
421 | pseye_sccb_setreg(sc, 0x29, 0xa0); |
422 | pseye_sccb_setreg(sc, 0x2c, 0xf0); |
423 | pseye_sccb_setreg(sc, 0x65, 0x20); |
424 | pseye_sccb_setreg(sc, 0x11, 0x01); |
425 | pseye_sccb_setreg(sc, 0x42, 0x7f); |
426 | pseye_sccb_setreg(sc, 0x63, 0xe0); |
427 | pseye_sccb_setreg(sc, 0x64, 0xff); |
428 | pseye_sccb_setreg(sc, 0x66, 0x00); |
429 | pseye_sccb_setreg(sc, 0x13, 0xf0); |
430 | pseye_sccb_setreg(sc, 0x0d, 0x41); |
431 | pseye_sccb_setreg(sc, 0x0f, 0xc5); |
432 | pseye_sccb_setreg(sc, 0x14, 0x11); |
433 | |
434 | pseye_sccb_setreg(sc, 0x22, 0x7f); |
435 | pseye_sccb_setreg(sc, 0x23, 0x03); |
436 | pseye_sccb_setreg(sc, 0x24, 0x40); |
437 | pseye_sccb_setreg(sc, 0x25, 0x30); |
438 | pseye_sccb_setreg(sc, 0x26, 0xa1); |
439 | pseye_sccb_setreg(sc, 0x2a, 0x00); |
440 | pseye_sccb_setreg(sc, 0x2b, 0x00); |
441 | pseye_sccb_setreg(sc, 0x6b, 0xaa); |
442 | pseye_sccb_setreg(sc, 0x13, 0xff); |
443 | |
444 | pseye_sccb_setreg(sc, 0x90, 0x05); |
445 | pseye_sccb_setreg(sc, 0x91, 0x01); |
446 | pseye_sccb_setreg(sc, 0x92, 0x03); |
447 | pseye_sccb_setreg(sc, 0x93, 0x00); |
448 | pseye_sccb_setreg(sc, 0x94, 0x60); |
449 | pseye_sccb_setreg(sc, 0x95, 0x3c); |
450 | pseye_sccb_setreg(sc, 0x96, 0x24); |
451 | pseye_sccb_setreg(sc, 0x97, 0x1e); |
452 | pseye_sccb_setreg(sc, 0x98, 0x62); |
453 | pseye_sccb_setreg(sc, 0x99, 0x80); |
454 | pseye_sccb_setreg(sc, 0x9a, 0x1e); |
455 | pseye_sccb_setreg(sc, 0x9b, 0x08); |
456 | pseye_sccb_setreg(sc, 0x9c, 0x20); |
457 | pseye_sccb_setreg(sc, 0x9e, 0x81); |
458 | |
459 | pseye_sccb_setreg(sc, 0xa6, 0x04); |
460 | pseye_sccb_setreg(sc, 0x7e, 0x0c); |
461 | pseye_sccb_setreg(sc, 0x7f, 0x16); |
462 | |
463 | pseye_sccb_setreg(sc, 0x80, 0x2a); |
464 | pseye_sccb_setreg(sc, 0x81, 0x4e); |
465 | pseye_sccb_setreg(sc, 0x82, 0x61); |
466 | pseye_sccb_setreg(sc, 0x83, 0x6f); |
467 | pseye_sccb_setreg(sc, 0x84, 0x7b); |
468 | pseye_sccb_setreg(sc, 0x85, 0x86); |
469 | pseye_sccb_setreg(sc, 0x86, 0x8e); |
470 | pseye_sccb_setreg(sc, 0x87, 0x97); |
471 | pseye_sccb_setreg(sc, 0x88, 0xa4); |
472 | pseye_sccb_setreg(sc, 0x89, 0xaf); |
473 | pseye_sccb_setreg(sc, 0x8a, 0xc5); |
474 | pseye_sccb_setreg(sc, 0x8b, 0xd7); |
475 | pseye_sccb_setreg(sc, 0x8c, 0xe8); |
476 | pseye_sccb_setreg(sc, 0x8d, 0x20); |
477 | |
478 | pseye_sccb_setreg(sc, 0x0c, 0x90); |
479 | |
480 | pseye_setregv(sc, 0xc0, 0x50); |
481 | pseye_setregv(sc, 0xc1, 0x3c); |
482 | pseye_setregv(sc, 0xc2, 0x0c); |
483 | |
484 | pseye_sccb_setreg(sc, 0x2b, 0x00); |
485 | pseye_sccb_setreg(sc, 0x22, 0x7f); |
486 | pseye_sccb_setreg(sc, 0x23, 0x03); |
487 | pseye_sccb_setreg(sc, 0x11, 0x01); |
488 | pseye_sccb_setreg(sc, 0x0c, 0xd0); |
489 | pseye_sccb_setreg(sc, 0x64, 0xff); |
490 | pseye_sccb_setreg(sc, 0x0d, 0x41); |
491 | |
492 | pseye_sccb_setreg(sc, 0x14, 0x41); |
493 | pseye_sccb_setreg(sc, 0x0e, 0xcd); |
494 | pseye_sccb_setreg(sc, 0xac, 0xbf); |
495 | pseye_sccb_setreg(sc, 0x8e, 0x00); |
496 | pseye_sccb_setreg(sc, 0x0c, 0xd0); |
497 | |
498 | pseye_stop(sc); |
499 | } |
500 | |
501 | static void |
502 | pseye_sccb_init(struct pseye_softc *sc) |
503 | { |
504 | pseye_setregv(sc, 0xe7, 0x3a); |
505 | pseye_setreg(sc, PSEYE_SCCB_ADDRESS, 0x60); |
506 | pseye_setreg(sc, PSEYE_SCCB_ADDRESS, 0x60); |
507 | pseye_setreg(sc, PSEYE_SCCB_ADDRESS, 0x60); |
508 | pseye_setreg(sc, PSEYE_SCCB_ADDRESS, 0x42); |
509 | } |
510 | |
511 | static void |
512 | pseye_stop(struct pseye_softc *sc) |
513 | { |
514 | pseye_led(sc, false); |
515 | pseye_setreg(sc, 0xe0, 0x09); |
516 | } |
517 | |
518 | static void |
519 | pseye_start(struct pseye_softc *sc) |
520 | { |
521 | pseye_led(sc, true); |
522 | pseye_setreg(sc, 0xe0, 0x00); |
523 | } |
524 | |
525 | static void |
526 | pseye_led(struct pseye_softc *sc, bool enabled) |
527 | { |
528 | uint8_t val; |
529 | |
530 | val = pseye_getreg(sc, 0x21); |
531 | pseye_setreg(sc, 0x21, val | 0x80); |
532 | |
533 | val = pseye_getreg(sc, 0x23); |
534 | if (enabled == true) |
535 | val |= 0x80; |
536 | else |
537 | val &= ~0x80; |
538 | pseye_setreg(sc, 0x23, val); |
539 | } |
540 | |
541 | static uint8_t |
542 | pseye_getreg(struct pseye_softc *sc, uint16_t reg) |
543 | { |
544 | usb_device_request_t req; |
545 | usbd_status err; |
546 | uint8_t buf; |
547 | |
548 | req.bmRequestType = UT_READ_VENDOR_DEVICE; |
549 | req.bRequest = 1; |
550 | USETW(req.wValue, 0x0000); |
551 | USETW(req.wIndex, reg); |
552 | USETW(req.wLength, 1); |
553 | |
554 | err = usbd_do_request(sc->sc_udev, &req, &buf); |
555 | if (err) { |
556 | aprint_error_dev(sc->sc_dev, "couldn't read reg 0x%04x: %s\n" , |
557 | reg, usbd_errstr(err)); |
558 | return 0xff; |
559 | } |
560 | |
561 | return buf; |
562 | } |
563 | |
564 | static void |
565 | pseye_setreg(struct pseye_softc *sc, uint16_t reg, uint8_t val) |
566 | { |
567 | usb_device_request_t req; |
568 | usbd_status err; |
569 | |
570 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE; |
571 | req.bRequest = 1; |
572 | USETW(req.wValue, 0x0000); |
573 | USETW(req.wIndex, reg); |
574 | USETW(req.wLength, 1); |
575 | |
576 | err = usbd_do_request(sc->sc_udev, &req, &val); |
577 | if (err) |
578 | aprint_error_dev(sc->sc_dev, "couldn't write reg 0x%04x: %s\n" , |
579 | reg, usbd_errstr(err)); |
580 | } |
581 | |
582 | static void |
583 | pseye_setregv(struct pseye_softc *sc, uint16_t reg, uint8_t val) |
584 | { |
585 | pseye_setreg(sc, reg, val); |
586 | if (pseye_getreg(sc, reg) != val) |
587 | aprint_error_dev(sc->sc_dev, "couldn't verify reg 0x%04x\n" , |
588 | reg); |
589 | } |
590 | |
591 | static void |
592 | pseye_sccb_setreg(struct pseye_softc *sc, uint8_t reg, uint8_t val) |
593 | { |
594 | pseye_setreg(sc, PSEYE_SCCB_SUBADDR, reg); |
595 | pseye_setreg(sc, PSEYE_SCCB_WRITE, val); |
596 | pseye_setreg(sc, PSEYE_SCCB_OPERATION, PSEYE_SCCB_OP_WRITE_3); |
597 | |
598 | if (pseye_sccb_status(sc) == false) |
599 | aprint_error_dev(sc->sc_dev, "couldn't write sccb reg 0x%04x\n" , |
600 | reg); |
601 | } |
602 | |
603 | static bool |
604 | pseye_sccb_status(struct pseye_softc *sc) |
605 | { |
606 | int retry = 5; |
607 | uint8_t reg; |
608 | |
609 | while (retry-- >= 0) { |
610 | reg = pseye_getreg(sc, PSEYE_SCCB_STATUS); |
611 | if (reg == 0x00) |
612 | return true; |
613 | else if (reg == 0x04) |
614 | return false; |
615 | } |
616 | |
617 | aprint_error_dev(sc->sc_dev, "timeout reading sccb status\n" ); |
618 | return false; |
619 | } |
620 | |
621 | static usbd_status |
622 | pseye_get_frame(struct pseye_softc *sc, uint32_t *plen) |
623 | { |
624 | if (sc->sc_dying) |
625 | return USBD_IOERROR; |
626 | |
627 | return usbd_bulk_transfer(sc->sc_bulkin_xfer, sc->sc_bulkin_pipe, |
628 | USBD_SHORT_XFER_OK, 1000, sc->sc_bulkin_buffer, plen); |
629 | } |
630 | |
631 | static int |
632 | pseye_init_pipes(struct pseye_softc *sc) |
633 | { |
634 | usbd_status err; |
635 | |
636 | if (sc->sc_dying) |
637 | return EIO; |
638 | |
639 | err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin, 0, |
640 | &sc->sc_bulkin_pipe); |
641 | if (err) { |
642 | aprint_error_dev(sc->sc_dev, "couldn't open bulk-in pipe: %s\n" , |
643 | usbd_errstr(err)); |
644 | return ENOMEM; |
645 | } |
646 | |
647 | return 0; |
648 | } |
649 | |
650 | int |
651 | pseye_close_pipes(struct pseye_softc *sc) |
652 | { |
653 | if (sc->sc_bulkin_pipe != NULL) { |
654 | usbd_abort_pipe(sc->sc_bulkin_pipe); |
655 | usbd_close_pipe(sc->sc_bulkin_pipe); |
656 | sc->sc_bulkin_pipe = NULL; |
657 | } |
658 | |
659 | return 0; |
660 | } |
661 | |
662 | static void |
663 | pseye_submit_payload(struct pseye_softc *sc, uint32_t tlen) |
664 | { |
665 | struct video_payload payload; |
666 | uvideo_payload_header_t *uvchdr; |
667 | uint8_t *buf = sc->sc_bulkin_buffer; |
668 | uint32_t len; |
669 | uint32_t brem = (640*480*2); |
670 | |
671 | while (brem > 0 && tlen > 0) { |
672 | len = min(tlen, PSEYE_BULKIN_BLKLEN); |
673 | if (len < UVIDEO_PAYLOAD_HEADER_SIZE) { |
674 | printf("pseye_submit_payload: len=%u\n" , len); |
675 | return; |
676 | } |
677 | |
678 | uvchdr = (uvideo_payload_header_t *)buf; |
679 | if (uvchdr->bHeaderLength != UVIDEO_PAYLOAD_HEADER_SIZE) |
680 | goto next; |
681 | if (uvchdr->bHeaderLength == len && |
682 | !(uvchdr->bmHeaderInfo & UV_END_OF_FRAME)) |
683 | goto next; |
684 | if (uvchdr->bmHeaderInfo & UV_ERROR) |
685 | return; |
686 | if ((uvchdr->bmHeaderInfo & UV_PRES_TIME) == 0) |
687 | goto next; |
688 | |
689 | payload.data = buf + uvchdr->bHeaderLength; |
690 | payload.size = min(brem, len - uvchdr->bHeaderLength); |
691 | payload.frameno = UGETDW(&buf[2]); |
692 | payload.end_of_frame = uvchdr->bmHeaderInfo & UV_END_OF_FRAME; |
693 | video_submit_payload(sc->sc_videodev, &payload); |
694 | |
695 | next: |
696 | tlen -= len; |
697 | buf += len; |
698 | brem -= payload.size; |
699 | } |
700 | } |
701 | |
702 | static void |
703 | pseye_transfer_thread(void *opaque) |
704 | { |
705 | struct pseye_softc *sc = opaque; |
706 | uint32_t len; |
707 | int error; |
708 | |
709 | while (sc->sc_running) { |
710 | len = sc->sc_bulkin_bufferlen; |
711 | error = pseye_get_frame(sc, &len); |
712 | if (error == USBD_NORMAL_COMPLETION) |
713 | pseye_submit_payload(sc, len); |
714 | } |
715 | |
716 | mutex_enter(&sc->sc_mtx); |
717 | cv_broadcast(&sc->sc_cv); |
718 | mutex_exit(&sc->sc_mtx); |
719 | |
720 | kthread_exit(0); |
721 | } |
722 | |
723 | /* video(9) API implementations */ |
724 | static int |
725 | pseye_open(void *opaque, int flags) |
726 | { |
727 | struct pseye_softc *sc = opaque; |
728 | |
729 | if (sc->sc_dying) |
730 | return EIO; |
731 | |
732 | pseye_start(sc); |
733 | |
734 | return 0; |
735 | } |
736 | |
737 | static void |
738 | pseye_close(void *opaque) |
739 | { |
740 | struct pseye_softc *sc = opaque; |
741 | |
742 | pseye_stop(sc); |
743 | } |
744 | |
745 | static const char * |
746 | pseye_get_devname(void *opaque) |
747 | { |
748 | return "PlayStation Eye" ; |
749 | } |
750 | |
751 | static const char * |
752 | pseye_get_businfo(void *opaque) |
753 | { |
754 | struct pseye_softc *sc = opaque; |
755 | |
756 | return sc->sc_businfo; |
757 | } |
758 | |
759 | static int |
760 | pseye_enum_format(void *opaque, uint32_t index, struct video_format *format) |
761 | { |
762 | if (index != 0) |
763 | return EINVAL; |
764 | return pseye_get_format(opaque, format); |
765 | } |
766 | |
767 | static int |
768 | pseye_get_format(void *opaque, struct video_format *format) |
769 | { |
770 | format->pixel_format = VIDEO_FORMAT_YUY2; /* XXX actually YUYV */ |
771 | format->width = 640; |
772 | format->height = 480; |
773 | format->aspect_x = 4; |
774 | format->aspect_y = 3; |
775 | format->sample_size = format->width * format->height * 2; |
776 | format->stride = format->width * 2; |
777 | format->color.primaries = VIDEO_COLOR_PRIMARIES_UNSPECIFIED; |
778 | format->color.gamma_function = VIDEO_GAMMA_FUNCTION_UNSPECIFIED; |
779 | format->color.matrix_coeff = VIDEO_MATRIX_COEFF_UNSPECIFIED; |
780 | format->interlace_flags = VIDEO_INTERLACE_ON; |
781 | format->priv = 0; |
782 | |
783 | return 0; |
784 | } |
785 | |
786 | static int |
787 | pseye_set_format(void *opaque, struct video_format *format) |
788 | { |
789 | #if notyet |
790 | if (format->pixel_format != VIDEO_FORMAT_YUYV) |
791 | return EINVAL; |
792 | if (format->width != 640 || format->height != 480) |
793 | return EINVAL; |
794 | #endif |
795 | /* XXX */ |
796 | return pseye_get_format(opaque, format); |
797 | } |
798 | |
799 | static int |
800 | pseye_try_format(void *opaque, struct video_format *format) |
801 | { |
802 | return pseye_get_format(opaque, format); |
803 | } |
804 | |
805 | static int |
806 | pseye_start_transfer(void *opaque) |
807 | { |
808 | struct pseye_softc *sc = opaque; |
809 | int err = 0; |
810 | |
811 | mutex_enter(&sc->sc_mtx); |
812 | if (sc->sc_running == 0) { |
813 | sc->sc_running = 1; |
814 | err = kthread_create(PRI_PSEYE, 0, NULL, pseye_transfer_thread, |
815 | opaque, NULL, "%s" , device_xname(sc->sc_dev)); |
816 | } else |
817 | aprint_error_dev(sc->sc_dev, "transfer already in progress\n" ); |
818 | mutex_exit(&sc->sc_mtx); |
819 | |
820 | return err; |
821 | } |
822 | |
823 | static int |
824 | pseye_stop_transfer(void *opaque) |
825 | { |
826 | struct pseye_softc *sc = opaque; |
827 | |
828 | mutex_enter(&sc->sc_mtx); |
829 | if (sc->sc_running) { |
830 | sc->sc_running = 0; |
831 | cv_wait_sig(&sc->sc_cv, &sc->sc_mtx); |
832 | } |
833 | mutex_exit(&sc->sc_mtx); |
834 | |
835 | return 0; |
836 | } |
837 | |
838 | MODULE(MODULE_CLASS_DRIVER, pseye, NULL); |
839 | |
840 | #ifdef _MODULE |
841 | #include "ioconf.c" |
842 | #endif |
843 | |
844 | static int |
845 | pseye_modcmd(modcmd_t cmd, void *opaque) |
846 | { |
847 | switch (cmd) { |
848 | case MODULE_CMD_INIT: |
849 | #ifdef _MODULE |
850 | return config_init_component(cfdriver_ioconf_pseye, |
851 | cfattach_ioconf_pseye, cfdata_ioconf_pseye); |
852 | #else |
853 | return 0; |
854 | #endif |
855 | case MODULE_CMD_FINI: |
856 | #ifdef _MODULE |
857 | return config_fini_component(cfdriver_ioconf_pseye, |
858 | cfattach_ioconf_pseye, cfdata_ioconf_pseye); |
859 | #else |
860 | return 0; |
861 | #endif |
862 | default: |
863 | return ENOTTY; |
864 | } |
865 | } |
866 | |