/****************************************************************************/
/*  Arthur Digital Solutions, 2013                                          */
/****************************************************************************/

#include <chips/LPC23xx/vic.h>
#include <chips/LPC23xx/scb.h>
#include <chips/LPC23xx/pcb.h>
#include <chips/LPC23xx/mci.h>
#include <chips/LPC23xx/dma.h>
#include <chips/LPC23xx/tim.h>
#include <exec/BX.h>
#include <stdio.h>
#include <uart.h>
#include <string.h>
#include <system.h>
#include <misc/dlink.h>
#include <monitor.h>
#include <sd.h>

/****************************************************************************/
/*  Definitions                                                             */
/****************************************************************************/

// card present p0[17]
// card WP      p0[18]

#define PRES_PORT       0
#define PRES_MASK       (1<<17)

#define WP_PORT         0
#define WP_MASK         (1<<18)

#define STKSIZE     200

#define MAXSBUF     9

int     bufbeg[MAXSBUF * (512/sizeof(int))] IN_AHB1;


/*
 *  Ezen bitek az SD statusban hibat jelentenek
 */
#define SDSTAT_ERR          0xfdf98000
#define SDSTAT_READYFORDATA 0x100


/****************************************************************************/
/*  Global data                                                             */
/****************************************************************************/

extern  BX_PROC     mainpcb;

/****************************************************************************/
/*  Local data                                                              */
/****************************************************************************/

static  int     cardstat = SD_NOCARD;
static  int     cards2 = ERR_NOCARD;
static  int     chgcnt;             // card change count (device ID...)
static  int     flushtmr;           // cache flush timer

static  int     cardprot;           // card is write protected
static  int     hcsd;
static  int     cardblocks;         // card size in blocks

static  int     rca;                // relative card address
static  int     stk[STKSIZE];       // process stack
static  BX_PROC sdpcb;              // process descriptor
static  BX_LOCK lock;               // MUTEX
static  BX_SEMA irqsema;            // MCI irq
static  DLLIST  free, cache;        // lists of free and used sector buffers

static  volatile int    irqstat;

typedef struct {
    DLLINK  link;
    int     sector;
    char    *buffer;
    char    dty;
    char    locked;
} sbuf_t;

static  sbuf_t  sbuff[MAXSBUF];
    
/****************************************************************************/
/*  Local prototypes                                                        */
/****************************************************************************/

static  int     readsector(int sector, char *buffer);
static  int     writeback(sbuf_t *p);
static  void    sdproc(void *x);
static  int     initcard(void);
static  void    initsbuff(void);
static  int     flush_cache(void);
static  int     sd_acmd(int code, int arg, int longresp);
static  int     sd_cmd(int code, int arg, int longresp);
static  int     _sd_cmd(int code, int arg, int longresp);
static  char    *statof(int s);
static  void    sdirq(void);

/****************************************************************************/
/*  Functions                                                               */
/****************************************************************************/

void    SdInit(int reset)
{
    if(reset){
        BX_LockObtain(&lock, -1);
        if(!cardprot)
            flush_cache();
        
        lpc_vic->intenclr = 1 << VIC_SDC;
        lpc_mci->mask0 = 0;
        cardstat = SD_RESET;
        lpc_mci->power = MCI_POWER_CTRL_PWOF;
        BX_ProcProtRaise(BX_PROT_IRQD);
        lpc_pcb->pinmode[1] = (lpc_pcb->pinmode[1] & ~PCB_MODE_MSK(20)) | PCB_MODE_SET(20, PCB_MODE_FLT);
        BX_ProcProtLower(BX_PROT_NORM);
        waitms(100);
        BX_LockRelease(&lock);
        return;
    }
    
    MonitorNewStack("sd", stk, STKSIZE);
    BX_LockInit(&lock, BX_POLICY_FPIN, 0);
    BX_ProcSpawn(&sdpcb, sdproc, NULL, stk + STKSIZE, PRIO_SD, BX_PROT_NORM );
}

int     SdCardSize(void)
{
    return cardblocks;
}

int     SdCardIsProtected(void)
{
    return cardprot;
}

/*
 *  Return: ERR_NOCARD vagy 1..999999
 */
