fwmem.c revision 110465
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 110465 2003-02-06 17:23:01Z 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 && !err) {
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				err = EINVAL;
297				break;
298			}
299			err = tsleep((caddr_t)xfer, FWPRI, "fwmrq", hz);
300			if (err !=0 || xfer->resp != 0
301					|| xfer->recv.buf == NULL)
302				err = EIO;
303			else
304				err = uiomove(xfer->recv.buf
305					+ xfer->recv.off + 4*3, 4, uio);
306		} else {
307			if (len > MAXLEN)
308				len = MAXLEN;
309			xfer = fwmem_read_block(fwdev, NULL, fwmem_speed,
310				dst_hi, dst_lo, len, fw_asy_callback);
311			if (xfer == NULL) {
312				err = EINVAL;
313				break;
314			}
315			err = tsleep((caddr_t)xfer, FWPRI, "fwmrb", hz);
316			if (err != 0 || xfer->resp != 0
317					|| xfer->recv.buf == NULL)
318				err = EIO;
319			else
320				err = uiomove(xfer->recv.buf
321					+ xfer->recv.off + 4*4, len, uio);
322		}
323		fw_xfer_free(xfer);
324	}
325	return err;
326}
327int
328fwmem_write (dev_t dev, struct uio *uio, int ioflag)
329{
330	struct firewire_softc *sc;
331	struct fw_device *fwdev;
332	struct fw_xfer *xfer;
333	int err = 0;
334        int unit = DEV2UNIT(dev);
335	u_int16_t dst_hi;
336	u_int32_t dst_lo, quad;
337	char *data;
338	off_t offset;
339	int len;
340
341	sc = devclass_get_softc(firewire_devclass, unit);
342	fwdev = fw_noderesolve_eui64(sc->fc, fwmem_eui64);
343	if (fwdev == NULL) {
344		printf("fwmem: no such device ID:%08x%08x\n",
345			fwmem_eui64.hi, fwmem_eui64.lo);
346		return EINVAL;
347	}
348
349	data = malloc(MAXLEN, M_FW, 0);
350	if (data == NULL)
351		return ENOMEM;
352
353	while(uio->uio_resid > 0 && !err) {
354		offset = uio->uio_offset;
355		dst_hi = (offset >> 32) & 0xffff;
356		dst_lo = offset & 0xffffffff;
357		len = uio->uio_resid;
358		if (len == 4 && (dst_lo & 3) == 0) {
359			err = uiomove((char *)&quad, sizeof(quad), uio);
360			xfer = fwmem_write_quad(fwdev, NULL, fwmem_speed,
361				dst_hi, dst_lo, quad, fw_asy_callback);
362			if (xfer == NULL) {
363				err = EINVAL;
364				break;
365			}
366			err = tsleep((caddr_t)xfer, FWPRI, "fwmwq", hz);
367			if (err !=0 || xfer->resp != 0)
368				err = EIO;
369		} else {
370			if (len > MAXLEN)
371				len = MAXLEN;
372			err = uiomove(data, len, uio);
373			if (err)
374				break;
375			xfer = fwmem_write_block(fwdev, NULL, fwmem_speed,
376				dst_hi, dst_lo, len, data, fw_asy_callback);
377			if (xfer == NULL) {
378				err = EINVAL;
379				break;
380			}
381			err = tsleep((caddr_t)xfer, FWPRI, "fwmwb", hz);
382			if (err != 0 || xfer->resp != 0)
383				err = EIO;
384		}
385		fw_xfer_free(xfer);
386	}
387	free(data, M_FW);
388	return err;
389}
390
391int
392fwmem_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
393{
394	return EINVAL;
395}
396int
397fwmem_poll (dev_t dev, int events, fw_proc *td)
398{
399	return EINVAL;
400}
401int
402fwmem_mmap (dev_t dev, vm_offset_t offset, int nproto)
403{
404	return EINVAL;
405}
406