fwmem.c revision 113584
1103285Sikob/*
2113584Ssimokawa * Copyright (c) 2002-2003
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 113584 2003-04-17 03:38:03Z 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/sysctl.h>
45103285Sikob
46103285Sikob#include <sys/bus.h>
47113584Ssimokawa#include <machine/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");
64110582SsimokawaSYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_lo, 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 *
72106816Ssimokawafwmem_xfer_req(
73106810Ssimokawa	struct fw_device *fwdev,
74106816Ssimokawa	caddr_t sc,
75106816Ssimokawa	int spd,
76113584Ssimokawa	int slen,
77113584Ssimokawa	int rlen,
78106816Ssimokawa	void *hand)
79103285Sikob{
80103285Sikob	struct fw_xfer *xfer;
81103285Sikob
82113584Ssimokawa	xfer = fw_xfer_alloc_buf(M_FWXFER, slen, rlen);
83106804Ssimokawa	if (xfer == NULL)
84103285Sikob		return NULL;
85106804Ssimokawa
86106810Ssimokawa	xfer->fc = fwdev->fc;
87106810Ssimokawa	xfer->dst = FWLOCALBUS | fwdev->dst;
88106816Ssimokawa	if (spd < 0)
89106816Ssimokawa		xfer->spd = fwdev->speed;
90106816Ssimokawa	else
91106816Ssimokawa		xfer->spd = min(spd, fwdev->speed);
92106804Ssimokawa	xfer->act.hand = hand;
93103285Sikob	xfer->retry_req = fw_asybusy;
94106816Ssimokawa	xfer->sc = sc;
95103285Sikob
96106816Ssimokawa	return xfer;
97106816Ssimokawa}
98106816Ssimokawa
99106816Ssimokawastruct fw_xfer *
100106816Ssimokawafwmem_read_quad(
101106816Ssimokawa	struct fw_device *fwdev,
102106816Ssimokawa	caddr_t	sc,
103106816Ssimokawa	u_int8_t spd,
104106816Ssimokawa	u_int16_t dst_hi,
105106816Ssimokawa	u_int32_t dst_lo,
106106816Ssimokawa	void (*hand)(struct fw_xfer *))
107106816Ssimokawa{
108106816Ssimokawa	struct fw_xfer *xfer;
109106816Ssimokawa	struct fw_pkt *fp;
110106816Ssimokawa
111113584Ssimokawa	xfer = fwmem_xfer_req(fwdev, sc, spd, 12, 16, hand);
112106816Ssimokawa	if (xfer == NULL)
113106816Ssimokawa		return NULL;
114106816Ssimokawa
115103285Sikob	fp = (struct fw_pkt *)xfer->send.buf;
116103285Sikob	fp->mode.rreqq.tcode = FWTCODE_RREQQ;
117113584Ssimokawa	fp->mode.rreqq.dst = xfer->dst;
118113584Ssimokawa	fp->mode.rreqq.dest_hi = dst_hi;
119113584Ssimokawa	fp->mode.rreqq.dest_lo = dst_lo;
120103285Sikob
121103285Sikob	if (fwmem_debug)
122106816Ssimokawa		printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst,
123106816Ssimokawa				dst_hi, dst_lo);
124106804Ssimokawa
125106810Ssimokawa	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
126103285Sikob		return xfer;
127106804Ssimokawa
128103285Sikob	fw_xfer_free(xfer);
129103285Sikob	return NULL;
130103285Sikob}
131103285Sikob
132103285Sikobstruct fw_xfer *
133106810Ssimokawafwmem_write_quad(
134106810Ssimokawa	struct fw_device *fwdev,
135106810Ssimokawa	caddr_t	sc,
136106810Ssimokawa	u_int8_t spd,
137106810Ssimokawa	u_int16_t dst_hi,
138106810Ssimokawa	u_int32_t dst_lo,
139106810Ssimokawa	u_int32_t data,
140106810Ssimokawa	void (*hand)(struct fw_xfer *))
141106810Ssimokawa{
142106810Ssimokawa	struct fw_xfer *xfer;
143106810Ssimokawa	struct fw_pkt *fp;
144106810Ssimokawa
145113584Ssimokawa	xfer = fwmem_xfer_req(fwdev, sc, spd, 16, 12, hand);
146106810Ssimokawa	if (xfer == NULL)
147106810Ssimokawa		return NULL;
148106810Ssimokawa
149106810Ssimokawa	fp = (struct fw_pkt *)xfer->send.buf;
150110072Ssimokawa	fp->mode.wreqq.tcode = FWTCODE_WREQQ;
151113584Ssimokawa	fp->mode.wreqq.dst = xfer->dst;
152113584Ssimokawa	fp->mode.wreqq.dest_hi = dst_hi;
153113584Ssimokawa	fp->mode.wreqq.dest_lo = dst_lo;
154106810Ssimokawa
155112523Ssimokawa	fp->mode.wreqq.data = data;
156106810Ssimokawa
157106810Ssimokawa	if (fwmem_debug)
158106816Ssimokawa		printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst,
159106816Ssimokawa			dst_hi, dst_lo, data);
160106810Ssimokawa
161106810Ssimokawa	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
162106810Ssimokawa		return xfer;
163106810Ssimokawa
164106810Ssimokawa	fw_xfer_free(xfer);
165106810Ssimokawa	return NULL;
166106810Ssimokawa}
167106810Ssimokawa
168106810Ssimokawastruct fw_xfer *
169103285Sikobfwmem_read_block(
170106810Ssimokawa	struct fw_device *fwdev,
171106810Ssimokawa	caddr_t	sc,
172106804Ssimokawa	u_int8_t spd,
173103285Sikob	u_int16_t dst_hi,
174103285Sikob	u_int32_t dst_lo,
175106804Ssimokawa	int len,
176106804Ssimokawa	void (*hand)(struct fw_xfer *))
177103285Sikob{
178103285Sikob	struct fw_xfer *xfer;
179103285Sikob	struct fw_pkt *fp;
180103285Sikob
181113584Ssimokawa	xfer = fwmem_xfer_req(fwdev, sc, spd, 16, roundup2(16+len,4), hand);
182106804Ssimokawa	if (xfer == NULL)
183103285Sikob		return NULL;
184106804Ssimokawa
185103285Sikob	fp = (struct fw_pkt *)xfer->send.buf;
186103285Sikob	fp->mode.rreqb.tcode = FWTCODE_RREQB;
187113584Ssimokawa	fp->mode.rreqb.dst = xfer->dst;
188113584Ssimokawa	fp->mode.rreqb.dest_hi = dst_hi;
189113584Ssimokawa	fp->mode.rreqb.dest_lo = dst_lo;
190113584Ssimokawa	fp->mode.rreqb.len = len;
191103285Sikob
192103285Sikob	if (fwmem_debug)
193106816Ssimokawa		printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst,
194106810Ssimokawa				dst_hi, dst_lo, len);
195106810Ssimokawa	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
196103285Sikob		return xfer;
197106804Ssimokawa
198103285Sikob	fw_xfer_free(xfer);
199103285Sikob	return NULL;
200103285Sikob}
201103285Sikob
202110337Ssimokawastruct fw_xfer *
203110337Ssimokawafwmem_write_block(
204110337Ssimokawa	struct fw_device *fwdev,
205110337Ssimokawa	caddr_t	sc,
206110337Ssimokawa	u_int8_t spd,
207110337Ssimokawa	u_int16_t dst_hi,
208110337Ssimokawa	u_int32_t dst_lo,
209110337Ssimokawa	int len,
210110337Ssimokawa	char *data,
211110337Ssimokawa	void (*hand)(struct fw_xfer *))
212110337Ssimokawa{
213110337Ssimokawa	struct fw_xfer *xfer;
214110337Ssimokawa	struct fw_pkt *fp;
215110337Ssimokawa
216113584Ssimokawa	xfer = fwmem_xfer_req(fwdev, sc, spd, roundup(16+len, 4), 12, hand);
217110337Ssimokawa	if (xfer == NULL)
218110337Ssimokawa		return NULL;
219110337Ssimokawa
220110337Ssimokawa	fp = (struct fw_pkt *)xfer->send.buf;
221110406Ssimokawa	fp->mode.wreqb.tcode = FWTCODE_WREQB;
222113584Ssimokawa	fp->mode.wreqb.dst = xfer->dst;
223113584Ssimokawa	fp->mode.wreqb.dest_hi = dst_hi;
224113584Ssimokawa	fp->mode.wreqb.dest_lo = dst_lo;
225113584Ssimokawa	fp->mode.wreqb.len = len;
226110337Ssimokawa	bcopy(data, &fp->mode.wreqb.payload[0], len);
227110337Ssimokawa
228110337Ssimokawa	if (fwmem_debug)
229110337Ssimokawa		printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst,
230110337Ssimokawa				dst_hi, dst_lo, len);
231110337Ssimokawa	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
232110337Ssimokawa		return xfer;
233110337Ssimokawa
234110337Ssimokawa	fw_xfer_free(xfer);
235110337Ssimokawa	return NULL;
236110337Ssimokawa}
237110337Ssimokawa
238110337Ssimokawa
239103285Sikobint
240103285Sikobfwmem_open (dev_t dev, int flags, int fmt, fw_proc *td)
241103285Sikob{
242110582Ssimokawa	struct fw_eui64 *eui;
243110582Ssimokawa
244113584Ssimokawa	eui = (struct fw_eui64 *)malloc(sizeof(struct fw_eui64),
245113584Ssimokawa							M_FW, M_WAITOK);
246110582Ssimokawa	if (eui == NULL)
247110582Ssimokawa		return ENOMEM;
248110582Ssimokawa	bcopy(&fwmem_eui64, eui, sizeof(struct fw_eui64));
249110582Ssimokawa	dev->si_drv1 = (void *)eui;
250110582Ssimokawa
251110582Ssimokawa	return (0);
252103285Sikob}
253103285Sikob
254103285Sikobint
255103285Sikobfwmem_close (dev_t dev, int flags, int fmt, fw_proc *td)
256103285Sikob{
257110582Ssimokawa	free(dev->si_drv1, M_FW);
258110582Ssimokawa	return (0);
259103285Sikob}
260103285Sikob
261103285Sikob#define MAXLEN 2048
262103285Sikob#define USE_QUAD 0
263103285Sikobint
264103285Sikobfwmem_read (dev_t dev, struct uio *uio, int ioflag)
265103285Sikob{
266103285Sikob	struct firewire_softc *sc;
267106810Ssimokawa	struct fw_device *fwdev;
268103285Sikob	struct fw_xfer *xfer;
269110337Ssimokawa	int err = 0;
270103285Sikob        int unit = DEV2UNIT(dev);
271103285Sikob	u_int16_t dst_hi;
272103285Sikob	u_int32_t dst_lo;
273103285Sikob	off_t offset;
274103285Sikob	int len;
275103285Sikob
276103285Sikob	sc = devclass_get_softc(firewire_devclass, unit);
277110582Ssimokawa	fwdev = fw_noderesolve_eui64(sc->fc, (struct fw_eui64 *)dev->si_drv1);
278106810Ssimokawa	if (fwdev == NULL) {
279110577Ssimokawa		if (fwmem_debug)
280110577Ssimokawa			printf("fwmem: no such device ID:%08x%08x\n",
281110577Ssimokawa					fwmem_eui64.hi, fwmem_eui64.lo);
282106810Ssimokawa		return EINVAL;
283106810Ssimokawa	}
284103285Sikob
285110465Ssimokawa	while(uio->uio_resid > 0 && !err) {
286103285Sikob		offset = uio->uio_offset;
287103285Sikob		dst_hi = (offset >> 32) & 0xffff;
288103285Sikob		dst_lo = offset & 0xffffffff;
289110337Ssimokawa		len = uio->uio_resid;
290110337Ssimokawa		if (len == 4 && (dst_lo & 3) == 0) {
291110337Ssimokawa			xfer = fwmem_read_quad(fwdev, NULL, fwmem_speed,
292106804Ssimokawa				dst_hi, dst_lo, fw_asy_callback);
293110465Ssimokawa			if (xfer == NULL) {
294110465Ssimokawa				err = EINVAL;
295110465Ssimokawa				break;
296110465Ssimokawa			}
297111956Ssimokawa			err = tsleep((caddr_t)xfer, FWPRI, "fwmrq", 0);
298111956Ssimokawa			if (xfer->recv.buf == NULL)
299110406Ssimokawa				err = EIO;
300111956Ssimokawa			else if (xfer->resp != 0)
301111956Ssimokawa				err = xfer->resp;
302111956Ssimokawa			else if (err == 0)
303113584Ssimokawa				err = uiomove(xfer->recv.buf + 4*3, 4, uio);
304110337Ssimokawa		} else {
305110337Ssimokawa			if (len > MAXLEN)
306110337Ssimokawa				len = MAXLEN;
307110337Ssimokawa			xfer = fwmem_read_block(fwdev, NULL, fwmem_speed,
308106804Ssimokawa				dst_hi, dst_lo, len, fw_asy_callback);
309110465Ssimokawa			if (xfer == NULL) {
310110465Ssimokawa				err = EINVAL;
311110465Ssimokawa				break;
312110465Ssimokawa			}
313111956Ssimokawa			err = tsleep((caddr_t)xfer, FWPRI, "fwmrb", 0);
314111956Ssimokawa			if (xfer->recv.buf == NULL)
315110406Ssimokawa				err = EIO;
316111956Ssimokawa			else if (xfer->resp != 0)
317111956Ssimokawa				err = xfer->resp;
318111956Ssimokawa			else if (err == 0)
319113584Ssimokawa				err = uiomove(xfer->recv.buf + 4*4, len, uio);
320110337Ssimokawa		}
321110337Ssimokawa		fw_xfer_free(xfer);
322103285Sikob	}
323103285Sikob	return err;
324103285Sikob}
325103285Sikobint
326103285Sikobfwmem_write (dev_t dev, struct uio *uio, int ioflag)
327103285Sikob{
328110337Ssimokawa	struct firewire_softc *sc;
329110337Ssimokawa	struct fw_device *fwdev;
330110337Ssimokawa	struct fw_xfer *xfer;
331110337Ssimokawa	int err = 0;
332110337Ssimokawa        int unit = DEV2UNIT(dev);
333110337Ssimokawa	u_int16_t dst_hi;
334110337Ssimokawa	u_int32_t dst_lo, quad;
335110337Ssimokawa	char *data;
336110337Ssimokawa	off_t offset;
337110337Ssimokawa	int len;
338110337Ssimokawa
339110337Ssimokawa	sc = devclass_get_softc(firewire_devclass, unit);
340110582Ssimokawa	fwdev = fw_noderesolve_eui64(sc->fc, (struct fw_eui64 *)dev->si_drv1);
341110337Ssimokawa	if (fwdev == NULL) {
342110577Ssimokawa		if (fwmem_debug)
343110577Ssimokawa			printf("fwmem: no such device ID:%08x%08x\n",
344110577Ssimokawa					fwmem_eui64.hi, fwmem_eui64.lo);
345110337Ssimokawa		return EINVAL;
346110337Ssimokawa	}
347110337Ssimokawa
348113584Ssimokawa	data = malloc(MAXLEN, M_FW, M_WAITOK);
349110337Ssimokawa	if (data == NULL)
350110337Ssimokawa		return ENOMEM;
351110337Ssimokawa
352110465Ssimokawa	while(uio->uio_resid > 0 && !err) {
353110337Ssimokawa		offset = uio->uio_offset;
354110337Ssimokawa		dst_hi = (offset >> 32) & 0xffff;
355110337Ssimokawa		dst_lo = offset & 0xffffffff;
356110337Ssimokawa		len = uio->uio_resid;
357110337Ssimokawa		if (len == 4 && (dst_lo & 3) == 0) {
358110337Ssimokawa			err = uiomove((char *)&quad, sizeof(quad), uio);
359110337Ssimokawa			xfer = fwmem_write_quad(fwdev, NULL, fwmem_speed,
360110337Ssimokawa				dst_hi, dst_lo, quad, fw_asy_callback);
361110465Ssimokawa			if (xfer == NULL) {
362110465Ssimokawa				err = EINVAL;
363110465Ssimokawa				break;
364110465Ssimokawa			}
365111956Ssimokawa			err = tsleep((caddr_t)xfer, FWPRI, "fwmwq", 0);
366111956Ssimokawa			if (xfer->resp != 0)
367111956Ssimokawa				err = xfer->resp;
368110337Ssimokawa		} else {
369110337Ssimokawa			if (len > MAXLEN)
370110337Ssimokawa				len = MAXLEN;
371110337Ssimokawa			err = uiomove(data, len, uio);
372110337Ssimokawa			if (err)
373110465Ssimokawa				break;
374110337Ssimokawa			xfer = fwmem_write_block(fwdev, NULL, fwmem_speed,
375110337Ssimokawa				dst_hi, dst_lo, len, data, fw_asy_callback);
376110465Ssimokawa			if (xfer == NULL) {
377110465Ssimokawa				err = EINVAL;
378110465Ssimokawa				break;
379110465Ssimokawa			}
380111956Ssimokawa			err = tsleep((caddr_t)xfer, FWPRI, "fwmwb", 0);
381111956Ssimokawa			if (xfer->resp != 0)
382111956Ssimokawa				err = xfer->resp;
383110337Ssimokawa		}
384110406Ssimokawa		fw_xfer_free(xfer);
385110337Ssimokawa	}
386110465Ssimokawa	free(data, M_FW);
387110337Ssimokawa	return err;
388103285Sikob}
389110337Ssimokawa
390103285Sikobint
391103285Sikobfwmem_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
392103285Sikob{
393110582Ssimokawa	int err = 0;
394110582Ssimokawa	switch (cmd) {
395110582Ssimokawa	case FW_SDEUI64:
396110582Ssimokawa		bcopy(data, dev->si_drv1, sizeof(struct fw_eui64));
397110582Ssimokawa		break;
398110582Ssimokawa	case FW_GDEUI64:
399110582Ssimokawa		bcopy(dev->si_drv1, data, sizeof(struct fw_eui64));
400110582Ssimokawa		break;
401110582Ssimokawa	default:
402110582Ssimokawa		err = EINVAL;
403110582Ssimokawa	}
404110582Ssimokawa	return(err);
405103285Sikob}
406103285Sikobint
407103285Sikobfwmem_poll (dev_t dev, int events, fw_proc *td)
408103285Sikob{
409103285Sikob	return EINVAL;
410103285Sikob}
411103285Sikobint
412113584Ssimokawa#if __FreeBSD_version < 500102
413111615Ssimokawafwmem_mmap (dev_t dev, vm_offset_t offset, int nproto)
414111615Ssimokawa#else
415113584Ssimokawafwmem_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto)
416111615Ssimokawa#endif
417103285Sikob{
418103285Sikob	return EINVAL;
419103285Sikob}
420