fwmem.c revision 113584
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 113584 2003-04-17 03:38:03Z 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	eui = (struct fw_eui64 *)malloc(sizeof(struct fw_eui64),
245							M_FW, M_WAITOK);
246	if (eui == NULL)
247		return ENOMEM;
248	bcopy(&fwmem_eui64, eui, sizeof(struct fw_eui64));
249	dev->si_drv1 = (void *)eui;
250
251	return (0);
252}
253
254int
255fwmem_close (dev_t dev, int flags, int fmt, fw_proc *td)
256{
257	free(dev->si_drv1, M_FW);
258	return (0);
259}
260
261#define MAXLEN 2048
262#define USE_QUAD 0
263int
264fwmem_read (dev_t dev, struct uio *uio, int ioflag)
265{
266	struct firewire_softc *sc;
267	struct fw_device *fwdev;
268	struct fw_xfer *xfer;
269	int err = 0;
270        int unit = DEV2UNIT(dev);
271	u_int16_t dst_hi;
272	u_int32_t dst_lo;
273	off_t offset;
274	int len;
275
276	sc = devclass_get_softc(firewire_devclass, unit);
277	fwdev = fw_noderesolve_eui64(sc->fc, (struct fw_eui64 *)dev->si_drv1);
278	if (fwdev == NULL) {
279		if (fwmem_debug)
280			printf("fwmem: no such device ID:%08x%08x\n",
281					fwmem_eui64.hi, fwmem_eui64.lo);
282		return EINVAL;
283	}
284
285	while(uio->uio_resid > 0 && !err) {
286		offset = uio->uio_offset;
287		dst_hi = (offset >> 32) & 0xffff;
288		dst_lo = offset & 0xffffffff;
289		len = uio->uio_resid;
290		if (len == 4 && (dst_lo & 3) == 0) {
291			xfer = fwmem_read_quad(fwdev, NULL, fwmem_speed,
292				dst_hi, dst_lo, fw_asy_callback);
293			if (xfer == NULL) {
294				err = EINVAL;
295				break;
296			}
297			err = tsleep((caddr_t)xfer, FWPRI, "fwmrq", 0);
298			if (xfer->recv.buf == NULL)
299				err = EIO;
300			else if (xfer->resp != 0)
301				err = xfer->resp;
302			else if (err == 0)
303				err = uiomove(xfer->recv.buf + 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				err = EINVAL;
311				break;
312			}
313			err = tsleep((caddr_t)xfer, FWPRI, "fwmrb", 0);
314			if (xfer->recv.buf == NULL)
315				err = EIO;
316			else if (xfer->resp != 0)
317				err = xfer->resp;
318			else if (err == 0)
319				err = uiomove(xfer->recv.buf + 4*4, len, uio);
320		}
321		fw_xfer_free(xfer);
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, (struct fw_eui64 *)dev->si_drv1);
341	if (fwdev == NULL) {
342		if (fwmem_debug)
343			printf("fwmem: no such device ID:%08x%08x\n",
344					fwmem_eui64.hi, fwmem_eui64.lo);
345		return EINVAL;
346	}
347
348	data = malloc(MAXLEN, M_FW, M_WAITOK);
349	if (data == NULL)
350		return ENOMEM;
351
352	while(uio->uio_resid > 0 && !err) {
353		offset = uio->uio_offset;
354		dst_hi = (offset >> 32) & 0xffff;
355		dst_lo = offset & 0xffffffff;
356		len = uio->uio_resid;
357		if (len == 4 && (dst_lo & 3) == 0) {
358			err = uiomove((char *)&quad, sizeof(quad), uio);
359			xfer = fwmem_write_quad(fwdev, NULL, fwmem_speed,
360				dst_hi, dst_lo, quad, fw_asy_callback);
361			if (xfer == NULL) {
362				err = EINVAL;
363				break;
364			}
365			err = tsleep((caddr_t)xfer, FWPRI, "fwmwq", 0);
366			if (xfer->resp != 0)
367				err = xfer->resp;
368		} else {
369			if (len > MAXLEN)
370				len = MAXLEN;
371			err = uiomove(data, len, uio);
372			if (err)
373				break;
374			xfer = fwmem_write_block(fwdev, NULL, fwmem_speed,
375				dst_hi, dst_lo, len, data, fw_asy_callback);
376			if (xfer == NULL) {
377				err = EINVAL;
378				break;
379			}
380			err = tsleep((caddr_t)xfer, FWPRI, "fwmwb", 0);
381			if (xfer->resp != 0)
382				err = xfer->resp;
383		}
384		fw_xfer_free(xfer);
385	}
386	free(data, M_FW);
387	return err;
388}
389
390int
391fwmem_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
392{
393	int err = 0;
394	switch (cmd) {
395	case FW_SDEUI64:
396		bcopy(data, dev->si_drv1, sizeof(struct fw_eui64));
397		break;
398	case FW_GDEUI64:
399		bcopy(dev->si_drv1, data, sizeof(struct fw_eui64));
400		break;
401	default:
402		err = EINVAL;
403	}
404	return(err);
405}
406int
407fwmem_poll (dev_t dev, int events, fw_proc *td)
408{
409	return EINVAL;
410}
411int
412#if __FreeBSD_version < 500102
413fwmem_mmap (dev_t dev, vm_offset_t offset, int nproto)
414#else
415fwmem_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto)
416#endif
417{
418	return EINVAL;
419}
420