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