fwmem.c revision 120664
1/*
2 * Copyright (c) 2002-2003
3 * 	Hidetoshi Shimokawa. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *
16 *	This product includes software developed by Hidetoshi Shimokawa.
17 *
18 * 4. Neither the name of the author nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 */
35
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD: head/sys/dev/firewire/fwmem.c 120664 2003-10-02 14:33:30Z simokawa $");
38
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/types.h>
42
43#include <sys/kernel.h>
44#include <sys/malloc.h>
45#include <sys/conf.h>
46#include <sys/sysctl.h>
47#if __FreeBSD_version < 500000
48#include <sys/buf.h>
49#else
50#include <sys/bio.h>
51#endif
52
53#include <sys/bus.h>
54#include <machine/bus.h>
55
56#include <sys/signal.h>
57#include <sys/mman.h>
58#include <sys/ioccom.h>
59
60#include <dev/firewire/firewire.h>
61#include <dev/firewire/firewirereg.h>
62#include <dev/firewire/fwmem.h>
63
64static int fwmem_speed=2, fwmem_debug=0;
65static struct fw_eui64 fwmem_eui64;
66SYSCTL_DECL(_hw_firewire);
67SYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem, CTLFLAG_RD, 0,
68	"FireWire Memory Access");
69SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_hi, CTLFLAG_RW,
70	&fwmem_eui64.hi, 0, "Fwmem target EUI64 high");
71SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_lo, CTLFLAG_RW,
72	&fwmem_eui64.lo, 0, "Fwmem target EUI64 low");
73SYSCTL_INT(_hw_firewire_fwmem, OID_AUTO, speed, CTLFLAG_RW, &fwmem_speed, 0,
74	"Fwmem link speed");
75SYSCTL_INT(_debug, OID_AUTO, fwmem_debug, CTLFLAG_RW, &fwmem_debug, 0,
76	"Fwmem driver debug flag");
77
78#define MAXLEN (512 << fwmem_speed)
79
80static struct fw_xfer *
81fwmem_xfer_req(
82	struct fw_device *fwdev,
83	caddr_t sc,
84	int spd,
85	int slen,
86	int rlen,
87	void *hand)
88{
89	struct fw_xfer *xfer;
90
91	xfer = fw_xfer_alloc(M_FWXFER);
92	if (xfer == NULL)
93		return NULL;
94
95	xfer->fc = fwdev->fc;
96	xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst;
97	if (spd < 0)
98		xfer->send.spd = fwdev->speed;
99	else
100		xfer->send.spd = min(spd, fwdev->speed);
101	xfer->act.hand = hand;
102	xfer->retry_req = fw_asybusy;
103	xfer->sc = sc;
104	xfer->send.pay_len = slen;
105	xfer->recv.pay_len = rlen;
106
107	return xfer;
108}
109
110struct fw_xfer *
111fwmem_read_quad(
112	struct fw_device *fwdev,
113	caddr_t	sc,
114	u_int8_t spd,
115	u_int16_t dst_hi,
116	u_int32_t dst_lo,
117	void *data,
118	void (*hand)(struct fw_xfer *))
119{
120	struct fw_xfer *xfer;
121	struct fw_pkt *fp;
122
123	xfer = fwmem_xfer_req(fwdev, (void *)sc, spd, 0, 4, hand);
124	if (xfer == NULL) {
125		return NULL;
126	}
127
128	fp = &xfer->send.hdr;
129	fp->mode.rreqq.tcode = FWTCODE_RREQQ;
130	fp->mode.rreqq.dest_hi = dst_hi;
131	fp->mode.rreqq.dest_lo = dst_lo;
132
133	xfer->send.payload = NULL;
134	xfer->recv.payload = (u_int32_t *)data;
135
136	if (fwmem_debug)
137		printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst,
138				dst_hi, dst_lo);
139
140	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
141		return xfer;
142
143	fw_xfer_free(xfer);
144	return NULL;
145}
146
147struct fw_xfer *
148fwmem_write_quad(
149	struct fw_device *fwdev,
150	caddr_t	sc,
151	u_int8_t spd,
152	u_int16_t dst_hi,
153	u_int32_t dst_lo,
154	void *data,
155	void (*hand)(struct fw_xfer *))
156{
157	struct fw_xfer *xfer;
158	struct fw_pkt *fp;
159
160	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand);
161	if (xfer == NULL)
162		return NULL;
163
164	fp = &xfer->send.hdr;
165	fp->mode.wreqq.tcode = FWTCODE_WREQQ;
166	fp->mode.wreqq.dest_hi = dst_hi;
167	fp->mode.wreqq.dest_lo = dst_lo;
168	fp->mode.wreqq.data = *(u_int32_t *)data;
169
170	xfer->send.payload = xfer->recv.payload = NULL;
171
172	if (fwmem_debug)
173		printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst,
174			dst_hi, dst_lo, *(u_int32_t *)data);
175
176	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
177		return xfer;
178
179	fw_xfer_free(xfer);
180	return NULL;
181}
182
183struct fw_xfer *
184fwmem_read_block(
185	struct fw_device *fwdev,
186	caddr_t	sc,
187	u_int8_t spd,
188	u_int16_t dst_hi,
189	u_int32_t dst_lo,
190	int len,
191	void *data,
192	void (*hand)(struct fw_xfer *))
193{
194	struct fw_xfer *xfer;
195	struct fw_pkt *fp;
196
197	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand);
198	if (xfer == NULL)
199		return NULL;
200
201	fp = &xfer->send.hdr;
202	fp->mode.rreqb.tcode = FWTCODE_RREQB;
203	fp->mode.rreqb.dest_hi = dst_hi;
204	fp->mode.rreqb.dest_lo = dst_lo;
205	fp->mode.rreqb.len = len;
206	fp->mode.rreqb.extcode = 0;
207
208	xfer->send.payload = NULL;
209	xfer->recv.payload = data;
210
211	if (fwmem_debug)
212		printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst,
213				dst_hi, dst_lo, len);
214	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
215		return xfer;
216
217	fw_xfer_free(xfer);
218	return NULL;
219}
220
221struct fw_xfer *
222fwmem_write_block(
223	struct fw_device *fwdev,
224	caddr_t	sc,
225	u_int8_t spd,
226	u_int16_t dst_hi,
227	u_int32_t dst_lo,
228	int len,
229	void *data,
230	void (*hand)(struct fw_xfer *))
231{
232	struct fw_xfer *xfer;
233	struct fw_pkt *fp;
234
235	xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand);
236	if (xfer == NULL)
237		return NULL;
238
239	fp = &xfer->send.hdr;
240	fp->mode.wreqb.tcode = FWTCODE_WREQB;
241	fp->mode.wreqb.dest_hi = dst_hi;
242	fp->mode.wreqb.dest_lo = dst_lo;
243	fp->mode.wreqb.len = len;
244	fp->mode.wreqb.extcode = 0;
245
246	xfer->send.payload = data;
247	xfer->recv.payload = NULL;
248
249	if (fwmem_debug)
250		printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst,
251				dst_hi, dst_lo, len);
252	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
253		return xfer;
254
255	fw_xfer_free(xfer);
256	return NULL;
257}
258
259
260int
261fwmem_open (dev_t dev, int flags, int fmt, fw_proc *td)
262{
263	struct fw_eui64 *eui;
264
265	if (dev->si_drv1 != NULL)
266		return (EBUSY);
267
268	eui = (struct fw_eui64 *)malloc(sizeof(struct fw_eui64),
269							M_FW, M_WAITOK);
270	if (eui == NULL)
271		return ENOMEM;
272	bcopy(&fwmem_eui64, eui, sizeof(struct fw_eui64));
273	dev->si_drv1 = (void *)eui;
274	dev->si_iosize_max = DFLTPHYS;
275
276	return (0);
277}
278
279int
280fwmem_close (dev_t dev, int flags, int fmt, fw_proc *td)
281{
282	free(dev->si_drv1, M_FW);
283	dev->si_drv1 = NULL;
284
285	return (0);
286}
287
288
289static void
290fwmem_biodone(struct fw_xfer *xfer)
291{
292	struct bio *bp;
293
294	bp = (struct bio *)xfer->sc;
295	bp->bio_error = xfer->resp;
296
297	if (bp->bio_error != 0) {
298		printf("%s: err=%d\n", __FUNCTION__, bp->bio_error);
299		bp->bio_flags |= BIO_ERROR;
300		bp->bio_resid = bp->bio_bcount;
301	}
302
303	fw_xfer_free(xfer);
304	biodone(bp);
305}
306
307void
308fwmem_strategy(struct bio *bp)
309{
310	struct firewire_softc *sc;
311	struct fw_device *fwdev;
312	struct fw_xfer *xfer;
313	dev_t dev;
314	int unit, err=0, s, iolen;
315
316	dev = bp->bio_dev;
317	/* XXX check request length */
318
319        unit = DEV2UNIT(dev);
320	sc = devclass_get_softc(firewire_devclass, unit);
321
322	s = splfw();
323	fwdev = fw_noderesolve_eui64(sc->fc, (struct fw_eui64 *)dev->si_drv1);
324	if (fwdev == NULL) {
325		if (fwmem_debug)
326			printf("fwmem: no such device ID:%08x%08x\n",
327					fwmem_eui64.hi, fwmem_eui64.lo);
328		err = EINVAL;
329		goto error;
330	}
331
332	iolen = MIN(bp->bio_bcount, MAXLEN);
333	if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
334		if (iolen == 4 && (bp->bio_offset & 3) == 0)
335			xfer = fwmem_read_quad(fwdev,
336			    (void *) bp, fwmem_speed,
337			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
338			    bp->bio_data, fwmem_biodone);
339		else
340			xfer = fwmem_read_block(fwdev,
341			    (void *) bp, fwmem_speed,
342			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
343			    iolen, bp->bio_data, fwmem_biodone);
344	} else {
345		if (iolen == 4 && (bp->bio_offset & 3) == 0)
346			xfer = fwmem_write_quad(fwdev,
347			    (void *)bp, fwmem_speed,
348			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
349			    bp->bio_data, fwmem_biodone);
350		else
351			xfer = fwmem_write_block(fwdev,
352			    (void *)bp, fwmem_speed,
353			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
354			    iolen, bp->bio_data, fwmem_biodone);
355	}
356	if (xfer == NULL) {
357		err = EIO;
358		goto error;
359	}
360	/* XXX */
361	bp->bio_resid = bp->bio_bcount - iolen;
362error:
363	splx(s);
364	if (err != 0) {
365		printf("%s: err=%d\n", __FUNCTION__, err);
366		bp->bio_error = err;
367		bp->bio_flags |= BIO_ERROR;
368		bp->bio_resid = bp->bio_bcount;
369		biodone(bp);
370	}
371}
372
373int
374fwmem_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
375{
376	int err = 0;
377	switch (cmd) {
378	case FW_SDEUI64:
379		bcopy(data, dev->si_drv1, sizeof(struct fw_eui64));
380		break;
381	case FW_GDEUI64:
382		bcopy(dev->si_drv1, data, sizeof(struct fw_eui64));
383		break;
384	default:
385		err = EINVAL;
386	}
387	return(err);
388}
389int
390fwmem_poll (dev_t dev, int events, fw_proc *td)
391{
392	return EINVAL;
393}
394int
395#if __FreeBSD_version < 500102
396fwmem_mmap (dev_t dev, vm_offset_t offset, int nproto)
397#else
398fwmem_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto)
399#endif
400{
401	return EINVAL;
402}
403