/*
 * (C) Copyright 2008
 *
 * Based on Linux kernel's USB device MSD gadget driver
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307	 USA
 *
 */
#define DEBUG
#include <common.h>
#include <circbuf.h>
#include <asm/errno.h>
#include <asm/byteorder.h>
#include "usbmsd.h"
#include <command.h>

/* Routines for unaligned data access */

static u16 inline get_be16(u8 * buf)
{
	return ((u16) buf[0] << 8) | ((u16) buf[1]);
}

static u32 inline get_be32(u8 * buf)
{
	return ((u32) buf[0] << 24) | ((u32) buf[1] << 16) |
			((u32) buf[2] << 8) | ((u32) buf[3]);
}

static void inline put_be16(u8 * buf, u16 val)
{
	buf[0] = val >> 8;
	buf[1] = val;
}

static void inline put_be32(u8 * buf, u32 val)
{
	buf[0] = val >> 24;
	buf[1] = val >> 16;
	buf[2] = val >> 8;
	buf[3] = val & 0xff;
}
/*
 * Buffers to hold input and output data
 */
#define USBMSD_BUFFER_SIZE 4096*20
static char usb_tx_buffer[USBMSD_BUFFER_SIZE];
static char *usb_rx_buffer;


/*
 * Instance variables
 */
static struct usb_device_instance	 device_instance[1];
static struct usb_bus_instance		 bus_instance[1];
static struct usb_configuration_instance config_instance[NUM_CONFIGS];
static struct usb_interface_instance interface_instance[NUM_INTERFACES];
static struct usb_alternate_instance alternate_instance[NUM_INTERFACES];
/* one extra for control endpoint */
static struct usb_endpoint_instance endpoint_instance[NUM_ENDPOINTS + 1];

static struct usb_msd_context 		g_usb_msd_context;


/*
 * Static allocation of urbs
 */
#define RX_ENDPOINT 	1
#define TX_ENDPOINT 	2

/*
 * Global flag
 */
u8 usbmsd_configured_flag = 0;


/*
 * Serial number
 */
static char serial_number[16];

/*
 * Descriptors
 */
static u8 wstrLang[4] = {4,USB_DT_STRING,0x9,0x4};
static u8 wstrManufacturer[2 + 2*(sizeof(CONFIG_USBD_MANUFACTURER)-1)];
static u8 wstrProduct[2 + 2*(sizeof(CONFIG_USBD_PRODUCT_NAME)-1)];
static u8 wstrSerial[2 + 2*(sizeof(serial_number) - 1)];
static u8 wstrConfiguration[2 + 2*(sizeof(CONFIG_USBD_CONFIGURATION_STR)-1)];
static u8 wstrInterface[2 + 2*(sizeof(CONFIG_USBD_INTERFACE_STR)-1)];

static struct usb_string_descriptor *usbmsd_string_table[STR_COUNT] = {
  (struct usb_string_descriptor*)wstrLang,
  (struct usb_string_descriptor*)wstrManufacturer,
  (struct usb_string_descriptor*)wstrProduct,
  (struct usb_string_descriptor*)wstrSerial,
  (struct usb_string_descriptor*)wstrConfiguration,
  (struct usb_string_descriptor*)wstrInterface
};

extern struct usb_string_descriptor **usb_strings; 

static struct usb_interface_descriptor interface_descriptors[NUM_INTERFACES];
static struct usb_endpoint_descriptor *ep_descriptor_ptrs[NUM_ENDPOINTS];
static struct usb_configuration_descriptor	*configuration_descriptor = 0;

static struct usb_device_descriptor device_descriptor = {
  .bLength	        = sizeof(struct usb_device_descriptor),
  .bDescriptorType	= USB_DT_DEVICE,
  .bcdUSB	        = cpu_to_le16(USB_BCD_VERSION),
  .bDeviceClass		= USBMSD_DEVICE_CLASS,
  .bDeviceSubClass	= USBMSD_DEVICE_SUBCLASS,
  .bDeviceProtocol	= USBMSD_DEVICE_PROTOCOL,
  .bMaxPacketSize0	= EP0_MAX_PACKET_SIZE,
  .idVendor	        = cpu_to_le16(CONFIG_USBD_VENDORID),
  .idProduct	    = CONFIG_USBD_PRODUCTID,
  .bcdDevice	    = cpu_to_le16(USBMSD_BCD_DEVICE),
  .iManufacturer	= STR_MANUFACTURER,
  .iProduct	        = STR_PRODUCT,
  .iSerialNumber	= STR_SERIAL,
  .bNumConfigurations= NUM_CONFIGS
};

struct gusbmsd_config_desc {

	struct usb_configuration_descriptor config_descriptors;
	struct usb_interface_descriptor	interface_descriptors[NUM_INTERFACES];
	struct usb_endpoint_descriptor data_endpoints[NUM_ENDPOINTS];

} __attribute__((packed));

