fwmem.c revision 117104
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 * $FreeBSD: head/sys/dev/firewire/fwmem.c 117104 2003-07-01 12:03:54Z simokawa $
35 */
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/types.h>
40
41#include <sys/kernel.h>
42#include <sys/malloc.h>
43#include <sys/conf.h>
44#include <sys/sysctl.h>
45
46#include <sys/bus.h>
47#include <machine/bus.h>
48
49#include <sys/signal.h>
50#include <sys/mman.h>
51#include <sys/ioccom.h>
52
53#include <dev/firewire/firewire.h>
54#include <dev/firewire/firewirereg.h>
55#include <dev/firewire/fwmem.h>
56
57static int fwmem_speed=2, fwmem_debug=0;
58static struct fw_eui64 fwmem_eui64;
59SYSCTL_DECL(_hw_firewire);
60SYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem, CTLFLAG_RD, 0,
61	"FireWire Memory Access");
62SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_hi, CTLFLAG_RW,
63	&fwmem_eui64.hi, 0, "Fwmem target EUI64 high");
64SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_lo, CTLFLAG_RW,
65	&fwmem_eui64.lo, 0, "Fwmem target EUI64 low");
66SYSCTL_INT(_hw_firewire_fwmem, OID_AUTO, speed, CTLFLAG_RW, &fwmem_speed, 0,
67	"Fwmem link speed");
68SYSCTL_INT(_debug, OID_AUTO, fwmem_debug, CTLFLAG_RW, &fwmem_debug, 0,
69	"Fwmem driver debug flag");
70
71static struct fw_xfer *
72fwmem_xfer_req(
73	struct fw_device *fwdev,
74	caddr_t sc,
75	int spd,
76	int slen,
77	int rlen,
78	void *hand)
79{
80	struct fw_xfer *xfer;
81
82	xfer = fw_xfer_alloc_buf(M_FWXFER, slen, rlen);
83	if (xfer == NULL)
84		return NULL;
85
86	xfer->fc = fwdev->fc;
87	xfer->dst = FWLOCALBUS | fwdev->dst;
88	if (spd < 0)
89		xfer->spd = fwdev->speed;
90	else
91		xfer->spd = min(spd, fwdev->speed);
92	xfer->act.hand = hand;
93	xfer->retry_req = fw_asybusy;
94	xfer->sc = sc;
95
96	return xfer;
97}
98
99struct fw_xfer *
100fwmem_read_quad(
101	struct fw_device *fwdev,
102	caddr_t	sc,
103	u_int8_t spd,
104	u_int16_t dst_hi,
105	u_int32_t dst_lo,
106	void (*hand)(struct fw_xfer *))
107{
108	struct fw_xfer *xfer;
109	struct fw_pkt *fp;
110
111	xfer = fwmem_xfer_req(fwdev, sc, spd, 12, 16, hand);
112	if (xfer == NULL)
113		return NULL;
114
115	fp = (struct fw_pkt *)xfer->send.buf;
116	fp->mode.rreqq.tcode = FWTCODE_RREQQ;
117	fp->mode.rreqq.dst = xfer->dst;
118	fp->mode.rreqq.dest_hi = dst_hi;
119	fp->mode.rreqq.dest_lo = dst_lo;
120
121	if (fwmem_debug)
122		printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst,
123				dst_hi, dst_lo);
124
125	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
126		return xfer;
127
128	fw_xfer_free(xfer);
129	return NULL;
130}
131
132struct fw_xfer *
133fwmem_write_quad(
134	struct fw_device *fwdev,
135	caddr_t	sc,
136	u_int8_t spd,
137	u_int16_t dst_hi,
138	u_int32_t dst_lo,
139	u_int32_t data,
140	void (*hand)(struct fw_xfer *))
141{
142	struct fw_xfer *xfer;
143	struct fw_pkt *fp;
144
145	xfer = fwmem_xfer_req(fwdev, sc, spd, 16, 12, hand);
146	if (xfer == NULL)
147		return NULL;
148
149	fp = (struct fw_pkt *)xfer->send.buf;
150	fp->mode.wreqq.tcode = FWTCODE_WREQQ;
151	fp->mode.wreqq.dst = xfer->dst;
152	fp->mode.wreqq.dest_hi = dst_hi;
153	fp->mode.wreqq.dest_lo = dst_lo;
154
155	fp->mode.wreqq.data = data;
156
157	if (fwmem_debug)
158		printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst,
159			dst_hi, dst_lo, data);
160
161	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
162		return xfer;
163
164	fw_xfer_free(xfer);
165	return NULL;
166}
167
168struct fw_xfer *
169fwmem_read_block(
170	struct fw_device *fwdev,
171	caddr_t	sc,
172	u_int8_t spd,
173	u_int16_t dst_hi,
174	u_int32_t dst_lo,
175	int len,
176	void (*hand)(struct fw_xfer *))
177{
178	struct fw_xfer *xfer;
179	struct fw_pkt *fp;
180
181	xfer = fwmem_xfer_req(fwdev, sc, spd, 16, roundup2(16+len,4), hand);
182	if (xfer == NULL)
183		return NULL;
184
185	fp = (struct fw_pkt *)xfer->send.buf;
186	fp->mode.rreqb.tcode = FWTCODE_RREQB;
187	fp->mode.rreqb.dst = xfer->dst;
188	fp->mode.rreqb.dest_hi = dst_hi;
189	fp->mode.rreqb.dest_lo = dst_lo;
190	fp->mode.rreqb.len = len;
191
192	if (fwmem_debug)
193		printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst,
194				dst_hi, dst_lo, len);
195	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
196		return xfer;
197
198	fw_xfer_free(xfer);
199	return NULL;
200}
201
202struct fw_xfer *
203fwmem_write_block(
204	struct fw_device *fwdev,
205	caddr_t	sc,
206	u_int8_t spd,
207	u_int16_t dst_hi,
208	u_int32_t dst_lo,
209	int len,
210	char *data,
211	void (*hand)(struct fw_xfer *))
212{
213	struct fw_xfer *xfer;
214	struct fw_pkt *fp;
215
216	xfer = fwmem_xfer_req(fwdev, sc, spd, roundup(16+len, 4), 12, hand);
217	if (xfer == NULL)
218		return NULL;
219
220	fp = (struct fw_pkt *)xfer->send.buf;
221	fp->mode.wreqb.tcode = FWTCODE_WREQB;
222	fp->mode.wreqb.dst = xfer->dst;
223	fp->mode.wreqb.dest_hi = dst_hi;
224	fp->mode.wreqb.dest_lo = dst_lo;
225	fp->mode.wreqb.len = len;
226	bcopy(data, &fp->mode.wreqb.payload[0], len);
227
228	if (fwmem_debug)
229		printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst,
230				dst_hi, dst_lo, len);
231	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
232		return xfer;
233
234	fw_xfer_free(xfer);
235	return NULL;
236}
237
238
239int
240fwmem_open (dev_t dev, int flags, int fmt, fw_proc *td)
241{
242	struct fw_eui64 *eui;
243
244	if (dev->si_drv1 != NULL)
245		return (EBUSY);
246
247	eui = (struct fw_eui64 *)malloc(sizeof(struct fw_eui64),
248							M_FW, M_WAITOK);
249	if (eui == NULL)
250		return ENOMEM;
251	bcopy(&fwmem_eui64, eui, sizeof(struct fw_eui64));
252	dev->si_drv1 = (void *)eui;
253
254	return (0);
255}
256
257int
258fwmem_close (dev_t dev, int flags, int fmt, fw_proc *td)
259{
260	free(dev->si_drv1, M_FW);
261	dev->si_drv1 = NULL;
262
263	return (0);
264}
265
266#define MAXLEN 2048
267#define USE_QUAD 0
268int
269fwmem_read (dev_t dev, struct uio *uio, int ioflag)
270{
271	struct firewire_softc *sc;
272	struct fw_device *fwdev;
273	struct fw_xfer *xfer;
274	int err = 0;
275        int unit = DEV2UNIT(dev);
276	u_int16_t dst_hi;
277	u_int32_t dst_lo;
278	off_t offset;
279	int len, s;
280
281	sc = devclass_get_softc(firewire_devclass, unit);
282	fwdev = fw_noderesolve_eui64(sc->fc, (struct fw_eui64 *)dev->si_drv1);
283	if (fwdev == NULL) {
284		if (fwmem_debug)
285			printf("fwmem: no such device ID:%08x%08x\n",
286					fwmem_eui64.hi, fwmem_eui64.lo);
287		return EINVAL;
288	}
289
290	while(uio->uio_resid > 0 && !err) {
291		offset = uio->uio_offset;
292		dst_hi = (offset >> 32) & 0xffff;
293		dst_lo = offset & 0xffffffff;
294		len = uio->uio_resid;
295		if (len == 4 && (dst_lo & 3) == 0) {
296			s = splfw();
297			xfer = fwmem_read_quad(fwdev, NULL, fwmem_speed,
298				dst_hi, dst_lo, fw_asy_callback);
299			if (xfer == NULL) {
300				err = EINVAL;
301				splx(s);
302				break;
303			}
304			err = tsleep((caddr_t)xfer, FWPRI, "fwmrq", 0);
305			splx(s);
306			if (xfer->recv.buf == NULL)
307				err = EIO;
308			else if (xfer->resp != 0)
309				err = xfer->resp;
310			else if (err == 0)
311				err = uiomove(xfer->recv.buf + 4*3, 4, uio);
312		} else {
313			if (len > MAXLEN)
314				len = MAXLEN;
315			s = splfw();
316			xfer = fwmem_read_block(fwdev, NULL, fwmem_speed,
317				dst_hi, dst_lo, len, fw_asy_callback);
318			if (xfer == NULL) {
319				err = EINVAL;
320				splx(s);
321				break;
322			}
323			err = tsleep((caddr_t)xfer, FWPRI, "fwmrb", 0);
324			splx(s);
325			if (xfer->recv.buf == NULL)
326				err = EIO;
327			else if (xfer->resp != 0)
328				err = xfer->resp;
329			else if (err == 0)
330				err = uiomove(xfer->recv.buf + 4*4, len, uio);
331		}
332		fw_xfer_free(xfer);
333	}
334	return err;
335}
336int
337fwmem_write (dev_t dev, struct uio *uio, int ioflag)
338{
339	struct firewire_softc *sc;
340	struct fw_device *fwdev;
341	struct fw_xfer *xfer;
342	int err = 0;
343        int unit = DEV2UNIT(dev);
344	u_int16_t dst_hi;
345	u_int32_t dst_lo, quad;
346	char *data;
347	off_t offset;
348	int len, s;
349
350	sc = devclass_get_softc(firewire_devclass, unit);
351	fwdev = fw_noderesolve_eui64(sc->fc, (struct fw_eui64 *)dev->si_drv1);
352	if (fwdev == NULL) {
353		if (fwmem_debug)
354			printf("fwmem: no such device ID:%08x%08x\n",
355					fwmem_eui64.hi, fwmem_eui64.lo);
356		return EINVAL;
357	}
358
359	data = malloc(MAXLEN, M_FW, M_WAITOK);
360	if (data == NULL)
361		return ENOMEM;
362
363	while(uio->uio_resid > 0 && !err) {
364		offset = uio->uio_offset;
365		dst_hi = (offset >> 32) & 0xffff;
366		dst_lo = offset & 0xffffffff;
367		len = uio->uio_resid;
368		if (len == 4 && (dst_lo & 3) == 0) {
369			err = uiomove((char *)&quad, sizeof(quad), uio);
370			s = splfw();
371			xfer = fwmem_write_quad(fwdev, NULL, fwmem_speed,
372				dst_hi, dst_lo, quad, fw_asy_callback);
373			if (xfer == NULL) {
374				err = EINVAL;
375				splx(s);
376				break;
377			}
378			err = tsleep((caddr_t)xfer, FWPRI, "fwmwq", 0);
379			splx(s);
380			if (xfer->resp != 0)
381				err = xfer->resp;
382		} else {
383			if (len > MAXLEN)
384				len = MAXLEN;
385			err = uiomove(data, len, uio);
386			if (err)
387				break;
388			s = splfw();
389			xfer = fwmem_write_block(fwdev, NULL, fwmem_speed,
390				dst_hi, dst_lo, len, data, fw_asy_callback);
391			if (xfer == NULL) {
392				err = EINVAL;
393				splx(s);
394				break;
395			}
396			err = tsleep((caddr_t)xfer, FWPRI, "fwmwb", 0);
397			splx(s);
398			if (xfer->resp != 0)
399				err = xfer->resp;
400		}
401		fw_xfer_free(xfer);
402	}
403	free(data, M_FW);
404	return err;
405}
406
407int
408fwmem_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
409{
410	int err = 0;
411	switch (cmd) {
412	case FW_SDEUI64:
413		bcopy(data, dev->si_drv1, sizeof(struct fw_eui64));
414		break;
415	case FW_GDEUI64:
416		bcopy(dev->si_drv1, data, sizeof(struct fw_eui64));
417		break;
418	default:
419		err = EINVAL;
420	}
421	return(err);
422}
423int
424fwmem_poll (dev_t dev, int events, fw_proc *td)
425{
426	return EINVAL;
427}
428int
429#if __FreeBSD_version < 500102
430fwmem_mmap (dev_t dev, vm_offset_t offset, int nproto)
431#else
432fwmem_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto)
433#endif
434{
435	return EINVAL;
436}
437