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