static struct gusbmsd_config_desc gusbmsd_cfg_desc[NUM_CONFIGS] = {
	{
		.config_descriptors = {
			.bLength		= sizeof(struct usb_configuration_descriptor),
			.bDescriptorType= USB_DT_CONFIG,
			.wTotalLength	= 32,	
			   /* (sizeof(struct usb_configuration_descriptor)*NUM_CONFIGS) +
				  (sizeof(struct usb_interface_descriptor)*NUM_INTERFACES) +
				  (sizeof(struct usb_endpoint_descriptor)*NUM_ENDPOINTS),
			   */
			.bNumInterfaces	= NUM_INTERFACES,
			.bConfigurationValue	= 1,
			.iConfiguration	= STR_CONFIG,
			.bmAttributes	= BMATTRIBUTE_SELF_POWERED | BMATTRIBUTE_RESERVED,
			.bMaxPower		= USBMSD_MAXPOWER
		},
		.interface_descriptors = {
			{
				.bLength	        = sizeof(struct usb_interface_descriptor),
				.bDescriptorType	= USB_DT_INTERFACE,
				.bInterfaceNumber	= 0,
				.bAlternateSetting	= 0,
				.bNumEndpoints      = NUM_ENDPOINTS,
				.bInterfaceClass	= USBMSD_INTERFACE_CLASS,
				.bInterfaceSubClass	= USBMSD_INTERFACE_SUBCLASS,
				.bInterfaceProtocol	= USBMSD_INTERFACE_PROTOCOL,
				.iInterface	        = STR_INTERFACE,
			},
		},
		.data_endpoints = {
			{
				.bLength	        = sizeof(struct usb_endpoint_descriptor),
				.bDescriptorType	= USB_DT_ENDPOINT,
				.bEndpointAddress	= USB_DIR_OUT | CONFIG_USBD_MSD_OUT_ENDPOINT,
				.bmAttributes       = USB_ENDPOINT_XFER_BULK,
				.wMaxPacketSize   	= CONFIG_USBD_MSD_OUT_PKTSIZE,
				.bInterval	        = 0
			}, 
			{
				.bLength		    = sizeof(struct usb_endpoint_descriptor),
				.bDescriptorType	= USB_DT_ENDPOINT,
				.bEndpointAddress	= USB_DIR_IN | CONFIG_USBD_MSD_IN_ENDPOINT,
				.bmAttributes       = USB_ENDPOINT_XFER_BULK,
				.wMaxPacketSize   	= CONFIG_USBD_MSD_IN_PKTSIZE,
				.bInterval	        = 0
			},
		},
	},
};

/* utility function for converting char* to wide string used by USB */
static void str2wide (char *str, u16 * wide)
{
	int i;

	for (i = 0; i < strlen (str) && str[i]; i++)
		wide[i] = (u16) str[i];
}

/*
 * Prototypes
 */
/* FIXME: linaro 4.5.3 2011.03/04 Os issue */
//static void usbmsd_init_strings (void) __attribute__((noinline)); 
static void usbmsd_init_strings (void);
static void usbmsd_init_instances (void);
static void usbmsd_init_endpoints (void);
static void usbmsd_event_handler (struct usb_device_instance *device, 
		usb_device_event_t event, int data);
static uchar usbmsd_configured (void);
static int start_write (struct usb_msd_context *fsg);
static int usbmsd_class_setup(struct usb_device_request *request, 
		struct urb *urb);
static int usbmsd_setup_data(struct usb_device_request *request, 
		void *buffer, uchar length);
void usbmsd_poll(void);
void usbmsd_receive(char * buf, int len);

/*********************************************************************************/
static int usbmsd_udc_init (void)
{
	char * sn;
	int snlen;

	if (!(sn = getenv("serial#"))) {
		sn = "SIRF100908";
	}
	snlen = strlen(sn);
	if (snlen > sizeof(serial_number) - 1) {
		snlen = sizeof(serial_number) - 1;
	}
	memcpy (serial_number, sn, snlen);
	serial_number[snlen] = '\0';

	/* Now, set up USB controller and infrastructure */
	/* Basic USB initialization */
	if(udc_init(usbmsd_receive)) {  
		printf(":%s: udc init failed\n", __func__);
		return -1;
	}
	usbmsd_init_strings ();
	usbmsd_init_instances ();

	/* Enable our device, initialize udc pointers */
	udc_startup_events (device_instance);	
	/* Enable pullup for host detection */
	udc_connect();		

	usbmsd_init_endpoints ();
	
	return 0;
}

/*********************************************************************************/
static void usbmsd_init_strings (void)
{
	struct usb_string_descriptor *string;

	string = (struct usb_string_descriptor *) wstrManufacturer;
	string->bLength = sizeof (wstrManufacturer);
	string->bDescriptorType = USB_DT_STRING;
	str2wide (CONFIG_USBD_MANUFACTURER, string->wData);

	string = (struct usb_string_descriptor *) wstrProduct;
	string->bLength = sizeof (wstrProduct);
	string->bDescriptorType = USB_DT_STRING;
	str2wide (CONFIG_USBD_PRODUCT_NAME, string->wData);

	string = (struct usb_string_descriptor *) wstrSerial;
	string->bLength = 2 + 2*strlen(serial_number);
	string->bDescriptorType = USB_DT_STRING;
	str2wide (serial_number, string->wData);

	string = (struct usb_string_descriptor *) wstrConfiguration;
	string->bLength = sizeof (wstrConfiguration);
	string->bDescriptorType = USB_DT_STRING;
	str2wide (CONFIG_USBD_CONFIGURATION_STR, string->wData);

	string = (struct usb_string_descriptor *) wstrInterface;
	string->bLength = sizeof (wstrInterface);
	string->bDescriptorType = USB_DT_STRING;
	str2wide (CONFIG_USBD_INTERFACE_STR, string->wData);

	/* Now, initialize the string table for ep0 handling */
	usb_strings = usbmsd_string_table;
}

