ota-hub-reactjs 0.0.14 → 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,8 @@
1
1
  import { DeviceConnectionState, AddConnectionProps } from "../base/device-whisperer.js";
2
2
  export declare class UsbTransport {
3
3
  device: USBDevice;
4
- interfaceNumber: number;
4
+ controlInterface: number;
5
+ dataInterface: number;
5
6
  endpointIn: number;
6
7
  endpointOut: number;
7
8
  private static readonly SET_LINE_CODING;
@@ -14,7 +14,8 @@ import { useEffect } from "react";
14
14
  // usb-transport.ts
15
15
  export class UsbTransport {
16
16
  constructor(device) {
17
- this.interfaceNumber = 0;
17
+ this.controlInterface = 0;
18
+ this.dataInterface = 1;
18
19
  this.endpointIn = 0;
19
20
  this.endpointOut = 0;
20
21
  this.device = device;
@@ -27,25 +28,40 @@ export class UsbTransport {
27
28
  if (this.device.configuration === null) {
28
29
  await this.device.selectConfiguration(1);
29
30
  }
30
- // Find the CDC Data interface (usually class 10) or just the first one with Bulk endpoints
31
31
  const interfaces = this.device.configuration?.interfaces || [];
32
- let targetInterface;
33
- for (const iface of interfaces) {
32
+ let ctrlIface;
33
+ let dataIface;
34
+ dataIface = interfaces.find(iface => {
34
35
  const endpoints = iface.alternate.endpoints;
35
- const hasIn = endpoints.some(e => e.direction === 'in' && e.type === 'bulk');
36
- const hasOut = endpoints.some(e => e.direction === 'out' && e.type === 'bulk');
37
- if (hasIn && hasOut) {
38
- targetInterface = iface;
39
- break;
36
+ return endpoints.some(e => e.direction === 'in' && e.type === 'bulk') &&
37
+ endpoints.some(e => e.direction === 'out' && e.type === 'bulk');
38
+ });
39
+ if (dataIface) {
40
+ this.dataInterface = dataIface.interfaceNumber;
41
+ ctrlIface = interfaces.find(i => i.interfaceNumber === this.dataInterface - 1)
42
+ || interfaces.find(i => i.alternate.interfaceClass === 2);
43
+ this.controlInterface = ctrlIface ? ctrlIface.interfaceNumber : this.dataInterface;
44
+ }
45
+ if (!dataIface) {
46
+ throw new Error("No serial-compatible Bulk interface found.");
47
+ }
48
+ // Note: On Android, if the OS has claimed Interface 0, this first call will fail.
49
+ try {
50
+ if (this.controlInterface !== this.dataInterface) {
51
+ await this.device.claimInterface(this.controlInterface);
40
52
  }
41
53
  }
42
- if (!targetInterface) {
43
- throw new Error("No serial-compatible interface found on device.");
54
+ catch (e) {
55
+ console.warn("Could not claim Control Interface (OS locked?). Proceeding to Data...", e);
56
+ // We continue, but setSignals might fail later.
57
+ }
58
+ try {
59
+ await this.device.claimInterface(this.dataInterface);
60
+ }
61
+ catch (e) {
62
+ throw new Error(`Failed to claim Data Interface. Android OS has locked the device driver. Try using a 'Vendor Specific' USB Class device or a native Serial App workaround.`);
44
63
  }
45
- this.interfaceNumber = targetInterface.interfaceNumber;
46
- await this.device.claimInterface(this.interfaceNumber);
47
- // Map endpoints
48
- const endpoints = targetInterface.alternate.endpoints;
64
+ const endpoints = dataIface.alternate.endpoints;
49
65
  this.endpointIn = endpoints.find(e => e.direction === 'in' && e.type === 'bulk').endpointNumber;
50
66
  this.endpointOut = endpoints.find(e => e.direction === 'out' && e.type === 'bulk').endpointNumber;
51
67
  await this.setBaudRate(baudRate);
@@ -57,7 +73,6 @@ export class UsbTransport {
57
73
  async write(data) {
58
74
  if (!this.device.opened)
59
75
  return;
60
- // Cast to 'unknown' then 'BufferSource' to satisfy the strict type definition
61
76
  await this.device.transferOut(this.endpointOut, data);
62
77
  }
63
78
  /**
@@ -85,14 +100,13 @@ export class UsbTransport {
85
100
  async setSignals({ dtr, rts }) {
86
101
  if (!this.device.opened)
87
102
  return;
88
- // CDC-ACM: DTR is bit 0, RTS is bit 1
89
103
  const value = (Number(dtr) | (Number(rts) << 1));
90
104
  await this.device.controlTransferOut({
91
105
  requestType: 'class',
92
106
  recipient: 'interface',
93
107
  request: UsbTransport.SET_CONTROL_LINE_STATE,
94
108
  value: value,
95
- index: this.interfaceNumber
109
+ index: this.controlInterface
96
110
  });
97
111
  }
98
112
  async setBaudRate(baud) {
@@ -102,20 +116,31 @@ export class UsbTransport {
102
116
  // 4 bytes: baud (LE), 1 byte: stop bits, 1 byte: parity, 1 byte: data bits
103
117
  const buffer = new ArrayBuffer(7);
104
118
  const view = new DataView(buffer);
105
- view.setUint32(0, baud, true); // Little Endian
106
- view.setUint8(4, 0); // 1 stop bit
107
- view.setUint8(5, 0); // No parity
108
- view.setUint8(6, 8); // 8 data bits
119
+ view.setUint32(0, baud, true);
120
+ view.setUint8(4, 0);
121
+ view.setUint8(5, 0);
122
+ view.setUint8(6, 8);
109
123
  await this.device.controlTransferOut({
110
124
  requestType: 'class',
111
125
  recipient: 'interface',
112
126
  request: UsbTransport.SET_LINE_CODING,
113
127
  value: 0,
114
- index: this.interfaceNumber
128
+ index: this.controlInterface
115
129
  }, buffer);
116
130
  }
117
131
  async disconnect() {
118
132
  if (this.device.opened) {
133
+ // Release both
134
+ try {
135
+ await this.device.releaseInterface(this.dataInterface);
136
+ }
137
+ catch (e) { }
138
+ if (this.controlInterface !== this.dataInterface) {
139
+ try {
140
+ await this.device.releaseInterface(this.controlInterface);
141
+ }
142
+ catch (e) { }
143
+ }
119
144
  await this.device.close();
120
145
  }
121
146
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ota-hub-reactjs",
3
- "version": "0.0.14",
3
+ "version": "0.0.15",
4
4
  "description": "ReactJS tools for building web apps to flash MCU devices such as esp32, brought to you by OTA Hub.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",