Skip to content

Commit

Permalink
Merge pull request #7 from raleighlittles/feature/webusb
Browse files Browse the repository at this point in the history
Add WebUSB implementation
  • Loading branch information
raleighlittles authored Mar 22, 2024
2 parents e8e0414 + 1b0d11c commit 24333e7
Show file tree
Hide file tree
Showing 2 changed files with 260 additions and 0 deletions.
225 changes: 225 additions & 0 deletions web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebUSB Example</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
crossorigin="anonymous"></script>

<body>

<nav class="navbar navbar-light bg-light">
<a class="navbar-brand">PlayStation Camera firmware programmer</a>
<a class="btn btn-light" href="#" role="button">About</a>
<img src="https://raleighlittles.github.io/blog/assets/images/logo.png" width="30" height="30" alt="">

</nav>

<div class="container">

<div class="row mt-5">
<div class="column">
<h2> Step #1 </h2>
</div>
<div class="column ml-3 mr-2">
<a class="btn btn-outline-primary" id="connectBtn">Connect to Device</a>
</div>
</div>

<div class="row mt-5">


<div class="column">
<h2> Step #2 </h2>
</div>

<div class="column">
<div class="mt-5 mb-3">
<label for="firmwareFileUploadBtnLabel" class="form-label">Select a firmware file to install</label>
<input class="form-control" type="file" id="firmwareFileUploadBtn">
</div>

</div>

</div>

<!-- to be able to change the progress bar later, the id needs to be on the INNER element, not the outer -->
<div class="progress mt-4 mb-4" role="progressbar" aria-label="Example with label" aria-valuenow="0"
aria-valuemin="0" aria-valuemax="100">
<div class="progress-bar" id="programmingProgressBar" style="width: 0%"></div>
</div>


<div class="row">
<h3> Debug Log </h3>
</div>

<div class="row">
<!-- Empty by default, filled in later -->
<textarea id="debugLogTextArea" name="log" rows="10" cols="100"> </textarea>
</div>

</div>

<script>

let usbDevice;

function updateDebugLog(newString) {

let debugLogElement = document.getElementById("debugLogTextArea");

let newLogEntry = `${Date().toLocaleString()} | ${newString} \n`;

debugLogElement.innerText += newLogEntry;

}


function updateProgressBar(value_num, value_denom) {

let progressBarElement = document.getElementById("programmingProgressBar");

let newPercentage = `${Math.round((value_num / value_denom) * 100, 2)}%`;

progressBarElement.style.width = newPercentage;
}


async function uploadFirmwareToDevice(usbDevice, firmwareFileAsBytes) {

const uint16Max = 65536;

// USB constants
const maxUsbPacketSize = 512;

const firmwareFileLen = firmwareFileAsBytes.byteLength;

let fileByteIdx = 0;

let wValue = 0;

let lowerTransactionIdx = 0x14; // 20d
let upperTransactionIdx = 0x15; // 21d

while (fileByteIdx < firmwareFileLen) {

let pktSize = Math.min(firmwareFileLen - fileByteIdx, maxUsbPacketSize);
let pktEndIdx = fileByteIdx + pktSize;

let currTransactionIdx;

if (fileByteIdx < uint16Max) {
currTransactionIdx = lowerTransactionIdx;
}
else {
currTransactionIdx = upperTransactionIdx;
}

let transactionData = firmwareFileAsBytes.slice(fileByteIdx, pktEndIdx);

console.log(transactionData);

await usbDevice.controlTransferOut({ requestType: "standard", recipient: "device", request: 0x0, value: wValue, index: currTransactionIdx }, transactionData);

//console.log("Transferred", pktSize, "bytes. Value=", wValue, "index=", currTransactionIdx);

// Increment for next transaction
fileByteIdx += pktSize;

if (wValue > uint16Max - pktSize) {
// Can't perform the addition because it would overflow
// How much would it overflow by? That's the new value
wValue = (wValue - (uint16Max - pktSize));
} else {
// Regular case, no overflow
wValue += pktSize;
}

// Update progress bar
updateProgressBar(fileByteIdx, firmwareFileLen);

} // end while

// now send footer packet

let footerData = Array([0x5B]).buffer;
await usbDevice.controlTransferOut({ requestType: "standard", recipient: "device", request: 0x0, value: 0x2200, index: 0x8018 }, footerData);

} // end function

document.getElementById("connectBtn").addEventListener("click", async () => {

const vendorID = 0x05a9;
const productID = 0x0580;

navigator.usb.requestDevice({ filters: [{ vendorId: vendorID, productId: productID }] })
.then(selectedDevice => {
usbDevice = selectedDevice;
return usbDevice.open();
}).then(() => {

let msg = `Connected to USB device with vendorID=${vendorID}, productID=${productID} \n`;
console.log(msg);
//debugLogElement.innerText += msg;
updateDebugLog(msg);

})
// Request exclusive control over interface #0, remember, the device only has 1 interface
.then(() => usbDevice.claimInterface(0))
.catch(error => { console.error("Error connecting to USB device!", error); });
}

); // end connect click event listener

let firmwareFileUploadElement = document.getElementById("firmwareFileUploadBtn");

// https://stackoverflow.com/a/32556944/1576548
firmwareFileUploadElement.addEventListener("change", function () {

// console.log("Received upload: ", firmwareFileUploadElement.value);

var reader = new FileReader();

var fileByteArray = [];

reader.readAsArrayBuffer(this.files[0]);

reader.onloadend = function (evt) {

if (evt.target.readyState = FileReader.DONE) {

var arrayBuffer = evt.target.result;

var array = new Uint8Array(arrayBuffer);

for (var i = 0; i < array.length; i++) {
fileByteArray.push(array[i]);
}

let msg = `User uploaded file '${firmwareFileUploadElement.value}' with size '${arrayBuffer.byteLength}' bytes \n`;

console.log(msg);
//debugLogElement.innerText += msg;
updateDebugLog(msg);

console.log("array", array.buffer);

//console.log("fileByteArray", fileByteArray);

uploadFirmwareToDevice(usbDevice, fileByteArray).then(() => console.log("finished uploading firmware!"));
}
}

}); // end change file event listener
</script>
</body>

</html>
35 changes: 35 additions & 0 deletions web/index.html.bak
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebUSB Example</title>
</head>

<body>
<button id="connectButton">Connect to Device</button>

<script>
document.getElementById('connectButton').addEventListener('click', async () => {

const vendorID = 0x05a9;
const productID = 0x0580;

let usbDevice;

navigator.usb.requestDevice({ filters: [{ vendorId: vendorID, productId: productID }] })
.then(selectedDevice => {
usbDevice = selectedDevice;
return usbDevice.open(); // Begin a session.
}).then(() => console.log("Connected to USB device with vendorID=", vendorID, ", productID=", productID))
// Request exclusive control over interface #0, remember, the device only has 1 interface
.then(() => usbDevice.claimInterface(0))
.catch(error => { console.error(error); });
}

); // end click event listener
</script>
</body>

</html>

0 comments on commit 24333e7

Please sign in to comment.