/*********************************************************************************/
static void usbmsd_init_instances (void)
{
	int i;

	/* initialize device instance */
	memset (device_instance, 0, sizeof (struct usb_device_instance));
	device_instance->device_state = STATE_INIT;
	device_instance->device_descriptor = &device_descriptor;
	device_instance->event = usbmsd_event_handler;
	device_instance->cdc_recv_setup = usbmsd_class_setup;
	device_instance->bus = bus_instance;
	device_instance->configurations = NUM_CONFIGS;
	device_instance->configuration_instance_array = config_instance;

	/* initialize bus instance */
	memset (bus_instance, 0, sizeof (struct usb_bus_instance));
	bus_instance->device = device_instance;
	bus_instance->endpoint_array = endpoint_instance;
	bus_instance->max_endpoints = 1;
	bus_instance->maxpacketsize = 64;
	bus_instance->serial_number_str = serial_number;

	/* configuration instance */
	memset (config_instance, 0, sizeof (struct usb_configuration_instance));
	config_instance->interfaces = NUM_INTERFACES;
	config_instance->configuration_descriptor = &gusbmsd_cfg_desc[0].config_descriptors;
	config_instance->interface_instance_array = interface_instance;

	/* assign endpoint descriptors */
	ep_descriptor_ptrs[0] = &gusbmsd_cfg_desc[0].data_endpoints[0];
	ep_descriptor_ptrs[1] =	&gusbmsd_cfg_desc[0].data_endpoints[1];

	/* interface instance */
	memset (interface_instance, 0,
		sizeof (struct usb_interface_instance));
	interface_instance->alternates = 1;
	interface_instance->alternates_instance_array = alternate_instance;
	/* alternates instance */
	memset (alternate_instance, 0,
		sizeof (struct usb_alternate_instance));
	alternate_instance->interface_descriptor = gusbmsd_cfg_desc[0].interface_descriptors;
	alternate_instance->endpoints = NUM_ENDPOINTS;
	alternate_instance->endpoints_descriptor_array = ep_descriptor_ptrs;

	/* endpoint instances */
	memset (&endpoint_instance[0], 0,sizeof (struct usb_endpoint_instance));
	endpoint_instance[0].endpoint_address = 0;
	endpoint_instance[0].rcv_packetSize = EP0_MAX_PACKET_SIZE;
	endpoint_instance[0].rcv_attributes = USB_ENDPOINT_XFER_CONTROL;
	endpoint_instance[0].tx_packetSize = EP0_MAX_PACKET_SIZE;
	endpoint_instance[0].tx_attributes = USB_ENDPOINT_XFER_CONTROL;

	udc_setup_ep (device_instance, 0, &endpoint_instance[0]);

	for (i = 1; i <= NUM_ENDPOINTS; i++) {
		memset (&endpoint_instance[i], 
				0, sizeof (struct usb_endpoint_instance));

		endpoint_instance[i].endpoint_address =
			ep_descriptor_ptrs[i - 1]->bEndpointAddress;
		endpoint_instance[i].rcv_packetSize =
			le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize);
		endpoint_instance[i].rcv_attributes =
			ep_descriptor_ptrs[i - 1]->bmAttributes;

		endpoint_instance[i].tx_packetSize =
			le16_to_cpu(ep_descriptor_ptrs[i - 1]->wMaxPacketSize);

		endpoint_instance[i].tx_attributes =
			ep_descriptor_ptrs[i - 1]->bmAttributes;

		urb_link_init (&endpoint_instance[i].rcv);
		urb_link_init (&endpoint_instance[i].rdy);
		urb_link_init (&endpoint_instance[i].tx);
		urb_link_init (&endpoint_instance[i].done);
	}
}

/*********************************************************************************/
static void usbmsd_init_endpoints (void)
{
	int i;

	bus_instance->max_endpoints = NUM_ENDPOINTS + 1;
	for (i = 1; i <= NUM_ENDPOINTS; i++) {
		udc_setup_ep (device_instance, i, &endpoint_instance[i]);
	}
}


/*********************************************************************************/

static int start_write (struct usb_msd_context *fsg)
{
	struct usb_endpoint_instance *endpoint = &endpoint_instance[TX_ENDPOINT];
	if (!usbmsd_configured ()) {
		error("MSD not initialized yet\n");
		return -EBUSY;
	}
	udc_endpoint_write (endpoint, usb_tx_buffer, fsg->inreq.length);
	return 0;
}

/*********************************************************************************/
u8 usbmsd_configured (void)
{
	return usbmsd_configured_flag;
}

/*********************************************************************************/
static int get_next_command(struct usb_msd_context *fsg)
{
	struct bulk_cb_wrap *cbw = (struct bulk_cb_wrap *)usb_rx_buffer;
	/* Poll for data; the data will be copied
	 * to the input buffer */

	debug("Waiting for CBW\n");
	/* Queue for an OUT transfer for CBW */
	udc_endpoint_queue_read(&endpoint_instance[RX_ENDPOINT], USB_BULK_CB_WRAP_LEN);
	do{
		usbmsd_poll();
		cbw = (struct bulk_cb_wrap *)usb_rx_buffer;
	}while ((fsg->rx_buffer_data_size == 0) && usbmsd_configured());

	fsg->cmnd_size = cbw->Length;
	memcpy(fsg->cmnd, cbw->CDB, fsg->cmnd_size);
	if (cbw->Flags & USB_BULK_IN_FLAG)
		fsg->data_dir = DATA_DIR_TO_HOST;
	else
		fsg->data_dir = DATA_DIR_FROM_HOST;
	fsg->data_size = le32_to_cpu(cbw->DataTransferLength);
	if (fsg->data_size == 0)
		fsg->data_dir = DATA_DIR_NONE;
	fsg->lun = cbw->Lun;
	fsg->tag = cbw->Tag;

	fsg->rx_buffer_data_size = 0;
	//debug("Rxd CBW: 0%x 0%x 0%x 0%x\n", cbw->Lun, cbw->Length, cbw->Tag, fsg->data_size);
	return 0;
}

/*********************************************************************************/

/* Check whether the command is properly formed and whether its data size
 * and direction agree with the values we already have. */
