fwmem.c revision 110406
1/*
2 * Copyright (C) 2002
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 110406 2003-02-05 17:19:34Z 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/uio.h>
45#include <sys/sysctl.h>
46
47#include <sys/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_low, 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 *fwmem_xfer_req(struct fw_device *, caddr_t,
72							int, int, void *);
73
74static struct fw_xfer *
75fwmem_xfer_req(
76	struct fw_device *fwdev,
77	caddr_t sc,
78	int spd,
79	int len,
80	void *hand)
81{
82	struct fw_xfer *xfer;
83
84	xfer = fw_xfer_alloc(M_FWXFER);
85	if (xfer == NULL)
86		return NULL;
87
88	xfer->fc = fwdev->fc;
89	xfer->dst = FWLOCALBUS | fwdev->dst;
90	if (spd < 0)
91		xfer->spd = fwdev->speed;
92	else
93		xfer->spd = min(spd, fwdev->speed);
94	xfer->send.len = len;
95	xfer->send.buf = malloc(len, M_FW, M_NOWAIT | M_ZERO);
96
97	if (xfer->send.buf == NULL) {
98		fw_xfer_free(xfer);
99		return NULL;
100	}
101
102	xfer->send.off = 0;
103	xfer->act.hand = hand;
104	xfer->retry_req = fw_asybusy;
105	xfer->sc = sc;
106
107	return xfer;
108}
109
110struct fw_xfer *
111fwmem_read_quad(
112	struct fw_device *fwdev,
113	caddr_t	sc,
114	u_int8_t spd,
115	u_int16_t dst_hi,
116	u_int32_t dst_lo,
117	void (*hand)(struct fw_xfer *))
118{
119	struct fw_xfer *xfer;
120	struct fw_pkt *fp;
121
122	xfer = fwmem_xfer_req(fwdev, sc, spd, 12, hand);
123	if (xfer == NULL)
124		return NULL;
125
126	fp = (struct fw_pkt *)xfer->send.buf;
127	fp->mode.rreqq.tcode = FWTCODE_RREQQ;
128	fp->mode.rreqq.dst = htons(xfer->dst);
129	fp->mode.rreqq.dest_hi = htons(dst_hi);
130	fp->mode.rreqq.dest_lo = htonl(dst_lo);
131
132	if (fwmem_debug)
133		printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst,
134				dst_hi, dst_lo);
135
136	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
137		return xfer;
138
139	fw_xfer_free(xfer);
140	return NULL;
141}
142
143struct fw_xfer *
144fwmem_write_quad(
145	struct fw_device *fwdev,
146	caddr_t	sc,
147	u_int8_t spd,
148	u_int16_t dst_hi,
149	u_int32_t dst_lo,
150	u_int32_t data,
151	void (*hand)(struct fw_xfer *))
152{
153	struct fw_xfer *xfer;
154	struct fw_pkt *fp;
155
156	xfer = fwmem_xfer_req(fwdev, sc, spd, 16, hand);
157	if (xfer == NULL)
158		return NULL;
159
160	fp = (struct fw_pkt *)xfer->send.buf;
161	fp->mode.wreqq.tcode = FWTCODE_WREQQ;
162	fp->mode.wreqq.dst = htons(xfer->dst);
163	fp->mode.wreqq.dest_hi = htons(dst_hi);
164	fp->mode.wreqq.dest_lo = htonl(dst_lo);
165
166	fp->mode.wreqq.data = htonl(data);
167
168	if (fwmem_debug)
169		printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst,
170			dst_hi, dst_lo, data);
171
172	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
173		return xfer;
174
175	fw_xfer_free(xfer);
176	return NULL;
177}
178
179struct fw_xfer *
180fwmem_read_block(
181	struct fw_device *fwdev,
182	caddr_t	sc,
183	u_int8_t spd,
184	u_int16_t dst_hi,
185	u_int32_t dst_lo,
186	int len,
187	void (*hand)(struct fw_xfer *))
188{
189	struct fw_xfer *xfer;
190	struct fw_pkt *fp;
191
192	xfer = fwmem_xfer_req(fwdev, sc, spd, 16, hand);
193	if (xfer == NULL)
194		return NULL;
195
196	fp = (struct fw_pkt *)xfer->send.buf;
197	fp->mode.rreqb.tcode = FWTCODE_RREQB;
198	fp->mode.rreqb.dst = htons(xfer->dst);
199	fp->mode.rreqb.dest_hi = htons(dst_hi);
200	fp->mode.rreqb.dest_lo = htonl(dst_lo);
201	fp->mode.rreqb.len = htons(len);
202
203	if (fwmem_debug)
204		printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst,
205				dst_hi, dst_lo, len);
206	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
207		return xfer;
208
209	fw_xfer_free(xfer);
210	return NULL;
211}
212
213struct fw_xfer *
214fwmem_write_block(
215	struct fw_device *fwdev,
216	caddr_t	sc,
217	u_int8_t spd,
218	u_int16_t dst_hi,
219	u_int32_t dst_lo,
220	int len,
221	char *data,
222	void (*hand)(struct fw_xfer *))
223{
224	struct fw_xfer *xfer;
225	struct fw_pkt *fp;
226
227	xfer = fwmem_xfer_req(fwdev, sc, spd, 16 + roundup(len, 4), hand);
228	if (xfer == NULL)
229		return NULL;
230
231	fp = (struct fw_pkt *)xfer->send.buf;
232	fp->mode.wreqb.tcode = FWTCODE_WREQB;
233	fp->mode.wreqb.dst = htons(xfer->dst);
234	fp->mode.wreqb.dest_hi = htons(dst_hi);
235	fp->mode.wreqb.dest_lo = htonl(dst_lo);
236	fp->mode.wreqb.len = htons(len);
237	bcopy(data, &fp->mode.wreqb.payload[0], len);
238
239	if (fwmem_debug)
240		printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst,
241				dst_hi, dst_lo, len);
242	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
243		return xfer;
244
245	fw_xfer_free(xfer);
246	return NULL;
247}
248
249
250int
251fwmem_open (dev_t dev, int flags, int fmt, fw_proc *td)
252{
253	int err = 0;
254	return err;
255}
256
257int
258fwmem_close (dev_t dev, int flags, int fmt, fw_proc *td)
259{
260	int err = 0;
261	return err;
262}
263
264#define MAXLEN 2048
265#define USE_QUAD 0
266int
267fwmem_read (dev_t dev, struct uio *uio, int ioflag)
268{
269	struct firewire_softc *sc;
270	struct fw_device *fwdev;
271	struct fw_xfer *xfer;
272	int err = 0;
273        int unit = DEV2UNIT(dev);
274	u_int16_t dst_hi;
275	u_int32_t dst_lo;
276	off_t offset;
277	int len;
278
279	sc = devclass_get_softc(firewire_devclass, unit);
280	fwdev = fw_noderesolve_eui64(sc->fc, fwmem_eui64);
281	if (fwdev == NULL) {
282		printf("fwmem: no such device ID:%08x%08x\n",
283			fwmem_eui64.hi, fwmem_eui64.lo);
284		return EINVAL;
285	}
286
287	while(uio->uio_resid > 0) {
288		offset = uio->uio_offset;
289		dst_hi = (offset >> 32) & 0xffff;
290		dst_lo = offset & 0xffffffff;
291		len = uio->uio_resid;
292		if (len == 4 && (dst_lo & 3) == 0) {
293			xfer = fwmem_read_quad(fwdev, NULL, fwmem_speed,
294				dst_hi, dst_lo, fw_asy_callback);
295			if (xfer == NULL)
296				return EINVAL;
297			err = tsleep((caddr_t)xfer, FWPRI, "fwmrq", hz);
298			if (err !=0 || xfer->resp != 0
299					|| xfer->recv.buf == NULL)
300				err = EIO;
301			else
302				err = uiomove(xfer->recv.buf
303					+ xfer->recv.off + 4*3, 4, uio);
304		} else {
305			if (len > MAXLEN)
306				len = MAXLEN;
307			xfer = fwmem_read_block(fwdev, NULL, fwmem_speed,
308				dst_hi, dst_lo, len, fw_asy_callback);
309			if (xfer == NULL)
310				return EINVAL;
311			err = tsleep((caddr_t)xfer, FWPRI, "fwmrb", hz);
312			if (err != 0 || xfer->resp != 0
313					|| xfer->recv.buf == NULL)
314				err = EIO;
315			else
316				err = uiomove(xfer->recv.buf
317					+ xfer->recv.off + 4*4, len, uio);
318		}
319		fw_xfer_free(xfer);
320		if (err)
321			return err;
322	}
323	return err;
324}
325int
326fwmem_write (dev_t dev, struct uio *uio, int ioflag)
327{
328	struct firewire_softc *sc;
329	struct fw_device *fwdev;
330	struct fw_xfer *xfer;
331	int err = 0;
332        int unit = DEV2UNIT(dev);
333	u_int16_t dst_hi;
334	u_int32_t dst_lo, quad;
335	char *data;
336	off_t offset;
337	int len;
338
339	sc = devclass_get_softc(firewire_devclass, unit);
340	fwdev = fw_noderesolve_eui64(sc->fc, fwmem_eui64);
341	if (fwdev == NULL) {
342		printf("fwmem: no such device ID:%08x%08x\n",
343			fwmem_eui64.hi, fwmem_eui64.lo);
344		return EINVAL;
345	}
346
347	data = malloc(MAXLEN, M_FW, 0);
348	if (data == NULL)
349		return ENOMEM;
350
351	while(uio->uio_resid > 0) {
352		offset = uio->uio_offset;
353		dst_hi = (offset >> 32) & 0xffff;
354		dst_lo = offset & 0xffffffff;
355		len = uio->uio_resid;
356		if (len == 4 && (dst_lo & 3) == 0) {
357			err = uiomove((char *)&quad, sizeof(quad), uio);
358			xfer = fwmem_write_quad(fwdev, NULL, fwmem_speed,
359				dst_hi, dst_lo, quad, fw_asy_callback);
360			if (xfer == NULL)
361				return EINVAL;
362			err = tsleep((caddr_t)xfer, FWPRI, "fwmwq", hz);
363			if (err !=0 || xfer->resp != 0)
364				err = EIO;
365		} else {
366			if (len > MAXLEN)
367				len = MAXLEN;
368			err = uiomove(data, len, uio);
369			if (err)
370				return err;
371			xfer = fwmem_write_block(fwdev, NULL, fwmem_speed,
372				dst_hi, dst_lo, len, data, fw_asy_callback);
373			if (xfer == NULL)
374				return EINVAL;
375			err = tsleep((caddr_t)xfer, FWPRI, "fwmwb", hz);
376			if (err != 0 || xfer->resp != 0)
377				err = EIO;
378		}
379		fw_xfer_free(xfer);
380	}
381	return err;
382}
383
384int
385fwmem_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
386{
387	return EINVAL;
388}
389int
390fwmem_poll (dev_t dev, int events, fw_proc *td)
391{
392	return EINVAL;
393}
394int
395fwmem_mmap (dev_t dev, vm_offset_t offset, int nproto)
396{
397	return EINVAL;
398}
399