123a6997eSAbdelkader Boudih /*
223a6997eSAbdelkader Boudih * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
323a6997eSAbdelkader Boudih *
423a6997eSAbdelkader Boudih * SPDX-License-Identifier: BSD-2-Clause
523a6997eSAbdelkader Boudih */
623a6997eSAbdelkader Boudih
723a6997eSAbdelkader Boudih /*
823a6997eSAbdelkader Boudih * fwdv(4) - AV/C DV capture driver for FireWire camcorders
923a6997eSAbdelkader Boudih *
1023a6997eSAbdelkader Boudih * References:
1123a6997eSAbdelkader Boudih * AV/C General Specification 4.1 (TA Document 2001012)
1223a6997eSAbdelkader Boudih * IEC 61883-1, IEC 61883-2
1323a6997eSAbdelkader Boudih */
1423a6997eSAbdelkader Boudih
1523a6997eSAbdelkader Boudih #include <sys/param.h>
1623a6997eSAbdelkader Boudih #include <sys/systm.h>
1723a6997eSAbdelkader Boudih #include <sys/module.h>
1823a6997eSAbdelkader Boudih #include <sys/bus.h>
1923a6997eSAbdelkader Boudih #include <sys/kernel.h>
2023a6997eSAbdelkader Boudih #include <sys/conf.h>
2123a6997eSAbdelkader Boudih #include <sys/malloc.h>
2223a6997eSAbdelkader Boudih #include <sys/lock.h>
2323a6997eSAbdelkader Boudih #include <sys/mutex.h>
2423a6997eSAbdelkader Boudih #include <sys/sysctl.h>
2523a6997eSAbdelkader Boudih #include <sys/taskqueue.h>
2623a6997eSAbdelkader Boudih #include <sys/fcntl.h>
2723a6997eSAbdelkader Boudih #include <sys/poll.h>
2823a6997eSAbdelkader Boudih #include <sys/selinfo.h>
2923a6997eSAbdelkader Boudih #include <sys/uio.h>
3023a6997eSAbdelkader Boudih #include <sys/mbuf.h>
3123a6997eSAbdelkader Boudih
3223a6997eSAbdelkader Boudih #include <dev/firewire/firewire.h>
3323a6997eSAbdelkader Boudih #include <dev/firewire/firewirereg.h>
3423a6997eSAbdelkader Boudih #include <dev/firewire/iec13213.h>
3523a6997eSAbdelkader Boudih #include <dev/firewire/iec68113.h>
3623a6997eSAbdelkader Boudih #include <dev/firewire/fwdv.h>
3723a6997eSAbdelkader Boudih #include <dev/firewire/fw_helpers.h>
3823a6997eSAbdelkader Boudih
3923a6997eSAbdelkader Boudih static MALLOC_DEFINE(M_FWDV, "fwdv", "AV/C DV over FireWire");
4023a6997eSAbdelkader Boudih
4123a6997eSAbdelkader Boudih static int debug = 0;
4223a6997eSAbdelkader Boudih static int iso_channel = FWDV_DEFAULT_ISO_CH;
4323a6997eSAbdelkader Boudih SYSCTL_DECL(_hw_firewire);
4423a6997eSAbdelkader Boudih static SYSCTL_NODE(_hw_firewire, OID_AUTO, fwdv, CTLFLAG_RD | CTLFLAG_MPSAFE,
4523a6997eSAbdelkader Boudih 0, "AV/C DV");
4623a6997eSAbdelkader Boudih SYSCTL_INT(_hw_firewire_fwdv, OID_AUTO, debug, CTLFLAG_RWTUN, &debug, 0,
4723a6997eSAbdelkader Boudih "fwdv debug level");
4823a6997eSAbdelkader Boudih SYSCTL_INT(_hw_firewire_fwdv, OID_AUTO, iso_channel, CTLFLAG_RWTUN,
4923a6997eSAbdelkader Boudih &iso_channel, 0, "ISO channel for DV receive");
5023a6997eSAbdelkader Boudih
5123a6997eSAbdelkader Boudih #define FWDV_DEBUG(lev, fmt, ...) \
5223a6997eSAbdelkader Boudih do { \
5323a6997eSAbdelkader Boudih if (debug >= (lev)) \
5423a6997eSAbdelkader Boudih printf("fwdv: " fmt, ## __VA_ARGS__); \
5523a6997eSAbdelkader Boudih } while (0)
5623a6997eSAbdelkader Boudih
5723a6997eSAbdelkader Boudih static int fwdv_probe(device_t);
5823a6997eSAbdelkader Boudih static int fwdv_attach(device_t);
5923a6997eSAbdelkader Boudih static int fwdv_detach(device_t);
6023a6997eSAbdelkader Boudih static void fwdv_probe_task(void *, int);
6123a6997eSAbdelkader Boudih
6223a6997eSAbdelkader Boudih static int fwdv_avc_command(struct fwdv_softc *, const uint8_t *, int,
6323a6997eSAbdelkader Boudih uint8_t *, int *);
6423a6997eSAbdelkader Boudih static int fwdv_avc_unit_info(struct fwdv_softc *);
6523a6997eSAbdelkader Boudih static int fwdv_avc_play(struct fwdv_softc *);
6623a6997eSAbdelkader Boudih static int fwdv_avc_stop(struct fwdv_softc *);
6723a6997eSAbdelkader Boudih
6823a6997eSAbdelkader Boudih static int fwdv_read_opcr(struct fwdv_softc *, int, uint32_t *);
6923a6997eSAbdelkader Boudih
7023a6997eSAbdelkader Boudih static int fwdv_iso_start(struct fwdv_softc *);
7123a6997eSAbdelkader Boudih static void fwdv_iso_stop(struct fwdv_softc *);
7223a6997eSAbdelkader Boudih static void fwdv_iso_input(struct fw_xferq *);
7323a6997eSAbdelkader Boudih
7423a6997eSAbdelkader Boudih static void fwdv_fcp_handler(struct fw_xfer *);
7523a6997eSAbdelkader Boudih
7623a6997eSAbdelkader Boudih static d_open_t fwdv_cdev_open;
7723a6997eSAbdelkader Boudih static d_close_t fwdv_cdev_close;
7823a6997eSAbdelkader Boudih static d_read_t fwdv_cdev_read;
7923a6997eSAbdelkader Boudih static d_poll_t fwdv_cdev_poll;
8023a6997eSAbdelkader Boudih static d_ioctl_t fwdv_cdev_ioctl;
8123a6997eSAbdelkader Boudih
8223a6997eSAbdelkader Boudih static struct cdevsw fwdv_cdevsw = {
8323a6997eSAbdelkader Boudih .d_version = D_VERSION,
8423a6997eSAbdelkader Boudih .d_flags = D_TRACKCLOSE,
8523a6997eSAbdelkader Boudih .d_open = fwdv_cdev_open,
8623a6997eSAbdelkader Boudih .d_close = fwdv_cdev_close,
8723a6997eSAbdelkader Boudih .d_read = fwdv_cdev_read,
8823a6997eSAbdelkader Boudih .d_poll = fwdv_cdev_poll,
8923a6997eSAbdelkader Boudih .d_ioctl = fwdv_cdev_ioctl,
9023a6997eSAbdelkader Boudih .d_name = "fwdv",
9123a6997eSAbdelkader Boudih };
9223a6997eSAbdelkader Boudih
9323a6997eSAbdelkader Boudih static int
fwdv_read_quadlet(struct fwdv_softc * sc,uint16_t addr_hi,uint32_t addr_lo,uint32_t * val)9423a6997eSAbdelkader Boudih fwdv_read_quadlet(struct fwdv_softc *sc, uint16_t addr_hi, uint32_t addr_lo,
9523a6997eSAbdelkader Boudih uint32_t *val)
9623a6997eSAbdelkader Boudih {
9723a6997eSAbdelkader Boudih uint16_t dst;
9823a6997eSAbdelkader Boudih uint8_t spd;
9923a6997eSAbdelkader Boudih
10023a6997eSAbdelkader Boudih FWDV_LOCK(sc);
10123a6997eSAbdelkader Boudih if (sc->fwdev == NULL) {
10223a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
10323a6997eSAbdelkader Boudih return (ENXIO);
10423a6997eSAbdelkader Boudih }
10523a6997eSAbdelkader Boudih dst = FWLOCALBUS | sc->fwdev->dst;
10623a6997eSAbdelkader Boudih spd = min(sc->fwdev->speed, FWSPD_S400);
10723a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
10823a6997eSAbdelkader Boudih
10923a6997eSAbdelkader Boudih return (fw_read_quadlet(sc->fd.fc, M_FWDV, dst, spd,
11023a6997eSAbdelkader Boudih addr_hi, addr_lo, val));
11123a6997eSAbdelkader Boudih }
11223a6997eSAbdelkader Boudih
11323a6997eSAbdelkader Boudih static void
fwdv_fcp_handler(struct fw_xfer * xfer)11423a6997eSAbdelkader Boudih fwdv_fcp_handler(struct fw_xfer *xfer)
11523a6997eSAbdelkader Boudih {
11623a6997eSAbdelkader Boudih struct fwdv_softc *sc = (struct fwdv_softc *)xfer->sc;
11723a6997eSAbdelkader Boudih struct fw_pkt *fp;
11823a6997eSAbdelkader Boudih uint16_t src_id;
11923a6997eSAbdelkader Boudih int len;
12023a6997eSAbdelkader Boudih
12123a6997eSAbdelkader Boudih fp = &xfer->recv.hdr;
12223a6997eSAbdelkader Boudih
12323a6997eSAbdelkader Boudih if (xfer->resp != 0) {
12423a6997eSAbdelkader Boudih FWDV_DEBUG(1, "FCP response error: %d\n", xfer->resp);
12523a6997eSAbdelkader Boudih goto done;
12623a6997eSAbdelkader Boudih }
12723a6997eSAbdelkader Boudih
12823a6997eSAbdelkader Boudih len = fp->mode.wreqb.len;
12923a6997eSAbdelkader Boudih if (len < 4 || len > FCP_MAX_FRAME_LEN) {
13023a6997eSAbdelkader Boudih FWDV_DEBUG(1, "FCP response bad len: %d\n", len);
13123a6997eSAbdelkader Boudih goto done;
13223a6997eSAbdelkader Boudih }
13323a6997eSAbdelkader Boudih
13423a6997eSAbdelkader Boudih if (xfer->recv.pay_len < 4) {
13523a6997eSAbdelkader Boudih FWDV_DEBUG(1, "FCP response short payload: %d\n",
13623a6997eSAbdelkader Boudih xfer->recv.pay_len);
13723a6997eSAbdelkader Boudih goto done;
13823a6997eSAbdelkader Boudih }
13923a6997eSAbdelkader Boudih if (xfer->recv.pay_len < len)
14023a6997eSAbdelkader Boudih len = xfer->recv.pay_len;
14123a6997eSAbdelkader Boudih
14223a6997eSAbdelkader Boudih src_id = fp->mode.wreqb.src;
14323a6997eSAbdelkader Boudih FWDV_LOCK(sc);
14423a6997eSAbdelkader Boudih if (sc->fwdev == NULL ||
14523a6997eSAbdelkader Boudih (FWLOCALBUS | sc->fwdev->dst) != src_id) {
14623a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
14723a6997eSAbdelkader Boudih FWDV_DEBUG(2, "FCP response from unknown node 0x%04x\n",
14823a6997eSAbdelkader Boudih src_id);
14923a6997eSAbdelkader Boudih goto done;
15023a6997eSAbdelkader Boudih }
15123a6997eSAbdelkader Boudih
15223a6997eSAbdelkader Boudih if (xfer->recv.payload != NULL) {
15323a6997eSAbdelkader Boudih memcpy(sc->fcp_resp, xfer->recv.payload,
15423a6997eSAbdelkader Boudih min(len, FCP_MAX_FRAME_LEN));
15523a6997eSAbdelkader Boudih sc->fcp_resp_len = len;
15623a6997eSAbdelkader Boudih sc->fcp_resp_ready = 1;
15723a6997eSAbdelkader Boudih wakeup(&sc->fcp_resp_ready);
15823a6997eSAbdelkader Boudih }
15923a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
16023a6997eSAbdelkader Boudih
16123a6997eSAbdelkader Boudih done:
16223a6997eSAbdelkader Boudih STAILQ_INSERT_TAIL(&sc->fcp_bind.xferlist, xfer, link);
16323a6997eSAbdelkader Boudih }
16423a6997eSAbdelkader Boudih
16523a6997eSAbdelkader Boudih static int
fwdv_avc_command(struct fwdv_softc * sc,const uint8_t * cmd,int cmd_len,uint8_t * resp,int * resp_len)16623a6997eSAbdelkader Boudih fwdv_avc_command(struct fwdv_softc *sc, const uint8_t *cmd, int cmd_len,
16723a6997eSAbdelkader Boudih uint8_t *resp, int *resp_len)
16823a6997eSAbdelkader Boudih {
16923a6997eSAbdelkader Boudih struct fw_xfer *xfer;
17023a6997eSAbdelkader Boudih struct fw_pkt *fp;
17123a6997eSAbdelkader Boudih uint16_t dst;
17223a6997eSAbdelkader Boudih uint8_t spd;
17323a6997eSAbdelkader Boudih int err, padded_len;
17423a6997eSAbdelkader Boudih
17523a6997eSAbdelkader Boudih FWDV_LOCK(sc);
17623a6997eSAbdelkader Boudih while (sc->avc_busy) {
17723a6997eSAbdelkader Boudih if (sc->state == FWDV_STATE_DETACHING) {
17823a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
17923a6997eSAbdelkader Boudih return (ENXIO);
18023a6997eSAbdelkader Boudih }
18123a6997eSAbdelkader Boudih msleep(&sc->avc_busy, &sc->mtx, PWAIT, "fwdvavc", hz);
18223a6997eSAbdelkader Boudih }
18323a6997eSAbdelkader Boudih sc->avc_busy = 1;
18423a6997eSAbdelkader Boudih
18523a6997eSAbdelkader Boudih if (sc->fwdev == NULL) {
18623a6997eSAbdelkader Boudih sc->avc_busy = 0;
18723a6997eSAbdelkader Boudih wakeup(&sc->avc_busy);
18823a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
18923a6997eSAbdelkader Boudih return (ENXIO);
19023a6997eSAbdelkader Boudih }
19123a6997eSAbdelkader Boudih dst = FWLOCALBUS | sc->fwdev->dst;
19223a6997eSAbdelkader Boudih spd = min(sc->fwdev->speed, FWSPD_S400);
19323a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
19423a6997eSAbdelkader Boudih
19523a6997eSAbdelkader Boudih padded_len = roundup2(cmd_len, 4);
19623a6997eSAbdelkader Boudih
19723a6997eSAbdelkader Boudih xfer = fw_xfer_alloc_buf(M_FWDV, padded_len, 0);
19823a6997eSAbdelkader Boudih if (xfer == NULL) {
19923a6997eSAbdelkader Boudih err = ENOMEM;
20023a6997eSAbdelkader Boudih goto done;
20123a6997eSAbdelkader Boudih }
20223a6997eSAbdelkader Boudih
20323a6997eSAbdelkader Boudih xfer->fc = sc->fd.fc;
20423a6997eSAbdelkader Boudih xfer->hand = fw_xferwake;
20523a6997eSAbdelkader Boudih
20623a6997eSAbdelkader Boudih fp = &xfer->send.hdr;
20723a6997eSAbdelkader Boudih fp->mode.wreqb.tcode = FWTCODE_WREQB;
20823a6997eSAbdelkader Boudih fp->mode.wreqb.dst = dst;
20923a6997eSAbdelkader Boudih fp->mode.wreqb.dest_hi = FCP_COMMAND_ADDR >> 32;
21023a6997eSAbdelkader Boudih fp->mode.wreqb.dest_lo = FCP_COMMAND_ADDR & 0xffffffff;
21123a6997eSAbdelkader Boudih fp->mode.wreqb.len = padded_len;
21223a6997eSAbdelkader Boudih fp->mode.wreqb.extcode = 0;
21323a6997eSAbdelkader Boudih xfer->send.spd = spd;
21423a6997eSAbdelkader Boudih xfer->send.pay_len = padded_len;
21523a6997eSAbdelkader Boudih
21623a6997eSAbdelkader Boudih memcpy(xfer->send.payload, cmd, cmd_len);
21723a6997eSAbdelkader Boudih if (padded_len > cmd_len)
21823a6997eSAbdelkader Boudih memset((uint8_t *)xfer->send.payload + cmd_len, 0,
21923a6997eSAbdelkader Boudih padded_len - cmd_len);
22023a6997eSAbdelkader Boudih
22123a6997eSAbdelkader Boudih FWDV_LOCK(sc);
22223a6997eSAbdelkader Boudih sc->fcp_resp_ready = 0;
22323a6997eSAbdelkader Boudih sc->fcp_resp_len = 0;
22423a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
22523a6997eSAbdelkader Boudih
22623a6997eSAbdelkader Boudih FWDV_DEBUG(2, "AVC cmd: %02x %02x %02x %02x (len=%d)\n",
22723a6997eSAbdelkader Boudih cmd[0], cmd[1], cmd_len > 2 ? cmd[2] : 0,
22823a6997eSAbdelkader Boudih cmd_len > 3 ? cmd[3] : 0, cmd_len);
22923a6997eSAbdelkader Boudih
23023a6997eSAbdelkader Boudih err = fw_xfer_request_wait(xfer->fc, xfer, 2 * hz);
23123a6997eSAbdelkader Boudih if (err != 0) {
23223a6997eSAbdelkader Boudih fw_xfer_free_buf(xfer);
23323a6997eSAbdelkader Boudih goto done;
23423a6997eSAbdelkader Boudih }
23523a6997eSAbdelkader Boudih
23623a6997eSAbdelkader Boudih if (xfer->resp != 0 ||
23723a6997eSAbdelkader Boudih xfer->recv.hdr.mode.wres.rtcode != FWRCODE_COMPLETE) {
23823a6997eSAbdelkader Boudih FWDV_DEBUG(1, "FCP write failed: resp=%d rtcode=%d\n",
23923a6997eSAbdelkader Boudih xfer->resp, xfer->recv.hdr.mode.wres.rtcode);
24023a6997eSAbdelkader Boudih err = EIO;
24123a6997eSAbdelkader Boudih }
24223a6997eSAbdelkader Boudih fw_xfer_free_buf(xfer);
24323a6997eSAbdelkader Boudih if (err != 0)
24423a6997eSAbdelkader Boudih goto done;
24523a6997eSAbdelkader Boudih
24623a6997eSAbdelkader Boudih FWDV_LOCK(sc);
24723a6997eSAbdelkader Boudih for (int interim_retry = 0; interim_retry < 3; interim_retry++) {
24823a6997eSAbdelkader Boudih while (!sc->fcp_resp_ready) {
24923a6997eSAbdelkader Boudih if (sc->state == FWDV_STATE_DETACHING ||
25023a6997eSAbdelkader Boudih sc->fwdev == NULL) {
25123a6997eSAbdelkader Boudih err = ENXIO;
25223a6997eSAbdelkader Boudih goto done_locked;
25323a6997eSAbdelkader Boudih }
25423a6997eSAbdelkader Boudih err = msleep(&sc->fcp_resp_ready, &sc->mtx, PCATCH,
25523a6997eSAbdelkader Boudih "fwdvfcp", 3 * hz);
25623a6997eSAbdelkader Boudih if (err) {
25723a6997eSAbdelkader Boudih err = (err == EWOULDBLOCK) ? ETIMEDOUT : err;
25823a6997eSAbdelkader Boudih goto done_locked;
25923a6997eSAbdelkader Boudih }
26023a6997eSAbdelkader Boudih }
26123a6997eSAbdelkader Boudih
26223a6997eSAbdelkader Boudih if (sc->fcp_resp_len >= 1 &&
26323a6997eSAbdelkader Boudih (sc->fcp_resp[0] >> 4) == AVC_RESP_INTERIM) {
26423a6997eSAbdelkader Boudih FWDV_DEBUG(2, "AVC INTERIM, waiting for final\n");
26523a6997eSAbdelkader Boudih sc->fcp_resp_ready = 0;
26623a6997eSAbdelkader Boudih if (interim_retry == 2) {
26723a6997eSAbdelkader Boudih FWDV_DEBUG(1, "AVC INTERIM timeout\n");
26823a6997eSAbdelkader Boudih err = ETIMEDOUT;
26923a6997eSAbdelkader Boudih goto done_locked;
27023a6997eSAbdelkader Boudih }
27123a6997eSAbdelkader Boudih continue;
27223a6997eSAbdelkader Boudih }
27323a6997eSAbdelkader Boudih break;
27423a6997eSAbdelkader Boudih }
27523a6997eSAbdelkader Boudih
27623a6997eSAbdelkader Boudih if (resp != NULL && resp_len != NULL) {
27723a6997eSAbdelkader Boudih int copy_len = min(sc->fcp_resp_len, *resp_len);
27823a6997eSAbdelkader Boudih memcpy(resp, sc->fcp_resp, copy_len);
27923a6997eSAbdelkader Boudih *resp_len = copy_len;
28023a6997eSAbdelkader Boudih }
28123a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
28223a6997eSAbdelkader Boudih
28323a6997eSAbdelkader Boudih FWDV_DEBUG(2, "AVC resp: %02x %02x %02x %02x (len=%d)\n",
28423a6997eSAbdelkader Boudih sc->fcp_resp[0], sc->fcp_resp[1],
28523a6997eSAbdelkader Boudih sc->fcp_resp_len > 2 ? sc->fcp_resp[2] : 0,
28623a6997eSAbdelkader Boudih sc->fcp_resp_len > 3 ? sc->fcp_resp[3] : 0,
28723a6997eSAbdelkader Boudih sc->fcp_resp_len);
28823a6997eSAbdelkader Boudih
28923a6997eSAbdelkader Boudih FWDV_LOCK(sc);
29023a6997eSAbdelkader Boudih sc->avc_busy = 0;
29123a6997eSAbdelkader Boudih wakeup(&sc->avc_busy);
29223a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
29323a6997eSAbdelkader Boudih
29423a6997eSAbdelkader Boudih return (0);
29523a6997eSAbdelkader Boudih
29623a6997eSAbdelkader Boudih done_locked:
29723a6997eSAbdelkader Boudih sc->avc_busy = 0;
29823a6997eSAbdelkader Boudih wakeup(&sc->avc_busy);
29923a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
30023a6997eSAbdelkader Boudih return (err);
30123a6997eSAbdelkader Boudih
30223a6997eSAbdelkader Boudih done:
30323a6997eSAbdelkader Boudih FWDV_LOCK(sc);
30423a6997eSAbdelkader Boudih sc->avc_busy = 0;
30523a6997eSAbdelkader Boudih wakeup(&sc->avc_busy);
30623a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
30723a6997eSAbdelkader Boudih return (err);
30823a6997eSAbdelkader Boudih }
30923a6997eSAbdelkader Boudih
31023a6997eSAbdelkader Boudih static int
fwdv_avc_unit_info(struct fwdv_softc * sc)31123a6997eSAbdelkader Boudih fwdv_avc_unit_info(struct fwdv_softc *sc)
31223a6997eSAbdelkader Boudih {
31323a6997eSAbdelkader Boudih uint8_t cmd[8], resp[8];
31423a6997eSAbdelkader Boudih int resp_len, err;
31523a6997eSAbdelkader Boudih
31623a6997eSAbdelkader Boudih cmd[0] = (AVC_CTYPE_STATUS << 4) | 0;
31723a6997eSAbdelkader Boudih cmd[1] = AVC_ADDR_UNIT;
31823a6997eSAbdelkader Boudih cmd[2] = AVC_OP_UNIT_INFO;
31923a6997eSAbdelkader Boudih cmd[3] = 0xff;
32023a6997eSAbdelkader Boudih cmd[4] = 0xff;
32123a6997eSAbdelkader Boudih cmd[5] = 0xff;
32223a6997eSAbdelkader Boudih cmd[6] = 0xff;
32323a6997eSAbdelkader Boudih cmd[7] = 0xff;
32423a6997eSAbdelkader Boudih
32523a6997eSAbdelkader Boudih resp_len = sizeof(resp);
32623a6997eSAbdelkader Boudih err = fwdv_avc_command(sc, cmd, 8, resp, &resp_len);
32723a6997eSAbdelkader Boudih if (err)
32823a6997eSAbdelkader Boudih return (err);
32923a6997eSAbdelkader Boudih
33023a6997eSAbdelkader Boudih if (resp_len < 8) {
33123a6997eSAbdelkader Boudih device_printf(sc->fd.dev,
33223a6997eSAbdelkader Boudih "UNIT INFO: short response (%d bytes)\n", resp_len);
33323a6997eSAbdelkader Boudih return (EIO);
33423a6997eSAbdelkader Boudih }
33523a6997eSAbdelkader Boudih if ((resp[0] >> 4) != AVC_RESP_STABLE) {
33623a6997eSAbdelkader Boudih device_printf(sc->fd.dev,
33723a6997eSAbdelkader Boudih "UNIT INFO: unexpected response 0x%02x\n", resp[0]);
33823a6997eSAbdelkader Boudih return (EIO);
33923a6997eSAbdelkader Boudih }
34023a6997eSAbdelkader Boudih
34123a6997eSAbdelkader Boudih FWDV_DEBUG(1, "UNIT INFO: unit_type=0x%02x, company_id=%02x%02x%02x\n",
34223a6997eSAbdelkader Boudih resp[4] >> 3, resp[5], resp[6], resp[7]);
34323a6997eSAbdelkader Boudih
34423a6997eSAbdelkader Boudih return (0);
34523a6997eSAbdelkader Boudih }
34623a6997eSAbdelkader Boudih
34723a6997eSAbdelkader Boudih static int
fwdv_avc_tape_control(struct fwdv_softc * sc,uint8_t opcode,uint8_t operand,const char * name)34823a6997eSAbdelkader Boudih fwdv_avc_tape_control(struct fwdv_softc *sc, uint8_t opcode, uint8_t operand,
34923a6997eSAbdelkader Boudih const char *name)
35023a6997eSAbdelkader Boudih {
35123a6997eSAbdelkader Boudih uint8_t cmd[4], resp[4];
35223a6997eSAbdelkader Boudih int resp_len, err;
35323a6997eSAbdelkader Boudih
35423a6997eSAbdelkader Boudih cmd[0] = (AVC_CTYPE_CONTROL << 4) | 0;
35523a6997eSAbdelkader Boudih cmd[1] = AVC_ADDR_TAPE0;
35623a6997eSAbdelkader Boudih cmd[2] = opcode;
35723a6997eSAbdelkader Boudih cmd[3] = operand;
35823a6997eSAbdelkader Boudih
35923a6997eSAbdelkader Boudih resp_len = sizeof(resp);
36023a6997eSAbdelkader Boudih err = fwdv_avc_command(sc, cmd, 4, resp, &resp_len);
36123a6997eSAbdelkader Boudih if (err)
36223a6997eSAbdelkader Boudih return (err);
36323a6997eSAbdelkader Boudih if (resp_len < 1)
36423a6997eSAbdelkader Boudih return (EIO);
36523a6997eSAbdelkader Boudih
36623a6997eSAbdelkader Boudih if ((resp[0] >> 4) != AVC_RESP_ACCEPTED) {
36723a6997eSAbdelkader Boudih device_printf(sc->fd.dev, "%s rejected: 0x%02x\n",
36823a6997eSAbdelkader Boudih name, resp[0]);
36923a6997eSAbdelkader Boudih return (EIO);
37023a6997eSAbdelkader Boudih }
37123a6997eSAbdelkader Boudih
37223a6997eSAbdelkader Boudih FWDV_DEBUG(1, "%s accepted\n", name);
37323a6997eSAbdelkader Boudih return (0);
37423a6997eSAbdelkader Boudih }
37523a6997eSAbdelkader Boudih
37623a6997eSAbdelkader Boudih static int
fwdv_avc_play(struct fwdv_softc * sc)37723a6997eSAbdelkader Boudih fwdv_avc_play(struct fwdv_softc *sc)
37823a6997eSAbdelkader Boudih {
37923a6997eSAbdelkader Boudih
38023a6997eSAbdelkader Boudih return (fwdv_avc_tape_control(sc, AVC_OP_PLAY, AVC_PLAY_FWD, "PLAY"));
38123a6997eSAbdelkader Boudih }
38223a6997eSAbdelkader Boudih
38323a6997eSAbdelkader Boudih static int
fwdv_avc_stop(struct fwdv_softc * sc)38423a6997eSAbdelkader Boudih fwdv_avc_stop(struct fwdv_softc *sc)
38523a6997eSAbdelkader Boudih {
38623a6997eSAbdelkader Boudih
38723a6997eSAbdelkader Boudih return (fwdv_avc_tape_control(sc, AVC_OP_WIND, AVC_WIND_STOP, "STOP"));
38823a6997eSAbdelkader Boudih }
38923a6997eSAbdelkader Boudih
39023a6997eSAbdelkader Boudih static int
fwdv_read_opcr(struct fwdv_softc * sc,int plug,uint32_t * val)39123a6997eSAbdelkader Boudih fwdv_read_opcr(struct fwdv_softc *sc, int plug, uint32_t *val)
39223a6997eSAbdelkader Boudih {
39323a6997eSAbdelkader Boudih
39423a6997eSAbdelkader Boudih return (fwdv_read_quadlet(sc, CSR_BASE_HI,
39523a6997eSAbdelkader Boudih CSR_BASE_LO | PCR_oPCR(plug), val));
39623a6997eSAbdelkader Boudih }
39723a6997eSAbdelkader Boudih
39823a6997eSAbdelkader Boudih static int
fwdv_iso_start(struct fwdv_softc * sc)39923a6997eSAbdelkader Boudih fwdv_iso_start(struct fwdv_softc *sc)
40023a6997eSAbdelkader Boudih {
40123a6997eSAbdelkader Boudih struct firewire_comm *fc = sc->fd.fc;
40223a6997eSAbdelkader Boudih struct fw_xferq *xferq;
40323a6997eSAbdelkader Boudih uint32_t opcr_val;
40423a6997eSAbdelkader Boudih int dma_ch, err;
40523a6997eSAbdelkader Boudih uint8_t ch;
40623a6997eSAbdelkader Boudih
40723a6997eSAbdelkader Boudih mtx_assert(&sc->mtx, MA_NOTOWNED);
40823a6997eSAbdelkader Boudih
40923a6997eSAbdelkader Boudih FWDV_LOCK(sc);
41023a6997eSAbdelkader Boudih if (sc->dma_ch >= 0 || sc->state == FWDV_STATE_STREAMING) {
41123a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
41223a6997eSAbdelkader Boudih return (0);
41323a6997eSAbdelkader Boudih }
41423a6997eSAbdelkader Boudih if (sc->state == FWDV_STATE_DETACHING) {
41523a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
41623a6997eSAbdelkader Boudih return (ENXIO);
41723a6997eSAbdelkader Boudih }
41823a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
41923a6997eSAbdelkader Boudih
42023a6997eSAbdelkader Boudih ch = (uint8_t)(iso_channel & ISO_CHANNEL_MASK);
42123a6997eSAbdelkader Boudih err = fwdv_read_opcr(sc, 0, &opcr_val);
42223a6997eSAbdelkader Boudih if (err == 0 && (opcr_val & OPCR_ONLINE)) {
42323a6997eSAbdelkader Boudih uint8_t dev_ch = (opcr_val & OPCR_CHANNEL_MASK) >>
42423a6997eSAbdelkader Boudih OPCR_CHANNEL_SHIFT;
42523a6997eSAbdelkader Boudih if (dev_ch < ISO_CHANNEL_MAX) {
42623a6997eSAbdelkader Boudih ch = dev_ch;
42723a6997eSAbdelkader Boudih FWDV_DEBUG(1, "oPCR[0] channel=%d\n", ch);
42823a6997eSAbdelkader Boudih }
42923a6997eSAbdelkader Boudih }
43023a6997eSAbdelkader Boudih
43123a6997eSAbdelkader Boudih dma_ch = fw_open_isodma(fc, 0);
43223a6997eSAbdelkader Boudih if (dma_ch < 0) {
43323a6997eSAbdelkader Boudih device_printf(sc->fd.dev, "no IR DMA channel available\n");
43423a6997eSAbdelkader Boudih return (EBUSY);
43523a6997eSAbdelkader Boudih }
43623a6997eSAbdelkader Boudih
43723a6997eSAbdelkader Boudih FWDV_LOCK(sc);
43823a6997eSAbdelkader Boudih if (sc->dma_ch >= 0) {
43923a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
44023a6997eSAbdelkader Boudih fc->ir[dma_ch]->flag &= ~FWXFERQ_OPEN;
44123a6997eSAbdelkader Boudih return (0);
44223a6997eSAbdelkader Boudih }
44323a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
44423a6997eSAbdelkader Boudih
44523a6997eSAbdelkader Boudih xferq = fc->ir[dma_ch];
44623a6997eSAbdelkader Boudih xferq->flag |= FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_STREAM;
44723a6997eSAbdelkader Boudih sc->iso_channel = ch;
44823a6997eSAbdelkader Boudih xferq->flag &= ~FWXFERQ_CHTAGMASK;
44923a6997eSAbdelkader Boudih xferq->flag |= (ch & ISO_CHANNEL_MASK) | (1 << 6);
45023a6997eSAbdelkader Boudih
45123a6997eSAbdelkader Boudih xferq->sc = (caddr_t)sc;
45223a6997eSAbdelkader Boudih xferq->hand = fwdv_iso_input;
45323a6997eSAbdelkader Boudih xferq->bnchunk = FWDV_ISO_NCHUNK;
45423a6997eSAbdelkader Boudih xferq->bnpacket = 1;
45523a6997eSAbdelkader Boudih xferq->psize = FWDV_ISO_PKTSIZE;
45623a6997eSAbdelkader Boudih xferq->queued = 0;
45723a6997eSAbdelkader Boudih xferq->buf = NULL;
45823a6997eSAbdelkader Boudih
45923a6997eSAbdelkader Boudih xferq->bulkxfer = malloc(sizeof(struct fw_bulkxfer) * xferq->bnchunk,
46023a6997eSAbdelkader Boudih M_FWDV, M_WAITOK | M_ZERO);
46123a6997eSAbdelkader Boudih
46223a6997eSAbdelkader Boudih fw_iso_init_chunks(xferq);
46323a6997eSAbdelkader Boudih
46423a6997eSAbdelkader Boudih sc->frame_size = FWDV_MAX_FRAME_SIZE;
46523a6997eSAbdelkader Boudih sc->frame_buf = malloc(sc->frame_size, M_FWDV, M_WAITOK | M_ZERO);
46623a6997eSAbdelkader Boudih sc->read_buf = malloc(sc->frame_size, M_FWDV, M_WAITOK | M_ZERO);
46723a6997eSAbdelkader Boudih sc->frame_offset = 0;
46823a6997eSAbdelkader Boudih sc->frame_ready = 0;
46923a6997eSAbdelkader Boudih sc->frame_dropped = 0;
47023a6997eSAbdelkader Boudih sc->frame_count = 0;
47123a6997eSAbdelkader Boudih sc->dv_detected = 0;
47223a6997eSAbdelkader Boudih
47323a6997eSAbdelkader Boudih err = fc->irx_enable(fc, dma_ch);
47423a6997eSAbdelkader Boudih if (err) {
47523a6997eSAbdelkader Boudih device_printf(sc->fd.dev, "failed to start IR DMA: %d\n", err);
47623a6997eSAbdelkader Boudih goto fail;
47723a6997eSAbdelkader Boudih }
47823a6997eSAbdelkader Boudih
47923a6997eSAbdelkader Boudih FWDV_LOCK(sc);
48023a6997eSAbdelkader Boudih if (sc->state == FWDV_STATE_DETACHING || sc->fwdev == NULL) {
48123a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
48223a6997eSAbdelkader Boudih fc->irx_disable(fc, dma_ch);
48323a6997eSAbdelkader Boudih err = ENXIO;
48423a6997eSAbdelkader Boudih goto fail;
48523a6997eSAbdelkader Boudih }
48623a6997eSAbdelkader Boudih sc->dma_ch = dma_ch;
48723a6997eSAbdelkader Boudih sc->state = FWDV_STATE_STREAMING;
48823a6997eSAbdelkader Boudih wakeup(sc);
48923a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
49023a6997eSAbdelkader Boudih
49123a6997eSAbdelkader Boudih return (0);
49223a6997eSAbdelkader Boudih
49323a6997eSAbdelkader Boudih fail:
49423a6997eSAbdelkader Boudih fw_iso_free_chunks(xferq, M_FWDV);
49523a6997eSAbdelkader Boudih xferq->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_OPEN | FWXFERQ_STREAM |
49623a6997eSAbdelkader Boudih FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_CHTAGMASK);
49723a6997eSAbdelkader Boudih xferq->hand = NULL;
49823a6997eSAbdelkader Boudih
49923a6997eSAbdelkader Boudih free(sc->frame_buf, M_FWDV);
50023a6997eSAbdelkader Boudih free(sc->read_buf, M_FWDV);
50123a6997eSAbdelkader Boudih sc->frame_buf = NULL;
50223a6997eSAbdelkader Boudih sc->read_buf = NULL;
50323a6997eSAbdelkader Boudih sc->dma_ch = -1;
50423a6997eSAbdelkader Boudih
50523a6997eSAbdelkader Boudih return (err);
50623a6997eSAbdelkader Boudih }
50723a6997eSAbdelkader Boudih
50823a6997eSAbdelkader Boudih static void
fwdv_iso_stop(struct fwdv_softc * sc)50923a6997eSAbdelkader Boudih fwdv_iso_stop(struct fwdv_softc *sc)
51023a6997eSAbdelkader Boudih {
51123a6997eSAbdelkader Boudih struct firewire_comm *fc = sc->fd.fc;
51223a6997eSAbdelkader Boudih struct fw_xferq *xferq;
51323a6997eSAbdelkader Boudih int dma_ch;
51423a6997eSAbdelkader Boudih
51523a6997eSAbdelkader Boudih FWDV_LOCK(sc);
51623a6997eSAbdelkader Boudih dma_ch = sc->dma_ch;
51723a6997eSAbdelkader Boudih if (dma_ch < 0) {
51823a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
51923a6997eSAbdelkader Boudih return;
52023a6997eSAbdelkader Boudih }
52123a6997eSAbdelkader Boudih sc->dma_ch = -1;
52223a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
52323a6997eSAbdelkader Boudih
52423a6997eSAbdelkader Boudih xferq = fc->ir[dma_ch];
52523a6997eSAbdelkader Boudih
52623a6997eSAbdelkader Boudih if (xferq->flag & FWXFERQ_RUNNING)
52723a6997eSAbdelkader Boudih fc->irx_disable(fc, dma_ch);
52823a6997eSAbdelkader Boudih
52923a6997eSAbdelkader Boudih FWDV_LOCK(sc);
53023a6997eSAbdelkader Boudih fw_iso_wait_inactive_locked(&sc->mtx, &sc->iso_active, "fwdvis");
53123a6997eSAbdelkader Boudih sc->frame_ready = 0;
53223a6997eSAbdelkader Boudih while (sc->read_in_progress)
53323a6997eSAbdelkader Boudih msleep(&sc->read_in_progress, &sc->mtx, PWAIT, "fwdvst", hz);
53423a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
53523a6997eSAbdelkader Boudih
53623a6997eSAbdelkader Boudih xferq->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_OPEN | FWXFERQ_STREAM |
53723a6997eSAbdelkader Boudih FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_CHTAGMASK);
53823a6997eSAbdelkader Boudih xferq->hand = NULL;
53923a6997eSAbdelkader Boudih
54023a6997eSAbdelkader Boudih fw_iso_free_chunks(xferq, M_FWDV);
54123a6997eSAbdelkader Boudih
54223a6997eSAbdelkader Boudih free(sc->frame_buf, M_FWDV);
54323a6997eSAbdelkader Boudih free(sc->read_buf, M_FWDV);
54423a6997eSAbdelkader Boudih sc->frame_buf = NULL;
54523a6997eSAbdelkader Boudih sc->read_buf = NULL;
54623a6997eSAbdelkader Boudih }
54723a6997eSAbdelkader Boudih
54823a6997eSAbdelkader Boudih static void
fwdv_iso_input(struct fw_xferq * xferq)54923a6997eSAbdelkader Boudih fwdv_iso_input(struct fw_xferq *xferq)
55023a6997eSAbdelkader Boudih {
55123a6997eSAbdelkader Boudih struct fwdv_softc *sc = (struct fwdv_softc *)xferq->sc;
55223a6997eSAbdelkader Boudih struct fw_bulkxfer *sxfer;
55323a6997eSAbdelkader Boudih struct fw_pkt *fp;
55423a6997eSAbdelkader Boudih struct ciphdr *ciph;
55523a6997eSAbdelkader Boudih struct dvdbc *dv;
55623a6997eSAbdelkader Boudih struct mbuf *m;
55723a6997eSAbdelkader Boudih uint8_t *ptr, *end;
55823a6997eSAbdelkader Boudih uint32_t plen;
55923a6997eSAbdelkader Boudih uint8_t *tmp;
56023a6997eSAbdelkader Boudih int dma_ch;
56123a6997eSAbdelkader Boudih
56223a6997eSAbdelkader Boudih FWDV_LOCK(sc);
56323a6997eSAbdelkader Boudih dma_ch = sc->dma_ch;
56423a6997eSAbdelkader Boudih if (dma_ch < 0 || sc->frame_buf == NULL) {
56523a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
56623a6997eSAbdelkader Boudih return;
56723a6997eSAbdelkader Boudih }
56823a6997eSAbdelkader Boudih sc->iso_active = 1;
56923a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
57023a6997eSAbdelkader Boudih
57123a6997eSAbdelkader Boudih while ((sxfer = STAILQ_FIRST(&xferq->stvalid)) != NULL) {
57223a6997eSAbdelkader Boudih STAILQ_REMOVE_HEAD(&xferq->stvalid, link);
57323a6997eSAbdelkader Boudih
57423a6997eSAbdelkader Boudih m = fw_iso_dequeue(xferq, sxfer, sc->fd.fc);
57523a6997eSAbdelkader Boudih if (m == NULL)
57623a6997eSAbdelkader Boudih continue;
57723a6997eSAbdelkader Boudih
57823a6997eSAbdelkader Boudih fp = mtod(m, struct fw_pkt *);
57923a6997eSAbdelkader Boudih plen = fp->mode.stream.len;
58023a6997eSAbdelkader Boudih if (plen <= sizeof(struct ciphdr)) {
58123a6997eSAbdelkader Boudih m_freem(m);
58223a6997eSAbdelkader Boudih continue;
58323a6997eSAbdelkader Boudih }
58423a6997eSAbdelkader Boudih
58523a6997eSAbdelkader Boudih if (m->m_len < (int)(4 + sizeof(struct ciphdr))) {
58623a6997eSAbdelkader Boudih m_freem(m);
58723a6997eSAbdelkader Boudih continue;
58823a6997eSAbdelkader Boudih }
58923a6997eSAbdelkader Boudih
59023a6997eSAbdelkader Boudih ciph = (struct ciphdr *)(mtod(m, uint8_t *) + 4);
59123a6997eSAbdelkader Boudih
59223a6997eSAbdelkader Boudih if (ciph->fmt != CIP_FMT_DVCR) {
59323a6997eSAbdelkader Boudih m_freem(m);
59423a6997eSAbdelkader Boudih continue;
59523a6997eSAbdelkader Boudih }
59623a6997eSAbdelkader Boudih
59723a6997eSAbdelkader Boudih if (!sc->dv_detected) {
59823a6997eSAbdelkader Boudih sc->dv_system = ciph->fdf.dv.fs;
59923a6997eSAbdelkader Boudih sc->dv_detected = 1;
60023a6997eSAbdelkader Boudih if (ciph->fdf.dv.stype == CIP_STYPE_HD) {
60123a6997eSAbdelkader Boudih sc->frame_size = DV_DVCPRO_HD_FRAME;
60223a6997eSAbdelkader Boudih } else {
60323a6997eSAbdelkader Boudih sc->frame_size = sc->dv_system ?
60423a6997eSAbdelkader Boudih DV_SD_PAL_FRAME : DV_SD_NTSC_FRAME;
60523a6997eSAbdelkader Boudih }
60623a6997eSAbdelkader Boudih printf("fwdv: DV stype=%d %s detected (frame=%u)\n",
60723a6997eSAbdelkader Boudih ciph->fdf.dv.stype,
60823a6997eSAbdelkader Boudih sc->dv_system ? "PAL" : "NTSC",
60923a6997eSAbdelkader Boudih sc->frame_size);
61023a6997eSAbdelkader Boudih }
61123a6997eSAbdelkader Boudih
61223a6997eSAbdelkader Boudih ptr = (uint8_t *)(ciph + 1);
61323a6997eSAbdelkader Boudih end = mtod(m, uint8_t *) + 4 + plen;
61423a6997eSAbdelkader Boudih if (end > mtod(m, uint8_t *) + m->m_len)
61523a6997eSAbdelkader Boudih end = mtod(m, uint8_t *) + m->m_len;
61623a6997eSAbdelkader Boudih
61723a6997eSAbdelkader Boudih while (ptr + DV_DIF_BLOCK_SIZE <= end) {
61823a6997eSAbdelkader Boudih dv = (struct dvdbc *)ptr;
61923a6997eSAbdelkader Boudih
62023a6997eSAbdelkader Boudih if (dv->sct == DV_SCT_HEADER && dv->dseq == 0) {
62123a6997eSAbdelkader Boudih if (sc->frame_offset > 0) {
62223a6997eSAbdelkader Boudih if (sc->frame_offset >= sc->frame_size) {
62323a6997eSAbdelkader Boudih FWDV_LOCK(sc);
62423a6997eSAbdelkader Boudih if (sc->read_in_progress) {
62523a6997eSAbdelkader Boudih sc->frame_dropped++;
62623a6997eSAbdelkader Boudih } else {
62723a6997eSAbdelkader Boudih if (sc->frame_ready)
62823a6997eSAbdelkader Boudih sc->frame_dropped++;
62923a6997eSAbdelkader Boudih tmp = sc->read_buf;
63023a6997eSAbdelkader Boudih sc->read_buf =
63123a6997eSAbdelkader Boudih sc->frame_buf;
63223a6997eSAbdelkader Boudih sc->frame_buf = tmp;
63323a6997eSAbdelkader Boudih sc->frame_ready = 1;
63423a6997eSAbdelkader Boudih sc->frame_count++;
63523a6997eSAbdelkader Boudih wakeup(sc);
63623a6997eSAbdelkader Boudih selwakeup(&sc->rsel);
63723a6997eSAbdelkader Boudih }
63823a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
63923a6997eSAbdelkader Boudih } else {
64023a6997eSAbdelkader Boudih sc->frame_dropped++;
64123a6997eSAbdelkader Boudih }
64223a6997eSAbdelkader Boudih }
64323a6997eSAbdelkader Boudih sc->frame_offset = 0;
64423a6997eSAbdelkader Boudih
64523a6997eSAbdelkader Boudih if (sc->dv_system == 1 &&
64623a6997eSAbdelkader Boudih (dv->payload[0] & DV_DSF_12) == 0)
64723a6997eSAbdelkader Boudih dv->payload[0] |= DV_DSF_12;
64823a6997eSAbdelkader Boudih }
64923a6997eSAbdelkader Boudih
65023a6997eSAbdelkader Boudih if (sc->frame_offset + DV_DIF_BLOCK_SIZE <=
65123a6997eSAbdelkader Boudih sc->frame_size) {
65223a6997eSAbdelkader Boudih memcpy(sc->frame_buf + sc->frame_offset,
65323a6997eSAbdelkader Boudih ptr, DV_DIF_BLOCK_SIZE);
65423a6997eSAbdelkader Boudih sc->frame_offset += DV_DIF_BLOCK_SIZE;
65523a6997eSAbdelkader Boudih } else {
65623a6997eSAbdelkader Boudih sc->frame_dropped++;
65723a6997eSAbdelkader Boudih sc->frame_offset = 0;
65823a6997eSAbdelkader Boudih }
65923a6997eSAbdelkader Boudih
66023a6997eSAbdelkader Boudih ptr += DV_DIF_BLOCK_SIZE;
66123a6997eSAbdelkader Boudih }
66223a6997eSAbdelkader Boudih
66323a6997eSAbdelkader Boudih m_freem(m);
66423a6997eSAbdelkader Boudih }
66523a6997eSAbdelkader Boudih
66623a6997eSAbdelkader Boudih fw_iso_rearm_done(xferq, sc->fd.fc, &sc->mtx, &sc->iso_active,
66723a6997eSAbdelkader Boudih &sc->dma_ch, dma_ch);
66823a6997eSAbdelkader Boudih }
66923a6997eSAbdelkader Boudih
67023a6997eSAbdelkader Boudih static int
fwdv_cdev_open(struct cdev * dev,int oflags,int devtype,struct thread * td)67123a6997eSAbdelkader Boudih fwdv_cdev_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
67223a6997eSAbdelkader Boudih {
67323a6997eSAbdelkader Boudih struct fwdv_softc *sc = dev->si_drv1;
67423a6997eSAbdelkader Boudih int err = 0;
67523a6997eSAbdelkader Boudih
67623a6997eSAbdelkader Boudih FWDV_LOCK(sc);
67723a6997eSAbdelkader Boudih if (sc->state == FWDV_STATE_DETACHING) {
67823a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
67923a6997eSAbdelkader Boudih return (ENXIO);
68023a6997eSAbdelkader Boudih }
68123a6997eSAbdelkader Boudih while (sc->state == FWDV_STATE_STARTING) {
68223a6997eSAbdelkader Boudih err = msleep(sc, &sc->mtx, PCATCH, "fwdvst", 5 * hz);
68323a6997eSAbdelkader Boudih if (err) {
68423a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
68523a6997eSAbdelkader Boudih return (err == EWOULDBLOCK ? EAGAIN : err);
68623a6997eSAbdelkader Boudih }
68723a6997eSAbdelkader Boudih }
68823a6997eSAbdelkader Boudih if (sc->state == FWDV_STATE_DETACHING) {
68923a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
69023a6997eSAbdelkader Boudih return (ENXIO);
69123a6997eSAbdelkader Boudih }
69223a6997eSAbdelkader Boudih if (sc->state != FWDV_STATE_PROBED &&
69323a6997eSAbdelkader Boudih sc->state != FWDV_STATE_STREAMING) {
69423a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
69523a6997eSAbdelkader Boudih return (EAGAIN);
69623a6997eSAbdelkader Boudih }
69723a6997eSAbdelkader Boudih
69823a6997eSAbdelkader Boudih sc->open_count++;
69923a6997eSAbdelkader Boudih if (sc->open_count == 1 && sc->state == FWDV_STATE_PROBED) {
70023a6997eSAbdelkader Boudih sc->state = FWDV_STATE_STARTING;
70123a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
70223a6997eSAbdelkader Boudih err = fwdv_iso_start(sc);
70323a6997eSAbdelkader Boudih if (err) {
70423a6997eSAbdelkader Boudih FWDV_LOCK(sc);
70523a6997eSAbdelkader Boudih sc->open_count--;
70623a6997eSAbdelkader Boudih if (sc->state == FWDV_STATE_STARTING) {
70723a6997eSAbdelkader Boudih sc->state = FWDV_STATE_PROBED;
70823a6997eSAbdelkader Boudih wakeup(sc);
70923a6997eSAbdelkader Boudih }
71023a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
71123a6997eSAbdelkader Boudih }
71223a6997eSAbdelkader Boudih } else {
71323a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
71423a6997eSAbdelkader Boudih }
71523a6997eSAbdelkader Boudih return (err);
71623a6997eSAbdelkader Boudih }
71723a6997eSAbdelkader Boudih
71823a6997eSAbdelkader Boudih static int
fwdv_cdev_close(struct cdev * dev,int fflag,int devtype,struct thread * td)71923a6997eSAbdelkader Boudih fwdv_cdev_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
72023a6997eSAbdelkader Boudih {
72123a6997eSAbdelkader Boudih struct fwdv_softc *sc = dev->si_drv1;
72223a6997eSAbdelkader Boudih
72323a6997eSAbdelkader Boudih FWDV_LOCK(sc);
72423a6997eSAbdelkader Boudih sc->open_count--;
72523a6997eSAbdelkader Boudih if (sc->open_count <= 0) {
72623a6997eSAbdelkader Boudih sc->open_count = 0;
72723a6997eSAbdelkader Boudih if (sc->state == FWDV_STATE_STREAMING) {
72823a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
72923a6997eSAbdelkader Boudih fwdv_iso_stop(sc);
73023a6997eSAbdelkader Boudih FWDV_LOCK(sc);
73123a6997eSAbdelkader Boudih if (sc->state != FWDV_STATE_DETACHING)
73223a6997eSAbdelkader Boudih sc->state = FWDV_STATE_PROBED;
73323a6997eSAbdelkader Boudih }
73423a6997eSAbdelkader Boudih }
73523a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
73623a6997eSAbdelkader Boudih return (0);
73723a6997eSAbdelkader Boudih }
73823a6997eSAbdelkader Boudih
73923a6997eSAbdelkader Boudih static int
fwdv_cdev_read(struct cdev * dev,struct uio * uio,int ioflag)74023a6997eSAbdelkader Boudih fwdv_cdev_read(struct cdev *dev, struct uio *uio, int ioflag)
74123a6997eSAbdelkader Boudih {
74223a6997eSAbdelkader Boudih struct fwdv_softc *sc = dev->si_drv1;
74323a6997eSAbdelkader Boudih int err;
74423a6997eSAbdelkader Boudih
74523a6997eSAbdelkader Boudih FWDV_LOCK(sc);
74623a6997eSAbdelkader Boudih while (!sc->frame_ready) {
74723a6997eSAbdelkader Boudih if (sc->state != FWDV_STATE_STREAMING) {
74823a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
74923a6997eSAbdelkader Boudih return (ENXIO);
75023a6997eSAbdelkader Boudih }
75123a6997eSAbdelkader Boudih if (ioflag & FNONBLOCK) {
75223a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
75323a6997eSAbdelkader Boudih return (EAGAIN);
75423a6997eSAbdelkader Boudih }
75523a6997eSAbdelkader Boudih err = msleep(sc, &sc->mtx, PCATCH, "fwdvrd", 0);
75623a6997eSAbdelkader Boudih if (err) {
75723a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
75823a6997eSAbdelkader Boudih return (err);
75923a6997eSAbdelkader Boudih }
76023a6997eSAbdelkader Boudih }
76123a6997eSAbdelkader Boudih
76223a6997eSAbdelkader Boudih sc->read_in_progress = 1;
76323a6997eSAbdelkader Boudih sc->frame_ready = 0;
76423a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
76523a6997eSAbdelkader Boudih
76623a6997eSAbdelkader Boudih err = uiomove(sc->read_buf, min(uio->uio_resid, sc->frame_size),
76723a6997eSAbdelkader Boudih uio);
76823a6997eSAbdelkader Boudih
76923a6997eSAbdelkader Boudih FWDV_LOCK(sc);
77023a6997eSAbdelkader Boudih sc->read_in_progress = 0;
77123a6997eSAbdelkader Boudih wakeup(&sc->read_in_progress);
77223a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
77323a6997eSAbdelkader Boudih
77423a6997eSAbdelkader Boudih return (err);
77523a6997eSAbdelkader Boudih }
77623a6997eSAbdelkader Boudih
77723a6997eSAbdelkader Boudih static int
fwdv_cdev_poll(struct cdev * dev,int events,struct thread * td)77823a6997eSAbdelkader Boudih fwdv_cdev_poll(struct cdev *dev, int events, struct thread *td)
77923a6997eSAbdelkader Boudih {
78023a6997eSAbdelkader Boudih struct fwdv_softc *sc = dev->si_drv1;
78123a6997eSAbdelkader Boudih int revents = 0;
78223a6997eSAbdelkader Boudih
78323a6997eSAbdelkader Boudih FWDV_LOCK(sc);
78423a6997eSAbdelkader Boudih if (sc->state != FWDV_STATE_STREAMING) {
78523a6997eSAbdelkader Boudih revents = POLLHUP;
78623a6997eSAbdelkader Boudih } else if (events & (POLLIN | POLLRDNORM)) {
78723a6997eSAbdelkader Boudih if (sc->frame_ready)
78823a6997eSAbdelkader Boudih revents |= events & (POLLIN | POLLRDNORM);
78923a6997eSAbdelkader Boudih else
79023a6997eSAbdelkader Boudih selrecord(td, &sc->rsel);
79123a6997eSAbdelkader Boudih }
79223a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
79323a6997eSAbdelkader Boudih
79423a6997eSAbdelkader Boudih return (revents);
79523a6997eSAbdelkader Boudih }
79623a6997eSAbdelkader Boudih
79723a6997eSAbdelkader Boudih static int
fwdv_cdev_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int fflag,struct thread * td)79823a6997eSAbdelkader Boudih fwdv_cdev_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
79923a6997eSAbdelkader Boudih struct thread *td)
80023a6997eSAbdelkader Boudih {
80123a6997eSAbdelkader Boudih struct fwdv_softc *sc = dev->si_drv1;
80223a6997eSAbdelkader Boudih struct fwdv_info *info;
80323a6997eSAbdelkader Boudih
80423a6997eSAbdelkader Boudih switch (cmd) {
80523a6997eSAbdelkader Boudih case FWDV_GINFO:
80623a6997eSAbdelkader Boudih info = (struct fwdv_info *)data;
80723a6997eSAbdelkader Boudih memset(info, 0, sizeof(*info));
80823a6997eSAbdelkader Boudih FWDV_LOCK(sc);
80923a6997eSAbdelkader Boudih info->state = sc->state;
81023a6997eSAbdelkader Boudih info->iso_channel = sc->iso_channel;
81123a6997eSAbdelkader Boudih info->dv_system = sc->dv_detected ? sc->dv_system : 0xff;
81223a6997eSAbdelkader Boudih info->frame_size = sc->frame_size;
81323a6997eSAbdelkader Boudih info->frame_count = sc->frame_count;
81423a6997eSAbdelkader Boudih info->frame_dropped = sc->frame_dropped;
81523a6997eSAbdelkader Boudih info->eui_hi = sc->eui_hi;
81623a6997eSAbdelkader Boudih info->eui_lo = sc->eui_lo;
81723a6997eSAbdelkader Boudih strlcpy(info->vendor, sc->vendor, sizeof(info->vendor));
81823a6997eSAbdelkader Boudih strlcpy(info->model, sc->model, sizeof(info->model));
81923a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
82023a6997eSAbdelkader Boudih return (0);
82123a6997eSAbdelkader Boudih
82223a6997eSAbdelkader Boudih case FWDV_PLAY:
82323a6997eSAbdelkader Boudih return (fwdv_avc_play(sc));
82423a6997eSAbdelkader Boudih
82523a6997eSAbdelkader Boudih case FWDV_STOP:
82623a6997eSAbdelkader Boudih return (fwdv_avc_stop(sc));
82723a6997eSAbdelkader Boudih
82823a6997eSAbdelkader Boudih case FWDV_FFWD:
82923a6997eSAbdelkader Boudih return (fwdv_avc_tape_control(sc, AVC_OP_WIND,
83023a6997eSAbdelkader Boudih AVC_WIND_FFWD, "FFWD"));
83123a6997eSAbdelkader Boudih
83223a6997eSAbdelkader Boudih case FWDV_REW:
83323a6997eSAbdelkader Boudih return (fwdv_avc_tape_control(sc, AVC_OP_WIND,
83423a6997eSAbdelkader Boudih AVC_WIND_REW, "REW"));
83523a6997eSAbdelkader Boudih
83623a6997eSAbdelkader Boudih default:
83723a6997eSAbdelkader Boudih return (ENOTTY);
83823a6997eSAbdelkader Boudih }
83923a6997eSAbdelkader Boudih }
84023a6997eSAbdelkader Boudih
84123a6997eSAbdelkader Boudih static void
fwdv_parse_identity(struct fwdv_softc * sc)84223a6997eSAbdelkader Boudih fwdv_parse_identity(struct fwdv_softc *sc)
84323a6997eSAbdelkader Boudih {
84423a6997eSAbdelkader Boudih struct fw_device *fwdev = sc->fwdev;
84523a6997eSAbdelkader Boudih struct crom_context cc;
84623a6997eSAbdelkader Boudih
84723a6997eSAbdelkader Boudih sc->vendor[0] = '\0';
84823a6997eSAbdelkader Boudih sc->model[0] = '\0';
84923a6997eSAbdelkader Boudih
85023a6997eSAbdelkader Boudih if (fwdev == NULL)
85123a6997eSAbdelkader Boudih return;
85223a6997eSAbdelkader Boudih
85323a6997eSAbdelkader Boudih crom_init_context(&cc, fwdev->csrrom);
85423a6997eSAbdelkader Boudih
85523a6997eSAbdelkader Boudih crom_search_key(&cc, CSRKEY_VENDOR);
85623a6997eSAbdelkader Boudih crom_next(&cc);
85723a6997eSAbdelkader Boudih crom_parse_text(&cc, sc->vendor, sizeof(sc->vendor));
85823a6997eSAbdelkader Boudih
85923a6997eSAbdelkader Boudih crom_search_key(&cc, CSRKEY_MODEL);
86023a6997eSAbdelkader Boudih crom_next(&cc);
86123a6997eSAbdelkader Boudih crom_parse_text(&cc, sc->model, sizeof(sc->model));
86223a6997eSAbdelkader Boudih
86323a6997eSAbdelkader Boudih }
86423a6997eSAbdelkader Boudih
86523a6997eSAbdelkader Boudih
86623a6997eSAbdelkader Boudih static void
fwdv_probe_task(void * arg,int pending __unused)86723a6997eSAbdelkader Boudih fwdv_probe_task(void *arg, int pending __unused)
86823a6997eSAbdelkader Boudih {
86923a6997eSAbdelkader Boudih struct fwdv_softc *sc = (struct fwdv_softc *)arg;
87023a6997eSAbdelkader Boudih int err, restart;
87123a6997eSAbdelkader Boudih
87223a6997eSAbdelkader Boudih FWDV_LOCK(sc);
87323a6997eSAbdelkader Boudih if (sc->state == FWDV_STATE_DETACHING || sc->fwdev == NULL) {
87423a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
87523a6997eSAbdelkader Boudih return;
87623a6997eSAbdelkader Boudih }
87723a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
87823a6997eSAbdelkader Boudih
87923a6997eSAbdelkader Boudih fwdv_parse_identity(sc);
88023a6997eSAbdelkader Boudih err = fwdv_avc_unit_info(sc);
88123a6997eSAbdelkader Boudih if (err) {
88223a6997eSAbdelkader Boudih device_printf(sc->fd.dev,
88323a6997eSAbdelkader Boudih "UNIT INFO failed (%d), device may not be ready\n", err);
88423a6997eSAbdelkader Boudih }
88523a6997eSAbdelkader Boudih
88623a6997eSAbdelkader Boudih FWDV_LOCK(sc);
88723a6997eSAbdelkader Boudih if (sc->state == FWDV_STATE_DETACHING || sc->fwdev == NULL) {
88823a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
88923a6997eSAbdelkader Boudih return;
89023a6997eSAbdelkader Boudih }
89123a6997eSAbdelkader Boudih sc->state = FWDV_STATE_PROBED;
89223a6997eSAbdelkader Boudih restart = (sc->open_count > 0);
89323a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
89423a6997eSAbdelkader Boudih
89523a6997eSAbdelkader Boudih if (restart)
89623a6997eSAbdelkader Boudih fwdv_iso_start(sc);
89723a6997eSAbdelkader Boudih }
89823a6997eSAbdelkader Boudih
89923a6997eSAbdelkader Boudih
90023a6997eSAbdelkader Boudih
90123a6997eSAbdelkader Boudih static int
fwdv_probe(device_t dev)90223a6997eSAbdelkader Boudih fwdv_probe(device_t dev)
90323a6997eSAbdelkader Boudih {
904*2b9ed8daSAbdelkader Boudih struct fw_unit *unit;
905*2b9ed8daSAbdelkader Boudih
906*2b9ed8daSAbdelkader Boudih unit = fw_get_unit(dev);
907*2b9ed8daSAbdelkader Boudih if (unit == NULL)
908*2b9ed8daSAbdelkader Boudih return (ENXIO);
909*2b9ed8daSAbdelkader Boudih
910*2b9ed8daSAbdelkader Boudih if (unit->spec_id != CSRVAL_1394TA)
911*2b9ed8daSAbdelkader Boudih return (ENXIO);
912*2b9ed8daSAbdelkader Boudih if (unit->sw_version != CSR_PROTAVC)
913*2b9ed8daSAbdelkader Boudih return (ENXIO);
91423a6997eSAbdelkader Boudih
91523a6997eSAbdelkader Boudih device_set_desc(dev, "AV/C DV Capture over FireWire");
916*2b9ed8daSAbdelkader Boudih return (BUS_PROBE_DEFAULT);
91723a6997eSAbdelkader Boudih }
91823a6997eSAbdelkader Boudih
91923a6997eSAbdelkader Boudih static int
fwdv_attach(device_t dev)92023a6997eSAbdelkader Boudih fwdv_attach(device_t dev)
92123a6997eSAbdelkader Boudih {
92223a6997eSAbdelkader Boudih struct fwdv_softc *sc;
92323a6997eSAbdelkader Boudih struct firewire_comm *fc;
924*2b9ed8daSAbdelkader Boudih struct fw_unit *unit;
925*2b9ed8daSAbdelkader Boudih struct fw_device *fwdev;
92623a6997eSAbdelkader Boudih int err;
92723a6997eSAbdelkader Boudih
928*2b9ed8daSAbdelkader Boudih unit = fw_get_unit(dev);
929*2b9ed8daSAbdelkader Boudih if (unit == NULL)
930*2b9ed8daSAbdelkader Boudih return (ENXIO);
931*2b9ed8daSAbdelkader Boudih fwdev = unit->fwdev;
932*2b9ed8daSAbdelkader Boudih if (fwdev == NULL)
933*2b9ed8daSAbdelkader Boudih return (ENXIO);
934*2b9ed8daSAbdelkader Boudih
93523a6997eSAbdelkader Boudih sc = device_get_softc(dev);
93623a6997eSAbdelkader Boudih sc->fd.dev = dev;
937*2b9ed8daSAbdelkader Boudih sc->fd.fc = fw_get_comm(dev);
93823a6997eSAbdelkader Boudih fc = sc->fd.fc;
93923a6997eSAbdelkader Boudih mtx_init(&sc->mtx, "fwdv", NULL, MTX_DEF);
94023a6997eSAbdelkader Boudih
941*2b9ed8daSAbdelkader Boudih sc->fwdev = fwdev;
942*2b9ed8daSAbdelkader Boudih sc->eui_hi = fwdev->eui.hi;
943*2b9ed8daSAbdelkader Boudih sc->eui_lo = fwdev->eui.lo;
94423a6997eSAbdelkader Boudih sc->state = FWDV_STATE_IDLE;
94523a6997eSAbdelkader Boudih sc->dma_ch = -1;
94623a6997eSAbdelkader Boudih sc->open_count = 0;
94723a6997eSAbdelkader Boudih sc->dv_system = 0;
94823a6997eSAbdelkader Boudih sc->dv_detected = 0;
94923a6997eSAbdelkader Boudih sc->avc_busy = 0;
95023a6997eSAbdelkader Boudih sc->iso_active = 0;
95123a6997eSAbdelkader Boudih knlist_init_mtx(&sc->rsel.si_note, &sc->mtx);
95223a6997eSAbdelkader Boudih TASK_INIT(&sc->probe_task, 0, fwdv_probe_task, sc);
95323a6997eSAbdelkader Boudih
95423a6997eSAbdelkader Boudih sc->cdev = make_dev(&fwdv_cdevsw, device_get_unit(dev),
95523a6997eSAbdelkader Boudih UID_ROOT, GID_OPERATOR, 0660, "fwdv%d", device_get_unit(dev));
95623a6997eSAbdelkader Boudih if (sc->cdev == NULL) {
95723a6997eSAbdelkader Boudih device_printf(dev, "make_dev failed\n");
95823a6997eSAbdelkader Boudih knlist_destroy(&sc->rsel.si_note);
95923a6997eSAbdelkader Boudih mtx_destroy(&sc->mtx);
96023a6997eSAbdelkader Boudih return (ENXIO);
96123a6997eSAbdelkader Boudih }
96223a6997eSAbdelkader Boudih sc->cdev->si_drv1 = sc;
96323a6997eSAbdelkader Boudih
96423a6997eSAbdelkader Boudih STAILQ_INIT(&sc->fcp_bind.xferlist);
96523a6997eSAbdelkader Boudih sc->fcp_bind.start = FCP_RESPONSE_ADDR;
96623a6997eSAbdelkader Boudih sc->fcp_bind.end = FCP_RESPONSE_ADDR + FCP_MAX_FRAME_LEN - 1;
96723a6997eSAbdelkader Boudih sc->fcp_bind.sc = sc;
96823a6997eSAbdelkader Boudih if (fw_xferlist_add(&sc->fcp_bind.xferlist, M_FWDV,
96923a6997eSAbdelkader Boudih 0, FCP_MAX_FRAME_LEN, FWDV_FCP_XFERS, fc, sc,
97023a6997eSAbdelkader Boudih fwdv_fcp_handler) < FWDV_FCP_XFERS) {
97123a6997eSAbdelkader Boudih device_printf(dev, "fw_xferlist_add: allocation failed\n");
97223a6997eSAbdelkader Boudih fw_xferlist_remove(&sc->fcp_bind.xferlist);
97323a6997eSAbdelkader Boudih destroy_dev(sc->cdev);
97423a6997eSAbdelkader Boudih knlist_destroy(&sc->rsel.si_note);
97523a6997eSAbdelkader Boudih mtx_destroy(&sc->mtx);
97623a6997eSAbdelkader Boudih return (ENOMEM);
97723a6997eSAbdelkader Boudih }
97823a6997eSAbdelkader Boudih
97923a6997eSAbdelkader Boudih err = fw_bindadd(fc, &sc->fcp_bind);
98023a6997eSAbdelkader Boudih if (err) {
98123a6997eSAbdelkader Boudih device_printf(dev, "fw_bindadd failed: %d\n", err);
98223a6997eSAbdelkader Boudih fw_xferlist_remove(&sc->fcp_bind.xferlist);
98323a6997eSAbdelkader Boudih destroy_dev(sc->cdev);
98423a6997eSAbdelkader Boudih knlist_destroy(&sc->rsel.si_note);
98523a6997eSAbdelkader Boudih mtx_destroy(&sc->mtx);
98623a6997eSAbdelkader Boudih return (err);
98723a6997eSAbdelkader Boudih }
98823a6997eSAbdelkader Boudih
989*2b9ed8daSAbdelkader Boudih if (taskqueue_enqueue(taskqueue_thread, &sc->probe_task) != 0)
990*2b9ed8daSAbdelkader Boudih device_printf(dev, "probe task enqueue failed\n");
99123a6997eSAbdelkader Boudih
99223a6997eSAbdelkader Boudih return (0);
99323a6997eSAbdelkader Boudih }
99423a6997eSAbdelkader Boudih
99523a6997eSAbdelkader Boudih static int
fwdv_detach(device_t dev)99623a6997eSAbdelkader Boudih fwdv_detach(device_t dev)
99723a6997eSAbdelkader Boudih {
99823a6997eSAbdelkader Boudih struct fwdv_softc *sc;
99923a6997eSAbdelkader Boudih
100023a6997eSAbdelkader Boudih sc = device_get_softc(dev);
100123a6997eSAbdelkader Boudih
100223a6997eSAbdelkader Boudih FWDV_LOCK(sc);
100323a6997eSAbdelkader Boudih sc->state = FWDV_STATE_DETACHING;
100423a6997eSAbdelkader Boudih wakeup(sc);
100523a6997eSAbdelkader Boudih wakeup(&sc->fcp_resp_ready);
100623a6997eSAbdelkader Boudih wakeup(&sc->avc_busy);
100723a6997eSAbdelkader Boudih FWDV_UNLOCK(sc);
100823a6997eSAbdelkader Boudih
100923a6997eSAbdelkader Boudih taskqueue_drain(taskqueue_thread, &sc->probe_task);
101023a6997eSAbdelkader Boudih fwdv_iso_stop(sc);
101123a6997eSAbdelkader Boudih
101223a6997eSAbdelkader Boudih fw_bindremove(sc->fd.fc, &sc->fcp_bind);
101323a6997eSAbdelkader Boudih fw_xferlist_remove(&sc->fcp_bind.xferlist);
101423a6997eSAbdelkader Boudih
101523a6997eSAbdelkader Boudih if (sc->cdev != NULL)
101623a6997eSAbdelkader Boudih destroy_dev(sc->cdev);
101723a6997eSAbdelkader Boudih
101823a6997eSAbdelkader Boudih seldrain(&sc->rsel);
101923a6997eSAbdelkader Boudih knlist_destroy(&sc->rsel.si_note);
102023a6997eSAbdelkader Boudih mtx_destroy(&sc->mtx);
102123a6997eSAbdelkader Boudih
102223a6997eSAbdelkader Boudih return (0);
102323a6997eSAbdelkader Boudih }
102423a6997eSAbdelkader Boudih
102523a6997eSAbdelkader Boudih static device_method_t fwdv_methods[] = {
102623a6997eSAbdelkader Boudih DEVMETHOD(device_probe, fwdv_probe),
102723a6997eSAbdelkader Boudih DEVMETHOD(device_attach, fwdv_attach),
102823a6997eSAbdelkader Boudih DEVMETHOD(device_detach, fwdv_detach),
102923a6997eSAbdelkader Boudih DEVMETHOD_END
103023a6997eSAbdelkader Boudih };
103123a6997eSAbdelkader Boudih
103223a6997eSAbdelkader Boudih static driver_t fwdv_driver = {
103323a6997eSAbdelkader Boudih "fwdv",
103423a6997eSAbdelkader Boudih fwdv_methods,
103523a6997eSAbdelkader Boudih sizeof(struct fwdv_softc)
103623a6997eSAbdelkader Boudih };
103723a6997eSAbdelkader Boudih
103823a6997eSAbdelkader Boudih DRIVER_MODULE(fwdv, firewire, fwdv_driver, NULL, NULL);
103923a6997eSAbdelkader Boudih MODULE_VERSION(fwdv, 1);
104023a6997eSAbdelkader Boudih MODULE_DEPEND(fwdv, firewire, 1, 1, 1);
1041