static int check_command(struct usb_msd_context *fsg, int cmnd_size,
		enum data_direction data_dir, unsigned int mask,
		int needs_medium, const char *name)
{
	int			i;
	static const char	dirletter[4] = {'u', 'o', 'i', 'n'};
	char			hdlen[20];
	struct lun		*curlun;

	hdlen[0] = 0;
	if (fsg->data_dir != DATA_DIR_UNKNOWN)
		sprintf(hdlen, ", H%c=%u", dirletter[(int) fsg->data_dir],
				fsg->data_size);
#if 0
	debug("SCSI command: %s;  Dc=%d, D%c=%u;  Hc=%d%s\n",
			name, cmnd_size, dirletter[(int) data_dir],
			fsg->data_size_from_cmnd, fsg->cmnd_size, hdlen);
#endif
	/* We can't reply at all until we know the correct data direction
	 * and size. */
	if (fsg->data_size_from_cmnd == 0)
		data_dir = DATA_DIR_NONE;
	if (fsg->data_dir == DATA_DIR_UNKNOWN) {/* this should not happen */	
		fsg->data_dir = data_dir;
		fsg->data_size = fsg->data_size_from_cmnd;

	} else {					// Bulk-only
		if (fsg->data_size < fsg->data_size_from_cmnd) {

			/* Host data size < Device data size is a phase error.
			 * Carry out the command, but only transfer as much
			 * as we are allowed. */
			fsg->data_size_from_cmnd = fsg->data_size;
			fsg->phase_error = 1;
		}
	}
	fsg->residue = fsg->data_size;

	/* Conflicting data directions is a phase error */
	if (fsg->data_dir != data_dir && fsg->data_size_from_cmnd > 0) {
		fsg->phase_error = 1;
		error("Phase Error\n");
		return -EINVAL;
	}
	/* Check the LUN */
	if (fsg->lun >= 0 && fsg->lun < fsg->nluns) {
		fsg->curlun = curlun = &fsg->luns[fsg->lun];
		if (fsg->cmnd[0] != SC_REQUEST_SENSE) {
			curlun->sense_data = SS_NO_SENSE;
			curlun->sense_data_info = 0;
			curlun->info_valid = 0;
		}
	} else {
		fsg->curlun = curlun = NULL;
		fsg->bad_lun_okay = 0;

		/* INQUIRY and REQUEST SENSE commands are explicitly allowed
		 * to use unsupported LUNs; all others may not. */
		if (fsg->cmnd[0] != SC_INQUIRY &&
				fsg->cmnd[0] != SC_REQUEST_SENSE) {
			error("unsupported LUN %d\n", fsg->lun);
			return -EINVAL;
		}
	}

	/* Check that only command bytes listed in the mask are non-zero */
	fsg->cmnd[1] &= 0x1f;			// Mask away the LUN
	for (i = 1; i < cmnd_size; ++i) {
		if (fsg->cmnd[i] && !(mask & (1 << i))) {
			if (curlun)
				curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
			error("Wrong command mask\n");
			return -EINVAL;
		}
	}
	return 0;
}

/*********************************************************************************/
static int do_inquiry(struct usb_msd_context *fsg, char * buf)
{
	static char vendor_id[] = "U-BOOT  ";
	static char product_id[] = "USB MASS STORAGE";

	if (!fsg->curlun) {		// Unsupported LUNs are okay
		fsg->bad_lun_okay = 1;
		memset(buf, 0, 36);
		buf[0] = 0x7f;		// Unsupported, no device-type
		return 36;
	}

	memset(buf, 0, 8);	// direct-access device
	buf[1] = 0x80;		//removable
	buf[2] = 4; // 2;		// ANSI SCSI level 2
	buf[3] = 2;		// SCSI-2 INQUIRY data format
	buf[4] = 31;		// Additional length
				// No special options
	sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, product_id,
			0x51AF);
	return 36;
}

/*********************************************************************************/

static int do_mode_select(struct usb_msd_context *fsg, char * buf)
{
	struct lun	*curlun = fsg->curlun;

	/* We don't support MODE SELECT */
	curlun->sense_data = SS_INVALID_COMMAND;
	return -EINVAL;
}

/*********************************************************************************/
static int do_mode_sense(struct usb_msd_context *fsg, char * buf)
{
	struct lun	*curlun = fsg->curlun;
	int		mscmnd = fsg->cmnd[0];
	char		*buf0 = buf;
	int		pc, page_code;
	int		changeable_values, all_pages;
	int		valid_page = 0;
	int		len, limit;

	if ((fsg->cmnd[1] & ~0x08) != 0) {		// Mask away DBD
		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
		return -EINVAL;
	}
	pc = fsg->cmnd[2] >> 6;
	page_code = fsg->cmnd[2] & 0x3f;
	if (pc == 3) {
		curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
		return -EINVAL;
	}
	changeable_values = (pc == 1);
	all_pages = (page_code == 0x3f);

	/* Write the mode parameter header.  Fixed values are: default
	 * medium type, no cache control (DPOFUA), and no block descriptors.
	 * The only variable value is the WriteProtect bit.  We will fill in
	 * the mode data length later. */
	memset(buf, 0, 8);
	if (mscmnd == SC_MODE_SENSE_6) {
		buf[2] = (curlun->ro ? 0x80 : 0x00);		// WP, DPOFUA
		buf += 4;
		limit = 255;
	} else {			// SC_MODE_SENSE_10
		buf[3] = (curlun->ro ? 0x80 : 0x00);		// WP, DPOFUA
		buf += 8;
		limit = 65535;		// Should really be mod_data.buflen
	}

	/* No block descriptors */

	/* The mode pages, in numerical order.  The only page we support
	 * is the Caching page. */
	if (page_code == 0x08 || all_pages) {
		valid_page = 1;
		buf[0] = 0x08;		// Page code
		buf[1] = 10;		// Page length
		memset(buf+2, 0, 10);	// None of the fields are changeable

		if (!changeable_values) {
			buf[2] = 0x04;	// Write cache enable,
					// Read cache not disabled
					// No cache retention priorities
			put_be16((u8*)&buf[4], 0xffff);  // Don't disable prefetch
					// Minimum prefetch = 0
			put_be16((u8*)&buf[8], 0xffff);  // Maximum prefetch
			put_be16((u8*)&buf[10], 0xffff); // Maximum prefetch ceiling
		}
		buf += 12;
	}

	/* Check that a valid page was requested and the mode data length
	 * isn't too long. */
	len = buf - buf0;
	if (!valid_page || len > limit) {
		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
		return -EINVAL;
	}

	/*  Store the mode data length */
	if (mscmnd == SC_MODE_SENSE_6)
		buf0[0] = len - 1;
	else
		put_be16((u8*)buf0, len - 2);
	return len;
}

