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