1
0
mirror of https://github.com/mik3y/usb-serial-for-android synced 2025-06-07 16:06:10 +00:00

unify open() error handling, more tests, minor cleanup

This commit is contained in:
kai-morich 2019-11-02 13:03:11 +01:00
parent 6869eff88a
commit 18b5b6e648
8 changed files with 618 additions and 507 deletions

View File

@ -229,7 +229,7 @@ public class DeviceTest implements SerialInputOutputManager.Listener {
private byte[] telnetRead(int expectedLength) throws Exception { private byte[] telnetRead(int expectedLength) throws Exception {
long end = System.currentTimeMillis() + TELNET_READ_WAIT; long end = System.currentTimeMillis() + TELNET_READ_WAIT;
ByteBuffer buf = ByteBuffer.allocate(4096); ByteBuffer buf = ByteBuffer.allocate(8192);
while(System.currentTimeMillis() < end) { while(System.currentTimeMillis() < end) {
if(telnetReadStream.available() > 0) { if(telnetReadStream.available() > 0) {
buf.put((byte) telnetReadStream.read()); buf.put((byte) telnetReadStream.read());
@ -498,23 +498,21 @@ public class DeviceTest implements SerialInputOutputManager.Listener {
doReadWrite(""); doReadWrite("");
usbClose(); usbClose();
usbSerialPort = usbSerialDriver.getPorts().get(test_device_port);
try { try {
usbSerialPort.close(); usbSerialPort.close();
fail("already closed expected"); fail("already closed expected");
} catch (IOException ignored) { } catch (IOException ignored) {
} catch (NullPointerException ignored) {
} }
try { try {
usbWrite(new byte[]{0x00}); usbWrite(new byte[]{0x00});
fail("write error expected"); fail("write error expected");
} catch (IOException ignored) { } catch (IOException ignored) {
} catch (NullPointerException ignored) {
} }
try { try {
usbRead(1); usbRead(1);
//fail("read error expected"); fail("read error expected");
} catch (IOException ignored) { } catch (IOException ignored) {
} catch (NullPointerException ignored) {
} }
try { try {
usbParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE); usbParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE);
@ -522,11 +520,22 @@ public class DeviceTest implements SerialInputOutputManager.Listener {
} catch (IOException ignored) { } catch (IOException ignored) {
} catch (NullPointerException ignored) { } catch (NullPointerException ignored) {
} }
usbSerialPort = null;
usbOpen(true); usbOpen(true);
telnetParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE); telnetParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE);
usbParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE); usbParameters(9600, 8, 1, UsbSerialPort.PARITY_NONE);
doReadWrite(""); doReadWrite("");
// close before iomanager
assertEquals(SerialInputOutputManager.State.RUNNING, usbIoManager.getState());
usbSerialPort.close();
for (int i = 0; i < 1000; i++) {
if (usbIoManager.getState() == SerialInputOutputManager.State.STOPPED)
break;
Thread.sleep(1);
}
assertEquals(SerialInputOutputManager.State.STOPPED, usbIoManager.getState());
} }
@Test @Test
@ -684,21 +693,21 @@ public class DeviceTest implements SerialInputOutputManager.Listener {
usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
telnetParameters(19200, 7, 1, UsbSerialPort.PARITY_NONE); telnetParameters(19200, 7, 1, UsbSerialPort.PARITY_NONE);
telnetWrite(new byte[] {0x00}); telnetWrite(new byte[] {0x00});
Thread.sleep(1); // one bit is 0.05 milliseconds long, wait >> stop bit Thread.sleep(10); // one bit is 0.05 milliseconds long, wait >> stop bit
telnetWrite(new byte[] {(byte)0xff}); telnetWrite(new byte[] {(byte)0xff});
data = usbRead(2); data = usbRead(2);
assertThat("19200/7N1", data, equalTo(new byte[] {(byte)0x80, (byte)0xff})); assertThat("19200/7N1", data, equalTo(new byte[] {(byte)0x80, (byte)0xff}));
telnetParameters(19200, 6, 1, UsbSerialPort.PARITY_NONE); telnetParameters(19200, 6, 1, UsbSerialPort.PARITY_NONE);
telnetWrite(new byte[] {0x00}); telnetWrite(new byte[] {0x00});
Thread.sleep(1); Thread.sleep(10);
telnetWrite(new byte[] {(byte)0xff}); telnetWrite(new byte[] {(byte)0xff});
data = usbRead(2); data = usbRead(2);
assertThat("19000/6N1", data, equalTo(new byte[] {(byte)0xc0, (byte)0xff})); assertThat("19000/6N1", data, equalTo(new byte[] {(byte)0xc0, (byte)0xff}));
telnetParameters(19200, 5, 1, UsbSerialPort.PARITY_NONE); telnetParameters(19200, 5, 1, UsbSerialPort.PARITY_NONE);
telnetWrite(new byte[] {0x00}); telnetWrite(new byte[] {0x00});
Thread.sleep(1); Thread.sleep(10);
telnetWrite(new byte[] {(byte)0xff}); telnetWrite(new byte[] {(byte)0xff});
data = usbRead(2); data = usbRead(2);
assertThat("19000/5N1", data, equalTo(new byte[] {(byte)0xe0, (byte)0xff})); assertThat("19000/5N1", data, equalTo(new byte[] {(byte)0xe0, (byte)0xff}));
@ -708,7 +717,7 @@ public class DeviceTest implements SerialInputOutputManager.Listener {
telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE); telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
usbParameters(19200, 7, 1, UsbSerialPort.PARITY_NONE); usbParameters(19200, 7, 1, UsbSerialPort.PARITY_NONE);
usbWrite(new byte[]{0x00}); usbWrite(new byte[]{0x00});
Thread.sleep(1); Thread.sleep(10);
usbWrite(new byte[]{(byte) 0xff}); usbWrite(new byte[]{(byte) 0xff});
data = telnetRead(2); data = telnetRead(2);
assertThat("19000/7N1", data, equalTo(new byte[]{(byte) 0x80, (byte) 0xff})); assertThat("19000/7N1", data, equalTo(new byte[]{(byte) 0x80, (byte) 0xff}));
@ -719,7 +728,7 @@ public class DeviceTest implements SerialInputOutputManager.Listener {
try { try {
usbParameters(19200, 6, 1, UsbSerialPort.PARITY_NONE); usbParameters(19200, 6, 1, UsbSerialPort.PARITY_NONE);
usbWrite(new byte[]{0x00}); usbWrite(new byte[]{0x00});
Thread.sleep(1); Thread.sleep(10);
usbWrite(new byte[]{(byte) 0xff}); usbWrite(new byte[]{(byte) 0xff});
data = telnetRead(2); data = telnetRead(2);
assertThat("19000/6N1", data, equalTo(new byte[]{(byte) 0xc0, (byte) 0xff})); assertThat("19000/6N1", data, equalTo(new byte[]{(byte) 0xc0, (byte) 0xff}));
@ -730,7 +739,7 @@ public class DeviceTest implements SerialInputOutputManager.Listener {
try { try {
usbParameters(19200, 5, 1, UsbSerialPort.PARITY_NONE); usbParameters(19200, 5, 1, UsbSerialPort.PARITY_NONE);
usbWrite(new byte[] {0x00}); usbWrite(new byte[] {0x00});
Thread.sleep(1); Thread.sleep(5);
usbWrite(new byte[] {(byte)0xff}); usbWrite(new byte[] {(byte)0xff});
data = telnetRead(2); data = telnetRead(2);
assertThat("19000/5N1", data, equalTo(new byte[] {(byte)0xe0, (byte)0xff})); assertThat("19000/5N1", data, equalTo(new byte[] {(byte)0xe0, (byte)0xff}));
@ -1217,5 +1226,109 @@ public class DeviceTest implements SerialInputOutputManager.Listener {
assertTrue("not closed in time", i<100); assertTrue("not closed in time", i<100);
Thread.sleep(1); Thread.sleep(1);
} }
} }
@Test
public void wrongDriver() throws Exception {
UsbDeviceConnection wrongDeviceConnection;
UsbSerialDriver wrongSerialDriver;
UsbSerialPort wrongSerialPort;
if(!(usbSerialDriver instanceof CdcAcmSerialDriver)) {
wrongDeviceConnection = usbManager.openDevice(usbSerialDriver.getDevice());
wrongSerialDriver = new CdcAcmSerialDriver(usbSerialDriver.getDevice());
wrongSerialPort = wrongSerialDriver.getPorts().get(0);
try {
wrongSerialPort.open(wrongDeviceConnection);
wrongSerialPort.setParameters(115200, UsbSerialPort.DATABITS_8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE); // ch340 fails here
wrongSerialPort.write(new byte[]{1}, 1000); // pl2302 does not fail, but sends with wrong baud rate
if(!(usbSerialDriver instanceof ProlificSerialDriver))
fail("error expected");
} catch (IOException ignored) {
}
try {
if(usbSerialDriver instanceof ProlificSerialDriver) {
assertNotEquals(new byte[]{1}, telnetRead());
}
wrongSerialPort.close();
if(!(usbSerialDriver instanceof Ch34xSerialDriver |
usbSerialDriver instanceof ProlificSerialDriver))
fail("error expected");
} catch (IOException ignored) {
}
}
if(!(usbSerialDriver instanceof Ch34xSerialDriver)) {
wrongDeviceConnection = usbManager.openDevice(usbSerialDriver.getDevice());
wrongSerialDriver = new Ch34xSerialDriver(usbSerialDriver.getDevice());
wrongSerialPort = wrongSerialDriver.getPorts().get(0);
try {
wrongSerialPort.open(wrongDeviceConnection);
fail("error expected");
} catch (IOException ignored) {
}
try {
wrongSerialPort.close();
fail("error expected");
} catch (IOException ignored) {
}
}
// FTDI only recovers from Cp21xx control commands with power toggle, so skip this combination!
if(!(usbSerialDriver instanceof Cp21xxSerialDriver | usbSerialDriver instanceof FtdiSerialDriver)) {
wrongDeviceConnection = usbManager.openDevice(usbSerialDriver.getDevice());
wrongSerialDriver = new Cp21xxSerialDriver(usbSerialDriver.getDevice());
wrongSerialPort = wrongSerialDriver.getPorts().get(0);
try {
wrongSerialPort.open(wrongDeviceConnection);
//if(usbSerialDriver instanceof FtdiSerialDriver)
// wrongSerialPort.setParameters(115200, UsbSerialPort.DATABITS_8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE); // ch340 fails here
fail("error expected");
} catch (IOException ignored) {
}
try {
wrongSerialPort.close();
//if(!(usbSerialDriver instanceof FtdiSerialDriver))
// fail("error expected");
} catch (IOException ignored) {
}
}
if(!(usbSerialDriver instanceof FtdiSerialDriver)) {
wrongDeviceConnection = usbManager.openDevice(usbSerialDriver.getDevice());
wrongSerialDriver = new FtdiSerialDriver(usbSerialDriver.getDevice());
wrongSerialPort = wrongSerialDriver.getPorts().get(0);
try {
wrongSerialPort.open(wrongDeviceConnection);
if(usbSerialDriver instanceof Cp21xxSerialDriver)
wrongSerialPort.setParameters(115200, UsbSerialPort.DATABITS_8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE); // ch340 fails here
fail("error expected");
} catch (IOException ignored) {
}
try {
wrongSerialPort.close();
if(!(usbSerialDriver instanceof Cp21xxSerialDriver))
fail("error expected");
} catch (IOException ignored) {
}
}
if(!(usbSerialDriver instanceof ProlificSerialDriver)) {
wrongDeviceConnection = usbManager.openDevice(usbSerialDriver.getDevice());
wrongSerialDriver = new ProlificSerialDriver(usbSerialDriver.getDevice());
wrongSerialPort = wrongSerialDriver.getPorts().get(0);
try {
wrongSerialPort.open(wrongDeviceConnection);
fail("error expected");
} catch (IOException ignored) {
}
try {
wrongSerialPort.close();
fail("error expected");
} catch (IOException ignored) {
}
}
// test that device recovers from wrong commands
usbOpen(true);
telnetParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
usbParameters(19200, 8, 1, UsbSerialPort.PARITY_NONE);
doReadWrite("");
}
} }

View File

@ -1,454 +1,455 @@
/* Copyright 2011-2013 Google Inc. /* Copyright 2011-2013 Google Inc.
* Copyright 2013 mike wakerly <opensource@hoho.com> * Copyright 2013 mike wakerly <opensource@hoho.com>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either * License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version. * 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, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software * License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA. * USA.
* *
* Project home page: https://github.com/mik3y/usb-serial-for-android * Project home page: https://github.com/mik3y/usb-serial-for-android
*/ */
package com.hoho.android.usbserial.driver; package com.hoho.android.usbserial.driver;
import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbRequest; import android.hardware.usb.UsbRequest;
import android.util.Log; import android.util.Log;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* USB CDC/ACM serial driver implementation. * USB CDC/ACM serial driver implementation.
* *
* @author mike wakerly (opensource@hoho.com) * @author mike wakerly (opensource@hoho.com)
* @see <a * @see <a
* href="http://www.usb.org/developers/devclass_docs/usbcdc11.pdf">Universal * href="http://www.usb.org/developers/devclass_docs/usbcdc11.pdf">Universal
* Serial Bus Class Definitions for Communication Devices, v1.1</a> * Serial Bus Class Definitions for Communication Devices, v1.1</a>
*/ */
public class CdcAcmSerialDriver implements UsbSerialDriver { public class CdcAcmSerialDriver implements UsbSerialDriver {
private final String TAG = CdcAcmSerialDriver.class.getSimpleName(); private final String TAG = CdcAcmSerialDriver.class.getSimpleName();
private final UsbDevice mDevice; private final UsbDevice mDevice;
private final UsbSerialPort mPort; private final UsbSerialPort mPort;
private UsbRequest mUsbRequest; private UsbRequest mUsbRequest;
public CdcAcmSerialDriver(UsbDevice device) { public CdcAcmSerialDriver(UsbDevice device) {
mDevice = device; mDevice = device;
mPort = new CdcAcmSerialPort(device, 0); mPort = new CdcAcmSerialPort(device, 0);
} }
@Override @Override
public UsbDevice getDevice() { public UsbDevice getDevice() {
return mDevice; return mDevice;
} }
@Override @Override
public List<UsbSerialPort> getPorts() { public List<UsbSerialPort> getPorts() {
return Collections.singletonList(mPort); return Collections.singletonList(mPort);
} }
class CdcAcmSerialPort extends CommonUsbSerialPort { class CdcAcmSerialPort extends CommonUsbSerialPort {
private UsbInterface mControlInterface; private UsbInterface mControlInterface;
private UsbInterface mDataInterface; private UsbInterface mDataInterface;
private UsbEndpoint mControlEndpoint; private UsbEndpoint mControlEndpoint;
private UsbEndpoint mReadEndpoint; private UsbEndpoint mReadEndpoint;
private UsbEndpoint mWriteEndpoint; private UsbEndpoint mWriteEndpoint;
private int mControlIndex; private int mControlIndex;
private boolean mRts = false; private boolean mRts = false;
private boolean mDtr = false; private boolean mDtr = false;
private static final int USB_RECIP_INTERFACE = 0x01; private static final int USB_RECIP_INTERFACE = 0x01;
private static final int USB_RT_ACM = UsbConstants.USB_TYPE_CLASS | USB_RECIP_INTERFACE; private static final int USB_RT_ACM = UsbConstants.USB_TYPE_CLASS | USB_RECIP_INTERFACE;
private static final int SET_LINE_CODING = 0x20; // USB CDC 1.1 section 6.2 private static final int SET_LINE_CODING = 0x20; // USB CDC 1.1 section 6.2
private static final int GET_LINE_CODING = 0x21; private static final int GET_LINE_CODING = 0x21;
private static final int SET_CONTROL_LINE_STATE = 0x22; private static final int SET_CONTROL_LINE_STATE = 0x22;
private static final int SEND_BREAK = 0x23; private static final int SEND_BREAK = 0x23;
public CdcAcmSerialPort(UsbDevice device, int portNumber) { public CdcAcmSerialPort(UsbDevice device, int portNumber) {
super(device, portNumber); super(device, portNumber);
} }
@Override @Override
public UsbSerialDriver getDriver() { public UsbSerialDriver getDriver() {
return CdcAcmSerialDriver.this; return CdcAcmSerialDriver.this;
} }
@Override @Override
public void open(UsbDeviceConnection connection) throws IOException { public void open(UsbDeviceConnection connection) throws IOException {
if (mConnection != null) { if (mConnection != null) {
throw new IOException("Already open"); throw new IOException("Already open");
} }
mConnection = connection; mConnection = connection;
boolean opened = false; boolean opened = false;
try { try {
if (1 == mDevice.getInterfaceCount()) {
if (1 == mDevice.getInterfaceCount()) { Log.d(TAG,"device might be castrated ACM device, trying single interface logic");
Log.d(TAG,"device might be castrated ACM device, trying single interface logic"); openSingleInterface();
openSingleInterface(); } else {
} else { Log.d(TAG,"trying default interface logic");
Log.d(TAG,"trying default interface logic"); openInterface();
openInterface(); }
} opened = true;
} finally {
opened = true; if (!opened) {
} finally { close();
if (!opened) { }
mConnection = null; }
// just to be on the save side }
mControlEndpoint = null;
mReadEndpoint = null; private void openSingleInterface() throws IOException {
mWriteEndpoint = null; // the following code is inspired by the cdc-acm driver
} // in the linux kernel
}
} mControlIndex = 0;
mControlInterface = mDevice.getInterface(0);
private void openSingleInterface() throws IOException { Log.d(TAG, "Control iface=" + mControlInterface);
// the following code is inspired by the cdc-acm driver
// in the linux kernel mDataInterface = mDevice.getInterface(0);
Log.d(TAG, "data iface=" + mDataInterface);
mControlIndex = 0;
mControlInterface = mDevice.getInterface(0); if (!mConnection.claimInterface(mControlInterface, true)) {
Log.d(TAG, "Control iface=" + mControlInterface); throw new IOException("Could not claim shared control/data interface.");
}
mDataInterface = mDevice.getInterface(0);
Log.d(TAG, "data iface=" + mDataInterface); int endCount = mControlInterface.getEndpointCount();
if (!mConnection.claimInterface(mControlInterface, true)) { if (endCount < 3) {
throw new IOException("Could not claim shared control/data interface."); Log.d(TAG,"not enough endpoints - need 3. count=" + mControlInterface.getEndpointCount());
} throw new IOException("Insufficient number of endpoints(" + mControlInterface.getEndpointCount() + ")");
}
int endCount = mControlInterface.getEndpointCount();
// Analyse endpoints for their properties
if (endCount < 3) { mControlEndpoint = null;
Log.d(TAG,"not enough endpoints - need 3. count=" + mControlInterface.getEndpointCount()); mReadEndpoint = null;
throw new IOException("Insufficient number of endpoints(" + mControlInterface.getEndpointCount() + ")"); mWriteEndpoint = null;
} for (int i = 0; i < endCount; ++i) {
UsbEndpoint ep = mControlInterface.getEndpoint(i);
// Analyse endpoints for their properties if ((ep.getDirection() == UsbConstants.USB_DIR_IN) &&
mControlEndpoint = null; (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_INT)) {
mReadEndpoint = null; Log.d(TAG,"Found controlling endpoint");
mWriteEndpoint = null; mControlEndpoint = ep;
for (int i = 0; i < endCount; ++i) { } else if ((ep.getDirection() == UsbConstants.USB_DIR_IN) &&
UsbEndpoint ep = mControlInterface.getEndpoint(i); (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) {
if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && Log.d(TAG,"Found reading endpoint");
(ep.getType() == UsbConstants.USB_ENDPOINT_XFER_INT)) { mReadEndpoint = ep;
Log.d(TAG,"Found controlling endpoint"); } else if ((ep.getDirection() == UsbConstants.USB_DIR_OUT) &&
mControlEndpoint = ep; (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) {
} else if ((ep.getDirection() == UsbConstants.USB_DIR_IN) && Log.d(TAG,"Found writing endpoint");
(ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) { mWriteEndpoint = ep;
Log.d(TAG,"Found reading endpoint"); }
mReadEndpoint = ep;
} else if ((ep.getDirection() == UsbConstants.USB_DIR_OUT) &&
(ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)) { if ((mControlEndpoint != null) &&
Log.d(TAG,"Found writing endpoint"); (mReadEndpoint != null) &&
mWriteEndpoint = ep; (mWriteEndpoint != null)) {
} Log.d(TAG,"Found all required endpoints");
break;
}
if ((mControlEndpoint != null) && }
(mReadEndpoint != null) &&
(mWriteEndpoint != null)) { if ((mControlEndpoint == null) ||
Log.d(TAG,"Found all required endpoints"); (mReadEndpoint == null) ||
break; (mWriteEndpoint == null)) {
} Log.d(TAG,"Could not establish all endpoints");
} throw new IOException("Could not establish all endpoints");
}
if ((mControlEndpoint == null) || }
(mReadEndpoint == null) ||
(mWriteEndpoint == null)) { private void openInterface() throws IOException {
Log.d(TAG,"Could not establish all endpoints"); Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount());
throw new IOException("Could not establish all endpoints");
} mControlInterface = null;
} mDataInterface = null;
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
private void openInterface() throws IOException { UsbInterface usbInterface = mDevice.getInterface(i);
Log.d(TAG, "claiming interfaces, count=" + mDevice.getInterfaceCount()); if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM) {
mControlIndex = i;
mControlInterface = null; mControlInterface = usbInterface;
mDataInterface = null; }
for (int i = 0; i < mDevice.getInterfaceCount(); i++) { if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) {
UsbInterface usbInterface = mDevice.getInterface(i); mDataInterface = usbInterface;
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_COMM) { }
mControlIndex = i; }
mControlInterface = usbInterface;
} if(mControlInterface == null) {
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_CDC_DATA) { throw new IOException("no control interface.");
mDataInterface = usbInterface; }
} Log.d(TAG, "Control iface=" + mControlInterface);
}
if (!mConnection.claimInterface(mControlInterface, true)) {
if(mControlInterface == null) { throw new IOException("Could not claim control interface.");
throw new IOException("no control interface."); }
}
Log.d(TAG, "Control iface=" + mControlInterface); mControlEndpoint = mControlInterface.getEndpoint(0);
if (mControlEndpoint.getDirection() != UsbConstants.USB_DIR_IN || mControlEndpoint.getType() != UsbConstants.USB_ENDPOINT_XFER_INT) {
if (!mConnection.claimInterface(mControlInterface, true)) { throw new IOException("invalid control endpoint");
throw new IOException("Could not claim control interface."); }
}
if(mDataInterface == null) {
mControlEndpoint = mControlInterface.getEndpoint(0); throw new IOException("no data interface.");
if (mControlEndpoint.getDirection() != UsbConstants.USB_DIR_IN || mControlEndpoint.getType() != UsbConstants.USB_ENDPOINT_XFER_INT) { }
throw new IOException("invalid control endpoint"); Log.d(TAG, "data iface=" + mDataInterface);
}
if (!mConnection.claimInterface(mDataInterface, true)) {
if(mDataInterface == null) { throw new IOException("Could not claim data interface.");
throw new IOException("no data interface."); }
}
Log.d(TAG, "data iface=" + mDataInterface); mReadEndpoint = null;
mWriteEndpoint = null;
if (!mConnection.claimInterface(mDataInterface, true)) { for (int i = 0; i < mDataInterface.getEndpointCount(); i++) {
throw new IOException("Could not claim data interface."); UsbEndpoint ep = mDataInterface.getEndpoint(i);
} if (ep.getDirection() == UsbConstants.USB_DIR_IN && ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)
mReadEndpoint = ep;
mReadEndpoint = null; if (ep.getDirection() == UsbConstants.USB_DIR_OUT && ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK)
mWriteEndpoint = null; mWriteEndpoint = ep;
for (int i = 0; i < mDataInterface.getEndpointCount(); i++) { }
UsbEndpoint ep = mDataInterface.getEndpoint(i); if (mReadEndpoint == null || mWriteEndpoint == null) {
if (ep.getDirection() == UsbConstants.USB_DIR_IN && ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) throw new IOException("Could not get read&write endpoints.");
mReadEndpoint = ep; }
if (ep.getDirection() == UsbConstants.USB_DIR_OUT && ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) }
mWriteEndpoint = ep;
} private int sendAcmControlMessage(int request, int value, byte[] buf) throws IOException {
if (mReadEndpoint == null || mWriteEndpoint == null) { int len = mConnection.controlTransfer(
throw new IOException("Could not get read&write endpoints."); USB_RT_ACM, request, value, mControlIndex, buf, buf != null ? buf.length : 0, 5000);
} if(len < 0) {
} throw new IOException("controlTransfer failed.");
}
private int sendAcmControlMessage(int request, int value, byte[] buf) throws IOException { return len;
int len = mConnection.controlTransfer( }
USB_RT_ACM, request, value, mControlIndex, buf, buf != null ? buf.length : 0, 5000);
if(len < 0) { @Override
throw new IOException("controlTransfer failed."); public void close() throws IOException {
} if (mConnection == null) {
return len; throw new IOException("Already closed");
} }
synchronized (this) {
@Override if (mUsbRequest != null)
public void close() throws IOException { mUsbRequest.cancel();
if (mConnection == null) { }
throw new IOException("Already closed"); mControlEndpoint = null;
} mReadEndpoint = null;
synchronized (this) { mWriteEndpoint = null;
if (mUsbRequest != null) try {
mUsbRequest.cancel(); mConnection.releaseInterface(mControlInterface);
} mConnection.releaseInterface(mDataInterface);
try { } catch(Exception ignored) {}
mConnection.releaseInterface(mControlInterface); try {
mConnection.releaseInterface(mDataInterface); mConnection.close();
} catch(Exception ignored) {} } finally {
try { mConnection = null;
mConnection.close(); }
} finally { }
mConnection = null;
} @Override
} public int read(byte[] dest, int timeoutMillis) throws IOException {
if(mConnection == null) {
@Override throw new IOException("Connection closed");
public int read(byte[] dest, int timeoutMillis) throws IOException { }
final UsbRequest request = new UsbRequest(); final UsbRequest request = new UsbRequest();
try { try {
if(mConnection == null) request.initialize(mConnection, mReadEndpoint);
throw new IOException("Connection closed"); final ByteBuffer buf = ByteBuffer.wrap(dest);
request.initialize(mConnection, mReadEndpoint); if (!request.queue(buf, dest.length)) {
final ByteBuffer buf = ByteBuffer.wrap(dest); throw new IOException("Error queueing request.");
if (!request.queue(buf, dest.length)) { }
throw new IOException("Error queueing request."); mUsbRequest = request;
} final UsbRequest response = mConnection.requestWait();
mUsbRequest = request; synchronized (this) {
final UsbRequest response = mConnection.requestWait(); mUsbRequest = null;
synchronized (this) { }
mUsbRequest = null; if (response == null) {
} throw new IOException("Null response");
if (response == null) { }
throw new IOException("Null response");
} final int nread = buf.position();
if (nread > 0) {
final int nread = buf.position(); //Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length)));
if (nread > 0) { return nread;
//Log.d(TAG, HexDump.dumpHexString(dest, 0, Math.min(32, dest.length))); } else {
return nread; return 0;
} else { }
return 0; } finally {
} mUsbRequest = null;
} finally { request.close();
mUsbRequest = null; }
request.close(); }
}
} @Override
public int write(byte[] src, int timeoutMillis) throws IOException {
@Override // TODO(mikey): Nearly identical to FtdiSerial write. Refactor.
public int write(byte[] src, int timeoutMillis) throws IOException { int offset = 0;
// TODO(mikey): Nearly identical to FtdiSerial write. Refactor.
int offset = 0; if(mConnection == null) {
throw new IOException("Connection closed");
while (offset < src.length) { }
final int writeLength; while (offset < src.length) {
final int amtWritten; final int writeLength;
final int amtWritten;
synchronized (mWriteBufferLock) {
final byte[] writeBuffer; synchronized (mWriteBufferLock) {
final byte[] writeBuffer;
writeLength = Math.min(src.length - offset, mWriteBuffer.length);
if (offset == 0) { writeLength = Math.min(src.length - offset, mWriteBuffer.length);
writeBuffer = src; if (offset == 0) {
} else { writeBuffer = src;
// bulkTransfer does not support offsets, make a copy. } else {
System.arraycopy(src, offset, mWriteBuffer, 0, writeLength); // bulkTransfer does not support offsets, make a copy.
writeBuffer = mWriteBuffer; System.arraycopy(src, offset, mWriteBuffer, 0, writeLength);
} writeBuffer = mWriteBuffer;
}
amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength,
timeoutMillis); amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength,
} timeoutMillis);
if (amtWritten <= 0) { }
throw new IOException("Error writing " + writeLength if (amtWritten <= 0) {
+ " bytes at offset " + offset + " length=" + src.length); throw new IOException("Error writing " + writeLength
} + " bytes at offset " + offset + " length=" + src.length);
}
Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength);
offset += amtWritten; Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength);
} offset += amtWritten;
return offset; }
} return offset;
}
@Override
public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException { @Override
byte stopBitsByte; public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException {
switch (stopBits) { byte stopBitsByte;
case STOPBITS_1: stopBitsByte = 0; break; switch (stopBits) {
case STOPBITS_1_5: stopBitsByte = 1; break; case STOPBITS_1: stopBitsByte = 0; break;
case STOPBITS_2: stopBitsByte = 2; break; case STOPBITS_1_5: stopBitsByte = 1; break;
default: throw new IllegalArgumentException("Bad value for stopBits: " + stopBits); case STOPBITS_2: stopBitsByte = 2; break;
} default: throw new IllegalArgumentException("Bad value for stopBits: " + stopBits);
}
byte parityBitesByte;
switch (parity) { byte parityBitesByte;
case PARITY_NONE: parityBitesByte = 0; break; switch (parity) {
case PARITY_ODD: parityBitesByte = 1; break; case PARITY_NONE: parityBitesByte = 0; break;
case PARITY_EVEN: parityBitesByte = 2; break; case PARITY_ODD: parityBitesByte = 1; break;
case PARITY_MARK: parityBitesByte = 3; break; case PARITY_EVEN: parityBitesByte = 2; break;
case PARITY_SPACE: parityBitesByte = 4; break; case PARITY_MARK: parityBitesByte = 3; break;
default: throw new IllegalArgumentException("Bad value for parity: " + parity); case PARITY_SPACE: parityBitesByte = 4; break;
} default: throw new IllegalArgumentException("Bad value for parity: " + parity);
}
byte[] msg = {
(byte) ( baudRate & 0xff), byte[] msg = {
(byte) ((baudRate >> 8 ) & 0xff), (byte) ( baudRate & 0xff),
(byte) ((baudRate >> 16) & 0xff), (byte) ((baudRate >> 8 ) & 0xff),
(byte) ((baudRate >> 24) & 0xff), (byte) ((baudRate >> 16) & 0xff),
stopBitsByte, (byte) ((baudRate >> 24) & 0xff),
parityBitesByte, stopBitsByte,
(byte) dataBits}; parityBitesByte,
sendAcmControlMessage(SET_LINE_CODING, 0, msg); (byte) dataBits};
} sendAcmControlMessage(SET_LINE_CODING, 0, msg);
}
@Override
public boolean getCD() throws IOException { @Override
return false; // TODO public boolean getCD() throws IOException {
} return false; // TODO
}
@Override
public boolean getCTS() throws IOException { @Override
return false; // TODO public boolean getCTS() throws IOException {
} return false; // TODO
}
@Override
public boolean getDSR() throws IOException { @Override
return false; // TODO public boolean getDSR() throws IOException {
} return false; // TODO
}
@Override
public boolean getDTR() throws IOException { @Override
return mDtr; public boolean getDTR() throws IOException {
} return mDtr;
}
@Override
public void setDTR(boolean value) throws IOException { @Override
mDtr = value; public void setDTR(boolean value) throws IOException {
setDtrRts(); mDtr = value;
} setDtrRts();
}
@Override
public boolean getRI() throws IOException { @Override
return false; // TODO public boolean getRI() throws IOException {
} return false; // TODO
}
@Override
public boolean getRTS() throws IOException { @Override
return mRts; public boolean getRTS() throws IOException {
} return mRts;
}
@Override
public void setRTS(boolean value) throws IOException { @Override
mRts = value; public void setRTS(boolean value) throws IOException {
setDtrRts(); mRts = value;
} setDtrRts();
}
private void setDtrRts() throws IOException {
int value = (mRts ? 0x2 : 0) | (mDtr ? 0x1 : 0); private void setDtrRts() throws IOException {
sendAcmControlMessage(SET_CONTROL_LINE_STATE, value, null); int value = (mRts ? 0x2 : 0) | (mDtr ? 0x1 : 0);
} sendAcmControlMessage(SET_CONTROL_LINE_STATE, value, null);
}
}
}
public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>(); public static Map<Integer, int[]> getSupportedDevices() {
supportedDevices.put(UsbId.VENDOR_ARDUINO, final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>();
new int[] { supportedDevices.put(UsbId.VENDOR_ARDUINO,
UsbId.ARDUINO_UNO, new int[] {
UsbId.ARDUINO_UNO_R3, UsbId.ARDUINO_UNO,
UsbId.ARDUINO_MEGA_2560, UsbId.ARDUINO_UNO_R3,
UsbId.ARDUINO_MEGA_2560_R3, UsbId.ARDUINO_MEGA_2560,
UsbId.ARDUINO_SERIAL_ADAPTER, UsbId.ARDUINO_MEGA_2560_R3,
UsbId.ARDUINO_SERIAL_ADAPTER_R3, UsbId.ARDUINO_SERIAL_ADAPTER,
UsbId.ARDUINO_MEGA_ADK, UsbId.ARDUINO_SERIAL_ADAPTER_R3,
UsbId.ARDUINO_MEGA_ADK_R3, UsbId.ARDUINO_MEGA_ADK,
UsbId.ARDUINO_LEONARDO, UsbId.ARDUINO_MEGA_ADK_R3,
UsbId.ARDUINO_MICRO, UsbId.ARDUINO_LEONARDO,
}); UsbId.ARDUINO_MICRO,
supportedDevices.put(UsbId.VENDOR_VAN_OOIJEN_TECH, });
new int[] { supportedDevices.put(UsbId.VENDOR_VAN_OOIJEN_TECH,
UsbId.VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL, new int[] {
}); UsbId.VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL,
supportedDevices.put(UsbId.VENDOR_ATMEL, });
new int[] { supportedDevices.put(UsbId.VENDOR_ATMEL,
UsbId.ATMEL_LUFA_CDC_DEMO_APP, new int[] {
}); UsbId.ATMEL_LUFA_CDC_DEMO_APP,
supportedDevices.put(UsbId.VENDOR_LEAFLABS, });
new int[] { supportedDevices.put(UsbId.VENDOR_LEAFLABS,
UsbId.LEAFLABS_MAPLE, new int[] {
}); UsbId.LEAFLABS_MAPLE,
supportedDevices.put(UsbId.VENDOR_ARM, });
new int[] { supportedDevices.put(UsbId.VENDOR_ARM,
UsbId.ARM_MBED, new int[] {
}); UsbId.ARM_MBED,
return supportedDevices; });
} return supportedDevices;
}
}
}