int     SdStatus(void)
{
    return cards2;
}


int     SdRead(int sector, void *to, int off, int len)
{
int     e, *p;
sbuf_t  *sb;

    BX_LockObtain(&lock, -1);

    if(cardstat != SD_READY){
        BX_LockRelease(&lock);
        return ERR_NOCARD;
    }

    if((sb = (sbuf_t *)DL_SeeHead(&cache)) != NULL){
        do{
            if(sb->sector == sector)
                break;
        
        }while((sb = (sbuf_t *)DL_Next((&(sb->link)))) != NULL);
    }
    
    if(sb){
        /*
         *  A cache-ben van a szektor, nem onnan adjuk vissza 
         */
        DL_Remove(&cache, &(sb->link));
        DL_PutHead(&cache, &(sb->link));
        memcpy(to, sb->buffer + off, len);
        BX_LockRelease(&lock);
        return 0;
    }
    
    if(off == 0 && len == 512 && (char *)to >= AHB1_BEG && (char *)to < AHB1_BEG + AHB1_LEN){
        e = readsector(sector, to);
        BX_LockRelease(&lock);
        return e;
    }
    
    /*
     *  Nincs a cache-ben a kerdeses szektor.
     *  Eloszor is keresunk egy ures cache entry-t
     */
    if((sb = (sbuf_t *)DL_GetTail(&free)) == NULL){
        if((sb = (sbuf_t *)DL_GetTail(&cache)) == NULL){
            BX_LockRelease(&lock);
            return ERR_INTERNAL;
        }
        if(sb->dty && ((e = writeback(sb)) < 0)){
            DL_PutTail(&cache, &(sb->link));
            BX_LockRelease(&lock);
            return e;
        }
    }
    
    if((e = readsector(sector, sb->buffer)) != 0){
        DL_PutTail(&free, &(sb->link));
        BX_LockRelease(&lock);
        return e;
    }
    sb->sector = sector;
    sb->locked = 0;
    sb->dty = 0;
    
    DL_PutHead(&cache, &(sb->link));
    
    memcpy(to, sb->buffer + off, len);
    BX_LockRelease(&lock);
    return 0;
}


int     SdWrite(int sector, void *from, int off, int len, int flags)
{
int     e, *p;
sbuf_t  *sb;

    BX_LockObtain(&lock, -1);

    if(cardstat != SD_READY){
        BX_LockRelease(&lock);
        return ERR_NOCARD;
    }
    
    if(cardprot){
        BX_LockRelease(&lock);
        return ERR_WRITEPROT;
    }

    if((sb = (sbuf_t *)DL_SeeHead(&cache)) != NULL){
        do{
            if(sb->sector == sector)
                break;
        
        }while((sb = (sbuf_t *)DL_Next((&(sb->link)))) != NULL);
    }
    
    if(sb){
        /*
         *  Mar a cache-ben van a szektor
         */
        
        DL_Remove(&cache, &(sb->link));
        DL_PutHead(&cache, &(sb->link));
    }
    else{
        /*
         *  Nincs a cache-ben a kerdeses szektor.
         *  Eloszor is keresunk egy ures cache entry-t
         */
        if((sb = (sbuf_t *)DL_GetTail(&free)) == NULL){
            /*
             *  Ha nincs ures, akkor leszedjuk a legregebbit a cache-bol
             */
            if((sb = (sbuf_t *)DL_GetTail(&cache)) == NULL){
                BX_LockRelease(&lock);
                return ERR_INTERNAL;
            }
            if(sb->dty && ((e = writeback(sb)) < 0)){
                DL_PutTail(&cache, &(sb->link));
                BX_LockRelease(&lock);
                return e;
            }
        }
        
        if(len != 512){
            if(flags & WF_NOREAD)
                memset(sb->buffer, 0, 512);
            else if((e = readsector(sector, sb->buffer)) != 0){
                DL_PutTail(&free, &(sb->link));
                BX_LockRelease(&lock);
                return e;
            }
        }
        sb->sector = sector;
        sb->locked = 0;
        sb->dty = 0;
        DL_PutHead(&cache, &(sb->link));
    }
    
    memcpy(sb->buffer + off, from, len);
    sb->dty |= !!len;
    flushtmr = 10;      // 10*100ms = 1 sec
    
    if((flags & WF_FLUSH) && ((e = writeback(sb)) < 0)){
        BX_LockRelease(&lock);
        return e;
    }
    BX_LockRelease(&lock);
    return 0;
}


