/*! \fn AhdlcRx(void *arg)
 * \brief Asynchronous HDLC receiver thread.
 *
 *
 * Running at high priority.
 */
THREAD(AhdlcRx, arg)
{
    NUTDEVICE *dev = arg;
    NUTDEVICE *netdev;
    AHDLCDCB *dcb = dev->dev_dcb;
    IFNET *ifn;
    NETBUF *nb;
    uint8_t *rxbuf;
    uint8_t *rxptr;
    uint16_t rxcnt;
    uint8_t ch;
    uint16_t tbx;
    uint8_t inframe;
    uint8_t escaped;
    uint16_t rxfcs;

    NutThreadSetPriority(9);
    for (;;) {
        /*
         * Reset variables to their initial state
         */
        rxptr = 0;
        rxcnt = 0;
        escaped = 0;
        rxfcs = AHDLC_INITFCS;
        inframe = 0;

        for (;;) {
            /*
             * Wait until the network interface has been attached.
             * This will be initiated by the application calling
             * NutNetIfConfig(), which in turn calls a HDLC_SETIFNET
             * ioctl() to store the NUTDEVICE pointer of the network
             * device in dev_icb and trigger an event on dcb_mf_evt.
             */
            while ((netdev = dev->dev_icb) == 0) {
                if (NutEventWait(&dcb->dcb_mf_evt, 1000) == 0) {
                    NutSleep(100);
                }
            }
            ifn = netdev->dev_icb;
            dcb->dcb_rtimeout = 1000;
            inframe = 0;

            /*
             * Allocate the receive buffer, if this fails, we are in a
             * low memory situation. Take a nap and see, if the
             * situation improved.
             */
            if ((rxbuf = NutHeapAlloc(dcb->dcb_rx_mru)) != 0) {
                break;
            }
            NutSleep(1000);
        }

        /*
         * Signal the link driver that we are up.
         */
        ifn->if_send = AhdlcOutput;
        netdev->dev_ioctl(netdev, LCP_LOWERUP, 0);

        for (;;) {
            /*
             * If we are still connected to a network, fetch the next
             * character from the buffer.
             */
            while (dcb->dcb_rd_idx == dcb->dcb_rx_idx) {
                if (dev->dev_icb == 0)
                    break;
                // TODO: Check for idle timeout. 
                if (NutEventWait(&dcb->dcb_rx_rdy, dcb->dcb_rtimeout)) {
                    continue;
                }
            }

            /*
             * Leave loop if network interface is detached
             */
            if (dev->dev_icb == 0)
                break;

            /*
             * If RAW mode is active, we are not allowing any data encapsulation
             * processing. So we just sleep for a while.
             */
            if (dcb->dcb_modeflags & UART_MF_RAWMODE) {
                /*
                 * It is a must to sleep here, because if we just yield it could create
                 * too much processing in here and stall processing elsewhere. This gives
                 * opportunity to other threads to process incoming data from USART.
                 */
                NutSleep(100);
                continue;
            }

            /*
             * Read next character from input buffer
             */
            ch = dcb->dcb_rx_buf[dcb->dcb_rd_idx++];

            if (inframe) {
                if (ch != AHDLC_FLAG) {
                    if (ch == AHDLC_ESCAPE) {
                        escaped = 1;
                        continue;
                    }
                    if (escaped) {
                        ch ^= AHDLC_TRANS;
                        escaped = 0;
                    }

                    /*
                     * Unless the peer lied to us about the negotiated MRU,
                     * we should never get a frame which is too long. If it
                     * happens, toss it away and grab the next incoming one.
                     */
                    if (rxcnt++ < dcb->dcb_rx_mru) {
                        /* Update calculated checksum and store character in buffer. */
                        tbx = (uint16_t) ((uint8_t) rxfcs ^ ch) << 1;
                        rxfcs >>= 8;
                        rxfcs ^= ((uint16_t) PRG_RDB(fcstab + tbx) << 8) | PRG_RDB(fcstab + tbx + 1);
                        *rxptr++ = ch;
                    } else
                        inframe = 0;
                    continue;
                }

                if (rxcnt > 6 && rxfcs == AHDLC_GOODFCS) {
                    /*
                       * If the frame checksum is valid, create a NETBUF
                       * and pass it to the network specific receive handler.
                     */
                    rxcnt -= 2;
                    if ((nb = NutNetBufAlloc(0, NBAF_DATALINK, rxcnt)) != 0) {
                        memcpy(nb->nb_dl.vp, rxbuf, rxcnt);
                        (*ifn->if_recv) (netdev, nb);
                    }
                }
            }

            /*
             * If frame flag is received, resync frame processing
             */
            if (ch == AHDLC_FLAG) {
                inframe = 1;
                escaped = 0;
                rxptr = rxbuf;
                rxcnt = 0;
                rxfcs = AHDLC_INITFCS;
            }
        }
/*!
 * \brief Mount a partition.
 *
 * Nut/OS doesn't provide specific routines for mounting. Instead routines
 * for opening files are used.
 *
 * Applications should not directly call this function, but use the high
 * level stdio routines for opening a file.
 *
 * \param dev  Pointer to the MMC device.
 * \param name Partition number followed by a slash followed by a name
 *             of the file system device. Both items are optional. If no 
 *             file system driver name is given, the first file system
 *             driver found in the list of registered devices will be 
 *             used. If no partition number is specified or if partition
 *             zero is given, the first active primary partition will be 
 *             used.
 * \param mode Opening mode. Currently ignored, but 
 *             \code _O_RDWR | _O_BINARY \endcode should be used for
 *             compatibility with future enhancements.
 * \param acc  File attributes, ignored.
 *
 * \return Pointer to a newly created file pointer to the mounted
 *         partition or NUTFILE_EOF in case of any error.
 */
static NUTFILE *At91MciMount(NUTDEVICE * dev, CONST char *name, int mode, int acc)
{
    int partno = 0;
    int i;
    NUTDEVICE *fsdev;
    NUTFILE *nfp;
    MCIFCB *fcb;
    DOSPART *part;
    MCIFC *ifc = (MCIFC *) dev->dev_icb;
    FSCP_VOL_MOUNT mparm;

    if (At91MciDiscover(ifc)) {
        errno = ENODEV;
        return NUTFILE_EOF;
    }

    /* Parse the name for a partition number and a file system driver. */
    if (*name) {
        partno = atoi(name);
        do {
            name++;
        } while (*name && *name != '/');
        if (*name == '/') {
            name++;
        }
    }
#ifdef NUTDEBUG
    printf("['%s'-PART%d]", name, partno);
#endif

    /*
     * Check the list of registered devices for the given name of the
     * files system driver. If none has been specified, get the first
     * file system driver in the list. Hopefully the application
     * registered one only.
     */
    for (fsdev = nutDeviceList; fsdev; fsdev = fsdev->dev_next) {
        if (*name == 0) {
            if (fsdev->dev_type == IFTYP_FS) {
                break;
            }
        } else if (strcmp(fsdev->dev_name, name) == 0) {
            break;
        }
    }

    if (fsdev == 0) {
#ifdef NUTDEBUG
        printf("[No FSDriver]");
#endif
        errno = ENODEV;
        return NUTFILE_EOF;
    }

    if ((fcb = NutHeapAllocClear(sizeof(MCIFCB))) == 0) {
        errno = ENOMEM;
        return NUTFILE_EOF;
    }
    fcb->fcb_fsdev = fsdev;

    /* Initialize MMC access mutex semaphore. */
    NutEventPost(&mutex);

    /* Read MBR. */
    if (At91MciReadSingle(ifc, 0, fcb->fcb_blkbuf)) {
        NutHeapFree(fcb);
        return NUTFILE_EOF;
    }

    /* Read partition table. */
    part = (DOSPART *) & fcb->fcb_blkbuf[DOSPART_SECTORPOS];
    for (i = 1; i <= 4; i++) {
        if (partno) {
            if (i == partno) {
                /* Found specified partition number. */
                fcb->fcb_part = *part;
                break;
            }
        } else if (part->part_state & 0x80) {
            /* Located first active partition. */
            fcb->fcb_part = *part;
            break;
        }
        part++;
    }

    if (fcb->fcb_part.part_type == PTYPE_EMPTY) {
        NutHeapFree(fcb);
        return NUTFILE_EOF;
    }

    if ((nfp = NutHeapAlloc(sizeof(NUTFILE))) == 0) {
        NutHeapFree(fcb);
        errno = ENOMEM;
        return NUTFILE_EOF;
    }
    nfp->nf_next = 0;
    nfp->nf_dev = dev;
    nfp->nf_fcb = fcb;

    /*
     * Mount the file system volume.
     */
    mparm.fscp_bmnt = nfp;
    mparm.fscp_part_type = fcb->fcb_part.part_type;
    if (fsdev->dev_ioctl(fsdev, FS_VOL_MOUNT, &mparm)) {
        At91MciUnmount(nfp);
        return NUTFILE_EOF;
    }
    return nfp;
}