001package jmri.jmrix; 002 003import java.io.DataInputStream; 004import java.io.DataOutputStream; 005import java.util.ArrayList; 006import java.util.List; 007import javax.annotation.Nonnull; 008import javax.usb.UsbControlIrp; 009import javax.usb.UsbDevice; 010import javax.usb.UsbDisconnectedException; 011import javax.usb.UsbException; 012 013import jmri.SystemConnectionMemo; 014import jmri.util.usb.UsbUtil; 015import org.slf4j.Logger; 016import org.slf4j.LoggerFactory; 017 018/** 019 * Enables basic setup of a USB interface for a jmrix implementation. 020 * 021 * @author George Warner Copyright (c) 2017-2018 022 */ 023public class UsbPortAdapter extends AbstractPortController { 024 025 private Short vendorID = 0; 026 private Short productID = 0; 027 private String serialNumber = null; 028 protected UsbDevice usbDevice = null; 029 030 public UsbPortAdapter(SystemConnectionMemo memo) { 031 super(memo); 032 } 033 034 public Short getVendorID() { 035 return vendorID; 036 } 037 038 public void setVendorID(Short value) { 039 vendorID = value; 040 } 041 042 public Short getProductID() { 043 return productID; 044 } 045 046 public void setProductID(Short value) { 047 productID = value; 048 } 049 050 /** 051 * Get the device serial number. 052 * 053 * @return the serial number or null if there is no serial number 054 */ 055 public String getSerialNumber() { 056 if (serialNumber != null && serialNumber.trim().isEmpty()) { 057 serialNumber = null; 058 } 059 return serialNumber; 060 } 061 062 /** 063 * Set the device serial number. 064 * 065 * @param serialNumber the serial number; if null, empty, or only containing 066 * whitespace, sets property to null 067 */ 068 public void setSerialNumber(String serialNumber) { 069 if (serialNumber == null || serialNumber.trim().isEmpty()) { 070 this.serialNumber = null; 071 } else { 072 this.serialNumber = serialNumber; 073 } 074 } 075 076 public UsbDevice getUsbDevice() { 077 if (usbDevice == null) { 078 log.debug("Getting device at {}", port); 079 String error = openPort(port, serialNumber); 080 if (error != null) { 081 log.error("Could not open {}",error); 082 } 083 } 084 return usbDevice; 085 } 086 087 public String openPort(String portName, String serialNumber) { 088 usbDevice = UsbUtil.getMatchingDevice(vendorID, productID, serialNumber, portName); 089 if (usbDevice == null) { 090 List< UsbDevice> usbDevices = UsbUtil.getMatchingDevices(vendorID, productID, serialNumber); 091 if (usbDevices.size() == 1) { 092 usbDevice = usbDevices.get(0); 093 } else { 094 // search for device with same vendor/product ID, but possibly different serial number 095 usbDevices = UsbUtil.getMatchingDevices(vendorID, productID, null); 096 if (usbDevices.size() == 1) { 097 usbDevice = usbDevices.get(0); 098 } else { 099 return String.format("Single USB device with vendor id %s and product id %s not found.", vendorID, productID); 100 } 101 } 102 } 103 return null; 104 } 105 106 /** 107 * {@inheritDoc} 108 */ 109 @Override 110 public void connect() throws java.io.IOException { 111 log.debug("connect()"); 112 } 113 114 /** 115 * {@inheritDoc} 116 */ 117 @Override 118 public DataInputStream getInputStream() { 119 log.debug("getInputStream()"); 120 return null; 121 } 122 123 /** 124 * {@inheritDoc} 125 */ 126 @Override 127 public DataOutputStream getOutputStream() { 128 log.debug("getOutputStream()"); 129 return null; 130 } 131 132 /** 133 * {@inheritDoc} 134 */ 135 @Override 136 public void recover() { 137 log.debug("recover()"); 138 } 139 140 /** 141 * {@inheritDoc} 142 */ 143 @Override 144 public void configure() { 145 log.debug("configure()"); 146 } 147 148 /** 149 * Get the list of USB locations with devices matching a single 150 * vendor/product ID combination. These are "portNames" to match the calling 151 * API. 152 * 153 * @return the list of locations with matching devices; this is an empty 154 * list if there are no matches 155 */ 156 @Nonnull 157 public List<String> getPortNames() { 158 log.debug("getPortNames()"); 159 160 List<String> results = new ArrayList<>(); 161 List<UsbDevice> usbDevices = UsbUtil.getMatchingDevices(vendorID, productID, null); 162 usbDevices.forEach((device) -> { 163 results.add(UsbUtil.getLocation(device)); 164 }); 165 166 return results; 167 } 168 169 private String port = null; 170 171 public void setPort(String s) { 172 log.debug("setPort('{}')", s); 173 port = s; 174 } 175 176 /** 177 * {@inheritDoc} 178 */ 179 @Override 180 public String getCurrentPortName() { 181 log.debug("getCurrentPortName()"); 182 return port; 183 } 184 185 /** 186 * send USB control transfer 187 * 188 * @param requestType the request type 189 * @param request the request 190 * @param value the value 191 * @param index the index 192 * @param data the data 193 * @return true if successful sent 194 */ 195 public boolean sendControlTransfer(int requestType, int request, int value, int index, byte[] data) { 196 boolean result = false; // assume failure (pessimist!) 197 if (usbDevice != null) { 198 try { 199 UsbControlIrp usbControlIrp = usbDevice.createUsbControlIrp( 200 (byte) requestType, (byte) request, 201 (short) value, (short) index); 202 if (data == null) { 203 data = new byte[0]; 204 } 205 usbControlIrp.setData(data); 206 usbControlIrp.setLength(data.length); 207 208 //log.trace("sendControlTransfer, requestType: {}, request: {}, value: {}, index: {}, data: {}", requestType, request, value, index, getByteString(data)); 209 usbDevice.syncSubmit(usbControlIrp); 210 result = true; // it's good! 211 } catch (IllegalArgumentException | UsbException | UsbDisconnectedException e) { 212 log.error("Exception transferring control", e); 213 } 214 } 215 return result; 216 } 217 218 private final static Logger log = LoggerFactory.getLogger(UsbPortAdapter.class 219 ); 220}