/*********************************************************************************/
static int do_prevent_allow(struct usb_msd_context *fsg)
{
	/* nothing to do */
	return 0;
}

/*********************************************************************************/
extern ulong mmc_read(ulong start, lbaint_t blkcnt, uchar *dst);

static int do_read(struct usb_msd_context *fsg, char * buf)
{
	struct lun		*curlun = fsg->curlun;
	u32			lba;
	int			rc;
	u32			amount_left;
	u64			file_offset;
	unsigned int		amount;
	/* Get the starting Logical Block Address and check that it's
	 * not too big */
	if (fsg->cmnd[0] == SC_READ_6)
		lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]);
	else {
		lba = get_be32(&fsg->cmnd[2]);

		/* We allow DPO (Disable Page Out = don't save data in the
		 * cache) and FUA (Force Unit Access = don't read from the
		 * cache), but we don't implement them. */
		if ((fsg->cmnd[1] & ~0x18) != 0) {
			curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
			error("Invalid field in cdb \n");
			return -EINVAL;
		}
	}
	if (lba >= (u32)curlun->num_sectors) {
		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
		error("Invalid LBA %d\n", lba);
		error("Max is %d\n", curlun->num_sectors);
		return -EINVAL;
	}
	file_offset =  (u64)((u64)lba << 9);
	debug("CMDR: LBA 0x%x size %u\n", lba,fsg->data_size_from_cmnd) ;
	/* Carry out the file reads */
	amount_left = fsg->data_size_from_cmnd;
	if (amount_left == 0){
		error("Data size is zero\n");
		return -EIO;		// No default reply
	}

	while (amount_left > 0) {

		/* Figure out how much we need to read:
		 * Try to read the remaining amount.
		 * But don't read more than the buffer size.
		 * And don't try to read past the end of the file.
		 */
		amount = min(amount_left, USBMSD_BUFFER_SIZE);

		/* block read alignment */
        if(amount % 512)
        	amount = (amount/512) * 512;

		/* If we were asked to read past the end of file,
		 * end with an empty buffer. */
		if (amount + file_offset > curlun->file_length) {
			curlun->sense_data =
					SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
			curlun->sense_data_info = file_offset >> 9;
			curlun->info_valid = 1;
			fsg->inreq.length = 0;
			error("Can't read more than the size of the card\n");
			error("am %d file offset %d file length %d\n",
					amount, (int)file_offset, (int)curlun->file_length);
			break;
		}

		/* Perform the read */
		debug("MMCR: addr 0x%x block size %d\n", 
				(u32)(file_offset>>9), amount>>9);
		rc = mmc_read((ulong)(file_offset>>9),amount >>9, (uchar*)buf);
		if (!rc) {
			error("error in file read: %d\n",
					rc);
			curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
			curlun->sense_data_info = file_offset >> 9;
			curlun->info_valid = 1;
			fsg->inreq.length = 0;
			break;
		} 
		file_offset  += amount;
		amount_left  -= amount;
		fsg->residue -= amount;
		fsg->inreq.length = amount;
		//debug("R done residue is %d\n", fsg->residue);

		/* Send this buffer and go read some more */
		/* last packet is sent in finish_reply */
		if(amount_left != 0)
			start_write(fsg);
	}

	return -EIO;		// No default reply
}
/*********************************************************************************/
static int do_read_capacity(struct usb_msd_context *fsg, char * buf)
{
	struct lun	*curlun = fsg->curlun;
	u32		lba = get_be32(&fsg->cmnd[2]);
	int		pmi = fsg->cmnd[8];

	/* Check the PMI and LBA fields */
	if (pmi > 1 || (pmi == 0 && lba != 0)) {
		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
		return -EINVAL;
	}

	put_be32((u8*)&buf[0], curlun->num_sectors - 1);	// Max logical block
	put_be32((u8*)&buf[4], 512);				// Block length
	return 8;
}

/*********************************************************************************/
static int do_read_format_capacities(struct usb_msd_context *fsg,
			char * buf)
{
	struct lun	*curlun = fsg->curlun;

	buf[0] = buf[1] = buf[2] = 0;
	buf[3] = 8;		// Only the Current/Maximum Capacity Descriptor
	buf += 4;

	put_be32((u8*)&buf[0], curlun->num_sectors);		// Number of blocks
	put_be32((u8*)&buf[4], 512);				// Block length
	buf[4] = 0x02;					// Current capacity
	return 12;
}

/*********************************************************************************/
static int do_start_stop(struct usb_msd_context *fsg)
{
	int start;

	/* this is non removable device
	 * this command should not come for 
	 * non removable device */
	start = fsg->cmnd[4] & 0x01;
	fsg->stopped = !start;	
	return 0;
}

/*********************************************************************************/

static int do_request_sense(struct usb_msd_context *fsg, char * buf)
{
	struct lun	*curlun = fsg->curlun;
	u32		sd, sdinfo;
	int		valid;

	if (!curlun) {		// Unsupported LUNs are okay
		fsg->bad_lun_okay = 1;
		sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
		sdinfo = 0;
		valid = 0;
	} else {
		sd = curlun->sense_data;
		sdinfo = curlun->sense_data_info;
		valid = curlun->info_valid << 7;
		curlun->sense_data = SS_NO_SENSE;
		curlun->sense_data_info = 0;
		curlun->info_valid = 0;
	}

	memset(buf, 0, 18);
	buf[0] = valid | 0x70;			// Valid, current error
	buf[2] = SK(sd);
	put_be32((u8*)&buf[3], sdinfo);		// Sense information
	buf[7] = 18 - 8;			// Additional sense length
	buf[12] = ASC(sd);
	buf[13] = ASCQ(sd);
	return 18;
}

/*********************************************************************************/
static int do_synchronize_cache(struct usb_msd_context *fsg)
{
	/* nothing to do */

	return 0;
}

