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