LCOV - code coverage report
Current view: top level - dev/usb - usb_mem.c (source / functions) Hit Total Coverage
Test: 6.4 Lines: 0 88 0.0 %
Date: 2018-10-19 03:25:38 Functions: 0 5 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*      $OpenBSD: usb_mem.c,v 1.30 2018/03/07 04:39:54 jmatthew Exp $ */
       2             : /*      $NetBSD: usb_mem.c,v 1.26 2003/02/01 06:23:40 thorpej Exp $     */
       3             : 
       4             : /*
       5             :  * Copyright (c) 1998 The NetBSD Foundation, Inc.
       6             :  * All rights reserved.
       7             :  *
       8             :  * This code is derived from software contributed to The NetBSD Foundation
       9             :  * by Lennart Augustsson (lennart@augustsson.net) at
      10             :  * Carlstedt Research & Technology.
      11             :  *
      12             :  * Redistribution and use in source and binary forms, with or without
      13             :  * modification, are permitted provided that the following conditions
      14             :  * are met:
      15             :  * 1. Redistributions of source code must retain the above copyright
      16             :  *    notice, this list of conditions and the following disclaimer.
      17             :  * 2. Redistributions in binary form must reproduce the above copyright
      18             :  *    notice, this list of conditions and the following disclaimer in the
      19             :  *    documentation and/or other materials provided with the distribution.
      20             :  *
      21             :  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
      22             :  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
      23             :  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
      24             :  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
      25             :  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      26             :  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
      27             :  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
      28             :  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
      29             :  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      30             :  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      31             :  * POSSIBILITY OF SUCH DAMAGE.
      32             :  */
      33             : 
      34             : /*
      35             :  * USB DMA memory allocation.
      36             :  * We need to allocate a lot of small (many 8 byte, some larger)
      37             :  * memory blocks that can be used for DMA.  Using the bus_dma
      38             :  * routines directly would incur large overheads in space and time.
      39             :  */
      40             : 
      41             : #include <sys/param.h>
      42             : #include <sys/systm.h>
      43             : #include <sys/kernel.h>
      44             : #include <sys/malloc.h>
      45             : #include <sys/queue.h>
      46             : #include <sys/timeout.h>
      47             : #include <sys/device.h>           /* for usbdivar.h */
      48             : #include <machine/bus.h>
      49             : 
      50             : #include <dev/usb/usb.h>
      51             : #include <dev/usb/usbdi.h>
      52             : #include <dev/usb/usbdivar.h>     /* just for struct usb_dma */
      53             : #include <dev/usb/usb_mem.h>
      54             : 
      55             : #ifdef USB_DEBUG
      56             : #define DPRINTF(x)      do { if (usbdebug) printf x; } while (0)
      57             : #define DPRINTFN(n,x)   do { if (usbdebug>(n)) printf x; } while (0)
      58             : extern int usbdebug;
      59             : #else
      60             : #define DPRINTF(x)
      61             : #define DPRINTFN(n,x)
      62             : #endif
      63             : 
      64             : #define USB_MEM_SMALL 64
      65             : #define USB_MEM_CHUNKS 64
      66             : #define USB_MEM_BLOCK (USB_MEM_SMALL * USB_MEM_CHUNKS)
      67             : 
      68             : struct usb_frag_dma {
      69             :         struct usb_dma_block *block;
      70             :         u_int offs;
      71             :         LIST_ENTRY(usb_frag_dma) next;
      72             : };
      73             : 
      74             : usbd_status     usb_block_allocmem(bus_dma_tag_t, size_t, size_t,
      75             :                     struct usb_dma_block **);
      76             : void            usb_block_freemem(struct usb_dma_block *);
      77             : 
      78             : LIST_HEAD(, usb_dma_block) usb_blk_freelist =
      79             :         LIST_HEAD_INITIALIZER(usb_blk_freelist);
      80             : int usb_blk_nfree = 0;
      81             : /* XXX should have different free list for different tags (for speed) */
      82             : LIST_HEAD(, usb_frag_dma) usb_frag_freelist =
      83             :         LIST_HEAD_INITIALIZER(usb_frag_freelist);
      84             : 
      85             : usbd_status
      86           0 : usb_block_allocmem(bus_dma_tag_t tag, size_t size, size_t align,
      87             :     struct usb_dma_block **dmap)
      88             : {
      89             :         int error;
      90             :         struct usb_dma_block *p;
      91             :         int s;
      92             : 
      93             :         DPRINTFN(5, ("usb_block_allocmem: size=%lu align=%lu\n",
      94             :                      (u_long)size, (u_long)align));
      95             : 
      96           0 :         s = splusb();
      97             :         /* First check the free list. */
      98           0 :         for (p = LIST_FIRST(&usb_blk_freelist); p; p = LIST_NEXT(p, next)) {
      99           0 :                 if (p->tag == tag && p->size >= size && p->align >= align) {
     100           0 :                         LIST_REMOVE(p, next);
     101           0 :                         usb_blk_nfree--;
     102           0 :                         splx(s);
     103           0 :                         *dmap = p;
     104             :                         DPRINTFN(6,("usb_block_allocmem: free list size=%lu\n",
     105             :                                     (u_long)p->size));
     106           0 :                         return (USBD_NORMAL_COMPLETION);
     107             :                 }
     108             :         }
     109           0 :         splx(s);
     110             : 
     111           0 :         assertwaitok();
     112             : 
     113             :         DPRINTFN(6, ("usb_block_allocmem: no free\n"));
     114           0 :         p = malloc(sizeof *p, M_USB, M_NOWAIT);
     115           0 :         if (p == NULL)
     116           0 :                 return (USBD_NOMEM);
     117             : 
     118           0 :         p->tag = tag;
     119           0 :         p->size = size;
     120           0 :         p->align = align;
     121           0 :         error = bus_dmamem_alloc(tag, p->size, align, 0,
     122             :                                  p->segs, nitems(p->segs),
     123             :                                  &p->nsegs, BUS_DMA_NOWAIT);
     124           0 :         if (error)
     125             :                 goto free0;
     126             : 
     127           0 :         error = bus_dmamem_map(tag, p->segs, p->nsegs, p->size,
     128             :                                &p->kaddr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
     129           0 :         if (error)
     130             :                 goto free1;
     131             : 
     132           0 :         error = bus_dmamap_create(tag, p->size, 1, p->size,
     133             :                                   0, BUS_DMA_NOWAIT, &p->map);
     134           0 :         if (error)
     135             :                 goto unmap;
     136             : 
     137           0 :         error = bus_dmamap_load(tag, p->map, p->kaddr, p->size, NULL,
     138             :                                 BUS_DMA_NOWAIT);
     139           0 :         if (error)
     140             :                 goto destroy;
     141             : 
     142           0 :         *dmap = p;
     143           0 :         return (USBD_NORMAL_COMPLETION);
     144             : 
     145             : destroy:
     146           0 :         bus_dmamap_destroy(tag, p->map);
     147             : unmap:
     148           0 :         bus_dmamem_unmap(tag, p->kaddr, p->size);
     149             : free1:
     150           0 :         bus_dmamem_free(tag, p->segs, p->nsegs);
     151             : free0:
     152           0 :         free(p, M_USB, sizeof *p);
     153           0 :         return (USBD_NOMEM);
     154           0 : }
     155             : 
     156             : #if 0
     157             : void
     158             : usb_block_real_freemem(struct usb_dma_block *p)
     159             : {
     160             :         assertwaitok();
     161             :         bus_dmamap_unload(p->tag, p->map);
     162             :         bus_dmamap_destroy(p->tag, p->map);
     163             :         bus_dmamem_unmap(p->tag, p->kaddr, p->size);
     164             :         bus_dmamem_free(p->tag, p->segs, p->nsegs);
     165             :         free(p, M_USB, sizeof *p);
     166             : }
     167             : #endif
     168             : 
     169             : /*
     170             :  * Do not free the memory unconditionally since we might be called
     171             :  * from an interrupt context and that is BAD.
     172             :  * XXX when should we really free?
     173             :  */
     174             : void
     175           0 : usb_block_freemem(struct usb_dma_block *p)
     176             : {
     177             :         int s;
     178             : 
     179             :         DPRINTFN(6, ("usb_block_freemem: size=%lu\n", (u_long)p->size));
     180           0 :         s = splusb();
     181           0 :         LIST_INSERT_HEAD(&usb_blk_freelist, p, next);
     182           0 :         usb_blk_nfree++;
     183           0 :         splx(s);
     184           0 : }
     185             : 
     186             : usbd_status
     187           0 : usb_allocmem(struct usbd_bus *bus, size_t size, size_t align, struct usb_dma *p)
     188             : {
     189           0 :         bus_dma_tag_t tag = bus->dmatag;
     190             :         usbd_status err;
     191             :         struct usb_frag_dma *f;
     192           0 :         struct usb_dma_block *b;
     193             :         int i;
     194             :         int s;
     195             : 
     196             :         /* If the request is large then just use a full block. */
     197           0 :         if (size > USB_MEM_SMALL || align > USB_MEM_SMALL) {
     198             :                 DPRINTFN(1, ("usb_allocmem: large alloc %d\n", (int)size));
     199           0 :                 size = (size + USB_MEM_BLOCK - 1) & ~(USB_MEM_BLOCK - 1);
     200           0 :                 err = usb_block_allocmem(tag, size, align, &p->block);
     201           0 :                 if (!err) {
     202           0 :                         p->block->frags = NULL;
     203           0 :                         p->offs = 0;
     204           0 :                 }
     205           0 :                 return (err);
     206             :         }
     207             : 
     208           0 :         s = splusb();
     209             :         /* Check for free fragments. */
     210           0 :         for (f = LIST_FIRST(&usb_frag_freelist); f; f = LIST_NEXT(f, next))
     211           0 :                 if (f->block->tag == tag)
     212             :                         break;
     213           0 :         if (f == NULL) {
     214             :                 DPRINTFN(1, ("usb_allocmem: adding fragments\n"));
     215           0 :                 err = usb_block_allocmem(tag, USB_MEM_BLOCK, USB_MEM_SMALL,&b);
     216           0 :                 if (err) {
     217           0 :                         splx(s);
     218           0 :                         return (err);
     219             :                 }
     220           0 :                 b->frags = mallocarray(USB_MEM_CHUNKS, sizeof(*f), M_USB,
     221             :                     M_NOWAIT);
     222           0 :                 if (b->frags == NULL) {
     223           0 :                         splx(s);
     224           0 :                         usb_block_freemem(b);
     225           0 :                         return (USBD_NOMEM);
     226             :                 }
     227           0 :                 for (i = 0; i < USB_MEM_CHUNKS; i++) {
     228           0 :                         f = &b->frags[i];
     229           0 :                         f->block = b;
     230           0 :                         f->offs = USB_MEM_SMALL * i;
     231           0 :                         LIST_INSERT_HEAD(&usb_frag_freelist, f, next);
     232             :                 }
     233           0 :                 f = LIST_FIRST(&usb_frag_freelist);
     234           0 :         }
     235           0 :         p->block = f->block;
     236           0 :         p->offs = f->offs;
     237           0 :         LIST_REMOVE(f, next);
     238           0 :         splx(s);
     239             :         DPRINTFN(5, ("usb_allocmem: use frag=%p size=%d\n", f, (int)size));
     240           0 :         return (USBD_NORMAL_COMPLETION);
     241           0 : }
     242             : 
     243             : void
     244           0 : usb_freemem(struct usbd_bus *bus, struct usb_dma *p)
     245             : {
     246             :         struct usb_frag_dma *f;
     247             :         int s;
     248             : 
     249           0 :         if (p->block->frags == NULL) {
     250             :                 DPRINTFN(1, ("usb_freemem: large free\n"));
     251           0 :                 usb_block_freemem(p->block);
     252           0 :                 return;
     253             :         }
     254           0 :         s = splusb();
     255           0 :         f = &p->block->frags[p->offs / USB_MEM_SMALL];
     256           0 :         LIST_INSERT_HEAD(&usb_frag_freelist, f, next);
     257           0 :         splx(s);
     258             :         DPRINTFN(5, ("usb_freemem: frag=%p block=%p\n", f, f->block));
     259           0 : }
     260             : 
     261             : void
     262           0 : usb_syncmem(struct usb_dma *p, bus_addr_t offset, bus_size_t len, int ops)
     263             : {
     264           0 :         bus_dmamap_sync(p->block->tag, p->block->map, p->offs + offset,
     265             :             len, ops);
     266           0 : }

Generated by: LCOV version 1.13