/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.driver.block.scsi.cdrom;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.nio.ByteBuffer;
import org.jnode.driver.Driver;
import org.jnode.driver.DriverException;
import org.jnode.driver.RemovableDeviceAPI;
import org.jnode.driver.block.FSBlockAlignmentSupport;
import org.jnode.driver.block.FSBlockDeviceAPI;
import org.jnode.driver.bus.scsi.SCSIDevice;
import org.jnode.driver.bus.scsi.SCSIDeviceAPI;
import org.jnode.driver.bus.scsi.SCSIException;
import org.jnode.driver.bus.scsi.cdb.mmc.CDBStartStopUnit;
import org.jnode.driver.bus.scsi.cdb.mmc.CapacityData;
import org.jnode.driver.bus.scsi.cdb.mmc.MMCUtils;
import org.jnode.partitions.PartitionTableEntry;
import org.jnode.util.ByteBufferUtils;
import org.jnode.util.TimeoutException;
/**
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
public class SCSICDROMDriver extends Driver implements FSBlockDeviceAPI,
RemovableDeviceAPI {
private boolean locked;
private final FSBlockAlignmentSupport blockAlignment;
private CapacityData capacity;
private boolean changed;
public SCSICDROMDriver() {
this.blockAlignment = new FSBlockAlignmentSupport(this, 2048);
}
protected void startDevice() throws DriverException {
this.locked = false;
this.changed = true;
this.capacity = null;
this.blockAlignment.setAlignment(2048);
final SCSIDevice dev = (SCSIDevice) getDevice();
dev.registerAPI(SCSIDeviceAPI.class, new SCSIDevice.SCSIDeviceAPIImpl(
dev));
dev.registerAPI(RemovableDeviceAPI.class, this);
dev.registerAPI(FSBlockDeviceAPI.class, blockAlignment);
}
protected void stopDevice() throws DriverException {
try {
unlock();
} catch (IOException ex) {
throw new DriverException(ex);
} finally {
final SCSIDevice dev = (SCSIDevice) getDevice();
dev.unregisterAPI(RemovableDeviceAPI.class);
dev.unregisterAPI(FSBlockDeviceAPI.class);
dev.unregisterAPI(SCSIDeviceAPI.class);
}
}
public PartitionTableEntry getPartitionTableEntry() {
return null;
}
public int getSectorSize() throws IOException {
processChanged();
return capacity.getBlockLength();
}
public void flush() throws IOException {
// Do nothing
}
public long getLength() throws IOException {
processChanged();
return capacity.getBlockLength() & capacity.getLogicalBlockAddress();
}
public void read(long devOffset, ByteBuffer destBuf)
throws IOException {
//TODO optimize it also to use ByteBuffer at lower level
ByteBufferUtils.ByteArray destBA = ByteBufferUtils.toByteArray(destBuf);
byte[] dest = destBA.toArray();
int destOffset = 0;
int length = dest.length;
processChanged();
if (capacity == null) {
throw new IOException("No medium");
}
final int blockLength = capacity.getBlockLength();
final int lba = (int) (devOffset / blockLength);
final int nrBlocks = length / blockLength;
final SCSIDevice dev = (SCSIDevice) getDevice();
try {
MMCUtils.readData(dev, lba, nrBlocks, dest, destOffset);
} catch (SCSIException ex) {
final IOException ioe = new IOException();
ioe.initCause(ex);
throw ioe;
} catch (TimeoutException ex) {
final IOException ioe = new IOException();
ioe.initCause(ex);
throw ioe;
} catch (InterruptedException ex) {
throw new InterruptedIOException();
}
destBA.refreshByteBuffer();
}
public void write(long devOffset, ByteBuffer src)
throws IOException {
throw new IOException("Readonly device");
}
/**
* Can this device be locked.
*
* @return {@code true} if the device can be locked, otherwise {@code false}.
*/
public boolean canLock() {
return true;
}
/**
* Can this device be ejected.
*
* @return {@code true} if the device can be ejected, otherwise {@code false}.
*/
public boolean canEject() {
return true;
}
/**
* Lock the device.
*
* @throws IOException
*/
public synchronized void lock() throws IOException {
if (!locked) {
final SCSIDevice dev = (SCSIDevice) getDevice();
try {
MMCUtils.setMediaRemoval(dev, true, false);
} catch (SCSIException ex) {
final IOException ioe = new IOException();
ioe.initCause(ex);
throw ioe;
} catch (TimeoutException ex) {
final IOException ioe = new IOException();
ioe.initCause(ex);
throw ioe;
} catch (InterruptedException ex) {
throw new InterruptedIOException();
}
locked = true;
}
}
/**
* Unlock the device.
*
* @throws IOException
*/
public synchronized void unlock() throws IOException {
if (!locked) {
final SCSIDevice dev = (SCSIDevice) getDevice();
try {
MMCUtils.setMediaRemoval(dev, false, false);
} catch (SCSIException ex) {
final IOException ioe = new IOException();
ioe.initCause(ex);
throw ioe;
} catch (TimeoutException ex) {
final IOException ioe = new IOException();
ioe.initCause(ex);
throw ioe;
} catch (InterruptedException ex) {
throw new InterruptedIOException();
}
locked = false;
}
}
/**
* Is this device locked.
*
* @see org.jnode.driver.RemovableDeviceAPI#isLocked()
*/
public boolean isLocked() {
return locked;
}
/**
* Eject this device.
*
* @throws IOException
*/
public void eject() throws IOException {
if (locked) {
throw new IOException("Device is locked");
}
final SCSIDevice dev = (SCSIDevice) getDevice();
try {
MMCUtils.startStopUnit(dev, CDBStartStopUnit.Action.EJECT, false);
} catch (SCSIException ex) {
final IOException ioe = new IOException();
ioe.initCause(ex);
throw ioe;
} catch (TimeoutException ex) {
final IOException ioe = new IOException();
ioe.initCause(ex);
throw ioe;
} catch (InterruptedException ex) {
throw new InterruptedIOException();
}
}
/**
* Load this device.
*
* @throws IOException
*/
public void load() throws IOException {
if (locked) {
throw new IOException("Device is locked");
}
final SCSIDevice dev = (SCSIDevice) getDevice();
try {
MMCUtils.startStopUnit(dev, CDBStartStopUnit.Action.LOAD, false);
} catch (SCSIException ex) {
final IOException ioe = new IOException();
ioe.initCause(ex);
throw ioe;
} catch (TimeoutException ex) {
final IOException ioe = new IOException();
ioe.initCause(ex);
throw ioe;
} catch (InterruptedException ex) {
throw new InterruptedIOException();
}
}
/**
* Process the changed flag.
*
* @throws IOException
*/
private void processChanged() throws IOException {
if (changed) {
this.capacity = null;
final SCSIDevice dev = (SCSIDevice) getDevice();
try {
// Gets the capacity.
this.capacity = MMCUtils.readCapacity(dev);
this.blockAlignment.setAlignment(capacity.getBlockLength());
} catch (SCSIException ex) {
final IOException ioe = new IOException();
ioe.initCause(ex);
throw ioe;
} catch (TimeoutException ex) {
final IOException ioe = new IOException();
ioe.initCause(ex);
throw ioe;
} catch (InterruptedException ex) {
throw new InterruptedIOException();
}
changed = false;
}
}
}