/*********************************************************************************/
static int do_verify(struct usb_msd_context *fsg)
{
	struct lun		*curlun = fsg->curlun;
	u32			lba;
	u32			verification_length;
	/* Get the starting Logical Block Address and check that it's
	 * not too big */
	lba = get_be32(&fsg->cmnd[2]);
	if (lba >= (u32)curlun->num_sectors) {
		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
		return -EINVAL;
	}

	/* We allow DPO (Disable Page Out = don't save data in the
	 * cache) but we don't implement it. */
	if ((fsg->cmnd[1] & ~0x10) != 0) {
		curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
		return -EINVAL;
	}

	verification_length = get_be16(&fsg->cmnd[7]);
	if (verification_length == 0)
		return -EIO;		// No default reply

	return 0;
}

/*********************************************************************************/
extern ulong mmc_write(ulong start, lbaint_t blkcnt, const void *src);
static int do_write(struct usb_msd_context *fsg)
{
	struct lun		*curlun = fsg->curlun;
	u32			lba;
	u32			amount_left_to_req = 0, amount_left_to_write = 0;
	u64			file_offset = 0;
	unsigned int		amount;
	int			rc;
	u32			amount_read;
	u32 			timeout_counter;


	if (curlun->ro) {
		error("Write called for read only device\n");
		curlun->sense_data = SS_WRITE_PROTECTED;
		return -EINVAL;
	}
	/* Get the starting Logical Block Address and check that it's
	 * not too big */
	if (fsg->cmnd[0] == SC_WRITE_6)
		lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]);
	else {
		lba = get_be32(&fsg->cmnd[2]);
	}
	if (lba >= curlun->num_sectors) {
		error("LBA (%d) larger than num sectors %d\n", 
				lba, curlun->num_sectors);
		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
		return -EINVAL;
	}

	debug("CMDW: LBA 0x%x size %d\n", lba, fsg->data_size_from_cmnd);
	/* Carry out the file writes */
	file_offset =  (u64)((u64)lba << 9);
	amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd;

	while (amount_left_to_req > 0) {

		/* Queue a request for more data from the host */

		/* Figure out how much we want to get:
		 * Try to get the remaining amount.
		 * But don't get more than the buffer size.
		 * And don't try to go past the end of the file.
		 * Finally, round down to a block boundary. */

		amount = min(amount_left_to_req, USBMSD_BUFFER_SIZE);
		amount = min(amount, curlun->file_length - file_offset);
		if (amount + file_offset > curlun->file_length) {
			curlun->sense_data =
				SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
			curlun->sense_data_info = file_offset >> 9;
			curlun->info_valid = 1;
			error("offset %u larger than the size %u\n",
				       (int)(amount + file_offset),(int)curlun->file_length);
			break;
		}

		/* initiate read from host */
		timeout_counter = 1000000000;
		udc_endpoint_queue_read(&endpoint_instance[RX_ENDPOINT], amount);
		do {
			usbmsd_poll();
			amount_read = fsg->rx_buffer_data_size;
			timeout_counter--;
		}while(amount_read == 0 && timeout_counter>0 );
		
		/* timed out before completing data rx */
		if(amount_read == 0)
		{
			error("Timeout reading data\n");
			fsg->short_packet_received = 1;
			break;
		}

        if(amount % 512)
        	amount = (amount/512) * 512;

		debug("MMCW: addr 0x%x size %d\n", 
				(u32)(file_offset>>9), amount_read);
/*		rc = mmc_multi_write(file_offset,
				usb_rx_buffer, amount_read); 
*/
		rc = mmc_write(file_offset>>9, amount_read>>9, (const void *)usb_rx_buffer);
		if (!rc) {
			error("Error in mmc block write\n");
			curlun->sense_data = SS_WRITE_ERROR;
			curlun->sense_data_info = file_offset >> 9;
			curlun->info_valid = 1;
			break;
		}
		file_offset += amount_read;
		amount_left_to_req -= amount_read;

		fsg->residue -= amount_read;
		fsg->rx_buffer_data_size = 0;
	}
	return -EIO;		// No default reply
}