int     SdFlush(void)
{
int     e;

    BX_LockObtain(&lock, -1);

    if(cardstat != SD_READY){
        BX_LockRelease(&lock);
        return ERR_NOCARD;
    }
    
    if(cardprot){
        BX_LockRelease(&lock);
        return ERR_WRITEPROT;
    }
    e = flush_cache();
    BX_LockRelease(&lock);
    return e;
    
}

/****************************************************************************/
/*  Local functions                                                         */
/****************************************************************************/

static  int     flush_cache(void)
{
sbuf_t  *sb;
int     e;

    e = 0;
    
    if((sb = (sbuf_t *)DL_SeeHead(&cache)) != NULL){
        do{
            if(sb->dty && ((e = writeback(sb)) != 0))
                break;
        
        }while((sb = (sbuf_t *)DL_Next((&(sb->link)))) != NULL);
    }
    if(e == 0)
        flushtmr = 0;
    
    return e;
}

/*
 *  Fizikai szektor olvasas
 */
static  int     readsector(int sector, char *buffer)
{
int     e, *data, r;
unsigned    tb;

    lpc_dma->conf = 1;

//  printf("readsector: sector: %d, buffer: #%d\n", sector, (buffer - ((char *)bufbeg)) / 512);

    lpc_dma->chan[0].srcaddr = (void *)lpc_mci->fifo;
    lpc_dma->chan[0].dstaddr = buffer;
    lpc_dma->chan[0].lli = NULL;
    lpc_dma->chan[0].ctrl = DMA_CTRL_SB_SIZE(DMA_SIZE_8) | 
                            DMA_CTRL_DB_SIZE(DMA_SIZE_1) |
                            DMA_CTRL_SWIDTH(DMA_WIDTH_WORD) |
                            DMA_CTRL_DWIDTH(DMA_WIDTH_WORD) |
                            DMA_CTRL_DI;
                           
    lpc_dma->chan[0].conf = DMA_CONF_E |
                            DMA_CONF_SRC_PER(DMA_PER_SDMMC) |
                            DMA_CONF_FLOW_CTRL(DMA_FLOW_PER_MEM_SRC);
    
    lpc_mci->clr = 0x7ff;

    lpc_mci->datatimer = 1800000;           // 100ms at 18MHz MCICLK
    lpc_mci->datalength = 512;
    lpc_mci->datactrl = MCI_DATACTRL_ENABLE | 
                        MCI_DATACTRL_DIRECTION |
                        MCI_DATACTRL_DMAENABLE |
                        MCI_DATACTRL_BLOCKSIZE(9);  // 2^9=512
                        
    if((e = sd_cmd(17, (hcsd) ? sector : sector*512, 0)) < 0)
        return e;
    
    if(lpc_mci->response[0] & SDSTAT_ERR)
        return ERR_SDSTAT;
    
    irqstat = 0;
    lpc_mci->mask0 = MCI_DATACRCFAIL | MCI_DATATIMEOUT | MCI_TXUNDERRUN |
                     MCI_RXOVERRUN   | MCI_DATAEND     | MCI_STARTBITERR |
                     MCI_DATABLOCKEND;
    BX_SemaGet(&irqsema, 1, -1);
    
    if(irqstat & MCI_DATABLOCKEND){
        /*
         *  Megvarjuk, amig a DMA is vegez. Ezt nem interruptosan csinaljuk, 
         *  mert csak par byte-ot kell itt megvarni, lassabb lenne 
         *  a context switch...
         */
        tb = lpc_tim2->tc;
        while(lpc_dma->chan[0].conf & DMA_CONF_E){
            if(lpc_tim2->tc - tb > US2CLK(100000)){
                lpc_dma->chan[0].conf = 0;
                lpc_mci->datactrl = 0;
                return ERR_DMA;
            }
        }
        return 0;
    }
    else{
        lpc_dma->chan[0].conf = 0;
        lpc_mci->datactrl = 0;
        return ERR_SDDATA;
    }
}

