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