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/spibus/spigen.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) 2015 Brian Fundakowski Feldman.  All rights reserved.
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer.
    9  * 2. Redistributions in binary form must reproduce the above copyright
   10  *    notice, this list of conditions and the following disclaimer in the
   11  *    documentation and/or other materials provided with the distribution.
   12  *
   13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   23  */
   24 
   25 #include <sys/cdefs.h>
   26 __FBSDID("$FreeBSD: releng/11.1/sys/dev/spibus/spigen.c 311342 2017-01-05 00:26:57Z gonzo $");
   27 
   28 #include <sys/param.h>
   29 #include <sys/systm.h>
   30 #include <sys/bus.h>
   31 #include <sys/conf.h>
   32 #include <sys/kernel.h>
   33 #include <sys/lock.h>
   34 #include <sys/malloc.h>
   35 #include <sys/mman.h>
   36 #include <sys/mutex.h>
   37 #include <sys/module.h>
   38 #include <sys/proc.h>
   39 #include <sys/rwlock.h>
   40 #include <sys/spigenio.h>
   41 #include <sys/sysctl.h>
   42 #include <sys/types.h>
   43  
   44 #include <vm/vm.h>
   45 #include <vm/vm_extern.h>
   46 #include <vm/vm_object.h>
   47 #include <vm/vm_page.h>
   48 #include <vm/vm_pager.h>
   49 
   50 #include <dev/spibus/spi.h>
   51 
   52 #include "spibus_if.h"
   53 
   54 struct spigen_softc {
   55         device_t sc_dev;
   56         struct cdev *sc_cdev;
   57         struct mtx sc_mtx;
   58         uint32_t sc_clock_speed;
   59         uint32_t sc_command_length_max; /* cannot change while mmapped */
   60         uint32_t sc_data_length_max;    /* cannot change while mmapped */
   61         vm_object_t sc_mmap_buffer;     /* command, then data */
   62         vm_offset_t sc_mmap_kvaddr;
   63         size_t sc_mmap_buffer_size;
   64         int sc_mmap_busy;
   65         int sc_debug;
   66 };
   67 
   68 static int
   69 spigen_probe(device_t dev)
   70 {
   71 
   72         device_set_desc(dev, "SPI Generic IO");
   73 
   74         return (BUS_PROBE_NOWILDCARD);
   75 }
   76 
   77 static int spigen_open(struct cdev *, int, int, struct thread *);
   78 static int spigen_ioctl(struct cdev *, u_long, caddr_t, int, struct thread *);
   79 static int spigen_close(struct cdev *, int, int, struct thread *);
   80 static d_mmap_single_t spigen_mmap_single;
   81 
   82 static struct cdevsw spigen_cdevsw = {
   83         .d_version =     D_VERSION,
   84         .d_name =        "spigen",
   85         .d_open =        spigen_open,
   86         .d_ioctl =       spigen_ioctl,
   87         .d_mmap_single = spigen_mmap_single,
   88         .d_close =       spigen_close
   89 };
   90 
   91 static int
   92 spigen_command_length_max_proc(SYSCTL_HANDLER_ARGS)
   93 {
   94         struct spigen_softc *sc = (struct spigen_softc *)arg1;
   95         uint32_t command_length_max;
   96         int error;
   97 
   98         mtx_lock(&sc->sc_mtx);
   99         command_length_max = sc->sc_command_length_max;
  100         mtx_unlock(&sc->sc_mtx);
  101         error = sysctl_handle_int(oidp, &command_length_max,
  102             sizeof(command_length_max), req);
  103         if (error == 0 && req->newptr != NULL) {
  104                 mtx_lock(&sc->sc_mtx);
  105                 if (sc->sc_mmap_buffer != NULL)
  106                         error = EBUSY;
  107                 else
  108                         sc->sc_command_length_max = command_length_max;
  109                 mtx_unlock(&sc->sc_mtx);
  110         }
  111         return (error);
  112 }
  113 
  114 static int
  115 spigen_data_length_max_proc(SYSCTL_HANDLER_ARGS)
  116 {
  117         struct spigen_softc *sc = (struct spigen_softc *)arg1;
  118         uint32_t data_length_max;
  119         int error;
  120 
  121         mtx_lock(&sc->sc_mtx);
  122         data_length_max = sc->sc_data_length_max;
  123         mtx_unlock(&sc->sc_mtx);
  124         error = sysctl_handle_int(oidp, &data_length_max,
  125             sizeof(data_length_max), req);
  126         if (error == 0 && req->newptr != NULL) {
  127                 mtx_lock(&sc->sc_mtx);
  128                 if (sc->sc_mmap_buffer != NULL)
  129                         error = EBUSY;
  130                 else
  131                         sc->sc_data_length_max = data_length_max;
  132                 mtx_unlock(&sc->sc_mtx);
  133         }
  134         return (error);
  135 }
  136 
  137 static void
  138 spigen_sysctl_init(struct spigen_softc *sc)
  139 {
  140         struct sysctl_ctx_list *ctx;
  141         struct sysctl_oid *tree_node;
  142         struct sysctl_oid_list *tree;
  143 
  144         /*
  145          * Add system sysctl tree/handlers.
  146          */
  147         ctx = device_get_sysctl_ctx(sc->sc_dev);
  148         tree_node = device_get_sysctl_tree(sc->sc_dev);
  149         tree = SYSCTL_CHILDREN(tree_node);
  150         SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "command_length_max",
  151             CTLFLAG_MPSAFE | CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc),
  152             spigen_command_length_max_proc, "IU", "SPI command header portion (octets)");
  153         SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "data_length_max",
  154             CTLFLAG_MPSAFE | CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc),
  155             spigen_data_length_max_proc, "IU", "SPI data trailer portion (octets)");
  156         SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "data", CTLFLAG_RW,
  157             &sc->sc_debug, 0, "debug flags");
  158 
  159 }
  160 
  161 static int
  162 spigen_attach(device_t dev)
  163 {
  164         struct spigen_softc *sc;
  165         const int unit = device_get_unit(dev);
  166 
  167         sc = device_get_softc(dev);
  168         sc->sc_dev = dev;
  169         sc->sc_cdev = make_dev(&spigen_cdevsw, unit,
  170             UID_ROOT, GID_OPERATOR, 0660, "spigen%d", unit);
  171         sc->sc_cdev->si_drv1 = dev;
  172         sc->sc_command_length_max = PAGE_SIZE;
  173         sc->sc_data_length_max = PAGE_SIZE;
  174         mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF);
  175         spigen_sysctl_init(sc);
  176 
  177         return (0);
  178 }
  179 
  180 static int 
  181 spigen_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
  182 {
  183 
  184         return (0);
  185 }
  186 
  187 static int
  188 spigen_transfer(struct cdev *cdev, struct spigen_transfer *st)
  189 {
  190         struct spi_command transfer = SPI_COMMAND_INITIALIZER;
  191         device_t dev = cdev->si_drv1;
  192         struct spigen_softc *sc = device_get_softc(dev);
  193         int error = 0;
  194 
  195         mtx_lock(&sc->sc_mtx);
  196         if (st->st_command.iov_len == 0)
  197                 error = EINVAL;
  198         else if (st->st_command.iov_len > sc->sc_command_length_max ||
  199             st->st_data.iov_len > sc->sc_data_length_max)
  200                 error = ENOMEM;
  201         mtx_unlock(&sc->sc_mtx);
  202         if (error)
  203                 return (error);
  204         
  205 #if 0
  206         device_printf(dev, "cmd %p %u data %p %u\n", st->st_command.iov_base,
  207             st->st_command.iov_len, st->st_data.iov_base, st->st_data.iov_len);
  208 #endif
  209         transfer.tx_cmd = transfer.rx_cmd = malloc(st->st_command.iov_len,
  210             M_DEVBUF, M_WAITOK);
  211         if (transfer.tx_cmd == NULL)
  212                 return (ENOMEM);
  213         if (st->st_data.iov_len > 0) {
  214                 transfer.tx_data = transfer.rx_data = malloc(st->st_data.iov_len,
  215                     M_DEVBUF, M_WAITOK);
  216                 if (transfer.tx_data == NULL) {
  217                         free(transfer.tx_cmd, M_DEVBUF);
  218                         return (ENOMEM);
  219                 }
  220         }
  221         else
  222                 transfer.tx_data = transfer.rx_data = NULL;
  223 
  224         error = copyin(st->st_command.iov_base, transfer.tx_cmd,
  225             transfer.tx_cmd_sz = transfer.rx_cmd_sz = st->st_command.iov_len);  
  226         if ((error == 0) && (st->st_data.iov_len > 0))
  227                 error = copyin(st->st_data.iov_base, transfer.tx_data,
  228                     transfer.tx_data_sz = transfer.rx_data_sz =
  229                                           st->st_data.iov_len); 
  230         if (error == 0)
  231                 error = SPIBUS_TRANSFER(device_get_parent(dev), dev, &transfer);
  232         if (error == 0) {
  233                 error = copyout(transfer.rx_cmd, st->st_command.iov_base,
  234                     transfer.rx_cmd_sz);
  235                 if ((error == 0) && (st->st_data.iov_len > 0))
  236                         error = copyout(transfer.rx_data, st->st_data.iov_base,
  237                             transfer.rx_data_sz);
  238         }
  239 
  240         free(transfer.tx_cmd, M_DEVBUF);
  241         free(transfer.tx_data, M_DEVBUF);
  242         return (error);
  243 }
  244 
  245 static int
  246 spigen_transfer_mmapped(struct cdev *cdev, struct spigen_transfer_mmapped *stm)
  247 {
  248         struct spi_command transfer = SPI_COMMAND_INITIALIZER;
  249         device_t dev = cdev->si_drv1;
  250         struct spigen_softc *sc = device_get_softc(dev);
  251         int error = 0;
  252 
  253         mtx_lock(&sc->sc_mtx);
  254         if (sc->sc_mmap_busy)
  255                 error = EBUSY;
  256         else if (stm->stm_command_length > sc->sc_command_length_max ||
  257             stm->stm_data_length > sc->sc_data_length_max)
  258                 error = E2BIG;
  259         else if (sc->sc_mmap_buffer == NULL)
  260                 error = EINVAL;
  261         else if (sc->sc_mmap_buffer_size <
  262             stm->stm_command_length + stm->stm_data_length)
  263                 error = ENOMEM;
  264         if (error == 0)
  265                 sc->sc_mmap_busy = 1;
  266         mtx_unlock(&sc->sc_mtx);
  267         if (error)
  268                 return (error);
  269         
  270         transfer.tx_cmd = transfer.rx_cmd = (void *)sc->sc_mmap_kvaddr;
  271         transfer.tx_cmd_sz = transfer.rx_cmd_sz = stm->stm_command_length;
  272         transfer.tx_data = transfer.rx_data =
  273             (void *)(sc->sc_mmap_kvaddr + stm->stm_command_length);
  274         transfer.tx_data_sz = transfer.rx_data_sz = stm->stm_data_length;
  275         error = SPIBUS_TRANSFER(device_get_parent(dev), dev, &transfer);
  276 
  277         mtx_lock(&sc->sc_mtx);
  278         KASSERT(sc->sc_mmap_busy, ("mmap no longer marked busy"));
  279         sc->sc_mmap_busy = 0;
  280         mtx_unlock(&sc->sc_mtx);
  281         return (error);
  282 }
  283 
  284 static int
  285 spigen_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
  286     struct thread *td)
  287 {
  288         device_t dev = cdev->si_drv1;
  289         struct spigen_softc *sc = device_get_softc(dev);
  290         int error;
  291 
  292         switch (cmd) {
  293         case SPIGENIOC_TRANSFER:
  294                 error = spigen_transfer(cdev, (struct spigen_transfer *)data);
  295                 break;
  296         case SPIGENIOC_TRANSFER_MMAPPED:
  297                 error = spigen_transfer_mmapped(cdev, (struct spigen_transfer_mmapped *)data);
  298                 break;
  299         case SPIGENIOC_GET_CLOCK_SPEED:
  300                 mtx_lock(&sc->sc_mtx);
  301                 *(uint32_t *)data = sc->sc_clock_speed;
  302                 /* XXX TODO: implement spibus ivar call */
  303                 mtx_unlock(&sc->sc_mtx);
  304                 error = 0;
  305                 break;
  306         case SPIGENIOC_SET_CLOCK_SPEED:
  307                 mtx_lock(&sc->sc_mtx);
  308                 sc->sc_clock_speed = *(uint32_t *)data;
  309                 mtx_unlock(&sc->sc_mtx);
  310                 error = 0;
  311                 break;
  312         default:
  313                 error = EOPNOTSUPP;
  314         }
  315         return (error);
  316 }
  317 
  318 static int
  319 spigen_mmap_single(struct cdev *cdev, vm_ooffset_t *offset,
  320     vm_size_t size, struct vm_object **object, int nprot)
  321 {
  322         device_t dev = cdev->si_drv1;
  323         struct spigen_softc *sc = device_get_softc(dev);
  324         vm_page_t *m;
  325         size_t n, pages;
  326 
  327         if (size == 0 ||
  328             (nprot & (PROT_EXEC | PROT_READ | PROT_WRITE))
  329             != (PROT_READ | PROT_WRITE))
  330                 return (EINVAL);
  331         size = roundup2(size, PAGE_SIZE);
  332         pages = size / PAGE_SIZE;
  333 
  334         mtx_lock(&sc->sc_mtx);
  335         if (sc->sc_mmap_buffer != NULL) {
  336                 mtx_unlock(&sc->sc_mtx);
  337                 return (EBUSY);
  338         } else if (size > sc->sc_command_length_max + sc->sc_data_length_max) {
  339                 mtx_unlock(&sc->sc_mtx);
  340                 return (E2BIG);
  341         }
  342         sc->sc_mmap_buffer_size = size;
  343         *offset = 0;
  344         sc->sc_mmap_buffer = *object = vm_pager_allocate(OBJT_PHYS, 0, size,
  345             nprot, *offset, curthread->td_ucred);
  346         m = malloc(sizeof(*m) * pages, M_TEMP, M_WAITOK);
  347         VM_OBJECT_WLOCK(*object);
  348         vm_object_reference_locked(*object); // kernel and userland both
  349         for (n = 0; n < pages; n++) {
  350                 m[n] = vm_page_grab(*object, n,
  351                     VM_ALLOC_NOBUSY | VM_ALLOC_ZERO | VM_ALLOC_WIRED);
  352                 m[n]->valid = VM_PAGE_BITS_ALL;
  353         }
  354         VM_OBJECT_WUNLOCK(*object);
  355         sc->sc_mmap_kvaddr = kva_alloc(size);
  356         pmap_qenter(sc->sc_mmap_kvaddr, m, pages);
  357         free(m, M_TEMP);
  358         mtx_unlock(&sc->sc_mtx);
  359 
  360         if (*object == NULL)
  361                  return (EINVAL);
  362         return (0);
  363 }
  364 
  365 static int 
  366 spigen_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
  367 {
  368         device_t dev = cdev->si_drv1;
  369         struct spigen_softc *sc = device_get_softc(dev);
  370 
  371         mtx_lock(&sc->sc_mtx);
  372         if (sc->sc_mmap_buffer != NULL) {
  373                 pmap_qremove(sc->sc_mmap_kvaddr,
  374                     sc->sc_mmap_buffer_size / PAGE_SIZE);
  375                 kva_free(sc->sc_mmap_kvaddr, sc->sc_mmap_buffer_size);
  376                 sc->sc_mmap_kvaddr = 0;
  377                 vm_object_deallocate(sc->sc_mmap_buffer);
  378                 sc->sc_mmap_buffer = NULL;
  379                 sc->sc_mmap_buffer_size = 0;
  380         }
  381         mtx_unlock(&sc->sc_mtx);
  382         return (0);
  383 }
  384 
  385 static int
  386 spigen_detach(device_t dev)
  387 {
  388 
  389         return (EIO);
  390 }
  391 
  392 static devclass_t spigen_devclass;
  393 
  394 static device_method_t spigen_methods[] = {
  395         /* Device interface */
  396         DEVMETHOD(device_probe,         spigen_probe),
  397         DEVMETHOD(device_attach,        spigen_attach),
  398         DEVMETHOD(device_detach,        spigen_detach),
  399 
  400         { 0, 0 }
  401 };
  402 
  403 static driver_t spigen_driver = {
  404         "spigen",
  405         spigen_methods,
  406         sizeof(struct spigen_softc),
  407 };
  408 
  409 DRIVER_MODULE(spigen, spibus, spigen_driver, spigen_devclass, 0, 0);

Cache object: ec0e387e9b10d9efea5a9409bf4577eb


[ 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.