fwmem.c revision 124145
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 124145 2004-01-05 14:21:18Z 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#include <sys/fcntl.h>
60
61#include <dev/firewire/firewire.h>
62#include <dev/firewire/firewirereg.h>
63#include <dev/firewire/fwmem.h>
64
65static int fwmem_speed=2, fwmem_debug=0;
66static struct fw_eui64 fwmem_eui64;
67SYSCTL_DECL(_hw_firewire);
68SYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem, CTLFLAG_RD, 0,
69	"FireWire Memory Access");
70SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_hi, CTLFLAG_RW,
71	&fwmem_eui64.hi, 0, "Fwmem target EUI64 high");
72SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_lo, CTLFLAG_RW,
73	&fwmem_eui64.lo, 0, "Fwmem target EUI64 low");
74SYSCTL_INT(_hw_firewire_fwmem, OID_AUTO, speed, CTLFLAG_RW, &fwmem_speed, 0,
75	"Fwmem link speed");
76SYSCTL_INT(_debug, OID_AUTO, fwmem_debug, CTLFLAG_RW, &fwmem_debug, 0,
77	"Fwmem driver debug flag");
78
79MALLOC_DEFINE(M_FWMEM, "fwmem", "fwmem/FireWire");
80
81#define MAXLEN (512 << fwmem_speed)
82
83struct fwmem_softc {
84	struct fw_eui64 eui;
85	int refcount;
86};
87
88static struct fw_xfer *
89fwmem_xfer_req(
90	struct fw_device *fwdev,
91	caddr_t sc,
92	int spd,
93	int slen,
94	int rlen,
95	void *hand)
96{
97	struct fw_xfer *xfer;
98
99	xfer = fw_xfer_alloc(M_FWMEM);
100	if (xfer == NULL)
101		return NULL;
102
103	xfer->fc = fwdev->fc;
104	xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst;
105	if (spd < 0)
106		xfer->send.spd = fwdev->speed;
107	else
108		xfer->send.spd = min(spd, fwdev->speed);
109	xfer->act.hand = hand;
110	xfer->retry_req = fw_asybusy;
111	xfer->sc = sc;
112	xfer->send.pay_len = slen;
113	xfer->recv.pay_len = rlen;
114
115	return xfer;
116}
117
118struct fw_xfer *
119fwmem_read_quad(
120	struct fw_device *fwdev,
121	caddr_t	sc,
122	u_int8_t spd,
123	u_int16_t dst_hi,
124	u_int32_t dst_lo,
125	void *data,
126	void (*hand)(struct fw_xfer *))
127{
128	struct fw_xfer *xfer;
129	struct fw_pkt *fp;
130
131	xfer = fwmem_xfer_req(fwdev, (void *)sc, spd, 0, 4, hand);
132	if (xfer == NULL) {
133		return NULL;
134	}
135
136	fp = &xfer->send.hdr;
137	fp->mode.rreqq.tcode = FWTCODE_RREQQ;
138	fp->mode.rreqq.dest_hi = dst_hi;
139	fp->mode.rreqq.dest_lo = dst_lo;
140
141	xfer->send.payload = NULL;
142	xfer->recv.payload = (u_int32_t *)data;
143
144	if (fwmem_debug)
145		printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst,
146				dst_hi, dst_lo);
147
148	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
149		return xfer;
150
151	fw_xfer_free(xfer);
152	return NULL;
153}
154
155struct fw_xfer *
156fwmem_write_quad(
157	struct fw_device *fwdev,
158	caddr_t	sc,
159	u_int8_t spd,
160	u_int16_t dst_hi,
161	u_int32_t dst_lo,
162	void *data,
163	void (*hand)(struct fw_xfer *))
164{
165	struct fw_xfer *xfer;
166	struct fw_pkt *fp;
167
168	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand);
169	if (xfer == NULL)
170		return NULL;
171
172	fp = &xfer->send.hdr;
173	fp->mode.wreqq.tcode = FWTCODE_WREQQ;
174	fp->mode.wreqq.dest_hi = dst_hi;
175	fp->mode.wreqq.dest_lo = dst_lo;
176	fp->mode.wreqq.data = *(u_int32_t *)data;
177
178	xfer->send.payload = xfer->recv.payload = NULL;
179
180	if (fwmem_debug)
181		printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst,
182			dst_hi, dst_lo, *(u_int32_t *)data);
183
184	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
185		return xfer;
186
187	fw_xfer_free(xfer);
188	return NULL;
189}
190
191struct fw_xfer *
192fwmem_read_block(
193	struct fw_device *fwdev,
194	caddr_t	sc,
195	u_int8_t spd,
196	u_int16_t dst_hi,
197	u_int32_t dst_lo,
198	int len,
199	void *data,
200	void (*hand)(struct fw_xfer *))
201{
202	struct fw_xfer *xfer;
203	struct fw_pkt *fp;
204
205	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand);
206	if (xfer == NULL)
207		return NULL;
208
209	fp = &xfer->send.hdr;
210	fp->mode.rreqb.tcode = FWTCODE_RREQB;
211	fp->mode.rreqb.dest_hi = dst_hi;
212	fp->mode.rreqb.dest_lo = dst_lo;
213	fp->mode.rreqb.len = len;
214	fp->mode.rreqb.extcode = 0;
215
216	xfer->send.payload = NULL;
217	xfer->recv.payload = data;
218
219	if (fwmem_debug)
220		printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst,
221				dst_hi, dst_lo, len);
222	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
223		return xfer;
224
225	fw_xfer_free(xfer);
226	return NULL;
227}
228
229struct fw_xfer *
230fwmem_write_block(
231	struct fw_device *fwdev,
232	caddr_t	sc,
233	u_int8_t spd,
234	u_int16_t dst_hi,
235	u_int32_t dst_lo,
236	int len,
237	void *data,
238	void (*hand)(struct fw_xfer *))
239{
240	struct fw_xfer *xfer;
241	struct fw_pkt *fp;
242
243	xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand);
244	if (xfer == NULL)
245		return NULL;
246
247	fp = &xfer->send.hdr;
248	fp->mode.wreqb.tcode = FWTCODE_WREQB;
249	fp->mode.wreqb.dest_hi = dst_hi;
250	fp->mode.wreqb.dest_lo = dst_lo;
251	fp->mode.wreqb.len = len;
252	fp->mode.wreqb.extcode = 0;
253
254	xfer->send.payload = data;
255	xfer->recv.payload = NULL;
256
257	if (fwmem_debug)
258		printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst,
259				dst_hi, dst_lo, len);
260	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
261		return xfer;
262
263	fw_xfer_free(xfer);
264	return NULL;
265}
266
267
268int
269fwmem_open (dev_t dev, int flags, int fmt, fw_proc *td)
270{
271	struct fwmem_softc *fms;
272
273	if (dev->si_drv1 != NULL) {
274		if ((flags & FWRITE) != 0)
275			return (EBUSY);
276		fms = (struct fwmem_softc *)dev->si_drv1;
277		fms->refcount ++;
278	} else {
279		fms = (struct fwmem_softc *)malloc(sizeof(struct fwmem_softc),
280							M_FWMEM, M_WAITOK);
281		if (fms == NULL)
282			return ENOMEM;
283		bcopy(&fwmem_eui64, &fms->eui, sizeof(struct fw_eui64));
284		dev->si_drv1 = (void *)fms;
285		dev->si_iosize_max = DFLTPHYS;
286		fms->refcount = 1;
287	}
288	if (fwmem_debug)
289		printf("%s: refcount=%d\n", __FUNCTION__, fms->refcount);
290
291	return (0);
292}
293
294int
295fwmem_close (dev_t dev, int flags, int fmt, fw_proc *td)
296{
297	struct fwmem_softc *fms;
298
299	fms = (struct fwmem_softc *)dev->si_drv1;
300	fms->refcount --;
301	if (fwmem_debug)
302		printf("%s: refcount=%d\n", __FUNCTION__, fms->refcount);
303	if (fms->refcount < 1) {
304		free(dev->si_drv1, M_FW);
305		dev->si_drv1 = NULL;
306	}
307
308	return (0);
309}
310
311
312static void
313fwmem_biodone(struct fw_xfer *xfer)
314{
315	struct bio *bp;
316
317	bp = (struct bio *)xfer->sc;
318	bp->bio_error = xfer->resp;
319
320	if (bp->bio_error != 0) {
321		if (fwmem_debug)
322			printf("%s: err=%d\n", __FUNCTION__, bp->bio_error);
323		bp->bio_flags |= BIO_ERROR;
324		bp->bio_resid = bp->bio_bcount;
325	}
326
327	fw_xfer_free(xfer);
328	biodone(bp);
329}
330
331void
332fwmem_strategy(struct bio *bp)
333{
334	struct firewire_softc *sc;
335	struct fwmem_softc *fms;
336	struct fw_device *fwdev;
337	struct fw_xfer *xfer;
338	dev_t dev;
339	int unit, err=0, s, iolen;
340
341	dev = bp->bio_dev;
342	/* XXX check request length */
343
344        unit = DEV2UNIT(dev);
345	sc = devclass_get_softc(firewire_devclass, unit);
346
347	s = splfw();
348	fms = (struct fwmem_softc *)dev->si_drv1;
349	fwdev = fw_noderesolve_eui64(sc->fc, &fms->eui);
350	if (fwdev == NULL) {
351		if (fwmem_debug)
352			printf("fwmem: no such device ID:%08x%08x\n",
353					fms->eui.hi, fms->eui.lo);
354		err = EINVAL;
355		goto error;
356	}
357
358	iolen = MIN(bp->bio_bcount, MAXLEN);
359	if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
360		if (iolen == 4 && (bp->bio_offset & 3) == 0)
361			xfer = fwmem_read_quad(fwdev,
362			    (void *) bp, fwmem_speed,
363			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
364			    bp->bio_data, fwmem_biodone);
365		else
366			xfer = fwmem_read_block(fwdev,
367			    (void *) bp, fwmem_speed,
368			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
369			    iolen, bp->bio_data, fwmem_biodone);
370	} else {
371		if (iolen == 4 && (bp->bio_offset & 3) == 0)
372			xfer = fwmem_write_quad(fwdev,
373			    (void *)bp, fwmem_speed,
374			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
375			    bp->bio_data, fwmem_biodone);
376		else
377			xfer = fwmem_write_block(fwdev,
378			    (void *)bp, fwmem_speed,
379			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
380			    iolen, bp->bio_data, fwmem_biodone);
381	}
382	if (xfer == NULL) {
383		err = EIO;
384		goto error;
385	}
386	/* XXX */
387	bp->bio_resid = bp->bio_bcount - iolen;
388error:
389	splx(s);
390	if (err != 0) {
391		if (fwmem_debug)
392			printf("%s: err=%d\n", __FUNCTION__, err);
393		bp->bio_error = err;
394		bp->bio_flags |= BIO_ERROR;
395		bp->bio_resid = bp->bio_bcount;
396		biodone(bp);
397	}
398}
399
400int
401fwmem_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
402{
403	struct fwmem_softc *fms;
404	int err = 0;
405
406	fms = (struct fwmem_softc *)dev->si_drv1;
407	switch (cmd) {
408	case FW_SDEUI64:
409		bcopy(data, &fms->eui, sizeof(struct fw_eui64));
410		break;
411	case FW_GDEUI64:
412		bcopy(&fms->eui, data, sizeof(struct fw_eui64));
413		break;
414	default:
415		err = EINVAL;
416	}
417	return(err);
418}
419int
420fwmem_poll (dev_t dev, int events, fw_proc *td)
421{
422	return EINVAL;
423}
424int
425#if __FreeBSD_version < 500102
426fwmem_mmap (dev_t dev, vm_offset_t offset, int nproto)
427#else
428fwmem_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto)
429#endif
430{
431	return EINVAL;
432}
433