]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/proto/proto_core.c
Merge sendmail 8.15.2 to HEAD
[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_DRQ != PROTO_RES_UNUSED &&
58     SYS_RES_MEMORY != PROTO_RES_UNUSED &&
59     SYS_RES_IOPORT != PROTO_RES_UNUSED);
60 CTASSERT(SYS_RES_IRQ != PROTO_RES_PCICFG &&
61     SYS_RES_DRQ != PROTO_RES_PCICFG &&
62     SYS_RES_MEMORY != PROTO_RES_PCICFG &&
63     SYS_RES_IOPORT != PROTO_RES_PCICFG);
64 CTASSERT(SYS_RES_IRQ != PROTO_RES_BUSDMA &&
65     SYS_RES_DRQ != PROTO_RES_BUSDMA &&
66     SYS_RES_MEMORY != PROTO_RES_BUSDMA &&
67     SYS_RES_IOPORT != PROTO_RES_BUSDMA);
68
69 devclass_t proto_devclass;
70 char proto_driver_name[] = "proto";
71
72 static d_open_t proto_open;
73 static d_close_t proto_close;
74 static d_read_t proto_read;
75 static d_write_t proto_write;
76 static d_ioctl_t proto_ioctl;
77 static d_mmap_t proto_mmap;
78
79 struct cdevsw proto_devsw = {
80         .d_version = D_VERSION,
81         .d_flags = 0,
82         .d_name = proto_driver_name,
83         .d_open = proto_open,
84         .d_close = proto_close,
85         .d_read = proto_read,
86         .d_write = proto_write,
87         .d_ioctl = proto_ioctl,
88         .d_mmap = proto_mmap,
89 };
90
91 static MALLOC_DEFINE(M_PROTO, "PROTO", "PROTO driver");
92
93 int
94 proto_add_resource(struct proto_softc *sc, int type, int rid,
95     struct resource *res)
96 {
97         struct proto_res *r;
98
99         if (type == PROTO_RES_UNUSED)
100                 return (EINVAL);
101         if (sc->sc_rescnt == PROTO_RES_MAX)
102                 return (ENOSPC);
103
104         r = sc->sc_res + sc->sc_rescnt++;
105         r->r_type = type;
106         r->r_rid = rid;
107         r->r_d.res = res;
108         return (0);
109 }
110
111 #ifdef notyet
112 static int
113 proto_intr(void *arg)
114 {
115         struct proto_softc *sc = arg;
116
117         /* XXX TODO */
118         return (FILTER_HANDLED);
119 }
120 #endif
121
122 int
123 proto_attach(device_t dev)
124 {
125         struct proto_softc *sc;
126         struct proto_res *r;
127         u_int res;
128
129         sc = device_get_softc(dev);
130         sc->sc_dev = dev;
131
132         for (res = 0; res < sc->sc_rescnt; res++) {
133                 r = sc->sc_res + res;
134                 switch (r->r_type) {
135                 case SYS_RES_IRQ:
136                         /* XXX TODO */
137                         break;
138                 case SYS_RES_DRQ:
139                         break;
140                 case SYS_RES_MEMORY:
141                 case SYS_RES_IOPORT:
142                         r->r_size = rman_get_size(r->r_d.res);
143                         r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666,
144                             "proto/%s/%02x.%s", device_get_desc(dev), r->r_rid,
145                             (r->r_type == SYS_RES_IOPORT) ? "io" : "mem");
146                         r->r_u.cdev->si_drv1 = sc;
147                         r->r_u.cdev->si_drv2 = r;
148                         break;
149                 case PROTO_RES_PCICFG:
150                         r->r_size = 4096;
151                         r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666,
152                             "proto/%s/pcicfg", device_get_desc(dev));
153                         r->r_u.cdev->si_drv1 = sc;
154                         r->r_u.cdev->si_drv2 = r;
155                         break;
156                 case PROTO_RES_BUSDMA:
157                         r->r_d.busdma = proto_busdma_attach(sc);
158                         r->r_size = 0;  /* no read(2) nor write(2) */
159                         r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666,
160                             "proto/%s/busdma", device_get_desc(dev));
161                         r->r_u.cdev->si_drv1 = sc;
162                         r->r_u.cdev->si_drv2 = r;
163                         break;
164                 }
165         }
166         return (0);
167 }
168
169 int
170 proto_detach(device_t dev)
171 {
172         struct proto_softc *sc;
173         struct proto_res *r;
174         u_int res;
175
176         sc = device_get_softc(dev);
177
178         /* Don't detach if we have open device files. */
179         for (res = 0; res < sc->sc_rescnt; res++) {
180                 r = sc->sc_res + res;
181                 if (r->r_opened)
182                         return (EBUSY);
183         }
184
185         for (res = 0; res < sc->sc_rescnt; res++) {
186                 r = sc->sc_res + res;
187                 switch (r->r_type) {
188                 case SYS_RES_IRQ:
189                         /* XXX TODO */
190                         bus_release_resource(dev, r->r_type, r->r_rid,
191                             r->r_d.res);
192                         break;
193                 case SYS_RES_DRQ:
194                         bus_release_resource(dev, r->r_type, r->r_rid,
195                             r->r_d.res);
196                         break;
197                 case SYS_RES_MEMORY:
198                 case SYS_RES_IOPORT:
199                         bus_release_resource(dev, r->r_type, r->r_rid,
200                             r->r_d.res);
201                         destroy_dev(r->r_u.cdev);
202                         break;
203                 case PROTO_RES_PCICFG:
204                         destroy_dev(r->r_u.cdev);
205                         break;
206                 case PROTO_RES_BUSDMA:
207                         proto_busdma_detach(sc, r->r_d.busdma);
208                         destroy_dev(r->r_u.cdev);
209                         break;
210                 }
211                 r->r_type = PROTO_RES_UNUSED;
212         }
213         sc->sc_rescnt = 0;
214         return (0);
215 }
216
217 /*
218  * Device functions
219  */
220
221 static int
222 proto_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
223 {
224         struct proto_res *r;
225
226         r = cdev->si_drv2;
227         if (!atomic_cmpset_acq_ptr(&r->r_opened, 0UL, (uintptr_t)td->td_proc))
228                 return (EBUSY);
229         return (0);
230 }
231
232 static int
233 proto_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
234 {
235         struct proto_res *r;
236         struct proto_softc *sc;
237
238         sc = cdev->si_drv1;
239         r = cdev->si_drv2;
240         if (!atomic_cmpset_acq_ptr(&r->r_opened, (uintptr_t)td->td_proc, 0UL))
241                 return (ENXIO);
242         if (r->r_type == PROTO_RES_BUSDMA)
243                 proto_busdma_cleanup(sc, r->r_d.busdma);
244         return (0);
245 }
246
247 static int
248 proto_read(struct cdev *cdev, struct uio *uio, int ioflag)
249 {
250         union {
251                 uint8_t x1[8];
252                 uint16_t x2[4];
253                 uint32_t x4[2];
254                 uint64_t x8[1];
255         } buf;
256         struct proto_softc *sc;
257         struct proto_res *r;
258         device_t dev;
259         off_t ofs;
260         u_long width;
261         int error;
262
263         sc = cdev->si_drv1;
264         dev = sc->sc_dev;
265         r = cdev->si_drv2;
266
267         width = uio->uio_resid;
268         if (width < 1 || width > 8 || bitcount16(width) > 1)
269                 return (EIO);
270         ofs = uio->uio_offset;
271         if (ofs + width > r->r_size)
272                 return (EIO);
273
274         switch (width) {
275         case 1:
276                 buf.x1[0] = (r->r_type == PROTO_RES_PCICFG) ?
277                     pci_read_config(dev, ofs, 1) : bus_read_1(r->r_d.res, ofs);
278                 break;
279         case 2:
280                 buf.x2[0] = (r->r_type == PROTO_RES_PCICFG) ?
281                     pci_read_config(dev, ofs, 2) : bus_read_2(r->r_d.res, ofs);
282                 break;
283         case 4:
284                 buf.x4[0] = (r->r_type == PROTO_RES_PCICFG) ?
285                     pci_read_config(dev, ofs, 4) : bus_read_4(r->r_d.res, ofs);
286                 break;
287 #ifndef __i386__
288         case 8:
289                 if (r->r_type == PROTO_RES_PCICFG)
290                         return (EINVAL);
291                 buf.x8[0] = bus_read_8(r->r_d.res, ofs);
292                 break;
293 #endif
294         default:
295                 return (EIO);
296         }
297
298         error = uiomove(&buf, width, uio);
299         return (error);
300 }
301
302 static int
303 proto_write(struct cdev *cdev, struct uio *uio, int ioflag)
304 {
305         union {
306                 uint8_t x1[8];
307                 uint16_t x2[4];
308                 uint32_t x4[2];
309                 uint64_t x8[1];
310         } buf;
311         struct proto_softc *sc;
312         struct proto_res *r;
313         device_t dev;
314         off_t ofs;
315         u_long width;
316         int error;
317
318         sc = cdev->si_drv1;
319         dev = sc->sc_dev;
320         r = cdev->si_drv2;
321
322         width = uio->uio_resid;
323         if (width < 1 || width > 8 || bitcount16(width) > 1)
324                 return (EIO);
325         ofs = uio->uio_offset;
326         if (ofs + width > r->r_size)
327                 return (EIO);
328
329         error = uiomove(&buf, width, uio);
330         if (error)
331                 return (error);
332
333         switch (width) {
334         case 1:
335                 if (r->r_type == PROTO_RES_PCICFG)
336                         pci_write_config(dev, ofs, buf.x1[0], 1);
337                 else
338                         bus_write_1(r->r_d.res, ofs, buf.x1[0]);
339                 break;
340         case 2:
341                 if (r->r_type == PROTO_RES_PCICFG)
342                         pci_write_config(dev, ofs, buf.x2[0], 2);
343                 else
344                         bus_write_2(r->r_d.res, ofs, buf.x2[0]);
345                 break;
346         case 4:
347                 if (r->r_type == PROTO_RES_PCICFG)
348                         pci_write_config(dev, ofs, buf.x4[0], 4);
349                 else
350                         bus_write_4(r->r_d.res, ofs, buf.x4[0]);
351                 break;
352 #ifndef __i386__
353         case 8:
354                 if (r->r_type == PROTO_RES_PCICFG)
355                         return (EINVAL);
356                 bus_write_8(r->r_d.res, ofs, buf.x8[0]);
357                 break;
358 #endif
359         default:
360                 return (EIO);
361         }
362
363         return (0);
364 }
365
366 static int
367 proto_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
368     struct thread *td)
369 {
370         struct proto_ioc_region *region;
371         struct proto_ioc_busdma *busdma;
372         struct proto_res *r;
373         struct proto_softc *sc;
374         int error;
375
376         sc = cdev->si_drv1;
377         r = cdev->si_drv2;
378
379         error = 0;
380         switch (cmd) {
381         case PROTO_IOC_REGION:
382                 if (r->r_type == PROTO_RES_BUSDMA) {
383                         error = EINVAL;
384                         break;
385                 }
386                 region = (struct proto_ioc_region *)data;
387                 region->size = r->r_size;
388                 if (r->r_type == PROTO_RES_PCICFG)
389                         region->address = 0;
390                 else
391                         region->address = rman_get_start(r->r_d.res);
392                 break;
393         case PROTO_IOC_BUSDMA:
394                 if (r->r_type != PROTO_RES_BUSDMA) {
395                         error = EINVAL;
396                         break;
397                 }
398                 busdma = (struct proto_ioc_busdma *)data;
399                 error = proto_busdma_ioctl(sc, r->r_d.busdma, busdma, td);
400                 break;
401         default:
402                 error = ENOIOCTL;
403                 break;
404         }
405         return (error);
406 }
407
408 static int
409 proto_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
410     int prot, vm_memattr_t *memattr)
411 {
412         struct proto_res *r;
413
414         if (offset & PAGE_MASK)
415                 return (EINVAL);
416         if (prot & PROT_EXEC)
417                 return (EACCES);
418
419         r = cdev->si_drv2;
420
421         switch (r->r_type) {
422         case SYS_RES_MEMORY:
423                 if (offset >= r->r_size)
424                         return (EINVAL);
425                 *paddr = rman_get_start(r->r_d.res) + offset;
426 #ifndef __sparc64__
427                 *memattr = VM_MEMATTR_UNCACHEABLE;
428 #endif
429                 break;
430         case PROTO_RES_BUSDMA:
431                 if (!proto_busdma_mmap_allowed(r->r_d.busdma, offset))
432                         return (EINVAL);
433                 *paddr = offset;
434                 break;
435         default:
436                 return (ENXIO);
437         }
438         return (0);
439 }