Changeset 254


Ignore:
Timestamp:
Aug 27, 2013, 5:41:51 PM (11 years ago)
Author:
alain
Message:

Improving support for Chained Buffer DMA in drivers.c file.

Location:
soft/giet_vm
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • soft/giet_vm/display/main.c

    r249 r254  
    11#include <stdio.h>
    22
    3 #define NBLOCS 32   // (128 * 128) / 512
     3#define NBLOCKS 32   // number of blocks per image (128 * 128) / 512
    44
    5 unsigned char buf[128*128] __attribute__((aligned(64)));
     5unsigned char buf0[128*128] __attribute__((aligned(64)));
     6unsigned char buf1[128*128] __attribute__((aligned(64)));
    67
    78__attribute__((constructor)) int main(void)
     
    1011    unsigned int  base = 0;
    1112
    12     while (base < 10 * NBLOCS)
     13    /* initialise CDMA transfert (double buffer) */
     14
     15    giet_tty_printf( "*** Starting task DISPLAY_CMA on processor %d at cycle %d\n",
     16                     giet_procid(), giet_proctime() );
     17    giet_tty_printf( "  - buf0 address (LSB) = %x\n", (unsigned int)buf0 );
     18    giet_tty_printf( "  - buf1 address (LSB) = %x\n", (unsigned int)buf1 );
     19
     20    x = giet_fb_cma_init( buf0,       // buf0 base address
     21                          buf1,       // buf1 base address
     22                          128*128 );  // buffer size (bytes)
     23    giet_assert( (x==0), "echec giet_fb_cdma_init" );
     24
     25    while (base < 10 * NBLOCKS)
    1326    {
    14         giet_tty_printf("\n *** image %d *** at date = %d \n",
    15                 base / NBLOCS, giet_proctime());
     27        /* Phase 1 : transfer one image from disk to buf0 and display  */
    1628
    17         /* Phase 1 : lecture image sur le disque et transfert vers buf */
    18         x = giet_ioc_read(base, buf, NBLOCS);
    19         if ( x )
    20         {
    21             giet_tty_printf("echec giet_ioc_read = %d at date : %d\n", x , giet_proctime() );
    22             giet_exit();
    23         }
     29        giet_tty_printf( "\n *** image %d *** at date = %d \n",
     30                         (base/NBLOCKS), giet_proctime() );
     31
     32        x = giet_ioc_read( base,            // lba on disk
     33                           buf0,            // user buffer address
     34                           NBLOCKS );       // number of blocks
     35        giet_assert( (x==0) , "echec giet_ioc_read for buf0" );
     36
    2437        x = giet_ioc_completed();
    25         if ( x )
    26         {
    27             giet_tty_printf("echec giet_ioc_completed = %d at date : %d\n", x, giet_proctime() );
    28             giet_exit();
    29         }
    30         giet_tty_printf("ioc_read  completed at date = %d \n", giet_proctime());
     38        giet_assert( (x==0) , "echec giet_ioc_completed for buf0" );
    3139
    32         // Phase 2 : transfert de buf vers le frame buffer par dma
    33         x = giet_fb_sync_write(0, buf, 128 * 128);
    34         if ( x )
    35         {
    36             giet_tty_printf("echec giet_fb_write = %d at date : %d\n", x, giet_proctime() );
    37             giet_exit();
    38         }
     40        giet_tty_printf( "ioc_read buf0 completed at date = %d \n", giet_proctime() );
    3941
    40 /*
    41         giet_tty_printf("fb_write ok at date : %d\n", giet_proctime() );
     42        x = giet_fb_cma_write( 0 );         // buf0
     43        giet_assert( (x==0) , "echec giet_fb_cma_write for buf0" );
     44       
     45        base = base + NBLOCKS;
    4246
    43         x = giet_fb_completed();
    44         if ( x )
    45         {
    46             giet_tty_printf("echec giet_fb_completed = %d at date : %d\n", x, giet_proctime() );
    47             giet_exit();
    48         }
    49 */
    50         giet_tty_printf("display completed at date = %d \n", giet_proctime());
     47        /* Phase 2 : transfer one image from disk to buf1 and display  */
    5148
    52         base = base + NBLOCS;
    53     }
     49        giet_tty_printf( "\n *** image %d *** at date = %d \n",
     50                         (base/NBLOCKS), giet_proctime() );
    5451
     52        x = giet_ioc_read( base,            // lba on disk
     53                           buf1,            // user buffer address
     54                           NBLOCKS );       // number of locks
     55        giet_assert( (x==0) , "echec giet_ioc_read for buf1");
     56
     57        x = giet_ioc_completed();
     58        giet_assert( (x==0) , "echec giet_ioc_completed for buf1");
     59
     60        giet_tty_printf( "ioc_read buf1 completed at date = %d \n", giet_proctime() );
     61
     62        x = giet_fb_cma_write( 1 );         // buf1
     63        giet_assert( (x==0) , "echec giet_fb_cma_write for buf1" );
     64       
     65        base = base + NBLOCKS;
     66    } // end while
     67
     68    giet_fb_cma_stop();
    5569    giet_exit();
    56 
    5770    return 0;
    5871}
  • soft/giet_vm/giet_config.h

    r253 r254  
    1515/* Debug parameters */
    1616
    17 #define BOOT_DEBUG_MAPPING       1                      /* trace map_info checking */
     17#define BOOT_DEBUG_MAPPING       0                      /* trace map_info checking */
    1818#define BOOT_DEBUG_PT                0                  /* trace page tables initialisation */
    1919#define BOOT_DEBUG_VOBJS             0                  /* trace vobjs initialisation */
  • soft/giet_vm/hello/main.c

    r207 r254  
    55        char                byte;
    66    unsigned int        proc = giet_procid();
    7     unsigned int*       illegal = (unsigned int*)0xFFFFFFF0;
    87
     8    giet_tty_printf("Starting task HELLO on processor %d at cycle %d\n",
     9                    giet_procid(), giet_proctime() );
     10 
    911        while (1)
    1012        {
     
    1214        giet_tty_getc((void*)&byte);
    1315        if ( byte == 'q' ) giet_exit();
    14         if ( byte == 'x' ) *illegal = 1;
    1516        }
    1617} // end main
  • soft/giet_vm/pgcd/main.c

    r191 r254  
    77    unsigned int opy;
    88
    9     giet_tty_printf(" Interactive PGCD \n");
     9    giet_tty_printf("Starting task PGCD on processor %d at cycle %d\n",
     10                   giet_procid(), giet_proctime());
    1011
    1112    while (1)
     
    2021        if( (opx == 0) || (opy == 0) )
    2122        {
    22             giet_tty_printf("operands must be larger than 0\n");
     23           giet_tty_printf("operands must be larger than 0\n");
    2324        }
    2425        else
  • soft/giet_vm/sys/drivers.c

    r253 r254  
    715715    _ioc_iommu_npages = (user_vpn_max - user_vpn_min) + 1;
    716716
    717     // invalidate local data cache in case of memory write
    718     if (to_mem) _dcache_buf_invalidate((void *) user_vaddr, length);
     717    // If no IOB, invalidate L1 cache in case of memory write
     718    if ( to_mem && (USE_IOB == 0) ) _dcache_buf_invalidate((void *) user_vaddr, length);
    719719
    720720#if GIET_DEBUG_IOC_DRIVER
     
    742742#endif
    743743
    744     // Invalidate L2 cache if IO Bridge is used
     744    // If IOB, invalidate L2 cache in case of memory write
    745745    if ( to_mem && USE_IOB ) _memc_inval( buf_paddr, length );
    746746   
     
    12671267// the VciChbufDma component (non replicated) to transfer a flow of images from
    12681268// an user space chained buffer (two buffers) to the frame buffer.
    1269 // A CMA channel is allocated to the task requesting it in the mapping_info,
     1269// A CMA channel must be allocated to the task requesting it in the mapping_info,
    12701270// and stored in the task context.
    12711271//////////////////////////////////////////////////////////////////////////////////
     
    13581358// - The SRC chbuf descriptor contain two slots (two user buffers)
    13591359// - The DST chbuf descriptor contains only one slot (frame buffer)
     1360
    13601361typedef struct fb_cma_channel_s
    13611362{
    1362     paddr_t buf0;   // physical address + status for user buffer 0
    1363     paddr_t buf1;   // physical address + status for user buffer 1
    1364     paddr_t fbf;    // physical address + status for frame buffer
     1363    paddr_t       buf0;     // physical address + status for user buffer 0
     1364    paddr_t       buf1;     // physical address + status for user buffer 1
     1365    paddr_t       fbf;      // physical address + status for frame buffer
     1366    unsigned int  length;   // buffer length (number of bytes)
     1367    unsigned int  padding;  // unused (just to hahe channel size = 32 bytes)
    13651368} fb_cma_channel_t;
    13661369
    1367 in_unckdata volatile fb_cma_channel_t _fb_cma_channel[NB_CMA_CHANNELS];
     1370in_unckdata volatile fb_cma_channel_t _fb_cma_channel[NB_CMA_CHANNELS] __attribute__((aligned(64)));
     1371in_unckdata volatile paddr_t          _fb_cma_desc_paddr[NB_CMA_CHANNELS];
    13681372
    13691373//////////////////////////////////////////////////////////////////////////////////
    13701374// _fb_cma_init()
    1371 // This function does two things:
    1372 // 1) Initialises the SRC chbuf descriptor (two buffers), and the DST
    1373 //    chbuf descriptor (one single frame buffer), after translating 
    1374 //    virtual addresses to physical addresses, and checking access rights.
    1375 // 2) Starts the CMA hardware channel, that will permanently try to display
    1376 //    images as soon as the SRC buffers are filled.
     1375// This function uses the _fb_cma_channel[] and _fb_cma_desc_paddr[] arrays,
     1376// that are both indexed by the channel index.
     1377// where each entry contains one fb_cma_channel structure (defining two
     1378// SRC and DST chbuf descriptors), and does four things:
     1379//
     1380// 1) computes the physical addresses for the two source user buffers, for
     1381//    the destination frame buffer. It initialises the channel descriptor
     1382//    _fb_cma_channel[i], containing the SRC chbuf descriptor (two buffers),
     1383//    the DST chbuf descriptor (one single frame buffer), and the buffer length.
     1384//
     1385// 2) computes the physical address for the channel descriptor and register it
     1386//    in the _fb_cma_desc_paddr[i].
     1387//   
     1388// 3) makes a SYNC request to L2 cache for channel descriptor, because the
     1389//    channel descriptor is directly accessed in XRAM by the CMA component.
     1390//
     1391// 4) Starts the CMA hardware channel, that will poll the channel descriptor
     1392//    to fransfer an user buffer to the frame buffer as soon as the source
     1393//    user buffer is marked valid.
     1394//
    13771395// Arguments are:
    13781396// - vbase0 : virtual base address of the first user buffer.
     
    13871405#if NB_CMA_CHANNELS > 0
    13881406
    1389     unsigned int  channel_id;       // CMA channel index
    1390     unsigned int  user_ptab;        // page table virtual address
    1391     unsigned int  ko;               // unsuccessfull V2P translation
    1392     unsigned int  vpn;              // virtual page number
    1393     unsigned int  flags;            // protection flags
    1394     unsigned int  ppn;              // physical page number
    1395     paddr_t       src_chbuf_pbase;  // physical address for SRC chbuf descriptor
    1396     paddr_t       dst_chbuf_pbase;  // physical address for SRC chbuf descriptor
     1407    unsigned int  channel_id;          // CMA channel index
     1408    unsigned int  user_ptab;           // page table virtual address
     1409    unsigned int  ko;                  // unsuccessfull V2P translation
     1410    unsigned int  vaddr;               // virtual address
     1411    unsigned int  flags;               // protection flags
     1412    unsigned int  ppn;                 // physical page number
     1413    paddr_t       channel_pbase;       // physical address of channel descriptor
    13971414
    13981415    // get CMA channel index
     
    14061423    }
    14071424
    1408     // check user buffer virtual addresses and length alignment
     1425    // checking size for channel descriptor
     1426    if ( sizeof(fb_cma_channel_t) != 32 )
     1427    {
     1428        _get_lock(&_tty_put_lock);
     1429        _puts("\n[GIET ERROR] in _fb_cma_init() : bad fb_cma_channel size\n");
     1430        _release_lock(&_tty_put_lock);
     1431        return 1;
     1432    }
     1433
     1434    // checking channel descriptor alignment (32 bytes)
     1435    if ( (unsigned int)(&_fb_cma_channel[channel_id]) & 0x1F )
     1436    {
     1437        _get_lock(&_tty_put_lock);
     1438        _puts("\n[GIET ERROR] in _fb_cma_init() : bad fb_cma_channel alignment\n");
     1439        _release_lock(&_tty_put_lock);
     1440        return 1;
     1441    }
     1442
     1443    // checking user buffer virtual addresses and length alignment
    14091444    if ( ((unsigned int)vbase0 & 0x3) || ((unsigned int)vbase1 & 0x3) || (length & 0x3) )
    14101445    {
     
    14181453    user_ptab = _get_context_slot(CTX_PTAB_ID);
    14191454
    1420     // get frame buffer virtual address
    1421 
    14221455    // compute and register frame buffer physical address
    1423     vpn = ((unsigned int)&seg_fbf_base) >> 12;
    1424     ko = _v2p_translate( (page_table_t*) user_ptab,
    1425                          vpn,
     1456    vaddr = ((unsigned int)&seg_fbf_base);
     1457    ko    = _v2p_translate( (page_table_t*) user_ptab,
     1458                         (vaddr >> 12),
    14261459                         &ppn,
    14271460                         &flags );
     
    14331466        return 1;
    14341467    }
    1435     _fb_cma_channel[channel_id].fbf = ((paddr_t)ppn << 12) | (vpn & 0x00000FFF);
     1468    _fb_cma_channel[channel_id].fbf = ((paddr_t)ppn << 12) | (vaddr & 0x00000FFF);
    14361469
    14371470    // Compute and register first user buffer physical address
    1438     vpn = (unsigned int)vbase0 >> 12;
     1471    vaddr = (unsigned int)vbase0;
    14391472    ko = _v2p_translate( (page_table_t*) user_ptab,
    1440                          vpn,
     1473                         (vaddr >> 12),
    14411474                         &ppn,
    14421475                         &flags );
     
    14551488        return 1;
    14561489    }
    1457     _fb_cma_channel[channel_id].buf0 = ((paddr_t)ppn << 12) | (vpn & 0x00000FFF);
     1490    _fb_cma_channel[channel_id].buf0 = ((paddr_t)ppn << 12) | (vaddr & 0x00000FFF);
    14581491
    14591492    // Compute and register second user buffer physical address
    1460     vpn = (unsigned int)vbase1 >> 12;
     1493    vaddr = (unsigned int)vbase1;
    14611494    ko = _v2p_translate( (page_table_t*) user_ptab,
    1462                          vpn,
     1495                         (vaddr >> 12),
    14631496                         &ppn,
    14641497                         &flags );
     
    14771510        return 1;
    14781511    }
    1479     _fb_cma_channel[channel_id].buf1 = ((paddr_t)ppn << 12) | (vpn & 0x00000FFF);
    1480 
    1481     // Compute physical adress of the SRC chbuf descriptor
    1482     vpn = ((unsigned int)(&_fb_cma_channel[channel_id].buf0)) >> 12;
     1512    _fb_cma_channel[channel_id].buf1 = ((paddr_t)ppn << 12) | (vaddr & 0x00000FFF);
     1513
     1514    // register buffer length in channel descriptor
     1515    _fb_cma_channel[channel_id].length = length;
     1516
     1517    // Compute and register physical adress of the channel descriptor
     1518    vaddr = (unsigned int)(&_fb_cma_channel[channel_id]);
    14831519    ko = _v2p_translate( (page_table_t*) user_ptab,
    1484                          vpn,
     1520                         (vaddr >> 12),
    14851521                         &ppn,
    14861522                         &flags );
     
    14881524    {
    14891525        _get_lock(&_tty_put_lock);
    1490         _puts("\n[GIET ERROR] in _fb_cma_init() : SRC chbuf descriptor unmapped\n");
     1526        _puts("\n[GIET ERROR] in _fb_cma_init() : channel descriptor unmapped\n");
    14911527        _release_lock(&_tty_put_lock);
    14921528        return 1;
    14931529    }
    1494     src_chbuf_pbase = (((paddr_t)ppn) << 12) | (vpn & 0x00000FFF);
    1495 
    1496     // Compute physical adress of the DST chbuf descriptor
    1497     vpn = ((unsigned int)(&_fb_cma_channel[channel_id].fbf)) >> 12;
    1498     ko = _v2p_translate( (page_table_t*) user_ptab,
    1499                          vpn,
    1500                          &ppn,
    1501                          &flags );
    1502     if (ko)
    1503     {
    1504         _get_lock(&_tty_put_lock);
    1505         _puts("\n[GIET ERROR] in _fb_cma_init() : DST chbuf descriptor unmapped\n");
    1506         _release_lock(&_tty_put_lock);
    1507         return 1;
    1508     }
    1509     dst_chbuf_pbase = (((paddr_t)ppn) << 12) | (vpn & 0x00000FFF);
     1530    channel_pbase = (((paddr_t)ppn) << 12) | (vaddr & 0x00000FFF);
     1531    _fb_cma_desc_paddr[channel_id] = channel_pbase;
     1532
     1533#if GIET_DEBUG_CMA_DRIVER
     1534_puts("\n");
     1535_puts("- fbf       pbase = ");
     1536_putl( _fb_cma_channel[channel_id].fbf );
     1537_puts("\n");
     1538_puts("- buf0      pbase = ");
     1539_putl( _fb_cma_channel[channel_id].buf0 );
     1540_puts("\n");
     1541_puts("- buf1      pbase = ");
     1542_putl( _fb_cma_channel[channel_id].buf1 );
     1543_puts("\n");
     1544_puts("- channel   pbase = ");
     1545_putl( channel_pbase );
     1546_puts("\n");
     1547#endif
     1548
     1549    // SYNC request for channel descriptor
     1550    _memc_sync( channel_pbase, 32 );
    15101551
    15111552    // CMA channel activation
     
    15131554    unsigned int  offset     = channel_id * CHBUF_CHANNEL_SPAN;
    15141555
    1515     cma_vbase[offset + CHBUF_SRC_DESC]  = (unsigned int)(src_chbuf_pbase & 0xFFFFFFFF);
    1516     cma_vbase[offset + CHBUF_SRC_EXT]   = (unsigned int)(src_chbuf_pbase >> 32);
     1556    cma_vbase[offset + CHBUF_SRC_DESC]  = (unsigned int)(channel_pbase & 0xFFFFFFFF);
     1557    cma_vbase[offset + CHBUF_SRC_EXT]   = (unsigned int)(channel_pbase >> 32);
    15171558    cma_vbase[offset + CHBUF_SRC_NBUFS] = 2;
    1518     cma_vbase[offset + CHBUF_DST_DESC]  = (unsigned int)(dst_chbuf_pbase & 0xFFFFFFFF);
    1519     cma_vbase[offset + CHBUF_DST_EXT]   = (unsigned int)(dst_chbuf_pbase >> 32);
     1559    cma_vbase[offset + CHBUF_DST_DESC]  = (unsigned int)(channel_pbase & 0xFFFFFFFF) + 16;
     1560    cma_vbase[offset + CHBUF_DST_EXT]   = (unsigned int)(channel_pbase >> 32);
    15201561    cma_vbase[offset + CHBUF_DST_NBUFS] = 1;
    15211562    cma_vbase[offset + CHBUF_BUF_SIZE]  = length;
     
    15361577//////////////////////////////////////////////////////////////////////////////////
    15371578// _fb_cma_write()
    1538 // This function updates the status of the SRC and DST chbuf descriptors
    1539 // to allows the CMA component to transfer another buffer.
     1579// This function makes a SYNC request for the source user buffer.
     1580// Then it updates the status of the SRC and DST chbuf descriptors, to allow
     1581// the CMA component to transfer the source user buffer buffer to the destination
     1582// frame buffer, and makes a SYNC request for the channel descriptor.
     1583//
    15401584// - buffer_id : user buffer index (0 => buf0 / not 0 => buf1)
    15411585// Returns 0 if success, > 0 if error
     
    15451589#if NB_CMA_CHANNELS > 0
    15461590
     1591    paddr_t         buf_paddr;
     1592    unsigned int    buf_length;
     1593
    15471594    // get CMA channel index
    15481595    unsigned int channel_id = _get_context_slot(CTX_CMA_ID);
    1549     if ( channel_id >= NB_CMA_CHANNELS )
    1550     {
    1551         _get_lock(&_tty_put_lock);
    1552         _puts("\n[GIET ERROR] in _fb_cma_write() : CMA channel index too large\n");
    1553         _release_lock(&_tty_put_lock);
    1554         return 1;
    1555     }
     1596
     1597    // SYNC request for the source user buffer
     1598    if ( buffer_id == 0 )  buf_paddr = _fb_cma_channel[channel_id].buf0;
     1599    else                   buf_paddr = _fb_cma_channel[channel_id].buf1;
     1600    buf_length = _fb_cma_channel[channel_id].length;
     1601    _memc_sync( buf_paddr, buf_length );
     1602
    15561603    // set SRC full
    15571604    if ( buffer_id == 0 )
    1558     _fb_cma_channel[channel_id].buf0 = _fb_cma_channel[channel_id].buf0
    1559                                        | 0x8000000000000000ULL;
     1605    _fb_cma_channel[channel_id].buf0 = buf_paddr | 0x8000000000000000ULL;
    15601606    else
    1561     _fb_cma_channel[channel_id].buf1 = _fb_cma_channel[channel_id].buf1
    1562                                        | 0x8000000000000000ULL;
     1607    _fb_cma_channel[channel_id].buf1 = buf_paddr | 0x8000000000000000ULL;
     1608
    15631609    // set DST empty
    15641610    _fb_cma_channel[channel_id].fbf  = _fb_cma_channel[channel_id].fbf
    15651611                                       & 0x7FFFFFFFFFFFFFFFULL;
     1612
     1613    // SYNC request for the channel descriptor
     1614    buf_paddr  = _fb_cma_desc_paddr[channel_id];
     1615    buf_length = 32;
     1616    _memc_sync( buf_paddr, buf_length );
     1617
    15661618    return 0;
    15671619
     
    15861638    // get CMA channel allocated
    15871639    unsigned int channel_id = _get_context_slot(CTX_CMA_ID);
    1588     if ( channel_id >= NB_CMA_CHANNELS )
    1589     {
    1590         _get_lock(&_tty_put_lock);
    1591         _puts("\n[GIET ERROR] in _fb_cma_stop() : CMA channel index too large\n");
    1592         _release_lock(&_tty_put_lock);
    1593         return 1;
    1594     }
     1640
    15951641    // CMA channel desactivation
    15961642    unsigned int* cma_vbase = (unsigned int *)&seg_cma_base;
     
    17041750///////////////////////////////////////////////////////////////////////////////////
    17051751// _memc_inval()
    1706 // This function invalidate all cache lines covering a memory buffer defined
     1752// This function invalidates all cache lines covering a memory buffer defined
    17071753// by the physical base address, and the length.
    17081754// The buffer address MSB are used to compute the cluster index.
     
    17241770    mmc_address[MEMC_BUF_LENGTH] = buf_length;
    17251771    mmc_address[MEMC_CMD_TYPE]   = MEMC_CMD_INVAL;
     1772
     1773    // release the lock protecting MEMC
     1774    mmc_address[MEMC_LOCK] = 0;
     1775}
     1776///////////////////////////////////////////////////////////////////////////////////
     1777// _memc_sync()
     1778// This function copies to external RAM all cache lines covering a memory buffer
     1779// defined by the physical base address, and the length, if they are dirty.
     1780// The buffer address MSB are used to compute the cluster index.
     1781///////////////////////////////////////////////////////////////////////////////////
     1782void _memc_sync( paddr_t      buf_paddr,
     1783                 unsigned int buf_length )
     1784{
     1785    unsigned int cluster_id    = (unsigned int)((buf_paddr>>32)/(256/NB_CLUSTERS));
     1786
     1787    unsigned int * mmc_address = (unsigned int *) ((unsigned int)&seg_mmc_base +
     1788                                 (cluster_id * (unsigned int)&vseg_cluster_increment));
     1789
     1790    // get the lock protecting exclusive access to MEMC
     1791    while ( mmc_address[MEMC_LOCK] ) { asm volatile("nop"); }
     1792
     1793    // write inval arguments
     1794    mmc_address[MEMC_ADDR_LO]    = (unsigned int)buf_paddr;
     1795    mmc_address[MEMC_ADDR_HI]    = (unsigned int)(buf_paddr>>32);
     1796    mmc_address[MEMC_BUF_LENGTH] = buf_length;
     1797    mmc_address[MEMC_CMD_TYPE]   = MEMC_CMD_SYNC;
    17261798
    17271799    // release the lock protecting MEMC
Note: See TracChangeset for help on using the changeset viewer.