Home | History | Annotate | Line # | Download | only in ieee1394
fwmem.c revision 1.9
      1 /*	$NetBSD: fwmem.c,v 1.9 2008/11/12 12:36:11 ad Exp $	*/
      2 /*-
      3  * Copyright (c) 2002-2003
      4  * 	Hidetoshi Shimokawa. All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. All advertising materials mentioning features or use of this software
     15  *    must display the following acknowledgement:
     16  *
     17  *	This product includes software developed by Hidetoshi Shimokawa.
     18  *
     19  * 4. Neither the name of the author nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  *
     35  */
     36 
     37 #include <sys/cdefs.h>
     38 __KERNEL_RCSID(0, "$NetBSD: fwmem.c,v 1.9 2008/11/12 12:36:11 ad Exp $");
     39 #if defined(__FreeBSD__)
     40 __FBSDID("$FreeBSD: src/sys/dev/firewire/fwmem.c,v 1.34 2007/06/06 14:31:36 simokawa Exp $");
     41 #endif
     42 
     43 #if defined(__FreeBSD__)
     44 #include <sys/param.h>
     45 #include <sys/systm.h>
     46 #include <sys/types.h>
     47 
     48 #include <sys/kernel.h>
     49 #include <sys/malloc.h>
     50 #include <sys/conf.h>
     51 #include <sys/sysctl.h>
     52 #if defined(__DragonFly__) || __FreeBSD_version < 500000
     53 #include <sys/buf.h>
     54 #else
     55 #include <sys/bio.h>
     56 #endif
     57 
     58 #include <sys/bus.h>
     59 #include <sys/bus.h>
     60 
     61 #include <sys/signal.h>
     62 #include <sys/mman.h>
     63 #include <sys/ioccom.h>
     64 #include <sys/fcntl.h>
     65 #include <sys/ktr.h>
     66 
     67 #ifdef __DragonFly__
     68 #include "fw_port.h"
     69 #include "firewire.h"
     70 #include "firewirereg.h"
     71 #include "fwmem.h"
     72 #else
     73 #include <dev/firewire/fw_port.h>
     74 #include <dev/firewire/firewire.h>
     75 #include <dev/firewire/firewirereg.h>
     76 #include <dev/firewire/fwmem.h>
     77 #endif
     78 #elif defined(__NetBSD__)
     79 #include <sys/param.h>
     80 #include <sys/device.h>
     81 #include <sys/errno.h>
     82 #include <sys/buf.h>
     83 #include <sys/conf.h>
     84 #include <sys/fcntl.h>
     85 #include <sys/malloc.h>
     86 #include <sys/sysctl.h>
     87 
     88 #include <sys/bus.h>
     89 
     90 #include <dev/ieee1394/fw_port.h>
     91 #include <dev/ieee1394/firewire.h>
     92 #include <dev/ieee1394/firewirereg.h>
     93 #include <dev/ieee1394/fwmem.h>
     94 #endif
     95 
     96 static int fwmem_speed=2, fwmem_debug=0;
     97 static struct fw_eui64 fwmem_eui64;
     98 #if defined(__FreeBSD__)
     99 SYSCTL_DECL(_hw_firewire);
    100 SYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem, CTLFLAG_RD, 0,
    101 	"FireWire Memory Access");
    102 SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_hi, CTLFLAG_RW,
    103 	&fwmem_eui64.hi, 0, "Fwmem target EUI64 high");
    104 SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_lo, CTLFLAG_RW,
    105 	&fwmem_eui64.lo, 0, "Fwmem target EUI64 low");
    106 SYSCTL_INT(_hw_firewire_fwmem, OID_AUTO, speed, CTLFLAG_RW, &fwmem_speed, 0,
    107 	"Fwmem link speed");
    108 SYSCTL_INT(_debug, OID_AUTO, fwmem_debug, CTLFLAG_RW, &fwmem_debug, 0,
    109 	"Fwmem driver debug flag");
    110 MALLOC_DEFINE(M_FWMEM, "fwmem", "fwmem/FireWire");
    111 #elif defined(__NetBSD__)
    112 static int sysctl_fwmem_verify(SYSCTLFN_PROTO, int, int);
    113 static int sysctl_fwmem_verify_speed(SYSCTLFN_PROTO);
    114 
    115 /*
    116  * Setup sysctl(3) MIB, hw.fwmem.*
    117  *
    118  * TBD condition CTLFLAG_PERMANENT on being a module or not
    119  */
    120 SYSCTL_SETUP(sysctl_fwmem, "sysctl fwmem subtree setup")
    121 {
    122 	int rc, fwmem_node_num;
    123 	const struct sysctlnode *node;
    124 
    125 	if ((rc = sysctl_createv(clog, 0, NULL, NULL,
    126 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL,
    127 	    NULL, 0, NULL, 0, CTL_HW, CTL_EOL)) != 0) {
    128 		goto err;
    129 	}
    130 
    131 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    132 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "fwmem",
    133 	    SYSCTL_DESCR("IEEE1394 Memory Access"),
    134 	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) {
    135 		goto err;
    136 	}
    137 	fwmem_node_num = node->sysctl_num;
    138 
    139 	/* fwmem target EUI64 high/low */
    140 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    141 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
    142 	    "eui64_hi", SYSCTL_DESCR("Fwmem target EUI64 high"),
    143 	    NULL, 0, &fwmem_eui64.hi,
    144 	    0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) {
    145 		goto err;
    146 	}
    147 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    148 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
    149 	    "eui64_lo", SYSCTL_DESCR("Fwmem target EUI64 low"),
    150 	    NULL, 0, &fwmem_eui64.lo,
    151 	    0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) {
    152 		goto err;
    153 	}
    154 
    155 	/* fwmem link speed */
    156 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    157 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
    158 	    "speed", SYSCTL_DESCR("Fwmem link speed"),
    159 	    sysctl_fwmem_verify_speed, 0, &fwmem_speed,
    160 	    0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) {
    161 		goto err;
    162 	}
    163 
    164 	/* fwmem driver debug flag */
    165 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    166 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
    167 	    "fwmem_debug", SYSCTL_DESCR("Fwmem driver debug flag"),
    168 	    NULL, 0, &fwmem_debug,
    169 	    0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) {
    170 		goto err;
    171 	}
    172 
    173 	return;
    174 
    175 err:
    176 	printf("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
    177 }
    178 
    179 static int
    180 sysctl_fwmem_verify(SYSCTLFN_ARGS, int lower, int upper)
    181 {
    182 	int error, t;
    183 	struct sysctlnode node;
    184 
    185 	node = *rnode;
    186 	t = *(int*)rnode->sysctl_data;
    187 	node.sysctl_data = &t;
    188 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    189 	if (error || newp == NULL)
    190 		return (error);
    191 
    192 	if (t < lower || t > upper)
    193 		return (EINVAL);
    194 
    195 	*(int*)rnode->sysctl_data = t;
    196 
    197 	return (0);
    198 }
    199 
    200 static int
    201 sysctl_fwmem_verify_speed(SYSCTLFN_ARGS)
    202 {
    203 	return (sysctl_fwmem_verify(SYSCTLFN_CALL(rnode), 0, FWSPD_S400));
    204 }
    205 
    206 MALLOC_DEFINE(M_FWMEM, "fwmem", "fwmem/IEEE1394");
    207 #endif
    208 
    209 #define MAXLEN (512 << fwmem_speed)
    210 
    211 struct fwmem_softc {
    212 	struct fw_eui64 eui;
    213 	struct firewire_softc *sc;
    214 	int refcount;
    215 	STAILQ_HEAD(, fw_xfer) xferlist;
    216 };
    217 
    218 static struct fw_xfer *
    219 fwmem_xfer_req(
    220 	struct fw_device *fwdev,
    221 	void *sc,
    222 	int spd,
    223 	int slen,
    224 	int rlen,
    225 	void *hand)
    226 {
    227 	struct fw_xfer *xfer;
    228 
    229 	xfer = fw_xfer_alloc(M_FWMEM);
    230 	if (xfer == NULL)
    231 		return NULL;
    232 
    233 	xfer->fc = fwdev->fc;
    234 	xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst;
    235 	if (spd < 0)
    236 		xfer->send.spd = fwdev->speed;
    237 	else
    238 		xfer->send.spd = min(spd, fwdev->speed);
    239 	xfer->hand = hand;
    240 	xfer->sc = sc;
    241 	xfer->send.pay_len = slen;
    242 	xfer->recv.pay_len = rlen;
    243 
    244 	return xfer;
    245 }
    246 
    247 struct fw_xfer *
    248 fwmem_read_quad(
    249 	struct fw_device *fwdev,
    250 	void *	sc,
    251 	uint8_t spd,
    252 	uint16_t dst_hi,
    253 	uint32_t dst_lo,
    254 	void *data,
    255 	void (*hand)(struct fw_xfer *))
    256 {
    257 	struct fw_xfer *xfer;
    258 	struct fw_pkt *fp;
    259 
    260 	xfer = fwmem_xfer_req(fwdev, (void *)sc, spd, 0, 4, hand);
    261 	if (xfer == NULL) {
    262 		return NULL;
    263 	}
    264 
    265 	fp = &xfer->send.hdr;
    266 	fp->mode.rreqq.tcode = FWTCODE_RREQQ;
    267 	fp->mode.rreqq.dest_hi = dst_hi;
    268 	fp->mode.rreqq.dest_lo = dst_lo;
    269 
    270 	xfer->send.payload = NULL;
    271 	xfer->recv.payload = (uint32_t *)data;
    272 
    273 	if (fwmem_debug)
    274 		printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst,
    275 				dst_hi, dst_lo);
    276 
    277 	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
    278 		return xfer;
    279 
    280 	fw_xfer_free(xfer);
    281 	return NULL;
    282 }
    283 
    284 struct fw_xfer *
    285 fwmem_write_quad(
    286 	struct fw_device *fwdev,
    287 	void *	sc,
    288 	uint8_t spd,
    289 	uint16_t dst_hi,
    290 	uint32_t dst_lo,
    291 	void *data,
    292 	void (*hand)(struct fw_xfer *))
    293 {
    294 	struct fw_xfer *xfer;
    295 	struct fw_pkt *fp;
    296 
    297 	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand);
    298 	if (xfer == NULL)
    299 		return NULL;
    300 
    301 	fp = &xfer->send.hdr;
    302 	fp->mode.wreqq.tcode = FWTCODE_WREQQ;
    303 	fp->mode.wreqq.dest_hi = dst_hi;
    304 	fp->mode.wreqq.dest_lo = dst_lo;
    305 	fp->mode.wreqq.data = *(uint32_t *)data;
    306 
    307 	xfer->send.payload = xfer->recv.payload = NULL;
    308 
    309 	if (fwmem_debug)
    310 		printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst,
    311 			dst_hi, dst_lo, *(uint32_t *)data);
    312 
    313 	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
    314 		return xfer;
    315 
    316 	fw_xfer_free(xfer);
    317 	return NULL;
    318 }
    319 
    320 struct fw_xfer *
    321 fwmem_read_block(
    322 	struct fw_device *fwdev,
    323 	void *	sc,
    324 	uint8_t spd,
    325 	uint16_t dst_hi,
    326 	uint32_t dst_lo,
    327 	int len,
    328 	void *data,
    329 	void (*hand)(struct fw_xfer *))
    330 {
    331 	struct fw_xfer *xfer;
    332 	struct fw_pkt *fp;
    333 
    334 	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand);
    335 	if (xfer == NULL)
    336 		return NULL;
    337 
    338 	fp = &xfer->send.hdr;
    339 	fp->mode.rreqb.tcode = FWTCODE_RREQB;
    340 	fp->mode.rreqb.dest_hi = dst_hi;
    341 	fp->mode.rreqb.dest_lo = dst_lo;
    342 	fp->mode.rreqb.len = len;
    343 	fp->mode.rreqb.extcode = 0;
    344 
    345 	xfer->send.payload = NULL;
    346 	xfer->recv.payload = data;
    347 
    348 	if (fwmem_debug)
    349 		printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst,
    350 				dst_hi, dst_lo, len);
    351 	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
    352 		return xfer;
    353 
    354 	fw_xfer_free(xfer);
    355 	return NULL;
    356 }
    357 
    358 struct fw_xfer *
    359 fwmem_write_block(
    360 	struct fw_device *fwdev,
    361 	void *	sc,
    362 	uint8_t spd,
    363 	uint16_t dst_hi,
    364 	uint32_t dst_lo,
    365 	int len,
    366 	void *data,
    367 	void (*hand)(struct fw_xfer *))
    368 {
    369 	struct fw_xfer *xfer;
    370 	struct fw_pkt *fp;
    371 
    372 	xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand);
    373 	if (xfer == NULL)
    374 		return NULL;
    375 
    376 	fp = &xfer->send.hdr;
    377 	fp->mode.wreqb.tcode = FWTCODE_WREQB;
    378 	fp->mode.wreqb.dest_hi = dst_hi;
    379 	fp->mode.wreqb.dest_lo = dst_lo;
    380 	fp->mode.wreqb.len = len;
    381 	fp->mode.wreqb.extcode = 0;
    382 
    383 	xfer->send.payload = data;
    384 	xfer->recv.payload = NULL;
    385 
    386 	if (fwmem_debug)
    387 		printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst,
    388 				dst_hi, dst_lo, len);
    389 	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
    390 		return xfer;
    391 
    392 	fw_xfer_free(xfer);
    393 	return NULL;
    394 }
    395 
    396 
    397 FW_OPEN(fwmem)
    398 {
    399 	struct fwmem_softc *fms;
    400 	struct fw_xfer *xfer;
    401 	FW_OPEN_START;
    402 
    403 	if (dev->si_drv1 != NULL) {
    404 		if ((flags & FWRITE) != 0) {
    405 			FW_GUNLOCK(sc->fc);
    406 			return (EBUSY);
    407 		}
    408 		FW_GUNLOCK(sc->fc);
    409 		fms = (struct fwmem_softc *)dev->si_drv1;
    410 		fms->refcount ++;
    411 	} else {
    412 		dev->si_drv1 = (void *)-1;
    413 		FW_GUNLOCK(sc->fc);
    414 		dev->si_drv1 = malloc(sizeof(struct fwmem_softc),
    415 		    M_FWMEM, M_WAITOK);
    416 		if (dev->si_drv1 == NULL)
    417 			return ENOMEM;
    418 		dev->si_iosize_max = DFLTPHYS;
    419 		fms = (struct fwmem_softc *)dev->si_drv1;
    420 		bcopy(&fwmem_eui64, &fms->eui, sizeof(struct fw_eui64));
    421 		fms->sc = sc;
    422 		fms->refcount = 1;
    423 		STAILQ_INIT(&fms->xferlist);
    424 		xfer = fw_xfer_alloc(M_FWMEM);
    425 		STAILQ_INSERT_TAIL(&fms->xferlist, xfer, link);
    426 	}
    427 	if (fwmem_debug)
    428 		printf("%s: refcount=%d\n", __func__, fms->refcount);
    429 
    430 	return (0);
    431 }
    432 
    433 FW_CLOSE(fwmem)
    434 {
    435 	struct fwmem_softc *fms;
    436 	struct fw_xfer *xfer;
    437 	FW_CLOSE_START;
    438 
    439 	fms = (struct fwmem_softc *)dev->si_drv1;
    440 
    441 	FW_GLOCK(fms->sc->fc);
    442 	fms->refcount --;
    443 	FW_GUNLOCK(fms->sc->fc);
    444 	if (fwmem_debug)
    445 		printf("%s: refcount=%d\n", __func__, fms->refcount);
    446 	if (fms->refcount < 1) {
    447 		while ((xfer = STAILQ_FIRST(&fms->xferlist)) != NULL) {
    448 			STAILQ_REMOVE_HEAD(&fms->xferlist, link);
    449 			fw_xfer_free(xfer);
    450 		}
    451 		free(dev->si_drv1, M_FW);
    452 		dev->si_drv1 = NULL;
    453 	}
    454 
    455 	return (0);
    456 }
    457 
    458 
    459 static void
    460 fwmem_biodone(struct fw_xfer *xfer)
    461 {
    462 	struct bio *bp;
    463 
    464 	bp = (struct bio *)xfer->sc;
    465 	bp->bio_error = xfer->resp;
    466 
    467 	if (bp->bio_error != 0) {
    468 		if (fwmem_debug)
    469 			printf("%s: err=%d\n", __func__, bp->bio_error);
    470 		bp->bio_resid = bp->bio_bcount;
    471 	}
    472 
    473 	CTR0(KTR_DEV, "biodone0");
    474 	fw_xfer_free(xfer);
    475 	CTR0(KTR_DEV, "biodone1");
    476 	biodone(bp);
    477 	CTR0(KTR_DEV, "biodone2");
    478 }
    479 
    480 void
    481 fwmem_strategy(struct bio *bp)
    482 {
    483 	FW_STRATEGY_START;
    484 	struct fwmem_softc *fms;
    485 	struct fw_device *fwdev;
    486 	struct fw_xfer *xfer;
    487 	int err = 0, s, iolen;
    488 
    489 	CTR0(KTR_DEV, "strategy");
    490 
    491 	/* XXX check request length */
    492 
    493 	s = splfw();
    494 	fms = (struct fwmem_softc *)dev->si_drv1;
    495 	fwdev = fw_noderesolve_eui64(fms->sc->fc, &fms->eui);
    496 	if (fwdev == NULL) {
    497 		if (fwmem_debug)
    498 			printf("fwmem: no such device ID:%08x%08x\n",
    499 					fms->eui.hi, fms->eui.lo);
    500 		err = EINVAL;
    501 		goto error;
    502 	}
    503 
    504 	iolen = MIN(bp->bio_bcount, MAXLEN);
    505 	if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
    506 		if (iolen == 4 && (bp->bio_offset & 3) == 0)
    507 			xfer = fwmem_read_quad(fwdev,
    508 			    (void *) bp, fwmem_speed,
    509 			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
    510 			    bp->bio_data, fwmem_biodone);
    511 		else
    512 			xfer = fwmem_read_block(fwdev,
    513 			    (void *) bp, fwmem_speed,
    514 			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
    515 			    iolen, bp->bio_data, fwmem_biodone);
    516 	} else {
    517 		if (iolen == 4 && (bp->bio_offset & 3) == 0)
    518 			xfer = fwmem_write_quad(fwdev,
    519 			    (void *)bp, fwmem_speed,
    520 			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
    521 			    bp->bio_data, fwmem_biodone);
    522 		else
    523 			xfer = fwmem_write_block(fwdev,
    524 			    (void *)bp, fwmem_speed,
    525 			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
    526 			    iolen, bp->bio_data, fwmem_biodone);
    527 	}
    528 	if (xfer == NULL) {
    529 		err = EIO;
    530 		goto error;
    531 	}
    532 	/* XXX */
    533 	bp->bio_resid = bp->bio_bcount - iolen;
    534 error:
    535 	splx(s);
    536 	if (err != 0) {
    537 		if (fwmem_debug)
    538 			printf("%s: err=%d\n", __func__, err);
    539 		bp->bio_error = err;
    540 		bp->bio_resid = bp->bio_bcount;
    541 		biodone(bp);
    542 	}
    543 }
    544 
    545 FW_IOCTL(fwmem)
    546 {
    547 	FW_IOCTL_START;
    548 	struct fwmem_softc *fms;
    549 	int err = 0;
    550 
    551 	fms = (struct fwmem_softc *)dev->si_drv1;
    552 	switch (cmd) {
    553 	case FW_SDEUI64:
    554 		bcopy(data, &fms->eui, sizeof(struct fw_eui64));
    555 		break;
    556 	case FW_GDEUI64:
    557 		bcopy(&fms->eui, data, sizeof(struct fw_eui64));
    558 		break;
    559 	default:
    560 		err = EINVAL;
    561 	}
    562 	return(err);
    563 }
    564 
    565 FW_POLL(fwmem)
    566 {
    567 	return EINVAL;
    568 }
    569 
    570 FW_MMAP(fwmem)
    571 {
    572 	return EINVAL;
    573 }
    574