diff --git a/README.md b/README.md index 1a1dfc5..eb33d1d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,26 @@ -#Usage +# Warning : Use this tool at your own risks, nobody can be taken as responsible if it damage your calculator !!! + +# Usage The buttons are actually diconnected, if you want to test, dive through the code. + +# Things to change +- Have a more js-like code approach ! + +# Actual possibilities +- You can send files to flash memory (at least fls0, but maybe crd0 too), but only below ~1.5 ko (it freezes beyond, will patch this later) +- You can list files, make directories, remove directories etc... +- you can optimize your flash +- you can get your device infos + +# Future possibilities + +- Make a user friendly interface +- Support all p7 protocol's commands +- Screen casting ? (not a usb but a graphic difficulty) +- other stuffs + +# Thanks to : + +Simon Lothar for his fx-reverse doc +Cakeisalie5 for his p7 command line tool \ No newline at end of file diff --git a/index.js b/index.js index 12c6127..e55efe3 100644 --- a/index.js +++ b/index.js @@ -1,233 +1,14 @@ -"use strict"; -let device = undefined; -let fileList = undefined; - - -let p7SendBuffer = new Uint8Array(1032); //Max send buffer size -let p7ReceiveBuffer = new Uint8Array(1032 + 1); //Max receive buffer size - -let p7ReceivedPacketInfo = { - 'type' : undefined, - 'subtype' : undefined, - 'extended' : undefined, - 'dataSize' : undefined, - 'data' : undefined, - 'checksum' : undefined, - 'packetSize': undefined -}; - - -let p7DeviceInformation = { - 'hardwareIdentifier' : undefined, - 'processorIdentifier' : undefined, - 'preprogrammedROMCapacity' : undefined, - 'flashROMCapacity' : undefined, - 'ramCapacity' : undefined, - 'prepogrammedROMVersion' : undefined, - 'bootCodeVersion' : undefined, - 'bootCodeOffset' : undefined, - 'bootCodeSize' : undefined, - 'osCodeVersion' : undefined, - 'osCodeOffset' : undefined, - 'osCodeSize' : undefined, - 'protocolVersion' : undefined, - 'productID' : undefined, - 'nameSetByUserInSystem' : undefined -}; - -var fls0 = []; -var crd0 = []; - -/* List the files of a filesystem (fls0, crd0...) */ -/* Return 0 on success and the error on failure */ -async function list(fileSystem) { - let err = 0; - - //Send a command packet requesting the calculator to list it's file (in the flash memory) and receive the response packet - if (err = await p7SendExtendedCommandPacket(flashCommandSubtype.fileInfoTransferAllRequest, 0, 0, 0, ["", "", "", "", fileSystem, ""])) - return "Couldn't send the command packet to the calculator : " + err; - - //Send a roleswap packet so that the calculator can become the active side and send the list of it's files (in flash memory) - if (err = await p7SendBasicPacket(p7PacketType.roleswap, roleswapSubtype.default)) - return "Couldn't send the roleswap packet : " + err; - - //Reset the list since the calculator send each time all it's elements - window[fileSystem] = []; - - //Receive and parse the file list - while(p7ReceivedPacketInfo.type !== p7PacketType.roleswap) { - window[fileSystem].push(createFileObject(String.fromCharCode.apply(null, p7CommandPacketGetField(4)), String.fromCharCode.apply(null, p7CommandPacketGetField(0)), String.fromCharCode.apply(null, p7CommandPacketGetField(1)), p7CommandPacketGetFilesize())); - - if (err = await p7SendBasicPacket(p7PacketType.ack, ackSubtype.default)) - return "Couldn't send the acknowledgement packet : " + err; - } - return 0; -}; - -async function createDirectory(name, filesystem) { - let err = 0; - //Send a command packet which asks the calculator to create a directory - if (err = await p7SendExtendedCommandPacket(flashCommandSubtype.createDirectory, 0, 0, 0, [name, "", "", "", filesystem, ""])) - return 'Couldn\'t send the command : ' + err; - - //Check the response packet type - if (p7ReceivedPacketInfo.type === p7PacketType.ack) - return 0; - return "The response packet wasn't as expected."; -} - -async function deleteDirectory(name, filesystem) { - let err = 0; - //Send a command packet which asks the calculator to delete a directory - if (err = await p7SendExtendedCommandPacket(flashCommandSubtype.deleteDirectory, 0, 0, 0, [name, "", "", "", filesystem, ""])) - return 'Couldn\'t send the command : ' + err; - - //Check the response packet type - if (p7ReceivedPacketInfo.type === p7PacketType.ack) - return 0; - return "The response packet wasn't as expected."; -} - -/* Doesn't work ?? */ -async function renameDirectory(oldName, newName, filesystem) { - let err = 0; - //Send a command packet which asks the calculator to delete a directory - if (err = await p7SendExtendedCommandPacket(flashCommandSubtype.renameDirectory, 0, 0, 0, [oldName, newName, "", "", filesystem, ""])) - return 'Couldn\'t send the command : ' + err; - - //check the response packet type - if (p7ReceivedPacketInfo.type === p7PacketType.ack) - return 0; - return "The response packet wasn't as expected."; -} - -/* Doesn't work ?? */ -async function changeDirectory(name, filesystem) { - let err = 0; - //Send a command packet which asks the calculator change working directory - if (err = await p7SendExtendedCommandPacket(flashCommandSubtype.changeDirectory, 0, 0, 0, [name, "", "", "", filesystem, ""])) - return 'Couldn\'t send the command : ' + err; - - if (p7ReceivedPacketInfo.type === p7PacketType.ack) - return 0; - return "The response packet wasn't as expected."; -} - -/* TODO: finish it */ -async function fileTransfer(file, path, fileSystem) { - let err = 0; - console.log('Sending file transfer request'); - //Send a command packet which asks the calculator if we can send a file - if (err = await p7SendExtendedCommandPacket(flashCommandSubtype.fileTransfer, 0, 0, file.size, [path, file.name, "", "", fileSystem, ""])) - return 'Couldn\'t send the command : ' + err; - - console.log(p7SendBuffer.slice(0, 64)); - if (p7ReceivedPacketInfo.type === p7PacketType.error && p7ReceivedPacketInfo.subtype === errorSubtype.overwriteRequest) { - if (err = (confirm('The file already exists, overwrite ?')) ? await p7SendBasicPacket(p7PacketType.ack, ackSubtype.overwriteYes) : await p7SendBasicPacket(p7PacketType.error, errorSubtype.overwriteNo)) - return 'Couldn\'t send the overwrite response packet : ' + err; - if (p7ReceivedPacketInfo.type === p7PacketType.error && p7ReceivedPacketInfo.subtype === errorSubtype.overwriteImpossible) - return 'The calculator couldn\'t overwrite the file : ' + err; - } - - console.log('Sending data'); - if (err = await p7SendData(flashCommandSubtype.fileTransfer, content)) - return 'Couldn\'t send the file : ' + err; - - if (p7ReceivedPacketInfo.type ==! p7PacketType.ack) - return "unexpected response packet"; - return 0; -} - -/* TODO: add promise system */ -function getCapacity(fileSystem) { - return new Promise((resolve, reject) => { - let err = 0; - //Send a command packet requesting the calculator to send it's capacity (free memory) - if (err = p7SendExtendedCommandPacket(flashCommandSubtype.capacityTransmitRequest, 0, 0, 0, ["", "", "", "", fileSystem, ""])) - reject('Couldn\'t send the command packet to the calculator : ' + err); - - //Send a roleswap packet so that the calculator can become the active side and send it's capacity - if (err = p7SendBasicPacket(p7PacketType.roleswap, roleswapSubtype.default)) - reject('Couldn\'t send the roleswap packet : ' + err); - - let freeMemory = p7CommandPacketGetFilesize(); - - //Send an acknowledgement packet and receive the response packet - if (err = p7SendBasicPacket(p7PacketType.ack, roleswapSubtype.default)) - reject('Couldn\'t send the acknowledgement packet : ' + err); - - if (p7ReceivedPacketInfo.type !== p7PacketType.roleswap) - reject('The calculator didn\'t respond with the expected packet : ' + p7ReceivedPacketInfo.type + ' (packet type) ' + p7ReceivedPacketInfo.subtype + ' (packet subtype).'); - - //Everything went right - resolve(freeMemory); - }); -}; - - - -async function optimize(fileSystem) { - let err = 0 - if (err = await p7SendExtendedCommandPacket(flashCommandSubtype.optimizeFilesystem, 0, 0, 0, ["", "", "", "", fileSystem, ""])) - return "Couldn't optimize the fileSystem : " + err; - return 0; +'use strict' +/*function test(){ + throw 'geswg'; + console.log('err'); } -/* Main function */ -/* Return 0 on success and the error on failure */ -async function talkToCalculator() { - let err = await p7Initialization(); - if (err) - return "Failed to initiate the connection : " + err; - - console.log("%cExecute user request", "font-size: 14px; color: white"); +try { + test(); + console.log('err'); +} catch(err) { + console.error(err); +}*/ - //if (err = fileTransfer(0, )) - - let capacity = 0; - console.log(getCapacity('fls0').then((success) => capacity = success, (failure) => failure)); - console.log(capacity); - //var file = document.getElementById('input').files[0]; - //var file = await window.showOpenFilePicker(); - //await fileTransfer(file, "", 'fls0'); - //await list('fls0'); - //console.log(file); - /* Terminate packet handling */ - -// //Check if the packet is a terminate packet -// if (p7ReceivedPacketInfo.type === p7PacketType.terminate) -// return 'The calculator closed the connection' + (err = await p7DecodeTerminatePacket()) ? ';but there was an error exiting the session : ' + err : '.'; - - - document.getElementById('demo').innerHTML = - 'Hardware identifier : ' + p7DeviceInformation['Hardware identifier'] + - '
Processor identifier : ' + p7DeviceInformation['Processor identifier'] + - '
Preprogrammed ROM capaciy : ' + p7DeviceInformation['Preprogrammed ROM capaciy']+ - '
Flash ROM capacity : ' + p7DeviceInformation['Flash ROM capacity'] + - '
RAM capacity : ' + p7DeviceInformation['RAM capacity'] + - '
Prepogrammed ROM version : ' + p7DeviceInformation['Prepogrammed ROM version'] + - '
Boot code version : ' + p7DeviceInformation['Boot code version'] + - '
Boot code offset : ' + p7DeviceInformation['Boot code offset'] + - '
Boot code size : ' + p7DeviceInformation['Boot code size'] + - '
Os code version : ' + p7DeviceInformation['Os code version'] + - '
Os code offset : ' + p7DeviceInformation['Os code offset'] + - '
Os code size : ' + p7DeviceInformation['Os code size'] + - '
Protocol version : ' + p7DeviceInformation['Protocol version'] + - '
Product ID : ' + p7DeviceInformation['Product ID'] + - '
Name set by user in system : ' + p7DeviceInformation['Name set by user in system']; - - await device.transferOut(1, p7SendBuffer.slice(0, p7MakeBasicPacket(p7PacketType['terminate'], - terminateSubtype['default']))); -} - - -window.onload = (event) => { - document.querySelector('#connect').onclick = async () => { - let err = 0; - - if (err = await talkToCalculator()) - console.log('%c%s', "color: red", err); - - } -} diff --git a/internal.js b/internal.js index 93c683c..8a9514d 100644 --- a/internal.js +++ b/internal.js @@ -1,153 +1,189 @@ "use strict"; -/* Compute the (p7-like) checksum of a buffer */ -function p7Checksum(buffer, length) -{ - //Sum individual bytes in Subtype (ST) and following fields - let checksum = 0; - let i = 1; - for(length -= 2 ; i < length ; i++ ) - checksum+=buffer[i]; - /* NOT + 1, as defined in fxReverse documentation. +var p7 = { + device: {}, + make: {}, + decode: {}, + receive: {}, + send: {} +}; + + +(function() { + /* ---------------------Utilitaries--------------------- */ + + /* Sum individual bytes + * NOT + 1, as defined in fxReverse documentation. * Be sure it's under 256! */ - return (((~checksum) + 1) & 0xFF); -} + Uint8Array.prototype.p7Checksum = function () { + return (((~(this.reduce((accumulator, currentValue) => accumulator + currentValue))) + 1) & 0xFF) + } -/* Return an array with the ascii values of each hex digit*/ -function p7HexToAscii(hexNumber, digits) { - return p7StringToAscii(hexNumber.toString(16).toUpperCase().padStart(digits, 0)); -} + /* Convert a string into an array of ascii numbers */ + String.prototype.toAscii = function () { + return this.split('').map((char) => char.charCodeAt(0)) + } -/* Return an array with the ascii value of each character */ -function p7StringToAscii(string) { - return string.split('').map(letter => letter.charCodeAt(0)); -} + /* Convert an hex number into an array of ascii values + * Use the uppercase representation of the number */ + Number.prototype.hexToAscii = function (padding) { + return this.toString(16).toUpperCase().padStart(padding, 0).toAscii(); + } -/* Create a basic (Non-Extended) packet and push it to p7SendBuffer */ -/* return it's length (6) */ -function p7MakeBasicPacket(type, subtype) { - //Set Type (T) field - p7SendBuffer.set([type], 0); + /* Convert an ascii array into a string */ + p7.asciiToString = (array) => String.fromCharCode.apply(null, array); - //Set Subtype (ST) field - p7SendBuffer.set(p7HexToAscii(subtype, 2), 1); + /* Convert an ascii array representing an hex number into a number */ + //Uint8Array.prototype.fromAsc + p7.asciiToHex = (array) => Number('0x' + p7.asciiToString(array)); + + + p7.sendBuffer = new Uint8Array(1032); + p7.receiveBuffer = undefined;//new Uint8Array(1032 + 1); + + /* ---------------------Packet making--------------------- */ + + /* Create a basic (Non-Extended) packet and push it to p7.sendBuffer */ + /* return it's length (6) */ + p7.make.basicPacket = (type, subtype) => { + //Set the Type (T), Subtype (ST) and Extended (EX) fields + p7.sendBuffer.set([type] + .concat(subtype.hexToAscii(2)) + .concat([0x30]) + , 0); + + //Set the Checksum (CS) field + p7.sendBuffer.set(p7.sendBuffer.slice(1, 4).p7Checksum().hexToAscii(2), 4); - //Set Extended (EX) field - p7SendBuffer.set([0x30], 3); + //Return the packet length + return 6; + } - //Set Checksum (CS) field - p7SendBuffer.set(p7HexToAscii(p7Checksum(p7SendBuffer, 6), 2), 4); - - //Return basic packet length - return 6; -} + /* Fill the Data (D) field of a command packet in p7.sendBuffer */ + /* return the Data (D) field length */ + p7.make.commandPacketDataField = function (dataType, fileSize, commandArguments) { + //Set Overwrite (OW), Data type (DT) and File size (FS) fields + p7.sendBuffer.set([0x30, 0x30, /* Overwrite */ + 0x30, 0x30] /* Data type field ?? */ + .concat(fileSize.hexToAscii(8)) /* File size field */ + , 8); -/* Fill Data (D) field of a command packet in p7SendBuffer */ -/* return Data (D) field length */ -function p7MakeCommandPacketDataField(forceOverwrite, dataType, fileSize, commandArguments) { - //Set Overwrite (OW) field - p7SendBuffer.set([0x30, (forceOverwrite) ? ((forceOverwrite == 1) ? 0x31 : 0x32) : 0x30], 8); + + let index = 18; + //Set Size of Data {1-6} (SD{1-6}) fields + commandArguments.forEach(element => p7.sendBuffer.set(element.length.hexToAscii(2), (index += 2))); + + //Set Data {1-6} (D{1-6}) fields + let data = commandArguments.join(''); + p7.sendBuffer.set(data.toAscii(), 32); + + return data.length + 24; /* Overwrite (OW), Data type (DT), File size (FS) and Data size (DS) subfields of the Data (D) field of the packet */; + } - //Set Data type (DT) field ???? - p7SendBuffer.set([0x30, 0x30], 10); + /* Create an extended command packet and push it to p7.sendBuffer */ + /* return it's length */ + p7.make.extendedCommandPacket = function (subtype, dataType, fileSize, commandArguments) { + let dataSize = p7.make.commandPacketDataField(dataType, fileSize, commandArguments); /* Data field */ + + //Set Type (T), Subtype (ST), Extended (EX) and Data size (DT) fields + p7.sendBuffer.set([0x01] /* Type field */ + .concat(subtype.hexToAscii(2)) /* Subtype field */ + .concat([0x31]) /* Extended field */ + .concat(dataSize.hexToAscii(4)) /* Data size field */ + , 0); + + //Set Checksum (CS) field + p7.sendBuffer.set(p7.sendBuffer.slice(1, dataSize + 8).p7Checksum().hexToAscii(2), dataSize + 8); - //Set Filesize (FS) field - p7SendBuffer.set(p7HexToAscii(fileSize, 8), 12); + //Return it's length + return dataSize + 10 /* Type (T), Subtype (ST), Extended (EX), Data size (DT) and Checksum (CS) fields */; + } - let packetSize = 20; + /* Create a data packet and push it to p7.sendBuffer */ + /* return it's length */ + p7.make.dataPacket = function (subType, totalNumber, currentNumber, data) { + /* Set Type (T), Subtype (ST), Extended (EX), Data size (DS), + * Data (D) fields and Data (D) subfields : + * Total Number (TN), Current Number (CN), and Data (DD) */ + p7.sendBuffer.set([0x02] /* Type field */ + .concat(subType.hexToAscii(2)) /* Subtype (ST) field */ + .concat([0x31]) /* Extended (EX) field */ + .concat( /* Data size (DS) field */ + (data.length + 8 /* Total number (TN) and Current number (CN) subfields */ ).hexToAscii(4)) + .concat(totalNumber.hexToAscii(4)) /* Total number (TN) subfield */ + .concat(currentNumber.hexToAscii(4)) /* Current number (CN) subfield */ + .concat(data) /* Data (DD) subfield */ + , 0); + + //Set Checksum (CS) field + p7.sendBuffer.set(p7.sendBuffer.slice(1, data.length + 16).p7Checksum().hexToAscii(2), data.length + 16); - //Set Size of Data {1-6} (SD{1-6}) field - for (let i = 0 ; i < 6 ; packetSize += 2 , i++) - p7SendBuffer.set((commandArguments[i]) ? p7HexToAscii(commandArguments[i].length, 2) : [0x30, 0x30], packetSize); - - - //Set Data {1-6} (D{1-6}) field - for (let i = 0 ; i < 6 ; i++) - if (commandArguments[i]) { - p7SendBuffer.set(p7StringToAscii(commandArguments[i]), packetSize); - packetSize += commandArguments[i].length; + //Return it's length + return data.length + 18 /* All other fields and subfields */; + } + + + /* ---------------------Packet decoding--------------------- */ + + /* Return all the informations of the device, and trim string's overage (255) */ + p7.decode.extendedAckPacket = () => { + return { + hardwareIdentifier : p7.asciiToString(p7ReceiveBuffer.slice(8, 16)), + processorIdentifier : p7.asciiToString(p7ReceiveBuffer.slice(16, 32)), + preprogrammedROMCapacity : p7.asciiToString(p7ReceiveBuffer.slice(32, 40)), + flashROMCapacity : p7.asciiToString(p7ReceiveBuffer.slice(40, 48)), + ramCapacity : p7.asciiToString(p7ReceiveBuffer.slice(48, 56)), + prepogrammedROMVersion : p7.asciiToString(p7ReceiveBuffer.slice(56, 72).filter(number => number !== 255)), + bootCodeVersion : p7.asciiToString(p7ReceiveBuffer.slice(72, 88).filter(number => number !== 255)), + bootCodeOffset : p7.asciiToString(p7ReceiveBuffer.slice(88, 96).filter(number => number !== 255)), + bootCodeSize : p7.asciiToString(p7ReceiveBuffer.slice(96, 104).filter(number => number !== 255)), + osCodeVersion : p7.asciiToString(p7ReceiveBuffer.slice(104, 120).filter(number => number !== 255)), + osCodeOffset : p7.asciiToString(p7ReceiveBuffer.slice(120, 128)), + osCodeSize : p7.asciiToString(p7ReceiveBuffer.slice(128, 136)), + protocolVersion : p7.asciiToString(p7ReceiveBuffer.slice(136, 140)), + productID : p7.asciiToString(p7ReceiveBuffer.slice(140, 156).filter(number => number !== 255)), + username : p7.asciiToString(p7ReceiveBuffer.slice(156, 172).filter(number => number !== 255)) + } + }; + + /* Return the value (as an number) of the Filesize (FS) subfield of the Data (D) field of an extended command packet */ + p7.decode.commandPacketFilesize = () => p7.asciiToHex(p7.receiveBuffer.slice(12, 20)); + + /* ---------------------Packet receiving--------------------- */ + + /* Doesn't work for now on */ + p7.receive.packet = async function () { + /*try { + let array = undefined; + do { + array = await p7.device.transferIn(2, 64); + console.log(array); + } while (array.data.byteLength == 0) + + p7.receiveBuffer = Uint8Array(await p7.device.transferIn(2, 64).data.buffer); + } catch (err) { + throw new Error("Couldn't receive the first 64 bytes of the packet : " + err.message); } - return packetSize - 8; /* Type, Subtype, Extended, Data size fields of the packet */; -} + let packetInfo = {}; + + //Set Type (T) field + packetInfo.type = p7.receiveBuffer[0]; -/* Create an extended command packet and push it to p7SendBuffer */ -/* return it's length */ -function p7MakeExtendedCommandPacket(subtype, forceOverwrite, dataType, fileSize, commandArguments) { - //Set Type (T) field - p7SendBuffer.set([0x01], 0); + //Set Subtype (ST) field + packetInfo.subtype = p7.asciiToHex(p7.receiveBuffer.slice(1, 2)); - //Set Subtype (ST) field - p7SendBuffer.set(p7HexToAscii(subtype, 2), 1); + //Set Extended (EX) field + packetInfo.extended = (p7.receiveBuffer[3] === 0x31); - //Set Extended (EX) field - p7SendBuffer.set([0x31], 3); - - //Set Data (D) field - let dataSize = p7MakeCommandPacketDataField(forceOverwrite, dataType, fileSize, commandArguments); - - //Set Data size (DS) field - p7SendBuffer.set(p7HexToAscii(dataSize, 4), 4); - - let packetSize = dataSize + 10 /* Type, Subtype, Extended, Data size and Checksum fields */; - - //Set Checksum (CS) field - p7SendBuffer.set(p7HexToAscii(p7Checksum(p7SendBuffer, packetSize), 2), packetSize - 2); - - //Return it's length - return packetSize; -} - -/* Create a data packet and push it to p7SendBuffer */ -/* return it's length */ -function p7MakeDataPacket(subType, totalNumber, currentNumber, data) { - packetSize = data.length + 18 /* Type, Subtype, Extended, Data size, Data['Total Number'], Data['Current number'] and checksum fields */; - - //Set Type (T) field - p7SendBuffer.set([0x02], 0); - - //Set Subtype (ST) field - p7SendBuffer.set(p7HexToAscii(subType, 2), 1); - - //Set Extended (EX) field - p7SendBuffer.set([0x31], 3); - - //Set Data size (DS) field - p7SendBuffer.set(p7HexToAscii(data.length + 8 /* Total number (TN) and Current number (CN) fields */, 4), 4); - - //Set Total number (TN) field - p7SendBuffer.set(p7HexToAscii(totalNumber, 4), 8); - - //Set Current number (CN) field - p7SendBuffer.set(p7HexToAscii(currentNumber, 4), 12); - - //Set Data (DD) field - p7SendBuffer.set(data, 16); - - //Set Checksum (CS) field - p7SendBuffer.set(p7HexToAscii(p7Checksum(p7SendBuffer, packetSize), 2), packetSize - 2); - - //Return it's length - return packetSize; -} - -/* Receive a packet and parse it's properties to p7ReceivedPacketInfo */ -/* Make sure their aren't checksums error (ask for a resend if necessary) */ -async function p7ReceivePacket() { - console.log("%cReceiving...", "color: orange"); - let checksumError = 0; - let err = 0; - do { - //Send an error packet with subtype resend request if there was a checksum error - if (checksumError) - if (err = await p7SendBasicPacket(p7PacketType.error, errorSubtype.resendRequest)) - return "Couldn't send an error packet with resend request subtype (there was a checksum error) : " + err; + console.log(packetInfo); +*/ let transferInResult = 0; - + let err = 0; //Recieve and parse the packet do { - transferInResult = await device.transferIn(2, 64).then( + transferInResult = await p7.device.transferIn(2, 64).then( (result) => { return result; }, (error) => { @@ -155,274 +191,195 @@ async function p7ReceivePacket() { }); if (err) return "Couldn't receive the first 64 bytes of the packet : " + err; + console.log('retrying'); } while(!transferInResult.data.byteLength); - - p7ReceivedPacketInfo.packetSize = 4; /*Type, Subtype and Extended field*/ + + //p7ReceivedPacketInfo.packetSize = 4; /*Type, Subtype and Extended field*/ //Copy the received buffer to p7ReceiveBuffer - p7ReceiveBuffer.set(new Uint8Array(transferInResult.data.buffer), 0); + p7.receiveBuffer.set(new Uint8Array(transferInResult.data.buffer), 0); + console.log(p7.receiveBuffer.slice(0, 25)); + } - //Set Type (T) field - p7ReceivedPacketInfo.type = p7ReceiveBuffer[0]; + /* ---------------------Packet sending--------------------- */ - //Set Subtype (ST) field - p7ReceivedPacketInfo.subtype = Number('0x' + String.fromCharCode(p7ReceiveBuffer[1], p7ReceiveBuffer[2]), 16); - - //Set Extended (EX) field - p7ReceivedPacketInfo.extended = (p7ReceiveBuffer[3] === 0x31); - - if (!p7ReceivedPacketInfo.extended) { - p7ReceivedPacketInfo.packetSize += 2; /*Checksum field*/ + /* Doesn't work for now on */ + p7.send.packet = async function (length) { - //Set Checksum (CS) field - p7ReceivedPacketInfo.checksum = Number('0x' + String.fromCharCode(p7ReceiveBuffer[4], p7ReceiveBuffer[5]), 16); - - //Compute the checksum - checksumError = (p7ReceivedPacketInfo.checksum !== p7Checksum(p7ReceiveBuffer, p7ReceivedPacketInfo.packetSize)); + /*try { + await p7.device.transferOut(1, p7.sendBuffer.slice(0, length)); + } catch (err) { + throw new Error("Couldn't send the packet : " + err.message); + }*/ + + let err = 0; + + //Send the packet and return the error on failure + if (err = await p7.device.transferOut(1, p7.sendBuffer.slice(0, length)).then((success) => 0, (failure) => failure)) + return "Couldn't send the packet : " + err; + }; + + /* ---------------------Initialization and exiting--------------------- */ + + /* Initiate the connexion with the calculator */ + p7.init = async function () { + try { + p7.device = await navigator.usb.requestDevice({filters:[ + {'vendorId': 0x07cf , 'productId': 0x6101}, /* fx-9750gII */ + {'vendorId': 0x07cf , 'productId': 0x6102}] /* fx-CP400 */ + }); + } catch (err) { + throw new Error("Couldn't find the calculator : " + err.message); } - else { - p7ReceivedPacketInfo.packetSize += 4; /*Data size field*/ + await p7.device.open(); + + if (p7.device.configuration === null) + await p7.device.selectConfiguration(1); /* Only existing one so it should be selected by default, just checking */ + + if (p7.device.configuration.interfaces[0].claimed === false) + await p7.device.claimInterface(0); /* Same as for the configuration */ - //Set Data size (DS) field - p7ReceivedPacketInfo.dataSize = Number('0x' + String.fromCharCode.apply(null, p7ReceiveBuffer.slice(4, 8)), 10); + console.log(p7.device); - //Add the checksum bytes - p7ReceivedPacketInfo.dataSize+=2; + /* Everything went right */ + return 0; + } + + /* End the connexion with the calculator */ + p7.exit = async function () { + + } - p7ReceivedPacketInfo.packetSize += (p7ReceivedPacketInfo.dataSize > 56) ? 56 : p7ReceivedPacketInfo.dataSize; - p7ReceivedPacketInfo.dataSize -= 56; + document.getElementById('connect').addEventListener('click', async function () { + try { + await p7.init(); + } catch (err) { + console.error(err); + return err; + } - //Receive the rest of the data - while(p7ReceivedPacketInfo.dataSize > 0) - { - transferInResult = await device.transferIn(2, 64).then( - (result) => { - return result; - }, (error) => { - err = error; - }); + /* Receive and send some usb control message, as defined in fxReverse documentation + * + * Adapted version (some parameters changed since fxReverse's writing) of usb_control_msg() function's protoype + * from linux-manual : https://manpages.debian.org/jessie-backports/linux-manual-4.8/usb_control_msg.9.en.html + * + * int usb_control_msg(struct usb_device * dev, + * uint8_t requesttype, + * uint8_t request, + * uint16_t value, + * uint16_t index, + * void * data, + * uint16_t size, + * int timeout); + * + * fxReverse2:13: + * + * int init_connection() { + * char *buffer = calloc(0x29, sizeof(char)); + * + * //Receive 0x12 bytes of data + * usb_control_message(usb_handle, + * 0x80, = 0b10000000 + * - D7 = 1 -> Data transfer direction: Device-to-host + * - D6...D5 = 0 -> Type: Standard + * - D4...D0 = 0 -> Recipient: Device + * 0x6, + * 0x0100, + * 0x0, + * buffer, + * 0x12, + * 200); + * + * //Same arguments (except timeout), but value is 0x200 and it receives 0x29 bytes of data + * usb_control_message(usb_handle, 0x80, 0x6, 0x200, 0, buffer, 0x29, 250); + * + * + * usb_control_message(usb_handle, + * 0x41, = 0b01000001 + * - D7 = 0 -> Data transfer direction: Host-to-device + * - D6...D5 = 2 -> Type: Vendor + * - D4...D0 = 1 -> Interface: Interface + * 0x1, + * 0x0, + * 0, + * buffer, + * 0x0, + * 250); + * + * free(buffer); + * return 0; + * } - if (err) - return "Couldn't receive the " + packetSize + "th and following 64 bytes of the packet : " + err; - - p7ReceiveBuffer.set(new Uint8Array(transferInResult.data.buffer), p7ReceivedPacketInfo.packetSize); - p7ReceivedPacketInfo.packetSize += (p7ReceivedPacketInfo.dataSize > 64) ? 64 : p7ReceivedPacketInfo.dataSize; - p7ReceivedPacketInfo.dataSize-=64; + **************************************************************** + + let transferInResult = undefined; + let controlData = undefined; + + try { + transferInResult = await p7.device.controlTransferIn({ + requestType: 'standard', + recipient: 'device', + request: 0x06, // GET_DESCRIPTOR + value: 0x0100, // Descriptor Type and Descriptor Index + index: 0x0000 + }, 0x12); // Length + } catch (err) { + console.log(err); } - //Set Checksum (CS) field - p7ReceivedPacketInfo.checksum = Number('0x' + String.fromCharCode(p7ReceiveBuffer[p7ReceivedPacketInfo.packetSize - 2], p7ReceiveBuffer[p7ReceivedPacketInfo.packetSize - 1]), 16); - - //Compute the checksum - checksumError = (p7ReceivedPacketInfo.checksum !== p7Checksum(p7ReceiveBuffer, p7ReceivedPacketInfo.packetSize)); - } - } while (checksumError) - console.log(p7ReceivedPacketInfo); - return 0; -} + controlData = new Uint8Array(transferInResult.data.buffer); + + console.log('vendor id : 0x' + controlData[9].toString(16).padStart(2, 0) + controlData[8].toString(16).padStart(2,0)); + console.log('product id : 0x' + controlData[11].toString(16).padStart(2, 0) + controlData[10].toString(16).padStart(2,0)); + try { + transferInResult = await p7.device.controlTransferIn({ + requestType: 'standard', + recipient: 'device', + request: 0x06, // GET_DESCRIPTOR + value: 0x0200, // Descriptor Type and Descriptor Index + index: 0x0000 + }, 0x29); // Length + } catch (err) { + console.log(err); + } -/* Send a packet */ -/* Receive the response and resend the packet if the response was an error with subtype resend request */ -/* Return 0 on success and the error on failure */ -async function p7SendPacket(packetLength) { - console.log(p7SendBuffer.slice(0, packetLength)); + controlData = new Uint8Array(transferInResult.data.buffer); - let err = 0; + console.log(controlData); - //Send the packet and return the error on failure - if (err = await device.transferOut(1, p7SendBuffer.slice(0, packetLength)).then((success) => 0, (failure) => failure)) - return "Couldn't send the packet : " + err; + try { + await p7.device.controlTransferOut({ + requestType: 'vendor', + recipient: 'interface', + request: 0x01, + value: 0x0000, + index: 0x0000 + }, controlData); + } catch (err) { + console.log(err); + } */ - //Receive the packet and return the error on failure - if (err = await p7ReceivePacket()) - return "Couldn't receive the response packet : " + err; + p7.make.basicPacket(0x06, 0x00); + console.log(p7.sendBuffer.slice(0, 8)); +/* + try { + await p7.send.packet(6); + } catch (err) { + console.log('err'); + }*/ - //Resend the packet if the response was an error packet with subtype resend request and return the error on failure - for (let i = 0 ; (p7ReceivedPacketInfo.type === p7PacketType.error && p7ReceivedPacketInfo.subtype === errorSubtype.resendRequest) ; i++) { + let err = 0; + if (err = await p7.send.packet(6)) + console.log('err' + err); - if (err = await device.transferOut(1, p7SendBuffer.slice(0, packetLength)).then((success) => {console.log(success) ; return 0}, (failure) => {return failure})) - return "Couldn't send the packet : " + err; - if (err = await p7ReceivePacket()) - return "Couldn't receive the response packet : " + err; - if (i > 2) - return "Too much resend request, the calculator might be broken."; - } + /* Error: Stay in an endless loop */ + await p7.receive.packet(); + }); - //Everything went right - return 0; -} - - -/* Send a basic (non-Extended) packet */ -/* Return 0 on success and the error on failure */ -async function p7SendBasicPacket(type, subtype) { - console.log("%cSending basic packet...", "color: green"); - return await p7SendPacket(p7MakeBasicPacket(type, subtype)); -} - -/* Send an extended command packet */ -/* Return 0 on success and the error on failure */ -async function p7SendExtendedCommandPacket(subtype, forceOverwrite, dataType, fileSize, commandArguments) { - console.log("%cSending extended command packet...", "color: green"); - return await p7SendPacket(p7MakeExtendedCommandPacket(subtype, forceOverwrite, dataType, fileSize, commandArguments)); -} - -/* Send an extended data packet */ -/* Return 0 on success and the error on failure */ -async function p7SendDataPacket(subtype, totalNumber, currentNumber, data) { - console.log("%cSending data packet...", "color: green"); - return p7SendPacket(p7MakeDataPacket(subtype, totalNumber, currentNumber, data)); -} - -/* Split the data into 256 bytes-long buffer and send all of them */ -/* Return 0 on success and the error on failure */ -async function p7SendData(subtype, data) { - let err = 0; - let lastPacketSize = (data.byteLength & 255); //Equivalent to data.byteLength % 256, but way faster ! - let totalPacketNumber = (data.byteLength >> 8); //Equivalent to data.byteLength / 256, but way way waaaaaay faster, and return an integer quotient, not a decimal one !! - console.log(lastPacketSize + ' ' + totalPacketNumber); - totalPacketNumber += (lastPacketSize) ? 1 : 0; - let currentPacketNumber = 1; - let currentPacketFirstByteIndex = 0; - - //Send all the 256 bytes-long data packets - for (; currentPacketNumber < totalPacketNumber ; currentPacketNumber++) - if (err = await p7SendDataPacket(subtype, totalPacketNumber, currentPacketNumber, data.slice(currentPacketFirstByteIndex, (currentPacketFirstByteIndex += 256)))) - return 'Couldn\'t send the ' + currentPacketNumber + ' data packet : ' + err; - - //Send the last data packet (if it exists) - if (lastPacketSize) - if (err = await p7SendDataPacket(subtype, totalPacketNumber, currentPacketNumber, data.slice(currentPacketFirstByteIndex, currentPacketFirstByteIndex + lastPacketSize))) - return 'Couldn\'t send the last data packet (' + lastPacketSize + ' bytes) : ' + err; - - - console.log(p7SendBuffer.slice(0, 64)); - console.log(p7ReceivedPacketInfo); - //Everything went allright - return 0; -} - -/* Set all device information feilds, and trim string's overage ('ÿ') */ -function p7DecodeExtendedAckPacket() { - p7DeviceInformation['Hardware identifier'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(8, 16)); - p7DeviceInformation['Processor identifier'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(16, 32)); - p7DeviceInformation['Preprogrammed ROM capacity'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(32, 40)); - p7DeviceInformation['Flash ROM capacity'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(40, 48)); - p7DeviceInformation['RAM capacity'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(48, 56)); - p7DeviceInformation['Prepogrammed ROM version'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(56, 72)).replaceAll('ÿ', '');; - p7DeviceInformation['Boot code version'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(72, 88)).replaceAll('ÿ', ''); - p7DeviceInformation['Boot code offset'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(88, 96)).replaceAll('ÿ', ''); - p7DeviceInformation['Boot code size'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(96, 104)).replaceAll('ÿ', ''); - p7DeviceInformation['Os code version'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(104, 120)).replaceAll('ÿ', ''); - p7DeviceInformation['Os code offset'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(120, 128)); - p7DeviceInformation['Os code size'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(128, 136)); - p7DeviceInformation['Protocol version'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(136, 140)); - p7DeviceInformation['Product ID'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(140, 156)).replaceAll('ÿ', '');; - p7DeviceInformation['Name set by user in system'] = String.fromCharCode.apply(null, p7ReceiveBuffer.slice(156, 172)).replaceAll('ÿ', ''); - - return 0; -} - -/* Decode a terminate packet and close the connection */ -async function p7DecodeTerminatePacket() { - let err = 0; - switch (p7ReceivedPacketInfo.subtype) { - case terminateSubtype.default: - alert('The calculator asked to close the connection.'); - case terminateSubtype.userRequest: - alert('The calculator said you closed the connection!'); - case terminateSubtype.timeouts: - alert('The calculator closed the connection because it was inactive for more than 6 minutes.'); - case terminateSubtype.overwriteRequest: - alert('The calculator didn\'t accept the overwrite request and closed the connection.'); - default: - alert('The calculator asked to terminate communication for an unknown reason : 0x' + p7ReceivedPacketInfo.subtype.toString(16) + ' (the hex code of the reason).'); - } - if (err = await p7SendBasicPacket(p7PacketType.ack, ackSubtype.default)) - return 'Couldn\'t send acknowledgement packet : ' + err; - return (err = await p7Exit()) ? 'Couldn\'t exit properly : ' + err : 0; -} - -/* Return the value (as an number) of Filesize (FS) subfield of Data (D) field of an extended command packet */ -function p7CommandPacketGetFilesize() { - return Number('0x' + String.fromCharCode.apply(null, p7ReceiveBuffer.slice(12, 20)), 16); -} - -/* Return the trimed receive buffer only containing Data (DD) subfield of Data (D) field of an extended command packet */ -function p7CommandPacketGetField(field) { - let fieldStart = 32; /* D1 field first byte's position */ - for (let i = 0 ; i < 2 * field ; i += 2) - fieldStart += Number('0x' + String.fromCharCode(p7ReceiveBuffer[20 + i], p7ReceiveBuffer[21 + i]), 16); - - let fieldEnd = fieldStart + Number('0x' + String.fromCharCode(p7ReceiveBuffer[20 + 2 * field], p7ReceiveBuffer[21 + 2 * field]), 16); - return p7ReceiveBuffer.slice(fieldStart, fieldEnd); -} - -/* Connect to the calculator */ -/* Return 0 on success and the error on failure */ -async function p7Initialization() { - let err = 0; - err = await navigator.usb.requestDevice({filters:[{'vendorId': 0x07cf , 'productId': 0x6101}, {'vendorId': 0x07cf , 'productId': 0x6102}]}).then( - (selectedDevice) => { - device = selectedDevice; - return 0; - }, (error) => error - ); - - if (err) - return "Couldn't get the selected device (maybe the user didn't select one) : " + err; - - if (err = await device.open().then(() => 0, (error) => error)) - return "Couldn't connect to the calculator : " + err; - - //It's the only existing configuration (should be selected by default), just checkin' - if (err = await device.selectConfiguration(1).then(() => 0, (error) => error)) - return "Couldn't select the configuration (configurationValue : 1) : " + err; - - if (err = await device.claimInterface(0 /* It's the only existing one */).then(() => 0, (error) => error)) - return "Couldn't claim the interface (interfaceNumber : 0) : " + err; - - console.log(device); - - console.log('%cInitialisation packet', "font-size: 14px; color: white"); - - if (err = await p7SendBasicPacket(p7PacketType['check'], checkSubtype['initialization'])) - return "Failed to send init packet : " + err; - - - if (!(p7ReceivedPacketInfo.type === p7PacketType.ack && p7ReceivedPacketInfo.subtype === ackSubtype.default)) - return "Not the expected response packet."; /* TODO: handle this error */ - - - console.log('%cRequesting device\'s informations', "font-size: 14px; color: white"); - - - if (err = await p7SendBasicPacket(p7PacketType['command'], sysCommandSubtype['getDeviceInfo'])) - return "Failed to request device info : " + err; - - if (!(p7ReceivedPacketInfo.type === p7PacketType.ack && p7ReceivedPacketInfo.subtype === ackSubtype.extendedAck)) - return "Not the expected response packet."; /* TODO: add a handle to this error */ - - p7DecodeExtendedAckPacket(); - - return 0; -} - -/* Disconnect from the calculator */ -/* Return 0 on success and the error on failure */ -async function p7Exit() { - let err = 0; - return (err = await device.close().then((success) => {return 0}, (failure) => {return failure})) ? "There was an error disconnecting from the calculator : " + err : 0; -} - -/* Return an object describing a file (or a directory) */ -function createFileObject(device, path, name, size) { - //Directories never have size, only their own path - return (size) ? {type: 'file', path: path, name: name, size: size} : {type: 'directory', path: path}; -} \ No newline at end of file +})(); \ No newline at end of file