/*
 *  Fizikailag kiir egy szektort a kartyara
 */
static  int     writeback(sbuf_t *p)
{
int     e, n;

    lpc_dma->conf = 1;

    if((e = sd_cmd(24, (hcsd) ? p->sector : p->sector * 512, 0)) < 0)
        return e;
    
    if(lpc_mci->response[0] & SDSTAT_ERR)
        return ERR_SDSTAT;
    
    lpc_dma->chan[0].srcaddr = p->buffer;
    lpc_dma->chan[0].dstaddr = (void *)lpc_mci->fifo;
    lpc_dma->chan[0].lli = NULL;
    lpc_dma->chan[0].ctrl = DMA_CTRL_SB_SIZE(DMA_SIZE_1) |
                            DMA_CTRL_DB_SIZE(DMA_SIZE_8) |
                            DMA_CTRL_SWIDTH(DMA_WIDTH_WORD) |
                            DMA_CTRL_DWIDTH(DMA_WIDTH_WORD) |
                            DMA_CTRL_SI;

    lpc_dma->chan[0].conf = DMA_CONF_E |
                            DMA_CONF_DST_PER(DMA_PER_SDMMC) |
                            DMA_CONF_FLOW_CTRL(DMA_FLOW_MEM_PER_DST);
    
    lpc_mci->datatimer = 4500000;           // 250ms at 18MHz MCICLK
    lpc_mci->datalength = 512;
    
    lpc_mci->datactrl = MCI_DATACTRL_ENABLE | 
                        MCI_DATACTRL_DMAENABLE |
                        MCI_DATACTRL_BLOCKSIZE(9);  // 2^9=512
    
    irqstat = 0;
    lpc_mci->mask0 = MCI_DATACRCFAIL | MCI_DATATIMEOUT | MCI_TXUNDERRUN |
                     MCI_RXOVERRUN   | MCI_DATAEND     | MCI_STARTBITERR |
                     MCI_DATABLOCKEND;
    BX_SemaGet(&irqsema, 1, -1);
    lpc_dma->chan[0].conf = 0;
    
    /*
     *  Ha atvitte a DMA a szektort, akkor megvarjuk az iras veget
     */
    if(irqstat & MCI_DATABLOCKEND){
        for(n = 0; n < 250; ++n){
            if((e = sd_cmd(13, rca, 0)) < 0)
                retunr e;
            
            if(lpc_mci->response[0] & SDSTAT_ERR)
                return ERR_SDSTAT;
            
            if(lpc_mci->response[0] & SDSTAT_READYFORDATA){
                p->dty = 0;
                return ERR_OK;
            }
            waitms(1);
        }
        return ERR_TIMEOUT;
    }
    else{
        lpc_mci->datactrl = 0;
        return ERR_SDDATA;
    }
}


static  void    sdirq(void)
{
    irqstat = lpc_mci->stat;
    BX_SemaPut(&irqsema, 1);
    lpc_mci->mask0 = 0;
}


/*
 *  Ez az SD kartya figyelo processz
 */
