1 /* $NetBSD: fwmem.c,v 1.19 2018/09/03 16:29:31 riastradh 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 __KERNEL_RCSID(0, "$NetBSD: fwmem.c,v 1.19 2018/09/03 16:29:31 riastradh Exp $");
39
40 #include <sys/param.h>
41 #include <sys/device.h>
42 #include <sys/errno.h>
43 #include <sys/buf.h>
44 #include <sys/bus.h>
45 #include <sys/conf.h>
46 #include <sys/fcntl.h>
47 #include <sys/malloc.h>
48 #include <sys/sysctl.h>
49
50 #include <dev/ieee1394/firewire.h>
51 #include <dev/ieee1394/firewirereg.h>
52 #include <dev/ieee1394/fwmem.h>
53
54 #include "ioconf.h"
55
56 static int fwmem_speed=2, fwmem_debug=0;
57 static struct fw_eui64 fwmem_eui64;
58
59 static int sysctl_fwmem_verify(SYSCTLFN_PROTO, int, int);
60 static int sysctl_fwmem_verify_speed(SYSCTLFN_PROTO);
61
62 static struct fw_xfer *fwmem_xfer_req(struct fw_device *, void *, int, int,
63 int, void *);
64 static void fwmem_biodone(struct fw_xfer *);
65
66 /*
67 * Setup sysctl(3) MIB, hw.fwmem.*
68 *
69 * TBD condition CTLFLAG_PERMANENT on being a module or not
70 */
71 SYSCTL_SETUP(sysctl_fwmem, "sysctl fwmem subtree setup")
72 {
73 int rc, fwmem_node_num;
74 const struct sysctlnode *node;
75
76 if ((rc = sysctl_createv(clog, 0, NULL, &node,
77 CTLFLAG_PERMANENT, CTLTYPE_NODE, "fwmem",
78 SYSCTL_DESCR("IEEE1394 Memory Access"),
79 NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL)) != 0) {
80 goto err;
81 }
82 fwmem_node_num = node->sysctl_num;
83
84 /* fwmem target EUI64 high/low */
85 if ((rc = sysctl_createv(clog, 0, NULL, &node,
86 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
87 "eui64_hi", SYSCTL_DESCR("Fwmem target EUI64 high"),
88 NULL, 0, &fwmem_eui64.hi,
89 0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) {
90 goto err;
91 }
92 if ((rc = sysctl_createv(clog, 0, NULL, &node,
93 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
94 "eui64_lo", SYSCTL_DESCR("Fwmem target EUI64 low"),
95 NULL, 0, &fwmem_eui64.lo,
96 0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) {
97 goto err;
98 }
99
100 /* fwmem link speed */
101 if ((rc = sysctl_createv(clog, 0, NULL, &node,
102 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
103 "speed", SYSCTL_DESCR("Fwmem link speed"),
104 sysctl_fwmem_verify_speed, 0, &fwmem_speed,
105 0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) {
106 goto err;
107 }
108
109 /* fwmem driver debug flag */
110 if ((rc = sysctl_createv(clog, 0, NULL, &node,
111 CTLFLAG_PERMANENT | CTLFLAG_READWRITE, CTLTYPE_INT,
112 "fwmem_debug", SYSCTL_DESCR("Fwmem driver debug flag"),
113 NULL, 0, &fwmem_debug,
114 0, CTL_HW, fwmem_node_num, CTL_CREATE, CTL_EOL)) != 0) {
115 goto err;
116 }
117
118 return;
119
120 err:
121 printf("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
122 }
123
124 static int
125 sysctl_fwmem_verify(SYSCTLFN_ARGS, int lower, int upper)
126 {
127 int error, t;
128 struct sysctlnode node;
129
130 node = *rnode;
131 t = *(int*)rnode->sysctl_data;
132 node.sysctl_data = &t;
133 error = sysctl_lookup(SYSCTLFN_CALL(&node));
134 if (error || newp == NULL)
135 return error;
136
137 if (t < lower || t > upper)
138 return EINVAL;
139
140 *(int*)rnode->sysctl_data = t;
141
142 return 0;
143 }
144
145 static int
146 sysctl_fwmem_verify_speed(SYSCTLFN_ARGS)
147 {
148
149 return sysctl_fwmem_verify(SYSCTLFN_CALL(rnode), 0, FWSPD_S400);
150 }
151
152 #define MAXLEN (512 << fwmem_speed)
153
154 struct fwmem_softc {
155 struct fw_eui64 eui;
156 struct firewire_softc *sc;
157 int refcount;
158 STAILQ_HEAD(, fw_xfer) xferlist;
159 };
160
161
162 int
163 fwmem_open(dev_t dev, int flags, int fmt, struct lwp *td)
164 {
165 struct firewire_softc *sc;
166 struct fwmem_softc *fms;
167 struct fw_xfer *xfer;
168
169 sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
170 if (sc == NULL)
171 return ENXIO;
172
173 if (sc->si_drv1 != NULL) {
174 if ((flags & FWRITE) != 0) {
175 return EBUSY;
176 }
177 fms = (struct fwmem_softc *)sc->si_drv1;
178 fms->refcount++;
179 } else {
180 sc->si_drv1 = (void *)-1;
181 sc->si_drv1 = malloc(sizeof(struct fwmem_softc),
182 M_FW, M_WAITOK);
183 if (sc->si_drv1 == NULL)
184 return ENOMEM;
185 fms = (struct fwmem_softc *)sc->si_drv1;
186 memcpy(&fms->eui, &fwmem_eui64, sizeof(struct fw_eui64));
187 fms->sc = sc;
188 fms->refcount = 1;
189 STAILQ_INIT(&fms->xferlist);
190 xfer = fw_xfer_alloc(M_FW);
191 STAILQ_INSERT_TAIL(&fms->xferlist, xfer, link);
192 }
193 if (fwmem_debug)
194 printf("%s: refcount=%d\n", __func__, fms->refcount);
195
196 return 0;
197 }
198
199 int
200 fwmem_close(dev_t dev, int flags, int fmt, struct lwp *td)
201 {
202 struct firewire_softc *sc;
203 struct fwmem_softc *fms;
204 struct fw_xfer *xfer;
205
206 sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
207 if (sc == NULL)
208 return ENXIO;
209
210 fms = (struct fwmem_softc *)sc->si_drv1;
211
212 fms->refcount--;
213 if (fwmem_debug)
214 printf("%s: refcount=%d\n", __func__, fms->refcount);
215 if (fms->refcount < 1) {
216 while ((xfer = STAILQ_FIRST(&fms->xferlist)) != NULL) {
217 STAILQ_REMOVE_HEAD(&fms->xferlist, link);
218 fw_xfer_free(xfer);
219 }
220 free(sc->si_drv1, M_FW);
221 sc->si_drv1 = NULL;
222 }
223
224 return 0;
225 }
226
227 void
228 fwmem_strategy(struct bio *bp)
229 {
230 struct firewire_softc *sc;
231 struct fwmem_softc *fms;
232 struct fw_device *fwdev;
233 struct fw_xfer *xfer;
234 dev_t dev = bp->bio_dev;
235 int iolen, err = 0, s;
236
237 sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
238 if (sc == NULL)
239 return;
240
241 /* XXX check request length */
242
243 s = splvm();
244 fms = (struct fwmem_softc *)sc->si_drv1;
245 fwdev = fw_noderesolve_eui64(fms->sc->fc, &fms->eui);
246 if (fwdev == NULL) {
247 if (fwmem_debug)
248 printf("fwmem: no such device ID:%08x%08x\n",
249 fms->eui.hi, fms->eui.lo);
250 err = EINVAL;
251 goto error;
252 }
253
254 iolen = MIN(bp->bio_bcount, MAXLEN);
255 if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
256 if (iolen == 4 && (bp->bio_offset & 3) == 0)
257 xfer = fwmem_read_quad(fwdev, (void *) bp, fwmem_speed,
258 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
259 bp->bio_data, fwmem_biodone);
260 else
261 xfer = fwmem_read_block(fwdev, (void *) bp, fwmem_speed,
262 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
263 iolen, bp->bio_data, fwmem_biodone);
264 } else {
265 if (iolen == 4 && (bp->bio_offset & 3) == 0)
266 xfer = fwmem_write_quad(fwdev, (void *)bp, fwmem_speed,
267 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
268 bp->bio_data, fwmem_biodone);
269 else
270 xfer = fwmem_write_block(fwdev, (void *)bp, fwmem_speed,
271 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
272 iolen, bp->bio_data, fwmem_biodone);
273 }
274 if (xfer == NULL) {
275 err = EIO;
276 goto error;
277 }
278 /* XXX */
279 bp->bio_resid = bp->bio_bcount - iolen;
280 error:
281 splx(s);
282 if (err != 0) {
283 if (fwmem_debug)
284 printf("%s: err=%d\n", __func__, err);
285 bp->bio_error = err;
286 bp->bio_resid = bp->bio_bcount;
287 biodone(bp);
288 }
289 }
290
291 int
292 fwmem_ioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *td)
293 {
294 struct firewire_softc *sc;
295 struct fwmem_softc *fms;
296 int err = 0;
297
298 sc = device_lookup_private(&ieee1394if_cd, DEV2UNIT(dev));
299 if (sc == NULL)
300 return ENXIO;
301
302 fms = (struct fwmem_softc *)sc->si_drv1;
303 switch (cmd) {
304 case FW_SDEUI64:
305 memcpy(&fms->eui, data, sizeof(struct fw_eui64));
306 break;
307
308 case FW_GDEUI64:
309 memcpy(data, &fms->eui, sizeof(struct fw_eui64));
310 break;
311
312 default:
313 err = EINVAL;
314 }
315 return err;
316 }
317
318
319 struct fw_xfer *
320 fwmem_read_quad(struct fw_device *fwdev, void * sc, uint8_t spd,
321 uint16_t dst_hi, uint32_t dst_lo, void *data,
322 void (*hand)(struct fw_xfer *))
323 {
324 struct fw_xfer *xfer;
325 struct fw_pkt *fp;
326
327 xfer = fwmem_xfer_req(fwdev, (void *)sc, spd, 0, 4, hand);
328 if (xfer == NULL)
329 return NULL;
330
331 fp = &xfer->send.hdr;
332 fp->mode.rreqq.tcode = FWTCODE_RREQQ;
333 fp->mode.rreqq.dest_hi = dst_hi;
334 fp->mode.rreqq.dest_lo = dst_lo;
335
336 xfer->send.payload = NULL;
337 xfer->recv.payload = (uint32_t *)data;
338
339 if (fwmem_debug)
340 aprint_error("fwmem_read_quad: %d %04x:%08x\n",
341 fwdev->dst, dst_hi, dst_lo);
342
343 if (fw_asyreq(xfer->fc, -1, xfer) == 0)
344 return xfer;
345
346 fw_xfer_free(xfer);
347 return NULL;
348 }
349
350 struct fw_xfer *
351 fwmem_write_quad(struct fw_device *fwdev, void *sc, uint8_t spd,
352 uint16_t dst_hi, uint32_t dst_lo, void *data,
353 void (*hand)(struct fw_xfer *))
354 {
355 struct fw_xfer *xfer;
356 struct fw_pkt *fp;
357
358 xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand);
359 if (xfer == NULL)
360 return NULL;
361
362 fp = &xfer->send.hdr;
363 fp->mode.wreqq.tcode = FWTCODE_WREQQ;
364 fp->mode.wreqq.dest_hi = dst_hi;
365 fp->mode.wreqq.dest_lo = dst_lo;
366 fp->mode.wreqq.data = *(uint32_t *)data;
367
368 xfer->send.payload = xfer->recv.payload = NULL;
369
370 if (fwmem_debug)
371 aprint_error("fwmem_write_quad: %d %04x:%08x %08x\n",
372 fwdev->dst, dst_hi, dst_lo, *(uint32_t *)data);
373
374 if (fw_asyreq(xfer->fc, -1, xfer) == 0)
375 return xfer;
376
377 fw_xfer_free(xfer);
378 return NULL;
379 }
380
381 struct fw_xfer *
382 fwmem_read_block(struct fw_device *fwdev, void *sc, uint8_t spd,
383 uint16_t dst_hi, uint32_t dst_lo, int len, void *data,
384 void (*hand)(struct fw_xfer *))
385 {
386 struct fw_xfer *xfer;
387 struct fw_pkt *fp;
388
389 xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand);
390 if (xfer == NULL)
391 return NULL;
392
393 fp = &xfer->send.hdr;
394 fp->mode.rreqb.tcode = FWTCODE_RREQB;
395 fp->mode.rreqb.dest_hi = dst_hi;
396 fp->mode.rreqb.dest_lo = dst_lo;
397 fp->mode.rreqb.len = len;
398 fp->mode.rreqb.extcode = 0;
399
400 xfer->send.payload = NULL;
401 xfer->recv.payload = data;
402
403 if (fwmem_debug)
404 aprint_error("fwmem_read_block: %d %04x:%08x %d\n",
405 fwdev->dst, dst_hi, dst_lo, len);
406 if (fw_asyreq(xfer->fc, -1, xfer) == 0)
407 return xfer;
408
409 fw_xfer_free(xfer);
410 return NULL;
411 }
412
413 struct fw_xfer *
414 fwmem_write_block(struct fw_device *fwdev, void *sc, uint8_t spd,
415 uint16_t dst_hi, uint32_t dst_lo, int len, void *data,
416 void (*hand)(struct fw_xfer *))
417 {
418 struct fw_xfer *xfer;
419 struct fw_pkt *fp;
420
421 xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand);
422 if (xfer == NULL)
423 return NULL;
424
425 fp = &xfer->send.hdr;
426 fp->mode.wreqb.tcode = FWTCODE_WREQB;
427 fp->mode.wreqb.dest_hi = dst_hi;
428 fp->mode.wreqb.dest_lo = dst_lo;
429 fp->mode.wreqb.len = len;
430 fp->mode.wreqb.extcode = 0;
431
432 xfer->send.payload = data;
433 xfer->recv.payload = NULL;
434
435 if (fwmem_debug)
436 aprint_error("fwmem_write_block: %d %04x:%08x %d\n",
437 fwdev->dst, dst_hi, dst_lo, len);
438 if (fw_asyreq(xfer->fc, -1, xfer) == 0)
439 return xfer;
440
441 fw_xfer_free(xfer);
442 return NULL;
443 }
444
445
446 static struct fw_xfer *
447 fwmem_xfer_req(struct fw_device *fwdev, void *sc, int spd, int slen, int rlen,
448 void *hand)
449 {
450 struct fw_xfer *xfer;
451
452 xfer = fw_xfer_alloc(M_FW);
453 if (xfer == NULL)
454 return NULL;
455
456 xfer->fc = fwdev->fc;
457 xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst;
458 if (spd < 0)
459 xfer->send.spd = fwdev->speed;
460 else
461 xfer->send.spd = uimin(spd, fwdev->speed);
462 xfer->hand = hand;
463 xfer->sc = sc;
464 xfer->send.pay_len = slen;
465 xfer->recv.pay_len = rlen;
466
467 return xfer;
468 }
469
470 static void
471 fwmem_biodone(struct fw_xfer *xfer)
472 {
473 struct bio *bp;
474
475 bp = (struct bio *)xfer->sc;
476 bp->bio_error = xfer->resp;
477
478 if (bp->bio_error != 0) {
479 if (fwmem_debug)
480 printf("%s: err=%d\n", __func__, bp->bio_error);
481 bp->bio_resid = bp->bio_bcount;
482 }
483
484 fw_xfer_free(xfer);
485 biodone(bp);
486 }
Cache object: 452d42d96defca806e61294d6e7da824
|