xref: /freebsd/sys/dev/firewire/fwcam.c (revision 40e05479718460820cd24c22e59519a4f13eba3d)
1 /*
2  * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  */
6 
7 /*
8  * fwcam(4) - IIDC 1394-based Digital Camera driver
9  *
10  * Implements the IIDC v1.30 specification (TA Document 1999023) for
11  * FireWire digital cameras.
12  */
13 
14 #include <sys/param.h>
15 #include <sys/systm.h>
16 #include <sys/module.h>
17 #include <sys/bus.h>
18 #include <sys/kernel.h>
19 #include <sys/conf.h>
20 #include <sys/malloc.h>
21 #include <sys/lock.h>
22 #include <sys/mutex.h>
23 #include <sys/sysctl.h>
24 #include <sys/taskqueue.h>
25 #include <sys/fcntl.h>
26 #include <sys/poll.h>
27 #include <sys/selinfo.h>
28 #include <sys/uio.h>
29 #include <sys/mbuf.h>
30 
31 #include <dev/firewire/firewire.h>
32 #include <dev/firewire/firewirereg.h>
33 #include <dev/firewire/iec13213.h>
34 #include <dev/firewire/fwcam.h>
35 #include <dev/firewire/fw_helpers.h>
36 
37 static MALLOC_DEFINE(M_FWCAM, "fwcam", "IIDC FireWire Camera");
38 
39 static int debug = 0;
40 static int iso_channel = 0;
41 SYSCTL_DECL(_hw_firewire);
42 static SYSCTL_NODE(_hw_firewire, OID_AUTO, fwcam, CTLFLAG_RD | CTLFLAG_MPSAFE,
43     0, "IIDC Camera");
44 SYSCTL_INT(_hw_firewire_fwcam, OID_AUTO, debug, CTLFLAG_RWTUN, &debug, 0,
45     "fwcam debug level");
46 SYSCTL_INT(_hw_firewire_fwcam, OID_AUTO, iso_channel, CTLFLAG_RWTUN,
47     &iso_channel, 0, "ISO channel for isochronous receive (default 0)");
48 
49 #define FWCAM_DEBUG(lev, fmt, ...)					\
50 	do {								\
51 		if (debug >= (lev))					\
52 			printf("fwcam: " fmt, ## __VA_ARGS__);		\
53 	} while (0)
54 
55 static void	fwcam_identify(driver_t *, device_t);
56 static int	fwcam_probe(device_t);
57 static int	fwcam_attach(device_t);
58 static int	fwcam_detach(device_t);
59 static void	fwcam_post_busreset(void *);
60 static void	fwcam_probe_task(void *, int);
61 static void	fwcam_post_explore(void *);
62 static int	fwcam_iso_start(struct fwcam_softc *);
63 static void	fwcam_iso_stop(struct fwcam_softc *);
64 static void	fwcam_iso_input(struct fw_xferq *);
65 
66 static d_open_t		fwcam_cdev_open;
67 static d_close_t	fwcam_cdev_close;
68 static d_read_t		fwcam_cdev_read;
69 static d_poll_t		fwcam_cdev_poll;
70 static d_ioctl_t	fwcam_cdev_ioctl;
71 
72 static struct cdevsw fwcam_cdevsw = {
73 	.d_version =	D_VERSION,
74 	.d_flags =	D_TRACKCLOSE,
75 	.d_open =	fwcam_cdev_open,
76 	.d_close =	fwcam_cdev_close,
77 	.d_read =	fwcam_cdev_read,
78 	.d_poll =	fwcam_cdev_poll,
79 	.d_ioctl =	fwcam_cdev_ioctl,
80 	.d_name =	"fwcam",
81 };
82 
83 static uint32_t
fwcam_find_iidc(struct fw_device * fwdev)84 fwcam_find_iidc(struct fw_device *fwdev)
85 {
86 	struct crom_context cc;
87 	struct csrreg *reg;
88 	uint32_t cmd_base;
89 
90 	if (!crom_has_specver(fwdev->csrrom, CSRVAL_1394TA, CSR_PROTCAM130) &&
91 	    !crom_has_specver(fwdev->csrrom, CSRVAL_1394TA, CSR_PROTCAM120) &&
92 	    !crom_has_specver(fwdev->csrrom, CSRVAL_1394TA, CSR_PROTCAM104))
93 		return (0);
94 
95 	cmd_base = 0;
96 	crom_init_context(&cc, fwdev->csrrom);
97 	reg = crom_search_key(&cc, IIDC_CROM_CMD_BASE);
98 	if (reg != NULL && reg->val != 0)
99 		cmd_base = reg->val;
100 
101 	return (cmd_base);
102 }
103 
104 static int
fwcam_read_quadlet(struct fwcam_softc * sc,uint32_t offset,uint32_t * val)105 fwcam_read_quadlet(struct fwcam_softc *sc, uint32_t offset, uint32_t *val)
106 {
107 	uint16_t dst;
108 	uint8_t spd;
109 	int err;
110 
111 	FWCAM_LOCK(sc);
112 	if (sc->fwdev == NULL) {
113 		FWCAM_UNLOCK(sc);
114 		return (ENXIO);
115 	}
116 	dst = FWLOCALBUS | sc->fwdev->dst;
117 	spd = min(sc->fwdev->speed, FWSPD_S400);
118 	FWCAM_UNLOCK(sc);
119 
120 	err = fw_read_quadlet(sc->fd.fc, M_FWCAM, dst, spd,
121 	    sc->cmd_hi, sc->cmd_lo + offset, val);
122 	if (err)
123 		FWCAM_DEBUG(1, "read_quadlet: offset=0x%x err=%d\n",
124 		    offset, err);
125 	return (err);
126 }
127 
128 static int
fwcam_write_quadlet(struct fwcam_softc * sc,uint32_t offset,uint32_t val)129 fwcam_write_quadlet(struct fwcam_softc *sc, uint32_t offset, uint32_t val)
130 {
131 	uint16_t dst;
132 	uint8_t spd;
133 	int err;
134 
135 	FWCAM_LOCK(sc);
136 	if (sc->fwdev == NULL) {
137 		FWCAM_UNLOCK(sc);
138 		return (ENXIO);
139 	}
140 	dst = FWLOCALBUS | sc->fwdev->dst;
141 	spd = min(sc->fwdev->speed, FWSPD_S400);
142 	FWCAM_UNLOCK(sc);
143 
144 	err = fw_write_quadlet(sc->fd.fc, M_FWCAM, dst, spd,
145 	    sc->cmd_hi, sc->cmd_lo + offset, val);
146 	if (err)
147 		FWCAM_DEBUG(1, "write_quadlet: offset=0x%x err=%d\n",
148 		    offset, err);
149 	return (err);
150 }
151 
152 #if 0
153 static const char *
154 fwcam_format_name(int format)
155 {
156 	static const char *names[] = {
157 		"VGA (Format_0)",
158 		"Super VGA 1 (Format_1)",
159 		"Super VGA 2 (Format_2)",
160 		"Reserved",
161 		"Reserved",
162 		"Reserved",
163 		"Still Image (Format_6)",
164 		"Partial Image (Format_7)",
165 	};
166 
167 	if (format >= 0 && format <= 7)
168 		return (names[format]);
169 	return ("Unknown");
170 }
171 #endif
172 
173 static int
fwcam_read_capabilities(struct fwcam_softc * sc)174 fwcam_read_capabilities(struct fwcam_softc *sc)
175 {
176 	int err;
177 
178 	err = fwcam_read_quadlet(sc, IIDC_V_FORMAT_INQ, &sc->formats);
179 	if (err) {
180 		device_printf(sc->fd.dev,
181 		    "failed to read V_FORMAT_INQ: %d\n", err);
182 		return (err);
183 	}
184 
185 	err = fwcam_read_quadlet(sc, IIDC_BASIC_FUNC_INQ, &sc->basic_func);
186 	if (err) {
187 		device_printf(sc->fd.dev,
188 		    "failed to read BASIC_FUNC_INQ: %d\n", err);
189 		return (err);
190 	}
191 
192 	err = fwcam_read_quadlet(sc, IIDC_FEATURE_HI_INQ, &sc->features_hi);
193 	if (err)
194 		sc->features_hi = 0;
195 
196 	err = fwcam_read_quadlet(sc, IIDC_FEATURE_LO_INQ, &sc->features_lo);
197 	if (err)
198 		sc->features_lo = 0;
199 
200 	return (0);
201 }
202 
203 static int
fwcam_power_on(struct fwcam_softc * sc)204 fwcam_power_on(struct fwcam_softc *sc)
205 {
206 	uint32_t val;
207 	int err, retries;
208 
209 	err = fwcam_read_quadlet(sc, IIDC_BASIC_FUNC_INQ, &val);
210 	if (err) {
211 		device_printf(sc->fd.dev,
212 		    "cannot read BASIC_FUNC_INQ: %d\n", err);
213 		return (err);
214 	}
215 
216 	if ((val & IIDC_CAM_POWER_CTRL) == 0) {
217 		FWCAM_DEBUG(1, "no power control, assuming powered\n");
218 		return (0);
219 	}
220 
221 	err = fwcam_read_quadlet(sc, IIDC_CAMERA_POWER, &val);
222 	if (err == 0 && (val & IIDC_POWER_ON)) {
223 		FWCAM_DEBUG(1, "camera already powered on\n");
224 		return (0);
225 	}
226 
227 	err = fwcam_write_quadlet(sc, IIDC_CAMERA_POWER, IIDC_POWER_ON);
228 	if (err) {
229 		device_printf(sc->fd.dev,
230 		    "failed to write CAMERA_POWER: %d\n", err);
231 		return (err);
232 	}
233 
234 	for (retries = 0; retries < 50; retries++) {
235 		pause("fwcampw", hz / 10);
236 
237 		if (sc->state == FWCAM_STATE_DETACHING)
238 			return (ENXIO);
239 
240 		err = fwcam_read_quadlet(sc, IIDC_CAMERA_POWER, &val);
241 		if (err)
242 			continue;	/* read may fail while powering up */
243 
244 		if (val & IIDC_POWER_ON)
245 			return (0);
246 	}
247 
248 	device_printf(sc->fd.dev, "camera power-on timeout (5s)\n");
249 	return (ETIMEDOUT);
250 }
251 
252 static void
fwcam_probe_task(void * arg,int pending __unused)253 fwcam_probe_task(void *arg, int pending __unused)
254 {
255 	struct fwcam_softc *sc = (struct fwcam_softc *)arg;
256 	int err;
257 
258 	if (sc->state == FWCAM_STATE_DETACHING || sc->fwdev == NULL)
259 		return;
260 
261 	err = fwcam_power_on(sc);
262 	if (err) {
263 		device_printf(sc->fd.dev,
264 		    "power-on failed (%d), trying to read anyway\n", err);
265 	}
266 
267 	if (sc->state == FWCAM_STATE_DETACHING)
268 		return;
269 
270 	if (fwcam_read_capabilities(sc) == 0) {
271 		uint32_t val;
272 
273 		if (fwcam_read_quadlet(sc, IIDC_CUR_V_FORMAT, &val) == 0)
274 			sc->cur_format = (val >> 29) & 0x7;
275 		if (fwcam_read_quadlet(sc, IIDC_CUR_V_MODE, &val) == 0)
276 			sc->cur_mode = (val >> 29) & 0x7;
277 		if (fwcam_read_quadlet(sc, IIDC_CUR_V_FRM_RATE, &val) == 0)
278 			sc->cur_framerate = (val >> 29) & 0x7;
279 
280 		FWCAM_LOCK(sc);
281 		if (sc->state == FWCAM_STATE_DETACHING) {
282 			FWCAM_UNLOCK(sc);
283 			return;
284 		}
285 		sc->state = FWCAM_STATE_PROBED;
286 		FWCAM_UNLOCK(sc);
287 
288 		if (sc->open_count > 0 &&
289 		    sc->state != FWCAM_STATE_DETACHING)
290 			fwcam_iso_start(sc);
291 	}
292 }
293 
294 /*
295  * Compute expected frame size for current format/mode.
296  * Format_0 (VGA) modes:
297  *   Mode 0: 160x120 YUV444  = 160*120*3 = 57600
298  *   Mode 1: 320x240 YUV422  = 320*240*2 = 153600
299  *   Mode 2: 640x480 YUV411  = 640*480*3/2 = 460800
300  *   Mode 3: 640x480 YUV422  = 640*480*2 = 614400
301  *   Mode 4: 640x480 RGB8    = 640*480*3 = 921600
302  *   Mode 5: 640x480 Mono8   = 640*480 = 307200
303  */
304 static uint32_t
fwcam_frame_size(struct fwcam_softc * sc)305 fwcam_frame_size(struct fwcam_softc *sc)
306 {
307 	static const uint32_t fmt0_sizes[] = {
308 		160 * 120 * 3,		/* mode 0: YUV444 */
309 		320 * 240 * 2,		/* mode 1: YUV422 */
310 		640 * 480 * 3 / 2,	/* mode 2: YUV411 */
311 		640 * 480 * 2,		/* mode 3: YUV422 */
312 		640 * 480 * 3,		/* mode 4: RGB8 */
313 		640 * 480,		/* mode 5: Mono8 */
314 	};
315 
316 	if (sc->cur_format == IIDC_FMT_VGA && sc->cur_mode < nitems(fmt0_sizes))
317 		return (fmt0_sizes[sc->cur_mode]);
318 
319 	/* Default to largest VGA mode */
320 	return (FWCAM_MAX_FRAME_SIZE);
321 }
322 
323 static int
fwcam_iso_start(struct fwcam_softc * sc)324 fwcam_iso_start(struct fwcam_softc *sc)
325 {
326 	struct firewire_comm *fc = sc->fd.fc;
327 	struct fw_xferq *xferq;
328 	uint32_t val;
329 	int dma_ch, err;
330 
331 	mtx_assert(&sc->mtx, MA_NOTOWNED);
332 
333 	FWCAM_LOCK(sc);
334 	if (sc->dma_ch >= 0 || sc->state == FWCAM_STATE_STREAMING) {
335 		FWCAM_UNLOCK(sc);
336 		return (0);	/* already running or starting */
337 	}
338 	if (sc->state == FWCAM_STATE_DETACHING) {
339 		FWCAM_UNLOCK(sc);
340 		return (ENXIO);
341 	}
342 	FWCAM_UNLOCK(sc);
343 
344 	dma_ch = fw_open_isodma(fc, 0);
345 	if (dma_ch < 0) {
346 		device_printf(sc->fd.dev, "no IR DMA channel available\n");
347 		return (EBUSY);
348 	}
349 
350 	FWCAM_LOCK(sc);
351 	if (sc->dma_ch >= 0) {
352 		FWCAM_UNLOCK(sc);
353 		fc->ir[dma_ch]->flag &= ~FWXFERQ_OPEN;
354 		return (0);
355 	}
356 	FWCAM_UNLOCK(sc);
357 
358 	xferq = fc->ir[dma_ch];
359 	xferq->flag |= FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_STREAM;
360 
361 	sc->iso_channel = (uint8_t)(iso_channel & FWXFERQ_CHTAGMASK);
362 	xferq->flag &= ~FWXFERQ_CHTAGMASK;
363 	xferq->flag |= sc->iso_channel & FWXFERQ_CHTAGMASK;
364 
365 	xferq->sc = (caddr_t)sc;
366 	xferq->hand = fwcam_iso_input;
367 	xferq->bnchunk = FWCAM_ISO_NCHUNK;
368 	xferq->bnpacket = 1;
369 	xferq->psize = FWCAM_ISO_PKTSIZE;
370 	xferq->queued = 0;
371 	xferq->buf = NULL;
372 
373 	xferq->bulkxfer = malloc(sizeof(struct fw_bulkxfer) * xferq->bnchunk,
374 	    M_FWCAM, M_WAITOK | M_ZERO);
375 
376 	fw_iso_init_chunks(xferq);
377 
378 	sc->frame_size = fwcam_frame_size(sc);
379 	sc->frame_buf = malloc(sc->frame_size, M_FWCAM, M_WAITOK | M_ZERO);
380 	sc->read_buf = malloc(sc->frame_size, M_FWCAM, M_WAITOK | M_ZERO);
381 	sc->frame_offset = 0;
382 	sc->frame_ready = 0;
383 	sc->frame_dropped = 0;
384 
385 	val = ((uint32_t)sc->iso_channel << IIDC_ISO_CH_SHIFT) |
386 	    ((uint32_t)sc->iso_speed << IIDC_ISO_SPEED_SHIFT);
387 	err = fwcam_write_quadlet(sc, IIDC_ISO_CHANNEL, val);
388 	if (err) {
389 		device_printf(sc->fd.dev,
390 		    "failed to set ISO_CHANNEL: %d\n", err);
391 		goto fail;
392 	}
393 
394 	err = fwcam_write_quadlet(sc, IIDC_ISO_EN, IIDC_ISO_EN_ON);
395 	if (err) {
396 		device_printf(sc->fd.dev,
397 		    "failed to enable ISO: %d\n", err);
398 		goto fail;
399 	}
400 
401 	err = fc->irx_enable(fc, dma_ch);
402 	if (err) {
403 		device_printf(sc->fd.dev,
404 		    "failed to start IR DMA: %d\n", err);
405 		fwcam_write_quadlet(sc, IIDC_ISO_EN, 0);
406 		goto fail;
407 	}
408 
409 	FWCAM_LOCK(sc);
410 	if (sc->state == FWCAM_STATE_DETACHING) {
411 		FWCAM_UNLOCK(sc);
412 		fc->irx_disable(fc, dma_ch);
413 		fwcam_write_quadlet(sc, IIDC_ISO_EN, 0);
414 		err = ENXIO;
415 		goto fail;
416 	}
417 	sc->dma_ch = dma_ch;
418 	sc->state = FWCAM_STATE_STREAMING;
419 	FWCAM_UNLOCK(sc);
420 
421 	return (0);
422 
423 fail:
424 	fw_iso_free_chunks(xferq, M_FWCAM);
425 	xferq->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_OPEN | FWXFERQ_STREAM |
426 	    FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_CHTAGMASK);
427 	xferq->hand = NULL;
428 
429 	free(sc->frame_buf, M_FWCAM);
430 	free(sc->read_buf, M_FWCAM);
431 	sc->frame_buf = NULL;
432 	sc->read_buf = NULL;
433 	sc->dma_ch = -1;
434 
435 	return (err);
436 }
437 
438 static void
fwcam_iso_stop(struct fwcam_softc * sc)439 fwcam_iso_stop(struct fwcam_softc *sc)
440 {
441 	struct firewire_comm *fc = sc->fd.fc;
442 	struct fw_xferq *xferq;
443 	int dma_ch;
444 
445 	FWCAM_LOCK(sc);
446 	dma_ch = sc->dma_ch;
447 	if (dma_ch < 0) {
448 		FWCAM_UNLOCK(sc);
449 		return;
450 	}
451 	sc->dma_ch = -1;	/* claim ownership of teardown */
452 	FWCAM_UNLOCK(sc);
453 
454 	xferq = fc->ir[dma_ch];
455 
456 	if (xferq->flag & FWXFERQ_RUNNING)
457 		fc->irx_disable(fc, dma_ch);
458 
459 	if (sc->fwdev != NULL)
460 		fwcam_write_quadlet(sc, IIDC_ISO_EN, 0);
461 
462 	FWCAM_LOCK(sc);
463 	fw_iso_wait_inactive_locked(&sc->mtx, &sc->iso_active, "fwcamis");
464 	sc->frame_ready = 0;
465 	while (sc->read_in_progress)
466 		msleep(&sc->read_in_progress, &sc->mtx, PWAIT, "fwcamst", hz);
467 	FWCAM_UNLOCK(sc);
468 
469 	xferq->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_OPEN | FWXFERQ_STREAM |
470 	    FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_CHTAGMASK);
471 	xferq->hand = NULL;
472 
473 	fw_iso_free_chunks(xferq, M_FWCAM);
474 
475 	free(sc->frame_buf, M_FWCAM);
476 	free(sc->read_buf, M_FWCAM);
477 	sc->frame_buf = NULL;
478 	sc->read_buf = NULL;
479 }
480 
481 static void
fwcam_iso_input(struct fw_xferq * xferq)482 fwcam_iso_input(struct fw_xferq *xferq)
483 {
484 	struct fwcam_softc *sc = (struct fwcam_softc *)xferq->sc;
485 	struct fw_bulkxfer *sxfer;
486 	struct fw_pkt *fp;
487 	struct mbuf *m;
488 	uint8_t *payload;
489 	uint32_t plen;
490 	uint8_t *tmp;
491 	int dma_ch;
492 
493 	FWCAM_LOCK(sc);
494 	dma_ch = sc->dma_ch;
495 	if (dma_ch < 0 || sc->frame_buf == NULL) {
496 		FWCAM_UNLOCK(sc);
497 		return;
498 	}
499 	sc->iso_active = 1;
500 	FWCAM_UNLOCK(sc);
501 
502 	while ((sxfer = STAILQ_FIRST(&xferq->stvalid)) != NULL) {
503 		STAILQ_REMOVE_HEAD(&xferq->stvalid, link);
504 
505 		m = fw_iso_dequeue(xferq, sxfer, sc->fd.fc);
506 		if (m == NULL)
507 			continue;
508 
509 		fp = mtod(m, struct fw_pkt *);
510 		plen = fp->mode.stream.len;
511 		if (plen == 0) {
512 			m_freem(m);
513 			continue;	/* empty packet (padding) */
514 		}
515 
516 		if (fp->mode.stream.sy == 1) {
517 			if (sc->frame_offset > 0) {
518 				if (sc->frame_offset == sc->frame_size) {
519 					FWCAM_LOCK(sc);
520 					if (sc->read_in_progress) {
521 						sc->frame_dropped++;
522 					} else {
523 						if (sc->frame_ready)
524 							sc->frame_dropped++;
525 						tmp = sc->read_buf;
526 						sc->read_buf = sc->frame_buf;
527 						sc->frame_buf = tmp;
528 						sc->frame_ready = 1;
529 						wakeup(sc);
530 						selwakeup(&sc->rsel);
531 					}
532 					FWCAM_UNLOCK(sc);
533 				} else {
534 					sc->frame_dropped++;
535 				}
536 			}
537 			sc->frame_offset = 0;
538 		}
539 
540 		if (m->m_len < 4) {
541 			m_freem(m);
542 			continue;
543 		}
544 		payload = mtod(m, uint8_t *) + 4;
545 		if (plen > (uint32_t)(m->m_len - 4))
546 			plen = m->m_len - 4;
547 
548 		if (sc->frame_offset + plen <= sc->frame_size) {
549 			memcpy(sc->frame_buf + sc->frame_offset, payload, plen);
550 			sc->frame_offset += plen;
551 		} else {
552 			sc->frame_dropped++;
553 			sc->frame_offset = 0;
554 		}
555 
556 		m_freem(m);
557 	}
558 
559 	fw_iso_rearm_done(xferq, sc->fd.fc, &sc->mtx, &sc->iso_active,
560 	    &sc->dma_ch, dma_ch);
561 }
562 
563 static int
fwcam_cdev_open(struct cdev * dev,int oflags,int devtype,struct thread * td)564 fwcam_cdev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
565 {
566 	struct fwcam_softc *sc = dev->si_drv1;
567 	int err = 0;
568 
569 	FWCAM_LOCK(sc);
570 	if (sc->state == FWCAM_STATE_DETACHING) {
571 		FWCAM_UNLOCK(sc);
572 		return (ENXIO);
573 	}
574 	if (sc->state != FWCAM_STATE_PROBED &&
575 	    sc->state != FWCAM_STATE_STREAMING) {
576 		FWCAM_UNLOCK(sc);
577 		return (EAGAIN);	/* not yet probed */
578 	}
579 
580 	sc->open_count++;
581 	if (sc->open_count == 1 && sc->state == FWCAM_STATE_PROBED) {
582 		FWCAM_UNLOCK(sc);
583 		err = fwcam_iso_start(sc);
584 		if (err) {
585 			FWCAM_LOCK(sc);
586 			sc->open_count--;
587 			FWCAM_UNLOCK(sc);
588 		}
589 	} else {
590 		FWCAM_UNLOCK(sc);
591 	}
592 	return (err);
593 }
594 
595 static int
fwcam_cdev_close(struct cdev * dev,int fflag,int devtype,struct thread * td)596 fwcam_cdev_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
597 {
598 	struct fwcam_softc *sc = dev->si_drv1;
599 
600 	FWCAM_LOCK(sc);
601 	sc->open_count--;
602 	if (sc->open_count <= 0) {
603 		sc->open_count = 0;
604 		if (sc->state == FWCAM_STATE_STREAMING) {
605 			FWCAM_UNLOCK(sc);
606 			fwcam_iso_stop(sc);
607 			FWCAM_LOCK(sc);
608 			if (sc->state != FWCAM_STATE_DETACHING)
609 				sc->state = FWCAM_STATE_PROBED;
610 		}
611 	}
612 	FWCAM_UNLOCK(sc);
613 	return (0);
614 }
615 
616 static int
fwcam_cdev_read(struct cdev * dev,struct uio * uio,int ioflag)617 fwcam_cdev_read(struct cdev *dev, struct uio *uio, int ioflag)
618 {
619 	struct fwcam_softc *sc = dev->si_drv1;
620 	int err;
621 
622 	FWCAM_LOCK(sc);
623 	while (!sc->frame_ready) {
624 		if (sc->state != FWCAM_STATE_STREAMING) {
625 			FWCAM_UNLOCK(sc);
626 			return (ENXIO);
627 		}
628 		if (ioflag & FNONBLOCK) {
629 			FWCAM_UNLOCK(sc);
630 			return (EAGAIN);
631 		}
632 		err = msleep(sc, &sc->mtx, PCATCH, "fwcamrd", 5 * hz);
633 		if (err) {
634 			FWCAM_UNLOCK(sc);
635 			return (err);
636 		}
637 	}
638 
639 	sc->frame_ready = 0;
640 	if (sc->read_buf == NULL) {
641 		FWCAM_UNLOCK(sc);
642 		return (ENXIO);
643 	}
644 	sc->read_in_progress = 1;
645 	FWCAM_UNLOCK(sc);
646 
647 	err = uiomove(sc->read_buf,
648 	    MIN(uio->uio_resid, sc->frame_size), uio);
649 
650 	FWCAM_LOCK(sc);
651 	sc->read_in_progress = 0;
652 	wakeup(&sc->read_in_progress);
653 	FWCAM_UNLOCK(sc);
654 
655 	return (err);
656 }
657 
658 static int
fwcam_cdev_poll(struct cdev * dev,int events,struct thread * td)659 fwcam_cdev_poll(struct cdev *dev, int events, struct thread *td)
660 {
661 	struct fwcam_softc *sc = dev->si_drv1;
662 	int revents = 0;
663 
664 	FWCAM_LOCK(sc);
665 	if (events & (POLLIN | POLLRDNORM)) {
666 		if (sc->frame_ready)
667 			revents |= events & (POLLIN | POLLRDNORM);
668 		else
669 			selrecord(td, &sc->rsel);
670 	}
671 	FWCAM_UNLOCK(sc);
672 	return (revents);
673 }
674 
675 static const uint32_t fwcam_feat_inq[] = {
676 	[FWCAM_FEAT_BRIGHTNESS]   = IIDC_BRIGHTNESS_INQ,
677 	[FWCAM_FEAT_AUTO_EXPOSURE] = IIDC_AUTO_EXPOSURE_INQ,
678 	[FWCAM_FEAT_SHARPNESS]    = IIDC_SHARPNESS_INQ,
679 	[FWCAM_FEAT_WHITE_BALANCE] = IIDC_WHITE_BAL_INQ,
680 	[FWCAM_FEAT_HUE]         = IIDC_HUE_INQ,
681 	[FWCAM_FEAT_SATURATION]  = IIDC_SATURATION_INQ,
682 	[FWCAM_FEAT_GAMMA]       = IIDC_GAMMA_INQ,
683 	[FWCAM_FEAT_SHUTTER]     = IIDC_SHUTTER_INQ,
684 	[FWCAM_FEAT_GAIN]        = IIDC_GAIN_INQ,
685 	[FWCAM_FEAT_IRIS]        = IIDC_IRIS_INQ,
686 	[FWCAM_FEAT_FOCUS]       = IIDC_FOCUS_INQ,
687 	[FWCAM_FEAT_TEMPERATURE] = IIDC_TEMPERATURE_INQ,
688 	[FWCAM_FEAT_TRIGGER]     = IIDC_TRIGGER_INQ,
689 };
690 
691 static const uint32_t fwcam_feat_ctrl[] = {
692 	[FWCAM_FEAT_BRIGHTNESS]   = IIDC_BRIGHTNESS,
693 	[FWCAM_FEAT_AUTO_EXPOSURE] = IIDC_AUTO_EXPOSURE,
694 	[FWCAM_FEAT_SHARPNESS]    = IIDC_SHARPNESS,
695 	[FWCAM_FEAT_WHITE_BALANCE] = IIDC_WHITE_BALANCE,
696 	[FWCAM_FEAT_HUE]         = IIDC_HUE,
697 	[FWCAM_FEAT_SATURATION]  = IIDC_SATURATION,
698 	[FWCAM_FEAT_GAMMA]       = IIDC_GAMMA,
699 	[FWCAM_FEAT_SHUTTER]     = IIDC_SHUTTER,
700 	[FWCAM_FEAT_GAIN]        = IIDC_GAIN,
701 	[FWCAM_FEAT_IRIS]        = IIDC_IRIS,
702 	[FWCAM_FEAT_FOCUS]       = IIDC_FOCUS,
703 	[FWCAM_FEAT_TEMPERATURE] = IIDC_TEMPERATURE,
704 	[FWCAM_FEAT_TRIGGER]     = IIDC_TRIGGER_MODE,
705 	[FWCAM_FEAT_ZOOM]        = IIDC_ZOOM,
706 	[FWCAM_FEAT_PAN]         = IIDC_PAN,
707 	[FWCAM_FEAT_TILT]        = IIDC_TILT,
708 };
709 
710 static int
fwcam_get_feature(struct fwcam_softc * sc,struct fwcam_feature * feat)711 fwcam_get_feature(struct fwcam_softc *sc, struct fwcam_feature *feat)
712 {
713 	uint32_t inq, ctrl;
714 	int err;
715 
716 	if (feat->id >= FWCAM_FEAT_MAX)
717 		return (EINVAL);
718 
719 	feat->flags = 0;
720 	feat->min = 0;
721 	feat->max = 0;
722 	if (feat->id < nitems(fwcam_feat_inq) &&
723 	    fwcam_feat_inq[feat->id] != 0) {
724 		err = fwcam_read_quadlet(sc, fwcam_feat_inq[feat->id], &inq);
725 		if (err)
726 			return (err);
727 
728 		if (inq & (1 << 31))
729 			feat->flags |= FWCAM_FEATF_PRESENT;
730 		if (inq & (1 << 28))
731 			feat->flags |= FWCAM_FEATF_ONOFF;
732 		if (inq & (1 << 27))
733 			feat->flags |= FWCAM_FEATF_AUTO;
734 		if (inq & (1 << 26))
735 			feat->flags |= FWCAM_FEATF_MANUAL;
736 		feat->min = (inq >> 12) & 0xfff;
737 		feat->max = inq & 0xfff;
738 	}
739 
740 	if (feat->id >= nitems(fwcam_feat_ctrl) ||
741 	    fwcam_feat_ctrl[feat->id] == 0)
742 		return (EINVAL);
743 
744 	err = fwcam_read_quadlet(sc, fwcam_feat_ctrl[feat->id], &ctrl);
745 	if (err)
746 		return (err);
747 
748 	feat->value = ctrl & 0xfff;
749 	/* White balance has U/B in bits [20:31] and V/R in bits [8:19] */
750 	if (feat->id == FWCAM_FEAT_WHITE_BALANCE)
751 		feat->value2 = (ctrl >> 12) & 0xfff;
752 	else
753 		feat->value2 = 0;
754 
755 	return (0);
756 }
757 
758 static int
fwcam_set_feature(struct fwcam_softc * sc,struct fwcam_feature * feat)759 fwcam_set_feature(struct fwcam_softc *sc, struct fwcam_feature *feat)
760 {
761 	uint32_t ctrl, val;
762 	int err;
763 
764 	if (feat->id >= FWCAM_FEAT_MAX)
765 		return (EINVAL);
766 	if (feat->id >= nitems(fwcam_feat_ctrl) ||
767 	    fwcam_feat_ctrl[feat->id] == 0)
768 		return (EINVAL);
769 
770 	err = fwcam_read_quadlet(sc, fwcam_feat_ctrl[feat->id], &ctrl);
771 	if (err)
772 		return (err);
773 
774 	if (feat->id == FWCAM_FEAT_WHITE_BALANCE) {
775 		/* White balance: value=V/R (low 12), value2=U/B (high 12) */
776 		val = (ctrl & 0xff000000) |
777 		    ((feat->value2 & 0xfff) << 12) |
778 		    (feat->value & 0xfff);
779 	} else {
780 		/* Preserve upper bits, set new value in lower 12 */
781 		val = (ctrl & 0xfffff000) | (feat->value & 0xfff);
782 	}
783 
784 	return (fwcam_write_quadlet(sc, fwcam_feat_ctrl[feat->id], val));
785 }
786 
787 static int
fwcam_cdev_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int fflag,struct thread * td)788 fwcam_cdev_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
789     int fflag, struct thread *td)
790 {
791 	struct fwcam_softc *sc = dev->si_drv1;
792 	struct fwcam_mode *mode;
793 	struct fwcam_feature *feat;
794 	struct fwcam_info *info;
795 	int err;
796 
797 	if (sc->fwdev == NULL)
798 		return (ENXIO);
799 
800 	switch (cmd) {
801 	case FWCAM_GMODE:
802 		mode = (struct fwcam_mode *)data;
803 		mode->format = sc->cur_format;
804 		mode->mode = sc->cur_mode;
805 		mode->framerate = sc->cur_framerate;
806 		mode->frame_size = sc->frame_size ?
807 		    sc->frame_size : fwcam_frame_size(sc);
808 		return (0);
809 
810 	case FWCAM_SMODE:
811 	    {
812 		int was_streaming = 0;
813 
814 		mode = (struct fwcam_mode *)data;
815 		if (mode->format > 7 || mode->mode > 7 || mode->framerate > 7)
816 			return (EINVAL);
817 
818 		if (mode->format != IIDC_FMT_VGA)
819 			return (EINVAL);
820 		if (!(sc->formats & (1 << (31 - mode->format))))
821 			return (EINVAL);
822 
823 		FWCAM_LOCK(sc);
824 		if (sc->state == FWCAM_STATE_DETACHING) {
825 			FWCAM_UNLOCK(sc);
826 			return (ENXIO);
827 		}
828 		if (sc->state == FWCAM_STATE_STREAMING) {
829 			was_streaming = 1;
830 			FWCAM_UNLOCK(sc);
831 			fwcam_iso_stop(sc);
832 			FWCAM_LOCK(sc);
833 			if (sc->state != FWCAM_STATE_DETACHING)
834 				sc->state = FWCAM_STATE_PROBED;
835 		}
836 		FWCAM_UNLOCK(sc);
837 
838 		err = fwcam_write_quadlet(sc, IIDC_CUR_V_FORMAT,
839 		    (uint32_t)mode->format << 29);
840 		if (err == 0)
841 			err = fwcam_write_quadlet(sc, IIDC_CUR_V_MODE,
842 			    (uint32_t)mode->mode << 29);
843 		if (err == 0)
844 			err = fwcam_write_quadlet(sc, IIDC_CUR_V_FRM_RATE,
845 			    (uint32_t)mode->framerate << 29);
846 
847 		if (err == 0) {
848 			sc->cur_format = mode->format;
849 			sc->cur_mode = mode->mode;
850 			sc->cur_framerate = mode->framerate;
851 			mode->frame_size = fwcam_frame_size(sc);
852 		}
853 
854 		if (was_streaming)
855 			fwcam_iso_start(sc);
856 		return (err);
857 	    }
858 
859 	case FWCAM_GFEAT:
860 		feat = (struct fwcam_feature *)data;
861 		return (fwcam_get_feature(sc, feat));
862 
863 	case FWCAM_SFEAT:
864 		feat = (struct fwcam_feature *)data;
865 		return (fwcam_set_feature(sc, feat));
866 
867 	case FWCAM_GINFO:
868 		info = (struct fwcam_info *)data;
869 		info->formats = sc->formats;
870 		info->basic_func = sc->basic_func;
871 		info->features_hi = sc->features_hi;
872 		info->features_lo = sc->features_lo;
873 		info->cur_format = sc->cur_format;
874 		info->cur_mode = sc->cur_mode;
875 		info->cur_framerate = sc->cur_framerate;
876 		info->state = sc->state;
877 		info->frame_size = sc->frame_size ?
878 		    sc->frame_size : fwcam_frame_size(sc);
879 		info->frame_dropped = sc->frame_dropped;
880 		info->iso_channel = sc->iso_channel;
881 		info->_pad[0] = info->_pad[1] = info->_pad[2] = 0;
882 		return (0);
883 
884 	default:
885 		return (ENOTTY);
886 	}
887 }
888 
889 static void
fwcam_post_explore(void * arg)890 fwcam_post_explore(void *arg)
891 {
892 	struct fwcam_softc *sc = (struct fwcam_softc *)arg;
893 	struct fw_device *fwdev;
894 	uint32_t cmd_base;
895 	int was_streaming, err;
896 
897 	FWCAM_LOCK(sc);
898 
899 	if (sc->state == FWCAM_STATE_DETACHING) {
900 		FWCAM_UNLOCK(sc);
901 		return;
902 	}
903 
904 	if (sc->fwdev != NULL) {
905 		STAILQ_FOREACH(fwdev, &sc->fd.fc->devices, link) {
906 			if (fwdev == sc->fwdev &&
907 			    fwdev->status == FWDEVATTACHED)
908 				break;
909 		}
910 		if (fwdev == NULL) {
911 			was_streaming = (sc->state == FWCAM_STATE_STREAMING);
912 			device_printf(sc->fd.dev, "camera disconnected%s\n",
913 			    was_streaming ? " (was streaming)" : "");
914 			sc->fwdev = NULL;
915 			sc->state = FWCAM_STATE_IDLE;
916 			wakeup(sc);		/* unblock readers */
917 			selwakeup(&sc->rsel);
918 			FWCAM_UNLOCK(sc);
919 
920 			if (was_streaming)
921 				fwcam_iso_stop(sc);
922 			FWCAM_LOCK(sc);
923 		}
924 	}
925 
926 	if (sc->fwdev == NULL) {
927 		STAILQ_FOREACH(fwdev, &sc->fd.fc->devices, link) {
928 			if (fwdev->status != FWDEVATTACHED)
929 				continue;
930 
931 			cmd_base = fwcam_find_iidc(fwdev);
932 			if (cmd_base == 0)
933 				continue;
934 
935 			sc->fwdev = fwdev;
936 			sc->cmd_hi = 0xffff;
937 			sc->cmd_lo = 0xf0000000 | (cmd_base << 2);
938 
939 			FWCAM_UNLOCK(sc);
940 			err = taskqueue_enqueue(taskqueue_thread,
941 			    &sc->probe_task);
942 			if (err)
943 				device_printf(sc->fd.dev,
944 				    "taskqueue_enqueue failed: %d\n", err);
945 			return;
946 		}
947 	}
948 
949 	FWCAM_UNLOCK(sc);
950 }
951 
952 static void
fwcam_post_busreset(void * arg __unused)953 fwcam_post_busreset(void *arg __unused)
954 {
955 
956 }
957 
958 static void
fwcam_identify(driver_t * driver,device_t parent)959 fwcam_identify(driver_t *driver, device_t parent)
960 {
961 
962 	if (device_find_child(parent, "fwcam", DEVICE_UNIT_ANY) == NULL)
963 		BUS_ADD_CHILD(parent, 0, "fwcam", DEVICE_UNIT_ANY);
964 }
965 
966 static int
fwcam_probe(device_t dev)967 fwcam_probe(device_t dev)
968 {
969 
970 	device_set_desc(dev, "IIDC Digital Camera over FireWire");
971 	return (0);
972 }
973 
974 static int
fwcam_attach(device_t dev)975 fwcam_attach(device_t dev)
976 {
977 	struct fwcam_softc *sc;
978 
979 	sc = device_get_softc(dev);
980 	sc->fd.dev = dev;
981 	sc->fd.fc = device_get_ivars(dev);
982 	mtx_init(&sc->mtx, "fwcam", NULL, MTX_DEF);
983 
984 	sc->fwdev = NULL;
985 	sc->state = FWCAM_STATE_IDLE;
986 	sc->dma_ch = -1;
987 	sc->iso_active = 0;
988 	sc->open_count = 0;
989 	knlist_init_mtx(&sc->rsel.si_note, &sc->mtx);
990 	TASK_INIT(&sc->probe_task, 0, fwcam_probe_task, sc);
991 
992 	sc->cdev = make_dev(&fwcam_cdevsw, device_get_unit(dev),
993 	    UID_ROOT, GID_OPERATOR, 0660, "fwcam%d", device_get_unit(dev));
994 	sc->cdev->si_drv1 = sc;
995 
996 	sc->fd.post_busreset = fwcam_post_busreset;
997 	sc->fd.post_explore = fwcam_post_explore;
998 
999 	fwcam_post_explore(sc);
1000 
1001 	return (0);
1002 }
1003 
1004 static int
fwcam_detach(device_t dev)1005 fwcam_detach(device_t dev)
1006 {
1007 	struct fwcam_softc *sc;
1008 
1009 	sc = device_get_softc(dev);
1010 
1011 	FWCAM_LOCK(sc);
1012 	sc->state = FWCAM_STATE_DETACHING;
1013 	wakeup(sc);	/* wake any sleeping readers */
1014 	FWCAM_UNLOCK(sc);
1015 
1016 	taskqueue_drain(taskqueue_thread, &sc->probe_task);
1017 	fwcam_iso_stop(sc);
1018 
1019 	if (sc->cdev != NULL)
1020 		destroy_dev(sc->cdev);
1021 
1022 	seldrain(&sc->rsel);
1023 	knlist_destroy(&sc->rsel.si_note);
1024 
1025 	FWCAM_LOCK(sc);
1026 	sc->fwdev = NULL;
1027 	FWCAM_UNLOCK(sc);
1028 
1029 	mtx_destroy(&sc->mtx);
1030 
1031 	return (0);
1032 }
1033 
1034 static device_method_t fwcam_methods[] = {
1035 	DEVMETHOD(device_identify,	fwcam_identify),
1036 	DEVMETHOD(device_probe,		fwcam_probe),
1037 	DEVMETHOD(device_attach,	fwcam_attach),
1038 	DEVMETHOD(device_detach,	fwcam_detach),
1039 
1040 	DEVMETHOD_END
1041 };
1042 
1043 static driver_t fwcam_driver = {
1044 	"fwcam",
1045 	fwcam_methods,
1046 	sizeof(struct fwcam_softc),
1047 };
1048 
1049 DRIVER_MODULE(fwcam, firewire, fwcam_driver, 0, 0);
1050 MODULE_VERSION(fwcam, 1);
1051 MODULE_DEPEND(fwcam, firewire, 1, 1, 1);
1052