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