View File

@ -130,11 +130,7 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
opened = true; opened = true;
} finally { } finally {
if (!opened) { if (!opened) {
try { close();
close();
} catch (IOException e) {
// Ignore IOExceptions during close()
}
} }
} }
} }
@ -162,10 +158,11 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
@Override @Override
public int read(byte[] dest, int timeoutMillis) throws IOException { public int read(byte[] dest, int timeoutMillis) throws IOException {
if(mConnection == null) {
throw new IOException("Connection closed");
}
final UsbRequest request = new UsbRequest(); final UsbRequest request = new UsbRequest();
try { try {
if(mConnection == null)
throw new IOException("Connection closed");
request.initialize(mConnection, mReadEndpoint); request.initialize(mConnection, mReadEndpoint);
final ByteBuffer buf = ByteBuffer.wrap(dest); final ByteBuffer buf = ByteBuffer.wrap(dest);
if (!request.queue(buf, dest.length)) { if (!request.queue(buf, dest.length)) {
@ -197,6 +194,9 @@ public class Ch34xSerialDriver implements UsbSerialDriver {
public int write(byte[] src, int timeoutMillis) throws IOException { public int write(byte[] src, int timeoutMillis) throws IOException {
int offset = 0; int offset = 0;
if(mConnection == null) {
throw new IOException("Connection closed");
}
while (offset < src.length) { while (offset < src.length) {
final int writeLength; final int writeLength;
final int amtWritten; final int amtWritten;

View File

@ -123,9 +123,13 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
return Cp21xxSerialDriver.this; return Cp21xxSerialDriver.this;
} }
private int setConfigSingle(int request, int value) { private int setConfigSingle(int request, int value) throws IOException {
return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request, value, int result = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request, value,
mPortNumber, null, 0, USB_WRITE_TIMEOUT_MILLIS); mPortNumber, null, 0, USB_WRITE_TIMEOUT_MILLIS);
if (result != 0) {
throw new IOException("Setting baudrate failed: result=" + result);
}
return result;
} }
@Override @Override
@ -163,11 +167,7 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
opened = true; opened = true;
} finally { } finally {
if (!opened) { if (!opened) {
try { close();
close();
} catch (IOException e) {
// Ignore IOExceptions during close()
}
} }
} }
} }
@ -197,10 +197,11 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
@Override @Override
public int read(byte[] dest, int timeoutMillis) throws IOException { public int read(byte[] dest, int timeoutMillis) throws IOException {
if(mConnection == null) {
throw new IOException("Connection closed");
}
final UsbRequest request = new UsbRequest(); final UsbRequest request = new UsbRequest();
try { try {
if(mConnection == null)
throw new IOException("Connection closed");
request.initialize(mConnection, mReadEndpoint); request.initialize(mConnection, mReadEndpoint);
final ByteBuffer buf = ByteBuffer.wrap(dest); final ByteBuffer buf = ByteBuffer.wrap(dest);
if (!request.queue(buf, dest.length)) { if (!request.queue(buf, dest.length)) {
@ -232,6 +233,9 @@ public class Cp21xxSerialDriver implements UsbSerialDriver {
public int write(byte[] src, int timeoutMillis) throws IOException { public int write(byte[] src, int timeoutMillis) throws IOException {
int offset = 0; int offset = 0;
if(mConnection == null) {
throw new IOException("Connection closed");
}
while (offset < src.length) { while (offset < src.length) {
final int writeLength; final int writeLength;
final int amtWritten; final int amtWritten;

View File

@ -257,7 +257,6 @@ public class FtdiSerialDriver implements UsbSerialDriver {
} finally { } finally {
if (!opened) { if (!opened) {
close(); close();
mConnection = null;
} }
} }
} }
@ -279,12 +278,13 @@ public class FtdiSerialDriver implements UsbSerialDriver {
@Override @Override
public int read(byte[] dest, int timeoutMillis) throws IOException { public int read(byte[] dest, int timeoutMillis) throws IOException {
if(mConnection == null) {
throw new IOException("Connection closed");
}
final UsbEndpoint endpoint = mDevice.getInterface(mPortNumber).getEndpoint(0); final UsbEndpoint endpoint = mDevice.getInterface(mPortNumber).getEndpoint(0);
final UsbRequest request = new UsbRequest(); final UsbRequest request = new UsbRequest();
final ByteBuffer buf = ByteBuffer.wrap(dest); final ByteBuffer buf = ByteBuffer.wrap(dest);
try { try {
if(mConnection == null)
throw new IOException("Connection closed");
request.initialize(mConnection, endpoint); request.initialize(mConnection, endpoint);
if (!request.queue(buf, dest.length)) { if (!request.queue(buf, dest.length)) {
throw new IOException("Error queueing request."); throw new IOException("Error queueing request.");
@ -308,6 +308,9 @@ public class FtdiSerialDriver implements UsbSerialDriver {
@Override @Override
public int write(byte[] src, int timeoutMillis) throws IOException { public int write(byte[] src, int timeoutMillis) throws IOException {
if(mConnection == null) {
throw new IOException("Connection closed");
}
final UsbEndpoint endpoint = mDevice.getInterface(mPortNumber).getEndpoint(1); final UsbEndpoint endpoint = mDevice.getInterface(mPortNumber).getEndpoint(1);
int offset = 0; int offset = 0;

View File

@ -65,25 +65,19 @@ public class ProbeTable {
try { try {
method = driverClass.getMethod("getSupportedDevices"); method = driverClass.getMethod("getSupportedDevices");
} catch (SecurityException e) { } catch (SecurityException | NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
final Map<Integer, int[]> devices; final Map<Integer, int[]> devices;
try { try {
devices = (Map<Integer, int[]>) method.invoke(null); devices = (Map<Integer, int[]>) method.invoke(null);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
for (Map.Entry<Integer, int[]> entry : devices.entrySet()) { for (Map.Entry<Integer, int[]> entry : devices.entrySet()) {
final int vendorId = entry.getKey().intValue(); final int vendorId = entry.getKey();
for (int productId : entry.getValue()) { for (int productId : entry.getValue()) {
addProduct(vendorId, productId, driverClass); addProduct(vendorId, productId, driverClass);
} }

View File

@ -336,8 +336,7 @@ public class ProlificSerialDriver implements UsbSerialDriver {
opened = true; opened = true;
} finally { } finally {
if (!opened) { if (!opened) {
mConnection = null; close();
connection.releaseInterface(usbInterface);
} }
} }
} }
@ -373,10 +372,11 @@ public class ProlificSerialDriver implements UsbSerialDriver {
@Override @Override
public int read(byte[] dest, int timeoutMillis) throws IOException { public int read(byte[] dest, int timeoutMillis) throws IOException {
if(mConnection == null) {
throw new IOException("Connection closed");
}
final UsbRequest request = new UsbRequest(); final UsbRequest request = new UsbRequest();
try { try {
if(mConnection == null)
throw new IOException("Connection closed");
request.initialize(mConnection, mReadEndpoint); request.initialize(mConnection, mReadEndpoint);
final ByteBuffer buf = ByteBuffer.wrap(dest); final ByteBuffer buf = ByteBuffer.wrap(dest);
if (!request.queue(buf, dest.length)) { if (!request.queue(buf, dest.length)) {
@ -404,6 +404,9 @@ public class ProlificSerialDriver implements UsbSerialDriver {
public int write(byte[] src, int timeoutMillis) throws IOException { public int write(byte[] src, int timeoutMillis) throws IOException {
int offset = 0; int offset = 0;
if(mConnection == null) {
throw new IOException("Connection closed");
}
while (offset < src.length) { while (offset < src.length) {
final int writeLength; final int writeLength;
final int amtWritten; final int amtWritten;

View File

@ -95,15 +95,8 @@ public class UsbSerialProber {
final Constructor<? extends UsbSerialDriver> ctor = final Constructor<? extends UsbSerialDriver> ctor =
driverClass.getConstructor(UsbDevice.class); driverClass.getConstructor(UsbDevice.class);
driver = ctor.newInstance(usbDevice); driver = ctor.newInstance(usbDevice);
} catch (NoSuchMethodException e) { } catch (NoSuchMethodException | IllegalArgumentException | InstantiationException |
throw new RuntimeException(e); IllegalAccessException | InvocationTargetException e) {
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
return driver; return driver;