Android Open Source - libaums Scsi Block Device






From Project

Back to project page libaums.

License

The source code is released under:

Apache License

If you think the Android project libaums listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * (C) Copyright 2014 mjahnen <jahnen@in.tum.de>
 */*from   ww  w.j a  v  a 2 s .c  om*/
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

package com.github.mjdev.libaums.driver.scsi;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;

import android.util.Log;

import com.github.mjdev.libaums.UsbCommunication;
import com.github.mjdev.libaums.driver.BlockDeviceDriver;
import com.github.mjdev.libaums.driver.scsi.commands.CommandBlockWrapper;
import com.github.mjdev.libaums.driver.scsi.commands.CommandBlockWrapper.Direction;
import com.github.mjdev.libaums.driver.scsi.commands.CommandStatusWrapper;
import com.github.mjdev.libaums.driver.scsi.commands.ScsiInquiry;
import com.github.mjdev.libaums.driver.scsi.commands.ScsiInquiryResponse;
import com.github.mjdev.libaums.driver.scsi.commands.ScsiRead10;
import com.github.mjdev.libaums.driver.scsi.commands.ScsiReadCapacity;
import com.github.mjdev.libaums.driver.scsi.commands.ScsiReadCapacityResponse;
import com.github.mjdev.libaums.driver.scsi.commands.ScsiTestUnitReady;
import com.github.mjdev.libaums.driver.scsi.commands.ScsiWrite10;

/**
 * This class is responsible for handling mass storage devices which follow the
 * SCSI standard. This class communicates with the mass storage device via the
 * different SCSI commands.
 * 
 * @author mjahnen
 * @see com.github.mjdev.libaums.driver.scsi.commands
 */
public class ScsiBlockDevice implements BlockDeviceDriver {

  private static final String TAG = ScsiBlockDevice.class.getSimpleName();

  private UsbCommunication usbCommunication;
  private ByteBuffer outBuffer;
  private byte[] cswBuffer;

  private int blockSize;
  private int lastBlockAddress;

  public ScsiBlockDevice(UsbCommunication usbCommunication) {
    this.usbCommunication = usbCommunication;
    outBuffer = ByteBuffer.allocate(31);
    cswBuffer = new byte[CommandStatusWrapper.SIZE];
  }

  /**
   * Issues a SCSI Inquiry to determine the connected device. After that it is
   * checked if the unit is ready. Logs a warning if the unit is not ready.
   * Finally the capacity of the mass storage device is read.
   * 
   * @throws IOException
   *             If initialing fails due to an unsupported device or if
   *             reading fails.
   * @see com.github.mjdev.libaums.driver.scsi.commands.ScsiInquiry
   * @see com.github.mjdev.libaums.driver.scsi.commands.ScsiInquiryResponse
   * @see com.github.mjdev.libaums.driver.scsi.commands.ScsiTestUnitReady
   * @see com.github.mjdev.libaums.driver.scsi.commands.ScsiReadCapacity
   * @see com.github.mjdev.libaums.driver.scsi.commands.ScsiReadCapacityResponse
   */
  @Override
  public void init() throws IOException {
    ByteBuffer inBuffer = ByteBuffer.allocate(36);
    ScsiInquiry inquiry = new ScsiInquiry();
    transferCommand(inquiry, inBuffer);
    // TODO support multiple luns!
    ScsiInquiryResponse inquiryResponse = ScsiInquiryResponse.read(inBuffer);
    Log.d(TAG, "inquiry response: " + inquiryResponse);

    if (inquiryResponse.getPeripheralQualifier() != 0
        || inquiryResponse.getPeripheralDeviceType() != 0) {
      throw new IOException("unsupported PeripheralQualifier or PeripheralDeviceType");
    }

    ScsiTestUnitReady testUnit = new ScsiTestUnitReady();
    if (!transferCommand(testUnit, null)) {
      Log.w(TAG, "unit not ready!");
    }

    ScsiReadCapacity readCapacity = new ScsiReadCapacity();
    transferCommand(readCapacity, inBuffer);
    ScsiReadCapacityResponse readCapacityResponse = ScsiReadCapacityResponse.read(inBuffer);
    blockSize = readCapacityResponse.getBlockLength();
    lastBlockAddress = readCapacityResponse.getLogicalBlockAddress();

    Log.i(TAG, "Block size: " + blockSize);
    Log.i(TAG, "Last block address: " + lastBlockAddress);
  }

