| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859 |
- /*
- * Copyright : (C) 2022 Phytium Information Technology, Inc.
- * All Rights Reserved.
- *
- * This program is OPEN SOURCE software: you can redistribute it and/or modify it
- * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd,
- * either version 1.0 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 Phytium Public License for more details.
- *
- *
- * FilePath: fusb_msc.c
- * Date: 2022-02-11 13:33:09
- * LastEditTime: 2022-02-17 17:49:43
- * Description: This files is for implementation of USB mass storage function
- *
- * Modify History:
- * Ver Who Date Changes
- * ----- ------ -------- --------------------------------------
- * 1.0 Zhugengyu 2022/2/7 init commit
- */
- #include <string.h>
- #include "fswap.h"
- #include "fsleep.h"
- #include "fassert.h"
- #include "fgeneric_timer.h"
- #include "fdebug.h"
- #include "fusb.h"
- #include "fusb_msc.h"
- #define FUSB_DEBUG_TAG "FUSB_MSC"
- #define FUSB_ERROR(format, ...) FT_DEBUG_PRINT_E(FUSB_DEBUG_TAG, format, ##__VA_ARGS__)
- #define FUSB_WARN(format, ...) FT_DEBUG_PRINT_W(FUSB_DEBUG_TAG, format, ##__VA_ARGS__)
- #define FUSB_INFO(format, ...) FT_DEBUG_PRINT_I(FUSB_DEBUG_TAG, format, ##__VA_ARGS__)
- #define FUSB_DEBUG(format, ...) FT_DEBUG_PRINT_D(FUSB_DEBUG_TAG, format, ##__VA_ARGS__)
- static inline tick_t FUsbMscGetTick(void)
- {
- return GenericTimerRead();
- }
- static inline tick_t FUsbMscStartTick(void)
- {
- GenericTimerStart();
- return FUsbMscGetTick();
- }
- static inline void FUsbMscStopTick(void)
- {
- GenericTimerStop();
- }
- static inline boolean FUsbMscTimeout(tick_t start_tick, tick_t timeout_tick)
- {
- return (FUsbMscGetTick() - start_tick) > timeout_tick;
- }
- static void FUsbMassStorageForceInit(FUsbDev *dev, u32 quirks);
- static int FUsbMscRwBlks(FUsbDev *dev, int start, int n, FUsbMassStorageDirection dir, u8 *buf);
- static const char *FUsbMscSubClassString[7] =
- {
- "(none)",
- "RBC",
- "MMC-2",
- "QIC-157",
- "UFI",
- "SFF-8070i",
- "SCSI transparent"
- };
- static const char *FUsbMscProtocolStrings[0x51] =
- {
- "Control/Bulk/Interrupt protocol (with command completion interrupt)",
- "Control/Bulk/Interrupt protocol (with no command completion interrupt)",
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- "Bulk-Only Transport"
- };
- /**
- * @name: FUsbMscCreateDisk
- * @msg: 调用应用程序实现的钩子函数,创建USB Disk
- * @return {*}
- * @param {FUsbDev} *dev, USB大容量存储设备实例
- */
- static void FUsbMscCreateDisk(FUsbDev *dev)
- {
- FASSERT(dev);
- if (FUsbDiskCreate)
- {
- FUsbDiskCreate(dev);
- MSC_INST(dev)->usbdisk_created = 1;
- }
- return;
- }
- /**
- * @name: FUsbMscRemoveDisk
- * @msg: 调用应用程序实现的钩子函数,移除USB Disk
- * @return {*}
- * @param {FUsbDev} *dev, USB大容量存储设备实例
- */
- static void FUsbMscRemoveDisk(FUsbDev *dev)
- {
- FASSERT(dev);
- if (MSC_INST(dev)->usbdisk_created && FUsbDiskRemove)
- FUsbDiskRemove(dev);
- return;
- }
- /**
- * @name: FUsbMscDestory
- * @msg: 移除USB大容量存储设备实例
- * @return {*}
- * @param {FUsbDev} *dev, USB大容量存储设备实例
- */
- static void FUsbMscDestory(FUsbDev *dev)
- {
- FASSERT(dev && dev->controller);
- FUsb *instance = dev->controller->usb;
- FASSERT(instance);
- if (NULL != dev->data)
- {
- FUsbMscRemoveDisk(dev);
- FUSB_FREE(instance, dev->data);
- }
- dev->data = NULL;
- return;
- }
- const int DEV_RESET = 0xff;
- const int GET_MAX_LUN = 0xfe;
- /* Many USB3 devices do not work with large transfer requests.
- * Limit the request size to 64KB chunks to ensure maximum compatibility. */
- const int MAX_CHUNK_BYTES = 1024 * 64;
- const unsigned int cbw_signature = 0x43425355; /* according to USB mass-bulk spec. helps to identify data packets */
- const unsigned int csw_signature = 0x53425355;
- /* following data structure following name convention in USB mass-bulk spec., which may not compiliance to other code */
- /* A packet containing a command block and associated information. */
- typedef struct
- {
- unsigned int dCBWSignature;
- unsigned int dCBWTag;
- unsigned int dCBWDataTransferLength;
- unsigned char bmCBWFlags;
- unsigned long bCBWLUN : 4; /* device Logical Unit Number (LUN) */
- unsigned long : 4;
- unsigned long bCBWCBLength : 5;
- unsigned long : 3;
- unsigned char CBWCB[31 - 15];
- } __attribute__((packed)) FUsbMscCbw; /* Command Block Wrapper (CBW) */
- /* A packet containing the status of a command block */
- typedef struct
- {
- unsigned int dCSWSignature;
- unsigned int dCSWTag;
- unsigned int dCSWDataResidue;
- unsigned char bCSWStatus;
- } __attribute__((packed)) FUsbMscCsw;
- enum
- {
- /*
- * MSC commands can be
- * successful,
- * fail with proper response or
- * fail totally, which results in detaching of the USB device
- * and immediate cleanup of the FUsbDev structure.
- * In the latter case the caller has to make sure, that he won't
- * use the device any more.
- */
- MSC_COMMAND_OK = 0,
- MSC_COMMAND_FAIL,
- MSC_COMMAND_DETACHED
- };
- static int FUsbMscRequestSense(FUsbDev *dev);
- static int FUsbMscRequestNoMedia(FUsbDev *dev);
- static void FUsbMscPoll(FUsbDev *dev);
- static int FUsbClearStall(FUsbEndpoint *ep)
- {
- FASSERT(ep);
- int ret = FUsbClearFeature(ep->dev, ep->endpoint, FUSB_ENDPOINT_HALT,
- FUsbGenerateReqType(FUSB_REQ_HOST_TO_DEVICE, FUSB_REQ_TYPE_STANDARD, FUSB_REQ_RECP_EP));
- ep->toggle = 0;
- return ret;
- }
- static int FUsbMscResetTransport(FUsbDev *dev)
- {
- FUsbDevReq dr;
- memset(&dr, 0, sizeof(dr));
- dr.bmRequestType = 0;
- dr.data_dir = FUSB_REQ_HOST_TO_DEVICE;
- dr.req_type = FUSB_REQ_TYPE_CLASS;
- dr.req_recp = FUSB_REQ_RECP_IF;
- dr.bRequest = DEV_RESET;
- dr.wValue = 0;
- dr.wIndex = 0;
- dr.wLength = 0;
- if (MSC_INST(dev)->quirks & FUSB_MSC_QUIRK_NO_RESET)
- return MSC_COMMAND_FAIL;
- /* if any of these fails, detach device, as we are lost */
- if (dev->controller->control(dev, FUSB_OUT, sizeof(dr), &dr, 0, 0) < 0 ||
- FUsbClearStall(MSC_INST(dev)->bulk_in) ||
- FUsbClearStall(MSC_INST(dev)->bulk_out))
- {
- FUSB_INFO("Detaching unresponsive device. ");
- FUsbDetachDev(dev->controller, dev->address);
- return MSC_COMMAND_DETACHED;
- }
- /* return fail as we are only called in case of failure */
- return MSC_COMMAND_FAIL;
- }
- /* device may stall this command, so beware! */
- static void FUsbMscInitLuns(FUsbDev *dev)
- {
- FUsbMassStorage *msc = MSC_INST(dev);
- FUsbDevReq dr;
- dr.bmRequestType = 0;
- dr.data_dir = FUSB_REQ_DEVICE_TO_HOST;
- dr.req_type = FUSB_REQ_TYPE_CLASS;
- dr.req_recp = FUSB_REQ_RECP_IF;
- dr.bRequest = GET_MAX_LUN;
- dr.wValue = 0;
- dr.wIndex = 0;
- dr.wLength = 1;
- /* send class-spefic request Get Max Lun */
- if ((MSC_INST(dev)->quirks & FUSB_MSC_QUIRK_NO_LUNS) ||
- (dev->controller->control(dev, FUSB_IN, sizeof(dr), &dr,
- sizeof(msc->num_luns), &msc->num_luns) < FUSB_CC_ZERO_BYTES))
- {
- msc->num_luns = 0; /* assume only 1 lun if req fails */
- }
- msc->num_luns++; /* Get Max LUN returns number of last LUN */
- msc->lun = 0;
- return;
- }
- unsigned int tag;
- static void FUsbMscWrapCbw(FUsbMscCbw *cbw, int datalen, FUsbMassStorageDirection dir, const u8 *cmd,
- int cmdlen, u8 lun)
- {
- memset(cbw, 0, sizeof(FUsbMscCbw));
- /* commands are typically shorter, but we don't want overflows */
- if ((size_t)cmdlen > sizeof(cbw->CBWCB))
- {
- cmdlen = (int)sizeof(cbw->CBWCB);
- }
- cbw->dCBWSignature = cbw_signature;
- cbw->dCBWTag = ++tag; /* command block tag to device */
- cbw->bCBWLUN = lun; /* logic unit number to send */
- cbw->dCBWDataTransferLength = datalen; /* number of bytes of data expect to transfer */
- cbw->bmCBWFlags = dir;
- memcpy(cbw->CBWCB, cmd, cmdlen); /* the command block to be executed by the device */
- cbw->bCBWCBLength = cmdlen; /* the valid length of the CBWCB in bytes */
- return;
- }
- static int FUsbMscGetCsw(FUsbEndpoint *ep, FUsbMscCsw *csw)
- {
- FUsbHc *ctrlr = ep->dev->controller;
- int ret = ctrlr->bulk(ep, sizeof(FUsbMscCsw), (u8 *)csw, 1);
- /* Some broken sticks send a zero-length packet at the end of their data
- transfer which would show up here. Skip it to get the actual CSW. */
- if (ret == 0)
- ret = ctrlr->bulk(ep, sizeof(FUsbMscCsw), (u8 *)csw, 1);
- if (ret < 0)
- {
- FUsbClearStall(ep);
- ret = ctrlr->bulk(ep, sizeof(FUsbMscCsw), (u8 *)csw, 1);
- if (ret < 0)
- return FUsbMscResetTransport(ep->dev);
- }
- if (ret != sizeof(FUsbMscCsw) || csw->dCSWTag != tag ||
- csw->dCSWSignature != csw_signature)
- {
- FUSB_INFO("MSC: received malformed CSW ");
- return FUsbMscResetTransport(ep->dev);
- }
- return MSC_COMMAND_OK;
- }
- static int FUsbMscExecCmd(FUsbDev *dev, FUsbMassStorageDirection dir, const u8 *cb, int cblen,
- u8 *buf, int buflen, int residue_ok)
- {
- FUsbMscCbw cbw;
- FUsbMscCsw csw;
- int always_succeed = 0;
- if ((cb[0] == 0x1b) && (cb[4] == 1)) /* check if it is Bulk-Only Mass Storage Reset request with reques type 00100001b */
- {
- /* start command, always succeed */
- always_succeed = 1;
- }
- FUsbMscWrapCbw(&cbw, buflen, dir, cb, cblen, MSC_INST(dev)->lun);
- if (dev->controller->bulk(MSC_INST(dev)->bulk_out, sizeof(cbw), (u8 *)&cbw, 0) < 0)
- {
- return FUsbMscResetTransport(dev);
- }
- if (buflen > 0)
- {
- if (dir == FUSB_DIR_DATA_IN)
- {
- if (dev->controller->bulk(MSC_INST(dev)->bulk_in, buflen, buf, 0) < 0)
- FUsbClearStall(MSC_INST(dev)->bulk_in);
- }
- else
- {
- if (dev->controller->bulk(MSC_INST(dev)->bulk_out, buflen, buf, 0) < 0)
- FUsbClearStall(MSC_INST(dev)->bulk_out);
- }
- }
- int ret = FUsbMscGetCsw(MSC_INST(dev)->bulk_in, &csw);
- if (ret)
- {
- return ret;
- }
- else if (always_succeed == 1)
- {
- /* return success, regardless of message */
- return MSC_COMMAND_OK;
- }
- else if (csw.bCSWStatus == 2)
- {
- /* phase error, reset transport */
- return FUsbMscResetTransport(dev);
- }
- else if (csw.bCSWStatus == 0)
- {
- if ((csw.dCSWDataResidue == 0) || residue_ok)
- /* no error, exit */
- return MSC_COMMAND_OK;
- else
- /* missed some bytes */
- return MSC_COMMAND_FAIL;
- }
- else
- {
- if (cb[0] == 0x03)
- /* requesting sense failed, that's bad */
- return MSC_COMMAND_FAIL;
- else if (cb[0] == 0)
- /* If command was TEST UNIT READY determine if the
- * device is of removable type indicating no media
- * found. */
- return FUsbMscRequestNoMedia(dev);
- /* error "check condition" or reserved error */
- ret = FUsbMscRequestSense(dev);
- /* return fail or the status of FUsbMscRequestSense if it's worse */
- return ret ? ret : MSC_COMMAND_FAIL;
- }
- }
- typedef struct
- {
- unsigned char command; /* 0 */
- unsigned char res1; /* 1 */
- unsigned int block; /* 2-5 */
- unsigned char res2; /* 6 */
- unsigned short numblocks; /* 7-8 */
- unsigned char control; /* 9 - the block is 10 bytes long */
- } __attribute__((packed)) FUsbMscCmdBlk;
- typedef struct
- {
- unsigned char command; /* 0 */
- unsigned char res1; /* 1 */
- unsigned char res2; /* 2 */
- unsigned char res3; /* 3 */
- union
- {
- /* 4 */
- struct
- {
- unsigned long start : 1; /* for START STOP UNIT */
- unsigned long : 7;
- };
- unsigned char length; /* for REQUEST SENSE */
- };
- unsigned char control; /* 5 */
- } __attribute__((packed)) FUsbMscCmdBlk6;
- /**
- * Like FUsbMscRwBlks, but for soft-sectors of 512b size. Converts the
- * start and count from 512b units.
- * Start and count must be aligned so that they match the native
- * sector size.
- *
- */
- /**
- * @name: FUsbMscRwBlk512
- * @msg: 读写USB大容量存储设备,以512字节为一块
- * @param dev device to access
- * @param start first sector to access
- * @param n number of sectors to access
- * @param dir direction of access: FUSB_DIR_DATA_IN == read, FUSB_DIR_DATA_OUT == write
- * @param buf buffer to read into or write from. Must be at least n*512 bytes
- * @return 0 on success, 1 on failure
- */
- int FUsbMscRwBlk512(FUsbDev *dev, int start, int n,
- FUsbMassStorageDirection dir, u8 *buf)
- {
- int blocksize_divider = MSC_INST(dev)->blocksize / 512;
- return FUsbMscRwBlks(dev, start / blocksize_divider,
- n / blocksize_divider, dir, buf);
- }
- /**
- * Reads or writes a number of sequential blocks on a USB storage device.
- * As it uses the READ(10) SCSI-2 command, it's limited to storage devices
- * of at most 2TB. It assumes sectors of 512 bytes.
- *
- * @param dev device to access
- * @param start first sector to access
- * @param n number of sectors to access
- * @param dir direction of access: FUSB_DIR_DATA_IN == read, FUSB_DIR_DATA_OUT == write
- * @param buf buffer to read into or write from. Must be at least n*sectorsize bytes
- * @return 0 on success, 1 on failure
- */
- static int FUsbMscRwChunk(FUsbDev *dev, int start, int n, FUsbMassStorageDirection dir, u8 *buf)
- {
- FUsbMscCmdBlk cb;
- memset(&cb, 0, sizeof(cb));
- if (dir == FUSB_DIR_DATA_IN)
- {
- /* read */
- cb.command = 0x28;
- }
- else
- {
- /* write */
- cb.command = 0x2a;
- }
- cb.block = htonl(start);
- cb.numblocks = htons(n);
- return FUsbMscExecCmd(dev, dir, (u8 *)&cb, sizeof(cb), buf,
- n * MSC_INST(dev)->blocksize, 0) != MSC_COMMAND_OK
- ? 1
- : 0;
- }
- /**
- * Reads or writes a number of sequential blocks on a USB storage device
- * that is split into MAX_CHUNK_BYTES size requests.
- *
- * As it uses the READ(10) SCSI-2 command, it's limited to storage devices
- * of at most 2TB. It assumes sectors of 512 bytes.
- *
- * @param dev device to access
- * @param start first sector to access
- * @param n number of sectors to access
- * @param dir direction of access: FUSB_DIR_DATA_IN == read,
- * FUSB_DIR_DATA_OUT == write
- * @param buf buffer to read into or write from.
- * Must be at least n*sectorsize bytes
- * @return 0 on success, 1 on failure
- */
- static int FUsbMscRwBlks(FUsbDev *dev, int start, int n, FUsbMassStorageDirection dir, u8 *buf)
- {
- int chunk_size = MAX_CHUNK_BYTES / MSC_INST(dev)->blocksize;
- int chunk;
- /* Read as many full chunks as needed. */
- for (chunk = 0; chunk < (n / chunk_size); chunk++)
- {
- if (FUsbMscRwChunk(dev, start + (chunk * chunk_size),
- chunk_size, dir,
- buf + (chunk * MAX_CHUNK_BYTES)) != MSC_COMMAND_OK)
- return 1;
- }
- /* Read any remaining partial chunk at the end. */
- if (n % chunk_size)
- {
- if (FUsbMscRwChunk(dev, start + (chunk * chunk_size),
- n % chunk_size, dir,
- buf + (chunk * MAX_CHUNK_BYTES)) != MSC_COMMAND_OK)
- return 1;
- }
- return 0;
- }
- /* Only request it, we don't interpret it.
- On certain errors, that's necessary to get devices out of
- a special state called "Contingent Allegiance Condition" */
- static int FUsbMscRequestSense(FUsbDev *dev)
- {
- u8 buf[19];
- FUsbMscCmdBlk6 cb;
- memset(&cb, 0, sizeof(cb));
- cb.command = 0x3;
- cb.length = sizeof(buf);
- return FUsbMscExecCmd(dev, FUSB_DIR_DATA_IN, (u8 *)&cb,
- sizeof(cb), buf, sizeof(buf), 1);
- }
- static int FUsbMscRequestNoMedia(FUsbDev *dev)
- {
- u8 buf[19];
- int ret;
- FUsbMscCmdBlk6 cb;
- memset(&cb, 0, sizeof(cb));
- cb.command = 0x3;
- cb.length = sizeof(buf);
- ret = FUsbMscExecCmd(dev, FUSB_DIR_DATA_IN, (u8 *)&cb,
- sizeof(cb), buf, sizeof(buf), 1);
- if (ret)
- return ret;
- /* Check if sense key is set to NOT READY. */
- if ((buf[2] & 0xf) != 2)
- return MSC_COMMAND_FAIL;
- /* Check if additional sense code is 0x3a. */
- if (buf[12] != 0x3a)
- return MSC_COMMAND_FAIL;
- /* No media is present. Return MSC_COMMAND_OK while marking the disk
- * not ready. */
- FUSB_INFO("Empty media found. ");
- MSC_INST(dev)->ready = FUSB_MSC_NOT_READY;
- return MSC_COMMAND_OK;
- }
- static int FUsbMscCheckIfReady(FUsbDev *dev)
- {
- FUsbMscCmdBlk6 cb;
- memset(&cb, 0, sizeof(cb)); /* full initialization for T-U-R */
- /* Bulk-Only Mass Storage Reset, class-specific request */
- return FUsbMscExecCmd(dev, FUSB_DIR_DATA_OUT, (u8 *)&cb,
- sizeof(cb), 0, 0, 0);
- }
- static int FUsbMscSpinUp(FUsbDev *dev)
- {
- FUsbMscCmdBlk6 cb;
- memset(&cb, 0, sizeof(cb));
- cb.command = 0x1b;
- cb.start = 1;
- return FUsbMscExecCmd(dev, FUSB_DIR_DATA_OUT, (u8 *)&cb,
- sizeof(cb), 0, 0, 0);
- }
- static int FUsbMscReadCapcity(FUsbDev *dev)
- {
- FUsbMscCmdBlk cb;
- memset(&cb, 0, sizeof(cb));
- cb.command = 0x25; /* read capacity */
- u32 buf[2];
- FUSB_INFO("Reading capacity of mass storage device. ");
- int count = 0, ret;
- while (count++ < 20)
- {
- switch (ret = FUsbMscExecCmd(dev, FUSB_DIR_DATA_IN, (u8 *)&cb,
- sizeof(cb), (u8 *)buf, 8, 0))
- {
- case MSC_COMMAND_OK:
- break;
- case MSC_COMMAND_FAIL:
- continue;
- default: /* if it's worse return */
- return ret;
- }
- break;
- }
- if (count >= 20)
- {
- /* still not successful, assume 2tb in 512byte sectors, which is just the same garbage as any other number, but probably more usable. */
- FUSB_WARN(" assuming 2 TB with 512-byte sectors as READ CAPACITY didn't answer. ");
- MSC_INST(dev)->numblocks = 0xffffffff;
- MSC_INST(dev)->blocksize = 512;
- }
- else
- {
- MSC_INST(dev)->numblocks = ntohl(buf[0]) + 1;
- MSC_INST(dev)->blocksize = ntohl(buf[1]);
- }
- FUSB_INFO(" %d %d-byte sectors (%d MB) ", MSC_INST(dev)->numblocks,
- MSC_INST(dev)->blocksize,
- /* round down high block counts to avoid integer overflow */
- MSC_INST(dev)->numblocks > 1000000
- ? (MSC_INST(dev)->numblocks / 1000) * MSC_INST(dev)->blocksize / 1000
- : MSC_INST(dev)->numblocks * MSC_INST(dev)->blocksize / 1000 / 1000);
- return MSC_COMMAND_OK;
- }
- static int FUsbMscWaitReady(FUsbDev *dev)
- {
- int i;
- /* SCSI/ATA specs say we have to wait up to 30s, but most devices
- * are ready much sooner. Use a 5 sec timeout to better accommodate
- * devices which fail to respond. */
- const tick_t timeout_tick = 1000000;
- FError ret = FUSB_ERR_WAIT_TIMEOUT;
- FUSB_INFO(" Waiting for device to become ready...");
- /* Initially mark the device ready. */
- MSC_INST(dev)->ready = FUSB_MSC_READY;
- tick_t start_tick = FUsbMscStartTick();
- do
- {
- switch (FUsbMscCheckIfReady(dev))
- {
- case MSC_COMMAND_OK:
- break;
- case MSC_COMMAND_FAIL:
- fsleep_millisec(100);
- FUSB_INFO(".");
- continue;
- default:
- /* Device detached, return immediately */
- return FUSB_MSC_DETACHED;
- }
- break;
- }
- while (!FUsbMscTimeout(start_tick, timeout_tick));
- if (FUsbMscTimeout(start_tick, timeout_tick))
- {
- FUSB_INFO("timeout. Device not ready. ");
- MSC_INST(dev)->ready = FUSB_MSC_NOT_READY;
- }
- /* Don't bother spinning up the storage device if the device is not
- * ready. This can happen when empty card readers are present.
- * Polling will pick it back up if readiness changes. */
- if (!MSC_INST(dev)->ready)
- return MSC_INST(dev)->ready;
- for (i = 0; i < 30; i++)
- {
- FUSB_INFO(".");
- switch (FUsbMscSpinUp(dev))
- {
- case MSC_COMMAND_OK:
- FUSB_INFO(" OK.");
- break;
- case MSC_COMMAND_FAIL:
- fsleep_millisec(100);
- continue;
- default:
- /* Device detached, return immediately */
- return FUSB_MSC_DETACHED;
- }
- break;
- }
- if (FUsbMscReadCapcity(dev) == MSC_COMMAND_DETACHED)
- return FUSB_MSC_DETACHED;
- return MSC_INST(dev)->ready;
- }
- /**
- * @name: FUsbMassStorageInit
- * @msg: USB大容量存储设备的初始化函数,由应用程序注册到FUSB框架中
- * @return {*}
- * @param {FUsbDev} *dev, USB大容量存储设备实例
- */
- void FUsbMassStorageInit(FUsbDev *dev)
- {
- FASSERT(dev && dev->configuration);
- FUsbConfigurationDescriptor *cd =
- (FUsbConfigurationDescriptor *)dev->configuration;
- FASSERT(FUSB_DESC_TYPE_CONFIG == cd->bDescriptorType);
- FUsbInterfaceDescriptor *interface =
- (FUsbInterfaceDescriptor *)(((char *)cd) + cd->bLength);
- FASSERT(FUSB_DESC_TYPE_INTERFACE == interface->bDescriptorType);
- if (FUSB_MASS_STORAGE_DEVICE != interface->bInterfaceClass)
- {
- FUSB_ERROR("Class %d not supported. ", interface->bInterfaceClass);
- return;
- }
- FUSB_INFO(" command set: %s protocol: %s ",
- FUsbMscSubClassString[interface->bInterfaceSubClass],
- FUsbMscProtocolStrings[interface->bInterfaceProtocol]);
- if (interface->bInterfaceProtocol != FUSB_MSC_PROTOCOL_BULK_ONLY)
- {
- FUSB_ERROR(" Protocol not supported. ");
- FUsbDetachDev(dev->controller, dev->address);
- return;
- }
- if ((interface->bInterfaceSubClass != FUSB_MSC_SUBCLASS_ATAPI_8020) && /* ATAPI 8020 */
- (interface->bInterfaceSubClass != FUSB_MSC_SUBCLASS_ATAPI_8070) && /* ATAPI 8070 */
- (interface->bInterfaceSubClass != FUSB_MSC_SUBCLASS_SCSI))
- {
- /* SCSI */
- /* Other protocols, such as ATAPI don't seem to be very popular. looks like ATAPI would be really easy to add, if necessary. */
- FUSB_ERROR(" Interface SubClass not supported. ");
- FUsbDetachDev(dev->controller, dev->address);
- return;
- }
- FUsbMassStorageForceInit(dev, 0);
- return;
- }
- /* Force a device to enumerate as MSC, without checking class/protocol types.
- It must still have a bulk endpoint pair and respond to MSC commands. */
- static void FUsbMassStorageForceInit(FUsbDev *dev, u32 quirks)
- {
- FASSERT(dev && dev->controller);
- int i;
- FUsb *instance = dev->controller->usb;
- FASSERT(instance);
- /* init .data before setting .destroy */
- dev->data = NULL;
- dev->destroy = FUsbMscDestory;
- dev->poll = FUsbMscPoll;
- FASSERT(NULL == dev->data);
- dev->data = FUSB_ALLOCATE(instance, sizeof(FUsbMassStorage), FUSB_DEFAULT_ALIGN);
- if (NULL == dev->data)
- {
- FUSB_ERROR("Not enough memory for USB MSC device. ");
- FASSERT(0);
- }
- MSC_INST(dev)->bulk_in = NULL;
- MSC_INST(dev)->bulk_out = NULL;
- MSC_INST(dev)->usbdisk_created = 0;
- MSC_INST(dev)->quirks = quirks;
- /* loop over all ep except ep0 to get bulk-in/bulk-out ep instance */
- for (i = 1; i <= dev->num_endp; i++)
- {
- if (dev->endpoints[i].endpoint == 0)
- continue;
- if (dev->endpoints[i].type != FUSB_BULK_EP)
- continue;
- if ((dev->endpoints[i].direction == FUSB_IN) && (MSC_INST(dev)->bulk_in == 0))
- MSC_INST(dev)->bulk_in = &dev->endpoints[i];
- if ((dev->endpoints[i].direction == FUSB_OUT) && (MSC_INST(dev)->bulk_out == 0))
- MSC_INST(dev)->bulk_out = &dev->endpoints[i];
- }
- /* check if non bulk-in ep */
- if (MSC_INST(dev)->bulk_in == NULL)
- {
- FUSB_ERROR("couldn't find bulk-in endpoint. ");
- FUsbDetachDev(dev->controller, dev->address);
- return;
- }
- /* check if non bulk-out ep */
- if (MSC_INST(dev)->bulk_out == NULL)
- {
- FUSB_ERROR("couldn't find bulk-out endpoint. ");
- FUsbDetachDev(dev->controller, dev->address);
- return;
- }
- FUSB_INFO("using endpoint %x as in, %x as out ",
- MSC_INST(dev)->bulk_in->endpoint,
- MSC_INST(dev)->bulk_out->endpoint);
- /* Some sticks need a little more time to get ready after SET_CONFIG. */
- fsleep_microsec(50);
- FUsbMscInitLuns(dev);
- FUSB_INFO(" has %d luns ", MSC_INST(dev)->num_luns);
- /* Test if msc is ready (nothing to do if it isn't). */
- if (FUsbMscWaitReady(dev) != FUSB_MSC_READY)
- return;
- /* Create the disk. */
- FUsbMscCreateDisk(dev);
- return;
- }
- static void FUsbMscPoll(FUsbDev *dev)
- {
- FUsbMassStorage *msc = MSC_INST(dev);
- int prev_ready = msc->ready;
- if (FUsbMscWaitReady(dev) == FUSB_MSC_DETACHED)
- return;
- if (!prev_ready && msc->ready)
- {
- FUSB_INFO("USB msc: not ready -> ready (lun %d) ", msc->lun);
- FUsbMscCreateDisk(dev);
- }
- else if (prev_ready && !msc->ready)
- {
- FUSB_INFO("USB msc: ready -> not ready (lun %d) ", msc->lun);
- FUsbMscRemoveDisk(dev);
- }
- else if (!prev_ready && !msc->ready)
- {
- u8 new_lun = (msc->lun + 1) % msc->num_luns;
- FUSB_INFO("USB msc: not ready (lun %d) -> lun %d ", msc->lun,
- new_lun);
- msc->lun = new_lun;
- }
- return;
- }
|