The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/proto/proto_core.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: f8bad1a12a93edcb2dd84dd8fa61fc34


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.