  /**
   * Transfers the desired command to the device. If the command has a data
   * phase the parameter <code>inBuffer</code> is used to store or read data
   * to resp. from it. The direction of the data phase is determined by
   * {@link com.github.mjdev.libaums.driver.scsi.commands.CommandBlockWrapper #getDirection()}
   * .
   * <p>
   * Return value is true if the status of the command status wrapper is
   * successful (
   * {@link com.github.mjdev.libaums.driver.scsi.commands.CommandStatusWrapper #getbCswStatus()}
   * ).
   * 
   * @param command
   *            The command which should be transferred.
   * @param inBuffer
   *            The buffer used for reading or writing.
   * @return True if the transaction was successful.
   * @throws IOException
   *             If something fails.
   */
  private boolean transferCommand(CommandBlockWrapper command, ByteBuffer inBuffer)
      throws IOException {
    byte[] outArray = outBuffer.array();
    outBuffer.clear();
    Arrays.fill(outArray, (byte) 0);

    command.serialize(outBuffer);
    int written = usbCommunication.bulkOutTransfer(outArray, outArray.length);
    if (written != outArray.length) {
      Log.e(TAG, "Writing all bytes on command " + command + " failed!");
    }

    int transferLength = command.getdCbwDataTransferLength();
    int read = 0;
    if (transferLength > 0) {
      byte[] inArray = inBuffer.array();

      if (command.getDirection() == Direction.IN) {
        do {
          int tmp = usbCommunication.bulkInTransfer(inArray, read + inBuffer.position(),
              inBuffer.remaining() - read);
          if (tmp == -1) {
            throw new IOException("reading failed!");
          }
          read += tmp;
        } while (read < transferLength);

        if (read != transferLength) {
          throw new IOException("Unexpected command size (" + read + ") on response to "
              + command);
        }
      } else {
        written = 0;
        do {
          int tmp = usbCommunication.bulkOutTransfer(inArray,
              written + inBuffer.position(), inBuffer.remaining() - written);
          if (tmp == -1) {
            throw new IOException("writing failed!");
          }
          written += tmp;
        } while (written < transferLength);

        if (written != transferLength) {
          throw new IOException("Could not write all bytes: " + command);
        }
      }
    }

    // expecting csw now
    read = usbCommunication.bulkInTransfer(cswBuffer, cswBuffer.length);
    if (read != CommandStatusWrapper.SIZE) {
      Log.e(TAG, "Unexpected command size while expecting csw");
    }

    CommandStatusWrapper csw = CommandStatusWrapper.read(ByteBuffer.wrap(cswBuffer));
    if (csw.getbCswStatus() != CommandStatusWrapper.COMMAND_PASSED) {
      Log.e(TAG, "Unsuccessful Csw status: " + csw.getbCswStatus());
    }

    if (csw.getdCswTag() != command.getdCbwTag()) {
      Log.e(TAG, "wrong csw tag!");
    }

    return csw.getbCswStatus() == CommandStatusWrapper.COMMAND_PASSED;
  }

  /**
   * This method reads from the device at the specific device offset. The
   * devOffset specifies at which block the reading should begin. That means
   * the devOffset is not in bytes!
   */
  @Override
  public void read(long devOffset, ByteBuffer dest) throws IOException {
    long time = System.currentTimeMillis();
    // TODO try to make this more efficient by for example only allocating
    // blockSize and making it global
    ByteBuffer buffer;
    if (dest.remaining() % blockSize != 0) {
      Log.i(TAG, "we have to round up size to next block sector");
      int rounded = blockSize - dest.remaining() % blockSize + dest.remaining();
      buffer = ByteBuffer.allocate(rounded);
      buffer.limit(rounded);
    } else {
      buffer = dest;
    }

    ScsiRead10 read = new ScsiRead10((int) devOffset, buffer.remaining(), blockSize);
    Log.d(TAG, "reading: " + read);
    transferCommand(read, buffer);

    if (dest.remaining() % blockSize != 0) {
      System.arraycopy(buffer.array(), 0, dest.array(), dest.position(), dest.remaining());
    }

    dest.position(dest.limit());

    Log.d(TAG, "read time: " + (System.currentTimeMillis() - time));
  }

