xref: /netbsd-src/sys/dev/ieee1394/fwmem.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
1 /*	$NetBSD: fwmem.c,v 1.2 2005/12/11 12:22:02 christos Exp $	*/
2 /*-
3  * Copyright (c) 2002-2003
4  * 	Hidetoshi Shimokawa. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *
17  *	This product includes software developed by Hidetoshi Shimokawa.
18  *
19  * 4. Neither the name of the author nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  */
36 
37 #include <sys/cdefs.h>
38 #ifdef __FBSDID
39 __FBSDID("$FreeBSD: /repoman/r/ncvs/src/sys/dev/firewire/fwmem.c,v 1.31 2005/01/06 01:42:41 imp Exp $");
40 #endif
41 
42 #if defined(__FreeBSD__)
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/types.h>
46 
47 #include <sys/kernel.h>
48 #include <sys/malloc.h>
49 #include <sys/conf.h>
50 #include <sys/sysctl.h>
51 #if defined(__DragonFly__) || __FreeBSD_version < 500000
52 #include <sys/buf.h>
53 #else
54 #include <sys/bio.h>
55 #endif
56 
57 #include <sys/bus.h>
58 #include <machine/bus.h>
59 
60 #include <sys/signal.h>
61 #include <sys/mman.h>
62 #include <sys/ioccom.h>
63 #include <sys/fcntl.h>
64 #include <sys/ktr.h>
65 
66 #ifdef __DragonFly__
67 #include "fw_port.h"
68 #include "firewire.h"
69 #include "firewirereg.h"
70 #include "fwmem.h"
71 #else
72 #include <dev/firewire/fw_port.h>
73 #include <dev/firewire/firewire.h>
74 #include <dev/firewire/firewirereg.h>
75 #include <dev/firewire/fwmem.h>
76 #endif
77 #elif defined(__NetBSD__)
78 #include <sys/param.h>
79 #include <sys/device.h>
80 #include <sys/errno.h>
81 #include <sys/buf.h>
82 #include <sys/conf.h>
83 #include <sys/fcntl.h>
84 #include <sys/malloc.h>
85 #include <sys/sysctl.h>
86 
87 #include <machine/bus.h>
88 
89 #include <dev/ieee1394/fw_port.h>
90 #include <dev/ieee1394/firewire.h>
91 #include <dev/ieee1394/firewirereg.h>
92 #include <dev/ieee1394/fwmem.h>
93 #endif
94 
95 static int fwmem_speed=2, fwmem_debug=0;
96 static struct fw_eui64 fwmem_eui64;
97 #if defined(__FreeBSD__)
98 SYSCTL_DECL(_hw_firewire);
99 SYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem, CTLFLAG_RD, 0,
100 	"FireWire Memory Access");
101 SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_hi, CTLFLAG_RW,
102 	&fwmem_eui64.hi, 0, "Fwmem target EUI64 high");
103 SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_lo, CTLFLAG_RW,
104 	&fwmem_eui64.lo, 0, "Fwmem target EUI64 low");
105 SYSCTL_INT(_hw_firewire_fwmem, OID_AUTO, speed, CTLFLAG_RW, &fwmem_speed, 0,
106 	"Fwmem link speed");
107 SYSCTL_INT(_debug, OID_AUTO, fwmem_debug, CTLFLAG_RW, &fwmem_debug, 0,
108 	"Fwmem driver debug flag");
109 MALLOC_DEFINE(M_FWMEM, "fwmem", "fwmem/FireWire");
110 #elif defined(__NetBSD__)
111 static int sysctl_fwmem_verify(SYSCTLFN_PROTO, int, int);
112 static int sysctl_fwmem_verify_speed(SYSCTLFN_PROTO);
113 
114 /*
115  * Setup sysctl(3) MIB, hw.fwmem.*
116  *
117  * TBD condition CTLFLAG_PERMANENT on being an LKM or not
118  */
119 SYSCTL_SETUP(sysctl_fwmem, "sysctl fwmem subtree setup")
120 {
121 	int rc, fwmem_node_num;
122 	const struct sysctlnode *node;
123 
124 	if ((rc = sysctl_createv(clog, 0, NULL, NULL,
125 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "hw", NULL,
126 	    NULL, 0, NULL, 0, CTL_HW, CTL_EOL)) != 0) {
127 		goto err;
128 	}
129 
130 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
131 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "fwmem",
132 	    SYSCTL_DESCR("IEEE1394 Memory Access"),
133 	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) {
134 		goto err;
135 	}
136 	fwmem_node_num = node->sysctl_num;
137 
138 	/* fwmem target EUI64 high/low */
139 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
140 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
141 	    "eui64_hi", SYSCTL_DESCR("Fwmem target EUI64 high"),
142 	    NULL, 0, &fwmem_eui64.hi,
143 	    0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) {
144 		goto err;
145 	}
146 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
147 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
148 	    "eui64_lo", SYSCTL_DESCR("Fwmem target EUI64 low"),
149 	    NULL, 0, &fwmem_eui64.lo,
150 	    0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) {
151 		goto err;
152 	}
153 
154 	/* fwmem link speed */
155 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
156 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
157 	    "speed", SYSCTL_DESCR("Fwmem link speed"),
158 	    sysctl_fwmem_verify_speed, 0, &fwmem_speed,
159 	    0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) {
160 		goto err;
161 	}
162 
163 	/* fwmem driver debug flag */
164 	if ((rc = sysctl_createv(clog, 0, NULL, &node,
165 	    CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
166 	    "fwmem_debug", SYSCTL_DESCR("Fwmem driver debug flag"),
167 	    NULL, 0, &fwmem_debug,
168 	    0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) {
169 		goto err;
170 	}
171 
172 	return;
173 
174 err:
175 	printf("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
176 }
177 
178 static int
179 sysctl_fwmem_verify(SYSCTLFN_ARGS, int lower, int upper)
180 {
181 	int error, t;
182 	struct sysctlnode node;
183 
184 	node = *rnode;
185 	t = *(int*)rnode->sysctl_data;
186 	node.sysctl_data = &t;
187 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
188 	if (error || newp == NULL)
189 		return (error);
190 
191 	if (t < lower || t > upper)
192 		return (EINVAL);
193 
194 	*(int*)rnode->sysctl_data = t;
195 
196 	return (0);
197 }
198 
199 static int
200 sysctl_fwmem_verify_speed(SYSCTLFN_ARGS)
201 {
202 	return (sysctl_fwmem_verify(SYSCTLFN_CALL(rnode), 0, FWSPD_S400));
203 }
204 
205 MALLOC_DEFINE(M_FWMEM, "fwmem", "fwmem/IEEE1394");
206 #endif
207 
208 #define MAXLEN (512 << fwmem_speed)
209 
210 struct fwmem_softc {
211 	struct fw_eui64 eui;
212 	int refcount;
213 	STAILQ_HEAD(, fw_xfer) xferlist;
214 };
215 
216 static struct fw_xfer *
217 fwmem_xfer_req(
218 	struct fw_device *fwdev,
219 	caddr_t sc,
220 	int spd,
221 	int slen,
222 	int rlen,
223 	void *hand)
224 {
225 	struct fw_xfer *xfer;
226 
227 	xfer = fw_xfer_alloc(M_FWMEM);
228 	if (xfer == NULL)
229 		return NULL;
230 
231 	xfer->fc = fwdev->fc;
232 	xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst;
233 	if (spd < 0)
234 		xfer->send.spd = fwdev->speed;
235 	else
236 		xfer->send.spd = min(spd, fwdev->speed);
237 	xfer->hand = hand;
238 	xfer->sc = sc;
239 	xfer->send.pay_len = slen;
240 	xfer->recv.pay_len = rlen;
241 
242 	return xfer;
243 }
244 
245 struct fw_xfer *
246 fwmem_read_quad(
247 	struct fw_device *fwdev,
248 	caddr_t	sc,
249 	uint8_t spd,
250 	uint16_t dst_hi,
251 	uint32_t dst_lo,
252 	void *data,
253 	void (*hand)(struct fw_xfer *))
254 {
255 	struct fw_xfer *xfer;
256 	struct fw_pkt *fp;
257 
258 	xfer = fwmem_xfer_req(fwdev, (void *)sc, spd, 0, 4, hand);
259 	if (xfer == NULL) {
260 		return NULL;
261 	}
262 
263 	fp = &xfer->send.hdr;
264 	fp->mode.rreqq.tcode = FWTCODE_RREQQ;
265 	fp->mode.rreqq.dest_hi = dst_hi;
266 	fp->mode.rreqq.dest_lo = dst_lo;
267 
268 	xfer->send.payload = NULL;
269 	xfer->recv.payload = (uint32_t *)data;
270 
271 	if (fwmem_debug)
272 		printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst,
273 				dst_hi, dst_lo);
274 
275 	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
276 		return xfer;
277 
278 	fw_xfer_free(xfer);
279 	return NULL;
280 }
281 
282 struct fw_xfer *
283 fwmem_write_quad(
284 	struct fw_device *fwdev,
285 	caddr_t	sc,
286 	uint8_t spd,
287 	uint16_t dst_hi,
288 	uint32_t dst_lo,
289 	void *data,
290 	void (*hand)(struct fw_xfer *))
291 {
292 	struct fw_xfer *xfer;
293 	struct fw_pkt *fp;
294 
295 	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand);
296 	if (xfer == NULL)
297 		return NULL;
298 
299 	fp = &xfer->send.hdr;
300 	fp->mode.wreqq.tcode = FWTCODE_WREQQ;
301 	fp->mode.wreqq.dest_hi = dst_hi;
302 	fp->mode.wreqq.dest_lo = dst_lo;
303 	fp->mode.wreqq.data = *(uint32_t *)data;
304 
305 	xfer->send.payload = xfer->recv.payload = NULL;
306 
307 	if (fwmem_debug)
308 		printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst,
309 			dst_hi, dst_lo, *(uint32_t *)data);
310 
311 	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
312 		return xfer;
313 
314 	fw_xfer_free(xfer);
315 	return NULL;
316 }
317 
318 struct fw_xfer *
319 fwmem_read_block(
320 	struct fw_device *fwdev,
321 	caddr_t	sc,
322 	uint8_t spd,
323 	uint16_t dst_hi,
324 	uint32_t dst_lo,
325 	int len,
326 	void *data,
327 	void (*hand)(struct fw_xfer *))
328 {
329 	struct fw_xfer *xfer;
330 	struct fw_pkt *fp;
331 
332 	xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand);
333 	if (xfer == NULL)
334 		return NULL;
335 
336 	fp = &xfer->send.hdr;
337 	fp->mode.rreqb.tcode = FWTCODE_RREQB;
338 	fp->mode.rreqb.dest_hi = dst_hi;
339 	fp->mode.rreqb.dest_lo = dst_lo;
340 	fp->mode.rreqb.len = len;
341 	fp->mode.rreqb.extcode = 0;
342 
343 	xfer->send.payload = NULL;
344 	xfer->recv.payload = data;
345 
346 	if (fwmem_debug)
347 		printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst,
348 				dst_hi, dst_lo, len);
349 	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
350 		return xfer;
351 
352 	fw_xfer_free(xfer);
353 	return NULL;
354 }
355 
356 struct fw_xfer *
357 fwmem_write_block(
358 	struct fw_device *fwdev,
359 	caddr_t	sc,
360 	uint8_t spd,
361 	uint16_t dst_hi,
362 	uint32_t dst_lo,
363 	int len,
364 	void *data,
365 	void (*hand)(struct fw_xfer *))
366 {
367 	struct fw_xfer *xfer;
368 	struct fw_pkt *fp;
369 
370 	xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand);
371 	if (xfer == NULL)
372 		return NULL;
373 
374 	fp = &xfer->send.hdr;
375 	fp->mode.wreqb.tcode = FWTCODE_WREQB;
376 	fp->mode.wreqb.dest_hi = dst_hi;
377 	fp->mode.wreqb.dest_lo = dst_lo;
378 	fp->mode.wreqb.len = len;
379 	fp->mode.wreqb.extcode = 0;
380 
381 	xfer->send.payload = data;
382 	xfer->recv.payload = NULL;
383 
384 	if (fwmem_debug)
385 		printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst,
386 				dst_hi, dst_lo, len);
387 	if (fw_asyreq(xfer->fc, -1, xfer) == 0)
388 		return xfer;
389 
390 	fw_xfer_free(xfer);
391 	return NULL;
392 }
393 
394 
395 FW_OPEN(fwmem)
396 {
397 	struct fwmem_softc *fms;
398 	struct fw_xfer *xfer;
399 	FW_OPEN_START;
400 
401 	if (dev->si_drv1 != NULL) {
402 		if ((flags & FWRITE) != 0)
403 			return (EBUSY);
404 		fms = (struct fwmem_softc *)dev->si_drv1;
405 		fms->refcount ++;
406 	} else {
407 		fms = (struct fwmem_softc *)malloc(sizeof(struct fwmem_softc),
408 							M_FWMEM, M_WAITOK);
409 		if (fms == NULL)
410 			return ENOMEM;
411 		bcopy(&fwmem_eui64, &fms->eui, sizeof(struct fw_eui64));
412 		dev->si_drv1 = (void *)fms;
413 		dev->si_iosize_max = DFLTPHYS;
414 		fms->refcount = 1;
415 		STAILQ_INIT(&fms->xferlist);
416 		xfer = fw_xfer_alloc(M_FWMEM);
417 		STAILQ_INSERT_TAIL(&fms->xferlist, xfer, link);
418 	}
419 	if (fwmem_debug)
420 		printf("%s: refcount=%d\n", __func__, fms->refcount);
421 
422 	return (0);
423 }
424 
425 FW_CLOSE(fwmem)
426 {
427 	struct fwmem_softc *fms;
428 	struct fw_xfer *xfer;
429 	FW_CLOSE_START;
430 
431 	fms = (struct fwmem_softc *)dev->si_drv1;
432 	fms->refcount --;
433 	if (fwmem_debug)
434 		printf("%s: refcount=%d\n", __func__, fms->refcount);
435 	if (fms->refcount < 1) {
436 		while ((xfer = STAILQ_FIRST(&fms->xferlist)) != NULL) {
437 			STAILQ_REMOVE_HEAD(&fms->xferlist, link);
438 			fw_xfer_free(xfer);
439 		}
440 		free(dev->si_drv1, M_FW);
441 		dev->si_drv1 = NULL;
442 	}
443 
444 	return (0);
445 }
446 
447 
448 static void
449 fwmem_biodone(struct fw_xfer *xfer)
450 {
451 	struct bio *bp;
452 
453 	bp = (struct bio *)xfer->sc;
454 	bp->bio_error = xfer->resp;
455 
456 	if (bp->bio_error != 0) {
457 		if (fwmem_debug)
458 			printf("%s: err=%d\n", __func__, bp->bio_error);
459 		bp->bio_flags |= BIO_ERROR;
460 		bp->bio_resid = bp->bio_bcount;
461 	}
462 
463 	CTR0(KTR_DEV, "biodone0");
464 	fw_xfer_free(xfer);
465 	CTR0(KTR_DEV, "biodone1");
466 	biodone(bp);
467 	CTR0(KTR_DEV, "biodone2");
468 }
469 
470 void
471 fwmem_strategy(struct bio *bp)
472 {
473 	FW_STRATEGY_START;
474 	struct fwmem_softc *fms;
475 	struct fw_device *fwdev;
476 	struct fw_xfer *xfer;
477 	int err=0, s, iolen;
478 
479 	CTR0(KTR_DEV, "strategy");
480 
481 	/* XXX check request length */
482 
483 	s = splfw();
484 	fms = (struct fwmem_softc *)dev->si_drv1;
485 	fwdev = fw_noderesolve_eui64(sc->fc, &fms->eui);
486 	if (fwdev == NULL) {
487 		if (fwmem_debug)
488 			printf("fwmem: no such device ID:%08x%08x\n",
489 					fms->eui.hi, fms->eui.lo);
490 		err = EINVAL;
491 		goto error;
492 	}
493 
494 	iolen = MIN(bp->bio_bcount, MAXLEN);
495 	if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
496 		if (iolen == 4 && (bp->bio_offset & 3) == 0)
497 			xfer = fwmem_read_quad(fwdev,
498 			    (void *) bp, fwmem_speed,
499 			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
500 			    bp->bio_data, fwmem_biodone);
501 		else
502 			xfer = fwmem_read_block(fwdev,
503 			    (void *) bp, fwmem_speed,
504 			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
505 			    iolen, bp->bio_data, fwmem_biodone);
506 	} else {
507 		if (iolen == 4 && (bp->bio_offset & 3) == 0)
508 			xfer = fwmem_write_quad(fwdev,
509 			    (void *)bp, fwmem_speed,
510 			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
511 			    bp->bio_data, fwmem_biodone);
512 		else
513 			xfer = fwmem_write_block(fwdev,
514 			    (void *)bp, fwmem_speed,
515 			    bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
516 			    iolen, bp->bio_data, fwmem_biodone);
517 	}
518 	if (xfer == NULL) {
519 		err = EIO;
520 		goto error;
521 	}
522 	/* XXX */
523 	bp->bio_resid = bp->bio_bcount - iolen;
524 error:
525 	splx(s);
526 	if (err != 0) {
527 		if (fwmem_debug)
528 			printf("%s: err=%d\n", __func__, err);
529 		bp->bio_error = err;
530 		bp->bio_flags |= BIO_ERROR;
531 		bp->bio_resid = bp->bio_bcount;
532 		biodone(bp);
533 	}
534 }
535 
536 FW_IOCTL(fwmem)
537 {
538 	FW_IOCTL_START;
539 	struct fwmem_softc *fms;
540 	int err = 0;
541 
542 	fms = (struct fwmem_softc *)dev->si_drv1;
543 	switch (cmd) {
544 	case FW_SDEUI64:
545 		bcopy(data, &fms->eui, sizeof(struct fw_eui64));
546 		break;
547 	case FW_GDEUI64:
548 		bcopy(&fms->eui, data, sizeof(struct fw_eui64));
549 		break;
550 	default:
551 		err = EINVAL;
552 	}
553 	return(err);
554 }
555 
556 FW_POLL(fwmem)
557 {
558 	return EINVAL;
559 }
560 
561 FW_MMAP(fwmem)
562 {
563 	return EINVAL;
564 }
565