1139749Simp/*-
2113584Ssimokawa * Copyright (c) 2002-2003
3103285Sikob * 	Hidetoshi Shimokawa. All rights reserved.
4272214Skan *
5103285Sikob * Redistribution and use in source and binary forms, with or without
6103285Sikob * modification, are permitted provided that the following conditions
7103285Sikob * are met:
8103285Sikob * 1. Redistributions of source code must retain the above copyright
9103285Sikob *    notice, this list of conditions and the following disclaimer.
10103285Sikob * 2. Redistributions in binary form must reproduce the above copyright
11103285Sikob *    notice, this list of conditions and the following disclaimer in the
12103285Sikob *    documentation and/or other materials provided with the distribution.
13103285Sikob * 3. All advertising materials mentioning features or use of this software
14103285Sikob *    must display the following acknowledgement:
15103285Sikob *
16103285Sikob *	This product includes software developed by Hidetoshi Shimokawa.
17103285Sikob *
18103285Sikob * 4. Neither the name of the author nor the names of its contributors
19103285Sikob *    may be used to endorse or promote products derived from this software
20103285Sikob *    without specific prior written permission.
21272214Skan *
22103285Sikob * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23103285Sikob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24103285Sikob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25103285Sikob * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26103285Sikob * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27103285Sikob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28103285Sikob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29103285Sikob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30103285Sikob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31103285Sikob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32103285Sikob * SUCH DAMAGE.
33272214Skan *
34103285Sikob */
35103285Sikob
36127468Ssimokawa#ifdef __FreeBSD__
37119418Sobrien#include <sys/cdefs.h>
38119418Sobrien__FBSDID("$FreeBSD$");
39127468Ssimokawa#endif
40119418Sobrien
41103285Sikob#include <sys/param.h>
42103285Sikob#include <sys/systm.h>
43103285Sikob#include <sys/types.h>
44103285Sikob
45103285Sikob#include <sys/kernel.h>
46103285Sikob#include <sys/malloc.h>
47103285Sikob#include <sys/conf.h>
48103285Sikob#include <sys/sysctl.h>
49120660Ssimokawa#include <sys/bio.h>
50103285Sikob
51103285Sikob#include <sys/bus.h>
52113584Ssimokawa#include <machine/bus.h>
53103285Sikob
54103285Sikob#include <sys/signal.h>
55103285Sikob#include <sys/mman.h>
56103285Sikob#include <sys/ioccom.h>
57122228Ssimokawa#include <sys/fcntl.h>
58103285Sikob
59103285Sikob#include <dev/firewire/firewire.h>
60103285Sikob#include <dev/firewire/firewirereg.h>
61103285Sikob#include <dev/firewire/fwmem.h>
62103285Sikob
63272214Skanstatic int fwmem_speed = 2, fwmem_debug = 0;
64106810Ssimokawastatic struct fw_eui64 fwmem_eui64;
65103285SikobSYSCTL_DECL(_hw_firewire);
66227309Sedstatic SYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem, CTLFLAG_RD, 0,
67108281Ssimokawa	"FireWire Memory Access");
68106810SsimokawaSYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_hi, CTLFLAG_RW,
69106810Ssimokawa	&fwmem_eui64.hi, 0, "Fwmem target EUI64 high");
70110582SsimokawaSYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_lo, CTLFLAG_RW,
71106810Ssimokawa	&fwmem_eui64.lo, 0, "Fwmem target EUI64 low");
72103285SikobSYSCTL_INT(_hw_firewire_fwmem, OID_AUTO, speed, CTLFLAG_RW, &fwmem_speed, 0,
73103285Sikob	"Fwmem link speed");
74103285SikobSYSCTL_INT(_debug, OID_AUTO, fwmem_debug, CTLFLAG_RW, &fwmem_debug, 0,
75103285Sikob	"Fwmem driver debug flag");
76103285Sikob
77227293Sedstatic MALLOC_DEFINE(M_FWMEM, "fwmem", "fwmem/FireWire");
78124145Ssimokawa
79120660Ssimokawa#define MAXLEN (512 << fwmem_speed)
80120660Ssimokawa
81122228Ssimokawastruct fwmem_softc {
82122228Ssimokawa	struct fw_eui64 eui;
83170374Ssimokawa	struct firewire_softc *sc;
84122228Ssimokawa	int refcount;
85122228Ssimokawa};
86122228Ssimokawa
87106816Ssimokawastatic struct fw_xfer *
88106816Ssimokawafwmem_xfer_req(
89106810Ssimokawa	struct fw_device *fwdev,
90106816Ssimokawa	caddr_t sc,
91106816Ssimokawa	int spd,
92113584Ssimokawa	int slen,
93113584Ssimokawa	int rlen,
94106816Ssimokawa	void *hand)
95103285Sikob{
96103285Sikob	struct fw_xfer *xfer;
97103285Sikob
98124145Ssimokawa	xfer = fw_xfer_alloc(M_FWMEM);
99106804Ssimokawa	if (xfer == NULL)
100103285Sikob		return NULL;
101106804Ssimokawa
102106810Ssimokawa	xfer->fc = fwdev->fc;
103120660Ssimokawa	xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst;
104106816Ssimokawa	if (spd < 0)
105120660Ssimokawa		xfer->send.spd = fwdev->speed;
106106816Ssimokawa	else
107120660Ssimokawa		xfer->send.spd = min(spd, fwdev->speed);
108167632Ssimokawa	xfer->hand = hand;
109106816Ssimokawa	xfer->sc = sc;
110120660Ssimokawa	xfer->send.pay_len = slen;
111120660Ssimokawa	xfer->recv.pay_len = rlen;
112103285Sikob
113106816Ssimokawa	return xfer;
114106816Ssimokawa}
115106816Ssimokawa
116106816Ssimokawastruct fw_xfer *
117106816Ssimokawafwmem_read_quad(
118106816Ssimokawa	struct fw_device *fwdev,
119106816Ssimokawa	caddr_t	sc,
120129585Sdfr	uint8_t spd,
121129585Sdfr	uint16_t dst_hi,
122129585Sdfr	uint32_t dst_lo,
123120660Ssimokawa	void *data,
124106816Ssimokawa	void (*hand)(struct fw_xfer *))
125106816Ssimokawa{
126106816Ssimokawa	struct fw_xfer *xfer;
127106816Ssimokawa	struct fw_pkt *fp;
128106816Ssimokawa
129272214Skan	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 4, hand);
130120660Ssimokawa	if (xfer == NULL) {
131106816Ssimokawa		return NULL;
132120660Ssimokawa	}
133106816Ssimokawa
134120660Ssimokawa	fp = &xfer->send.hdr;
135103285Sikob	fp->mode.rreqq.tcode = FWTCODE_RREQQ;
136113584Ssimokawa	fp->mode.rreqq.dest_hi = dst_hi;
137113584Ssimokawa	fp->mode.rreqq.dest_lo = dst_lo;
138103285Sikob
139120660Ssimokawa	xfer->send.payload = NULL;
140129585Sdfr	xfer->recv.payload = (uint32_t *)data;
141120660Ssimokawa
142103285Sikob	if (fwmem_debug)
143106816Ssimokawa		printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst,
144272214Skan		    dst_hi, dst_lo);
145106804Ssimokawa
146106810Ssimokawa	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
147103285Sikob		return xfer;
148106804Ssimokawa
149103285Sikob	fw_xfer_free(xfer);
150103285Sikob	return NULL;
151103285Sikob}
152103285Sikob
153103285Sikobstruct fw_xfer *
154106810Ssimokawafwmem_write_quad(
155106810Ssimokawa	struct fw_device *fwdev,
156106810Ssimokawa	caddr_t	sc,
157129585Sdfr	uint8_t spd,
158129585Sdfr	uint16_t dst_hi,
159129585Sdfr	uint32_t dst_lo,
160120660Ssimokawa	void *data,
161106810Ssimokawa	void (*hand)(struct fw_xfer *))
162106810Ssimokawa{
163106810Ssimokawa	struct fw_xfer *xfer;
164106810Ssimokawa	struct fw_pkt *fp;
165106810Ssimokawa
166120660Ssimokawa	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand);
167106810Ssimokawa	if (xfer == NULL)
168106810Ssimokawa		return NULL;
169106810Ssimokawa
170120660Ssimokawa	fp = &xfer->send.hdr;
171110072Ssimokawa	fp->mode.wreqq.tcode = FWTCODE_WREQQ;
172113584Ssimokawa	fp->mode.wreqq.dest_hi = dst_hi;
173113584Ssimokawa	fp->mode.wreqq.dest_lo = dst_lo;
174129585Sdfr	fp->mode.wreqq.data = *(uint32_t *)data;
175106810Ssimokawa
176120660Ssimokawa	xfer->send.payload = xfer->recv.payload = NULL;
177106810Ssimokawa
178106810Ssimokawa	if (fwmem_debug)
179106816Ssimokawa		printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst,
180272214Skan		    dst_hi, dst_lo, *(uint32_t *)data);
181106810Ssimokawa
182106810Ssimokawa	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
183106810Ssimokawa		return xfer;
184106810Ssimokawa
185106810Ssimokawa	fw_xfer_free(xfer);
186106810Ssimokawa	return NULL;
187106810Ssimokawa}
188106810Ssimokawa
189106810Ssimokawastruct fw_xfer *
190103285Sikobfwmem_read_block(
191106810Ssimokawa	struct fw_device *fwdev,
192106810Ssimokawa	caddr_t	sc,
193129585Sdfr	uint8_t spd,
194129585Sdfr	uint16_t dst_hi,
195129585Sdfr	uint32_t dst_lo,
196106804Ssimokawa	int len,
197120660Ssimokawa	void *data,
198106804Ssimokawa	void (*hand)(struct fw_xfer *))
199103285Sikob{
200103285Sikob	struct fw_xfer *xfer;
201103285Sikob	struct fw_pkt *fp;
202272214Skan
203120660Ssimokawa	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand);
204106804Ssimokawa	if (xfer == NULL)
205103285Sikob		return NULL;
206106804Ssimokawa
207120660Ssimokawa	fp = &xfer->send.hdr;
208103285Sikob	fp->mode.rreqb.tcode = FWTCODE_RREQB;
209113584Ssimokawa	fp->mode.rreqb.dest_hi = dst_hi;
210113584Ssimokawa	fp->mode.rreqb.dest_lo = dst_lo;
211113584Ssimokawa	fp->mode.rreqb.len = len;
212120660Ssimokawa	fp->mode.rreqb.extcode = 0;
213103285Sikob
214120660Ssimokawa	xfer->send.payload = NULL;
215120660Ssimokawa	xfer->recv.payload = data;
216120660Ssimokawa
217103285Sikob	if (fwmem_debug)
218106816Ssimokawa		printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst,
219272214Skan		    dst_hi, dst_lo, len);
220106810Ssimokawa	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
221103285Sikob		return xfer;
222106804Ssimokawa
223103285Sikob	fw_xfer_free(xfer);
224103285Sikob	return NULL;
225103285Sikob}
226103285Sikob
227110337Ssimokawastruct fw_xfer *
228110337Ssimokawafwmem_write_block(
229110337Ssimokawa	struct fw_device *fwdev,
230110337Ssimokawa	caddr_t	sc,
231129585Sdfr	uint8_t spd,
232129585Sdfr	uint16_t dst_hi,
233129585Sdfr	uint32_t dst_lo,
234110337Ssimokawa	int len,
235120660Ssimokawa	void *data,
236110337Ssimokawa	void (*hand)(struct fw_xfer *))
237110337Ssimokawa{
238110337Ssimokawa	struct fw_xfer *xfer;
239110337Ssimokawa	struct fw_pkt *fp;
240110337Ssimokawa
241120660Ssimokawa	xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand);
242110337Ssimokawa	if (xfer == NULL)
243110337Ssimokawa		return NULL;
244110337Ssimokawa
245120660Ssimokawa	fp = &xfer->send.hdr;
246110406Ssimokawa	fp->mode.wreqb.tcode = FWTCODE_WREQB;
247113584Ssimokawa	fp->mode.wreqb.dest_hi = dst_hi;
248113584Ssimokawa	fp->mode.wreqb.dest_lo = dst_lo;
249113584Ssimokawa	fp->mode.wreqb.len = len;
250120660Ssimokawa	fp->mode.wreqb.extcode = 0;
251110337Ssimokawa
252120660Ssimokawa	xfer->send.payload = data;
253120660Ssimokawa	xfer->recv.payload = NULL;
254120660Ssimokawa
255110337Ssimokawa	if (fwmem_debug)
256110337Ssimokawa		printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst,
257110337Ssimokawa				dst_hi, dst_lo, len);
258110337Ssimokawa	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
259110337Ssimokawa		return xfer;
260110337Ssimokawa
261110337Ssimokawa	fw_xfer_free(xfer);
262110337Ssimokawa	return NULL;
263110337Ssimokawa}
264110337Ssimokawa
265103285Sikobint
266272214Skanfwmem_open(struct cdev *dev, int flags, int fmt, fw_proc *td)
267103285Sikob{
268122228Ssimokawa	struct fwmem_softc *fms;
269170374Ssimokawa	struct firewire_softc *sc;
270170374Ssimokawa	int unit = DEV2UNIT(dev);
271110582Ssimokawa
272170374Ssimokawa	sc = devclass_get_softc(firewire_devclass, unit);
273170374Ssimokawa	if (sc == NULL)
274170374Ssimokawa		return (ENXIO);
275170374Ssimokawa
276170374Ssimokawa	FW_GLOCK(sc->fc);
277122228Ssimokawa	if (dev->si_drv1 != NULL) {
278170374Ssimokawa		if ((flags & FWRITE) != 0) {
279170374Ssimokawa			FW_GUNLOCK(sc->fc);
280272214Skan			return (EBUSY);
281170374Ssimokawa		}
282170374Ssimokawa		FW_GUNLOCK(sc->fc);
283272214Skan		fms = dev->si_drv1;
284272214Skan		fms->refcount++;
285122228Ssimokawa	} else {
286170374Ssimokawa		dev->si_drv1 = (void *)-1;
287170374Ssimokawa		FW_GUNLOCK(sc->fc);
288170374Ssimokawa		dev->si_drv1 = malloc(sizeof(struct fwmem_softc),
289272214Skan		    M_FWMEM, M_WAITOK);
290170374Ssimokawa		dev->si_iosize_max = DFLTPHYS;
291272214Skan		fms = dev->si_drv1;
292122228Ssimokawa		bcopy(&fwmem_eui64, &fms->eui, sizeof(struct fw_eui64));
293170374Ssimokawa		fms->sc = sc;
294122228Ssimokawa		fms->refcount = 1;
295122228Ssimokawa	}
296122228Ssimokawa	if (fwmem_debug)
297127468Ssimokawa		printf("%s: refcount=%d\n", __func__, fms->refcount);
298110582Ssimokawa
299110582Ssimokawa	return (0);
300103285Sikob}
301103285Sikob
302103285Sikobint
303130585Sphkfwmem_close (struct cdev *dev, int flags, int fmt, fw_proc *td)
304103285Sikob{
305122228Ssimokawa	struct fwmem_softc *fms;
306115786Ssimokawa
307272214Skan	fms = dev->si_drv1;
308170374Ssimokawa
309170374Ssimokawa	FW_GLOCK(fms->sc->fc);
310272214Skan	fms->refcount--;
311170374Ssimokawa	FW_GUNLOCK(fms->sc->fc);
312122228Ssimokawa	if (fwmem_debug)
313127468Ssimokawa		printf("%s: refcount=%d\n", __func__, fms->refcount);
314122228Ssimokawa	if (fms->refcount < 1) {
315137503Ssimokawa		free(dev->si_drv1, M_FWMEM);
316122228Ssimokawa		dev->si_drv1 = NULL;
317122228Ssimokawa	}
318122228Ssimokawa
319110582Ssimokawa	return (0);
320103285Sikob}
321103285Sikob
322120660Ssimokawa
323120660Ssimokawastatic void
324120660Ssimokawafwmem_biodone(struct fw_xfer *xfer)
325103285Sikob{
326120660Ssimokawa	struct bio *bp;
327103285Sikob
328120660Ssimokawa	bp = (struct bio *)xfer->sc;
329120660Ssimokawa	bp->bio_error = xfer->resp;
330120660Ssimokawa
331120660Ssimokawa	if (bp->bio_error != 0) {
332121381Ssimokawa		if (fwmem_debug)
333127468Ssimokawa			printf("%s: err=%d\n", __func__, bp->bio_error);
334120660Ssimokawa		bp->bio_flags |= BIO_ERROR;
335120660Ssimokawa		bp->bio_resid = bp->bio_bcount;
336106810Ssimokawa	}
337103285Sikob
338120660Ssimokawa	fw_xfer_free(xfer);
339120660Ssimokawa	biodone(bp);
340103285Sikob}
341120660Ssimokawa
342120660Ssimokawavoid
343120660Ssimokawafwmem_strategy(struct bio *bp)
344103285Sikob{
345122228Ssimokawa	struct fwmem_softc *fms;
346110337Ssimokawa	struct fw_device *fwdev;
347110337Ssimokawa	struct fw_xfer *xfer;
348130585Sphk	struct cdev *dev;
349277509Swill	int err = 0, iolen;
350110337Ssimokawa
351120660Ssimokawa	dev = bp->bio_dev;
352120660Ssimokawa	/* XXX check request length */
353120660Ssimokawa
354272214Skan	fms = dev->si_drv1;
355170374Ssimokawa	fwdev = fw_noderesolve_eui64(fms->sc->fc, &fms->eui);
356110337Ssimokawa	if (fwdev == NULL) {
357110577Ssimokawa		if (fwmem_debug)
358110577Ssimokawa			printf("fwmem: no such device ID:%08x%08x\n",
359272214Skan			    fms->eui.hi, fms->eui.lo);
360120660Ssimokawa		err = EINVAL;
361120660Ssimokawa		goto error;
362110337Ssimokawa	}
363110337Ssimokawa
364120660Ssimokawa	iolen = MIN(bp->bio_bcount, MAXLEN);
365296591Simp	if (bp->bio_cmd == BIO_READ) {
366120660Ssimokawa		if (iolen == 4 && (bp->bio_offset & 3) == 0)
367120660Ssimokawa			xfer = fwmem_read_quad(fwdev,
368272214Skan			    (void *)bp, fwmem_speed,
369120660Ssimokawa			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
370120660Ssimokawa			    bp->bio_data, fwmem_biodone);
371120660Ssimokawa		else
372120660Ssimokawa			xfer = fwmem_read_block(fwdev,
373272214Skan			    (void *)bp, fwmem_speed,
374120660Ssimokawa			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
375120660Ssimokawa			    iolen, bp->bio_data, fwmem_biodone);
376120660Ssimokawa	} else {
377120660Ssimokawa		if (iolen == 4 && (bp->bio_offset & 3) == 0)
378120660Ssimokawa			xfer = fwmem_write_quad(fwdev,
379120660Ssimokawa			    (void *)bp, fwmem_speed,
380120660Ssimokawa			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
381120660Ssimokawa			    bp->bio_data, fwmem_biodone);
382120660Ssimokawa		else
383120660Ssimokawa			xfer = fwmem_write_block(fwdev,
384120660Ssimokawa			    (void *)bp, fwmem_speed,
385120660Ssimokawa			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
386120660Ssimokawa			    iolen, bp->bio_data, fwmem_biodone);
387110337Ssimokawa	}
388120660Ssimokawa	if (xfer == NULL) {
389120660Ssimokawa		err = EIO;
390120660Ssimokawa		goto error;
391120660Ssimokawa	}
392120660Ssimokawa	/* XXX */
393120660Ssimokawa	bp->bio_resid = bp->bio_bcount - iolen;
394120660Ssimokawaerror:
395120660Ssimokawa	if (err != 0) {
396121381Ssimokawa		if (fwmem_debug)
397127468Ssimokawa			printf("%s: err=%d\n", __func__, err);
398120660Ssimokawa		bp->bio_error = err;
399120660Ssimokawa		bp->bio_flags |= BIO_ERROR;
400120660Ssimokawa		bp->bio_resid = bp->bio_bcount;
401120660Ssimokawa		biodone(bp);
402120660Ssimokawa	}
403103285Sikob}
404110337Ssimokawa
405103285Sikobint
406272214Skanfwmem_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
407103285Sikob{
408122228Ssimokawa	struct fwmem_softc *fms;
409110582Ssimokawa	int err = 0;
410122228Ssimokawa
411272214Skan	fms = dev->si_drv1;
412110582Ssimokawa	switch (cmd) {
413110582Ssimokawa	case FW_SDEUI64:
414122228Ssimokawa		bcopy(data, &fms->eui, sizeof(struct fw_eui64));
415110582Ssimokawa		break;
416110582Ssimokawa	case FW_GDEUI64:
417122228Ssimokawa		bcopy(&fms->eui, data, sizeof(struct fw_eui64));
418110582Ssimokawa		break;
419110582Ssimokawa	default:
420110582Ssimokawa		err = EINVAL;
421110582Ssimokawa	}
422272214Skan	return (err);
423103285Sikob}
424272214Skan
425103285Sikobint
426272214Skanfwmem_poll(struct cdev *dev, int events, fw_proc *td)
427272214Skan{
428103285Sikob	return EINVAL;
429103285Sikob}
430272214Skan
431103285Sikobint
432272214Skanfwmem_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
433201223Srnoland    int nproto, vm_memattr_t *memattr)
434272214Skan{
435103285Sikob	return EINVAL;
436103285Sikob}
437