blob: afbed87b4851b43eb78817dfb512a6cf0dbecd10 [file] [log] [blame]
/*
* Copyright (C) 2018 Synaptics Incorporated. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND
* SYNAPTICS EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE, AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY
* INTELLECTUAL PROPERTY RIGHTS. IN NO EVENT SHALL SYNAPTICS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, PUNITIVE, OR
* CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION WITH THE USE
* OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED AND
* BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF
* COMPETENT JURISDICTION DOES NOT PERMIT THE DISCLAIMER OF DIRECT
* DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' TOTAL CUMULATIVE LIABILITY
* TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. DOLLARS.
*/
#include "diag_common.h"
#include "diag_misc.h"
#include "Galois_memmap.h"
#include "soc.h"
#include "diag_gic.h"
#include "diag_cpu.h"
#include "diag_misc.h"
#include "global.h"
#include "usb_memmap.h"
#include "usb_typedefs.h"
#include "usb_mass_ufi.h"
#include "core.h"
static struct usb_mass_data usb_stor = {0};
static int g_udev_bulk_in_epnum = 1; // Mass Storage EP assignment
static int g_udev_bulk_out_epnum = 2; // Mass Storage EP assignment
static char *gp_mass_cdb_buff = (char *)USB_MASS_BULK_CDB_BASE;
static char *gp_mass_csw_buff = (char *)USB_MASS_BULK_CSW_BASE;
static char *gp_mass_data_buff = (char *)USB_MASS_BULK_DATA_BASE;
static int g_mass_lun = 0; // LUN for MASS Storage
static int g_mass_tag = 0;
static int g_mass_last_lba = 0; //Mass Storage last lba
static int g_usb_init = 0; //Indicate if usb is initialized in mass storage
int g_mass_blen = 512; // Mass Storage block len
int g_mass_blocking = 0;
int g_mass_storage_timeout = 0; // 1000 = 1 second, 0: no timeout
unsigned int g_usbTrCompleteCount = 0;
int g_usb_mass_isr_state = MASS_WAIT_NEW_CMD;
int g_usb_mass_data_dir;
int g_usb_mass_data_len;
char *g_usb_mass_data;
static void mymemcpy(char *d, char *s, int count)
{
while(count){
*d++ = *s++;
count--;
}
}
static unsigned int usb_mass_getmaxlun(int *maxlun)
{
struct usb_mass_data *us = &usb_stor;
int len;
int ret;
unsigned char *result = USB_memalloc(1);
len = urb_control(us->pusb_dev,
usb_rcvctrlpipe(us->pusb_dev, 0),
US_BBB_GET_MAX_LUN,
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
0, us->ifnum,
result, sizeof(char),
USB_CNTL_TIMEOUT * 5);
dbg_printf(PRN_RES,"Get Max LUN -> len = %d, result = %d\n", len, (int) *result);
g_mass_tag = 1; // Start Mass Storage CDW tag to 1
if (len > 0) {
*maxlun = *result;
ret = 0;
}else {
*maxlun = 0;
ret = -EINVAL;
}
USB_memfree(result);
return ret;
}
static int usb_mass_data_queue()
{
int res;
unsigned long pipe_bulk_in, pipe_bulk_out;
if (g_usb_mass_data_dir == USB_DIR_IN){
pipe_bulk_in = usb_rcvbulkpipe(pdev_usb, g_udev_bulk_in_epnum);
res = que_blk_msg(pdev_usb, pipe_bulk_in, g_usb_mass_data, g_usb_mass_data_len);
}
else{
pipe_bulk_out = usb_sndbulkpipe(pdev_usb, g_udev_bulk_out_epnum);
res = que_blk_msg(pdev_usb, pipe_bulk_out, g_usb_mass_data, g_usb_mass_data_len);
}
RETURN_ON_ERROR(res)
return 0;
}
static int usb_mass_status_queue()
{
unsigned long pipe_bulk_in;
int res;
pipe_bulk_in = usb_rcvbulkpipe(pdev_usb, g_udev_bulk_in_epnum);
res = que_blk_msg(pdev_usb, pipe_bulk_in, gp_mass_csw_buff, CSW_SIZE);
RETURN_ON_ERROR(res)
return 0;
}
void usb_mass_callback()
{
switch(g_usb_mass_isr_state){
case MASS_WAIT_CMD_DONE:
//Queue Data
dbg_disp("DATA PHASE\n");
usb_mass_data_queue();
g_usb_mass_isr_state = MASS_WAIT_DATA_DONE;
break;
case MASS_WAIT_DATA_DONE:
//Queue Status
dbg_disp("STATUS PHASE\n");
usb_mass_status_queue();
g_usb_mass_isr_state = MASS_WAIT_STATUS_DONE;
break;
case MASS_WAIT_STATUS_DONE:
dbg_disp("COMPLETE PHASE\n");
g_usbTrCompleteCount++; // Let MI know.
g_usb_mass_isr_state = MASS_WAIT_NEW_CMD;
break;
}
}
static int usb_mass_generic_queue(char *cdb, char *data, int data_len, int dir)
{
unsigned long pipe_bulk_out;
int res;
// Build pipe
pipe_bulk_out = usb_sndbulkpipe(pdev_usb, g_udev_bulk_out_epnum);
g_usb_mass_data_dir = dir;
g_usb_mass_data_len = data_len;
g_usb_mass_data = data;
mymemcpy(gp_mass_cdb_buff, cdb, CBW_SIZE);
dbg_dump_buffer((unsigned char *)gp_mass_cdb_buff,31);
g_usb_mass_isr_state = data_len ? MASS_WAIT_CMD_DONE:MASS_WAIT_DATA_DONE;
dbg_disp("COMMAND PHASE\n");
res = que_blk_msg(pdev_usb, pipe_bulk_out, gp_mass_cdb_buff, CBW_SIZE);
RETURN_ON_ERROR(res)
return 0;
}
static int usbmass_generic(char *cdb, char *data, int data_len, int dir)
{
unsigned long pipe_bulk_in, pipe_bulk_out;
int res;
if (g_mass_blocking)
{
// Build pipe
pipe_bulk_in = usb_rcvbulkpipe(pdev_usb, g_udev_bulk_in_epnum);
pipe_bulk_out = usb_sndbulkpipe(pdev_usb, g_udev_bulk_out_epnum);
//Command Phase
mymemcpy(gp_mass_cdb_buff, cdb, CBW_SIZE);
dbg_out("CDB[");dbg_outdw((unsigned long)gp_mass_cdb_buff);dbg_out("]");
dbg_dump_buffer((unsigned char *)gp_mass_cdb_buff,31);
res = snd_blk_msg(pdev_usb, pipe_bulk_out, gp_mass_cdb_buff, CBW_SIZE);
RETURN_ON_ERROR(res)
//Data Phase if data_len>0
if (data_len){
if (dir == USB_DIR_IN){
res = snd_blk_msg(pdev_usb, pipe_bulk_in, data, data_len);
RETURN_ON_ERROR(res)
dbg_out("POST DATA IN[");dbg_outdw((unsigned long)data);dbg_out("]");
dbg_dump_buffer((unsigned char *)data,min(data_len,8));
if (data_len>=16){
dbg_out("POST DATA IN[");dbg_outdw((unsigned long)(data+data_len-8));dbg_out("]");
dbg_dump_buffer((unsigned char *)(data+data_len-8),8);
}
}
else{
dbg_out("DATA_OUT["); dbg_outdw((unsigned long)data); dbg_out(",");dbg_outdw((unsigned int)data_len);
dbg_dump_buffer((unsigned char *)data,min(data_len,16));
res = snd_blk_msg(pdev_usb, pipe_bulk_out, data, data_len);
RETURN_ON_ERROR(res)
}
}
//Status phase
res = snd_blk_msg(pdev_usb, pipe_bulk_in, gp_mass_csw_buff, CSW_SIZE);
RETURN_ON_ERROR(res)
dbg_out("POST CSW[");dbg_outdw((unsigned long)gp_mass_csw_buff); dbg_out("]");
dbg_dump_buffer((unsigned char*)gp_mass_csw_buff,CSW_SIZE);
}
else{
unsigned int tr_compl_count_pre = g_usbTrCompleteCount;
int timeout=g_mass_storage_timeout;
usb_mass_generic_queue( cdb, data, data_len, dir);
while(g_usbTrCompleteCount==tr_compl_count_pre){
// To Do. Implement some kind of Time out here in case of error.
dbg_printf(PRN_INFO,".");
mdelay(1);
if ((!timeout) && g_mass_storage_timeout ){
dbg_printf(PRN_RES,"ERR: Mass Storage Timeout!\n");
return 1;
}
if (g_mass_storage_timeout)
timeout--;
}
}
return 0;
}
int usb_mass_ufi_generic
(
/* [IN] command object allocated by application*/
uint_8 opcode,
uint_8 lun,
uint_32 lbaddr,
uint_32 blen,
uint_8 cbwflags,
uchar_ptr buf,
uint_32 buf_len
)
{ /* Body */
//int status = USB_OK;
CBW_STRUCT *cbw_ptr = (CBW_STRUCT *)gp_mass_cdb_buff;
UFI_CBWCB_EXTENDED *ufi_ptr = (UFI_CBWCB_EXTENDED *)cbw_ptr->CBWCB;
memset(gp_mass_cdb_buff, 0, CBW_SIZE);
/* Construct UFI command buffer */
ufi_ptr->BUFIOPCODE = opcode;
ufi_ptr->BUFILUN = lun;
utou32swap(ufi_ptr->BUFILOGICALBLOCKADDRESS, lbaddr);
utou32swap(ufi_ptr->BLENGTH, blen);
/* Construct CBW fields (sig and tag will be filled up by class driver)*/
utou32(cbw_ptr->DCBWDATALENGTH, buf_len);
cbw_ptr->BMCBWFLAGS = cbwflags;
transfer_low_nibble(g_mass_lun, cbw_ptr->BCBWCBLUN);
transfer_low_nibble(sizeof(UFI_CBWCB_EXTENDED),
cbw_ptr->BCBWCBLENGTH);
utou32(cbw_ptr->DCBWSIGNATURE, CBW_SIGNATURE);
utou32(cbw_ptr->DCBWTAG, g_mass_tag++); // increment mass storage command tag here
return usbmass_generic((char*) cbw_ptr, (char *) buf, buf_len, cbwflags);
} /* Endbody */
void usb_mass_parse_capacity(char *b, int *last , int *len)
{
unsigned long last_lba, blk_len;
last_lba = (((unsigned long)b[0]) << 24) |
(((unsigned long)b[1]) << 16) |
(((unsigned long)b[2]) << 8) |
(((unsigned long)b[3]) & 0x0FF);
blk_len = (((unsigned long)b[4]) << 24) |
(((unsigned long)b[5]) << 16) |
(((unsigned long)b[6]) << 8) |
(((unsigned long)b[7]) & 0x0FF);
*last = last_lba;
*len = blk_len;
dbg_printf(PRN_RES,"USB MASS STORAGE INFO: LAST_LBA=0x%08lX BLK_LEN=%d bytes\n", last_lba, blk_len);
}
int usb_mass_probe(void)
{
struct usb_interface *iface;
int i;
struct usb_endpoint_descriptor *ep_desc;
//unsigned int flags = 0;
struct usb_device *dev = pdev_usb;
struct usb_mass_data *ss = &usb_stor;
unsigned int ifnum = 0;
/* let's examine the device now */
iface = &dev->config.if_desc[ifnum];
if (dev->descriptor.bDeviceClass != 0 ||
iface->desc.bInterfaceClass != USB_CLASS_MASS_STORAGE ||
iface->desc.bInterfaceSubClass < US_SC_MIN ||
iface->desc.bInterfaceSubClass > US_SC_MAX) {
debug("Not mass storage\n");
/* if it's not a mass storage, we go no further */
return 0;
}
memset(ss, 0, sizeof(struct usb_mass_data));
/* At this point, we know we've got a live one */
debug("\n\nUSB Mass Storage device detected\n");
/* Initialize the us_data structure with some useful info */
ss->ifnum = ifnum;
ss->pusb_dev = dev;
/*
* We are expecting a minimum of 2 endpoints - in and out (bulk).
* An optional interrupt is OK (necessary for CBI protocol).
* We will ignore any others.
*/
for (i = 0; i < iface->desc.bNumEndpoints; i++) {
ep_desc = &iface->ep_desc[i];
/* is it an BULK endpoint? */
if ((ep_desc->bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
if (ep_desc->bEndpointAddress & USB_DIR_IN)
ss->ep_in = ep_desc->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
else
ss->ep_out =
ep_desc->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
}
/* is it an interrupt endpoint? */
if ((ep_desc->bmAttributes &
USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
ss->ep_int = ep_desc->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
ss->irqinterval = ep_desc->bInterval;
}
}
debug("Endpoints In %d Out %d Int %d\n",
ss->ep_in, ss->ep_out, ss->ep_int);
g_udev_bulk_in_epnum = ss->ep_in;
g_udev_bulk_out_epnum = ss->ep_out;
return set_interface(dev, iface->desc.bInterfaceNumber, 0);
}
int usb_mass_start()
{
int res;
static int mass_started = 0;
if (!mass_started){
mass_started = 1;
// Dynamically allocate
gp_mass_cdb_buff = USB_memalloc(64);
gp_mass_csw_buff = USB_memalloc(64);
gp_mass_data_buff = USB_memalloc(USB_MASS_BULK_DATA_SIZE); //2MB
}
dbg_printf(PRN_RES,"\t--SET_INTERFACE..");
res = usb_mass_probe();
RETURN_ON_ERROR(res);
dbg_printf(PRN_RES,"OK\n");
dbg_printf(PRN_RES,"\t--GET_LUN..");
res = usb_mass_getmaxlun(&g_mass_lun);
RETURN_ON_ERROR(res);
dbg_printf(PRN_RES,"got:%d .. OK\n",g_mass_lun);
// TEST UNIT READY
dbg_printf(PRN_RES,"\t--TEST_UNIT_READY..");
res = usb_mass_ufi_test_unit_ready();
RETURN_ON_ERROR(res);
dbg_printf(PRN_RES,"OK\n");
USB_MASS_SENSE
// READ CAPACITY to get last_lba and blk_len, before we do any READ /WRITE block based.
dbg_printf(PRN_RES,"\t--READ_CAPACITY..");
res = usb_mass_ufi_read_capacity((uchar_ptr)gp_mass_data_buff, sizeof(MASS_STORAGE_READ_CAPACITY_CMD_STRUCT_INFO));
RETURN_ON_ERROR(res);
dbg_printf(PRN_RES,"OK\n");
//now parse the result to extract important mass storage information.
usb_mass_parse_capacity(gp_mass_data_buff, &g_mass_last_lba, &g_mass_blen);
USB_MASS_SENSE
// READ10
memset(gp_mass_data_buff,0,g_mass_blen); // SET read buffer to 0
dbg_printf(PRN_RES,"\t--READ10..");
res = usb_mass_ufi_read_10( 8, (uchar_ptr) gp_mass_data_buff, g_mass_blen , 1);
RETURN_ON_ERROR(res);
dbg_printf(PRN_RES,"OK\n");
// WRITE10
dbg_printf(PRN_RES,"\t--WRITE10..");
#if PLATFORM == VELOCE //we don't use File System in mass storage
// Initialize input buffer.
{ int i;
unsigned char *pb= (uchar_ptr)gp_mass_data_buff;
for(i=0;i<g_mass_blen;i++) *pb++=i&0x0ff;
}
res = usb_mass_ufi_write_10(8, (uchar_ptr)gp_mass_data_buff, g_mass_blen, 1);
RETURN_ON_ERROR(res);
dbg_printf(PRN_RES,"OK\n");
#else
dbg_printf(PRN_RES,"SKIPPED\n");
#endif
// INQUIRY
dbg_printf(PRN_RES,"\t--INQUIRY..");
res = usb_mass_ufi_inquiry((uchar_ptr)gp_mass_data_buff, sizeof(INQUIRY_DATA_FORMAT));
RETURN_ON_ERROR(res);
dbg_printf(PRN_RES,"OK\n");
// PREVENT ALLOW MEDIUM REMOVAL
dbg_printf(PRN_RES,"\t--PREVENT MEDIUM REMOVAL..");
res = usb_mass_ufi_prevent_allow_medium_removal(1); //prevent
RETURN_ON_ERROR(res);
dbg_printf(PRN_RES,"OK\n");
g_usb_init = 1;
return 0;
}
int usb_storage_init()
{
int res;
if (g_usb_init)
return 0;
res = usb_mass_start();
RETURN_ON_ERROR(res);
return 0;
}
int usb_storage_read(unsigned char *buff,int lba, int blk_count)
{
int res;
int bc,n;
res = usb_storage_init();
RETURN_ON_ERROR(res);
bc = 0;
while(bc < blk_count){
n = min(256,(blk_count-bc));
res = usb_mass_ufi_read_10( lba+bc, (uchar_ptr)buff+bc*g_mass_blen, n* g_mass_blen, n);
RETURN_ON_ERROR(res);
bc += n;
}
return 0;
}
int usb_storage_write(unsigned char *buff, int lba, int blk_count)
{
int res;
int bc,n;
bc = 0;
res = usb_storage_init();
RETURN_ON_ERROR(res);
while(bc < blk_count){
n = min(256,(blk_count-bc));
res = usb_mass_ufi_write_10(lba+bc, (uchar_ptr)buff+bc*g_mass_blen, n* g_mass_blen, n);
RETURN_ON_ERROR(res);
bc+= n;
}
return 0;
}