1 /*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (c) 2002-2003
5 * Hidetoshi Shimokawa. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 *
18 * This product includes software developed by Hidetoshi Shimokawa.
19 *
20 * 4. Neither the name of the author nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 */
37
38 #ifdef __FreeBSD__
39 #include <sys/cdefs.h>
40 #endif
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/types.h>
45
46 #include <sys/kernel.h>
47 #include <sys/malloc.h>
48 #include <sys/conf.h>
49 #include <sys/sysctl.h>
50 #include <sys/bio.h>
51
52 #include <sys/bus.h>
53 #include <machine/bus.h>
54
55 #include <sys/signal.h>
56 #include <sys/mman.h>
57 #include <sys/ioccom.h>
58 #include <sys/fcntl.h>
59
60 #include <dev/firewire/firewire.h>
61 #include <dev/firewire/firewirereg.h>
62 #include <dev/firewire/fwmem.h>
63
64 static int fwmem_speed = 2, fwmem_debug = 0;
65 static struct fw_eui64 fwmem_eui64;
66 SYSCTL_DECL(_hw_firewire);
67 static SYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem,
68 CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
69 "FireWire Memory Access");
70 SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_hi, CTLFLAG_RW,
71 &fwmem_eui64.hi, 0, "Fwmem target EUI64 high");
72 SYSCTL_UINT(_hw_firewire_fwmem, OID_AUTO, eui64_lo, CTLFLAG_RW,
73 &fwmem_eui64.lo, 0, "Fwmem target EUI64 low");
74 SYSCTL_INT(_hw_firewire_fwmem, OID_AUTO, speed, CTLFLAG_RW, &fwmem_speed, 0,
75 "Fwmem link speed");
76 SYSCTL_INT(_debug, OID_AUTO, fwmem_debug, CTLFLAG_RW, &fwmem_debug, 0,
77 "Fwmem driver debug flag");
78
79 static MALLOC_DEFINE(M_FWMEM, "fwmem", "fwmem/FireWire");
80
81 #define MAXLEN (512 << fwmem_speed)
82
83 struct fwmem_softc {
84 struct fw_eui64 eui;
85 struct firewire_softc *sc;
86 int refcount;
87 };
88
89 static struct fw_xfer *
fwmem_xfer_req(struct fw_device * fwdev,caddr_t sc,int spd,int slen,int rlen,void * hand)90 fwmem_xfer_req(
91 struct fw_device *fwdev,
92 caddr_t sc,
93 int spd,
94 int slen,
95 int rlen,
96 void *hand)
97 {
98 struct fw_xfer *xfer;
99
100 xfer = fw_xfer_alloc(M_FWMEM);
101 if (xfer == NULL)
102 return NULL;
103
104 xfer->fc = fwdev->fc;
105 xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst;
106 if (spd < 0)
107 xfer->send.spd = fwdev->speed;
108 else
109 xfer->send.spd = min(spd, fwdev->speed);
110 xfer->hand = hand;
111 xfer->sc = sc;
112 xfer->send.pay_len = slen;
113 xfer->recv.pay_len = rlen;
114
115 return xfer;
116 }
117
118 struct fw_xfer *
fwmem_read_quad(struct fw_device * fwdev,caddr_t sc,uint8_t spd,uint16_t dst_hi,uint32_t dst_lo,void * data,void (* hand)(struct fw_xfer *))119 fwmem_read_quad(
120 struct fw_device *fwdev,
121 caddr_t sc,
122 uint8_t spd,
123 uint16_t dst_hi,
124 uint32_t dst_lo,
125 void *data,
126 void (*hand)(struct fw_xfer *))
127 {
128 struct fw_xfer *xfer;
129 struct fw_pkt *fp;
130
131 xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 4, hand);
132 if (xfer == NULL) {
133 return NULL;
134 }
135
136 fp = &xfer->send.hdr;
137 fp->mode.rreqq.tcode = FWTCODE_RREQQ;
138 fp->mode.rreqq.dest_hi = dst_hi;
139 fp->mode.rreqq.dest_lo = dst_lo;
140
141 xfer->send.payload = NULL;
142 xfer->recv.payload = (uint32_t *)data;
143
144 if (fwmem_debug)
145 printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst,
146 dst_hi, dst_lo);
147
148 if (fw_asyreq(xfer->fc, -1, xfer) == 0)
149 return xfer;
150
151 fw_xfer_free(xfer);
152 return NULL;
153 }
154
155 struct fw_xfer *
fwmem_write_quad(struct fw_device * fwdev,caddr_t sc,uint8_t spd,uint16_t dst_hi,uint32_t dst_lo,void * data,void (* hand)(struct fw_xfer *))156 fwmem_write_quad(
157 struct fw_device *fwdev,
158 caddr_t sc,
159 uint8_t spd,
160 uint16_t dst_hi,
161 uint32_t dst_lo,
162 void *data,
163 void (*hand)(struct fw_xfer *))
164 {
165 struct fw_xfer *xfer;
166 struct fw_pkt *fp;
167
168 xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand);
169 if (xfer == NULL)
170 return NULL;
171
172 fp = &xfer->send.hdr;
173 fp->mode.wreqq.tcode = FWTCODE_WREQQ;
174 fp->mode.wreqq.dest_hi = dst_hi;
175 fp->mode.wreqq.dest_lo = dst_lo;
176 fp->mode.wreqq.data = *(uint32_t *)data;
177
178 xfer->send.payload = xfer->recv.payload = NULL;
179
180 if (fwmem_debug)
181 printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst,
182 dst_hi, dst_lo, *(uint32_t *)data);
183
184 if (fw_asyreq(xfer->fc, -1, xfer) == 0)
185 return xfer;
186
187 fw_xfer_free(xfer);
188 return NULL;
189 }
190
191 struct fw_xfer *
fwmem_read_block(struct fw_device * fwdev,caddr_t sc,uint8_t spd,uint16_t dst_hi,uint32_t dst_lo,int len,void * data,void (* hand)(struct fw_xfer *))192 fwmem_read_block(
193 struct fw_device *fwdev,
194 caddr_t sc,
195 uint8_t spd,
196 uint16_t dst_hi,
197 uint32_t dst_lo,
198 int len,
199 void *data,
200 void (*hand)(struct fw_xfer *))
201 {
202 struct fw_xfer *xfer;
203 struct fw_pkt *fp;
204
205 xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand);
206 if (xfer == NULL)
207 return NULL;
208
209 fp = &xfer->send.hdr;
210 fp->mode.rreqb.tcode = FWTCODE_RREQB;
211 fp->mode.rreqb.dest_hi = dst_hi;
212 fp->mode.rreqb.dest_lo = dst_lo;
213 fp->mode.rreqb.len = len;
214 fp->mode.rreqb.extcode = 0;
215
216 xfer->send.payload = NULL;
217 xfer->recv.payload = data;
218
219 if (fwmem_debug)
220 printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst,
221 dst_hi, dst_lo, len);
222 if (fw_asyreq(xfer->fc, -1, xfer) == 0)
223 return xfer;
224
225 fw_xfer_free(xfer);
226 return NULL;
227 }
228
229 struct fw_xfer *
fwmem_write_block(struct fw_device * fwdev,caddr_t sc,uint8_t spd,uint16_t dst_hi,uint32_t dst_lo,int len,void * data,void (* hand)(struct fw_xfer *))230 fwmem_write_block(
231 struct fw_device *fwdev,
232 caddr_t sc,
233 uint8_t spd,
234 uint16_t dst_hi,
235 uint32_t dst_lo,
236 int len,
237 void *data,
238 void (*hand)(struct fw_xfer *))
239 {
240 struct fw_xfer *xfer;
241 struct fw_pkt *fp;
242
243 xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand);
244 if (xfer == NULL)
245 return NULL;
246
247 fp = &xfer->send.hdr;
248 fp->mode.wreqb.tcode = FWTCODE_WREQB;
249 fp->mode.wreqb.dest_hi = dst_hi;
250 fp->mode.wreqb.dest_lo = dst_lo;
251 fp->mode.wreqb.len = len;
252 fp->mode.wreqb.extcode = 0;
253
254 xfer->send.payload = data;
255 xfer->recv.payload = NULL;
256
257 if (fwmem_debug)
258 printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst,
259 dst_hi, dst_lo, len);
260 if (fw_asyreq(xfer->fc, -1, xfer) == 0)
261 return xfer;
262
263 fw_xfer_free(xfer);
264 return NULL;
265 }
266
267 int
fwmem_open(struct cdev * dev,int flags,int fmt,fw_proc * td)268 fwmem_open(struct cdev *dev, int flags, int fmt, fw_proc *td)
269 {
270 struct fwmem_softc *fms;
271 struct firewire_softc *sc;
272 int unit = DEV2UNIT(dev);
273
274 sc = devclass_get_softc(firewire_devclass, unit);
275 if (sc == NULL)
276 return (ENXIO);
277
278 FW_GLOCK(sc->fc);
279 if (dev->si_drv1 != NULL) {
280 if ((flags & FWRITE) != 0) {
281 FW_GUNLOCK(sc->fc);
282 return (EBUSY);
283 }
284 FW_GUNLOCK(sc->fc);
285 fms = dev->si_drv1;
286 fms->refcount++;
287 } else {
288 dev->si_drv1 = (void *)-1;
289 FW_GUNLOCK(sc->fc);
290 dev->si_drv1 = malloc(sizeof(struct fwmem_softc),
291 M_FWMEM, M_WAITOK);
292 dev->si_iosize_max = DFLTPHYS;
293 fms = dev->si_drv1;
294 bcopy(&fwmem_eui64, &fms->eui, sizeof(struct fw_eui64));
295 fms->sc = sc;
296 fms->refcount = 1;
297 }
298 if (fwmem_debug)
299 printf("%s: refcount=%d\n", __func__, fms->refcount);
300
301 return (0);
302 }
303
304 int
fwmem_close(struct cdev * dev,int flags,int fmt,fw_proc * td)305 fwmem_close (struct cdev *dev, int flags, int fmt, fw_proc *td)
306 {
307 struct fwmem_softc *fms;
308
309 fms = dev->si_drv1;
310
311 FW_GLOCK(fms->sc->fc);
312 fms->refcount--;
313 FW_GUNLOCK(fms->sc->fc);
314 if (fwmem_debug)
315 printf("%s: refcount=%d\n", __func__, fms->refcount);
316 if (fms->refcount < 1) {
317 free(dev->si_drv1, M_FWMEM);
318 dev->si_drv1 = NULL;
319 }
320
321 return (0);
322 }
323
324
325 static void
fwmem_biodone(struct fw_xfer * xfer)326 fwmem_biodone(struct fw_xfer *xfer)
327 {
328 struct bio *bp;
329
330 bp = (struct bio *)xfer->sc;
331 bp->bio_error = xfer->resp;
332
333 if (bp->bio_error != 0) {
334 if (fwmem_debug)
335 printf("%s: err=%d\n", __func__, bp->bio_error);
336 bp->bio_flags |= BIO_ERROR;
337 bp->bio_resid = bp->bio_bcount;
338 }
339
340 fw_xfer_free(xfer);
341 biodone(bp);
342 }
343
344 void
fwmem_strategy(struct bio * bp)345 fwmem_strategy(struct bio *bp)
346 {
347 struct fwmem_softc *fms;
348 struct fw_device *fwdev;
349 struct fw_xfer *xfer;
350 struct cdev *dev;
351 int err = 0, iolen;
352
353 dev = bp->bio_dev;
354 /* XXX check request length */
355
356 fms = dev->si_drv1;
357 fwdev = fw_noderesolve_eui64(fms->sc->fc, &fms->eui);
358 if (fwdev == NULL) {
359 if (fwmem_debug)
360 printf("fwmem: no such device ID:%08x%08x\n",
361 fms->eui.hi, fms->eui.lo);
362 err = EINVAL;
363 goto error;
364 }
365
366 iolen = MIN(bp->bio_bcount, MAXLEN);
367 if (bp->bio_cmd == BIO_READ) {
368 if (iolen == 4 && (bp->bio_offset & 3) == 0)
369 xfer = fwmem_read_quad(fwdev,
370 (void *)bp, fwmem_speed,
371 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
372 bp->bio_data, fwmem_biodone);
373 else
374 xfer = fwmem_read_block(fwdev,
375 (void *)bp, fwmem_speed,
376 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
377 iolen, bp->bio_data, fwmem_biodone);
378 } else {
379 if (iolen == 4 && (bp->bio_offset & 3) == 0)
380 xfer = fwmem_write_quad(fwdev,
381 (void *)bp, fwmem_speed,
382 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
383 bp->bio_data, fwmem_biodone);
384 else
385 xfer = fwmem_write_block(fwdev,
386 (void *)bp, fwmem_speed,
387 bp->bio_offset >> 32, bp->bio_offset & 0xffffffff,
388 iolen, bp->bio_data, fwmem_biodone);
389 }
390 if (xfer == NULL) {
391 err = EIO;
392 goto error;
393 }
394 /* XXX */
395 bp->bio_resid = bp->bio_bcount - iolen;
396 error:
397 if (err != 0) {
398 if (fwmem_debug)
399 printf("%s: err=%d\n", __func__, err);
400 bp->bio_error = err;
401 bp->bio_flags |= BIO_ERROR;
402 bp->bio_resid = bp->bio_bcount;
403 biodone(bp);
404 }
405 }
406
407 int
fwmem_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int flag,fw_proc * td)408 fwmem_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
409 {
410 struct fwmem_softc *fms;
411 int err = 0;
412
413 fms = dev->si_drv1;
414 switch (cmd) {
415 case FW_SDEUI64:
416 bcopy(data, &fms->eui, sizeof(struct fw_eui64));
417 break;
418 case FW_GDEUI64:
419 bcopy(&fms->eui, data, sizeof(struct fw_eui64));
420 break;
421 default:
422 err = EINVAL;
423 }
424 return (err);
425 }
426
427 int
fwmem_poll(struct cdev * dev,int events,fw_proc * td)428 fwmem_poll(struct cdev *dev, int events, fw_proc *td)
429 {
430 return EINVAL;
431 }
432
433 int
fwmem_mmap(struct cdev * dev,vm_ooffset_t offset,vm_paddr_t * paddr,int nproto,vm_memattr_t * memattr)434 fwmem_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr,
435 int nproto, vm_memattr_t *memattr)
436 {
437 return EINVAL;
438 }
439