From e132eccb27f1a88540fa97598193d241be22f8c3 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 27 Jul 2014 16:42:29 -0700 Subject: [PATCH] Major rework to support Linux's in-box USB/IP implementation. It still works best on Windows, but interrupt devices seem to do fine on Linux --- jni/Android.mk | 1 + jni/Application.mk | 10 + jni/errno/Android.mk | 12 + jni/errno/errno_jni.c | 11 + libs/arm64-v8a/liberrno.so | Bin 0 -> 5368 bytes libs/armeabi-v7a/liberrno.so | Bin 0 -> 13476 bytes libs/armeabi/liberrno.so | Bin 0 -> 13468 bytes libs/mips/liberrno.so | Bin 0 -> 5252 bytes libs/mips64/liberrno.so | Bin 0 -> 5704 bytes libs/x86/liberrno.so | Bin 0 -> 5176 bytes libs/x86_64/liberrno.so | Bin 0 -> 5632 bytes src/org/cgutman/usbip/errno/Errno.java | 12 + src/org/cgutman/usbip/server/UsbIpServer.java | 31 +- .../usbip/server/UsbRequestHandler.java | 8 +- .../usbip/server/protocol/ProtoDefs.java | 2 +- .../server/protocol/cli/CommonPacket.java | 10 +- .../server/protocol/cli/DevListReply.java | 4 +- .../protocol/cli/ImportDeviceReply.java | 4 +- .../protocol/dev/UsbIpDevicePacket.java | 8 +- .../server/protocol/dev/UsbIpUnlinkUrb.java | 48 +++ .../protocol/dev/UsbIpUnlinkUrbReply.java | 28 ++ .../cgutman/usbip/service/UsbIpService.java | 292 ++++++++++++------ .../cgutman/usbip/usb/DescriptorReader.java | 26 -- .../cgutman/usbip/usb/UsbControlHelper.java | 63 ++++ src/org/cgutman/usbip/usb/XferUtils.java | 43 ++- 25 files changed, 463 insertions(+), 150 deletions(-) create mode 100644 jni/Android.mk create mode 100644 jni/Application.mk create mode 100644 jni/errno/Android.mk create mode 100644 jni/errno/errno_jni.c create mode 100755 libs/arm64-v8a/liberrno.so create mode 100755 libs/armeabi-v7a/liberrno.so create mode 100755 libs/armeabi/liberrno.so create mode 100755 libs/mips/liberrno.so create mode 100755 libs/mips64/liberrno.so create mode 100755 libs/x86/liberrno.so create mode 100755 libs/x86_64/liberrno.so create mode 100644 src/org/cgutman/usbip/errno/Errno.java create mode 100644 src/org/cgutman/usbip/server/protocol/dev/UsbIpUnlinkUrb.java create mode 100644 src/org/cgutman/usbip/server/protocol/dev/UsbIpUnlinkUrbReply.java delete mode 100644 src/org/cgutman/usbip/usb/DescriptorReader.java create mode 100644 src/org/cgutman/usbip/usb/UsbControlHelper.java diff --git a/jni/Android.mk b/jni/Android.mk new file mode 100644 index 0000000..5053e7d --- /dev/null +++ b/jni/Android.mk @@ -0,0 +1 @@ +include $(call all-subdir-makefiles) diff --git a/jni/Application.mk b/jni/Application.mk new file mode 100644 index 0000000..1c97370 --- /dev/null +++ b/jni/Application.mk @@ -0,0 +1,10 @@ +# Application.mk for USB/IP Server + +# Our minimum version is Android 3.1 +APP_PLATFORM := android-12 + +# Build for all ABIs +APP_ABI := all + +# We want an optimized build +APP_OPTIM := release diff --git a/jni/errno/Android.mk b/jni/errno/Android.mk new file mode 100644 index 0000000..2031596 --- /dev/null +++ b/jni/errno/Android.mk @@ -0,0 +1,12 @@ +# Android.mk for errno +MY_LOCAL_PATH := $(call my-dir) + +include $(call all-subdir-makefiles) + +LOCAL_PATH := $(MY_LOCAL_PATH) + +include $(CLEAR_VARS) +LOCAL_MODULE := errno +LOCAL_SRC_FILES := errno_jni.c + +include $(BUILD_SHARED_LIBRARY) diff --git a/jni/errno/errno_jni.c b/jni/errno/errno_jni.c new file mode 100644 index 0000000..4195d31 --- /dev/null +++ b/jni/errno/errno_jni.c @@ -0,0 +1,11 @@ +#include +#include + +#include + +JNIEXPORT jint JNICALL +Java_org_cgutman_usbip_errno_Errno_getErrno( + JNIEnv *env, jobject this) +{ + return errno; +} diff --git a/libs/arm64-v8a/liberrno.so b/libs/arm64-v8a/liberrno.so new file mode 100755 index 0000000000000000000000000000000000000000..6fec7e9b53270b2cc6af688705dbca387a1a5059 GIT binary patch literal 5368 zcmeHLO=ule6h7}wZDZ}vODR;1n&+aB5*%Jj3eheMi8WPV@!WO$VwOdm=;7#2Ma42&_bAKjH+0v@+tBCtm52$K%itS{+ z2W<$`X`Qd%{%X2EAl`t-6Yb%?i@R*`DehY}S)b@i+reG-DWTrUUDo>~_y21iTjD+E zbv;=@1uM!VZ!ql3E9v-C#tQ2X74*e=EB%l9=s)lDchC%wr?tm$PGH5lqTsdJ?Ln89Y9^t95r!(PKZ zRrx0raSxQ*N!o!7Xh;_V{<=ECKHwjBUeG{wL;s|yyKf`T8wDDAj~eH6KSe~njQMA0 zW?rd|&A#={q3VSHa`i~<@I>wC;fd*8DIib+zP}mf@Ruhx|LK&LPM9N1hI|Tzo)|yadkaiVGZ72^K|v@3KH*4||+v{1-zWLJoWQl|v+Jf*jw; zYqBVA{q_$(ORST(-~3;s6F|>7<42LcsP;7|C=RLp!2%!g%OlpFSA9wCujH9Lh8@`d E0uEmm5dZ)H literal 0 HcmV?d00001 diff --git a/libs/armeabi-v7a/liberrno.so b/libs/armeabi-v7a/liberrno.so new file mode 100755 index 0000000000000000000000000000000000000000..084844aabfacf377276ed8b176522f31c0a697a7 GIT binary patch literal 13476 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 literal 0 HcmV?d00001 diff --git a/libs/armeabi/liberrno.so b/libs/armeabi/liberrno.so new file mode 100755 index 0000000000000000000000000000000000000000..008766ac47c8011fae89fea73002ae5eef44f205 GIT binary patch literal 13468 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 XZkbPj{don8ZT|B$i1c|5`^~xMo*(zl znX{KU^Zx4Ei~iXyj)mc|+!hDLCSRe2f*(KjvO%97kGcn1#VH_(=_-jFaz8N zaGzX<@&1kYY)A!Ie#n76-nESgdW5}@=H&O$x8S8ceNi5Rd^3HvN8$fXMn8aT;@i-F zmeGHokw4DJUuWd6GV;Ta%e*-dD~b2(Ovvky?Fm`DX5=-(Bofc7Z~ERw*!A1tr63i3 z6>Nq|&iilsUN3HX^=4mn{I1teYGLFBaop{BtNPjulperQPfI(jC8|+BdD2aK6gnxd zr@YbTz}vtI3asHPU%X(khkgUdU{m-=d|pC5d%vzXc}mBtp-ms9<4bbVzR`bGKi znk_$EJ95X-%RxrG7q+KmSa9U}iiu5b!+vRVSfK5!%r8&M#f&@ydD53-YoYKA|6{Dc z!Duw%wkVUVOY362d)!)>Ii}AGo*7lx@SKrq(OLV_dc7#WP|7LKK%Rj-19=AW4CEQe zGmvK>&%j+}!1OYFAAJKLZ>F4Y?zr&u{UEscJA!ir=8NdS1z7#D(-3LiSjA@ z%BP)D6bEt84*VoIanR@tjVk4*PE`&X?e~|NHRTv4jsxH6E+wm#J^jtGe()E12V>=6 zVEmt-h0yHw#hSw4uqAHNN>r@;nz(V$47(dWaa(@U61TC{O}08?u41a&oD)}feeTOI zRWG=S>emu+gO;}u`<=jRHDG8Yal>w?JU@>8EpgGEhGvW~ekZIWHakbR*;C@yd!0_u z#UNq~Y|-_Vio;r81qrtLKZ&))I$^y`euFXYtWDMjzlpFuX=lE%yoRw!FYO4{He=F` zHB7KDspA|uYw%HkzS<6#U4XT2$kgHEnBcb$+Og&d?q_mc*8Y&VOD+8nw6z9i7eWi~0B;|S%7X`bwi~w%-`8mda;M_5)pJ9jJ9x)b) ST;GG1M1F-4zYiHQc7Fr#h%cJ} literal 0 HcmV?d00001 diff --git a/libs/mips64/liberrno.so b/libs/mips64/liberrno.so new file mode 100755 index 0000000000000000000000000000000000000000..b3a06b48ba746b66348f78f52a5d27dfd28a736a GIT binary patch literal 5704 zcmeHLO=w(I6h4!QHqka6Qy0-jI*8zulE+lLh(C*|ZA~jG>5n_{`DW&2=3(ZC_g*w9 zq5*%1Ad;OHf(p76ENf*Y;DY1!O0-( z@BQ3#_o!{Zd(@5r{F-(K4%`UaWoQ)<_Tgo)hCqys2urIl^#(6`{KCjSf58#-`4#zt@>fdud4v;)sikM*7laRKcKkV${$od zr97{^P_Q%A*MhdSqe*QqD?bK*<&5Gb!fqzw@fcmVEpazSS9jv^7#(prp3(KS;WyCg z$P>pi?U+BtjsE`X^*V#`VgJ9l%tOX5d6isa|FYO8$%QP(5q>1C^1jPHkvWa?BWs9! zu$W1CM&dgLr}tPWNYA={uzzdp6I5~RkFkA{`?;y#!&wDvz8TOAd z>|bWszsRr`*e-jr;3Qghui2}P{gT?Zb-t`x^8MO!;4eh&U@^KLl2wB=T#izDDR?{Z zJ8{FWHI~v=(Ds*-YSi_^IBs|R3+A^Grbb|(G1FpHP11Vp)G06Nh|*GCQ(j*b!C&AA zDYJTz2IPm9`PC%xlQf8>g=KDN*STjgv*h~{#c=M}gpW$4Y`lUC1ub3~cekk%L`)rQWR=z;uze6s5o$)2c7gs;- z_c6oY@~l2w{5<1l2jWX4i+zE&+}<%1*mq`mD=%Z6psD(xe#Frav%WKOE9H*opOgPA z**~kjUazbU{r|D+S19-HiQM!Nd2@)K;77j67ui<`?+=}yD4gFIz4q0}>l5@lr)x;V z3=A_c%)l@M!wd{F@SkPCJrD0b@9YV?iuV5bQO}DwAI_aS_iV8=_v&jWi)XxNicghK zpD90mx?C)U%iS=JT46g47Eku?6V~tX830k_Y$~UO6Z~}Tc{8*9=0CKbm7=22UZR{h zj^@Zqnn@a`L6y8$F3n%{I^8ttv=j2`EA5~a)yQiGNz~dWx%>F4sx6)ZUSgZf0eIBo;7uJUVgNrSoiWKdR1WLFqrL~fElvZ3dBY^!W7V4uL1gXC*HSP+%`zS!8XUjARg}*cmI+krW{a@gbhtCx6`c>5N$v)BwGFBn*&j)1Fpg&E; pzd2}R+0EYu+y2zMok7<-idWWeLR=Vj}{%_bwR^$Kx literal 0 HcmV?d00001 diff --git a/libs/x86/liberrno.so b/libs/x86/liberrno.so new file mode 100755 index 0000000000000000000000000000000000000000..6b85cd4bb85362b861d6b4e29bfb0dfcac72d13c GIT binary patch literal 5176 zcmeHLPiP!f82`3e+tr5c+D42`)ln8nTO5+Q7HbMxlD67NO%r;t>U4H@vO9GDgqcZ9 zLcxZjR>EQqf}W&MZ+ZwQUM<~&o}>p6k2$om!5kDsf{N?!_h#N^vwG00>_>j{{ri32 zd;8{1m^W{peCbpy785N!(j(f-AlQF@n8}DCyJS%MWS{Irh&hcl*hD;nu7|5+0}0|D zdMQ!;*OI7tBt#ko1p6|-18M{L4djVU_yqGDW8!X*UxlG@95I49;vDi&e-qUo(Mjan z$2TxGq5F3hBGr<;urS4z(0{n)iO@HJ)t)fE7pwM4M?MQbZiK;<{M>Xq{Mim)EGsu} zThngEF1d?NYy5wC$GUj6G*tRj**>3bR4ku2{2v)}65&&#P3e zlj>S<0%f3Bg~f96c3{KJ`ab)4=<8JSQgX9?kk1`HZ2DC?YjmT)(OOe)_zuufm^dyajWq{>=fAT0Y&VU4gC^ZNn#71+rIF@V#fqt6}>& z*{L8|sRoW&sLX;@%0D^hc)nY$aI*hSxEBa4r`j9bA1jg1d!_Fq_Y#+zJnk8SdqxYn z6yAI8EAqIPh-YByJo^!k0bFPDxMvCOmv$ca)FD9gxYr3I;_2@l>P#>DwjSi}Avz#8)SzvB)k(I&e08;EuC z_*`qzyekN3o)~bjU^)k{WxoxZ<8$1&0qBK=Pti5Yu-R{jK?r%{p{cIS5$^---(n`f U`=|Ml%Q3Z-!23iy(9Bc*0?LA_mH+?% literal 0 HcmV?d00001 diff --git a/libs/x86_64/liberrno.so b/libs/x86_64/liberrno.so new file mode 100755 index 0000000000000000000000000000000000000000..5bd8eeff67914290697cf5b9b54f80966cc3764c GIT binary patch literal 5632 zcmeHL&1)QG9DcXaX0p+hnrn8qH({ccJR|mqJ=Xc3JEB=D{v6FX1 zNy&N9d-;Q1{KoSt%f5G^-trsui=mo+5-!&hbH-oty>?XfD%HiL>9@Sac)q^ig;CUM zdo%W04HK&f(YlQn27cm;IUmPfocK{9u2x_g_4zmnDiafK+=kIqtSANzRctlTtqjN& zbB$#=C?$Ju^|si(fH_A#gzJ;{$>9QVY}nRmHMOrE?fTIM>xAc8%CAe+Cf-Q-S+x!5 zr8D)@h%ue9pXItP(iwwYLv(wqBQo{_Y*e|uY3*-eqv`Dpv1iww`9>VuUyBaX?Al$I z#V%bhZESyL=Qm|vGx;J;7GC&Ji| z1QqxvrFz-= z9v^)OZb)uqV0uCFFh9Tj-sxa0PfNKZWlqY+QeKhrxfDz2U-~o9pMm}i^k<+y1N|B3 z&%pm^26oPu+UK2fsGbvfrkvIIJ&Nm^i1QW%=T8XnRgLo;TkM9-ZHk8#f9Z7WiJ9&2 zocxs@TK8r0`3<z(VIxS@?8jy1sj!~Jqle7H5_a~}}74QIYdb_e-J-OVMHP{4e1DA3leepKafh0G?^& z^Eq`z|8r&ALtoSKni#k*72U%Q4m;e9( literal 0 HcmV?d00001 diff --git a/src/org/cgutman/usbip/errno/Errno.java b/src/org/cgutman/usbip/errno/Errno.java new file mode 100644 index 0000000..b7d699d --- /dev/null +++ b/src/org/cgutman/usbip/errno/Errno.java @@ -0,0 +1,12 @@ +package org.cgutman.usbip.errno; + +public class Errno { + static { + System.loadLibrary("errno"); + } + + // This is a really nasty hack to try to grab the error + // from a USB API request. It may return an undefined result + // if say the GC runs before this gets called. + public static native int getErrno(); +} diff --git a/src/org/cgutman/usbip/server/UsbIpServer.java b/src/org/cgutman/usbip/server/UsbIpServer.java index 671e200..effd823 100644 --- a/src/org/cgutman/usbip/server/UsbIpServer.java +++ b/src/org/cgutman/usbip/server/UsbIpServer.java @@ -1,8 +1,6 @@ package org.cgutman.usbip.server; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Map; @@ -15,6 +13,7 @@ import org.cgutman.usbip.server.protocol.cli.ImportDeviceRequest; import org.cgutman.usbip.server.protocol.dev.UsbIpDevicePacket; import org.cgutman.usbip.server.protocol.dev.UsbIpSubmitUrb; +import org.cgutman.usbip.server.protocol.dev.UsbIpUnlinkUrb; public class UsbIpServer { public static final int PORT = 3240; @@ -25,14 +24,19 @@ public class UsbIpServer { private ConcurrentHashMap connections = new ConcurrentHashMap(); // Returns true if a device is now attached - private boolean handleRequest(InputStream in, OutputStream out) throws IOException { - CommonPacket inMsg = CommonPacket.read(in); + private boolean handleRequest(Socket s) throws IOException { + CommonPacket inMsg = CommonPacket.read(s.getInputStream()); CommonPacket outMsg; + if (inMsg == null) { + s.close(); + return false; + } + boolean res = false; System.out.printf("In code: 0x%x\n", inMsg.code); if (inMsg.code == ProtoDefs.OP_REQ_DEVLIST) { - DevListReply dlReply = new DevListReply(); + DevListReply dlReply = new DevListReply(inMsg.version); dlReply.devInfoList = handler.getDevices(); if (dlReply.devInfoList == null) { dlReply.status = ProtoDefs.ST_NA; @@ -41,9 +45,9 @@ private boolean handleRequest(InputStream in, OutputStream out) throws IOExcepti } else if (inMsg.code == ProtoDefs.OP_REQ_IMPORT) { ImportDeviceRequest imReq = (ImportDeviceRequest)inMsg; - ImportDeviceReply imReply = new ImportDeviceReply(); + ImportDeviceReply imReply = new ImportDeviceReply(inMsg.version); - res = handler.attachToDevice(imReq.busid); + res = handler.attachToDevice(s, imReq.busid); if (res) { imReply.devInfo = handler.getDeviceByBusId(imReq.busid); if (imReply.devInfo == null) { @@ -64,7 +68,7 @@ else if (inMsg.code == ProtoDefs.OP_REQ_IMPORT) { } System.out.printf("Out code: 0x%x\n", outMsg.code); - out.write(outMsg.serialize()); + s.getOutputStream().write(outMsg.serialize()); return res; } @@ -74,6 +78,9 @@ private boolean handleDevRequest(Socket s) throws IOException { if (inMsg.command == UsbIpDevicePacket.USBIP_CMD_SUBMIT) { handler.submitUrbRequest(s, (UsbIpSubmitUrb) inMsg); } + else if (inMsg.command == UsbIpDevicePacket.USBIP_CMD_UNLINK) { + handler.abortUrbRequest(s, (UsbIpUnlinkUrb) inMsg); + } else { return false; } @@ -100,19 +107,19 @@ private void handleClient(final Socket s) { @Override public void run() { try { - // Disable Nagle s.setTcpNoDelay(true); + s.setKeepAlive(true); - InputStream in = s.getInputStream(); - OutputStream out = s.getOutputStream(); while (!isInterrupted()) { - if (handleRequest(in, out)) { + if (handleRequest(s)) { while (handleDevRequest(s)); } } } catch (IOException e) { System.out.println("Client disconnected"); } finally { + handler.cleanupSocket(s); + try { s.close(); } catch (IOException e) {} diff --git a/src/org/cgutman/usbip/server/UsbRequestHandler.java b/src/org/cgutman/usbip/server/UsbRequestHandler.java index 0fddafb..a714997 100644 --- a/src/org/cgutman/usbip/server/UsbRequestHandler.java +++ b/src/org/cgutman/usbip/server/UsbRequestHandler.java @@ -4,13 +4,17 @@ import java.util.List; import org.cgutman.usbip.server.protocol.dev.UsbIpSubmitUrb; +import org.cgutman.usbip.server.protocol.dev.UsbIpUnlinkUrb; public interface UsbRequestHandler { public List getDevices(); public UsbDeviceInfo getDeviceByBusId(String busId); - public boolean attachToDevice(String busId); - public void detachFromDevice(String busId); + public boolean attachToDevice(Socket s, String busId); + public void detachFromDevice(Socket s, String busId); public void submitUrbRequest(Socket s, UsbIpSubmitUrb msg); + public void abortUrbRequest(Socket s, UsbIpUnlinkUrb msg); + + public void cleanupSocket(Socket s); } diff --git a/src/org/cgutman/usbip/server/protocol/ProtoDefs.java b/src/org/cgutman/usbip/server/protocol/ProtoDefs.java index f44d19f..2c85761 100644 --- a/src/org/cgutman/usbip/server/protocol/ProtoDefs.java +++ b/src/org/cgutman/usbip/server/protocol/ProtoDefs.java @@ -1,7 +1,7 @@ package org.cgutman.usbip.server.protocol; public class ProtoDefs { - public static final short USBIP_VERSION = 0x0106; + /* public static final short USBIP_VERSION = 0x0111; */ public static final short OP_REQUEST = (short) (0x80 << 8); public static final short OP_REPLY = (0x00 << 8); diff --git a/src/org/cgutman/usbip/server/protocol/cli/CommonPacket.java b/src/org/cgutman/usbip/server/protocol/cli/CommonPacket.java index b5b791f..17e21f5 100644 --- a/src/org/cgutman/usbip/server/protocol/cli/CommonPacket.java +++ b/src/org/cgutman/usbip/server/protocol/cli/CommonPacket.java @@ -29,11 +29,11 @@ public CommonPacket(short version, short code, int status) { public static CommonPacket read(InputStream in) throws IOException { ByteBuffer bb = ByteBuffer.allocate(8); StreamUtils.readAll(in, bb.array()); - - short version = bb.getShort(); - if (version != ProtoDefs.USBIP_VERSION) { - throw new IOException("Unsupported protocol version: "+version); - } + + // We should check the version here, but it seems they like to + // increment it without actually changing the protocol, so I'm + // not going to. + bb.getShort(); CommonPacket pkt; short code = bb.getShort(); diff --git a/src/org/cgutman/usbip/server/protocol/cli/DevListReply.java b/src/org/cgutman/usbip/server/protocol/cli/DevListReply.java index 2522b0a..af25e48 100644 --- a/src/org/cgutman/usbip/server/protocol/cli/DevListReply.java +++ b/src/org/cgutman/usbip/server/protocol/cli/DevListReply.java @@ -13,8 +13,8 @@ public DevListReply(byte[] header) { super(header); } - public DevListReply() { - super(ProtoDefs.USBIP_VERSION, ProtoDefs.OP_REP_DEVLIST, ProtoDefs.ST_OK); + public DevListReply(short version) { + super(version, ProtoDefs.OP_REP_DEVLIST, ProtoDefs.ST_OK); } @Override diff --git a/src/org/cgutman/usbip/server/protocol/cli/ImportDeviceReply.java b/src/org/cgutman/usbip/server/protocol/cli/ImportDeviceReply.java index a7baaa9..a5e014f 100644 --- a/src/org/cgutman/usbip/server/protocol/cli/ImportDeviceReply.java +++ b/src/org/cgutman/usbip/server/protocol/cli/ImportDeviceReply.java @@ -10,8 +10,8 @@ public ImportDeviceReply(byte[] header) { super(header); } - public ImportDeviceReply() { - super(ProtoDefs.USBIP_VERSION, ProtoDefs.OP_REP_IMPORT, ProtoDefs.ST_OK); + public ImportDeviceReply(short version) { + super(version, ProtoDefs.OP_REP_IMPORT, ProtoDefs.ST_OK); } @Override diff --git a/src/org/cgutman/usbip/server/protocol/dev/UsbIpDevicePacket.java b/src/org/cgutman/usbip/server/protocol/dev/UsbIpDevicePacket.java index a1d24a1..9a2f71a 100644 --- a/src/org/cgutman/usbip/server/protocol/dev/UsbIpDevicePacket.java +++ b/src/org/cgutman/usbip/server/protocol/dev/UsbIpDevicePacket.java @@ -18,11 +18,11 @@ public abstract class UsbIpDevicePacket { public static final int USBIP_DIR_IN = 1; public static final int USBIP_STATUS_ENDPOINT_HALTED = -32; + public static final int USBIP_STATUS_URB_ABORTED = -54; public static final int USBIP_STATUS_DATA_OVERRUN = -75; + public static final int USBIP_STATUS_URB_TIMED_OUT = -110; public static final int USBIP_STATUS_SHORT_TRANSFER = -121; - - public static final int USBIP_SHORT_XFER_OKAY = 0x01; - + public static final int USBIP_HEADER_SIZE = 48; public int command; @@ -58,6 +58,8 @@ public static UsbIpDevicePacket read(InputStream in) throws IOException { { case USBIP_CMD_SUBMIT: return UsbIpSubmitUrb.read(bb.array(), in); + case USBIP_CMD_UNLINK: + return UsbIpUnlinkUrb.read(bb.array(), in); default: System.err.println("Unknown command: "+command); return null; diff --git a/src/org/cgutman/usbip/server/protocol/dev/UsbIpUnlinkUrb.java b/src/org/cgutman/usbip/server/protocol/dev/UsbIpUnlinkUrb.java new file mode 100644 index 0000000..cfa61ef --- /dev/null +++ b/src/org/cgutman/usbip/server/protocol/dev/UsbIpUnlinkUrb.java @@ -0,0 +1,48 @@ +package org.cgutman.usbip.server.protocol.dev; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import org.cgutman.usbip.utils.StreamUtils; + +public class UsbIpUnlinkUrb extends UsbIpDevicePacket { + public int seqNumToUnlink; + + public static final int WIRE_SIZE = 4; + + public UsbIpUnlinkUrb(byte[] header) { + super(header); + } + + public static UsbIpUnlinkUrb read(byte[] header, InputStream in) throws IOException { + UsbIpUnlinkUrb msg = new UsbIpUnlinkUrb(header); + + byte[] continuationHeader = new byte[WIRE_SIZE]; + StreamUtils.readAll(in, continuationHeader); + + ByteBuffer bb = ByteBuffer.wrap(continuationHeader).order(ByteOrder.BIG_ENDIAN); + msg.seqNumToUnlink = bb.getInt(); + + // Finish reading the remaining bytes of the header as padding + for (int i = 0; i < UsbIpDevicePacket.USBIP_HEADER_SIZE - (header.length + bb.position()); i++) { + in.read(); + } + + return msg; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()); + sb.append(String.format("Sequence number to unlink: %d\n", seqNumToUnlink)); + return sb.toString(); + } + + @Override + protected byte[] serializeInternal() { + throw new UnsupportedOperationException("Serializing not supported"); + } +} diff --git a/src/org/cgutman/usbip/server/protocol/dev/UsbIpUnlinkUrbReply.java b/src/org/cgutman/usbip/server/protocol/dev/UsbIpUnlinkUrbReply.java new file mode 100644 index 0000000..adc3bd7 --- /dev/null +++ b/src/org/cgutman/usbip/server/protocol/dev/UsbIpUnlinkUrbReply.java @@ -0,0 +1,28 @@ +package org.cgutman.usbip.server.protocol.dev; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +public class UsbIpUnlinkUrbReply extends UsbIpDevicePacket { + public int status; + + public UsbIpUnlinkUrbReply(int seqNum, int devId, int dir, int ep) { + super(UsbIpDevicePacket.USBIP_RET_UNLINK, seqNum, devId, dir, ep); + } + + protected byte[] serializeInternal() { + ByteBuffer bb = ByteBuffer.allocate(UsbIpDevicePacket.USBIP_HEADER_SIZE - 20).order(ByteOrder.BIG_ENDIAN); + + bb.putInt(status); + + return bb.array(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()); + sb.append(String.format("Status: 0x%x\n", status)); + return sb.toString(); + } +} diff --git a/src/org/cgutman/usbip/service/UsbIpService.java b/src/org/cgutman/usbip/service/UsbIpService.java index 5b5ac02..35c8085 100644 --- a/src/org/cgutman/usbip/service/UsbIpService.java +++ b/src/org/cgutman/usbip/service/UsbIpService.java @@ -5,6 +5,8 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; @@ -20,7 +22,9 @@ import org.cgutman.usbip.server.protocol.dev.UsbIpDevicePacket; import org.cgutman.usbip.server.protocol.dev.UsbIpSubmitUrb; import org.cgutman.usbip.server.protocol.dev.UsbIpSubmitUrbReply; -import org.cgutman.usbip.usb.DescriptorReader; +import org.cgutman.usbip.server.protocol.dev.UsbIpUnlinkUrb; +import org.cgutman.usbip.server.protocol.dev.UsbIpUnlinkUrbReply; +import org.cgutman.usbip.usb.UsbControlHelper; import org.cgutman.usbip.usb.UsbDeviceDescriptor; import org.cgutman.usbip.usb.XferUtils; import org.cgutman.usbipserverforandroid.R; @@ -39,10 +43,8 @@ import android.hardware.usb.UsbEndpoint; import android.hardware.usb.UsbInterface; import android.hardware.usb.UsbManager; -import android.hardware.usb.UsbRequest; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.WifiLock; -import android.os.Build; import android.os.IBinder; import android.os.PowerManager; import android.os.PowerManager.WakeLock; @@ -54,6 +56,7 @@ public class UsbIpService extends Service implements UsbRequestHandler { private SparseArray connections; private SparseArray permission; + private HashMap socketMap; private UsbIpServer server; private WakeLock cpuWakeLock; private WifiLock wifiLock; @@ -112,6 +115,7 @@ public void onCreate() { usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); connections = new SparseArray(); permission = new SparseArray(); + socketMap = new HashMap(); usbPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0); IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); @@ -237,15 +241,48 @@ else if ((possibleSpeeds & FLAG_POSSIBLE_SPEED_HIGH) != 0) { return UsbIpDevice.USB_SPEED_UNKNOWN; } } + + private static int deviceIdToBusNum(int deviceId) { + return deviceId / 1000; + } + + private static int deviceIdToDevNum(int deviceId) { + return deviceId % 1000; + } + + private static int devIdToDeviceId(int devId) { + // This is the same algorithm as Android uses + return ((devId >> 16) & 0xFF) * 1000 + (devId & 0xFF); + } + + private static int busIdToBusNum(String busId) { + if (busId.indexOf('-') == -1) { + return -1; + } + + return Integer.parseInt(busId.substring(0, busId.indexOf('-'))); + } + + private static int busIdToDevNum(String busId) { + if (busId.indexOf('-') == -1) { + return -1; + } + + return Integer.parseInt(busId.substring(busId.indexOf('-')+1)); + } + + private static int busIdToDeviceId(String busId) { + return devIdToDeviceId(((busIdToBusNum(busId) << 16) & 0xFF0000) | busIdToDevNum(busId)); + } - private UsbDeviceInfo getInfoForDevice(UsbDevice dev) { + private UsbDeviceInfo getInfoForDevice(UsbDevice dev, UsbDeviceConnection devConn) { UsbDeviceInfo info = new UsbDeviceInfo(); UsbIpDevice ipDev = new UsbIpDevice(); ipDev.path = dev.getDeviceName(); - ipDev.busid = String.format("%d", dev.getDeviceId()); - ipDev.busnum = 0; - ipDev.devnum = dev.getDeviceId(); + ipDev.busnum = deviceIdToBusNum(dev.getDeviceId()); + ipDev.devnum = deviceIdToDevNum(dev.getDeviceId()); + ipDev.busid = String.format("%d-%d", ipDev.busnum, ipDev.devnum); ipDev.idVendor = (short) dev.getVendorId(); ipDev.idProduct = (short) dev.getProductId(); @@ -277,7 +314,7 @@ private UsbDeviceInfo getInfoForDevice(UsbDevice dev) { if (context != null) { // Since we're attached already, we can directly query the USB descriptors // to fill some information that Android's USB API doesn't expose - devDesc = DescriptorReader.readDeviceDescriptor(context.devConn); + devDesc = UsbControlHelper.readDeviceDescriptor(context.devConn); ipDev.bcdDevice = devDesc.bcdDevice; ipDev.bNumConfigurations = devDesc.bNumConfigurations; @@ -293,7 +330,13 @@ public List getDevices() { ArrayList list = new ArrayList(); for (UsbDevice dev : usbManager.getDeviceList().values()) { - list.add(getInfoForDevice(dev)); + AttachedDeviceContext context = connections.get(dev.getDeviceId()); + UsbDeviceConnection devConn = null; + if (context != null) { + devConn = context.devConn; + } + + list.add(getInfoForDevice(dev, devConn)); } return list; @@ -329,6 +372,18 @@ private static void sendReply(Socket s, UsbIpSubmitUrbReply reply, int status) { } } + private static void sendReply(Socket s, UsbIpUnlinkUrbReply reply, int status) { + reply.status = status; + try { + // We need to synchronize to avoid writing on top of ourselves + synchronized (s) { + s.getOutputStream().write(reply.serialize()); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + // FIXME: This dispatching could use some refactoring so we don't have to pass // a million parameters to this guy private void dispatchRequest(final AttachedDeviceContext context, final Socket s, @@ -351,94 +406,79 @@ public void run() { selectedEndpoint.getEndpointNumber()); } - int res = XferUtils.doBulkTransfer(context.devConn,selectedEndpoint, buff.array(), msg.interval); + int res; + do { + res = XferUtils.doBulkTransfer(context.devConn, selectedEndpoint, buff.array(), 1000); + + if (context.requestPool.isShutdown()) { + // Bail if the queue is being torn down + return; + } + + if (!context.activeMessages.contains(msg)) { + // Somebody cancelled the URB, return without responding + return; + } + } while (res == -110); // ETIMEDOUT if (DEBUG) { System.out.printf("Bulk transfer complete with %d bytes (wanted %d)\n", res, msg.transferBufferLength); } - + if (res < 0) { - reply.status = ProtoDefs.ST_NA; + reply.status = res; } else { reply.actualLength = res; reply.status = ProtoDefs.ST_OK; } + + context.activeMessages.remove(msg); sendReply(s, reply, reply.status); } else if (selectedEndpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_INT) { - if (DEBUG) { System.out.printf("Interrupt transfer - %d bytes %s on EP %d\n", msg.transferBufferLength, msg.direction == UsbIpDevicePacket.USBIP_DIR_IN ? "in" : "out", selectedEndpoint.getEndpointNumber()); } - - UsbRequest req = new UsbRequest(); - req.initialize(context.devConn, selectedEndpoint); - - // Create a context for this request so we can identify it - // when we're done - UrbContext urbCtxt = new UrbContext(); - urbCtxt.originalMsg = msg; - urbCtxt.replyMsg = reply; - urbCtxt.buffer = buff; - req.setClientData(urbCtxt); - if (!req.queue(buff, msg.transferBufferLength)) { - System.err.println("Failed to queue request"); - sendReply(s, reply, ProtoDefs.ST_NA); - req.close(); - return; - } - // ---------------- msg is not safe to use below this point ----------- - - // This can return a different request than what we queued, - // so we have to lookup the correct reply for this guy - req = context.devConn.requestWait(); - urbCtxt = (UrbContext) req.getClientData(); - reply = urbCtxt.replyMsg; - UsbIpSubmitUrb originalMsg = urbCtxt.originalMsg; - - - // On Jelly Bean MR1 (4.2), they exposed the actual transfer length - // as the byte buffer's position. On previous platforms, we just assume - // the whole thing went through. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - // The byte buffer could have changed so we need to get it from - // the client data - reply.actualLength = urbCtxt.buffer.position(); - } - else { - reply.actualLength = originalMsg.transferBufferLength; - } - - req.close(); + int res; + do { + res = XferUtils.doInterruptTransfer(context.devConn, selectedEndpoint, buff.array(), 1000); + + if (context.requestPool.isShutdown()) { + // Bail if the queue is being torn down + return; + } + + if (!context.activeMessages.contains(msg)) { + // Somebody cancelled the URB, return without responding + return; + } + } while (res == -110); // ETIMEDOUT if (DEBUG) { System.out.printf("Interrupt transfer complete with %d bytes (wanted %d)\n", - reply.actualLength, originalMsg.transferBufferLength); + res, msg.transferBufferLength); } - if (reply.actualLength == 0) { - // Request actually failed - reply.status = ProtoDefs.ST_NA; + + if (res < 0) { + reply.status = res; } else { - // LGTM + reply.actualLength = res; reply.status = ProtoDefs.ST_OK; } - // Docs are conflicting on whether we need to fill this for non-iso - // transfers. Nothing in the client driver code seems to use it - // so I'm not going to... - reply.startFrame = 0; - + context.activeMessages.remove(msg); sendReply(s, reply, reply.status); } else { System.err.println("Unsupported endpoint type: "+selectedEndpoint.getType()); - sendReply(s, reply, ProtoDefs.ST_NA); + context.activeMessages.remove(msg); + server.killClient(s); } } }); @@ -449,18 +489,18 @@ public void submitUrbRequest(Socket s, UsbIpSubmitUrb msg) { UsbIpSubmitUrbReply reply = new UsbIpSubmitUrbReply(msg.seqNum, msg.devId, msg.direction, msg.ep); - UsbDevice dev = getDevice(msg.devId); + int deviceId = devIdToDeviceId(msg.devId); + + UsbDevice dev = getDevice(deviceId); if (dev == null) { // The device is gone, so terminate the client - cleanupDetachedDevice(msg.devId); server.killClient(s); return; } - AttachedDeviceContext context = connections.get(msg.devId); + AttachedDeviceContext context = connections.get(deviceId); if (context == null) { // This should never happen, but kill the connection if it does - cleanupDetachedDevice(msg.devId); server.killClient(s); return; } @@ -481,17 +521,36 @@ public void submitUrbRequest(Socket s, UsbIpSubmitUrb msg) { if (length != 0) { reply.inData = new byte[length]; } + + // This message is now active + context.activeMessages.add(msg); - int res = XferUtils.doControlTransfer(devConn, requestType, request, value, index, - (requestType & 0x80) != 0 ? reply.inData : msg.outData, length, msg.interval); + int res; + + do { + res = XferUtils.doControlTransfer(devConn, requestType, request, value, index, + (requestType & 0x80) != 0 ? reply.inData : msg.outData, length, 1000); + + if (context.requestPool.isShutdown()) { + // Bail if the queue is being torn down + return; + } + + if (!context.activeMessages.contains(msg)) { + // Somebody cancelled the URB, return without responding + return; + } + } while (res == -110); // ETIMEDOUT + if (res < 0) { - reply.status = ProtoDefs.ST_NA; + reply.status = res; } else { reply.actualLength = res; reply.status = ProtoDefs.ST_OK; } + context.activeMessages.remove(msg); sendReply(s, reply, reply.status); return; } @@ -545,14 +604,17 @@ public void submitUrbRequest(Socket s, UsbIpSubmitUrb msg) { buff = ByteBuffer.wrap(msg.outData); } + // This message is now active + context.activeMessages.add(msg); + // Dispatch this request asynchronously dispatchRequest(context, s, selectedEndpoint, buff, msg); } } - private UsbDevice getDevice(int busId) { + private UsbDevice getDevice(int deviceId) { for (UsbDevice dev : usbManager.getDeviceList().values()) { - if (dev.getDeviceId() == busId) { + if (dev.getDeviceId() == deviceId) { return dev; } } @@ -561,15 +623,7 @@ private UsbDevice getDevice(int busId) { } private UsbDevice getDevice(String busId) { - int id; - - try { - id = Integer.parseInt(busId); - } catch (NumberFormatException e) { - return null; - } - - return getDevice(id); + return getDevice(busIdToDeviceId(busId)); } @Override @@ -579,16 +633,27 @@ public UsbDeviceInfo getDeviceByBusId(String busId) { return null; } - return getInfoForDevice(dev); + AttachedDeviceContext context = connections.get(dev.getDeviceId()); + UsbDeviceConnection devConn = null; + if (context != null) { + devConn = context.devConn; + } + + return getInfoForDevice(dev, devConn); } @Override - public boolean attachToDevice(String busId) { + public boolean attachToDevice(Socket s, String busId) { UsbDevice dev = getDevice(busId); if (dev == null) { return false; } + if (connections.get(dev.getDeviceId()) != null) { + // Already attached + return false; + } + if (!usbManager.hasPermission(dev)) { // Try to get permission from the user permission.put(dev.getDeviceId(), null); @@ -617,7 +682,7 @@ public boolean attachToDevice(String busId) { // Claim all interfaces since we don't know which one the client wants for (int i = 0; i < dev.getInterfaceCount(); i++) { if (!devConn.claimInterface(dev.getInterface(i), true)) { - return false; + System.err.println("Unabled to claim interface "+dev.getInterface(i).getId()); } } @@ -626,7 +691,7 @@ public boolean attachToDevice(String busId) { context.devConn = devConn; context.device = dev; - // Count all endpoints on all intefaces + // Count all endpoints on all interfaces int endpointCount = 0; for (int i = 0; i < dev.getInterfaceCount(); i++) { endpointCount += dev.getInterface(i).getEndpointCount(); @@ -637,7 +702,11 @@ public boolean attachToDevice(String busId) { Long.MAX_VALUE, TimeUnit.DAYS, new LinkedBlockingQueue(), new ThreadPoolExecutor.DiscardPolicy()); + // Create the active message set + context.activeMessages = new HashSet(); + connections.put(dev.getDeviceId(), context); + socketMap.put(s, context); updateNotification(); return true; @@ -652,6 +721,9 @@ private void cleanupDetachedDevice(int deviceId) { // Clear the this attachment's context connections.remove(deviceId); + // Signal queue death + context.requestPool.shutdownNow(); + // Release our claim to the interfaces for (int i = 0; i < context.device.getInterfaceCount(); i++) { context.devConn.releaseInterface(context.device.getInterface(i)); @@ -659,12 +731,17 @@ private void cleanupDetachedDevice(int deviceId) { // Close the connection context.devConn.close(); + + // Wait for the queue to die + try { + context.requestPool.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); + } catch (InterruptedException e) {} updateNotification(); } @Override - public void detachFromDevice(String busId) { + public void detachFromDevice(Socket s, String busId) { UsbDevice dev = getDevice(busId); if (dev == null) { return; @@ -672,16 +749,47 @@ public void detachFromDevice(String busId) { cleanupDetachedDevice(dev.getDeviceId()); } - - class UrbContext { - public UsbIpSubmitUrb originalMsg; - public UsbIpSubmitUrbReply replyMsg; - public ByteBuffer buffer; + + @Override + public void cleanupSocket(Socket s) { + AttachedDeviceContext context = socketMap.remove(s); + if (context == null) { + return; + } + + cleanupDetachedDevice(context.device.getDeviceId()); + } + + @Override + public void abortUrbRequest(Socket s, UsbIpUnlinkUrb msg) { + AttachedDeviceContext context = socketMap.get(s); + if (context == null) { + return; + } + + UsbIpUnlinkUrbReply reply = new UsbIpUnlinkUrbReply(msg.seqNum, msg.devId, msg.direction, msg.ep); + + boolean found = false; + synchronized (context.activeMessages) { + for (UsbIpSubmitUrb urbMsg : context.activeMessages) { + if (msg.seqNumToUnlink == urbMsg.seqNum) { + context.activeMessages.remove(urbMsg); + found = true; + break; + } + } + } + + System.out.println("Removed URB? " + (found ? "yes" : "no")); + sendReply(s, reply, + found ? UsbIpSubmitUrb.USBIP_STATUS_URB_ABORTED : + -22); // EINVAL } class AttachedDeviceContext { public UsbDevice device; public UsbDeviceConnection devConn; public ThreadPoolExecutor requestPool; + public HashSet activeMessages; } } diff --git a/src/org/cgutman/usbip/usb/DescriptorReader.java b/src/org/cgutman/usbip/usb/DescriptorReader.java deleted file mode 100644 index e33b52a..0000000 --- a/src/org/cgutman/usbip/usb/DescriptorReader.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.cgutman.usbip.usb; - -import android.hardware.usb.UsbDeviceConnection; - -public class DescriptorReader { - - private static final int GET_DESCRIPTOR_REQUEST_TYPE = 0x80; - private static final int GET_DESCRIPTOR_REQUEST = 0x06; - - private static final int DEVICE_DESCRIPTOR_TYPE = 1; - - public static UsbDeviceDescriptor readDeviceDescriptor(UsbDeviceConnection devConn) { - byte[] descriptorBuffer = new byte[UsbDeviceDescriptor.DESCRIPTOR_SIZE]; - - - int res = XferUtils.doControlTransfer(devConn, GET_DESCRIPTOR_REQUEST_TYPE, - GET_DESCRIPTOR_REQUEST, - (DEVICE_DESCRIPTOR_TYPE << 8) | 0x00, // Devices only have 1 descriptor - 0, descriptorBuffer, descriptorBuffer.length, 0); - if (res != UsbDeviceDescriptor.DESCRIPTOR_SIZE) { - return null; - } - - return new UsbDeviceDescriptor(descriptorBuffer); - } -} diff --git a/src/org/cgutman/usbip/usb/UsbControlHelper.java b/src/org/cgutman/usbip/usb/UsbControlHelper.java new file mode 100644 index 0000000..3bcc28a --- /dev/null +++ b/src/org/cgutman/usbip/usb/UsbControlHelper.java @@ -0,0 +1,63 @@ +package org.cgutman.usbip.usb; + +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbEndpoint; + +public class UsbControlHelper { + + private static final int GET_DESCRIPTOR_REQUEST_TYPE = 0x80; + private static final int GET_DESCRIPTOR_REQUEST = 0x06; + + private static final int GET_STATUS_REQUEST_TYPE = 0x82; + private static final int GET_STATUS_REQUEST = 0x00; + + private static final int CLEAR_FEATURE_REQUEST_TYPE = 0x02; + private static final int CLEAR_FEATURE_REQUEST = 0x01; + + private static final int FEATURE_VALUE_HALT = 0x00; + + private static final int DEVICE_DESCRIPTOR_TYPE = 1; + + public static UsbDeviceDescriptor readDeviceDescriptor(UsbDeviceConnection devConn) { + byte[] descriptorBuffer = new byte[UsbDeviceDescriptor.DESCRIPTOR_SIZE]; + + + int res = XferUtils.doControlTransfer(devConn, GET_DESCRIPTOR_REQUEST_TYPE, + GET_DESCRIPTOR_REQUEST, + (DEVICE_DESCRIPTOR_TYPE << 8) | 0x00, // Devices only have 1 descriptor + 0, descriptorBuffer, descriptorBuffer.length, 0); + if (res != UsbDeviceDescriptor.DESCRIPTOR_SIZE) { + return null; + } + + return new UsbDeviceDescriptor(descriptorBuffer); + } + + public static boolean isEndpointHalted(UsbDeviceConnection devConn, UsbEndpoint endpoint) { + byte[] statusBuffer = new byte[2]; + + int res = XferUtils.doControlTransfer(devConn, GET_STATUS_REQUEST_TYPE, + GET_STATUS_REQUEST, + 0, + endpoint != null ? endpoint.getAddress() : 0, + statusBuffer, statusBuffer.length, 0); + if (res != statusBuffer.length) { + return false; + } + + return (statusBuffer[0] & 1) != 0; + } + + public static boolean clearHaltCondition(UsbDeviceConnection devConn, UsbEndpoint endpoint) { + int res = XferUtils.doControlTransfer(devConn, CLEAR_FEATURE_REQUEST_TYPE, + CLEAR_FEATURE_REQUEST, + FEATURE_VALUE_HALT, + endpoint.getAddress(), + null, 0, 0); + if (res < 0) { + return false; + } + + return true; + } +} diff --git a/src/org/cgutman/usbip/usb/XferUtils.java b/src/org/cgutman/usbip/usb/XferUtils.java index f9323d4..8e88b4e 100644 --- a/src/org/cgutman/usbip/usb/XferUtils.java +++ b/src/org/cgutman/usbip/usb/XferUtils.java @@ -1,12 +1,29 @@ package org.cgutman.usbip.usb; +import org.cgutman.usbip.errno.Errno; + import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; public class XferUtils { - public static int doBulkTransfer(UsbDeviceConnection devConn, UsbEndpoint endpoint, byte[] buff, int interval) { + public static int doInterruptTransfer(UsbDeviceConnection devConn, UsbEndpoint endpoint, byte[] buff, int timeout) { + // Interrupt transfers are implemented as one-shot bulk transfers + int res = devConn.bulkTransfer(endpoint, buff, + buff.length, timeout); + if (res < 0) { + res = -Errno.getErrno(); + if (res != -110) { + // Don't print for ETIMEDOUT + System.err.println("Interrupt Xfer failed: "+res); + } + } + + return res; + } + + public static int doBulkTransfer(UsbDeviceConnection devConn, UsbEndpoint endpoint, byte[] buff, int timeout) { int bytesTransferred = 0; while (bytesTransferred < buff.length) { byte[] remainingBuffer = new byte[buff.length - bytesTransferred]; @@ -17,11 +34,15 @@ public static int doBulkTransfer(UsbDeviceConnection devConn, UsbEndpoint endpoi } int res = devConn.bulkTransfer(endpoint, remainingBuffer, - remainingBuffer.length, interval); + remainingBuffer.length, timeout); if (res < 0) { // Failed transfer terminates the bulk transfer - System.err.println("Bulk Xfer failed: "+res); - return -1; + res = -Errno.getErrno(); + if (res != -110) { + // Don't print for ETIMEDOUT + System.err.println("Bulk Xfer failed: "+res); + } + return res; } if (endpoint.getDirection() == UsbConstants.USB_DIR_IN) { @@ -43,13 +64,25 @@ public static int doBulkTransfer(UsbDeviceConnection devConn, UsbEndpoint endpoi public static int doControlTransfer(UsbDeviceConnection devConn, int requestType, int request, int value, int index, byte[] buff, int length, int interval) { + + // Mask out possible sign expansions + requestType &= 0xFF; + request &= 0xFF; + value &= 0xFFFF; + index &= 0xFFFF; + length &= 0xFFFF; + System.out.printf("SETUP: %x %x %x %x %x\n", requestType, request, value, index, length); int res = devConn.controlTransfer(requestType, request, value, index, buff, length, interval); if (res < 0) { - System.err.println("Control Xfer failed: "+res); + res = -Errno.getErrno(); + if (res != -110) { + // Don't print for ETIMEDOUT + System.err.println("Control Xfer failed: "+res); + } } return res;