mirror of
				https://github.com/mik3y/usb-serial-for-android
				synced 2025-10-31 02:17:23 +00:00 
			
		
		
		
	
						commit
						b9b9c7268f
					
				| @ -17,4 +17,7 @@ | |||||||
|      |      | ||||||
|     <!-- 0x067B / 0x2303: Prolific PL2303 --> |     <!-- 0x067B / 0x2303: Prolific PL2303 --> | ||||||
|     <usb-device vendor-id="1659" product-id="8963" /> |     <usb-device vendor-id="1659" product-id="8963" /> | ||||||
|  |      | ||||||
|  |     <!-- 0x1a86 / 0x7523: Qinheng CH340 --> | ||||||
|  |     <usb-device vendor-id="6790" product-id="29987" /> | ||||||
| </resources> | </resources> | ||||||
|  | |||||||
| @ -0,0 +1,363 @@ | |||||||
|  | /* Copyright 2014 Andreas Butti | ||||||
|  |  * | ||||||
|  |  * 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. | ||||||
|  |  * | ||||||
|  |  * Project home page: https://github.com/mik3y/usb-serial-for-android | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | package com.hoho.android.usbserial.driver; | ||||||
|  | 
 | ||||||
|  | import android.hardware.usb.UsbConstants; | ||||||
|  | import android.hardware.usb.UsbDevice; | ||||||
|  | import android.hardware.usb.UsbDeviceConnection; | ||||||
|  | import android.hardware.usb.UsbEndpoint; | ||||||
|  | import android.hardware.usb.UsbInterface; | ||||||
|  | import android.util.Log; | ||||||
|  | 
 | ||||||
|  | import java.io.IOException; | ||||||
|  | import java.util.Collections; | ||||||
|  | import java.util.LinkedHashMap; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Map; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Driver for CH340, maybe also working with CH341, but not tested | ||||||
|  |  * See http://wch-ic.com/product/usb/ch340.asp | ||||||
|  |  * | ||||||
|  |  * @author Andreas Butti | ||||||
|  |  */ | ||||||
|  | public class Ch34xSerialDriver implements UsbSerialDriver { | ||||||
|  | 
 | ||||||
|  | 	private static final String TAG = Ch34xSerialDriver.class.getSimpleName(); | ||||||
|  | 
 | ||||||
|  | 	private final UsbDevice mDevice; | ||||||
|  | 	private final UsbSerialPort mPort; | ||||||
|  | 
 | ||||||
|  | 	public Ch34xSerialDriver(UsbDevice device) { | ||||||
|  | 		mDevice = device; | ||||||
|  | 		mPort = new Ch340SerialPort(mDevice, 0); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Override | ||||||
|  | 	public UsbDevice getDevice() { | ||||||
|  | 		return mDevice; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Override | ||||||
|  | 	public List<UsbSerialPort> getPorts() { | ||||||
|  | 		return Collections.singletonList(mPort); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public class Ch340SerialPort extends CommonUsbSerialPort { | ||||||
|  | 
 | ||||||
|  | 		private static final int USB_TIMEOUT_MILLIS = 5000; | ||||||
|  | 
 | ||||||
|  | 		private final int DEFAULT_BAUD_RATE = 9600; | ||||||
|  | 
 | ||||||
|  | 		private boolean dtr = false; | ||||||
|  | 		private boolean rts = false; | ||||||
|  | 
 | ||||||
|  | 		private UsbEndpoint mReadEndpoint; | ||||||
|  | 		private UsbEndpoint mWriteEndpoint; | ||||||
|  | 
 | ||||||
|  | 		public Ch340SerialPort(UsbDevice device, int portNumber) { | ||||||
|  | 			super(device, portNumber); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public UsbSerialDriver getDriver() { | ||||||
|  | 			return Ch34xSerialDriver.this; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public void open(UsbDeviceConnection connection) throws IOException { | ||||||
|  | 			if (mConnection != null) { | ||||||
|  | 				throw new IOException("Already opened."); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			mConnection = connection; | ||||||
|  | 			boolean opened = false; | ||||||
|  | 			try { | ||||||
|  | 				for (int i = 0; i < mDevice.getInterfaceCount(); i++) { | ||||||
|  | 					UsbInterface usbIface = mDevice.getInterface(i); | ||||||
|  | 					if (mConnection.claimInterface(usbIface, true)) { | ||||||
|  | 						Log.d(TAG, "claimInterface " + i + " SUCCESS"); | ||||||
|  | 					} else { | ||||||
|  | 						Log.d(TAG, "claimInterface " + i + " FAIL"); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				UsbInterface dataIface = mDevice.getInterface(mDevice.getInterfaceCount() - 1); | ||||||
|  | 				for (int i = 0; i < dataIface.getEndpointCount(); i++) { | ||||||
|  | 					UsbEndpoint ep = dataIface.getEndpoint(i); | ||||||
|  | 					if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) { | ||||||
|  | 						if (ep.getDirection() == UsbConstants.USB_DIR_IN) { | ||||||
|  | 							mReadEndpoint = ep; | ||||||
|  | 						} else { | ||||||
|  | 							mWriteEndpoint = ep; | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 				initialize(); | ||||||
|  | 				setBaudRate(DEFAULT_BAUD_RATE); | ||||||
|  | 
 | ||||||
|  | 				opened = true; | ||||||
|  | 			} finally { | ||||||
|  | 				if (!opened) { | ||||||
|  | 					try { | ||||||
|  | 						close(); | ||||||
|  | 					} catch (IOException e) { | ||||||
|  | 						// Ignore IOExceptions during close() | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public void close() throws IOException { | ||||||
|  | 			if (mConnection == null) { | ||||||
|  | 				throw new IOException("Already closed"); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// TODO: nothing sended on close, maybe needed? | ||||||
|  | 
 | ||||||
|  | 			try { | ||||||
|  | 				mConnection.close(); | ||||||
|  | 			} finally { | ||||||
|  | 				mConnection = null; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public int read(byte[] dest, int timeoutMillis) throws IOException { | ||||||
|  | 			final int numBytesRead; | ||||||
|  | 			synchronized (mReadBufferLock) { | ||||||
|  | 				int readAmt = Math.min(dest.length, mReadBuffer.length); | ||||||
|  | 				numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt, | ||||||
|  | 						timeoutMillis); | ||||||
|  | 				if (numBytesRead < 0) { | ||||||
|  | 					// This sucks: we get -1 on timeout, not 0 as preferred. | ||||||
|  | 					// We *should* use UsbRequest, except it has a bug/api oversight | ||||||
|  | 					// where there is no way to determine the number of bytes read | ||||||
|  | 					// in response :\ -- http://b.android.com/28023 | ||||||
|  | 					return 0; | ||||||
|  | 				} | ||||||
|  | 				System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead); | ||||||
|  | 			} | ||||||
|  | 			return numBytesRead; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public int write(byte[] src, int timeoutMillis) throws IOException { | ||||||
|  | 			int offset = 0; | ||||||
|  | 
 | ||||||
|  | 			while (offset < src.length) { | ||||||
|  | 				final int writeLength; | ||||||
|  | 				final int amtWritten; | ||||||
|  | 
 | ||||||
|  | 				synchronized (mWriteBufferLock) { | ||||||
|  | 					final byte[] writeBuffer; | ||||||
|  | 
 | ||||||
|  | 					writeLength = Math.min(src.length - offset, mWriteBuffer.length); | ||||||
|  | 					if (offset == 0) { | ||||||
|  | 						writeBuffer = src; | ||||||
|  | 					} else { | ||||||
|  | 						// bulkTransfer does not support offsets, make a copy. | ||||||
|  | 						System.arraycopy(src, offset, mWriteBuffer, 0, writeLength); | ||||||
|  | 						writeBuffer = mWriteBuffer; | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength, | ||||||
|  | 							timeoutMillis); | ||||||
|  | 				} | ||||||
|  | 				if (amtWritten <= 0) { | ||||||
|  | 					throw new IOException("Error writing " + writeLength | ||||||
|  | 							+ " bytes at offset " + offset + " length=" + src.length); | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength); | ||||||
|  | 				offset += amtWritten; | ||||||
|  | 			} | ||||||
|  | 			return offset; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private int controlOut(int request, int value, int index) { | ||||||
|  | 			final int REQTYPE_HOST_TO_DEVICE = 0x41; | ||||||
|  | 			return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request, | ||||||
|  | 					value, index, null, 0, USB_TIMEOUT_MILLIS); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		private int controlIn(int request, int value, int index, byte[] buffer) { | ||||||
|  | 			final int REQTYPE_HOST_TO_DEVICE = UsbConstants.USB_TYPE_VENDOR | UsbConstants.USB_DIR_IN; | ||||||
|  | 			return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request, | ||||||
|  | 					value, index, buffer, buffer.length, USB_TIMEOUT_MILLIS); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		private void checkState(String msg, int request, int value, int[] expected) throws IOException { | ||||||
|  | 			byte[] buffer = new byte[expected.length]; | ||||||
|  | 			int ret = controlIn(request, value, 0, buffer); | ||||||
|  | 
 | ||||||
|  | 			if (ret < 0) { | ||||||
|  | 				throw new IOException("Faild send cmd [" + msg + "]"); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if (ret != expected.length) { | ||||||
|  | 				throw new IOException("Expected " + expected.length + " bytes, but get " + ret + " [" + msg + "]"); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			for (int i = 0; i < expected.length; i++) { | ||||||
|  | 				if (expected[i] == -1) { | ||||||
|  | 					continue; | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				int current = buffer[i] & 0xff; | ||||||
|  | 				if (expected[i] != current) { | ||||||
|  | 					throw new IOException("Expected 0x" + Integer.toHexString(expected[i]) + " bytes, but get 0x" + Integer.toHexString(current) + " [" + msg + "]"); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private void writeHandshakeByte() throws IOException { | ||||||
|  | 			if (controlOut(0xa4, ~((dtr ? 1 << 5 : 0) | (rts ? 1 << 6 : 0)), 0) < 0) { | ||||||
|  | 				throw new IOException("Faild to set handshake byte"); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		private void initialize() throws IOException { | ||||||
|  | 			checkState("init #1", 0x5f, 0, new int[]{-1 /* 0x27, 0x30 */, 0x00}); | ||||||
|  | 
 | ||||||
|  | 			if (controlOut(0xa1, 0, 0) < 0) { | ||||||
|  | 				throw new IOException("init failed! #2"); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			setBaudRate(DEFAULT_BAUD_RATE); | ||||||
|  | 
 | ||||||
|  | 			checkState("init #4", 0x95, 0x2518, new int[]{-1 /* 0x56, c3*/, 0x00}); | ||||||
|  | 
 | ||||||
|  | 			if (controlOut(0x9a, 0x2518, 0x0050) < 0) { | ||||||
|  | 				throw new IOException("init failed! #5"); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			checkState("init #6", 0x95, 0x0706, new int[]{0xff, 0xee}); | ||||||
|  | 
 | ||||||
|  | 			if (controlOut(0xa1, 0x501f, 0xd90a) < 0) { | ||||||
|  | 				throw new IOException("init failed! #7"); | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			setBaudRate(DEFAULT_BAUD_RATE); | ||||||
|  | 
 | ||||||
|  | 			writeHandshakeByte(); | ||||||
|  | 
 | ||||||
|  | 			checkState("init #10", 0x95, 0x0706, new int[]{-1/* 0x9f, 0xff*/, 0xee}); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		private void setBaudRate(int baudRate) throws IOException { | ||||||
|  | 			int[] baud = new int[]{2400, 0xd901, 0x0038, 4800, 0x6402, | ||||||
|  | 					0x001f, 9600, 0xb202, 0x0013, 19200, 0xd902, 0x000d, 38400, | ||||||
|  | 					0x6403, 0x000a, 115200, 0xcc03, 0x0008}; | ||||||
|  | 
 | ||||||
|  | 			for (int i = 0; i < baud.length / 3; i++) { | ||||||
|  | 				if (baud[i * 3] == baudRate) { | ||||||
|  | 					int ret = controlOut(0x9a, 0x1312, baud[i * 3 + 1]); | ||||||
|  | 					if (ret < 0) { | ||||||
|  | 						throw new IOException("Error setting baud rate. #1"); | ||||||
|  | 					} | ||||||
|  | 					ret = controlOut(0x9a, 0x0f2c, baud[i * 3 + 2]); | ||||||
|  | 					if (ret < 0) { | ||||||
|  | 						throw new IOException("Error setting baud rate. #1"); | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					return; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 			throw new IOException("Baud rate " + baudRate + " currently not supported"); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public void setParameters(int baudRate, int dataBits, int stopBits, int parity) | ||||||
|  | 				throws IOException { | ||||||
|  | 			setBaudRate(baudRate); | ||||||
|  | 
 | ||||||
|  | 			// TODO databit, stopbit and paraty set not implemented | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public boolean getCD() throws IOException { | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public boolean getCTS() throws IOException { | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public boolean getDSR() throws IOException { | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public boolean getDTR() throws IOException { | ||||||
|  | 			return dtr; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public void setDTR(boolean value) throws IOException { | ||||||
|  | 			dtr = value; | ||||||
|  | 			writeHandshakeByte(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public boolean getRI() throws IOException { | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public boolean getRTS() throws IOException { | ||||||
|  | 			return rts; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public void setRTS(boolean value) throws IOException { | ||||||
|  | 			rts = value; | ||||||
|  | 			writeHandshakeByte(); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		@Override | ||||||
|  | 		public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException { | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	public static Map<Integer, int[]> getSupportedDevices() { | ||||||
|  | 		final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>(); | ||||||
|  | 		supportedDevices.put(UsbId.VENDOR_QINHENG, new int[]{ | ||||||
|  | 				UsbId.QINHENG_HL340 | ||||||
|  | 		}); | ||||||
|  | 		return supportedDevices; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -64,6 +64,9 @@ public final class UsbId { | |||||||
|     public static final int VENDOR_PROLIFIC = 0x067b; |     public static final int VENDOR_PROLIFIC = 0x067b; | ||||||
|     public static final int PROLIFIC_PL2303 = 0x2303; |     public static final int PROLIFIC_PL2303 = 0x2303; | ||||||
| 
 | 
 | ||||||
|  |     public static final int VENDOR_QINHENG = 0x1a86; | ||||||
|  |     public static final int QINHENG_HL340 = 0x7523; | ||||||
|  | 
 | ||||||
|     private UsbId() { |     private UsbId() { | ||||||
|         throw new IllegalAccessError("Non-instantiable class."); |         throw new IllegalAccessError("Non-instantiable class."); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -51,6 +51,7 @@ public class UsbSerialProber { | |||||||
|         probeTable.addDriver(Cp21xxSerialDriver.class); |         probeTable.addDriver(Cp21xxSerialDriver.class); | ||||||
|         probeTable.addDriver(FtdiSerialDriver.class); |         probeTable.addDriver(FtdiSerialDriver.class); | ||||||
|         probeTable.addDriver(ProlificSerialDriver.class); |         probeTable.addDriver(ProlificSerialDriver.class); | ||||||
|  |         probeTable.addDriver(Ch34xSerialDriver.class); | ||||||
|         return probeTable; |         return probeTable; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user