static  void    sdproc(void *x)
{
BX_TIME     tmr;
int         time, e;

    BX_LockObtain(&lock, -1);
    BX_SemaInit(&irqsema, BX_POLICY_FIFO, 0);
    
    BX_ProcProtRaise(BX_PROT_IRQD);
    
    lpc_scb->scs |= SCB_SCS_MCIPW;      // MCIPWR pin is active high

    lpc_scb->pconp     |= SCB_PCONP_MCI | SCB_PCONP_DMA;
    lpc_scb->pclksel1  |= SCB_PCLKSELX_DIV1 << SCB_PCLKSEL1_SFT_MCI;
    
    lpc_pcb->pinsel[1] |= PCB_P0_19_MCI_CLK | PCB_P0_20_MCI_CMD | 
                          PCB_P0_21_MCI_PWR | PCB_P0_22_MCI_DAT0;
    
    lpc_pcb->pinmode[1] &= ~(PCB_MODE_MSK(19) | 
                             PCB_MODE_MSK(20) |
                             PCB_MODE_MSK(21) |
                             PCB_MODE_MSK(22));

    lpc_pcb->pinmode[1] |= PCB_MODE_SET(19, PCB_MODE_FLT) |
                           PCB_MODE_SET(20, PCB_MODE_FLT) |
                           PCB_MODE_SET(21, PCB_MODE_PDN) |
                           PCB_MODE_SET(22, PCB_MODE_FLT);

    lpc_pcb->pinsel[4] |= PCB_P2_11_MCI_DAT1 | PCB_P2_12_MCI_DAT2 | 
                          PCB_P2_13_MCI_DAT3;
    
    lpc_pcb->pinmode[4] &= ~(PCB_MODE_MSK(11) |
                            PCB_MODE_MSK(12) |
                            PCB_MODE_MSK(13));

    lpc_pcb->pinmode[4] |= PCB_MODE_SET(11, PCB_MODE_FLT) |
                           PCB_MODE_SET(12, PCB_MODE_FLT) |
                           PCB_MODE_SET(13, PCB_MODE_FLT);

    BX_ProcProtLower(BX_PROT_NORM);
    
    /* --------- */

    /*
     *  Enable MCI interrupt
     */
    lpc_mci->mask0 = 0;
    lpc_vic->vectaddr[ VIC_SDC ] = sdirq;
    lpc_vic->vectpriority[ VIC_SDC ] = 1;
    lpc_vic->intenable = 1 << VIC_SDC;
    
    cardstat = SD_NOCARD;
    cards2   = ERR_NOCARD;
    
    BX_TimerInit(&tmr);
    BX_TimerAction(&tmr, (BX_DISR_FUNC)BX_EventPost, BX_ProcSelf(), (void *)SIG_TIMER);
    BX_TimerStart(&tmr, 300000, 0); // 300ms mulva jar le a timer
    
    for(;;BX_TimerStart(&tmr, time, 0)){
    
        BX_LockRelease(&lock);
        BX_EventWait(SIG_TIMER);
        BX_LockObtain(&lock, -1);
        
        time = 100000;
        
        if(cardstat == SD_RESET)    // ez sosem mulik el
            continue;
        
        if(lpc_fio->p[PRES_PORT].pin & PRES_MASK){      // nincs kartya
            cardblocks = 0;
            if(cardstat != SD_NOCARD){
                lpc_mci->power = MCI_POWER_CTRL_PWOF;
                BX_ProcProtRaise(BX_PROT_IRQD);
                lpc_pcb->pinmode[1] = (lpc_pcb->pinmode[1] & ~PCB_MODE_MSK(20)) |
                                           PCB_MODE_SET(20, PCB_MODE_FLT);
                BX_ProcProtLower(BX_PROT_NORM);
                cardstat = SD_NOCARD;
                cards2   = ERR_NOCARD;
                BX_EventPost(&mainpcb, SIG_CARDLOST);
                printf("eltunt a kartya\n");
            }
            continue;
        }
    
        switch(cardstat){
         case SD_NOCARD:
         case SD_BADCARD:
            lpc_mci->clock = MCI_CLOCK_ENABLE | 89;     // SDCLK = 72M / (2 * (n + 1)) == 400kHz
            lpc_mci->power = MCI_POWER_CTRL_PWUP;
            cardstat = SD_POWUP;
            rca = 0;
            printf("kartya bent\n");
            time = 400000;
            break;
            
         case SD_POWUP:
            lpc_mci->power = MCI_POWER_CTRL_PWON;
            BX_ProcProtRaise(BX_PROT_IRQD);
            lpc_pcb->pinmode[1] = (lpc_pcb->pinmode[1] & ~PCB_MODE_MSK(20)) |
                                       PCB_MODE_SET(20, PCB_MODE_PUP);
            BX_ProcProtLower(BX_PROT_NORM);
            cardstat = SD_PWRON;
            cardprot = !!(lpc_fio->p[WP_PORT].pin & WP_MASK);
            printf("powered.. write protesct is %s\n", cardprot ? "on" : "off");
            break;
            
         case SD_PWRON:
            if((e = initcard()) == 0){
                printf("%s card is ready\n", hcsd ? "HCSD" : "SD");
                initsbuff();
                cardstat = SD_READY;
                if(++chgcnt == 1000000)
                    chgcnt = 1;
                cards2 = chgcnt;
            }
            else{
                /*
                 *  Az initcard() hibat adott vissza, ugyhogy lekapcsoljuk a
                 *  kartya tapjat, es 3 masodperc mulva ujra megnezzuk,
                 *  hogy mi van vele
                 */
                printf("error code: %d, tap leo\n", e);
                BX_ProcProtRaise(BX_PROT_IRQD);
                lpc_pcb->pinmode[1] = (lpc_pcb->pinmode[1] & ~PCB_MODE_MSK(20)) |
                                           PCB_MODE_SET(20, PCB_MODE_FLT);
                BX_ProcProtLower(BX_PROT_NORM);
                lpc_mci->power = MCI_POWER_CTRL_PWOF;
                cardstat = SD_BADCARD;
                time = 3000000;
            }
            break;
        
         case SD_READY:
            /*
             *  Kartya inicializalva, itt csak az a dolgunk, hogy neha
             *  kiirjuk a kartyara a megvaltozott cache entry-ket
             */
            if(flushtmr && !--flushtmr)
                flush_cache();
            break;
            
         default:
            break;
        }
    }
}


