]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/firewire/fwmem.c
MFV r330973: 9164 assert: newds == os->os_dsl_dataset
[FreeBSD/FreeBSD.git] / sys / dev / firewire / fwmem.c
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 __FBSDID("$FreeBSD$");
41 #endif
42
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 #include <sys/bio.h>
52
53 #include <sys/bus.h>
54 #include <machine/bus.h>
55
56 #include <sys/signal.h>
57 #include <sys/mman.h>
58 #include <sys/ioccom.h>
59 #include <sys/fcntl.h>
60
61 #include <dev/firewire/firewire.h>
62 #include <dev/firewire/firewirereg.h>
63 #include <dev/firewire/fwmem.h>
64
65 static int fwmem_speed = 2, fwmem_debug = 0;
66 static struct fw_eui64 fwmem_eui64;
67 SYSCTL_DECL(_hw_firewire);
68 static SYSCTL_NODE(_hw_firewire, OID_AUTO, fwmem, CTLFLAG_RD, 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 *
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 *
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 *
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 *
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 *
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
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
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
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
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
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
428 fwmem_poll(struct cdev *dev, int events, fw_proc *td)
429 {
430         return EINVAL;
431 }
432
433 int
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 }