]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/proto/proto_core.c
Add DMA memory allocation and freeing.
[FreeBSD/FreeBSD.git] / sys / dev / proto / proto_core.c
1 /*-
2  * Copyright (c) 2014 Marcel Moolenaar
3  * 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  *
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  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/bus.h>
33 #include <sys/conf.h>
34 #include <sys/cons.h>
35 #include <sys/fcntl.h>
36 #include <sys/interrupt.h>
37 #include <sys/kdb.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/mman.h>
41 #include <sys/proc.h>
42 #include <sys/queue.h>
43 #include <sys/reboot.h>
44 #include <machine/bus.h>
45 #include <sys/rman.h>
46 #include <sys/uio.h>
47 #include <machine/resource.h>
48 #include <machine/stdarg.h>
49
50 #include <dev/pci/pcivar.h>
51
52 #include <dev/proto/proto.h>
53 #include <dev/proto/proto_dev.h>
54 #include <dev/proto/proto_busdma.h>
55
56 CTASSERT(SYS_RES_IRQ != PROTO_RES_UNUSED &&
57     SYS_RES_MEMORY != PROTO_RES_UNUSED &&
58     SYS_RES_IOPORT != PROTO_RES_UNUSED);
59 CTASSERT(SYS_RES_IRQ != PROTO_RES_PCICFG &&
60     SYS_RES_MEMORY != PROTO_RES_PCICFG &&
61     SYS_RES_IOPORT != PROTO_RES_PCICFG);
62 CTASSERT(SYS_RES_IRQ != PROTO_RES_BUSDMA &&
63     SYS_RES_MEMORY != PROTO_RES_BUSDMA &&
64     SYS_RES_IOPORT != PROTO_RES_BUSDMA);
65
66 devclass_t proto_devclass;
67 char proto_driver_name[] = "proto";
68
69 static d_open_t proto_open;
70 static d_close_t proto_close;
71 static d_read_t proto_read;
72 static d_write_t proto_write;
73 static d_ioctl_t proto_ioctl;
74 static d_mmap_t proto_mmap;
75
76 struct cdevsw proto_devsw = {
77         .d_version = D_VERSION,
78         .d_flags = 0,
79         .d_name = proto_driver_name,
80         .d_open = proto_open,
81         .d_close = proto_close,
82         .d_read = proto_read,
83         .d_write = proto_write,
84         .d_ioctl = proto_ioctl,
85         .d_mmap = proto_mmap,
86 };
87
88 static MALLOC_DEFINE(M_PROTO, "PROTO", "PROTO driver");
89
90 int
91 proto_add_resource(struct proto_softc *sc, int type, int rid,
92     struct resource *res)
93 {
94         struct proto_res *r;
95
96         if (type == PROTO_RES_UNUSED)
97                 return (EINVAL);
98         if (sc->sc_rescnt == PROTO_RES_MAX)
99                 return (ENOSPC);
100
101         r = sc->sc_res + sc->sc_rescnt++;
102         r->r_type = type;
103         r->r_rid = rid;
104         r->r_d.res = res;
105         return (0);
106 }
107
108 #ifdef notyet
109 static int
110 proto_intr(void *arg)
111 {
112         struct proto_softc *sc = arg;
113
114         /* XXX TODO */
115         return (FILTER_HANDLED);
116 }
117 #endif
118
119 int
120 proto_attach(device_t dev)
121 {
122         struct proto_softc *sc;
123         struct proto_res *r;
124         u_int res;
125
126         sc = device_get_softc(dev);
127         sc->sc_dev = dev;
128
129         for (res = 0; res < sc->sc_rescnt; res++) {
130                 r = sc->sc_res + res;
131                 switch (r->r_type) {
132                 case SYS_RES_IRQ:
133                         /* XXX TODO */
134                         break;
135                 case SYS_RES_MEMORY:
136                 case SYS_RES_IOPORT:
137                         r->r_size = rman_get_size(r->r_d.res);
138                         r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666,
139                             "proto/%s/%02x.%s", device_get_desc(dev), r->r_rid,
140                             (r->r_type == SYS_RES_IOPORT) ? "io" : "mem");
141                         r->r_u.cdev->si_drv1 = sc;
142                         r->r_u.cdev->si_drv2 = r;
143                         break;
144                 case PROTO_RES_PCICFG:
145                         r->r_size = 4096;
146                         r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666,
147                             "proto/%s/pcicfg", device_get_desc(dev));
148                         r->r_u.cdev->si_drv1 = sc;
149                         r->r_u.cdev->si_drv2 = r;
150                         break;
151                 case PROTO_RES_BUSDMA:
152                         r->r_d.busdma = proto_busdma_attach(sc);
153                         r->r_size = 0;  /* no read(2) nor write(2) */
154                         r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666,
155                             "proto/%s/busdma", device_get_desc(dev));
156                         r->r_u.cdev->si_drv1 = sc;
157                         r->r_u.cdev->si_drv2 = r;
158                         break;
159                 }
160         }
161         return (0);
162 }
163
164 int
165 proto_detach(device_t dev)
166 {
167         struct proto_softc *sc;
168         struct proto_res *r;
169         u_int res;
170
171         sc = device_get_softc(dev);
172
173         /* Don't detach if we have open device files. */
174         for (res = 0; res < sc->sc_rescnt; res++) {
175                 r = sc->sc_res + res;
176                 if (r->r_opened)
177                         return (EBUSY);
178         }
179
180         for (res = 0; res < sc->sc_rescnt; res++) {
181                 r = sc->sc_res + res;
182                 switch (r->r_type) {
183                 case SYS_RES_IRQ:
184                         /* XXX TODO */
185                         bus_release_resource(dev, r->r_type, r->r_rid,
186                             r->r_d.res);
187                         break;
188                 case SYS_RES_MEMORY:
189                 case SYS_RES_IOPORT:
190                         bus_release_resource(dev, r->r_type, r->r_rid,
191                             r->r_d.res);
192                         destroy_dev(r->r_u.cdev);
193                         break;
194                 case PROTO_RES_PCICFG:
195                         destroy_dev(r->r_u.cdev);
196                         break;
197                 case PROTO_RES_BUSDMA:
198                         proto_busdma_detach(sc, r->r_d.busdma);
199                         destroy_dev(r->r_u.cdev);
200                         break;
201                 }
202                 r->r_type = PROTO_RES_UNUSED;
203         }
204         sc->sc_rescnt = 0;
205         return (0);
206 }
207
208 /*
209  * Device functions
210  */
211
212 static int
213 proto_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
214 {
215         struct proto_res *r;
216
217         r = cdev->si_drv2;
218         if (!atomic_cmpset_acq_ptr(&r->r_opened, 0UL, (uintptr_t)td->td_proc))
219                 return (EBUSY);
220         return (0);
221 }
222
223 static int
224 proto_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
225 {
226         struct proto_res *r;
227         struct proto_softc *sc;
228
229         sc = cdev->si_drv1;
230         r = cdev->si_drv2;
231         if (!atomic_cmpset_acq_ptr(&r->r_opened, (uintptr_t)td->td_proc, 0UL))
232                 return (ENXIO);
233         if (r->r_type == PROTO_RES_BUSDMA)
234                 proto_busdma_cleanup(sc, r->r_d.busdma);
235         return (0);
236 }
237
238 static int
239 proto_read(struct cdev *cdev, struct uio *uio, int ioflag)
240 {
241         union {
242                 uint8_t x1[8];
243                 uint16_t x2[4];
244                 uint32_t x4[2];
245                 uint64_t x8[1];
246         } buf;
247         struct proto_softc *sc;
248         struct proto_res *r;
249         device_t dev;
250         off_t ofs;
251         u_long width;
252         int error;
253
254         sc = cdev->si_drv1;
255         dev = sc->sc_dev;
256         r = cdev->si_drv2;
257
258         width = uio->uio_resid;
259         if (width < 1 || width > 8 || bitcount16(width) > 1)
260                 return (EIO);
261         ofs = uio->uio_offset;
262         if (ofs + width > r->r_size)
263                 return (EIO);
264
265         switch (width) {
266         case 1:
267                 buf.x1[0] = (r->r_type == PROTO_RES_PCICFG) ?
268                     pci_read_config(dev, ofs, 1) : bus_read_1(r->r_d.res, ofs);
269                 break;
270         case 2:
271                 buf.x2[0] = (r->r_type == PROTO_RES_PCICFG) ?
272                     pci_read_config(dev, ofs, 2) : bus_read_2(r->r_d.res, ofs);
273                 break;
274         case 4:
275                 buf.x4[0] = (r->r_type == PROTO_RES_PCICFG) ?
276                     pci_read_config(dev, ofs, 4) : bus_read_4(r->r_d.res, ofs);
277                 break;
278 #ifndef __i386__
279         case 8:
280                 if (r->r_type == PROTO_RES_PCICFG)
281                         return (EINVAL);
282                 buf.x8[0] = bus_read_8(r->r_d.res, ofs);
283                 break;
284 #endif
285         default:
286                 return (EIO);
287         }
288
289         error = uiomove(&buf, width, uio);
290         return (error);
291 }
292
293 static int
294 proto_write(struct cdev *cdev, struct uio *uio, int ioflag)
295 {
296         union {
297                 uint8_t x1[8];
298                 uint16_t x2[4];
299                 uint32_t x4[2];
300                 uint64_t x8[1];
301         } buf;
302         struct proto_softc *sc;
303         struct proto_res *r;
304         device_t dev;
305         off_t ofs;
306         u_long width;
307         int error;
308
309         sc = cdev->si_drv1;
310         dev = sc->sc_dev;
311         r = cdev->si_drv2;
312
313         width = uio->uio_resid;
314         if (width < 1 || width > 8 || bitcount16(width) > 1)
315                 return (EIO);
316         ofs = uio->uio_offset;
317         if (ofs + width > r->r_size)
318                 return (EIO);
319
320         error = uiomove(&buf, width, uio);
321         if (error)
322                 return (error);
323
324         switch (width) {
325         case 1:
326                 if (r->r_type == PROTO_RES_PCICFG)
327                         pci_write_config(dev, ofs, buf.x1[0], 1);
328                 else
329                         bus_write_1(r->r_d.res, ofs, buf.x1[0]);
330                 break;
331         case 2:
332                 if (r->r_type == PROTO_RES_PCICFG)
333                         pci_write_config(dev, ofs, buf.x2[0], 2);
334                 else
335                         bus_write_2(r->r_d.res, ofs, buf.x2[0]);
336                 break;
337         case 4:
338                 if (r->r_type == PROTO_RES_PCICFG)
339                         pci_write_config(dev, ofs, buf.x4[0], 4);
340                 else
341                         bus_write_4(r->r_d.res, ofs, buf.x4[0]);
342                 break;
343 #ifndef __i386__
344         case 8:
345                 if (r->r_type == PROTO_RES_PCICFG)
346                         return (EINVAL);
347                 bus_write_8(r->r_d.res, ofs, buf.x8[0]);
348                 break;
349 #endif
350         default:
351                 return (EIO);
352         }
353
354         return (0);
355 }
356
357 static int
358 proto_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
359     struct thread *td)
360 {
361         struct proto_ioc_region *region;
362         struct proto_ioc_busdma *busdma;
363         struct proto_res *r;
364         struct proto_softc *sc;
365         int error;
366
367         sc = cdev->si_drv1;
368         r = cdev->si_drv2;
369
370         error = 0;
371         switch (cmd) {
372         case PROTO_IOC_REGION:
373                 if (r->r_type == PROTO_RES_BUSDMA) {
374                         error = EINVAL;
375                         break;
376                 }
377                 region = (struct proto_ioc_region *)data;
378                 region->size = r->r_size;
379                 if (r->r_type == PROTO_RES_PCICFG)
380                         region->address = 0;
381                 else
382                         region->address = rman_get_start(r->r_d.res);
383                 break;
384         case PROTO_IOC_BUSDMA:
385                 if (r->r_type != PROTO_RES_BUSDMA) {
386                         error = EINVAL;
387                         break;
388                 }
389                 busdma = (struct proto_ioc_busdma *)data;
390                 error = proto_busdma_ioctl(sc, r->r_d.busdma, busdma);
391                 break;
392         default:
393                 error = ENOIOCTL;
394                 break;
395         }
396         return (error);
397 }
398
399 static int
400 proto_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
401     int prot, vm_memattr_t *memattr)
402 {
403         struct proto_res *r;
404
405         r = cdev->si_drv2;
406
407         if (r->r_type != SYS_RES_MEMORY)
408                 return (ENXIO);
409         if (offset & PAGE_MASK)
410                 return (EINVAL);
411         if (prot & PROT_EXEC)
412                 return (EACCES);
413         if (offset >= r->r_size)
414                 return (EINVAL);
415         *paddr = rman_get_start(r->r_d.res) + offset;
416 #ifndef __sparc64__
417         *memattr = VM_MEMATTR_UNCACHEABLE;
418 #endif
419         return (0);
420 }