static  int     initcard(void)
{
int     e, z, i, j, r, blnr, blen;
int     *data;
char    c;
int     v2card;

    hcsd = 0;
    v2card = 0;

    /* A CMD8-ra csak V2.0 kartyak valaszolnak, regebbiek nem */
    
    if(sd_cmd(8, 0x155, 0) == 0){       // 2.7-3.6V, check pattern is 0x55
        v2card = 1; 

        /*
         *  Ellenorizni kell, hogy a kartya valasz tartalmazza-e a 0x55-ot
         */
        if((lpc_mci->response[0] & 0xff) != 0x55)
            return 1;
    }
    
    /*
     *  ACMD41-re csak az SD kartya valaszol, MMC nem.
     *  Hibas CRC-vel valaszol. A legfelso biten jelzi, ha ready 
     */
    for(z = 0; z < 10; ++z){
        e = sd_acmd(41, v2card ? 0x40ff8000 : 0x00ff8000, 0);
        if(lpc_mci->response[0] & 0x80000000)       // busy bit
            break;
        
        waitms(200);
    }
    if(z == 10)
        return 2;

    hcsd = !!(lpc_mci->response[0] & 0x40000000);   // HCSD

    if(sd_cmd(2, 0, 1) || sd_cmd(3, 0, 0))
        return 3;
    
    rca = lpc_mci->response[0];
    
    if(sd_cmd(9, rca, 1))                           // SEND CSD
        return 4;

    printf("CSD: %08x %08x %08x %08x\n", lpc_mci->response[0],
                                         lpc_mci->response[1], lpc_mci->response[2], lpc_mci->response[3]);

    if((lpc_mci->response[0] & 0xc0000000) == 0x40000000){  // big card
        /*
         * 69:48, de a felso 6 bit 0 kell legyen... tehat: 63-48
         * card size = (C_SIZE + 1) * 512 kilobyte
         */
        cardblocks = 1024 * (((lpc_mci->response[2] >> 16) & 0xffff) + 1);
    }
    else{   // rd_bl_len: 2 ^ 83:80
        blen = 1 << ((lpc_mci->response[1] >> 16) & 15);
        
            // c_size_mult: 2 ^ 49:47  46:32
            // c_size = 73:62
        blnr = (1 << (2 + ((lpc_mci->response[2] >> 15) & 7))) * (1 +
            (((lpc_mci->response[1] << 2)  & 0xffc) |
             ((lpc_mci->response[2] >> 30) & 0x003)));
        //
        // capacity[bytes] = (c_size + 1) * 2^c_size_mult * 2^rd_bl_len;

        cardblocks = (blen * blnr) / 512;
    }
    
    printf("SD card size is: %d blocks (~%d Mega)\n", cardblocks, cardblocks/2000);
    
    if(sd_cmd(7, rca, 0))                           // select card 
        return 5;
        
    lpc_mci->clock = MCI_CLOCK_ENABLE | 1;          // SDCLK = 18M (72M/(2*(n+1)))
    
    if(sd_cmd(16, 512, 0))                          // SET_BLOCKLEN
        return 6;
    
    if(sd_acmd(6, 2, 0))                            // wide bus mode
        return 7;
    
    lpc_mci->clock = MCI_CLOCK_ENABLE | 1 | MCI_CLOCK_WIDEBUS;  // SDCLK = 18MHz, 4 bit bus
    return 0;
}