  /**
   * This method writes from the device at the specific device offset. The
   * devOffset specifies at which block the writing should begin. That means
   * the devOffset is not in bytes!
   */
  @Override
  public void write(long devOffset, ByteBuffer src) throws IOException {
    long time = System.currentTimeMillis();
    // TODO try to make this more efficient by for example only allocating
    // blockSize and making it global
    ByteBuffer buffer;
    if (src.remaining() % blockSize != 0) {
      Log.i(TAG, "we have to round up size to next block sector");
      int rounded = blockSize - src.remaining() % blockSize + src.remaining();
      buffer = ByteBuffer.allocate(rounded);
      buffer.limit(rounded);
      System.arraycopy(src.array(), src.position(), buffer.array(), 0, src.remaining());
    } else {
      buffer = src;
    }

    ScsiWrite10 write = new ScsiWrite10((int) devOffset, buffer.remaining(), blockSize);
    Log.d(TAG, "writing: " + write);
    transferCommand(write, buffer);

    src.position(src.limit());

    Log.d(TAG, "write time: " + (System.currentTimeMillis() - time));
  }

  @Override
  public int getBlockSize() {
    return blockSize;
  }
}




Java Source Code List

com.github.mjdev.libaums.UsbCommunication.java
com.github.mjdev.libaums.UsbMassStorageDevice.java
com.github.mjdev.libaums.driver.BlockDeviceDriverFactory.java
com.github.mjdev.libaums.driver.BlockDeviceDriver.java
com.github.mjdev.libaums.driver.scsi.ScsiBlockDevice.java
com.github.mjdev.libaums.driver.scsi.commands.CommandBlockWrapper.java
com.github.mjdev.libaums.driver.scsi.commands.CommandStatusWrapper.java
com.github.mjdev.libaums.driver.scsi.commands.ScsiInquiryResponse.java
com.github.mjdev.libaums.driver.scsi.commands.ScsiInquiry.java
com.github.mjdev.libaums.driver.scsi.commands.ScsiRead10.java
com.github.mjdev.libaums.driver.scsi.commands.ScsiReadCapacityResponse.java
com.github.mjdev.libaums.driver.scsi.commands.ScsiReadCapacity.java
com.github.mjdev.libaums.driver.scsi.commands.ScsiRequestSense.java
com.github.mjdev.libaums.driver.scsi.commands.ScsiTestUnitReady.java
com.github.mjdev.libaums.driver.scsi.commands.ScsiWrite10.java
com.github.mjdev.libaums.fs.FileSystemFactory.java
com.github.mjdev.libaums.fs.FileSystem.java
com.github.mjdev.libaums.fs.UsbFile.java
com.github.mjdev.libaums.fs.fat32.ClusterChain.java
com.github.mjdev.libaums.fs.fat32.FAT.java
com.github.mjdev.libaums.fs.fat32.Fat32BootSector.java
com.github.mjdev.libaums.fs.fat32.Fat32FileSystem.java
com.github.mjdev.libaums.fs.fat32.FatDirectoryEntry.java
com.github.mjdev.libaums.fs.fat32.FatDirectory.java
com.github.mjdev.libaums.fs.fat32.FatFile.java
com.github.mjdev.libaums.fs.fat32.FatLfnDirectoryEntry.java
com.github.mjdev.libaums.fs.fat32.FsInfoStructure.java
com.github.mjdev.libaums.fs.fat32.ShortNameGenerator.java
com.github.mjdev.libaums.fs.fat32.ShortName.java
com.github.mjdev.libaums.partition.PartitionTableEntry.java
com.github.mjdev.libaums.partition.PartitionTableFactory.java
com.github.mjdev.libaums.partition.PartitionTable.java
com.github.mjdev.libaums.partition.Partition.java
com.github.mjdev.libaums.partition.mbr.MasterBootRecord.java
com.github.mjdev.libaums.usbfileman.MainActivity.java
com.github.mjdev.libaums.usbfileman.MoveClipboard.java
com.github.mjdev.libaums.usbfileman.UsbFileListAdapter.java