Skip to content

Commit 383948f

Browse files
committed
Import prototype with command sending
1 parent 8ec1a7c commit 383948f

File tree

4 files changed

+164
-0
lines changed

4 files changed

+164
-0
lines changed

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2021 Danny Lin <[email protected]>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

fastboot.js

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
let device = null;
2+
3+
class UsbError extends Error {
4+
constructor(message) {
5+
super(message);
6+
this.name = this.constructor.name;
7+
}
8+
}
9+
10+
async function connectFastboot() {
11+
device = await navigator.usb.requestDevice({
12+
filters: [
13+
{ vendorId: 0x18d1, productId: 0x4ee0 },
14+
],
15+
});
16+
console.log('dev', device);
17+
18+
// Validate device
19+
let interface = device.configurations[0].interfaces[0].alternates[0];
20+
if (interface.endpoints.length != 2) {
21+
throw new UsbError('Interface has wrong number of endpoints');
22+
}
23+
24+
if (interface.interfaceClass != 255 || interface.interfaceProtocol != 3 || interface.interfaceSubclass != 66) {
25+
throw new UsbError('Interface has wrong class, subclass, or protocol');
26+
}
27+
28+
let epIn = null;
29+
let epOut = null;
30+
for (let endpoint of interface.endpoints) {
31+
console.log('check endpoint', endpoint)
32+
if (endpoint.type != 'bulk') {
33+
throw new UsbError('Interface endpoint is not bulk');
34+
}
35+
36+
if (endpoint.direction == 'in') {
37+
if (epIn == null) {
38+
epIn = endpoint.endpointNumber;
39+
} else {
40+
throw new UsbError('Interface has multiple IN endpoints');
41+
}
42+
} else if (endpoint.direction == 'out') {
43+
if (epOut == null) {
44+
epOut = endpoint.endpointNumber;
45+
} else {
46+
throw new UsbError('Interface has multiple OUT endpoints');
47+
}
48+
}
49+
}
50+
console.log('eps: in', epIn, 'out', epOut);
51+
52+
await device.open();
53+
await device.reset();
54+
await device.selectConfiguration(1);
55+
await device.claimInterface(0); // fastboot
56+
}
57+
58+
async function sendCommand(device, command) {
59+
if (command.length > 64) {
60+
throw new RangeError();
61+
}
62+
63+
let cmdPacket = new TextEncoder('utf-8').encode(command);
64+
await device.transferOut(0x01, cmdPacket);
65+
66+
let returnStr = ''
67+
let response;
68+
do {
69+
let respPacket = await device.transferIn(0x01, 64);
70+
console.log('resppacket', respPacket)
71+
response = new TextDecoder().decode(respPacket.data);
72+
console.log('resppacket', respPacket, 'resp', response);
73+
74+
if (response.startsWith('OKAY')) {
75+
returnStr += response.substring(4);
76+
} else {
77+
returnStr += `[${response.substring(0, 4)}]: ${response.substring(4)}\n`;
78+
}
79+
} while (response.startsWith('INFO'));
80+
81+
return returnStr;
82+
}

index.html

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>fastboot.js demo</title>
5+
<script src="fastboot.js"></script>
6+
<script src="ui.js"></script>
7+
</head>
8+
9+
<body>
10+
<div>
11+
<p>Status: <span class="status-field">Not connected</span></p>
12+
<button onclick="connectDevice();">Connect device</button>
13+
</div>
14+
15+
<hr/>
16+
17+
<div>
18+
<form class="command-form">
19+
<label for="command">Command:</label>
20+
<input type="text" name="command" class="command-input">
21+
<input type="submit" value="Send">
22+
</form>
23+
<p>Result:</p>
24+
<pre class="result-field"></pre>
25+
</div>
26+
27+
<script>
28+
document.querySelector('.command-form').addEventListener('submit', sendFormCommand);
29+
</script>
30+
</body>
31+
</html>

ui.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
async function connectDevice() {
2+
let statusField = document.querySelector('.status-field');
3+
statusField.textContent = 'Connecting...';
4+
5+
try {
6+
await connectFastboot();
7+
} catch (error) {
8+
statusField.textContent = `Failed to connect to device: ${error.message}`;
9+
return;
10+
}
11+
12+
let product = await sendCommand(device, 'getvar:product');
13+
let serial = await sendCommand(device, 'getvar:serialno');
14+
let status = `Connected to ${product} (serial: ${serial})`;
15+
statusField.textContent = status;
16+
}
17+
18+
async function _sendFormCommand() {
19+
let inputField = document.querySelector('.command-input');
20+
let command = inputField.value;
21+
let result = await sendCommand(device, command);
22+
document.querySelector('.result-field').textContent = result;
23+
inputField.value = '';
24+
}
25+
26+
function sendFormCommand(event) {
27+
event.preventDefault();
28+
_sendFormCommand();
29+
return false;
30+
}

0 commit comments

Comments
 (0)