This repository has been archived by the owner on Jul 15, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 171
/
firmwareUpdate-main.ts
118 lines (110 loc) · 3.36 KB
/
firmwareUpdate-main.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import { log } from "@ledgerhq/logs";
import { Observable, from, of, EMPTY, concat, throwError } from "rxjs";
import {
concatMap,
delay,
scan,
distinctUntilChanged,
throttleTime,
} from "rxjs/operators";
import { CantOpenDevice, DeviceInOSUExpected } from "@ledgerhq/errors";
import type { FirmwareUpdateContext } from "../types/manager";
import { withDevicePolling, withDevice } from "./deviceAccess";
import getDeviceInfo from "./getDeviceInfo";
import flash from "./flash";
import installFinalFirmware from "./installFinalFirmware";
import { hasFinalFirmware } from "./hasFinalFirmware";
const wait2s = of({
type: "wait",
}).pipe(delay(2000));
type Res = {
installing: string | null | undefined;
progress: number;
};
const main = (
deviceId: string,
{ final, shouldFlashMCU }: FirmwareUpdateContext
): Observable<Res> => {
log("hw", "firmwareUpdate-main started");
const withFinal = hasFinalFirmware(final);
const withDeviceInfo = withDevicePolling(deviceId)(
(transport) => from(getDeviceInfo(transport)),
() => true // accept all errors. we're waiting forever condition that make getDeviceInfo work
);
const withDeviceInstall = (install) =>
withDevicePolling(deviceId)(
install,
(e) => e instanceof CantOpenDevice // this can happen if withDevicePolling was still seeing the device but it was then interrupted by a device reboot
);
const waitForBootloader = withDeviceInfo.pipe(
concatMap((deviceInfo) =>
deviceInfo.isBootloader ? EMPTY : concat(wait2s, waitForBootloader)
)
);
const potentialAutoFlash = withDeviceInfo.pipe(
concatMap((deviceInfo) =>
deviceInfo.isOSU
? EMPTY
: withDevice(deviceId)(
(transport) =>
new Observable((o) => {
const timeout = setTimeout(() => {
log("firmware", "potentialAutoFlash timeout");
o.complete();
}, 20000);
const disconnect = () => {
log("firmware", "potentialAutoFlash disconnect");
o.complete();
};
transport.on("disconnect", disconnect);
return () => {
clearTimeout(timeout);
transport.off("disconnect", disconnect);
};
})
)
)
);
const bootloaderLoop = withDeviceInfo.pipe(
concatMap((deviceInfo) =>
!deviceInfo.isBootloader
? EMPTY
: concat(withDeviceInstall(flash(final)), wait2s, bootloaderLoop)
)
);
const finalStep = !withFinal
? EMPTY
: withDeviceInfo.pipe(
concatMap((deviceInfo) =>
!deviceInfo.isOSU
? throwError(new DeviceInOSUExpected())
: withDeviceInstall(installFinalFirmware)
)
);
const all = shouldFlashMCU
? concat(waitForBootloader, bootloaderLoop, finalStep)
: concat(potentialAutoFlash, finalStep);
return all.pipe(
scan(
(acc: Res, e: any): Res => {
if (e.type === "install") {
return {
installing: e.step,
progress: 0,
};
}
if (e.type === "bulk-progress") {
return { ...acc, progress: e.progress };
}
return acc;
},
{
progress: 0,
installing: null,
}
),
distinctUntilChanged(),
throttleTime(100)
);
};
export default main;