Home | History | Annotate | Line # | Download | only in ieee1394
fwmem.c revision 1.13
      1 /*	$NetBSD: fwmem.c,v 1.13 2010/05/10 12:17:32 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.13 2010/05/10 12:17:32 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/kmem.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 = kmem_alloc(sizeof(struct fwmem_softc), KM_SLEEP);
    189 		if (sc->si_drv1 == NULL)
    190 			return ENOMEM;
    191 		fms = (struct fwmem_softc *)sc->si_drv1;
    192 		memcpy(&fms->eui, &fwmem_eui64, sizeof(struct fw_eui64));
    193 		fms->sc = sc;
    194 		fms->refcount = 1;
    195 		STAILQ_INIT(&fms->xferlist);
    196 		xfer = fw_xfer_alloc(M_FWMEM);
    197 		STAILQ_INSERT_TAIL(&fms->xferlist, xfer, link);
    198 	}
    199 	if (fwmem_debug)
    200 		printf("%s: refcount=%d\n", __func__, fms->refcount);
    201 
    202 	return 0;
    203 }
    204 
    205 int
    206 fwmem_close(dev_t dev, int flags, int fmt, struct lwp *td)
    207 {
    208 	struct firewire_softc *sc;
    209 	struct fwmem_softc *fms;
    210 	struct fw_xfer *xfer;
    211 
    212 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
    213 	if (sc == NULL)
    214 		return ENXIO;
    215 
    216 	fms = (struct fwmem_softc *)sc->si_drv1;
    217 
    218 	fms->refcount--;
    219 	if (fwmem_debug)
    220 		printf("%s: refcount=%d\n", __func__, fms->refcount);
    221 	if (fms->refcount < 1) {
    222 		while ((xfer = STAILQ_FIRST(&fms->xferlist)) != NULL) {
    223 			STAILQ_REMOVE_HEAD(&fms->xferlist, link);
    224 			fw_xfer_free(xfer);
    225 		}
    226 		kmem_free(sc->si_drv1, sizeof(struct fwmem_softc));
    227 		sc->si_drv1 = NULL;
    228 	}
    229 
    230 	return 0;
    231 }
    232 
    233 void
    234 fwmem_strategy(struct bio *bp)
    235 {
    236 	struct firewire_softc *sc;
    237 	struct fwmem_softc *fms;
    238 	struct fw_device *fwdev;
    239 	struct fw_xfer *xfer;
    240 	dev_t dev = bp->bio_dev;
    241 	int iolen, err = 0, s;
    242 
    243 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
    244 	if (sc == NULL)
    245 		return;
    246 
    247 	/* XXX check request length */
    248 
    249 	s = splvm();
    250 	fms = (struct fwmem_softc *)sc->si_drv1;
    251 	fwdev = fw_noderesolve_eui64(fms->sc->fc, &fms->eui);
    252 	if (fwdev == NULL) {
    253 		if (fwmem_debug)
    254 			printf("fwmem: no such device ID:%08x%08x\n",
    255 					fms->eui.hi, fms->eui.lo);
    256 		err = EINVAL;
    257 		goto error;
    258 	}
    259 
    260 	iolen = MIN(bp->bio_bcount, MAXLEN);
    261 	if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
    262 		if (iolen == 4 && (bp->bio_offset & 3) == 0)
    263 			xfer = fwmem_read_quad(fwdev, (void *) bp, fwmem_speed,
    264 			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
    265 			    bp->bio_data, fwmem_biodone);
    266 		else
    267 			xfer = fwmem_read_block(fwdev, (void *) bp, fwmem_speed,
    268 			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
    269 			    iolen, bp->bio_data, fwmem_biodone);
    270 	} else {
    271 		if (iolen == 4 && (bp->bio_offset & 3) == 0)
    272 			xfer = fwmem_write_quad(fwdev, (void *)bp, fwmem_speed,
    273 			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
    274 			    bp->bio_data, fwmem_biodone);
    275 		else
    276 			xfer = fwmem_write_block(fwdev, (void *)bp, fwmem_speed,
    277 			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
    278 			    iolen, bp->bio_data, fwmem_biodone);
    279 	}
    280 	if (xfer == NULL) {
    281 		err = EIO;
    282 		goto error;
    283 	}
    284 	/* XXX */
    285 	bp->bio_resid = bp->bio_bcount - iolen;
    286 error:
    287 	splx(s);
    288 	if (err != 0) {
    289 		if (fwmem_debug)
    290 			printf("%s: err=%d\n", __func__, err);
    291 		bp->bio_error = err;
    292 		bp->bio_resid = bp->bio_bcount;
    293 		biodone(bp);
    294 	}
    295 }
    296 
    297 int
    298 fwmem_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *td)
    299 {
    300 	struct firewire_softc *sc;
    301 	struct fwmem_softc *fms;
    302 	int err = 0;
    303 
    304 	sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
    305 	if (sc == NULL)
    306 		return ENXIO;
    307 
    308 	fms = (struct fwmem_softc *)sc->si_drv1;
    309 	switch (cmd) {
    310 	case FW_SDEUI64:
    311 		memcpy(&fms->eui, data, sizeof(struct fw_eui64));
    312 		break;
    313 
    314 	case FW_GDEUI64:
    315 		memcpy(data, &fms->eui, sizeof(struct fw_eui64));
    316 		break;
    317 
    318 	default:
    319 		err = EINVAL;
    320 	}
    321 	return err;
    322 }
    323 
    324 
    325 struct fw_xfer *
    326 fwmem_read_quad(struct fw_device *fwdev, void *	sc, uint8_t spd,
    327 		uint16_t dst_hi, uint32_t dst_lo, void *data,
    328 		void (*hand)(struct fw_xfer *))
    329 {
    330 	struct fw_xfer *xfer;
    331 	struct fw_pkt *fp;
    332 
    333 	xfer = fwmem_xfer_req(fwdev, (void *)sc, spd, 0, 4, hand);
    334 	if (xfer == NULL)
    335 		return NULL;
    336 
    337 	fp = &xfer->send.hdr;
    338 	fp->mode.rreqq.tcode = FWTCODE_RREQQ;
    339 	fp->mode.rreqq.dest_hi = dst_hi;
    340 	fp->mode.rreqq.dest_lo = dst_lo;
    341 
    342 	xfer->send.payload = NULL;
    343 	xfer->recv.payload = (uint32_t *)data;
    344 
    345 	if (fwmem_debug)
    346 		aprint_error("fwmem_read_quad: %d %04x:%08x\n",
    347 		    fwdev->dst, dst_hi, dst_lo);
    348 
    349 	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
    350 		return xfer;
    351 
    352 	fw_xfer_free(xfer);
    353 	return NULL;
    354 }
    355 
    356 struct fw_xfer *
    357 fwmem_write_quad(struct fw_device *fwdev, void *sc, uint8_t spd,
    358 		 uint16_t dst_hi, uint32_t dst_lo, void *data,
    359 		 void (*hand)(struct fw_xfer *))
    360 {
    361 	struct fw_xfer *xfer;
    362 	struct fw_pkt *fp;
    363 
    364 	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand);
    365 	if (xfer == NULL)
    366 		return NULL;
    367 
    368 	fp = &xfer->send.hdr;
    369 	fp->mode.wreqq.tcode = FWTCODE_WREQQ;
    370 	fp->mode.wreqq.dest_hi = dst_hi;
    371 	fp->mode.wreqq.dest_lo = dst_lo;
    372 	fp->mode.wreqq.data = *(uint32_t *)data;
    373 
    374 	xfer->send.payload = xfer->recv.payload = NULL;
    375 
    376 	if (fwmem_debug)
    377 		aprint_error("fwmem_write_quad: %d %04x:%08x %08x\n",
    378 		    fwdev->dst, dst_hi, dst_lo, *(uint32_t *)data);
    379 
    380 	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
    381 		return xfer;
    382 
    383 	fw_xfer_free(xfer);
    384 	return NULL;
    385 }
    386 
    387 struct fw_xfer *
    388 fwmem_read_block(struct fw_device *fwdev, void *sc, uint8_t spd,
    389 		 uint16_t dst_hi, uint32_t dst_lo, int len, void *data,
    390 		 void (*hand)(struct fw_xfer *))
    391 {
    392 	struct fw_xfer *xfer;
    393 	struct fw_pkt *fp;
    394 
    395 	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand);
    396 	if (xfer == NULL)
    397 		return NULL;
    398 
    399 	fp = &xfer->send.hdr;
    400 	fp->mode.rreqb.tcode = FWTCODE_RREQB;
    401 	fp->mode.rreqb.dest_hi = dst_hi;
    402 	fp->mode.rreqb.dest_lo = dst_lo;
    403 	fp->mode.rreqb.len = len;
    404 	fp->mode.rreqb.extcode = 0;
    405 
    406 	xfer->send.payload = NULL;
    407 	xfer->recv.payload = data;
    408 
    409 	if (fwmem_debug)
    410 		aprint_error("fwmem_read_block: %d %04x:%08x %d\n",
    411 		    fwdev->dst, dst_hi, dst_lo, len);
    412 	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
    413 		return xfer;
    414 
    415 	fw_xfer_free(xfer);
    416 	return NULL;
    417 }
    418 
    419 struct fw_xfer *
    420 fwmem_write_block(struct fw_device *fwdev, void *sc, uint8_t spd,
    421 		  uint16_t dst_hi, uint32_t dst_lo, int len, void *data,
    422 		  void (*hand)(struct fw_xfer *))
    423 {
    424 	struct fw_xfer *xfer;
    425 	struct fw_pkt *fp;
    426 
    427 	xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand);
    428 	if (xfer == NULL)
    429 		return NULL;
    430 
    431 	fp = &xfer->send.hdr;
    432 	fp->mode.wreqb.tcode = FWTCODE_WREQB;
    433 	fp->mode.wreqb.dest_hi = dst_hi;
    434 	fp->mode.wreqb.dest_lo = dst_lo;
    435 	fp->mode.wreqb.len = len;
    436 	fp->mode.wreqb.extcode = 0;
    437 
    438 	xfer->send.payload = data;
    439 	xfer->recv.payload = NULL;
    440 
    441 	if (fwmem_debug)
    442 		aprint_error("fwmem_write_block: %d %04x:%08x %d\n",
    443 		    fwdev->dst, dst_hi, dst_lo, len);
    444 	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
    445 		return xfer;
    446 
    447 	fw_xfer_free(xfer);
    448 	return NULL;
    449 }
    450 
    451 
    452 static struct fw_xfer *
    453 fwmem_xfer_req(struct fw_device *fwdev, void *sc, int spd, int slen, int rlen,
    454 	       void *hand)
    455 {
    456 	struct fw_xfer *xfer;
    457 
    458 	xfer = fw_xfer_alloc(M_FWMEM);
    459 	if (xfer == NULL)
    460 		return NULL;
    461 
    462 	xfer->fc = fwdev->fc;
    463 	xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst;
    464 	if (spd < 0)
    465 		xfer->send.spd = fwdev->speed;
    466 	else
    467 		xfer->send.spd = min(spd, fwdev->speed);
    468 	xfer->hand = hand;
    469 	xfer->sc = sc;
    470 	xfer->send.pay_len = slen;
    471 	xfer->recv.pay_len = rlen;
    472 
    473 	return xfer;
    474 }
    475 
    476 static void
    477 fwmem_biodone(struct fw_xfer *xfer)
    478 {
    479 	struct bio *bp;
    480 
    481 	bp = (struct bio *)xfer->sc;
    482 	bp->bio_error = xfer->resp;
    483 
    484 	if (bp->bio_error != 0) {
    485 		if (fwmem_debug)
    486 			printf("%s: err=%d\n", __func__, bp->bio_error);
    487 		bp->bio_resid = bp->bio_bcount;
    488 	}
    489 
    490 	fw_xfer_free(xfer);
    491 	biodone(bp);
    492 }
    493