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