fwmem.c revision 110465
1103285Sikob/*
2103285Sikob * Copyright (C) 2002
3103285Sikob * 	Hidetoshi Shimokawa. All rights reserved.
4103285Sikob *
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.
21103285Sikob *
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.
33103285Sikob *
34103285Sikob * $FreeBSD: head/sys/dev/firewire/fwmem.c 110465 2003-02-06 17:23:01Z simokawa $
35103285Sikob */
36103285Sikob
37103285Sikob#include <sys/param.h>
38103285Sikob#include <sys/systm.h>
39103285Sikob#include <sys/types.h>
40103285Sikob
41103285Sikob#include <sys/kernel.h>
42103285Sikob#include <sys/malloc.h>
43103285Sikob#include <sys/conf.h>
44103285Sikob#include <sys/uio.h>
45103285Sikob#include <sys/sysctl.h>
46103285Sikob
47103285Sikob#include <sys/bus.h>
48103285Sikob
49103285Sikob#include <sys/signal.h>
50103285Sikob#include <sys/mman.h>
51103285Sikob#include <sys/ioccom.h>
52103285Sikob
53103285Sikob#include <dev/firewire/firewire.h>
54103285Sikob#include <dev/firewire/firewirereg.h>
55103285Sikob#include <dev/firewire/fwmem.h>
56103285Sikob
57106810Ssimokawastatic int fwmem_speed=2, fwmem_debug=0;
58106810Ssimokawastatic struct fw_eui64 fwmem_eui64;
59103285SikobSYSCTL_DECL(_hw_firewire);
60103285SikobSYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem, CTLFLAG_RD, 0,
61108281Ssimokawa	"FireWire Memory Access");
62106810SsimokawaSYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_hi, CTLFLAG_RW,
63106810Ssimokawa	&fwmem_eui64.hi, 0, "Fwmem target EUI64 high");
64106810SsimokawaSYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_low, CTLFLAG_RW,
65106810Ssimokawa	&fwmem_eui64.lo, 0, "Fwmem target EUI64 low");
66103285SikobSYSCTL_INT(_hw_firewire_fwmem, OID_AUTO, speed, CTLFLAG_RW, &fwmem_speed, 0,
67103285Sikob	"Fwmem link speed");
68103285SikobSYSCTL_INT(_debug, OID_AUTO, fwmem_debug, CTLFLAG_RW, &fwmem_debug, 0,
69103285Sikob	"Fwmem driver debug flag");
70103285Sikob
71106816Ssimokawastatic struct fw_xfer *fwmem_xfer_req(struct fw_device *, caddr_t,
72106816Ssimokawa							int, int, void *);
73106816Ssimokawa
74106816Ssimokawastatic struct fw_xfer *
75106816Ssimokawafwmem_xfer_req(
76106810Ssimokawa	struct fw_device *fwdev,
77106816Ssimokawa	caddr_t sc,
78106816Ssimokawa	int spd,
79106816Ssimokawa	int len,
80106816Ssimokawa	void *hand)
81103285Sikob{
82103285Sikob	struct fw_xfer *xfer;
83103285Sikob
84110269Ssimokawa	xfer = fw_xfer_alloc(M_FWXFER);
85106804Ssimokawa	if (xfer == NULL)
86103285Sikob		return NULL;
87106804Ssimokawa
88106810Ssimokawa	xfer->fc = fwdev->fc;
89106810Ssimokawa	xfer->dst = FWLOCALBUS | fwdev->dst;
90106816Ssimokawa	if (spd < 0)
91106816Ssimokawa		xfer->spd = fwdev->speed;
92106816Ssimokawa	else
93106816Ssimokawa		xfer->spd = min(spd, fwdev->speed);
94106816Ssimokawa	xfer->send.len = len;
95110195Ssimokawa	xfer->send.buf = malloc(len, M_FW, M_NOWAIT | M_ZERO);
96106804Ssimokawa
97106816Ssimokawa	if (xfer->send.buf == NULL) {
98106816Ssimokawa		fw_xfer_free(xfer);
99106816Ssimokawa		return NULL;
100106816Ssimokawa	}
101106804Ssimokawa
102103285Sikob	xfer->send.off = 0;
103106804Ssimokawa	xfer->act.hand = hand;
104103285Sikob	xfer->retry_req = fw_asybusy;
105106816Ssimokawa	xfer->sc = sc;
106103285Sikob
107106816Ssimokawa	return xfer;
108106816Ssimokawa}
109106816Ssimokawa
110106816Ssimokawastruct fw_xfer *
111106816Ssimokawafwmem_read_quad(
112106816Ssimokawa	struct fw_device *fwdev,
113106816Ssimokawa	caddr_t	sc,
114106816Ssimokawa	u_int8_t spd,
115106816Ssimokawa	u_int16_t dst_hi,
116106816Ssimokawa	u_int32_t dst_lo,
117106816Ssimokawa	void (*hand)(struct fw_xfer *))
118106816Ssimokawa{
119106816Ssimokawa	struct fw_xfer *xfer;
120106816Ssimokawa	struct fw_pkt *fp;
121106816Ssimokawa
122106816Ssimokawa	xfer = fwmem_xfer_req(fwdev, sc, spd, 12, hand);
123106816Ssimokawa	if (xfer == NULL)
124106816Ssimokawa		return NULL;
125106816Ssimokawa
126103285Sikob	fp = (struct fw_pkt *)xfer->send.buf;
127103285Sikob	fp->mode.rreqq.tcode = FWTCODE_RREQQ;
128103285Sikob	fp->mode.rreqq.dst = htons(xfer->dst);
129103285Sikob	fp->mode.rreqq.dest_hi = htons(dst_hi);
130103285Sikob	fp->mode.rreqq.dest_lo = htonl(dst_lo);
131103285Sikob
132103285Sikob	if (fwmem_debug)
133106816Ssimokawa		printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst,
134106816Ssimokawa				dst_hi, dst_lo);
135106804Ssimokawa
136106810Ssimokawa	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
137103285Sikob		return xfer;
138106804Ssimokawa
139103285Sikob	fw_xfer_free(xfer);
140103285Sikob	return NULL;
141103285Sikob}
142103285Sikob
143103285Sikobstruct fw_xfer *
144106810Ssimokawafwmem_write_quad(
145106810Ssimokawa	struct fw_device *fwdev,
146106810Ssimokawa	caddr_t	sc,
147106810Ssimokawa	u_int8_t spd,
148106810Ssimokawa	u_int16_t dst_hi,
149106810Ssimokawa	u_int32_t dst_lo,
150106810Ssimokawa	u_int32_t data,
151106810Ssimokawa	void (*hand)(struct fw_xfer *))
152106810Ssimokawa{
153106810Ssimokawa	struct fw_xfer *xfer;
154106810Ssimokawa	struct fw_pkt *fp;
155106810Ssimokawa
156106816Ssimokawa	xfer = fwmem_xfer_req(fwdev, sc, spd, 16, hand);
157106810Ssimokawa	if (xfer == NULL)
158106810Ssimokawa		return NULL;
159106810Ssimokawa
160106810Ssimokawa	fp = (struct fw_pkt *)xfer->send.buf;
161110072Ssimokawa	fp->mode.wreqq.tcode = FWTCODE_WREQQ;
162106810Ssimokawa	fp->mode.wreqq.dst = htons(xfer->dst);
163106810Ssimokawa	fp->mode.wreqq.dest_hi = htons(dst_hi);
164106810Ssimokawa	fp->mode.wreqq.dest_lo = htonl(dst_lo);
165106810Ssimokawa
166106810Ssimokawa	fp->mode.wreqq.data = htonl(data);
167106810Ssimokawa
168106810Ssimokawa	if (fwmem_debug)
169106816Ssimokawa		printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst,
170106816Ssimokawa			dst_hi, dst_lo, data);
171106810Ssimokawa
172106810Ssimokawa	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
173106810Ssimokawa		return xfer;
174106810Ssimokawa
175106810Ssimokawa	fw_xfer_free(xfer);
176106810Ssimokawa	return NULL;
177106810Ssimokawa}
178106810Ssimokawa
179106810Ssimokawastruct fw_xfer *
180103285Sikobfwmem_read_block(
181106810Ssimokawa	struct fw_device *fwdev,
182106810Ssimokawa	caddr_t	sc,
183106804Ssimokawa	u_int8_t spd,
184103285Sikob	u_int16_t dst_hi,
185103285Sikob	u_int32_t dst_lo,
186106804Ssimokawa	int len,
187106804Ssimokawa	void (*hand)(struct fw_xfer *))
188103285Sikob{
189103285Sikob	struct fw_xfer *xfer;
190103285Sikob	struct fw_pkt *fp;
191103285Sikob
192106816Ssimokawa	xfer = fwmem_xfer_req(fwdev, sc, spd, 16, hand);
193106804Ssimokawa	if (xfer == NULL)
194103285Sikob		return NULL;
195106804Ssimokawa
196103285Sikob	fp = (struct fw_pkt *)xfer->send.buf;
197103285Sikob	fp->mode.rreqb.tcode = FWTCODE_RREQB;
198103285Sikob	fp->mode.rreqb.dst = htons(xfer->dst);
199103285Sikob	fp->mode.rreqb.dest_hi = htons(dst_hi);
200103285Sikob	fp->mode.rreqb.dest_lo = htonl(dst_lo);
201103285Sikob	fp->mode.rreqb.len = htons(len);
202103285Sikob
203103285Sikob	if (fwmem_debug)
204106816Ssimokawa		printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst,
205106810Ssimokawa				dst_hi, dst_lo, len);
206106810Ssimokawa	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
207103285Sikob		return xfer;
208106804Ssimokawa
209103285Sikob	fw_xfer_free(xfer);
210103285Sikob	return NULL;
211103285Sikob}
212103285Sikob
213110337Ssimokawastruct fw_xfer *
214110337Ssimokawafwmem_write_block(
215110337Ssimokawa	struct fw_device *fwdev,
216110337Ssimokawa	caddr_t	sc,
217110337Ssimokawa	u_int8_t spd,
218110337Ssimokawa	u_int16_t dst_hi,
219110337Ssimokawa	u_int32_t dst_lo,
220110337Ssimokawa	int len,
221110337Ssimokawa	char *data,
222110337Ssimokawa	void (*hand)(struct fw_xfer *))
223110337Ssimokawa{
224110337Ssimokawa	struct fw_xfer *xfer;
225110337Ssimokawa	struct fw_pkt *fp;
226110337Ssimokawa
227110337Ssimokawa	xfer = fwmem_xfer_req(fwdev, sc, spd, 16 + roundup(len, 4), hand);
228110337Ssimokawa	if (xfer == NULL)
229110337Ssimokawa		return NULL;
230110337Ssimokawa
231110337Ssimokawa	fp = (struct fw_pkt *)xfer->send.buf;
232110406Ssimokawa	fp->mode.wreqb.tcode = FWTCODE_WREQB;
233110337Ssimokawa	fp->mode.wreqb.dst = htons(xfer->dst);
234110337Ssimokawa	fp->mode.wreqb.dest_hi = htons(dst_hi);
235110337Ssimokawa	fp->mode.wreqb.dest_lo = htonl(dst_lo);
236110337Ssimokawa	fp->mode.wreqb.len = htons(len);
237110337Ssimokawa	bcopy(data, &fp->mode.wreqb.payload[0], len);
238110337Ssimokawa
239110337Ssimokawa	if (fwmem_debug)
240110337Ssimokawa		printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst,
241110337Ssimokawa				dst_hi, dst_lo, len);
242110337Ssimokawa	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
243110337Ssimokawa		return xfer;
244110337Ssimokawa
245110337Ssimokawa	fw_xfer_free(xfer);
246110337Ssimokawa	return NULL;
247110337Ssimokawa}
248110337Ssimokawa
249110337Ssimokawa
250103285Sikobint
251103285Sikobfwmem_open (dev_t dev, int flags, int fmt, fw_proc *td)
252103285Sikob{
253103285Sikob	int err = 0;
254103285Sikob	return err;
255103285Sikob}
256103285Sikob
257103285Sikobint
258103285Sikobfwmem_close (dev_t dev, int flags, int fmt, fw_proc *td)
259103285Sikob{
260103285Sikob	int err = 0;
261103285Sikob	return err;
262103285Sikob}
263103285Sikob
264103285Sikob#define MAXLEN 2048
265103285Sikob#define USE_QUAD 0
266103285Sikobint
267103285Sikobfwmem_read (dev_t dev, struct uio *uio, int ioflag)
268103285Sikob{
269103285Sikob	struct firewire_softc *sc;
270106810Ssimokawa	struct fw_device *fwdev;
271103285Sikob	struct fw_xfer *xfer;
272110337Ssimokawa	int err = 0;
273103285Sikob        int unit = DEV2UNIT(dev);
274103285Sikob	u_int16_t dst_hi;
275103285Sikob	u_int32_t dst_lo;
276103285Sikob	off_t offset;
277103285Sikob	int len;
278103285Sikob
279103285Sikob	sc = devclass_get_softc(firewire_devclass, unit);
280110072Ssimokawa	fwdev = fw_noderesolve_eui64(sc->fc, fwmem_eui64);
281106810Ssimokawa	if (fwdev == NULL) {
282106810Ssimokawa		printf("fwmem: no such device ID:%08x%08x\n",
283106810Ssimokawa			fwmem_eui64.hi, fwmem_eui64.lo);
284106810Ssimokawa		return EINVAL;
285106810Ssimokawa	}
286103285Sikob
287110465Ssimokawa	while(uio->uio_resid > 0 && !err) {
288103285Sikob		offset = uio->uio_offset;
289103285Sikob		dst_hi = (offset >> 32) & 0xffff;
290103285Sikob		dst_lo = offset & 0xffffffff;
291110337Ssimokawa		len = uio->uio_resid;
292110337Ssimokawa		if (len == 4 && (dst_lo & 3) == 0) {
293110337Ssimokawa			xfer = fwmem_read_quad(fwdev, NULL, fwmem_speed,
294106804Ssimokawa				dst_hi, dst_lo, fw_asy_callback);
295110465Ssimokawa			if (xfer == NULL) {
296110465Ssimokawa				err = EINVAL;
297110465Ssimokawa				break;
298110465Ssimokawa			}
299110337Ssimokawa			err = tsleep((caddr_t)xfer, FWPRI, "fwmrq", hz);
300110337Ssimokawa			if (err !=0 || xfer->resp != 0
301110337Ssimokawa					|| xfer->recv.buf == NULL)
302110406Ssimokawa				err = EIO;
303110406Ssimokawa			else
304110406Ssimokawa				err = uiomove(xfer->recv.buf
305110406Ssimokawa					+ xfer->recv.off + 4*3, 4, uio);
306110337Ssimokawa		} else {
307110337Ssimokawa			if (len > MAXLEN)
308110337Ssimokawa				len = MAXLEN;
309110337Ssimokawa			xfer = fwmem_read_block(fwdev, NULL, fwmem_speed,
310106804Ssimokawa				dst_hi, dst_lo, len, fw_asy_callback);
311110465Ssimokawa			if (xfer == NULL) {
312110465Ssimokawa				err = EINVAL;
313110465Ssimokawa				break;
314110465Ssimokawa			}
315110337Ssimokawa			err = tsleep((caddr_t)xfer, FWPRI, "fwmrb", hz);
316110337Ssimokawa			if (err != 0 || xfer->resp != 0
317110337Ssimokawa					|| xfer->recv.buf == NULL)
318110406Ssimokawa				err = EIO;
319110406Ssimokawa			else
320110406Ssimokawa				err = uiomove(xfer->recv.buf
321110406Ssimokawa					+ xfer->recv.off + 4*4, len, uio);
322110337Ssimokawa		}
323110337Ssimokawa		fw_xfer_free(xfer);
324103285Sikob	}
325103285Sikob	return err;
326103285Sikob}
327103285Sikobint
328103285Sikobfwmem_write (dev_t dev, struct uio *uio, int ioflag)
329103285Sikob{
330110337Ssimokawa	struct firewire_softc *sc;
331110337Ssimokawa	struct fw_device *fwdev;
332110337Ssimokawa	struct fw_xfer *xfer;
333110337Ssimokawa	int err = 0;
334110337Ssimokawa        int unit = DEV2UNIT(dev);
335110337Ssimokawa	u_int16_t dst_hi;
336110337Ssimokawa	u_int32_t dst_lo, quad;
337110337Ssimokawa	char *data;
338110337Ssimokawa	off_t offset;
339110337Ssimokawa	int len;
340110337Ssimokawa
341110337Ssimokawa	sc = devclass_get_softc(firewire_devclass, unit);
342110337Ssimokawa	fwdev = fw_noderesolve_eui64(sc->fc, fwmem_eui64);
343110337Ssimokawa	if (fwdev == NULL) {
344110337Ssimokawa		printf("fwmem: no such device ID:%08x%08x\n",
345110337Ssimokawa			fwmem_eui64.hi, fwmem_eui64.lo);
346110337Ssimokawa		return EINVAL;
347110337Ssimokawa	}
348110337Ssimokawa
349110337Ssimokawa	data = malloc(MAXLEN, M_FW, 0);
350110337Ssimokawa	if (data == NULL)
351110337Ssimokawa		return ENOMEM;
352110337Ssimokawa
353110465Ssimokawa	while(uio->uio_resid > 0 && !err) {
354110337Ssimokawa		offset = uio->uio_offset;
355110337Ssimokawa		dst_hi = (offset >> 32) & 0xffff;
356110337Ssimokawa		dst_lo = offset & 0xffffffff;
357110337Ssimokawa		len = uio->uio_resid;
358110337Ssimokawa		if (len == 4 && (dst_lo & 3) == 0) {
359110337Ssimokawa			err = uiomove((char *)&quad, sizeof(quad), uio);
360110337Ssimokawa			xfer = fwmem_write_quad(fwdev, NULL, fwmem_speed,
361110337Ssimokawa				dst_hi, dst_lo, quad, fw_asy_callback);
362110465Ssimokawa			if (xfer == NULL) {
363110465Ssimokawa				err = EINVAL;
364110465Ssimokawa				break;
365110465Ssimokawa			}
366110337Ssimokawa			err = tsleep((caddr_t)xfer, FWPRI, "fwmwq", hz);
367110406Ssimokawa			if (err !=0 || xfer->resp != 0)
368110406Ssimokawa				err = EIO;
369110337Ssimokawa		} else {
370110337Ssimokawa			if (len > MAXLEN)
371110337Ssimokawa				len = MAXLEN;
372110337Ssimokawa			err = uiomove(data, len, uio);
373110337Ssimokawa			if (err)
374110465Ssimokawa				break;
375110337Ssimokawa			xfer = fwmem_write_block(fwdev, NULL, fwmem_speed,
376110337Ssimokawa				dst_hi, dst_lo, len, data, fw_asy_callback);
377110465Ssimokawa			if (xfer == NULL) {
378110465Ssimokawa				err = EINVAL;
379110465Ssimokawa				break;
380110465Ssimokawa			}
381110337Ssimokawa			err = tsleep((caddr_t)xfer, FWPRI, "fwmwb", hz);
382110406Ssimokawa			if (err != 0 || xfer->resp != 0)
383110406Ssimokawa				err = EIO;
384110337Ssimokawa		}
385110406Ssimokawa		fw_xfer_free(xfer);
386110337Ssimokawa	}
387110465Ssimokawa	free(data, M_FW);
388110337Ssimokawa	return err;
389103285Sikob}
390110337Ssimokawa
391103285Sikobint
392103285Sikobfwmem_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
393103285Sikob{
394103285Sikob	return EINVAL;
395103285Sikob}
396103285Sikobint
397103285Sikobfwmem_poll (dev_t dev, int events, fw_proc *td)
398103285Sikob{
399103285Sikob	return EINVAL;
400103285Sikob}
401103285Sikobint
402103285Sikobfwmem_mmap (dev_t dev, vm_offset_t offset, int nproto)
403103285Sikob{
404103285Sikob	return EINVAL;
405103285Sikob}
406