fwmem.c revision 124145
11592Srgrimes/*
215645Sjoerg * Copyright (c) 2002-2003
315645Sjoerg * 	Hidetoshi Shimokawa. All rights reserved.
41592Srgrimes *
51592Srgrimes * Redistribution and use in source and binary forms, with or without
61592Srgrimes * modification, are permitted provided that the following conditions
71592Srgrimes * are met:
81592Srgrimes * 1. Redistributions of source code must retain the above copyright
91592Srgrimes *    notice, this list of conditions and the following disclaimer.
101592Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111592Srgrimes *    notice, this list of conditions and the following disclaimer in the
121592Srgrimes *    documentation and/or other materials provided with the distribution.
131592Srgrimes * 3. All advertising materials mentioning features or use of this software
141592Srgrimes *    must display the following acknowledgement:
151592Srgrimes *
161592Srgrimes *	This product includes software developed by Hidetoshi Shimokawa.
171592Srgrimes *
181592Srgrimes * 4. Neither the name of the author nor the names of its contributors
191592Srgrimes *    may be used to endorse or promote products derived from this software
201592Srgrimes *    without specific prior written permission.
211592Srgrimes *
221592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
231592Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
241592Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
251592Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
261592Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
271592Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
281592Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
291592Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
301592Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
311592Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
321592Srgrimes * SUCH DAMAGE.
331592Srgrimes *
341592Srgrimes */
3531331Scharnier
3615645Sjoerg#include <sys/cdefs.h>
3715645Sjoerg__FBSDID("$FreeBSD: head/sys/dev/firewire/fwmem.c 124145 2004-01-05 14:21:18Z simokawa $");
381592Srgrimes
391592Srgrimes#include <sys/param.h>
401592Srgrimes#include <sys/systm.h>
4131331Scharnier#include <sys/types.h>
4231331Scharnier
4331331Scharnier#include <sys/kernel.h>
4431331Scharnier#include <sys/malloc.h>
4550476Speter#include <sys/conf.h>
461592Srgrimes#include <sys/sysctl.h>
471592Srgrimes#if __FreeBSD_version < 500000
481592Srgrimes#include <sys/buf.h>
491592Srgrimes#else
5015645Sjoerg#include <sys/bio.h>
5115645Sjoerg#endif
5215645Sjoerg
5315645Sjoerg#include <sys/bus.h>
5431331Scharnier#include <machine/bus.h>
5515645Sjoerg
562286Sjkh#include <sys/signal.h>
5731331Scharnier#include <sys/mman.h>
5815645Sjoerg#include <sys/ioccom.h>
5931331Scharnier#include <sys/fcntl.h>
602286Sjkh
6115645Sjoerg#include <dev/firewire/firewire.h>
6215645Sjoerg#include <dev/firewire/firewirereg.h>
6315645Sjoerg#include <dev/firewire/fwmem.h>
642286Sjkh
6515645Sjoergstatic int fwmem_speed=2, fwmem_debug=0;
6615645Sjoergstatic struct fw_eui64 fwmem_eui64;
672286SjkhSYSCTL_DECL(_hw_firewire);
6815645SjoergSYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem, CTLFLAG_RD, 0,
691592Srgrimes	"FireWire Memory Access");
701592SrgrimesSYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_hi, CTLFLAG_RW,
7115645Sjoerg	&fwmem_eui64.hi, 0, "Fwmem target EUI64 high");
721592SrgrimesSYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_lo, CTLFLAG_RW,
7315645Sjoerg	&fwmem_eui64.lo, 0, "Fwmem target EUI64 low");
7415645SjoergSYSCTL_INT(_hw_firewire_fwmem, OID_AUTO, speed, CTLFLAG_RW, &fwmem_speed, 0,
7515645Sjoerg	"Fwmem link speed");
7615645SjoergSYSCTL_INT(_debug, OID_AUTO, fwmem_debug, CTLFLAG_RW, &fwmem_debug, 0,
7715645Sjoerg	"Fwmem driver debug flag");
781592Srgrimes
7915645SjoergMALLOC_DEFINE(M_FWMEM, "fwmem", "fwmem/FireWire");
8015645Sjoerg
8115645Sjoerg#define MAXLEN (512 << fwmem_speed)
8219697Spst
8319697Spststruct fwmem_softc {
8419697Spst	struct fw_eui64 eui;
8519697Spst	int refcount;
8619697Spst};
8719697Spst
8819697Spststatic struct fw_xfer *
8919697Spstfwmem_xfer_req(
9019697Spst	struct fw_device *fwdev,
9119697Spst	caddr_t sc,
9215645Sjoerg	int spd,
9315645Sjoerg	int slen,
941592Srgrimes	int rlen,
951592Srgrimes	void *hand)
961592Srgrimes{
9722400Sdavidn	struct fw_xfer *xfer;
981592Srgrimes
991592Srgrimes	xfer = fw_xfer_alloc(M_FWMEM);
1001592Srgrimes	if (xfer == NULL)
1011592Srgrimes		return NULL;
1021592Srgrimes
1031592Srgrimes	xfer->fc = fwdev->fc;
1041592Srgrimes	xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst;
1051592Srgrimes	if (spd < 0)
1061592Srgrimes		xfer->send.spd = fwdev->speed;
1071592Srgrimes	else
1081592Srgrimes		xfer->send.spd = min(spd, fwdev->speed);
1091592Srgrimes	xfer->act.hand = hand;
1101592Srgrimes	xfer->retry_req = fw_asybusy;
1111592Srgrimes	xfer->sc = sc;
1121592Srgrimes	xfer->send.pay_len = slen;
1131592Srgrimes	xfer->recv.pay_len = rlen;
1141592Srgrimes
1151592Srgrimes	return xfer;
1161592Srgrimes}
1171592Srgrimes
1181592Srgrimesstruct fw_xfer *
1191592Srgrimesfwmem_read_quad(
1201592Srgrimes	struct fw_device *fwdev,
1211592Srgrimes	caddr_t	sc,
1221592Srgrimes	u_int8_t spd,
1231592Srgrimes	u_int16_t dst_hi,
1241592Srgrimes	u_int32_t dst_lo,
1251592Srgrimes	void *data,
1261592Srgrimes	void (*hand)(struct fw_xfer *))
1271592Srgrimes{
12815645Sjoerg	struct fw_xfer *xfer;
12915645Sjoerg	struct fw_pkt *fp;
13015645Sjoerg
1311592Srgrimes	xfer = fwmem_xfer_req(fwdev, (void *)sc, spd, 0, 4, hand);
13240083Sjkh	if (xfer == NULL) {
13340083Sjkh		return NULL;
13415645Sjoerg	}
13515645Sjoerg
13615645Sjoerg	fp = &xfer->send.hdr;
13715645Sjoerg	fp->mode.rreqq.tcode = FWTCODE_RREQQ;
13815645Sjoerg	fp->mode.rreqq.dest_hi = dst_hi;
13915645Sjoerg	fp->mode.rreqq.dest_lo = dst_lo;
14015645Sjoerg
14115645Sjoerg	xfer->send.payload = NULL;
14215645Sjoerg	xfer->recv.payload = (u_int32_t *)data;
14315645Sjoerg
14422208Sdavidn	if (fwmem_debug)
14522208Sdavidn		printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst,
14622208Sdavidn				dst_hi, dst_lo);
14722208Sdavidn
14815645Sjoerg	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
14915645Sjoerg		return xfer;
15015645Sjoerg
15122208Sdavidn	fw_xfer_free(xfer);
15222208Sdavidn	return NULL;
1531592Srgrimes}
15415645Sjoerg
15515645Sjoergstruct fw_xfer *
1561592Srgrimesfwmem_write_quad(
1571592Srgrimes	struct fw_device *fwdev,
1581592Srgrimes	caddr_t	sc,
1591592Srgrimes	u_int8_t spd,
1601592Srgrimes	u_int16_t dst_hi,
1611592Srgrimes	u_int32_t dst_lo,
1621592Srgrimes	void *data,
1631592Srgrimes	void (*hand)(struct fw_xfer *))
16415645Sjoerg{
16515645Sjoerg	struct fw_xfer *xfer;
1661592Srgrimes	struct fw_pkt *fp;
1671592Srgrimes
1681592Srgrimes	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand);
1691592Srgrimes	if (xfer == NULL)
17015645Sjoerg		return NULL;
17115645Sjoerg
17215645Sjoerg	fp = &xfer->send.hdr;
17315645Sjoerg	fp->mode.wreqq.tcode = FWTCODE_WREQQ;
17415645Sjoerg	fp->mode.wreqq.dest_hi = dst_hi;
17515645Sjoerg	fp->mode.wreqq.dest_lo = dst_lo;
17615645Sjoerg	fp->mode.wreqq.data = *(u_int32_t *)data;
17715645Sjoerg
17831331Scharnier	xfer->send.payload = xfer->recv.payload = NULL;
17915645Sjoerg
18015645Sjoerg	if (fwmem_debug)
18115645Sjoerg		printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst,
18215645Sjoerg			dst_hi, dst_lo, *(u_int32_t *)data);
1831592Srgrimes
1841592Srgrimes	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
1852286Sjkh		return xfer;
1861592Srgrimes
1872286Sjkh	fw_xfer_free(xfer);
18815645Sjoerg	return NULL;
18922491Sdavidn}
19015645Sjoerg
19119697Spststruct fw_xfer *
1921592Srgrimesfwmem_read_block(
1931592Srgrimes	struct fw_device *fwdev,
1942391Sache	caddr_t	sc,
1952286Sjkh	u_int8_t spd,
19615645Sjoerg	u_int16_t dst_hi,
19745422Sbrian	u_int32_t dst_lo,
19845422Sbrian	int len,
1991592Srgrimes	void *data,
2001592Srgrimes	void (*hand)(struct fw_xfer *))
20115645Sjoerg{
2021592Srgrimes	struct fw_xfer *xfer;
20315645Sjoerg	struct fw_pkt *fp;
20415645Sjoerg
20515645Sjoerg	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand);
20615645Sjoerg	if (xfer == NULL)
20715645Sjoerg		return NULL;
20815645Sjoerg
20915645Sjoerg	fp = &xfer->send.hdr;
21022208Sdavidn	fp->mode.rreqb.tcode = FWTCODE_RREQB;
21122208Sdavidn	fp->mode.rreqb.dest_hi = dst_hi;
21222208Sdavidn	fp->mode.rreqb.dest_lo = dst_lo;
21322208Sdavidn	fp->mode.rreqb.len = len;
21422208Sdavidn	fp->mode.rreqb.extcode = 0;
21522208Sdavidn
21615645Sjoerg	xfer->send.payload = NULL;
2171592Srgrimes	xfer->recv.payload = data;
2181592Srgrimes
2191592Srgrimes	if (fwmem_debug)
2208870Srgrimes		printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst,
2211592Srgrimes				dst_hi, dst_lo, len);
2221592Srgrimes	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
2231592Srgrimes		return xfer;
22422208Sdavidn
2251592Srgrimes	fw_xfer_free(xfer);
2261592Srgrimes	return NULL;
2271592Srgrimes}
2281592Srgrimes
2291592Srgrimesstruct fw_xfer *
2301592Srgrimesfwmem_write_block(
2311592Srgrimes	struct fw_device *fwdev,
23222208Sdavidn	caddr_t	sc,
23322208Sdavidn	u_int8_t spd,
23422208Sdavidn	u_int16_t dst_hi,
23522208Sdavidn	u_int32_t dst_lo,
23622208Sdavidn	int len,
23722208Sdavidn	void *data,
23822208Sdavidn	void (*hand)(struct fw_xfer *))
23922208Sdavidn{
24022208Sdavidn	struct fw_xfer *xfer;
24122208Sdavidn	struct fw_pkt *fp;
24222208Sdavidn
24322491Sdavidn	xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand);
24422208Sdavidn	if (xfer == NULL)
2451592Srgrimes		return NULL;
2461592Srgrimes
24722208Sdavidn	fp = &xfer->send.hdr;
24822208Sdavidn	fp->mode.wreqb.tcode = FWTCODE_WREQB;
24922208Sdavidn	fp->mode.wreqb.dest_hi = dst_hi;
25022208Sdavidn	fp->mode.wreqb.dest_lo = dst_lo;
25122208Sdavidn	fp->mode.wreqb.len = len;
25222208Sdavidn	fp->mode.wreqb.extcode = 0;
25322208Sdavidn
25422208Sdavidn	xfer->send.payload = data;
25522208Sdavidn	xfer->recv.payload = NULL;
25622208Sdavidn
25722208Sdavidn	if (fwmem_debug)
25822208Sdavidn		printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst,
25922208Sdavidn				dst_hi, dst_lo, len);
26022208Sdavidn	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
26122208Sdavidn		return xfer;
26222208Sdavidn
26322208Sdavidn	fw_xfer_free(xfer);
26422491Sdavidn	return NULL;
26522208Sdavidn}
26622208Sdavidn
26722208Sdavidn
26822208Sdavidnint
26922208Sdavidnfwmem_open (dev_t dev, int flags, int fmt, fw_proc *td)
27022491Sdavidn{
27122208Sdavidn	struct fwmem_softc *fms;
27222208Sdavidn
27322208Sdavidn	if (dev->si_drv1 != NULL) {
27422208Sdavidn		if ((flags & FWRITE) != 0)
27522208Sdavidn			return (EBUSY);
27622208Sdavidn		fms = (struct fwmem_softc *)dev->si_drv1;
2771592Srgrimes		fms->refcount ++;
2781592Srgrimes	} else {
2791592Srgrimes		fms = (struct fwmem_softc *)malloc(sizeof(struct fwmem_softc),
28015645Sjoerg							M_FWMEM, M_WAITOK);
28122208Sdavidn		if (fms == NULL)
28222208Sdavidn			return ENOMEM;
28315645Sjoerg		bcopy(&fwmem_eui64, &fms->eui, sizeof(struct fw_eui64));
28415645Sjoerg		dev->si_drv1 = (void *)fms;
28515645Sjoerg		dev->si_iosize_max = DFLTPHYS;
28615645Sjoerg		fms->refcount = 1;
28715645Sjoerg	}
28815645Sjoerg	if (fwmem_debug)
28915645Sjoerg		printf("%s: refcount=%d\n", __FUNCTION__, fms->refcount);
29015645Sjoerg
29115645Sjoerg	return (0);
29215645Sjoerg}
29315645Sjoerg
29415645Sjoergint
29515645Sjoergfwmem_close (dev_t dev, int flags, int fmt, fw_proc *td)
29615645Sjoerg{
29715645Sjoerg	struct fwmem_softc *fms;
2981592Srgrimes
2991592Srgrimes	fms = (struct fwmem_softc *)dev->si_drv1;
30022491Sdavidn	fms->refcount --;
30122491Sdavidn	if (fwmem_debug)
30222491Sdavidn		printf("%s: refcount=%d\n", __FUNCTION__, fms->refcount);
30322491Sdavidn	if (fms->refcount < 1) {
30422491Sdavidn		free(dev->si_drv1, M_FW);
30522491Sdavidn		dev->si_drv1 = NULL;
30622491Sdavidn	}
30722491Sdavidn
30822491Sdavidn	return (0);
30922491Sdavidn}
31022491Sdavidn
31122208Sdavidn
3121592Srgrimesstatic void
3131592Srgrimesfwmem_biodone(struct fw_xfer *xfer)
3141592Srgrimes{
3151592Srgrimes	struct bio *bp;
3161592Srgrimes
3171592Srgrimes	bp = (struct bio *)xfer->sc;
3181592Srgrimes	bp->bio_error = xfer->resp;
3191592Srgrimes
3201592Srgrimes	if (bp->bio_error != 0) {
3211592Srgrimes		if (fwmem_debug)
3221592Srgrimes			printf("%s: err=%d\n", __FUNCTION__, bp->bio_error);
32321120Smsmith		bp->bio_flags |= BIO_ERROR;
32422208Sdavidn		bp->bio_resid = bp->bio_bcount;
32522208Sdavidn	}
32622208Sdavidn
32722208Sdavidn	fw_xfer_free(xfer);
32822208Sdavidn	biodone(bp);
32922208Sdavidn}
33022208Sdavidn
33122208Sdavidnvoid
33222208Sdavidnfwmem_strategy(struct bio *bp)
33322208Sdavidn{
33422208Sdavidn	struct firewire_softc *sc;
33522208Sdavidn	struct fwmem_softc *fms;
33622208Sdavidn	struct fw_device *fwdev;
33722208Sdavidn	struct fw_xfer *xfer;
33822491Sdavidn	dev_t dev;
33922208Sdavidn	int unit, err=0, s, iolen;
3401592Srgrimes
3411592Srgrimes	dev = bp->bio_dev;
3421592Srgrimes	/* XXX check request length */
34315659Sache
34415659Sache        unit = DEV2UNIT(dev);
34522208Sdavidn	sc = devclass_get_softc(firewire_devclass, unit);
3461592Srgrimes
3471592Srgrimes	s = splfw();
3481592Srgrimes	fms = (struct fwmem_softc *)dev->si_drv1;
3491592Srgrimes	fwdev = fw_noderesolve_eui64(sc->fc, &fms->eui);
3501592Srgrimes	if (fwdev == NULL) {
3511592Srgrimes		if (fwmem_debug)
35245291Speter			printf("fwmem: no such device ID:%08x%08x\n",
35345291Speter					fms->eui.hi, fms->eui.lo);
35445291Speter		err = EINVAL;
35545291Speter		goto error;
35645291Speter	}
35745291Speter
35845291Speter	iolen = MIN(bp->bio_bcount, MAXLEN);
35945291Speter	if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
36045291Speter		if (iolen == 4 && (bp->bio_offset & 3) == 0)
36145291Speter			xfer = fwmem_read_quad(fwdev,
36245291Speter			    (void *) bp, fwmem_speed,
36345291Speter			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
36445291Speter			    bp->bio_data, fwmem_biodone);
36545291Speter		else
36645291Speter			xfer = fwmem_read_block(fwdev,
36745291Speter			    (void *) bp, fwmem_speed,
36845291Speter			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
36926415Sdavidn			    iolen, bp->bio_data, fwmem_biodone);
37026415Sdavidn	} else {
37144615Sbrian		if (iolen == 4 && (bp->bio_offset & 3) == 0)
37244615Sbrian			xfer = fwmem_write_quad(fwdev,
37344615Sbrian			    (void *)bp, fwmem_speed,
37419697Spst			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
37519697Spst			    bp->bio_data, fwmem_biodone);
37619697Spst		else
37745291Speter			xfer = fwmem_write_block(fwdev,
3781592Srgrimes			    (void *)bp, fwmem_speed,
3791592Srgrimes			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
38015645Sjoerg			    iolen, bp->bio_data, fwmem_biodone);
3811592Srgrimes	}
3821592Srgrimes	if (xfer == NULL) {
3831592Srgrimes		err = EIO;
3841592Srgrimes		goto error;
3851592Srgrimes	}
3861592Srgrimes	/* XXX */
3871592Srgrimes	bp->bio_resid = bp->bio_bcount - iolen;
3881592Srgrimeserror:
38915645Sjoerg	splx(s);
39015645Sjoerg	if (err != 0) {
39115645Sjoerg		if (fwmem_debug)
39215645Sjoerg			printf("%s: err=%d\n", __FUNCTION__, err);
39315645Sjoerg		bp->bio_error = err;
39415645Sjoerg		bp->bio_flags |= BIO_ERROR;
39515645Sjoerg		bp->bio_resid = bp->bio_bcount;
39615645Sjoerg		biodone(bp);
39715645Sjoerg	}
39815645Sjoerg}
39915645Sjoerg
40022208Sdavidnint
40122208Sdavidnfwmem_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
40215645Sjoerg{
40315645Sjoerg	struct fwmem_softc *fms;
40415645Sjoerg	int err = 0;
4051592Srgrimes
4061592Srgrimes	fms = (struct fwmem_softc *)dev->si_drv1;
4071592Srgrimes	switch (cmd) {
4081592Srgrimes	case FW_SDEUI64:
40915645Sjoerg		bcopy(data, &fms->eui, sizeof(struct fw_eui64));
41015645Sjoerg		break;
41115645Sjoerg	case FW_GDEUI64:
41245291Speter		bcopy(&fms->eui, data, sizeof(struct fw_eui64));
41345291Speter		break;
4141592Srgrimes	default:
4151592Srgrimes		err = EINVAL;
4161592Srgrimes	}
4171592Srgrimes	return(err);
4181592Srgrimes}
41915645Sjoergint
4201592Srgrimesfwmem_poll (dev_t dev, int events, fw_proc *td)
4211592Srgrimes{
4221592Srgrimes	return EINVAL;
4231592Srgrimes}
4241592Srgrimesint
42515645Sjoerg#if __FreeBSD_version < 500102
42622208Sdavidnfwmem_mmap (dev_t dev, vm_offset_t offset, int nproto)
42722208Sdavidn#else
42822208Sdavidnfwmem_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto)
42922208Sdavidn#endif
43022208Sdavidn{
43122208Sdavidn	return EINVAL;
43222208Sdavidn}
43322208Sdavidn