/*
 * nand_thread.c
 *
 *  Created on: Apr 11, 2010
 *      Author: Limi
 */

#include "ssd/system.h"
#include "ssd/utils.h"
#include <threads.h>
#include "ssd/nand.h"

extern ngssd_mutex_t nandport_mutex;
extern nandport_t nandport;
extern ngssd_cond_t  condition_cond;

#include <stdio.h>
extern FILE *nandfp;

/**
 * main loop of nand chip thread
 *
 * Assumptions:
 * 1. port and chip initialization are left to caller
 * 2. currently no ECC code handling
 */
void *
nand_thread_main(){
	error_t res;
	nandcmd_t cmd;
	uint32_t page;
	uint8_t state;

 	/* allow cancelation*/
	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

	/* init chip*/
//	L("call nandInit()");
	nandInit();

//	L("open nand log file");	
	nandfp = fopen("log_nand", "w+");
	/* main loop
	 * - check nandport
	 * - if there's a request, do it
	 * - if not, keep checking*/
	while(1){
		/* acquire mutex so we can wait on it*/
		L_FP(nandfp, "acquire mutex so we can wait on it");
		while(ut_acquire_nand_port());
		L_FP(nandfp, "nand thread. mutex acquired before waiting. now wait");
		/* wait for cond signal
		 * to indicate pending cmd*/
		while(NANDPORT_GET_STATE(nandport) != NANDPORT_STATE_CMD){
			THREAD_COND_WAIT(&condition_cond, &nandport_mutex );
		}
//		L_FP(nandfp, "nand thread. got signaled!");
//		/* if no lock acquired continue looping*/
//		if(res){
//			L("nand thread failed trying to acquire lock. sleep 1 second");
//			sleep(1);
//			continue;
//		}
//
//		/* if no requets pending - free lock and continue*/
//		ut_get_nand_port_state(&state);
//		L_FP(nandfp, "state %d", state);
//		assert(state == NANDPORT_STATE_CMD);
//		L_FP(nandfp, "assert state successful");
		
		/* Signal arrived - request is pending.
		 * - execute
		 * - init command in port
		 * - free lock*/
		L_FP(nandfp, "nand thread acquired lock. state CMD. executing");
		cmd = NANDPORT_GET_CMD(nandport);
		L_FP(nandfp, "cmd %d", cmd);
		page = NANDPORT_GET_PAGE(nandport);
		if(cmd == NAND_CMD_READ){
			L_FP(nandfp, "cmd READ");
			/* TODO: partial read?*/
			nandReadPage(NANDPORT_GET_BUF_PTR(nandport), page);
			L_FP(nandfp, "cmd READ done");

//			int i;
//			for(i=0; i< NAND_TOTAL_SIZE;i++){
//				if(NANDPORT_GET_BUF_PTR(nandport)[i] != 0xff){
//					fprintf(nandfp, "\nbuf[%d] = %x", i, NANDPORT_GET_BUF_PTR(nandport)[i]);
//				}
//			}
		}
		else if (cmd == NAND_CMD_WRITE){
			L_FP(nandfp, "cmd WRITE");
			nandProgramPage(NANDPORT_GET_BUF_PTR(nandport), page);
		}
		else if(cmd == NAND_CMD_ERASE){
			L_FP(nandfp, "cmd ERASE");
			nandErase(page);
		}
		else{
//			L_FP(nandfp, "nandport cmd %d not recognized. exiting", NANDPORT_GET_CMD(nandport));
			assert(0);
			break;
		}

		L_FP(nandfp, "nand thread setting state done. RELINQUISHING!");
		NANDPORT_SET_STATE(nandport, NANDPORT_STATE_DONE);
		ut_free_nand_port();
	}

	/* if we got here, simply exit all (we should never get here)*/
	THREAD_EXIT();
}
