/*This file is prepared for Doxygen automatic documentation generation.*/
//! \file *********************************************************************
//!
//! \brief This file manages the mass storage task.
//!
//! - Compiler:           IAR EWAVR and GNU GCC for AVR
//! - Supported devices:  AT90USB1287, AT90USB1286, AT90USB647, AT90USB646
//!
//! \author               Atmel Corporation: http://www.atmel.com \n
//!                       Support and FAQ: http://support.atmel.no/
//!
//! ***************************************************************************

/* Copyright (c) 2009 Atmel Corporation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an Atmel
 * AVR product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE EXPRESSLY AND
 * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

//_____  I N C L U D E S ___________________________________________________

#include "config.h"
#include "conf_usb.h"
#include "storage_task.h"
#include "lib_mcu/usb/usb_drv.h"
#include "usb_descriptors.h"
#include "modules/usb/device_chap9/usb_standard_request.h"
#include "usb_specific_request.h"
#include "modules/scsi_decoder/scsi_decoder.h"
#include "modules/control_access/ctrl_access.h"


//_____ D E C L A R A T I O N S ____________________________________________



bit ms_data_direction;
static _MEM_TYPE_SLOW_ U8  dCBWTag[4];

extern _MEM_TYPE_SLOW_ U8  g_scsi_status;
extern _MEM_TYPE_FAST_ U32 g_scsi_data_remaining;
extern bit ms_multiple_drive;
extern _MEM_TYPE_SLOW_ U8  g_scsi_command[16];

extern U8   endpoint_status[MAX_EP_NB];

_MEM_TYPE_SLOW_ U8 usb_LUN;


void usb_mass_storage_cbw (void);
void usb_mass_storage_csw (void);

//!
//! @brief This function initializes the hardware/software ressources required for storage task.
//!
//!
//! @param none
//!
//! @return none
//!
//!/
void storage_task_init(void)
{
   Leds_init();
   Usb_enable_sof_interrupt();
   df_mem_init();    // Init the hw/sw ressources required to drive the DF.
}


//! @brief Entry point of the mass storage task management
//!
//! This function links the mass storage SCSI commands and the USB bus.
//!
//!
//! @param none
//!
//! @return none
void storage_task(void)
{
   if (Is_device_enumerated())
   {
      Usb_select_endpoint(EP_MS_OUT);
      if (Is_usb_receive_out())
      {
         usb_mass_storage_cbw();
         usb_mass_storage_csw();
      }
   }
}






//! @brief USB Command Block Wrapper (CBW) management
//!
//! This function decodes the CBW command and stores the SCSI command
//!
//! @warning Code:?? bytes (function code length)
//!
//! @param none
//!
//! @return none
void usb_mass_storage_cbw (void)
{
bit cbw_error;
U8  c;
U8  dummy;

   cbw_error = FALSE;
   Usb_select_endpoint(EP_MS_OUT);           //! check if dCBWSignature is correct
   if (0x55 != Usb_read_byte())
      { cbw_error = TRUE; } //! 'U'
   if (0x53 != Usb_read_byte())
      { cbw_error = TRUE; } //! 'S'
   if (0x42 != Usb_read_byte())
      { cbw_error = TRUE; } //! 'B'
   if (0x43 != Usb_read_byte())
      { cbw_error = TRUE; } //! 'C'
   if (cbw_error)
   {
      Usb_ack_receive_out();
      Usb_select_endpoint(EP_MS_IN);
      Usb_enable_stall_handshake();
      endpoint_status[(EP_MS_IN & MSK_EP_DIR)] = 0x01;
      return;
   }

   dCBWTag[0] = Usb_read_byte();             //! Store CBW Tag to be repeated in CSW
   dCBWTag[1] = Usb_read_byte();
   dCBWTag[2] = Usb_read_byte();
   dCBWTag[3] = Usb_read_byte();
   
   LSB0(g_scsi_data_remaining) = Usb_read_byte();
   LSB1(g_scsi_data_remaining) = Usb_read_byte();
   LSB2(g_scsi_data_remaining) = Usb_read_byte();
   LSB3(g_scsi_data_remaining) = Usb_read_byte();

   if (Usb_read_byte() != 0x00)              //! if (bmCBWFlags.bit7 == 1) {direction = IN}
   {
      Usb_set_ms_data_direction_in();
   }
   else
   {
      Usb_set_ms_data_direction_out();
   }

   usb_LUN = Usb_read_byte();

   if (!ms_multiple_drive)
   {
      usb_LUN = get_cur_lun();
   }

   dummy      = Usb_read_byte();                // dummy CBWCBLength read


   for (c=0; c<16; c++)                         // store scsi_command
   {
      g_scsi_command[c] = Usb_read_byte();
   }
   Usb_ack_receive_out();

   if (Is_usb_ms_data_direction_in())
   {
      Usb_select_endpoint(EP_MS_IN);
   }

   if (TRUE != scsi_decode_command())
   {
      U8 ep;
      Usb_enable_stall_handshake();
      if (Is_usb_ms_data_direction_in())
      {
         ep = (EP_MS_IN & MSK_EP_DIR);
      }else{
         ep = (EP_MS_OUT & MSK_EP_DIR);
      }
      endpoint_status[ep] = 0x01;
   }
}


//! @brief USB Command Status Wrapper (CSW) management
//!
//! This function sends the status in relation with the last CBW
//!
//!
//! @param none
//!
//! @return none
void usb_mass_storage_csw (void)
{
   Usb_select_endpoint(EP_MS_IN);
   while (Is_usb_endpoint_stall_requested())
   {
      Usb_select_endpoint(EP_CONTROL);
      if (Is_usb_receive_setup())       { usb_process_request(); }
      Usb_select_endpoint(EP_MS_IN);
   }

   Usb_select_endpoint(EP_MS_OUT);
   while (Is_usb_endpoint_stall_requested())
   {
      Usb_select_endpoint(EP_CONTROL);
      if (Is_usb_receive_setup())       { usb_process_request(); }
      Usb_select_endpoint(EP_MS_OUT);
   }


   Usb_select_endpoint(EP_MS_IN);
   while(!Is_usb_write_enabled())
   {
      if(!Is_usb_endpoint_enabled())   return; // USB Reset
   }
                                                         //! write CSW Signature
   Usb_write_byte(0x55);                                 //! 'U'
   Usb_write_byte(0x53);                                 //! 'S'
   Usb_write_byte(0x42);                                 //! 'B'
   Usb_write_byte(0x53);                                 //! 'S'
                                                         //! write stored CBW Tag
   Usb_write_byte(dCBWTag[0]);
   Usb_write_byte(dCBWTag[1]);
   Usb_write_byte(dCBWTag[2]);
   Usb_write_byte(dCBWTag[3]);
                                                         //! write data residue value
   Usb_write_byte( LSB0(g_scsi_data_remaining) );
   Usb_write_byte( LSB1(g_scsi_data_remaining) );
   Usb_write_byte( LSB2(g_scsi_data_remaining) );
   Usb_write_byte( LSB3(g_scsi_data_remaining) );

   //! write command status
   Usb_write_byte(g_scsi_status);                        //! 0 -> PASS, 1 -> FAIL

   Usb_send_in();
}
