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/mmc/mmcsd.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) 2006 Bernd Walter.  All rights reserved.
    3  * Copyright (c) 2006 M. Warner Losh.  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  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   24  *
   25  * Portions of this software may have been developed with reference to
   26  * the SD Simplified Specification.  The following disclaimer may apply:
   27  *
   28  * The following conditions apply to the release of the simplified
   29  * specification ("Simplified Specification") by the SD Card Association and
   30  * the SD Group. The Simplified Specification is a subset of the complete SD
   31  * Specification which is owned by the SD Card Association and the SD
   32  * Group. This Simplified Specification is provided on a non-confidential
   33  * basis subject to the disclaimers below. Any implementation of the
   34  * Simplified Specification may require a license from the SD Card
   35  * Association, SD Group, SD-3C LLC or other third parties.
   36  *
   37  * Disclaimers:
   38  *
   39  * The information contained in the Simplified Specification is presented only
   40  * as a standard specification for SD Cards and SD Host/Ancillary products and
   41  * is provided "AS-IS" without any representations or warranties of any
   42  * kind. No responsibility is assumed by the SD Group, SD-3C LLC or the SD
   43  * Card Association for any damages, any infringements of patents or other
   44  * right of the SD Group, SD-3C LLC, the SD Card Association or any third
   45  * parties, which may result from its use. No license is granted by
   46  * implication, estoppel or otherwise under any patent or other rights of the
   47  * SD Group, SD-3C LLC, the SD Card Association or any third party. Nothing
   48  * herein shall be construed as an obligation by the SD Group, the SD-3C LLC
   49  * or the SD Card Association to disclose or distribute any technical
   50  * information, know-how or other confidential information to any third party.
   51  */
   52 
   53 #include <sys/cdefs.h>
   54 __FBSDID("$FreeBSD: releng/8.3/sys/dev/mmc/mmcsd.c 188725 2009-02-17 19:17:25Z mav $");
   55 
   56 #include <sys/param.h>
   57 #include <sys/systm.h>
   58 #include <sys/bio.h>
   59 #include <sys/bus.h>
   60 #include <sys/conf.h>
   61 #include <sys/kernel.h>
   62 #include <sys/kthread.h>
   63 #include <sys/lock.h>
   64 #include <sys/malloc.h>
   65 #include <sys/module.h>
   66 #include <sys/mutex.h>
   67 #include <geom/geom_disk.h>
   68 
   69 #include <dev/mmc/mmcvar.h>
   70 #include <dev/mmc/mmcreg.h>
   71 
   72 #include "mmcbus_if.h"
   73 
   74 struct mmcsd_softc {
   75         device_t dev;
   76         struct mtx sc_mtx;
   77         struct disk *disk;
   78         struct proc *p;
   79         struct bio_queue_head bio_queue;
   80         daddr_t eblock, eend;   /* Range remaining after the last erase. */
   81         int running;
   82         int suspend;
   83 };
   84 
   85 /* bus entry points */
   86 static int mmcsd_probe(device_t dev);
   87 static int mmcsd_attach(device_t dev);
   88 static int mmcsd_detach(device_t dev);
   89 
   90 /* disk routines */
   91 static int mmcsd_open(struct disk *dp);
   92 static int mmcsd_close(struct disk *dp);
   93 static void mmcsd_strategy(struct bio *bp);
   94 static int mmcsd_dump(void *arg, void *virtual, vm_offset_t physical,
   95         off_t offset, size_t length);
   96 static void mmcsd_task(void *arg);
   97 
   98 static const char *mmcsd_card_name(device_t dev);
   99 static int mmcsd_bus_bit_width(device_t dev);
  100 
  101 #define MMCSD_LOCK(_sc)         mtx_lock(&(_sc)->sc_mtx)
  102 #define MMCSD_UNLOCK(_sc)       mtx_unlock(&(_sc)->sc_mtx)
  103 #define MMCSD_LOCK_INIT(_sc) \
  104         mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
  105             "mmcsd", MTX_DEF)
  106 #define MMCSD_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx);
  107 #define MMCSD_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED);
  108 #define MMCSD_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
  109 
  110 static int
  111 mmcsd_probe(device_t dev)
  112 {
  113 
  114         device_quiet(dev);
  115         device_set_desc(dev, "MMC/SD Memory Card");
  116         return (0);
  117 }
  118 
  119 static int
  120 mmcsd_attach(device_t dev)
  121 {
  122         struct mmcsd_softc *sc;
  123         struct disk *d;
  124         intmax_t mb;
  125         char unit;
  126 
  127         sc = device_get_softc(dev);
  128         sc->dev = dev;
  129         MMCSD_LOCK_INIT(sc);
  130 
  131         d = sc->disk = disk_alloc();
  132         d->d_open = mmcsd_open;
  133         d->d_close = mmcsd_close;
  134         d->d_strategy = mmcsd_strategy;
  135         d->d_dump = mmcsd_dump;
  136         d->d_name = "mmcsd";
  137         d->d_drv1 = sc;
  138         d->d_maxsize = 4*1024*1024;     /* Maximum defined SD card AU size. */
  139         d->d_sectorsize = mmc_get_sector_size(dev);
  140         d->d_mediasize = mmc_get_media_size(dev) * d->d_sectorsize;
  141         d->d_stripeoffset = 0;
  142         d->d_stripesize = mmc_get_erase_sector(dev) * d->d_sectorsize;
  143         d->d_unit = device_get_unit(dev);
  144         d->d_flags = DISKFLAG_CANDELETE;
  145         /*
  146          * Display in most natural units.  There's no cards < 1MB.
  147          * The SD standard goes to 2GiB, but the data format supports
  148          * up to 4GiB and some card makers push it up to this limit.
  149          * The SDHC standard only goes to 32GiB (the data format in
  150          * SDHC is good to 2TiB however, which isn't too ugly at
  151          * 2048GiBm, so we note it in passing here and don't add the
  152          * code to print TiB).
  153          */
  154         mb = d->d_mediasize >> 20;      /* 1MiB == 1 << 20 */
  155         unit = 'M';
  156         if (mb >= 10240) {              /* 1GiB = 1024 MiB */
  157                 unit = 'G';
  158                 mb /= 1024;
  159         }
  160         device_printf(dev, "%ju%cB <%s Memory Card>%s at %s %dMHz/%dbit\n",
  161             mb, unit, mmcsd_card_name(dev),
  162             mmc_get_read_only(dev) ? " (read-only)" : "",
  163             device_get_nameunit(device_get_parent(dev)),
  164             mmc_get_tran_speed(dev) / 1000000, mmcsd_bus_bit_width(dev));
  165         disk_create(d, DISK_VERSION);
  166         bioq_init(&sc->bio_queue);
  167 
  168         sc->running = 1;
  169         sc->suspend = 0;
  170         sc->eblock = sc->eend = 0;
  171         kproc_create(&mmcsd_task, sc, &sc->p, 0, 0, "task: mmc/sd card");
  172 
  173         return (0);
  174 }
  175 
  176 static int
  177 mmcsd_detach(device_t dev)
  178 {
  179         struct mmcsd_softc *sc = device_get_softc(dev);
  180 
  181         MMCSD_LOCK(sc);
  182         sc->suspend = 0;
  183         if (sc->running > 0) {
  184                 /* kill thread */
  185                 sc->running = 0;
  186                 wakeup(sc);
  187                 /* wait for thread to finish. */
  188                 while (sc->running != -1)
  189                         msleep(sc, &sc->sc_mtx, 0, "detach", 0);
  190         }
  191         MMCSD_UNLOCK(sc);
  192 
  193         /* Flush the request queue. */
  194         bioq_flush(&sc->bio_queue, NULL, ENXIO);
  195         /* kill disk */
  196         disk_destroy(sc->disk);
  197 
  198         MMCSD_LOCK_DESTROY(sc);
  199 
  200         return (0);
  201 }
  202 
  203 static int
  204 mmcsd_suspend(device_t dev)
  205 {
  206         struct mmcsd_softc *sc = device_get_softc(dev);
  207 
  208         MMCSD_LOCK(sc);
  209         sc->suspend = 1;
  210         if (sc->running > 0) {
  211                 /* kill thread */
  212                 sc->running = 0;
  213                 wakeup(sc);
  214                 /* wait for thread to finish. */
  215                 while (sc->running != -1)
  216                         msleep(sc, &sc->sc_mtx, 0, "detach", 0);
  217         }
  218         MMCSD_UNLOCK(sc);
  219         return (0);
  220 }
  221 
  222 static int
  223 mmcsd_resume(device_t dev)
  224 {
  225         struct mmcsd_softc *sc = device_get_softc(dev);
  226 
  227         MMCSD_LOCK(sc);
  228         sc->suspend = 0;
  229         if (sc->running <= 0) {
  230                 sc->running = 1;
  231                 MMCSD_UNLOCK(sc);
  232                 kproc_create(&mmcsd_task, sc, &sc->p, 0, 0, "task: mmc/sd card");
  233         } else
  234                 MMCSD_UNLOCK(sc);
  235         return (0);
  236 }
  237 
  238 static int
  239 mmcsd_open(struct disk *dp)
  240 {
  241         return (0);
  242 }
  243 
  244 static int
  245 mmcsd_close(struct disk *dp)
  246 {
  247         return (0);
  248 }
  249 
  250 static void
  251 mmcsd_strategy(struct bio *bp)
  252 {
  253         struct mmcsd_softc *sc;
  254 
  255         sc = (struct mmcsd_softc *)bp->bio_disk->d_drv1;
  256         MMCSD_LOCK(sc);
  257         if (sc->running > 0 || sc->suspend > 0) {
  258                 bioq_disksort(&sc->bio_queue, bp);
  259                 MMCSD_UNLOCK(sc);
  260                 wakeup(sc);
  261         } else {
  262                 MMCSD_UNLOCK(sc);
  263                 biofinish(bp, NULL, ENXIO);
  264         }
  265 }
  266 
  267 static daddr_t
  268 mmcsd_rw(struct mmcsd_softc *sc, struct bio *bp)
  269 {
  270         daddr_t block, end;
  271         struct mmc_command cmd;
  272         struct mmc_command stop;
  273         struct mmc_request req;
  274         struct mmc_data data;
  275         device_t dev = sc->dev;
  276         int sz = sc->disk->d_sectorsize;
  277 
  278         block = bp->bio_pblkno;
  279         end = bp->bio_pblkno + (bp->bio_bcount / sz);
  280         while (block < end) {
  281                 char *vaddr = bp->bio_data +
  282                     (block - bp->bio_pblkno) * sz;
  283                 int numblocks = min(end - block, mmc_get_max_data(dev));
  284                 memset(&req, 0, sizeof(req));
  285                 memset(&cmd, 0, sizeof(cmd));
  286                 memset(&stop, 0, sizeof(stop));
  287                 req.cmd = &cmd;
  288                 cmd.data = &data;
  289                 if (bp->bio_cmd == BIO_READ) {
  290                         if (numblocks > 1)
  291                                 cmd.opcode = MMC_READ_MULTIPLE_BLOCK;
  292                         else
  293                                 cmd.opcode = MMC_READ_SINGLE_BLOCK;
  294                 } else {
  295                         if (numblocks > 1)
  296                                 cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK;
  297                         else
  298                                 cmd.opcode = MMC_WRITE_BLOCK;
  299                 }
  300                 cmd.arg = block;
  301                 if (!mmc_get_high_cap(dev))
  302                         cmd.arg <<= 9;
  303                 cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
  304                 data.data = vaddr;
  305                 data.mrq = &req;
  306                 if (bp->bio_cmd == BIO_READ)
  307                         data.flags = MMC_DATA_READ;
  308                 else
  309                         data.flags = MMC_DATA_WRITE;
  310                 data.len = numblocks * sz;
  311                 if (numblocks > 1) {
  312                         data.flags |= MMC_DATA_MULTI;
  313                         stop.opcode = MMC_STOP_TRANSMISSION;
  314                         stop.arg = 0;
  315                         stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
  316                         req.stop = &stop;
  317                 }
  318 //              printf("Len %d  %lld-%lld flags %#x sz %d\n",
  319 //                  (int)data.len, (long long)block, (long long)end, data.flags, sz);
  320                 MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
  321                     &req);
  322                 if (req.cmd->error != MMC_ERR_NONE)
  323                         break;
  324                 block += numblocks;
  325         }
  326         return (block);
  327 }
  328 
  329 static daddr_t
  330 mmcsd_delete(struct mmcsd_softc *sc, struct bio *bp)
  331 {
  332         daddr_t block, end, start, stop;
  333         struct mmc_command cmd;
  334         struct mmc_request req;
  335         device_t dev = sc->dev;
  336         int sz = sc->disk->d_sectorsize;
  337         int erase_sector;
  338 
  339         block = bp->bio_pblkno;
  340         end = bp->bio_pblkno + (bp->bio_bcount / sz);
  341         /* Coalesce with part remaining from previous request. */
  342         if (block > sc->eblock && block <= sc->eend)
  343                 block = sc->eblock;
  344         if (end >= sc->eblock && end < sc->eend)
  345                 end = sc->eend;
  346         /* Safe round to the erase sector boundaries. */
  347         erase_sector = mmc_get_erase_sector(dev);
  348         start = block + erase_sector - 1;        /* Round up. */
  349         start -= start % erase_sector;
  350         stop = end;                             /* Round down. */
  351         stop -= end % erase_sector;      
  352         /* We can't erase area smaller then sector, store it for later. */
  353         if (start >= stop) {
  354                 sc->eblock = block;
  355                 sc->eend = end;
  356                 return (end);
  357         }
  358 
  359         /* Set erase start position. */
  360         memset(&req, 0, sizeof(req));
  361         memset(&cmd, 0, sizeof(cmd));
  362         req.cmd = &cmd;
  363         if (mmc_get_card_type(dev) == mode_sd)
  364                 cmd.opcode = SD_ERASE_WR_BLK_START;
  365         else
  366                 cmd.opcode = MMC_ERASE_GROUP_START;
  367         cmd.arg = start;
  368         if (!mmc_get_high_cap(dev))
  369                 cmd.arg <<= 9;
  370         cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
  371         MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
  372             &req);
  373         if (req.cmd->error != MMC_ERR_NONE) {
  374             printf("erase err1: %d\n", req.cmd->error);
  375             return (block);
  376         }
  377         /* Set erase stop position. */
  378         memset(&req, 0, sizeof(req));
  379         memset(&cmd, 0, sizeof(cmd));
  380         req.cmd = &cmd;
  381         if (mmc_get_card_type(dev) == mode_sd)
  382                 cmd.opcode = SD_ERASE_WR_BLK_END;
  383         else
  384                 cmd.opcode = MMC_ERASE_GROUP_END;
  385         cmd.arg = stop;
  386         if (!mmc_get_high_cap(dev))
  387                 cmd.arg <<= 9;
  388         cmd.arg--;
  389         cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
  390         MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
  391             &req);
  392         if (req.cmd->error != MMC_ERR_NONE) {
  393             printf("erase err2: %d\n", req.cmd->error);
  394             return (block);
  395         }
  396         /* Erase range. */
  397         memset(&req, 0, sizeof(req));
  398         memset(&cmd, 0, sizeof(cmd));
  399         req.cmd = &cmd;
  400         cmd.opcode = MMC_ERASE;
  401         cmd.arg = 0;
  402         cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
  403         MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev,
  404             &req);
  405         if (req.cmd->error != MMC_ERR_NONE) {
  406             printf("erase err3 %d\n", req.cmd->error);
  407             return (block);
  408         }
  409         /* Store one of remaining parts for the next call. */
  410         if (bp->bio_pblkno >= sc->eblock || block == start) {
  411                 sc->eblock = stop;      /* Predict next forward. */
  412                 sc->eend = end;
  413         } else {
  414                 sc->eblock = block;     /* Predict next backward. */
  415                 sc->eend = start;
  416         }
  417         return (end);
  418 }
  419 
  420 static int
  421 mmcsd_dump(void *arg, void *virtual, vm_offset_t physical,
  422         off_t offset, size_t length)
  423 {
  424         struct disk *disk = arg;
  425         struct mmcsd_softc *sc = (struct mmcsd_softc *)disk->d_drv1;
  426         device_t dev = sc->dev;
  427         struct bio bp;
  428         daddr_t block, end;
  429 
  430         /* length zero is special and really means flush buffers to media */
  431         if (!length)
  432                 return (0);
  433 
  434         bzero(&bp, sizeof(struct bio));
  435         bp.bio_disk = disk;
  436         bp.bio_pblkno = offset / disk->d_sectorsize;
  437         bp.bio_bcount = length;
  438         bp.bio_data = virtual;
  439         bp.bio_cmd = BIO_WRITE;
  440         end = bp.bio_pblkno + bp.bio_bcount / sc->disk->d_sectorsize;
  441         MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
  442         block = mmcsd_rw(sc, &bp);
  443         MMCBUS_RELEASE_BUS(device_get_parent(dev), dev);
  444         return ((end < block) ? EIO : 0);
  445 }
  446 
  447 static void
  448 mmcsd_task(void *arg)
  449 {
  450         struct mmcsd_softc *sc = (struct mmcsd_softc*)arg;
  451         struct bio *bp;
  452         int sz;
  453         daddr_t block, end;
  454         device_t dev;
  455 
  456         dev = sc->dev;
  457         while (1) {
  458                 MMCSD_LOCK(sc);
  459                 do {
  460                         if (sc->running == 0)
  461                                 goto out;
  462                         bp = bioq_takefirst(&sc->bio_queue);
  463                         if (bp == NULL)
  464                                 msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0);
  465                 } while (bp == NULL);
  466                 MMCSD_UNLOCK(sc);
  467                 if (bp->bio_cmd != BIO_READ && mmc_get_read_only(dev)) {
  468                         bp->bio_error = EROFS;
  469                         bp->bio_resid = bp->bio_bcount;
  470                         bp->bio_flags |= BIO_ERROR;
  471                         biodone(bp);
  472                         continue;
  473                 }
  474                 MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev);
  475                 sz = sc->disk->d_sectorsize;
  476                 block = bp->bio_pblkno;
  477                 end = bp->bio_pblkno + (bp->bio_bcount / sz);
  478                 if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) {
  479                         /* Access to the remaining erase block obsoletes it. */
  480                         if (block < sc->eend && end > sc->eblock)
  481                                 sc->eblock = sc->eend = 0;
  482                         block = mmcsd_rw(sc, bp);
  483                 } else if (bp->bio_cmd == BIO_DELETE) {
  484                         block = mmcsd_delete(sc, bp);
  485                 }
  486                 MMCBUS_RELEASE_BUS(device_get_parent(dev), dev);
  487                 if (block < end) {
  488                         bp->bio_error = EIO;
  489                         bp->bio_resid = (end - block) * sz;
  490                         bp->bio_flags |= BIO_ERROR;
  491                 }
  492                 biodone(bp);
  493         }
  494 out:
  495         /* tell parent we're done */
  496         sc->running = -1;
  497         MMCSD_UNLOCK(sc);
  498         wakeup(sc);
  499 
  500         kproc_exit(0);
  501 }
  502 
  503 static const char *
  504 mmcsd_card_name(device_t dev)
  505 {
  506         if (mmc_get_card_type(dev) == mode_mmc)
  507                 return ("MMC");
  508         if (mmc_get_high_cap(dev))
  509                 return ("SDHC");
  510         return ("SD");
  511 }
  512 
  513 static int
  514 mmcsd_bus_bit_width(device_t dev)
  515 {
  516         if (mmc_get_bus_width(dev) == bus_width_1)
  517                 return (1);
  518         if (mmc_get_bus_width(dev) == bus_width_4)
  519                 return (4);
  520         return (8);
  521 }
  522 
  523 static device_method_t mmcsd_methods[] = {
  524         DEVMETHOD(device_probe, mmcsd_probe),
  525         DEVMETHOD(device_attach, mmcsd_attach),
  526         DEVMETHOD(device_detach, mmcsd_detach),
  527         DEVMETHOD(device_suspend, mmcsd_suspend),
  528         DEVMETHOD(device_resume, mmcsd_resume),
  529         {0, 0},
  530 };
  531 
  532 static driver_t mmcsd_driver = {
  533         "mmcsd",
  534         mmcsd_methods,
  535         sizeof(struct mmcsd_softc),
  536 };
  537 static devclass_t mmcsd_devclass;
  538 
  539 DRIVER_MODULE(mmcsd, mmc, mmcsd_driver, mmcsd_devclass, 0, 0);

Cache object: 6752bfb8e4adee756cec38cfcf4b2387


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