/*********************************************************************************/
static int do_scsi_command(struct usb_msd_context *fsg)
{
	int			i;
	static char		unknown[16];
	int			reply = -EINVAL;

	fsg->phase_error = 0;
	fsg->short_packet_received = 0;

	switch (fsg->cmnd[0]) {

	case SC_INQUIRY:
		fsg->data_size_from_cmnd = fsg->cmnd[4];
		if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
				(1<<4), 0,
				"INQUIRY")) == 0)
			reply = do_inquiry(fsg, usb_tx_buffer);
		break;

	case SC_MODE_SELECT_6:
		fsg->data_size_from_cmnd = fsg->cmnd[4];
		if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST,
				(1<<1) | (1<<4), 0,
				"MODE SELECT(6)")) == 0)
			reply = do_mode_select(fsg, usb_tx_buffer);
		break;

	case SC_MODE_SELECT_10:
		fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
		if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST,
				(1<<1) | (3<<7), 0,
				"MODE SELECT(10)")) == 0)
			reply = do_mode_select(fsg, usb_tx_buffer);
		break;

	case SC_MODE_SENSE_6:
		fsg->data_size_from_cmnd = fsg->cmnd[4];
		if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
				(1<<1) | (1<<2) | (1<<4), 0,
				"MODE SENSE(6)")) == 0)
			reply = do_mode_sense(fsg, usb_tx_buffer);
		break;

	case SC_MODE_SENSE_10:
		fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
		if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
				(1<<1) | (1<<2) | (3<<7), 0,
				"MODE SENSE(10)")) == 0)
			reply = do_mode_sense(fsg, usb_tx_buffer);
		break;

	case SC_PREVENT_ALLOW_MEDIUM_REMOVAL:
		fsg->data_size_from_cmnd = 0;
		if ((reply = check_command(fsg, 6, DATA_DIR_NONE,
				(1<<4), 0,
				"PREVENT-ALLOW MEDIUM REMOVAL")) == 0)
			reply = do_prevent_allow(fsg);
		break;

	case SC_READ_6:
		i = fsg->cmnd[4];
		fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9;
		if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
				(7<<1) | (1<<4), 1,
				"READ(6)")) == 0)
			reply = do_read(fsg, usb_tx_buffer);
		break;

	case SC_READ_10:
		fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]) << 9;
		if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
				(1<<1) | (0xf<<2) | (3<<7), 1,
				"READ(10)")) == 0)
			reply = do_read(fsg, usb_tx_buffer);
		break;

	case SC_READ_12:
		fsg->data_size_from_cmnd = get_be32(&fsg->cmnd[6]) << 9;
		if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST,
				(1<<1) | (0xf<<2) | (0xf<<6), 1,
				"READ(12)")) == 0)
			reply = do_read(fsg, usb_tx_buffer);
		break;

	case SC_READ_CAPACITY:
		fsg->data_size_from_cmnd = 8;
		if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
				(0xf<<2) | (1<<8), 1,
				"READ CAPACITY")) == 0)
			reply = do_read_capacity(fsg, usb_tx_buffer);
		break;

	case SC_READ_FORMAT_CAPACITIES:
		fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
		if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
				(3<<7), 1,
				"READ FORMAT CAPACITIES")) == 0)
			reply = do_read_format_capacities(fsg, usb_tx_buffer);
		break;

	case SC_REQUEST_SENSE:
		fsg->data_size_from_cmnd = fsg->cmnd[4];
		if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST,
				(1<<4), 0,
				"REQUEST SENSE")) == 0)
			reply = do_request_sense(fsg, usb_tx_buffer);
		break;

	case SC_START_STOP_UNIT:
		fsg->data_size_from_cmnd = 0;
		if ((reply = check_command(fsg, 6, DATA_DIR_NONE,
				(1<<1) | (1<<4), 0,
				"START-STOP UNIT")) == 0)
			reply = do_start_stop(fsg);
		break;

	case SC_SYNCHRONIZE_CACHE:
		fsg->data_size_from_cmnd = 0;
		if ((reply = check_command(fsg, 10, DATA_DIR_NONE,
				(0xf<<2) | (3<<7), 1,
				"SYNCHRONIZE CACHE")) == 0)
			reply = do_synchronize_cache(fsg);
		break;

	case SC_TEST_UNIT_READY:
		fsg->data_size_from_cmnd = 0;
		 if ((reply = check_command(fsg, 6, DATA_DIR_NONE,
                                0, 1,
                                "TEST UNIT READY")) == 0) {
			 if(fsg->stopped) {
                                fsg->luns[fsg->lun].sense_data = SS_MEDIUM_NOT_PRESENT;
				fsg->exit_flag = 1;
                                reply = -EINVAL;
                        }
                }
		break;

	/* Although optional, this command is used by MS-Windows.  We
	 * support a minimal version: BytChk must be 0. */
	case SC_VERIFY:
		fsg->data_size_from_cmnd = 0;
		if ((reply = check_command(fsg, 10, DATA_DIR_NONE,
				(1<<1) | (0xf<<2) | (3<<7), 1,
				"VERIFY")) == 0)
			reply = do_verify(fsg);
		break;
	case SC_WRITE_6:
		i = fsg->cmnd[4];
		fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9;
		if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST,
				(7<<1) | (1<<4), 1,
				"WRITE(6)")) == 0)
			reply = do_write(fsg);
		break;

	case SC_WRITE_10:
		fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]) << 9;
		if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST,
				(1<<1) | (0xf<<2) | (3<<7), 1,
				"WRITE(10)")) == 0)
			reply = do_write(fsg);
		break;

	case SC_WRITE_12:
		fsg->data_size_from_cmnd = get_be32(&fsg->cmnd[6]) << 9;
		if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST,
				(1<<1) | (0xf<<2) | (0xf<<6), 1,
				"WRITE(12)")) == 0)
			reply = do_write(fsg);
		break;
	/* Some mandatory commands that we recognize but don't implement.
	 * They don't mean much in this setting.  It's left as an exercise
	 * for anyone interested to implement RESERVE and RELEASE in terms
	 * of Posix locks. */
	case SC_FORMAT_UNIT:
	case SC_RELEASE:
	case SC_RESERVE:
	case SC_SEND_DIAGNOSTIC:
		// Fall through

	default:
		fsg->data_size_from_cmnd = 0;
		sprintf(unknown, "Unknown x%02x", fsg->cmnd[0]);
		if ((reply = check_command(fsg, fsg->cmnd_size,
				DATA_DIR_UNKNOWN, 0xff, 0, unknown)) == 0) {
			fsg->curlun->sense_data = SS_INVALID_COMMAND;
			reply = -EINVAL;
		}
		break;
	}

	if (reply == -EINTR )
		return -EINTR;

	/* Set up the single reply buffer for finish_reply() */
	if (reply == -EINVAL)
		reply = 0;		// Error reply length
	if (reply >= 0 && fsg->data_dir == DATA_DIR_TO_HOST) {
		reply = min((u32) reply, fsg->data_size_from_cmnd);
		fsg->inreq.length = reply;
		if(fsg->cmnd[0] == SC_MODE_SENSE_6) {
			fsg->residue = 0;
			return 0;
		}
		fsg->residue -= reply;
	}				// Otherwise it's already set

	return 0;
}

