4685a82470cae609161714d66d53f053ea5a56f4
[fw/altos] / altosdroid / src / org / altusmetrum / AltosDroid / AltosUsb.java
1 /*
2  * Copyright © 2015 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 package org.altusmetrum.AltosDroid;
19
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.util.UUID;
24 import java.util.HashMap;
25
26 import android.content.Context;
27 import android.hardware.usb.*;
28 import android.app.*;
29 import android.os.Handler;
30 import android.util.Log;
31
32 import org.altusmetrum.altoslib_7.*;
33
34 public class AltosUsb extends AltosDroidLink {
35
36         // Debugging
37         private static final String TAG = "AltosUsb";
38         private static final boolean D = true;
39
40         private Thread           input_thread   = null;
41
42         private Handler          handler;
43
44         private UsbManager              manager;
45         private UsbDevice               device;
46         private UsbDeviceConnection     connection;
47         private UsbInterface            iface;
48         private UsbEndpoint             in, out;
49
50         private InputStream      input;
51         private OutputStream     output;
52
53         // Constructor
54         public AltosUsb(Context context, UsbDevice device, Handler handler) {
55                 super(handler);
56 //              set_debug(D);
57                 this.handler = handler;
58
59                 iface = null;
60                 in = null;
61                 out = null;
62
63                 int     niface = device.getInterfaceCount();
64
65                 for (int i = 0; i < niface; i++) {
66
67                         iface = device.getInterface(i);
68
69                         in = null;
70                         out = null;
71
72                         int nendpoints = iface.getEndpointCount();
73
74                         for (int e = 0; e < nendpoints; e++) {
75                                 UsbEndpoint     endpoint = iface.getEndpoint(e);
76
77                                 if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
78                                         switch (endpoint.getDirection()) {
79                                         case UsbConstants.USB_DIR_OUT:
80                                                 out = endpoint;
81                                                 break;
82                                         case UsbConstants.USB_DIR_IN:
83                                                 in = endpoint;
84                                                 break;
85                                         }
86                                 }
87                         }
88
89                         if (in != null && out != null)
90                                 break;
91                 }
92
93                 if (in != null && out != null) {
94                         Log.d(TAG, String.format("\tin %s out %s\n", in.toString(), out.toString()));
95
96                         manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
97
98                         if (manager == null) {
99                                 Log.d(TAG, "USB_SERVICE failed");
100                                 return;
101                         }
102
103                         connection = manager.openDevice(device);
104
105                         if (connection == null) {
106                                 Log.d(TAG, "openDevice failed");
107                                 return;
108                         }
109
110                         connection.claimInterface(iface, true);
111
112                         input_thread = new Thread(this);
113                         input_thread.start();
114
115                         // Configure the newly connected device for telemetry
116                         print("~\nE 0\n");
117                         set_monitor(false);
118                 }
119         }
120
121         static private boolean isAltusMetrum(UsbDevice device) {
122                 if (device.getVendorId() != AltosLib.vendor_altusmetrum)
123                         return false;
124                 if (device.getProductId() < AltosLib.product_altusmetrum_min)
125                         return false;
126                 if (device.getProductId() > AltosLib.product_altusmetrum_max)
127                         return false;
128                 return true;
129         }
130
131         static boolean matchProduct(int want_product, UsbDevice device) {
132
133                 if (!isAltusMetrum(device))
134                         return false;
135
136                 if (want_product == AltosLib.product_any)
137                         return true;
138
139                 int have_product = device.getProductId();
140
141                 if (want_product == AltosLib.product_basestation)
142                         return have_product == AltosLib.product_teledongle ||
143                                 have_product == AltosLib.product_teleterra ||
144                                 have_product == AltosLib.product_telebt ||
145                                 have_product == AltosLib.product_megadongle;
146
147                 if (want_product == AltosLib.product_altimeter)
148                         return have_product == AltosLib.product_telemetrum ||
149                                 have_product == AltosLib.product_telemega ||
150                                 have_product == AltosLib.product_easymega ||
151                                 have_product == AltosLib.product_telegps ||
152                                 have_product == AltosLib.product_easymini ||
153                                 have_product == AltosLib.product_telemini;
154
155                 if (have_product == AltosLib.product_altusmetrum)       /* old devices match any request */
156                         return true;
157
158                 if (want_product == have_product)
159                         return true;
160
161                 return false;
162         }
163
164         static public boolean request_permission(Context context, UsbDevice device, PendingIntent pi) {
165                 UsbManager      manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
166
167 //              if (manager.hasPermission(device))
168 //                      return true;
169
170                 Log.d(TAG, "request permission for USB device " + device.toString());
171
172                 manager.requestPermission(device, pi);
173                 return false;
174         }
175
176         static public UsbDevice find_device(Context context, int match_product) {
177                 UsbManager      manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
178
179                 HashMap<String,UsbDevice>       devices = manager.getDeviceList();
180
181                 for (UsbDevice  device : devices.values()) {
182                         int     vendor = device.getVendorId();
183                         int     product = device.getProductId();
184
185                         if (matchProduct(match_product, device)) {
186                                 Log.d(TAG, "found USB device " + device.toString());
187                                 return device;
188                         }
189                 }
190
191                 return null;
192         }
193
194         private void disconnected() {
195                 if (closed()) {
196                         if (D) Log.d(TAG, "disconnected after closed");
197                         return;
198                 }
199
200                 if (D) Log.d(TAG, "Sending disconnected message");
201                 handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
202         }
203
204         void close_device() {
205                 UsbDeviceConnection     tmp_connection;
206
207                 synchronized(this) {
208                         tmp_connection = connection;
209                         connection = null;
210                 }
211
212                 if (tmp_connection != null) {
213                         if (D) Log.d(TAG, "Closing USB device");
214                         tmp_connection.close();
215                 }
216         }
217
218         int read(byte[] buffer, int len) {
219                 int ret = connection.bulkTransfer(in, buffer, len, -1);
220                 if (D) Log.d(TAG, String.format("read(%d) = %d\n", len, ret));
221                 return ret;
222         }
223
224         int write(byte[] buffer, int len) {
225                 int ret = connection.bulkTransfer(out, buffer, len, -1);
226                 if (D) Log.d(TAG, String.format("write(%d) = %d\n", len, ret));
227                 return ret;
228         }
229
230         // Stubs of required methods when extending AltosLink
231         public boolean can_cancel_reply()   { return false; }
232         public boolean show_reply_timeout() { return true; }
233         public void hide_reply_timeout()    { }
234
235 }