Index: head/share/man/man4/proto.4 =================================================================== --- head/share/man/man4/proto.4 (revision 285706) +++ head/share/man/man4/proto.4 (revision 285707) @@ -1,172 +1,178 @@ .\" .\" Copyright (c) 2014, 2015 Marcel Moolenaar .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" .\" $FreeBSD$ .\" -.Dd July 3, 2015 +.Dd July 19, 2015 .Dt PROTO 4 .Os .\" .Sh NAME .Nm proto .Nd Generic prototyping and diagnostics driver .\" .Sh SYNOPSIS To compile this driver into the kernel, place the following line in your kernel configuration file: .Bd -ragged -offset indent .Cd "device proto" .Ed .Pp Alternatively, to load the driver as a module at boot time, place the following line in .Xr loader.conf 5 : .Bd -literal -offset indent proto_load="YES" +.Ed +.Pp +To have the driver attach to a device instead of its regular driver, +mention it in the list of devices assigned to the following loader variable: +.Bd -ragged -offset indent +hw.proto.attach="desc[,desc]" .Ed .\" .Sh DESCRIPTION The .Nm device driver attaches to PCI or ISA devices when no other device drivers are present for those devices and it creates device special files for all resources associated with the device. The driver itself has no knowledge of the device it attaches to. Programs can open these device special files and perform register-level reads and writes. As such, the .Nm device driver is nothing but a conduit or gateway between user space programs and the hardware device. .Pp Examples for why this is useful include hardware diagnostics and prototyping. In both these use cases, it is far more convenient to develop and run the logic in user space. Especially hardware diagnostics requires a somewhat user-friendly interface and adequate reporting. Neither is done easily as kernel code. .\" .Sh FILES All device special files corresponding to a PCI device are located under .Pa /dev/proto/pci::: with .Pa pci::: representing the location of the PCI device in the PCI hierarchy. A PCI location includes: .Pp .Bl -tag -width XXXXXX -compact -offset indent .It The PCI domain number .It The PCI bus number .It The PCI slot or device number .It The PCI function number .El .Pp Every PCI device has a device special file called .Pa pcicfg . This device special file gives access to the PCI configuration space. A device special file called .Pa busdma is also created. This device special file provides the interfaces needed for doing DMA. For each valid base address register (BAR), a device special file is created that contains the BAR offset and the resource type. A resource type can be either .Pa io or .Pa mem representing I/O port or memory mapped I/O space (resp.) .Pp ISA devices do not have a location. Instead, they are identified by the first I/O port address or first memory mapped I/O address. Consequently, all device special files corresponding to an ISA device are located under .Pa /dev/proto/isa: with .Pa addr the address in hexadecimal notation. For each I/O port or memory mapped I/O address, a device special file is created that contains the resource identification used by the kernel and the resource type. The resource type can be either .Pa io or .Pa mem representing I/O port or memory mapped I/O space (resp.) When the device has a DMA channel assigned to it, a device special file with the name .Pa busdma is created as well. This device special file provides the interfaces needed for doing DMA. .Pp If the ISA device is not a Plug-and-Play device nor present in the ACPI device tree, it must have the appropriate hints so that the kernel can reserve the resources for it. .\" .Sh EXAMPLES A single function PCI device in domain 0, on bus 1, in slot 2 and having a single memory mapped I/O region will have the following device special files: .Pp .Bl -tag -width XXXXXX -compact -offset indent .It Pa /dev/proto/pci0:1:2:0/10.mem .It Pa /dev/proto/pci0:1:2:0/pcicfg .El .Pp A legacy floppy controller will have the following device files: .Pp .Bl -tag -width XXXXXX -compact -offset indent .It Pa /dev/proto/isa:0x3f0/00.io .It Pa /dev/proto/isa:0x3f0/01.io .It Pa /dev/proto/isa:0x3f0/busdma .El .\" .Sh AUTHORS The .Nm device driver and this manual page were written by .An Marcel Moolenaar Aq Mt marcel@xcllnt.net . .Sh SECURITY CONSIDERATIONS Because programs have direct access to the hardware, the .Nm driver is inherently insecure. It is not advisable to use this driver on a production machine. .\" .Sh MISSING FUNCTIONALITY The .Nm driver does not yet support interrupts. Since interrupts cannot be handled by the driver itself, they must be converted into signals and delivered to the program that has registered for interrupts. A satisfactory mechanism for keeping the interrupt masked during the signal handling is still being worked out. .Pp DMA support for devices other than busmaster devices is not present yet. The details of how a program is to interact with the DMA controller still need to be fleshed out. Index: head/sys/dev/proto/proto.h =================================================================== --- head/sys/dev/proto/proto.h (revision 285706) +++ head/sys/dev/proto/proto.h (revision 285707) @@ -1,67 +1,68 @@ /*- - * Copyright (c) 2014 Marcel Moolenaar + * Copyright (c) 2014, 2015 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _DEV_PROTO_H_ #define _DEV_PROTO_H_ #define PROTO_RES_MAX 16 #define PROTO_RES_UNUSED 0 #define PROTO_RES_PCICFG 10 #define PROTO_RES_BUSDMA 11 struct proto_res { int r_type; int r_rid; union { struct resource *res; void *busdma; } r_d; u_long r_size; union { void *cookie; struct cdev *cdev; } r_u; uintptr_t r_opened; }; struct proto_softc { device_t sc_dev; struct proto_res sc_res[PROTO_RES_MAX]; int sc_rescnt; }; extern devclass_t proto_devclass; extern char proto_driver_name[]; int proto_add_resource(struct proto_softc *, int, int, struct resource *); +int proto_probe(device_t dev, const char *prefix, char ***devnamesp); int proto_attach(device_t dev); int proto_detach(device_t dev); #endif /* _DEV_PROTO_H_ */ Index: head/sys/dev/proto/proto_bus_isa.c =================================================================== --- head/sys/dev/proto/proto_bus_isa.c (revision 285706) +++ head/sys/dev/proto/proto_bus_isa.c (revision 285707) @@ -1,121 +1,124 @@ /*- * Copyright (c) 2015 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include static int proto_isa_probe(device_t dev); static int proto_isa_attach(device_t dev); static device_method_t proto_isa_methods[] = { /* Device interface */ DEVMETHOD(device_probe, proto_isa_probe), DEVMETHOD(device_attach, proto_isa_attach), DEVMETHOD(device_detach, proto_detach), DEVMETHOD_END }; static driver_t proto_isa_driver = { proto_driver_name, proto_isa_methods, sizeof(struct proto_softc), }; +static char proto_isa_prefix[] = "isa"; +static char **proto_isa_devnames; + static int proto_isa_probe(device_t dev) { struct sbuf *sb; struct resource *res; int rid, type; rid = 0; type = SYS_RES_IOPORT; res = bus_alloc_resource_any(dev, type, &rid, RF_ACTIVE); if (res == NULL) { type = SYS_RES_MEMORY; res = bus_alloc_resource_any(dev, type, &rid, RF_ACTIVE); } if (res == NULL) return (ENODEV); sb = sbuf_new_auto(); - sbuf_printf(sb, "isa:%#lx", rman_get_start(res)); + sbuf_printf(sb, "%s:%#lx", proto_isa_prefix, rman_get_start(res)); sbuf_finish(sb); device_set_desc_copy(dev, sbuf_data(sb)); sbuf_delete(sb); bus_release_resource(dev, type, rid, res); - return (BUS_PROBE_HOOVER); + return (proto_probe(dev, proto_isa_prefix, &proto_isa_devnames)); } static int proto_isa_alloc(device_t dev, int type, int nrids) { struct resource *res; struct proto_softc *sc; int count, rid; sc = device_get_softc(dev); count = 0; for (rid = 0; rid < nrids; rid++) { res = bus_alloc_resource_any(dev, type, &rid, RF_ACTIVE); if (res == NULL) break; proto_add_resource(sc, type, rid, res); count++; } if (type == SYS_RES_DRQ && count > 0) proto_add_resource(sc, PROTO_RES_BUSDMA, 0, NULL); return (count); } static int proto_isa_attach(device_t dev) { proto_isa_alloc(dev, SYS_RES_IRQ, ISA_NIRQ); proto_isa_alloc(dev, SYS_RES_DRQ, ISA_NDRQ); proto_isa_alloc(dev, SYS_RES_IOPORT, ISA_NPORT); proto_isa_alloc(dev, SYS_RES_MEMORY, ISA_NMEM); return (proto_attach(dev)); } DRIVER_MODULE(proto, acpi, proto_isa_driver, proto_devclass, NULL, NULL); DRIVER_MODULE(proto, isa, proto_isa_driver, proto_devclass, NULL, NULL); Index: head/sys/dev/proto/proto_bus_pci.c =================================================================== --- head/sys/dev/proto/proto_bus_pci.c (revision 285706) +++ head/sys/dev/proto/proto_bus_pci.c (revision 285707) @@ -1,115 +1,118 @@ /*- - * Copyright (c) 2014 Marcel Moolenaar + * Copyright (c) 2014, 2015 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include static int proto_pci_probe(device_t dev); static int proto_pci_attach(device_t dev); static device_method_t proto_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, proto_pci_probe), DEVMETHOD(device_attach, proto_pci_attach), DEVMETHOD(device_detach, proto_detach), DEVMETHOD_END }; static driver_t proto_pci_driver = { proto_driver_name, proto_pci_methods, sizeof(struct proto_softc), }; +static char proto_pci_prefix[] = "pci"; +static char **proto_pci_devnames; + static int proto_pci_probe(device_t dev) { struct sbuf *sb; if ((pci_read_config(dev, PCIR_HDRTYPE, 1) & PCIM_HDRTYPE) != 0) return (ENXIO); sb = sbuf_new_auto(); - sbuf_printf(sb, "pci%d:%d:%d:%d", pci_get_domain(dev), + sbuf_printf(sb, "%s%d:%d:%d:%d", proto_pci_prefix, pci_get_domain(dev), pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev)); sbuf_finish(sb); device_set_desc_copy(dev, sbuf_data(sb)); sbuf_delete(sb); - return (BUS_PROBE_HOOVER); + return (proto_probe(dev, proto_pci_prefix, &proto_pci_devnames)); } static int proto_pci_attach(device_t dev) { struct proto_softc *sc; struct resource *res; uint32_t val; int bar, rid, type; sc = device_get_softc(dev); proto_add_resource(sc, PROTO_RES_PCICFG, 0, NULL); proto_add_resource(sc, PROTO_RES_BUSDMA, 0, NULL); for (bar = 0; bar < PCIR_MAX_BAR_0; bar++) { rid = PCIR_BAR(bar); val = pci_read_config(dev, rid, 4); type = (PCI_BAR_IO(val)) ? SYS_RES_IOPORT : SYS_RES_MEMORY; res = bus_alloc_resource_any(dev, type, &rid, RF_ACTIVE); if (res == NULL) continue; proto_add_resource(sc, type, rid, res); if (type == SYS_RES_IOPORT) continue; /* Skip over adjacent BAR for 64-bit memory BARs. */ if ((val & PCIM_BAR_MEM_TYPE) == PCIM_BAR_MEM_64) bar++; } rid = 0; type = SYS_RES_IRQ; res = bus_alloc_resource_any(dev, type, &rid, RF_ACTIVE | RF_SHAREABLE); if (res != NULL) proto_add_resource(sc, type, rid, res); return (proto_attach(dev)); } DRIVER_MODULE(proto, pci, proto_pci_driver, proto_devclass, NULL, NULL); Index: head/sys/dev/proto/proto_core.c =================================================================== --- head/sys/dev/proto/proto_core.c (revision 285706) +++ head/sys/dev/proto/proto_core.c (revision 285707) @@ -1,439 +1,495 @@ /*- - * Copyright (c) 2014 Marcel Moolenaar + * Copyright (c) 2014, 2015 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include CTASSERT(SYS_RES_IRQ != PROTO_RES_UNUSED && SYS_RES_DRQ != PROTO_RES_UNUSED && SYS_RES_MEMORY != PROTO_RES_UNUSED && SYS_RES_IOPORT != PROTO_RES_UNUSED); CTASSERT(SYS_RES_IRQ != PROTO_RES_PCICFG && SYS_RES_DRQ != PROTO_RES_PCICFG && SYS_RES_MEMORY != PROTO_RES_PCICFG && SYS_RES_IOPORT != PROTO_RES_PCICFG); CTASSERT(SYS_RES_IRQ != PROTO_RES_BUSDMA && SYS_RES_DRQ != PROTO_RES_BUSDMA && SYS_RES_MEMORY != PROTO_RES_BUSDMA && SYS_RES_IOPORT != PROTO_RES_BUSDMA); devclass_t proto_devclass; char proto_driver_name[] = "proto"; static d_open_t proto_open; static d_close_t proto_close; static d_read_t proto_read; static d_write_t proto_write; static d_ioctl_t proto_ioctl; static d_mmap_t proto_mmap; struct cdevsw proto_devsw = { .d_version = D_VERSION, .d_flags = 0, .d_name = proto_driver_name, .d_open = proto_open, .d_close = proto_close, .d_read = proto_read, .d_write = proto_write, .d_ioctl = proto_ioctl, .d_mmap = proto_mmap, }; static MALLOC_DEFINE(M_PROTO, "PROTO", "PROTO driver"); int proto_add_resource(struct proto_softc *sc, int type, int rid, struct resource *res) { struct proto_res *r; if (type == PROTO_RES_UNUSED) return (EINVAL); if (sc->sc_rescnt == PROTO_RES_MAX) return (ENOSPC); r = sc->sc_res + sc->sc_rescnt++; r->r_type = type; r->r_rid = rid; r->r_d.res = res; return (0); } #ifdef notyet static int proto_intr(void *arg) { struct proto_softc *sc = arg; /* XXX TODO */ return (FILTER_HANDLED); } #endif + +int +proto_probe(device_t dev, const char *prefix, char ***devnamesp) +{ + char **devnames = *devnamesp; + const char *dn, *ep, *ev; + size_t pfxlen; + int idx, names; + + if (devnames == NULL) { + pfxlen = strlen(prefix); + names = 1; /* NULL pointer */ + ev = kern_getenv("hw.proto.attach"); + if (ev != NULL) { + dn = ev; + while (*dn != '\0') { + ep = dn; + while (*ep != ',' && *ep != '\0') + ep++; + if ((ep - dn) > pfxlen && + strncmp(dn, prefix, pfxlen) == 0) + names++; + dn = (*ep == ',') ? ep + 1 : ep; + } + } + devnames = malloc(names * sizeof(caddr_t), M_DEVBUF, + M_WAITOK | M_ZERO); + *devnamesp = devnames; + if (ev != NULL) { + dn = ev; + idx = 0; + while (*dn != '\0') { + ep = dn; + while (*ep != ',' && *ep != '\0') + ep++; + if ((ep - dn) > pfxlen && + strncmp(dn, prefix, pfxlen) == 0) { + devnames[idx] = malloc(ep - dn + 1, + M_DEVBUF, M_WAITOK | M_ZERO); + memcpy(devnames[idx], dn, ep - dn); + idx++; + } + dn = (*ep == ',') ? ep + 1 : ep; + } + freeenv(__DECONST(char *, ev)); + } + } + + dn = device_get_desc(dev); + while (*devnames != NULL) { + if (strcmp(dn, *devnames) == 0) + return (BUS_PROBE_SPECIFIC); + devnames++; + } + return (BUS_PROBE_HOOVER); +} int proto_attach(device_t dev) { struct proto_softc *sc; struct proto_res *r; u_int res; sc = device_get_softc(dev); sc->sc_dev = dev; for (res = 0; res < sc->sc_rescnt; res++) { r = sc->sc_res + res; switch (r->r_type) { case SYS_RES_IRQ: /* XXX TODO */ break; case SYS_RES_DRQ: break; case SYS_RES_MEMORY: case SYS_RES_IOPORT: r->r_size = rman_get_size(r->r_d.res); r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666, "proto/%s/%02x.%s", device_get_desc(dev), r->r_rid, (r->r_type == SYS_RES_IOPORT) ? "io" : "mem"); r->r_u.cdev->si_drv1 = sc; r->r_u.cdev->si_drv2 = r; break; case PROTO_RES_PCICFG: r->r_size = 4096; r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666, "proto/%s/pcicfg", device_get_desc(dev)); r->r_u.cdev->si_drv1 = sc; r->r_u.cdev->si_drv2 = r; break; case PROTO_RES_BUSDMA: r->r_d.busdma = proto_busdma_attach(sc); r->r_size = 0; /* no read(2) nor write(2) */ r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0666, "proto/%s/busdma", device_get_desc(dev)); r->r_u.cdev->si_drv1 = sc; r->r_u.cdev->si_drv2 = r; break; } } return (0); } int proto_detach(device_t dev) { struct proto_softc *sc; struct proto_res *r; u_int res; sc = device_get_softc(dev); /* Don't detach if we have open device files. */ for (res = 0; res < sc->sc_rescnt; res++) { r = sc->sc_res + res; if (r->r_opened) return (EBUSY); } for (res = 0; res < sc->sc_rescnt; res++) { r = sc->sc_res + res; switch (r->r_type) { case SYS_RES_IRQ: /* XXX TODO */ bus_release_resource(dev, r->r_type, r->r_rid, r->r_d.res); break; case SYS_RES_DRQ: bus_release_resource(dev, r->r_type, r->r_rid, r->r_d.res); break; case SYS_RES_MEMORY: case SYS_RES_IOPORT: bus_release_resource(dev, r->r_type, r->r_rid, r->r_d.res); destroy_dev(r->r_u.cdev); break; case PROTO_RES_PCICFG: destroy_dev(r->r_u.cdev); break; case PROTO_RES_BUSDMA: proto_busdma_detach(sc, r->r_d.busdma); destroy_dev(r->r_u.cdev); break; } r->r_type = PROTO_RES_UNUSED; } sc->sc_rescnt = 0; return (0); } /* * Device functions */ static int proto_open(struct cdev *cdev, int oflags, int devtype, struct thread *td) { struct proto_res *r; r = cdev->si_drv2; if (!atomic_cmpset_acq_ptr(&r->r_opened, 0UL, (uintptr_t)td->td_proc)) return (EBUSY); return (0); } static int proto_close(struct cdev *cdev, int fflag, int devtype, struct thread *td) { struct proto_res *r; struct proto_softc *sc; sc = cdev->si_drv1; r = cdev->si_drv2; if (!atomic_cmpset_acq_ptr(&r->r_opened, (uintptr_t)td->td_proc, 0UL)) return (ENXIO); if (r->r_type == PROTO_RES_BUSDMA) proto_busdma_cleanup(sc, r->r_d.busdma); return (0); } static int proto_read(struct cdev *cdev, struct uio *uio, int ioflag) { union { uint8_t x1[8]; uint16_t x2[4]; uint32_t x4[2]; uint64_t x8[1]; } buf; struct proto_softc *sc; struct proto_res *r; device_t dev; off_t ofs; u_long width; int error; sc = cdev->si_drv1; dev = sc->sc_dev; r = cdev->si_drv2; width = uio->uio_resid; if (width < 1 || width > 8 || bitcount16(width) > 1) return (EIO); ofs = uio->uio_offset; if (ofs + width > r->r_size) return (EIO); switch (width) { case 1: buf.x1[0] = (r->r_type == PROTO_RES_PCICFG) ? pci_read_config(dev, ofs, 1) : bus_read_1(r->r_d.res, ofs); break; case 2: buf.x2[0] = (r->r_type == PROTO_RES_PCICFG) ? pci_read_config(dev, ofs, 2) : bus_read_2(r->r_d.res, ofs); break; case 4: buf.x4[0] = (r->r_type == PROTO_RES_PCICFG) ? pci_read_config(dev, ofs, 4) : bus_read_4(r->r_d.res, ofs); break; #ifndef __i386__ case 8: if (r->r_type == PROTO_RES_PCICFG) return (EINVAL); buf.x8[0] = bus_read_8(r->r_d.res, ofs); break; #endif default: return (EIO); } error = uiomove(&buf, width, uio); return (error); } static int proto_write(struct cdev *cdev, struct uio *uio, int ioflag) { union { uint8_t x1[8]; uint16_t x2[4]; uint32_t x4[2]; uint64_t x8[1]; } buf; struct proto_softc *sc; struct proto_res *r; device_t dev; off_t ofs; u_long width; int error; sc = cdev->si_drv1; dev = sc->sc_dev; r = cdev->si_drv2; width = uio->uio_resid; if (width < 1 || width > 8 || bitcount16(width) > 1) return (EIO); ofs = uio->uio_offset; if (ofs + width > r->r_size) return (EIO); error = uiomove(&buf, width, uio); if (error) return (error); switch (width) { case 1: if (r->r_type == PROTO_RES_PCICFG) pci_write_config(dev, ofs, buf.x1[0], 1); else bus_write_1(r->r_d.res, ofs, buf.x1[0]); break; case 2: if (r->r_type == PROTO_RES_PCICFG) pci_write_config(dev, ofs, buf.x2[0], 2); else bus_write_2(r->r_d.res, ofs, buf.x2[0]); break; case 4: if (r->r_type == PROTO_RES_PCICFG) pci_write_config(dev, ofs, buf.x4[0], 4); else bus_write_4(r->r_d.res, ofs, buf.x4[0]); break; #ifndef __i386__ case 8: if (r->r_type == PROTO_RES_PCICFG) return (EINVAL); bus_write_8(r->r_d.res, ofs, buf.x8[0]); break; #endif default: return (EIO); } return (0); } static int proto_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, struct thread *td) { struct proto_ioc_region *region; struct proto_ioc_busdma *busdma; struct proto_res *r; struct proto_softc *sc; int error; sc = cdev->si_drv1; r = cdev->si_drv2; error = 0; switch (cmd) { case PROTO_IOC_REGION: if (r->r_type == PROTO_RES_BUSDMA) { error = EINVAL; break; } region = (struct proto_ioc_region *)data; region->size = r->r_size; if (r->r_type == PROTO_RES_PCICFG) region->address = 0; else region->address = rman_get_start(r->r_d.res); break; case PROTO_IOC_BUSDMA: if (r->r_type != PROTO_RES_BUSDMA) { error = EINVAL; break; } busdma = (struct proto_ioc_busdma *)data; error = proto_busdma_ioctl(sc, r->r_d.busdma, busdma, td); break; default: error = ENOIOCTL; break; } return (error); } static int proto_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr, int prot, vm_memattr_t *memattr) { struct proto_res *r; if (offset & PAGE_MASK) return (EINVAL); if (prot & PROT_EXEC) return (EACCES); r = cdev->si_drv2; switch (r->r_type) { case SYS_RES_MEMORY: if (offset >= r->r_size) return (EINVAL); *paddr = rman_get_start(r->r_d.res) + offset; #ifndef __sparc64__ *memattr = VM_MEMATTR_UNCACHEABLE; #endif break; case PROTO_RES_BUSDMA: if (!proto_busdma_mmap_allowed(r->r_d.busdma, offset)) return (EINVAL); *paddr = offset; break; default: return (ENXIO); } return (0); }