]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/proto/proto_core.c
zfs: merge openzfs/zfs@2e2a46e0a
[FreeBSD/FreeBSD.git] / sys / dev / proto / proto_core.c
1 /*-
2  * Copyright (c) 2014, 2015, 2019 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 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/bus.h>
31 #include <sys/conf.h>
32 #include <sys/cons.h>
33 #include <sys/fcntl.h>
34 #include <sys/interrupt.h>
35 #include <sys/kdb.h>
36 #include <sys/kernel.h>
37 #include <sys/malloc.h>
38 #include <sys/mman.h>
39 #include <sys/proc.h>
40 #include <sys/queue.h>
41 #include <sys/reboot.h>
42 #include <machine/bus.h>
43 #include <sys/rman.h>
44 #include <sys/uio.h>
45 #include <machine/resource.h>
46 #include <machine/stdarg.h>
47
48 #include <dev/pci/pcivar.h>
49
50 #include <dev/proto/proto.h>
51 #include <dev/proto/proto_dev.h>
52 #include <dev/proto/proto_busdma.h>
53
54 CTASSERT(SYS_RES_IRQ != PROTO_RES_UNUSED &&
55     SYS_RES_DRQ != PROTO_RES_UNUSED &&
56     SYS_RES_MEMORY != PROTO_RES_UNUSED &&
57     SYS_RES_IOPORT != PROTO_RES_UNUSED);
58 CTASSERT(SYS_RES_IRQ != PROTO_RES_PCICFG &&
59     SYS_RES_DRQ != 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_DRQ != PROTO_RES_BUSDMA &&
64     SYS_RES_MEMORY != PROTO_RES_BUSDMA &&
65     SYS_RES_IOPORT != PROTO_RES_BUSDMA);
66
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_probe(device_t dev, const char *prefix, char ***devnamesp)
121 {
122         char **devnames = *devnamesp;
123         const char *dn, *ep, *ev;
124         size_t pfxlen;
125         int idx, names;
126
127         if (devnames == NULL) {
128                 pfxlen = strlen(prefix);
129                 names = 1;      /* NULL pointer */
130                 ev = kern_getenv("hw.proto.attach");
131                 if (ev != NULL) {
132                         dn = ev;
133                         while (*dn != '\0') {
134                                 ep = dn;
135                                 while (*ep != ',' && *ep != '\0')
136                                         ep++;
137                                 if ((ep - dn) > pfxlen &&
138                                     strncmp(dn, prefix, pfxlen) == 0)
139                                         names++;
140                                 dn = (*ep == ',') ? ep + 1 : ep;
141                         }
142                 }
143                 devnames = malloc(names * sizeof(caddr_t), M_DEVBUF,
144                     M_WAITOK | M_ZERO);
145                 *devnamesp = devnames;
146                 if (ev != NULL) {
147                         dn = ev;
148                         idx = 0;
149                         while (*dn != '\0') {
150                                 ep = dn;
151                                 while (*ep != ',' && *ep != '\0')
152                                         ep++;
153                                 if ((ep - dn) > pfxlen &&
154                                     strncmp(dn, prefix, pfxlen) == 0) {
155                                         devnames[idx] = malloc(ep - dn + 1,
156                                             M_DEVBUF, M_WAITOK | M_ZERO);
157                                         memcpy(devnames[idx], dn, ep - dn);
158                                         idx++;
159                                 }
160                                 dn = (*ep == ',') ? ep + 1 : ep;
161                         }
162                         freeenv(__DECONST(char *, ev));
163                 }
164         }
165
166         dn = device_get_desc(dev);
167         while (*devnames != NULL) {
168                 if (strcmp(dn, *devnames) == 0)
169                         return (BUS_PROBE_SPECIFIC);
170                 devnames++;
171         }
172         return (BUS_PROBE_HOOVER);
173 }
174
175 int
176 proto_attach(device_t dev)
177 {
178         struct proto_softc *sc;
179         struct proto_res *r;
180         u_int res;
181
182         sc = device_get_softc(dev);
183         sc->sc_dev = dev;
184         mtx_init(&sc->sc_mtx, "proto-softc", NULL, MTX_DEF);
185
186         for (res = 0; res < sc->sc_rescnt; res++) {
187                 r = sc->sc_res + res;
188                 switch (r->r_type) {
189                 case SYS_RES_IRQ:
190                         /* XXX TODO */
191                         break;
192                 case SYS_RES_DRQ:
193                         break;
194                 case SYS_RES_MEMORY:
195                 case SYS_RES_IOPORT:
196                         r->r_size = rman_get_size(r->r_d.res);
197                         r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0600,
198                             "proto/%s/%02x.%s", device_get_desc(dev), r->r_rid,
199                             (r->r_type == SYS_RES_IOPORT) ? "io" : "mem");
200                         r->r_u.cdev->si_drv1 = sc;
201                         r->r_u.cdev->si_drv2 = r;
202                         break;
203                 case PROTO_RES_PCICFG:
204                         r->r_size = 4096;
205                         r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0600,
206                             "proto/%s/pcicfg", device_get_desc(dev));
207                         r->r_u.cdev->si_drv1 = sc;
208                         r->r_u.cdev->si_drv2 = r;
209                         break;
210                 case PROTO_RES_BUSDMA:
211                         r->r_d.busdma = proto_busdma_attach(sc);
212                         r->r_size = 0;  /* no read(2) nor write(2) */
213                         r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0600,
214                             "proto/%s/busdma", device_get_desc(dev));
215                         r->r_u.cdev->si_drv1 = sc;
216                         r->r_u.cdev->si_drv2 = r;
217                         break;
218                 }
219         }
220         return (0);
221 }
222
223 int
224 proto_detach(device_t dev)
225 {
226         struct proto_softc *sc;
227         struct proto_res *r;
228         u_int res;
229
230         sc = device_get_softc(dev);
231
232         mtx_lock(&sc->sc_mtx);
233         if (sc->sc_opencnt == 0)
234                 sc->sc_opencnt = -1;
235         mtx_unlock(&sc->sc_mtx);
236         if (sc->sc_opencnt > 0)
237                 return (EBUSY);
238
239         for (res = 0; res < sc->sc_rescnt; res++) {
240                 r = sc->sc_res + res;
241
242                 switch (r->r_type) {
243                 case SYS_RES_IRQ:
244                         /* XXX TODO */
245                         bus_release_resource(dev, r->r_type, r->r_rid,
246                             r->r_d.res);
247                         break;
248                 case SYS_RES_DRQ:
249                         bus_release_resource(dev, r->r_type, r->r_rid,
250                             r->r_d.res);
251                         break;
252                 case SYS_RES_MEMORY:
253                 case SYS_RES_IOPORT:
254                         destroy_dev(r->r_u.cdev);
255                         bus_release_resource(dev, r->r_type, r->r_rid,
256                             r->r_d.res);
257                         break;
258                 case PROTO_RES_PCICFG:
259                         destroy_dev(r->r_u.cdev);
260                         break;
261                 case PROTO_RES_BUSDMA:
262                         destroy_dev(r->r_u.cdev);
263                         proto_busdma_detach(sc, r->r_d.busdma);
264                         break;
265                 }
266                 r->r_type = PROTO_RES_UNUSED;
267         }
268         mtx_lock(&sc->sc_mtx);
269         sc->sc_rescnt = 0;
270         sc->sc_opencnt = 0;
271         mtx_unlock(&sc->sc_mtx);
272         mtx_destroy(&sc->sc_mtx);
273         return (0);
274 }
275
276 /*
277  * Device functions
278  */
279
280 static int
281 proto_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
282 {
283         struct proto_res *r;
284         struct proto_softc *sc;
285         int error;
286
287         sc = cdev->si_drv1;
288         mtx_lock(&sc->sc_mtx);
289         if (sc->sc_opencnt >= 0) {
290                 r = cdev->si_drv2;
291                 if (!r->r_opened) {
292                         r->r_opened = 1;
293                         sc->sc_opencnt++;
294                         error = 0;
295                 } else
296                         error = EBUSY;
297         } else
298                 error = ENXIO;
299         mtx_unlock(&sc->sc_mtx);
300         return (error);
301 }
302
303 static int
304 proto_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
305 {
306         struct proto_res *r;
307         struct proto_softc *sc;
308         int error;
309
310         sc = cdev->si_drv1;
311         mtx_lock(&sc->sc_mtx);
312         if (sc->sc_opencnt > 0) {
313                 r = cdev->si_drv2;
314                 if (r->r_opened) {
315                         if (r->r_type == PROTO_RES_BUSDMA)
316                                 proto_busdma_cleanup(sc, r->r_d.busdma);
317                         r->r_opened = 0;
318                         sc->sc_opencnt--;
319                         error = 0;
320                 } else
321                         error = ENXIO;
322         } else
323                 error = ENXIO;
324         mtx_unlock(&sc->sc_mtx);
325         return (error);
326 }
327
328 static int
329 proto_read(struct cdev *cdev, struct uio *uio, int ioflag)
330 {
331         union {
332                 uint8_t x1[8];
333                 uint16_t x2[4];
334                 uint32_t x4[2];
335                 uint64_t x8[1];
336         } buf;
337         struct proto_softc *sc;
338         struct proto_res *r;
339         device_t dev;
340         off_t ofs;
341         u_long width;
342         int error;
343
344         sc = cdev->si_drv1;
345         dev = sc->sc_dev;
346         r = cdev->si_drv2;
347
348         width = uio->uio_resid;
349         if (width < 1 || width > 8 || bitcount16(width) > 1)
350                 return (EIO);
351         ofs = uio->uio_offset;
352         if (ofs + width > r->r_size)
353                 return (EIO);
354
355         switch (width) {
356         case 1:
357                 buf.x1[0] = (r->r_type == PROTO_RES_PCICFG) ?
358                     pci_read_config(dev, ofs, 1) : bus_read_1(r->r_d.res, ofs);
359                 break;
360         case 2:
361                 buf.x2[0] = (r->r_type == PROTO_RES_PCICFG) ?
362                     pci_read_config(dev, ofs, 2) : bus_read_2(r->r_d.res, ofs);
363                 break;
364         case 4:
365                 buf.x4[0] = (r->r_type == PROTO_RES_PCICFG) ?
366                     pci_read_config(dev, ofs, 4) : bus_read_4(r->r_d.res, ofs);
367                 break;
368 #ifndef __i386__
369         case 8:
370                 if (r->r_type == PROTO_RES_PCICFG)
371                         return (EINVAL);
372                 buf.x8[0] = bus_read_8(r->r_d.res, ofs);
373                 break;
374 #endif
375         default:
376                 return (EIO);
377         }
378
379         error = uiomove(&buf, width, uio);
380         return (error);
381 }
382
383 static int
384 proto_write(struct cdev *cdev, struct uio *uio, int ioflag)
385 {
386         union {
387                 uint8_t x1[8];
388                 uint16_t x2[4];
389                 uint32_t x4[2];
390                 uint64_t x8[1];
391         } buf;
392         struct proto_softc *sc;
393         struct proto_res *r;
394         device_t dev;
395         off_t ofs;
396         u_long width;
397         int error;
398
399         sc = cdev->si_drv1;
400         dev = sc->sc_dev;
401         r = cdev->si_drv2;
402
403         width = uio->uio_resid;
404         if (width < 1 || width > 8 || bitcount16(width) > 1)
405                 return (EIO);
406         ofs = uio->uio_offset;
407         if (ofs + width > r->r_size)
408                 return (EIO);
409
410         error = uiomove(&buf, width, uio);
411         if (error)
412                 return (error);
413
414         switch (width) {
415         case 1:
416                 if (r->r_type == PROTO_RES_PCICFG)
417                         pci_write_config(dev, ofs, buf.x1[0], 1);
418                 else
419                         bus_write_1(r->r_d.res, ofs, buf.x1[0]);
420                 break;
421         case 2:
422                 if (r->r_type == PROTO_RES_PCICFG)
423                         pci_write_config(dev, ofs, buf.x2[0], 2);
424                 else
425                         bus_write_2(r->r_d.res, ofs, buf.x2[0]);
426                 break;
427         case 4:
428                 if (r->r_type == PROTO_RES_PCICFG)
429                         pci_write_config(dev, ofs, buf.x4[0], 4);
430                 else
431                         bus_write_4(r->r_d.res, ofs, buf.x4[0]);
432                 break;
433 #ifndef __i386__
434         case 8:
435                 if (r->r_type == PROTO_RES_PCICFG)
436                         return (EINVAL);
437                 bus_write_8(r->r_d.res, ofs, buf.x8[0]);
438                 break;
439 #endif
440         default:
441                 return (EIO);
442         }
443
444         return (0);
445 }
446
447 static int
448 proto_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
449     struct thread *td)
450 {
451         struct proto_ioc_region *region;
452         struct proto_ioc_busdma *busdma;
453         struct proto_res *r;
454         struct proto_softc *sc;
455         int error;
456
457         sc = cdev->si_drv1;
458         r = cdev->si_drv2;
459
460         error = 0;
461         switch (cmd) {
462         case PROTO_IOC_REGION:
463                 if (r->r_type == PROTO_RES_BUSDMA) {
464                         error = EINVAL;
465                         break;
466                 }
467                 region = (struct proto_ioc_region *)data;
468                 region->size = r->r_size;
469                 if (r->r_type == PROTO_RES_PCICFG)
470                         region->address = 0;
471                 else
472                         region->address = rman_get_start(r->r_d.res);
473                 break;
474         case PROTO_IOC_BUSDMA:
475                 if (r->r_type != PROTO_RES_BUSDMA) {
476                         error = EINVAL;
477                         break;
478                 }
479                 busdma = (struct proto_ioc_busdma *)data;
480                 error = proto_busdma_ioctl(sc, r->r_d.busdma, busdma, td);
481                 break;
482         default:
483                 error = ENOIOCTL;
484                 break;
485         }
486         return (error);
487 }
488
489 static int
490 proto_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
491     int prot, vm_memattr_t *memattr)
492 {
493         struct proto_res *r;
494
495         if (offset & PAGE_MASK)
496                 return (EINVAL);
497         if (prot & PROT_EXEC)
498                 return (EACCES);
499
500         r = cdev->si_drv2;
501
502         switch (r->r_type) {
503         case SYS_RES_MEMORY:
504                 if (offset >= r->r_size)
505                         return (EINVAL);
506                 *paddr = rman_get_start(r->r_d.res) + offset;
507                 *memattr = VM_MEMATTR_UNCACHEABLE;
508                 break;
509         case PROTO_RES_BUSDMA:
510                 if (!proto_busdma_mmap_allowed(r->r_d.busdma, offset))
511                         return (EINVAL);
512                 *paddr = offset;
513                 break;
514         default:
515                 return (ENXIO);
516         }
517         return (0);
518 }