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#ifdef __FreeBSD__
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD$");
39#endif
40
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/types.h>
44
45#include <sys/kernel.h>
46#include <sys/malloc.h>
47#include <sys/conf.h>
48#include <sys/sysctl.h>
49#include <sys/bio.h>
50
51#include <sys/bus.h>
52#include <machine/bus.h>
53
54#include <sys/signal.h>
55#include <sys/mman.h>
56#include <sys/ioccom.h>
57#include <sys/fcntl.h>
58
59#include <dev/firewire/firewire.h>
60#include <dev/firewire/firewirereg.h>
61#include <dev/firewire/fwmem.h>
62
63static int fwmem_speed = 2, fwmem_debug = 0;
64static struct fw_eui64 fwmem_eui64;
65SYSCTL_DECL(_hw_firewire);
66static SYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem, CTLFLAG_RD, 0,
67	"FireWire Memory Access");
68SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_hi, CTLFLAG_RW,
69	&fwmem_eui64.hi, 0, "Fwmem target EUI64 high");
70SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_lo, CTLFLAG_RW,
71	&fwmem_eui64.lo, 0, "Fwmem target EUI64 low");
72SYSCTL_INT(_hw_firewire_fwmem, OID_AUTO, speed, CTLFLAG_RW, &fwmem_speed, 0,
73	"Fwmem link speed");
74SYSCTL_INT(_debug, OID_AUTO, fwmem_debug, CTLFLAG_RW, &fwmem_debug, 0,
75	"Fwmem driver debug flag");
76
77static MALLOC_DEFINE(M_FWMEM, "fwmem", "fwmem/FireWire");
78
79#define MAXLEN (512 << fwmem_speed)
80
81struct fwmem_softc {
82	struct fw_eui64 eui;
83	struct firewire_softc *sc;
84	int refcount;
85};
86
87static struct fw_xfer *
88fwmem_xfer_req(
89	struct fw_device *fwdev,
90	caddr_t sc,
91	int spd,
92	int slen,
93	int rlen,
94	void *hand)
95{
96	struct fw_xfer *xfer;
97
98	xfer = fw_xfer_alloc(M_FWMEM);
99	if (xfer == NULL)
100		return NULL;
101
102	xfer->fc = fwdev->fc;
103	xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst;
104	if (spd < 0)
105		xfer->send.spd = fwdev->speed;
106	else
107		xfer->send.spd = min(spd, fwdev->speed);
108	xfer->hand = hand;
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	uint8_t spd,
121	uint16_t dst_hi,
122	uint32_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, 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 = (uint32_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	uint8_t spd,
158	uint16_t dst_hi,
159	uint32_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 = *(uint32_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, *(uint32_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	uint8_t spd,
194	uint16_t dst_hi,
195	uint32_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	uint8_t spd,
232	uint16_t dst_hi,
233	uint32_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
265int
266fwmem_open(struct cdev *dev, int flags, int fmt, fw_proc *td)
267{
268	struct fwmem_softc *fms;
269	struct firewire_softc *sc;
270	int unit = DEV2UNIT(dev);
271
272	sc = devclass_get_softc(firewire_devclass, unit);
273	if (sc == NULL)
274		return (ENXIO);
275
276	FW_GLOCK(sc->fc);
277	if (dev->si_drv1 != NULL) {
278		if ((flags & FWRITE) != 0) {
279			FW_GUNLOCK(sc->fc);
280			return (EBUSY);
281		}
282		FW_GUNLOCK(sc->fc);
283		fms = dev->si_drv1;
284		fms->refcount++;
285	} else {
286		dev->si_drv1 = (void *)-1;
287		FW_GUNLOCK(sc->fc);
288		dev->si_drv1 = malloc(sizeof(struct fwmem_softc),
289		    M_FWMEM, M_WAITOK);
290		dev->si_iosize_max = DFLTPHYS;
291		fms = dev->si_drv1;
292		bcopy(&fwmem_eui64, &fms->eui, sizeof(struct fw_eui64));
293		fms->sc = sc;
294		fms->refcount = 1;
295	}
296	if (fwmem_debug)
297		printf("%s: refcount=%d\n", __func__, fms->refcount);
298
299	return (0);
300}
301
302int
303fwmem_close (struct cdev *dev, int flags, int fmt, fw_proc *td)
304{
305	struct fwmem_softc *fms;
306
307	fms = dev->si_drv1;
308
309	FW_GLOCK(fms->sc->fc);
310	fms->refcount--;
311	FW_GUNLOCK(fms->sc->fc);
312	if (fwmem_debug)
313		printf("%s: refcount=%d\n", __func__, fms->refcount);
314	if (fms->refcount < 1) {
315		free(dev->si_drv1, M_FWMEM);
316		dev->si_drv1 = NULL;
317	}
318
319	return (0);
320}
321
322
323static void
324fwmem_biodone(struct fw_xfer *xfer)
325{
326	struct bio *bp;
327
328	bp = (struct bio *)xfer->sc;
329	bp->bio_error = xfer->resp;
330
331	if (bp->bio_error != 0) {
332		if (fwmem_debug)
333			printf("%s: err=%d\n", __func__, bp->bio_error);
334		bp->bio_flags |= BIO_ERROR;
335		bp->bio_resid = bp->bio_bcount;
336	}
337
338	fw_xfer_free(xfer);
339	biodone(bp);
340}
341
342void
343fwmem_strategy(struct bio *bp)
344{
345	struct fwmem_softc *fms;
346	struct fw_device *fwdev;
347	struct fw_xfer *xfer;
348	struct cdev *dev;
349	int err = 0, iolen;
350
351	dev = bp->bio_dev;
352	/* XXX check request length */
353
354	fms = dev->si_drv1;
355	fwdev = fw_noderesolve_eui64(fms->sc->fc, &fms->eui);
356	if (fwdev == NULL) {
357		if (fwmem_debug)
358			printf("fwmem: no such device ID:%08x%08x\n",
359			    fms->eui.hi, fms->eui.lo);
360		err = EINVAL;
361		goto error;
362	}
363
364	iolen = MIN(bp->bio_bcount, MAXLEN);
365	if (bp->bio_cmd == BIO_READ) {
366		if (iolen == 4 && (bp->bio_offset & 3) == 0)
367			xfer = fwmem_read_quad(fwdev,
368			    (void *)bp, fwmem_speed,
369			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
370			    bp->bio_data, fwmem_biodone);
371		else
372			xfer = fwmem_read_block(fwdev,
373			    (void *)bp, fwmem_speed,
374			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
375			    iolen, bp->bio_data, fwmem_biodone);
376	} else {
377		if (iolen == 4 && (bp->bio_offset & 3) == 0)
378			xfer = fwmem_write_quad(fwdev,
379			    (void *)bp, fwmem_speed,
380			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
381			    bp->bio_data, fwmem_biodone);
382		else
383			xfer = fwmem_write_block(fwdev,
384			    (void *)bp, fwmem_speed,
385			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
386			    iolen, bp->bio_data, fwmem_biodone);
387	}
388	if (xfer == NULL) {
389		err = EIO;
390		goto error;
391	}
392	/* XXX */
393	bp->bio_resid = bp->bio_bcount - iolen;
394error:
395	if (err != 0) {
396		if (fwmem_debug)
397			printf("%s: err=%d\n", __func__, err);
398		bp->bio_error = err;
399		bp->bio_flags |= BIO_ERROR;
400		bp->bio_resid = bp->bio_bcount;
401		biodone(bp);
402	}
403}
404
405int
406fwmem_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
407{
408	struct fwmem_softc *fms;
409	int err = 0;
410
411	fms = dev->si_drv1;
412	switch (cmd) {
413	case FW_SDEUI64:
414		bcopy(data, &fms->eui, sizeof(struct fw_eui64));
415		break;
416	case FW_GDEUI64:
417		bcopy(&fms->eui, data, sizeof(struct fw_eui64));
418		break;
419	default:
420		err = EINVAL;
421	}
422	return (err);
423}
424
425int
426fwmem_poll(struct cdev *dev, int events, fw_proc *td)
427{
428	return EINVAL;
429}
430
431int
432fwmem_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
433    int nproto, vm_memattr_t *memattr)
434{
435	return EINVAL;
436}
437