/*********************************************************************************/
static int finish_reply(struct usb_msd_context *fsg)
{
	int			rc = 0;

	switch (fsg->data_dir) {
	case DATA_DIR_NONE:
		break;			// Nothing to send
	case DATA_DIR_UNKNOWN:
		error("Unknown direction\n");
		break;
	/* All but the last buffer of data must have already been sent */
	case DATA_DIR_TO_HOST:
		if (fsg->data_size == 0)
			;		// Nothing to send

		/* If there's no residue, simply send the last buffer */
		else if (fsg->residue == 0) {
			start_write(fsg);
		}

		else {
			/* do nothing;error status will be sent */
			error("Error in transfer: residue %d\n", fsg->residue);
		}
		break;

	/* We have processed all we want from the data the host has sent.
	 * There may still be outstanding bulk-out requests. */
	case DATA_DIR_FROM_HOST:
		if (fsg->residue == 0)
			;		// Nothing to receive

		/* Did the host stop sending unexpectedly early? */
		else if (fsg->short_packet_received) {
			error("Received less than expected\n");
		}

		break;
	}
	if(rc)
		error("Returning %d\n", rc);
	return rc;
}

/*********************************************************************************/
static int send_status(struct usb_msd_context *fsg, char * buf)
{
	struct lun		*curlun = fsg->curlun;
	u8			status = USB_STATUS_PASS;
	u32			sd, sdinfo = 0;
	struct bulk_cs_wrap	*csw = (struct bulk_cs_wrap*)buf;

	if (curlun) {
		sd = curlun->sense_data;
		sdinfo = curlun->sense_data_info;
	} else if (fsg->bad_lun_okay)
		sd = SS_NO_SENSE;
	else
		sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;

	if (fsg->phase_error) {
		error( "sending phase-error status\n");
		status = USB_STATUS_PHASE_ERROR;
		sd = SS_INVALID_COMMAND;
	} else if (sd != SS_NO_SENSE) {
		error("sending command-failure status\n");
		status = USB_STATUS_FAIL;
		error("  sense data: SK x%02x, ASC x%02x, ASCQ x%02x;"
				"  info x%x\n",
				SK(sd), ASC(sd), ASCQ(sd), sdinfo);
	}

	/* Store and send the Bulk-only CSW */
	csw->Signature = __constant_cpu_to_le32(USB_BULK_CS_SIG);
	csw->Tag = fsg->tag;
	csw->Residue = cpu_to_le32(fsg->residue);
	csw->Status = status;

	fsg->inreq.length = USB_BULK_CS_WRAP_LEN;

	//debug("Sending status %d\n", status);
	start_write(fsg);

	return 0;
}

/*********************************************************************************/
/* Main thread for doing rx/tx called from do_usbmsd_export */
static int usb_msd_main_thread(struct usb_msd_context *fsg )
{
	/* The main loop */
	while ((fsg->exit_flag == 0) && usbmsd_configured()) {
		
		debug("%s\n", __func__);

		if (get_next_command(fsg))
			continue;

		if (do_scsi_command(fsg) || finish_reply(fsg))
			continue;

		if (send_status(fsg, usb_tx_buffer))
			continue;
	}
	return 0;
}

/*********************************************************************************/
static void usbmsd_event_handler (struct usb_device_instance *device,
				  usb_device_event_t event, int data)
{
	debug("Handling Event %d\n", event);
	switch (event) {
	case DEVICE_RESET:
		break;
	case DEVICE_BUS_INACTIVE:
		usbmsd_configured_flag = 0;
		break;
	case DEVICE_CONFIGURED:
		usbmsd_configured_flag = 1;
#if 0
		udc_endpoint_queue_read(&endpoint_instance[RX_ENDPOINT],
				USB_BULK_CB_WRAP_LEN);
#endif
		break;

	case DEVICE_ADDRESS_ASSIGNED:
		usbmsd_init_endpoints ();
		break;

	default:
		break;
	}
}

/*********************************************************************************/

static int usbmsd_class_setup(struct usb_device_request *request, struct urb *urb)
{
	debug("class request %d\n", request->bRequest);
	switch (request->bRequest){

	   	case USB_BULK_RESET_REQUEST:	/* FIXME */
	   		break;
	   	case USB_BULK_GET_MAX_LUN_REQUEST :	/* Required */
			*(u8*)(urb->buffer) = 0;
			urb->actual_length = 1;
			break;
	 	default:
			return -1;
	}
	return 0;
}

/*********************************************************************************/
static int usbmsd_setup_data(struct usb_device_request *request, void *buffer, u8 length)
{
	return 0;
}
/*********************************************************************************/

/*
 * Since interrupt handling has not yet been implemented, we use this function
 * to handle polling.  
 */
void usbmsd_poll (void)
{
	udc_irq();
}

/*********************************************************************************/
/* This function is called by the lower layer for passing
 * the data recevied from the host */
void usbmsd_receive(char * buf, int len)
{
	debug("pushing %d bytes of data\n", len);
	struct usb_msd_context *fsg = &g_usb_msd_context;
	usb_rx_buffer = buf;
	fsg->rx_buffer_data_size = len;
}

extern u32 mmc_get_capacity(void);
/*********************************************************************************/
void usb_msd_init_lun(struct usb_msd_context *fsg)
{
	fsg->luns[0].ro =0;
	fsg->luns[0].num_sectors = mmc_get_capacity();
	fsg->luns[0].file_length = (u64)(512*(u64)(fsg->luns[0].num_sectors));
	fsg->curlun = fsg->luns;
	fsg->nluns = 1;
	printf("Exporting SD/MMC Card with capacity %dMB\n",
			fsg->luns[0].num_sectors/(2*1024));
}

/*********************************************************************************/
int  usbmsd_init(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
	struct usb_msd_context *fsg = &g_usb_msd_context;
	
	usbmsd_udc_init();

    /* make sure the enumeration procedure done */
	while(!usbmsd_configured()) {
		usbmsd_poll();
	}
	debug("Starting USB MSD\n");
	usb_msd_init_lun(fsg);
	usb_msd_main_thread(fsg);
	debug("Exiting USB MSD\n");

	udc_disconnect();

	return 0;
}

/*********************************************************************************/
U_BOOT_CMD(
		usbmsdinit,     1,     1,      usbmsd_init, 
		"usbmsdinit      - Export MMC drive as MSD\n",
		"");

/*********************************************************************************/
