Home | History | Annotate | Line # | Download | only in ieee1394
fwmem.c revision 1.12
      1 /*	$NetBSD: fwmem.c,v 1.12 2010/03/29 03:05:27 kiyohara 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.12 2010/03/29 03:05:27 kiyohara Exp $");
     39 
     40 #include <sys/param.h>
     41 #include <sys/device.h>
     42 #include <sys/errno.h>
     43 #include <sys/buf.h>
     44 #include <sys/bus.h>
     45 #include <sys/conf.h>
     46 #include <sys/fcntl.h>
     47 #include <sys/malloc.h>
     48 #include <sys/sysctl.h>
     49 
     50 #include <dev/ieee1394/firewire.h>
     51 #include <dev/ieee1394/firewirereg.h>
     52 #include <dev/ieee1394/fwmem.h>
     53 
     54 #include "ioconf.h"
     55 
     56 static int fwmem_speed=2, fwmem_debug=0;
     57 static struct fw_eui64 fwmem_eui64;
     58 
     59 static int sysctl_fwmem_verify(SYSCTLFN_PROTO, int, int);
     60 static int sysctl_fwmem_verify_speed(SYSCTLFN_PROTO);
     61 
     62 static struct fw_xfer *fwmem_xfer_req(struct fw_device *, void *, int, int,
     63 				      int, void *);
     64 static void fwmem_biodone(struct fw_xfer *);
     65 
     66 /*
     67  * Setup sysctl(3) MIB, hw.fwmem.*
     68  *
     69  * TBD condition CTLFLAG_PERMANENT on being a module or not
     70  */
     71 SYSCTL_SETUP(sysctl_fwmem, "sysctl fwmem subtree setup")
     72 {
     73 	int rc, fwmem_node_num;
     74 	const struct sysctlnode *node;
     75 
     76 	if ((rc = sysctl_createv(clog, 0, NULL, NULL,
     77 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL,
     78 	    NULL, 0, NULL, 0, CTL_HW, CTL_EOL)) != 0) {
     79 		goto err;
     80 	}
     81 
     82 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
     83 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "fwmem",
     84 	    SYSCTL_DESCR("IEEE1394 Memory Access"),
     85 	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) {
     86 		goto err;
     87 	}
     88 	fwmem_node_num = node->sysctl_num;
     89 
     90 	/* fwmem target EUI64 high/low */
     91 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
     92 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
     93 	    "eui64_hi", SYSCTL_DESCR("Fwmem target EUI64 high"),
     94 	    NULL, 0, &fwmem_eui64.hi,
     95 	    0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) {
     96 		goto err;
     97 	}
     98 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
     99 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
    100 	    "eui64_lo", SYSCTL_DESCR("Fwmem target EUI64 low"),
    101 	    NULL, 0, &fwmem_eui64.lo,
    102 	    0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) {
    103 		goto err;
    104 	}
    105 
    106 	/* fwmem link speed */
    107 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    108 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
    109 	    "speed", SYSCTL_DESCR("Fwmem link speed"),
    110 	    sysctl_fwmem_verify_speed, 0, &fwmem_speed,
    111 	    0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) {
    112 		goto err;
    113 	}
    114 
    115 	/* fwmem driver debug flag */
    116 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
    117 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
    118 	    "fwmem_debug", SYSCTL_DESCR("Fwmem driver debug flag"),
    119 	    NULL, 0, &fwmem_debug,
    120 	    0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) {
    121 		goto err;
    122 	}
    123 
    124 	return;
    125 
    126 err:
    127 	printf("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
    128 }
    129 
    130 static int
    131 sysctl_fwmem_verify(SYSCTLFN_ARGS, int lower, int upper)
    132 {
    133 	int error, t;
    134 	struct sysctlnode node;
    135 
    136 	node = *rnode;
    137 	t = *(int*)rnode->sysctl_data;
    138 	node.sysctl_data = &t;
    139 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    140 	if (error || newp == NULL)
    141 		return error;
    142 
    143 	if (t < lower || t > upper)
    144 		return EINVAL;
    145 
    146 	*(int*)rnode->sysctl_data = t;
    147 
    148 	return 0;
    149 }
    150 
    151 static int
    152 sysctl_fwmem_verify_speed(SYSCTLFN_ARGS)
    153 {
    154 
    155 	return sysctl_fwmem_verify(SYSCTLFN_CALL(rnode), 0, FWSPD_S400);
    156 }
    157 
    158 MALLOC_DEFINE(M_FWMEM, "fwmem", "fwmem/IEEE1394");
    159 
    160 #define MAXLEN (512 << fwmem_speed)
    161 
    162 struct fwmem_softc {
    163 	struct fw_eui64 eui;
    164 	struct firewire_softc *sc;
    165 	int refcount;
    166 	STAILQ_HEAD(, fw_xfer) xferlist;
    167 };
    168 
    169 
    170 int
    171 fwmem_open(dev_t dev, int flags, int fmt, struct lwp *td)
    172 {
    173 	struct firewire_softc *sc;
    174 	struct fwmem_softc *fms;
    175 	struct fw_xfer *xfer;
    176 
    177 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
    178 	if (sc == NULL)
    179 		return ENXIO;
    180 
    181 	if (sc->si_drv1 != NULL) {
    182 		if ((flags & FWRITE) != 0) {
    183 			return EBUSY;
    184 		}
    185 		fms = (struct fwmem_softc *)sc->si_drv1;
    186 		fms->refcount++;
    187 	} else {
    188 		sc->si_drv1 = (void *)-1;
    189 		sc->si_drv1 = malloc(sizeof(struct fwmem_softc),
    190 		    M_FWMEM, M_WAITOK);
    191 		if (sc->si_drv1 == NULL)
    192 			return ENOMEM;
    193 		fms = (struct fwmem_softc *)sc->si_drv1;
    194 		memcpy(&fms->eui, &fwmem_eui64, sizeof(struct fw_eui64));
    195 		fms->sc = sc;
    196 		fms->refcount = 1;
    197 		STAILQ_INIT(&fms->xferlist);
    198 		xfer = fw_xfer_alloc(M_FWMEM);
    199 		STAILQ_INSERT_TAIL(&fms->xferlist, xfer, link);
    200 	}
    201 	if (fwmem_debug)
    202 		printf("%s: refcount=%d\n", __func__, fms->refcount);
    203 
    204 	return 0;
    205 }
    206 
    207 int
    208 fwmem_close(dev_t dev, int flags, int fmt, struct lwp *td)
    209 {
    210 	struct firewire_softc *sc;
    211 	struct fwmem_softc *fms;
    212 	struct fw_xfer *xfer;
    213 
    214 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
    215 	if (sc == NULL)
    216 		return ENXIO;
    217 
    218 	fms = (struct fwmem_softc *)sc->si_drv1;
    219 
    220 	fms->refcount--;
    221 	if (fwmem_debug)
    222 		printf("%s: refcount=%d\n", __func__, fms->refcount);
    223 	if (fms->refcount < 1) {
    224 		while ((xfer = STAILQ_FIRST(&fms->xferlist)) != NULL) {
    225 			STAILQ_REMOVE_HEAD(&fms->xferlist, link);
    226 			fw_xfer_free(xfer);
    227 		}
    228 		free(sc->si_drv1, M_FW);
    229 		sc->si_drv1 = NULL;
    230 	}
    231 
    232 	return 0;
    233 }
    234 
    235 void
    236 fwmem_strategy(struct bio *bp)
    237 {
    238 	struct firewire_softc *sc;
    239 	struct fwmem_softc *fms;
    240 	struct fw_device *fwdev;
    241 	struct fw_xfer *xfer;
    242 	dev_t dev = bp->bio_dev;
    243 	int iolen, err = 0, s;
    244 
    245 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
    246 	if (sc == NULL)
    247 		return;
    248 
    249 	/* XXX check request length */
    250 
    251 	s = splvm();
    252 	fms = (struct fwmem_softc *)sc->si_drv1;
    253 	fwdev = fw_noderesolve_eui64(fms->sc->fc, &fms->eui);
    254 	if (fwdev == NULL) {
    255 		if (fwmem_debug)
    256 			printf("fwmem: no such device ID:%08x%08x\n",
    257 					fms->eui.hi, fms->eui.lo);
    258 		err = EINVAL;
    259 		goto error;
    260 	}
    261 
    262 	iolen = MIN(bp->bio_bcount, MAXLEN);
    263 	if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
    264 		if (iolen == 4 && (bp->bio_offset & 3) == 0)
    265 			xfer = fwmem_read_quad(fwdev, (void *) bp, fwmem_speed,
    266 			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
    267 			    bp->bio_data, fwmem_biodone);
    268 		else
    269 			xfer = fwmem_read_block(fwdev, (void *) bp, fwmem_speed,
    270 			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
    271 			    iolen, bp->bio_data, fwmem_biodone);
    272 	} else {
    273 		if (iolen == 4 && (bp->bio_offset & 3) == 0)
    274 			xfer = fwmem_write_quad(fwdev, (void *)bp, fwmem_speed,
    275 			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
    276 			    bp->bio_data, fwmem_biodone);
    277 		else
    278 			xfer = fwmem_write_block(fwdev, (void *)bp, fwmem_speed,
    279 			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
    280 			    iolen, bp->bio_data, fwmem_biodone);
    281 	}
    282 	if (xfer == NULL) {
    283 		err = EIO;
    284 		goto error;
    285 	}
    286 	/* XXX */
    287 	bp->bio_resid = bp->bio_bcount - iolen;
    288 error:
    289 	splx(s);
    290 	if (err != 0) {
    291 		if (fwmem_debug)
    292 			printf("%s: err=%d\n", __func__, err);
    293 		bp->bio_error = err;
    294 		bp->bio_resid = bp->bio_bcount;
    295 		biodone(bp);
    296 	}
    297 }
    298 
    299 int
    300 fwmem_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *td)
    301 {
    302 	struct firewire_softc *sc;
    303 	struct fwmem_softc *fms;
    304 	int err = 0;
    305 
    306 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
    307 	if (sc == NULL)
    308 		return ENXIO;
    309 
    310 	fms = (struct fwmem_softc *)sc->si_drv1;
    311 	switch (cmd) {
    312 	case FW_SDEUI64:
    313 		memcpy(&fms->eui, data, sizeof(struct fw_eui64));
    314 		break;
    315 
    316 	case FW_GDEUI64:
    317 		memcpy(data, &fms->eui, sizeof(struct fw_eui64));
    318 		break;
    319 
    320 	default:
    321 		err = EINVAL;
    322 	}
    323 	return err;
    324 }
    325 
    326 
    327 struct fw_xfer *
    328 fwmem_read_quad(struct fw_device *fwdev, void *	sc, uint8_t spd,
    329 		uint16_t dst_hi, uint32_t dst_lo, void *data,
    330 		void (*hand)(struct fw_xfer *))
    331 {
    332 	struct fw_xfer *xfer;
    333 	struct fw_pkt *fp;
    334 
    335 	xfer = fwmem_xfer_req(fwdev, (void *)sc, spd, 0, 4, hand);
    336 	if (xfer == NULL)
    337 		return NULL;
    338 
    339 	fp = &xfer->send.hdr;
    340 	fp->mode.rreqq.tcode = FWTCODE_RREQQ;
    341 	fp->mode.rreqq.dest_hi = dst_hi;
    342 	fp->mode.rreqq.dest_lo = dst_lo;
    343 
    344 	xfer->send.payload = NULL;
    345 	xfer->recv.payload = (uint32_t *)data;
    346 
    347 	if (fwmem_debug)
    348 		aprint_error("fwmem_read_quad: %d %04x:%08x\n",
    349 		    fwdev->dst, dst_hi, dst_lo);
    350 
    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_quad(struct fw_device *fwdev, void *sc, uint8_t spd,
    360 		 uint16_t dst_hi, uint32_t dst_lo, void *data,
    361 		 void (*hand)(struct fw_xfer *))
    362 {
    363 	struct fw_xfer *xfer;
    364 	struct fw_pkt *fp;
    365 
    366 	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand);
    367 	if (xfer == NULL)
    368 		return NULL;
    369 
    370 	fp = &xfer->send.hdr;
    371 	fp->mode.wreqq.tcode = FWTCODE_WREQQ;
    372 	fp->mode.wreqq.dest_hi = dst_hi;
    373 	fp->mode.wreqq.dest_lo = dst_lo;
    374 	fp->mode.wreqq.data = *(uint32_t *)data;
    375 
    376 	xfer->send.payload = xfer->recv.payload = NULL;
    377 
    378 	if (fwmem_debug)
    379 		aprint_error("fwmem_write_quad: %d %04x:%08x %08x\n",
    380 		    fwdev->dst, dst_hi, dst_lo, *(uint32_t *)data);
    381 
    382 	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
    383 		return xfer;
    384 
    385 	fw_xfer_free(xfer);
    386 	return NULL;
    387 }
    388 
    389 struct fw_xfer *
    390 fwmem_read_block(struct fw_device *fwdev, void *sc, uint8_t spd,
    391 		 uint16_t dst_hi, uint32_t dst_lo, int len, void *data,
    392 		 void (*hand)(struct fw_xfer *))
    393 {
    394 	struct fw_xfer *xfer;
    395 	struct fw_pkt *fp;
    396 
    397 	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand);
    398 	if (xfer == NULL)
    399 		return NULL;
    400 
    401 	fp = &xfer->send.hdr;
    402 	fp->mode.rreqb.tcode = FWTCODE_RREQB;
    403 	fp->mode.rreqb.dest_hi = dst_hi;
    404 	fp->mode.rreqb.dest_lo = dst_lo;
    405 	fp->mode.rreqb.len = len;
    406 	fp->mode.rreqb.extcode = 0;
    407 
    408 	xfer->send.payload = NULL;
    409 	xfer->recv.payload = data;
    410 
    411 	if (fwmem_debug)
    412 		aprint_error("fwmem_read_block: %d %04x:%08x %d\n",
    413 		    fwdev->dst, dst_hi, dst_lo, len);
    414 	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
    415 		return xfer;
    416 
    417 	fw_xfer_free(xfer);
    418 	return NULL;
    419 }
    420 
    421 struct fw_xfer *
    422 fwmem_write_block(struct fw_device *fwdev, void *sc, uint8_t spd,
    423 		  uint16_t dst_hi, uint32_t dst_lo, int len, void *data,
    424 		  void (*hand)(struct fw_xfer *))
    425 {
    426 	struct fw_xfer *xfer;
    427 	struct fw_pkt *fp;
    428 
    429 	xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand);
    430 	if (xfer == NULL)
    431 		return NULL;
    432 
    433 	fp = &xfer->send.hdr;
    434 	fp->mode.wreqb.tcode = FWTCODE_WREQB;
    435 	fp->mode.wreqb.dest_hi = dst_hi;
    436 	fp->mode.wreqb.dest_lo = dst_lo;
    437 	fp->mode.wreqb.len = len;
    438 	fp->mode.wreqb.extcode = 0;
    439 
    440 	xfer->send.payload = data;
    441 	xfer->recv.payload = NULL;
    442 
    443 	if (fwmem_debug)
    444 		aprint_error("fwmem_write_block: %d %04x:%08x %d\n",
    445 		    fwdev->dst, dst_hi, dst_lo, len);
    446 	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
    447 		return xfer;
    448 
    449 	fw_xfer_free(xfer);
    450 	return NULL;
    451 }
    452 
    453 
    454 static struct fw_xfer *
    455 fwmem_xfer_req(struct fw_device *fwdev, void *sc, int spd, int slen, int rlen,
    456 	       void *hand)
    457 {
    458 	struct fw_xfer *xfer;
    459 
    460 	xfer = fw_xfer_alloc(M_FWMEM);
    461 	if (xfer == NULL)
    462 		return NULL;
    463 
    464 	xfer->fc = fwdev->fc;
    465 	xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst;
    466 	if (spd < 0)
    467 		xfer->send.spd = fwdev->speed;
    468 	else
    469 		xfer->send.spd = min(spd, fwdev->speed);
    470 	xfer->hand = hand;
    471 	xfer->sc = sc;
    472 	xfer->send.pay_len = slen;
    473 	xfer->recv.pay_len = rlen;
    474 
    475 	return xfer;
    476 }
    477 
    478 static void
    479 fwmem_biodone(struct fw_xfer *xfer)
    480 {
    481 	struct bio *bp;
    482 
    483 	bp = (struct bio *)xfer->sc;
    484 	bp->bio_error = xfer->resp;
    485 
    486 	if (bp->bio_error != 0) {
    487 		if (fwmem_debug)
    488 			printf("%s: err=%d\n", __func__, bp->bio_error);
    489 		bp->bio_resid = bp->bio_bcount;
    490 	}
    491 
    492 	fw_xfer_free(xfer);
    493 	biodone(bp);
    494 }
    495