static  void    initsbuff(void)
{
char    *p;
sbuf_t  *x;

    DL_Init(&free);
    DL_Init(&cache);
    
    p = (char *)bufbeg;
    
    for(x = sbuff; x < sbuff + MAXSBUF; ++x){
        x->sector = -1;
        x->buffer = p;
        x->dty    = 0;
        x->locked = 0;
        DL_PutTail(&free, &(x->link));
        p += 512;
    }
    flushtmr = 0;
}

    

static  int     sd_acmd(int code, int arg, int longresp)
{
int     e;

    e = _sd_cmd(55, rca, 0);
    if(e < 0){
        printf("acmd%d: cmd55: e: %d\n", code, e);
        return e;
    }
//  printf("a");
    return sd_cmd(code, arg, longresp);
}

static  int     sd_cmd(int code, int arg, int longresp)
{
int     r;

    r = _sd_cmd(code, arg, longresp);
return r;
    
    if(!longresp)
        printf("cmd%d: e:%d, sta: %s, resp: %08x\n", code, r, 
                statof(irqstat),  lpc_mci->response[0]);
    else{
        printf("cmd%d: e:%d, sta: %s, resp: ", code, r, statof(irqstat));
        printf("%08x %08x %08x %08x\n", lpc_mci->response[0], lpc_mci->response[1], 
                                        lpc_mci->response[2], lpc_mci->response[3]);
    }
    return r;
}


static  int     _sd_cmd(int code, int arg, int longresp)
{
int     r;

    for(r = 0; r < 30; ++r){
        irqstat = 0;
        lpc_mci->clr = 0x7ff;       // status clear
        lpc_mci->argument = arg;
        lpc_mci->mask0 = MCI_CMDCRCFAIL | MCI_CMDTIMEOUT | MCI_CMDRESPEND;
        lpc_mci->command  = MCI_COMMAND_INDEX(code) | MCI_COMMAND_RESPONSE |
                            MCI_COMMAND_ENABLE | ((longresp) ? MCI_COMMAND_LONGRSP : 0);
        
        BX_SemaGet(&irqsema, 1, -1);

        if(irqstat == 0)
            return ERR_IRQ;

        if(irqstat & MCI_CMDRESPEND) 
            return 0;
        
        if(irqstat & MCI_CMDCRCFAIL)
            return ERR_CMDCRC;
            
        waitms(1);
    }
    return ERR_CMDTIMO;
}


static  char    stxt[300];

static  const   char    *stats[] = {
    "CmdCrcFail ",              // 0
    "DataCrcFail ",
    "CmdTimeOut ",              // 2
    "DataTimeOut ",
    "TxUnderrun ",              // 4
    "RxOverrun ",
    "CmdRespEnd ",              // 6
    "CmdSent ",
    "DataEnd ",                 // 8
    "StartBitErr ",
    "DataBlockEnd ",            // 10
    "CmdActive ",
    "TxActive ",                // 12
    "RxActive ",
    "TxFifoHalfEmpty ",         // 14
    "RxFifoHalfFull ",
    "TxFifoFull ",              // 16
    "RxFifoFull ",
    "TxFifoEmpty ",             // 18
    "RxFifoEmpty ",
    "TxDataAvlbl ",             // 20
    "RxDataAvlbl "              // 21
};

static  char    *statof(int s)
{
int     mask;
const char  **p;

    stxt[0] = 0;
    p = stats;
    for(mask = 1; mask != 1<<22; mask <<= 1, ++p){
        if(s & mask)
            strcat(stxt, *p);
    }
    return stxt;
}
