Compare commits
5 Commits
759cd278a1
...
52a8446ffb
Author | SHA1 | Date |
---|---|---|
Thomas Touhey | 52a8446ffb | |
Thomas Touhey | 59a6046f50 | |
Thomas Touhey | b28d924b74 | |
Thomas Touhey | 41366d5aef | |
Thomas Touhey | a9b568172e |
|
@ -402,14 +402,15 @@ static int parse_medium_params(
|
|||
char const *raw_parity =
|
||||
get_casrc_setting_property(dstg, ostg, "parity");
|
||||
|
||||
medium->data.com.serial_speed = 9600;
|
||||
medium->data.com.serial_flags = CAHUTE_SERIAL_STOP_TWO;
|
||||
medium->data.com.serial_speed = 0;
|
||||
medium->data.com.serial_flags = 0;
|
||||
|
||||
if (raw_speed) {
|
||||
unsigned long speed = atol(raw_speed);
|
||||
|
||||
if (speed != 1200 && speed != 2400 && speed != 4800
|
||||
&& speed != 9600) {
|
||||
&& speed != 9600 && speed != 19200 && speed != 38400
|
||||
&& speed != 57600 && speed != 115200) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Invalid property baud=%s for %s.\n",
|
||||
|
@ -424,9 +425,9 @@ static int parse_medium_params(
|
|||
|
||||
if (!raw_parity)
|
||||
medium->data.com.serial_flags |= CAHUTE_SERIAL_PARITY_OFF;
|
||||
else if (!strcmp(raw_parity, "e"))
|
||||
else if (raw_parity[0] == 'e' || raw_parity[0] == 'E')
|
||||
medium->data.com.serial_flags |= CAHUTE_SERIAL_PARITY_EVEN;
|
||||
else if (!strcmp(raw_parity, "o"))
|
||||
else if (raw_parity[0] == 'o' || raw_parity[0] == 'O')
|
||||
medium->data.com.serial_flags |= CAHUTE_SERIAL_PARITY_ODD;
|
||||
else
|
||||
medium->data.com.serial_flags |= CAHUTE_SERIAL_PARITY_OFF;
|
||||
|
@ -445,15 +446,18 @@ static int parse_medium_params(
|
|||
|| get_casrc_setting_property(dstg, ostg, "9700")
|
||||
|| get_casrc_setting_property(dstg, ostg, "9800"))
|
||||
medium->data.com.serial_flags |=
|
||||
CAHUTE_SERIAL_CASIOLINK_VARIANT_CAS40;
|
||||
CAHUTE_SERIAL_PROTOCOL_CASIOLINK
|
||||
| CAHUTE_SERIAL_CASIOLINK_VARIANT_CAS40;
|
||||
else if (get_casrc_setting_property(dstg, ostg, "9750")
|
||||
|| get_casrc_setting_property(dstg, ostg, "9850")
|
||||
|| get_casrc_setting_property(dstg, ostg, "9950"))
|
||||
medium->data.com.serial_flags |=
|
||||
CAHUTE_SERIAL_CASIOLINK_VARIANT_CAS50;
|
||||
else
|
||||
CAHUTE_SERIAL_PROTOCOL_CASIOLINK
|
||||
| CAHUTE_SERIAL_CASIOLINK_VARIANT_CAS50;
|
||||
else if (get_casrc_setting_property(dstg, ostg, "afx")) /* Extended */
|
||||
medium->data.com.serial_flags |=
|
||||
CAHUTE_SERIAL_CASIOLINK_VARIANT_AUTO;
|
||||
CAHUTE_SERIAL_PROTOCOL_CASIOLINK
|
||||
| CAHUTE_SERIAL_CASIOLINK_VARIANT_CAS100;
|
||||
|
||||
medium->data.com.pause =
|
||||
get_casrc_setting_property(dstg, ostg, "pause") != NULL;
|
||||
|
|
89
cli/common.c
89
cli/common.c
|
@ -32,6 +32,17 @@
|
|||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* On some platforms, for directories, ftell() returns an insanely high
|
||||
* number that may be platform-specific, e.g. 9223372036854775807 (2^63 - 1),
|
||||
* which would correspond to the highest positive value of a long if defined
|
||||
* on 64 bits. In order to detect such cases in a reasonably
|
||||
* platform-independent manner, we want to cap the size of any file that
|
||||
* gets stored into memory.
|
||||
*
|
||||
* See ``read_file_contents()`` for more details on the usage of this
|
||||
* constant. */
|
||||
#define REASONABLE_FILE_CONTENT_LIMIT 134217728 /* 128 MiB */
|
||||
|
||||
/**
|
||||
* Get the current logging level as a string.
|
||||
*
|
||||
|
@ -122,6 +133,84 @@ extern void print_content(
|
|||
fprintf(stdout, "<CONVERSION FAILED: 0x%04X>", err);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read file contents into a buffer.
|
||||
*
|
||||
* @param path Path to the file to read.
|
||||
* @param datap Pointer to the data to allocate and populate.
|
||||
* @param sizep Pointer to the data size to populate.
|
||||
* @return 0 if ok, other otherwise.
|
||||
*/
|
||||
extern int
|
||||
read_file_contents(char const *path, cahute_u8 **datap, size_t *sizep) {
|
||||
cahute_u8 *data = NULL;
|
||||
size_t size;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(path, "rb");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Unable to open the file: %s\n", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fseek(fp, 0, SEEK_END)) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Unable to seek to the end of the file: %s\n",
|
||||
strerror(errno)
|
||||
);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
size = ftell(fp);
|
||||
if (size > REASONABLE_FILE_CONTENT_LIMIT) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Unable to open the file: file too big (over 128MiB) or "
|
||||
"unsupported file type (e.g. directory)\n"
|
||||
);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fseek(fp, 0, SEEK_SET)) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Unable to seek to the start of the file: %s\n",
|
||||
strerror(errno)
|
||||
);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!size) {
|
||||
fprintf(stderr, "File cannot be empty!\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data = malloc(size);
|
||||
if (!data) {
|
||||
fprintf(stderr, "malloc() failed.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!fread(data, size, 1, fp)) {
|
||||
fprintf(stderr, "Could not read file data: %s\n", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
*datap = data;
|
||||
*sizep = size;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
if (data)
|
||||
free(data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a line and allocate it.
|
||||
*
|
||||
|
|
|
@ -40,6 +40,9 @@ extern void print_content(
|
|||
int dest_encoding
|
||||
);
|
||||
|
||||
extern int
|
||||
read_file_contents(char const *path, cahute_u8 **datap, size_t *sizep);
|
||||
|
||||
/* Portable getdelim() implementation. */
|
||||
extern ssize_t
|
||||
portable_getdelim(char **sp, size_t *np, int delim, FILE *filep);
|
||||
|
|
|
@ -338,7 +338,7 @@ static inline int check_directory_name(char const *name) {
|
|||
return 1;
|
||||
|
||||
n = strnlen(name, 9);
|
||||
if (n > 8)
|
||||
if (!n || n > 8)
|
||||
return 0;
|
||||
|
||||
for (; n--; name++)
|
||||
|
@ -362,7 +362,7 @@ static inline int check_file_name(char const *name) {
|
|||
return 1;
|
||||
|
||||
n = strnlen(name, 13);
|
||||
if (n > 12)
|
||||
if (!n || n > 12)
|
||||
return 0;
|
||||
|
||||
for (; n--; name++)
|
||||
|
@ -597,6 +597,8 @@ int parse_args(int argc, char **argv, struct args *args) {
|
|||
o_output = strrchr(params[0], '/');
|
||||
if (!o_output)
|
||||
o_output = params[0];
|
||||
else
|
||||
o_output++;
|
||||
}
|
||||
|
||||
args->command = COMMAND_SEND;
|
||||
|
|
|
@ -138,75 +138,6 @@ static struct long_option const long_options[] = {
|
|||
LONG_OPTION_SENTINEL
|
||||
};
|
||||
|
||||
/**
|
||||
* Read file contents into a buffer.
|
||||
*
|
||||
* @param path Path to the file to read.
|
||||
* @param datap Pointer to the data to allocate and populate.
|
||||
* @param sizep Pointer to the data size to populate.
|
||||
* @return 0 if ok, other otherwise.
|
||||
*/
|
||||
static int
|
||||
read_file_contents(char const *path, cahute_u8 **datap, size_t *sizep) {
|
||||
cahute_u8 *data = NULL;
|
||||
size_t size;
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen(path, "rb");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Unable to open the file: %s\n", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (fseek(fp, 0, SEEK_END)) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Unable to seek to the end of the file: %s\n",
|
||||
strerror(errno)
|
||||
);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
size = ftell(fp);
|
||||
if (fseek(fp, 0, SEEK_SET)) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Unable to seek to the start of the file: %s\n",
|
||||
strerror(errno)
|
||||
);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!size) {
|
||||
fprintf(stderr, "File cannot be empty!\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
data = malloc(size);
|
||||
if (!data) {
|
||||
fprintf(stderr, "malloc() failed.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!fread(data, size, 1, fp)) {
|
||||
fprintf(stderr, "Could not read file data: %s\n", strerror(errno));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
*datap = data;
|
||||
*sizep = size;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
if (data)
|
||||
free(data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the command-line arguments.
|
||||
*
|
||||
|
|
|
@ -61,12 +61,13 @@ todo_include_todos = True
|
|||
mermaid_output_format = "raw"
|
||||
mermaid_init_js = """
|
||||
function isDarkMode() {
|
||||
const theme = document.body.getAttribute("data-theme");
|
||||
const color = (
|
||||
getComputedStyle(document.body)
|
||||
.getPropertyValue("--color-foreground-primary")
|
||||
);
|
||||
|
||||
if (color == "#ffffffcc")
|
||||
if (theme == "dark" || color == "#ffffffcc")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
|
|
@ -27,11 +27,18 @@ instructions depends on the system you want to install it on.
|
|||
Cahute and its command-line utilities can be installed using
|
||||
|homebrew| Homebrew_.
|
||||
|
||||
Once Homebrew is installed, you can install the `cahute formula
|
||||
<cahute homebrew formula_>`_::
|
||||
Once Homebrew is installed, **disconnect all calculators from your computer**
|
||||
and install the `cahute formula <cahute homebrew formula_>`_ with the
|
||||
following command::
|
||||
|
||||
brew install cahute
|
||||
|
||||
.. warning::
|
||||
|
||||
The installation requires that no calculator is currently connected
|
||||
to your computer through USB; having one currently in receive mode may
|
||||
result in the installation failing.
|
||||
|
||||
|archlinux| Archlinux, |manjaro| Manjaro Linux
|
||||
----------------------------------------------
|
||||
|
||||
|
|
|
@ -289,6 +289,14 @@ selected input or output format is a serial port.
|
|||
``9750``, ``9850``, ``9950``
|
||||
If set, use the fx-9750G header and payload format.
|
||||
|
||||
``afx``
|
||||
If set, use the AlgebraFX / Graph 100 header and payload format.
|
||||
|
||||
.. warning::
|
||||
|
||||
This is an extended option, that does not exist in the original
|
||||
CaS source.
|
||||
|
||||
``raw``
|
||||
If set, use the raw header and payload format.
|
||||
|
||||
|
@ -302,14 +310,14 @@ selected input or output format is a serial port.
|
|||
If none of the above are matched, the parity is set to even.
|
||||
|
||||
``baud``
|
||||
Baud rate to set to the serial connection, as exact string matches:
|
||||
Baud rate to set to the serial connection, from 1200 to 115200 bauds.
|
||||
|
||||
* ``1200``: 1200 bauds.
|
||||
* ``2400``: 2400 bauds.
|
||||
* ``4800``: 4800 bauds.
|
||||
* ``9600``: 9600 bauds.
|
||||
.. warning::
|
||||
|
||||
By default, the baud rate is set to 9600 bauds.
|
||||
Baud rates from 19200 and above are not available in the original
|
||||
CaS source.
|
||||
|
||||
By default, the baud rate depends on the selected model.
|
||||
|
||||
``dtr``
|
||||
If set, enable DTR on the serial connection.
|
||||
|
|
|
@ -8,10 +8,11 @@ Both sides are defined in advance, and do not exchange roles during transfer.
|
|||
In no cases can the receiver request a resource from the sender, as it
|
||||
must only respond to requests and receive what the sender chooses to send.
|
||||
|
||||
Initiate the connection
|
||||
-----------------------
|
||||
Initiate the connection using the CAS40 or CAS50 variant
|
||||
--------------------------------------------------------
|
||||
|
||||
In order to initiate the connection, the communication schema is the following:
|
||||
In order to initiate the connection with the CAS40 and CAS50 variants,
|
||||
the communication schema is the following:
|
||||
|
||||
.. mermaid::
|
||||
|
||||
|
@ -22,6 +23,44 @@ In order to initiate the connection, the communication schema is the following:
|
|||
sender->>receiver: Send a 0x16 (START)
|
||||
receiver->>sender: Send a 0x13 (ESTABLISHED)
|
||||
|
||||
See :ref:`casiolink-packet-format` for more information.
|
||||
|
||||
Initiate the connection using the CAS100 variant
|
||||
------------------------------------------------
|
||||
|
||||
The CAS100 initiation flow is more complete than the CAS40 and CAS50 variants:
|
||||
|
||||
.. mermaid::
|
||||
|
||||
sequenceDiagram
|
||||
Participant sender as Sender
|
||||
Participant receiver as Receiver
|
||||
|
||||
sender->>receiver: Send a 0x16 (START)
|
||||
receiver->>sender: Send a 0x13 (ESTABLISHED)
|
||||
|
||||
sender->>receiver: Send an MDL1 header (0x3A)
|
||||
receiver->>sender: Answer with an MDL1 header (0x3A)
|
||||
sender->>receiver: Acknowledge (0x06)
|
||||
receiver->>sender: Acknowledge (0x06)
|
||||
|
||||
.. note::
|
||||
|
||||
On cross-variant CASIOLINK reception, since the MDL1 header is received
|
||||
in the place any other data would be received in the CAS40 and CAS50
|
||||
variants, the MDL1 header and acknowledgement reactions must be
|
||||
managed in the data reception utilities rather than in the
|
||||
communication initialization.
|
||||
|
||||
However, when the CAS100 variant is selected explicitely by the user,
|
||||
the MDL1 header can and should be managed in the communication
|
||||
initialization directly, so that device information can be exploited.
|
||||
|
||||
See the following for more information:
|
||||
|
||||
* :ref:`casiolink-packet-format`;
|
||||
* :ref:`casiolink-cas100-mdl1`.
|
||||
|
||||
Send or receive data using the CAS40 or CAS50 variant
|
||||
-----------------------------------------------------
|
||||
|
||||
|
|
|
@ -119,6 +119,8 @@ The format of such headers is the following:
|
|||
- Checksum for the packet.
|
||||
-
|
||||
|
||||
.. _casiolink-cas100-adn1:
|
||||
|
||||
``ADN1`` headers
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -153,6 +155,8 @@ These packets seem to be used to send data.
|
|||
-
|
||||
- Integer (little endian), e.g. ``0x200`` (512).
|
||||
|
||||
.. _casiolink-cas100-adn2:
|
||||
|
||||
``ADN2`` headers
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -182,6 +186,34 @@ Unknown purpose.
|
|||
-
|
||||
- Integer (little endian), e.g. ``0x100`` (256).
|
||||
|
||||
.. _casiolink-cas100-bku1:
|
||||
|
||||
``BKU1`` headers
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Unknown purpose.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Offset
|
||||
- Size
|
||||
- Field name
|
||||
- Description
|
||||
- Values
|
||||
* - 0 (0x00)
|
||||
- 4 B
|
||||
- Data Type (*DT*)
|
||||
-
|
||||
- ``RAMS``, ``RAMI``, ``RAM1``
|
||||
* - 4 (0x04)
|
||||
- 4 B
|
||||
- ?
|
||||
-
|
||||
- Big endian 32-bit integer, e.g. ``0xE000``.
|
||||
|
||||
.. _casiolink-cas100-end1:
|
||||
|
||||
``END1`` headers
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -189,6 +221,8 @@ These packets are sent at the end of the communication.
|
|||
|
||||
They do not use additional data.
|
||||
|
||||
.. _casiolink-cas100-fcl1:
|
||||
|
||||
``FCL1`` headers
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -208,6 +242,8 @@ Unknown purpose.
|
|||
-
|
||||
- ``S000``
|
||||
|
||||
.. _casiolink-cas100-fmv1:
|
||||
|
||||
``FMV1`` headers
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -232,10 +268,62 @@ Unknown purpose.
|
|||
-
|
||||
- ``FR00`` (sic.)
|
||||
|
||||
.. _casiolink-cas100-mcs1:
|
||||
|
||||
``MCS1`` headers
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
These packets contain main memory data.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Offset
|
||||
- Size
|
||||
- Field name
|
||||
- Description
|
||||
- Values
|
||||
* - 0 (0x00)
|
||||
- 3 B
|
||||
- Reserved
|
||||
-
|
||||
- Set to ``\0``.
|
||||
* - 3 (0x03)
|
||||
- 2 B
|
||||
- File size
|
||||
-
|
||||
- Big-endian 16-bit integer (?).
|
||||
* - 5 (0x05)
|
||||
- 1 B
|
||||
- Data type
|
||||
-
|
||||
- 8-bit integer, among the following:
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Data type
|
||||
- Description
|
||||
* - ``0x01``
|
||||
- Program
|
||||
* - 6 (0x06)
|
||||
- 8 B
|
||||
- Data name
|
||||
-
|
||||
- ``0xFF`` optionally terminated string.
|
||||
* - 14 (0x0E)
|
||||
- 8 B
|
||||
- Group name
|
||||
-
|
||||
- ``0xFF`` optionally terminated string.
|
||||
|
||||
.. _casiolink-cas100-mdl1:
|
||||
|
||||
``MDL1`` headers
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
These packets seem to contain calculator model information.
|
||||
These packets contain initialization data for the CAS100 variant of the
|
||||
CASIOLINK protocol, with calculator model information.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
@ -287,6 +375,8 @@ These packets seem to contain calculator model information.
|
|||
- 4-char string, e.g. ``"0x07"`` (``0x30``, ``0x78``, ``0x30``,
|
||||
``0x37``).
|
||||
|
||||
.. _casiolink-cas100-req1:
|
||||
|
||||
``REQ1`` headers
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -306,6 +396,8 @@ These packets seem to be used to request information.
|
|||
-
|
||||
- ``INF1`` (System), ``FR00`` (Segment), ``MSG1`` (Language)
|
||||
|
||||
.. _casiolink-cas100-req2:
|
||||
|
||||
``REQ2`` headers
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -335,3 +427,29 @@ Unknown purpose.
|
|||
- ?
|
||||
-
|
||||
- Integer (little endian), e.g. ``0x20000000`` (512 * 1024 * 1024)
|
||||
|
||||
.. _casiolink-cas100-set1:
|
||||
|
||||
``SET1`` headers
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
Unknown purpose.
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Offset
|
||||
- Size
|
||||
- Field name
|
||||
- Description
|
||||
- Values
|
||||
* - 0 (0x00)
|
||||
- 2 B
|
||||
- ?
|
||||
-
|
||||
- ``\x30\x01``
|
||||
* - 2 (0x02)
|
||||
- 8 B
|
||||
- ?
|
||||
-
|
||||
- ``0xFF`` optionally terminated string, e.g. ``Y=Data``.
|
||||
|
|
680
lib/casiolink.c
680
lib/casiolink.c
|
@ -35,6 +35,15 @@ CAHUTE_LOCAL_DATA(cahute_u8 const *)
|
|||
pz_program_names =
|
||||
(cahute_u8 const *)"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ\xCD\xCE";
|
||||
|
||||
/* The MDL1 command for Graph 100 / AFX used for initialization when
|
||||
* in sender / control mode. The speed and parity are inserted into the
|
||||
* copy of this buffer before the checksum is recomputed and placed into
|
||||
* the last byte. */
|
||||
CAHUTE_LOCAL_DATA(cahute_u8 const *)
|
||||
default_mdl1_payload =
|
||||
(cahute_u8 const *)":MDL1GY351\xFF" "000000N1.03\0\0\x01\0\0\0\x04\0\0\0"
|
||||
"\x01\0\x03\xFF\xFF\xFF\xFF\0";
|
||||
|
||||
/* TIMEOUT_PACKET_TYPE is the timeout before reading the packet type, i.e.
|
||||
* the first byte, while TIMEOUT_PACKET_CONTENTS is the timeout before
|
||||
* reading any of the following bytes. */
|
||||
|
@ -66,30 +75,115 @@ cahute_casiolink_checksum(cahute_u8 const *data, size_t size) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Check if the last received data for a link is an end packet.
|
||||
* Answer a received CAS100 MDL1 header with the appropriate header,
|
||||
* then apply the serial settings provided within the header, then
|
||||
* handle the mutual acknowledgement.
|
||||
*
|
||||
* @param link Link to check.
|
||||
* @return 1 if the last received data is an end data, 0 otherwise.
|
||||
* This flow is described in "Initiate the connection using the CAS100
|
||||
* header", assuming the link is in receive mode, and is currently in the
|
||||
* state where it has received the initial MDL1 header in the link's
|
||||
* data buffer.
|
||||
*
|
||||
* Note that we actually answer with the same MDL1 to make the calculator
|
||||
* believe we are compatible with them.
|
||||
*
|
||||
* @param link Link for which to handle the exchange.
|
||||
* @return Cahute error.
|
||||
*/
|
||||
CAHUTE_INLINE(int) cahute_casiolink_is_end(cahute_link *link) {
|
||||
switch (link->protocol_state.casiolink.variant) {
|
||||
case CAHUTE_CASIOLINK_VARIANT_CAS40:
|
||||
if (!memcmp(&link->data_buffer[1], "\x17\xFF", 2))
|
||||
return 1;
|
||||
break;
|
||||
CAHUTE_LOCAL(int) cahute_casiolink_handle_mdl1(cahute_link *link) {
|
||||
unsigned long new_serial_speed = 0;
|
||||
unsigned long new_serial_flags = link->serial_flags;
|
||||
int mdl_correct = 1;
|
||||
cahute_u8 *buf = link->data_buffer;
|
||||
int err;
|
||||
|
||||
case CAHUTE_CASIOLINK_VARIANT_CAS50:
|
||||
if (!memcmp(&link->data_buffer[1], "END", 4))
|
||||
return 1;
|
||||
break;
|
||||
/* We want to store the provided information. */
|
||||
memcpy(
|
||||
link->protocol_state.casiolink.raw_device_info,
|
||||
&buf[5],
|
||||
CASIOLINK_RAW_DEVICE_INFO_BUFFER_SIZE
|
||||
);
|
||||
link->protocol_state.casiolink.flags |=
|
||||
CASIOLINK_FLAG_DEVICE_INFO_OBTAINED;
|
||||
|
||||
case CAHUTE_CASIOLINK_VARIANT_CAS100:
|
||||
if (!memcmp(&link->data_buffer[1], "END1", 4))
|
||||
return 1;
|
||||
break;
|
||||
/* Send the MDL1 answer now. */
|
||||
err = cahute_write_to_link(link, buf, 40);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* We should actually be receiving an acknowledgement, since we are
|
||||
* sending the same packet the calculator sent. */
|
||||
err = cahute_read_from_link(link, buf, 1, 0, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (buf[0] != PACKET_TYPE_ACK) {
|
||||
cahute_u8 const send_buf[] = {PACKET_TYPE_CORRUPTED};
|
||||
|
||||
err = cahute_write_to_link(link, send_buf, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
/* We want to decode the serial parameters, to ensure that we
|
||||
* can actually decode them. */
|
||||
new_serial_flags = link->serial_flags;
|
||||
new_serial_flags &= ~CAHUTE_SERIAL_PARITY_MASK;
|
||||
|
||||
if (!memcmp(&buf[11], "038400", 6))
|
||||
new_serial_speed = 38400;
|
||||
else {
|
||||
msg(ll_error, "Unsupported new serial speed:");
|
||||
mem(ll_error, &buf[11], 6);
|
||||
mdl_correct = 0;
|
||||
}
|
||||
|
||||
if (buf[17] == 'N')
|
||||
new_serial_flags |= CAHUTE_SERIAL_PARITY_OFF;
|
||||
else if (buf[17] == 'E')
|
||||
new_serial_flags |= CAHUTE_SERIAL_PARITY_EVEN;
|
||||
else if (buf[17] == 'O')
|
||||
new_serial_flags |= CAHUTE_SERIAL_PARITY_ODD;
|
||||
else {
|
||||
msg(ll_error, "Unsupported new serial parity:");
|
||||
mem(ll_error, &buf[17], 1);
|
||||
mdl_correct = 0;
|
||||
}
|
||||
|
||||
if (!mdl_correct) {
|
||||
cahute_u8 const send_buf[] = {PACKET_TYPE_CORRUPTED};
|
||||
|
||||
err = cahute_write_to_link(link, send_buf, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
/* We are ok with the sent MDL1, we can now send an acknowledgement.
|
||||
* The acknowledgement is already in our buffer, we can use that. */
|
||||
err = cahute_write_to_link(link, buf, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Only now that the exchange has taken place, we want to set
|
||||
* the serial params. */
|
||||
err = cahute_set_serial_params_to_link(
|
||||
link,
|
||||
new_serial_flags,
|
||||
new_serial_speed
|
||||
);
|
||||
if (err) {
|
||||
msg(ll_error,
|
||||
"Could not set the serial params; that makes our "
|
||||
"connection irrecoverable!");
|
||||
link->flags |= CAHUTE_LINK_FLAG_IRRECOVERABLE;
|
||||
return err;
|
||||
}
|
||||
|
||||
return CAHUTE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -115,7 +209,9 @@ cahute_casiolink_receive_raw_data(cahute_link *link, unsigned long timeout) {
|
|||
size_t part_sizes[5];
|
||||
int packet_type, err, variant = 0, checksum, checksum_alt;
|
||||
int log_part_data = 1, is_al_end = 0, is_end = 0, is_final = 0;
|
||||
int expected_data_packet_type = PACKET_TYPE_HEADER;
|
||||
|
||||
restart_reception:
|
||||
part_sizes[0] = 0;
|
||||
|
||||
do {
|
||||
|
@ -177,13 +273,18 @@ cahute_casiolink_receive_raw_data(cahute_link *link, unsigned long timeout) {
|
|||
if (link->protocol_state.casiolink.variant
|
||||
!= CAHUTE_CASIOLINK_VARIANT_AUTO) {
|
||||
variant = link->protocol_state.casiolink.variant;
|
||||
|
||||
msg(ll_info, "Received the following header:");
|
||||
mem(ll_info, buf, buf_size);
|
||||
} else {
|
||||
/* We want to try to determine the currently selected variant based
|
||||
* on the header's content. */
|
||||
if (!memcmp(&buf[1], "ADN1", 4) || !memcmp(&buf[1], "ADN2", 4)
|
||||
|| !memcmp(&buf[1], "END1", 4) || !memcmp(&buf[1], "FCL1", 4)
|
||||
|| !memcmp(&buf[1], "FMV1", 4) || !memcmp(&buf[1], "MDL1", 4)
|
||||
|| !memcmp(&buf[1], "REQ1", 4) || !memcmp(&buf[1], "REQ2", 4)) {
|
||||
|| !memcmp(&buf[1], "BKU1", 4) || !memcmp(&buf[1], "END1", 4)
|
||||
|| !memcmp(&buf[1], "FCL1", 4) || !memcmp(&buf[1], "FMV1", 4)
|
||||
|| !memcmp(&buf[1], "MCS1", 4) || !memcmp(&buf[1], "MDL1", 4)
|
||||
|| !memcmp(&buf[1], "REQ1", 4) || !memcmp(&buf[1], "REQ2", 4)
|
||||
|| !memcmp(&buf[1], "SET1", 4)) {
|
||||
/* The type seems to be a CAS100 header type we can use. */
|
||||
variant = CAHUTE_CASIOLINK_VARIANT_CAS100;
|
||||
|
||||
|
@ -474,13 +575,32 @@ cahute_casiolink_receive_raw_data(cahute_link *link, unsigned long timeout) {
|
|||
break;
|
||||
|
||||
case CAHUTE_CASIOLINK_VARIANT_CAS100:
|
||||
if (!memcmp(&buf[1], "END1", 4)) {
|
||||
if (!memcmp(&buf[1], "BKU1", 4)) {
|
||||
/* Backup packet for CAS100. */
|
||||
part_sizes[0] =
|
||||
(buf[9] << 24) | (buf[10] << 16) | (buf[11] << 8) | buf[12];
|
||||
} else if (!memcmp(&buf[1], "END1", 4)) {
|
||||
/* End packet for CAS100. */
|
||||
part_count = 0;
|
||||
is_end = 1;
|
||||
} else if (!memcmp(&buf[1], "MCS1", 4)) {
|
||||
/* Main memory packet for CAS100. */
|
||||
part_sizes[0] = (buf[8] << 8) | buf[9];
|
||||
if (!part_sizes[0])
|
||||
part_count = 0;
|
||||
} else if (!memcmp(&buf[1], "MDL1", 4)) {
|
||||
/* Initialization packet for CAS100. */
|
||||
err = cahute_casiolink_handle_mdl1(link);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* From here, we go back to the beginning. */
|
||||
goto restart_reception;
|
||||
} else if (!memcmp(&buf[1], "SET1", 4)) {
|
||||
/* TODO */
|
||||
part_count = 0;
|
||||
}
|
||||
|
||||
/* TODO */
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -549,143 +669,133 @@ cahute_casiolink_receive_raw_data(cahute_link *link, unsigned long timeout) {
|
|||
* depending on the part count & size using PACKET_TYPE_HEADER.
|
||||
* - For CAS100, the data is provided in multiple packets containing
|
||||
* 1024 bytes of data each, using PACKET_TYPE_DATA. */
|
||||
switch (variant) {
|
||||
case CAHUTE_CASIOLINK_VARIANT_CAS40:
|
||||
case CAHUTE_CASIOLINK_VARIANT_CAS50:
|
||||
buf = &buf[buf_size];
|
||||
buf = &buf[buf_size];
|
||||
|
||||
index = 1;
|
||||
total = part_count - 1 + part_repeat;
|
||||
for (part_i = 0; part_i < total; part_i++, index++) {
|
||||
size_t part_size =
|
||||
part_sizes[part_i >= part_count ? part_count - 1 : part_i];
|
||||
index = 1;
|
||||
total = part_count - 1 + part_repeat;
|
||||
for (part_i = 0; part_i < total; part_i++, index++) {
|
||||
size_t part_size =
|
||||
part_sizes[part_i >= part_count ? part_count - 1 : part_i];
|
||||
|
||||
msg(ll_info,
|
||||
"Reading data part %d/%d (%" CAHUTE_PRIuSIZE "o).",
|
||||
index,
|
||||
total,
|
||||
part_size);
|
||||
msg(ll_info,
|
||||
"Reading data part %d/%d (%" CAHUTE_PRIuSIZE "o).",
|
||||
index,
|
||||
total,
|
||||
part_size);
|
||||
|
||||
err = cahute_read_from_link(
|
||||
link,
|
||||
tmp_buf,
|
||||
1,
|
||||
TIMEOUT_PACKET_CONTENTS,
|
||||
TIMEOUT_PACKET_CONTENTS
|
||||
);
|
||||
if (err == CAHUTE_ERROR_TIMEOUT_START)
|
||||
return CAHUTE_ERROR_TIMEOUT;
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (tmp_buf[0] != PACKET_TYPE_HEADER) {
|
||||
msg(ll_error,
|
||||
"Expected 0x3A (':') packet type, got 0x%02X.",
|
||||
buf[0]);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
if (part_size) {
|
||||
size_t part_size_left = part_size;
|
||||
cahute_u8 *p = buf;
|
||||
|
||||
/* Use a loop to be able to follow the transfer progress
|
||||
* using logs. */
|
||||
while (part_size_left) {
|
||||
size_t to_read =
|
||||
part_size_left > 512 ? 512 : part_size_left;
|
||||
|
||||
err = cahute_read_from_link(
|
||||
link,
|
||||
p,
|
||||
to_read,
|
||||
TIMEOUT_PACKET_CONTENTS,
|
||||
TIMEOUT_PACKET_CONTENTS
|
||||
);
|
||||
if (err == CAHUTE_ERROR_TIMEOUT_START)
|
||||
return CAHUTE_ERROR_TIMEOUT;
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
part_size_left -= to_read;
|
||||
p += to_read;
|
||||
}
|
||||
}
|
||||
|
||||
if (part_size) {
|
||||
/* For color screenshots, sometimes the first byte is not
|
||||
* taken into account in the checksum calculation, as it's
|
||||
* metadata for the sheet and not the "actual data" of the
|
||||
* sheet. But sometimes it also gets the checksum right!
|
||||
* In any case, we want to compute and check both checksums
|
||||
* to see if at least one matches. */
|
||||
checksum = cahute_casiolink_checksum(buf, part_size);
|
||||
checksum_alt =
|
||||
cahute_casiolink_checksum(buf + 1, part_size - 1);
|
||||
} else {
|
||||
checksum = 0;
|
||||
checksum_alt = 0;
|
||||
}
|
||||
|
||||
/* Read and check the checksum. */
|
||||
err = cahute_read_from_link(
|
||||
link,
|
||||
tmp_buf + 1,
|
||||
1,
|
||||
TIMEOUT_PACKET_CONTENTS,
|
||||
TIMEOUT_PACKET_CONTENTS
|
||||
);
|
||||
if (err == CAHUTE_ERROR_TIMEOUT_START)
|
||||
return CAHUTE_ERROR_TIMEOUT;
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (checksum != tmp_buf[1] && checksum_alt != tmp_buf[1]) {
|
||||
cahute_u8 const send_buf[] = {PACKET_TYPE_INVALID_DATA};
|
||||
|
||||
msg(ll_warn,
|
||||
"Invalid checksum (expected: 0x%02X, computed: "
|
||||
"0x%02X).",
|
||||
tmp_buf[1],
|
||||
checksum);
|
||||
mem(ll_info, buf, part_size);
|
||||
|
||||
msg(ll_error, "Transfer will abort.");
|
||||
link->flags |= CAHUTE_LINK_FLAG_IRRECOVERABLE;
|
||||
|
||||
err = cahute_write_to_link(link, send_buf, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return CAHUTE_ERROR_CORRUPT;
|
||||
}
|
||||
|
||||
/* Acknowledge the data. */
|
||||
{
|
||||
cahute_u8 const send_buf[] = {PACKET_TYPE_ACK};
|
||||
|
||||
err = cahute_write_to_link(link, send_buf, 1);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
msg(ll_info,
|
||||
"Data part %d/%d received and acknowledged.",
|
||||
index,
|
||||
total);
|
||||
if (log_part_data)
|
||||
mem(ll_info, buf, part_size);
|
||||
|
||||
buf += part_size;
|
||||
buf_size += part_size;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* TODO */
|
||||
CAHUTE_RETURN_IMPL(
|
||||
"CASIOLINK data exchange was not implemented for CAS50."
|
||||
err = cahute_read_from_link(
|
||||
link,
|
||||
tmp_buf,
|
||||
1,
|
||||
TIMEOUT_PACKET_CONTENTS,
|
||||
TIMEOUT_PACKET_CONTENTS
|
||||
);
|
||||
if (err == CAHUTE_ERROR_TIMEOUT_START)
|
||||
return CAHUTE_ERROR_TIMEOUT;
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (tmp_buf[0] != expected_data_packet_type) {
|
||||
msg(ll_error,
|
||||
"Expected 0x3A (':') packet type, got 0x%02X.",
|
||||
buf[0]);
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
if (part_size) {
|
||||
size_t part_size_left = part_size;
|
||||
cahute_u8 *p = buf;
|
||||
|
||||
/* Use a loop to be able to follow the transfer progress
|
||||
* using logs. */
|
||||
while (part_size_left) {
|
||||
size_t to_read =
|
||||
part_size_left > 512 ? 512 : part_size_left;
|
||||
|
||||
err = cahute_read_from_link(
|
||||
link,
|
||||
p,
|
||||
to_read,
|
||||
TIMEOUT_PACKET_CONTENTS,
|
||||
TIMEOUT_PACKET_CONTENTS
|
||||
);
|
||||
if (err == CAHUTE_ERROR_TIMEOUT_START)
|
||||
return CAHUTE_ERROR_TIMEOUT;
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
part_size_left -= to_read;
|
||||
p += to_read;
|
||||
}
|
||||
}
|
||||
|
||||
if (part_size) {
|
||||
/* For color screenshots, sometimes the first byte is not
|
||||
* taken into account in the checksum calculation, as it's
|
||||
* metadata for the sheet and not the "actual data" of the
|
||||
* sheet. But sometimes it also gets the checksum right!
|
||||
* In any case, we want to compute and check both checksums
|
||||
* to see if at least one matches. */
|
||||
checksum = cahute_casiolink_checksum(buf, part_size);
|
||||
checksum_alt =
|
||||
cahute_casiolink_checksum(buf + 1, part_size - 1);
|
||||
} else {
|
||||
checksum = 0;
|
||||
checksum_alt = 0;
|
||||
}
|
||||
|
||||
/* Read and check the checksum. */
|
||||
err = cahute_read_from_link(
|
||||
link,
|
||||
tmp_buf + 1,
|
||||
1,
|
||||
TIMEOUT_PACKET_CONTENTS,
|
||||
TIMEOUT_PACKET_CONTENTS
|
||||
);
|
||||
if (err == CAHUTE_ERROR_TIMEOUT_START)
|
||||
return CAHUTE_ERROR_TIMEOUT;
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (checksum != tmp_buf[1] && checksum_alt != tmp_buf[1]) {
|
||||
cahute_u8 const send_buf[] = {PACKET_TYPE_INVALID_DATA};
|
||||
|
||||
msg(ll_warn,
|
||||
"Invalid checksum (expected: 0x%02X, computed: "
|
||||
"0x%02X).",
|
||||
tmp_buf[1],
|
||||
checksum);
|
||||
mem(ll_info, buf, part_size);
|
||||
|
||||
msg(ll_error, "Transfer will abort.");
|
||||
link->flags |= CAHUTE_LINK_FLAG_IRRECOVERABLE;
|
||||
|
||||
err = cahute_write_to_link(link, send_buf, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return CAHUTE_ERROR_CORRUPT;
|
||||
}
|
||||
|
||||
/* Acknowledge the data. */
|
||||
{
|
||||
cahute_u8 const send_buf[] = {PACKET_TYPE_ACK};
|
||||
|
||||
err = cahute_write_to_link(link, send_buf, 1);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
msg(ll_info,
|
||||
"Data part %d/%d received and acknowledged.",
|
||||
index,
|
||||
total);
|
||||
if (log_part_data
|
||||
&& part_size <= 4096) /* Let's not flood the terminal. */
|
||||
mem(ll_info, buf, part_size);
|
||||
|
||||
buf += part_size;
|
||||
buf_size += part_size;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -718,8 +828,8 @@ cahute_casiolink_receive_raw_data(cahute_link *link, unsigned long timeout) {
|
|||
* @return Cahute error.
|
||||
*/
|
||||
CAHUTE_EXTERN(int) cahute_casiolink_initiate(cahute_link *link) {
|
||||
cahute_u8 buf[1];
|
||||
int err;
|
||||
cahute_u8 *buf = link->data_buffer;
|
||||
int checksum, err;
|
||||
|
||||
if (link->flags & CAHUTE_LINK_FLAG_RECEIVER) {
|
||||
/* Expect an initiation flow. */
|
||||
|
@ -740,6 +850,51 @@ CAHUTE_EXTERN(int) cahute_casiolink_initiate(cahute_link *link) {
|
|||
err = cahute_write_to_link(link, buf, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* In the CAS100 variant, we actually expect an additional flow here,
|
||||
* being the MDL1 flow. */
|
||||
if (link->protocol_state.casiolink.variant
|
||||
== CAHUTE_CASIOLINK_VARIANT_CAS100) {
|
||||
err = cahute_read_from_link(
|
||||
link,
|
||||
buf,
|
||||
40,
|
||||
0,
|
||||
TIMEOUT_PACKET_CONTENTS
|
||||
);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
msg(ll_info, "Received data for MDL1 is the following:");
|
||||
mem(ll_info, buf, 40);
|
||||
|
||||
if (memcmp(buf, "\x3AMDL1", 5))
|
||||
err = CAHUTE_ERROR_UNKNOWN;
|
||||
|
||||
if (!err) {
|
||||
checksum = cahute_casiolink_checksum(buf + 1, 38);
|
||||
if (buf[39] != checksum)
|
||||
err = CAHUTE_ERROR_CORRUPT;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
cahute_u8 send_buf[1] = {PACKET_TYPE_CORRUPTED};
|
||||
|
||||
msg(ll_error,
|
||||
"Unknown or invalid packet when MDL1 was expected:");
|
||||
mem(ll_error, buf, 40);
|
||||
|
||||
err = cahute_write_to_link(link, send_buf, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
err = cahute_casiolink_handle_mdl1(link);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
/* Make the initiation flow. */
|
||||
buf[0] = PACKET_TYPE_START;
|
||||
|
@ -759,6 +914,102 @@ CAHUTE_EXTERN(int) cahute_casiolink_initiate(cahute_link *link) {
|
|||
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
/* In the CAS100 variant, we actually need to initiate an additional
|
||||
* flow here, being the MDL1 flow. */
|
||||
if (link->protocol_state.casiolink.variant
|
||||
== CAHUTE_CASIOLINK_VARIANT_CAS100) {
|
||||
char serial_params[7];
|
||||
|
||||
memcpy(buf, default_mdl1_payload, 40);
|
||||
|
||||
/* NOTE: sprintf() adds a terminating zero, but we don't care,
|
||||
* since we override buf[17] right after. */
|
||||
sprintf(serial_params, "%06lu", link->serial_speed);
|
||||
switch (link->serial_flags & CAHUTE_SERIAL_PARITY_MASK) {
|
||||
case CAHUTE_SERIAL_PARITY_EVEN:
|
||||
serial_params[6] = 'E';
|
||||
break;
|
||||
|
||||
case CAHUTE_SERIAL_PARITY_ODD:
|
||||
serial_params[6] = 'O';
|
||||
break;
|
||||
|
||||
default:
|
||||
serial_params[6] = 'N';
|
||||
}
|
||||
|
||||
memcpy(&buf[11], serial_params, 7);
|
||||
|
||||
buf[39] = cahute_casiolink_checksum(&buf[1], 38);
|
||||
|
||||
err = cahute_write_to_link(link, buf, 40);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = cahute_read_from_link(
|
||||
link,
|
||||
buf,
|
||||
40,
|
||||
0,
|
||||
TIMEOUT_PACKET_CONTENTS
|
||||
);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
msg(ll_info, "Received data for MDL1 is the following:");
|
||||
mem(ll_info, buf, 40);
|
||||
|
||||
err = 0;
|
||||
if (memcmp(buf, "\x3AMDL1", 5)
|
||||
|| memcmp(&buf[11], serial_params, 7))
|
||||
err = CAHUTE_ERROR_UNKNOWN;
|
||||
|
||||
if (!err) {
|
||||
checksum = cahute_casiolink_checksum(buf + 1, 38);
|
||||
if (buf[39] != checksum)
|
||||
err = CAHUTE_ERROR_CORRUPT;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
cahute_u8 send_buf[1] = {PACKET_TYPE_CORRUPTED};
|
||||
|
||||
msg(ll_error,
|
||||
"Unknown or invalid packet when MDL1 was expected:");
|
||||
mem(ll_error, buf, 40);
|
||||
|
||||
err = cahute_write_to_link(link, send_buf, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* We want to store the received MDL1 packet here.
|
||||
* TODO: We may want to check the speed here. */
|
||||
memcpy(
|
||||
link->protocol_state.casiolink.raw_device_info,
|
||||
&buf[5],
|
||||
CASIOLINK_RAW_DEVICE_INFO_BUFFER_SIZE
|
||||
);
|
||||
link->protocol_state.casiolink.flags |=
|
||||
CASIOLINK_FLAG_DEVICE_INFO_OBTAINED;
|
||||
|
||||
/* Send the acknowledgement. */
|
||||
buf[0] = PACKET_TYPE_ACK;
|
||||
|
||||
err = cahute_write_to_link(link, buf, 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Receive the initial acknowledgement. */
|
||||
err = cahute_read_from_link(link, buf, 1, 0, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (buf[0] != PACKET_TYPE_ACK)
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
return CAHUTE_OK;
|
||||
|
@ -793,7 +1044,7 @@ CAHUTE_EXTERN(int) cahute_casiolink_terminate(cahute_link *link) {
|
|||
buf[1] = 'E';
|
||||
buf[2] = 'N';
|
||||
buf[3] = 'D';
|
||||
buf[4] = '\0';
|
||||
buf[4] = '\xFF';
|
||||
|
||||
buf_size = 50;
|
||||
break;
|
||||
|
@ -806,17 +1057,7 @@ CAHUTE_EXTERN(int) cahute_casiolink_terminate(cahute_link *link) {
|
|||
break;
|
||||
}
|
||||
|
||||
/* Compute the checksum as well! */
|
||||
{
|
||||
cahute_u8 const *p = buf + 1;
|
||||
size_t left = buf_size - 2;
|
||||
int checksum = 0;
|
||||
|
||||
for (p = buf + 1, left = buf_size - 2; left; p++, left--)
|
||||
checksum += *p;
|
||||
|
||||
buf[buf_size - 1] = checksum & 255;
|
||||
}
|
||||
buf[buf_size - 1] = cahute_casiolink_checksum(&buf[1], buf_size - 2);
|
||||
|
||||
msg(ll_info, "Sending the following end packet:");
|
||||
mem(ll_info, buf, buf_size);
|
||||
|
@ -932,6 +1173,37 @@ cahute_casiolink_receive_data(
|
|||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case CAHUTE_CASIOLINK_VARIANT_CAS100:
|
||||
if (!memcmp(&buf[1], "MCS1", 4)) {
|
||||
cahute_u8 const *p;
|
||||
size_t name_size, group_size;
|
||||
|
||||
for (p = &buf[11]; p < &buf[19] && *p != 0xFF; p++)
|
||||
;
|
||||
name_size = (size_t)(p - &buf[11]);
|
||||
|
||||
for (p = &buf[19]; p < &buf[27] && *p != 0xFF; p++)
|
||||
;
|
||||
group_size = (size_t)(p - &buf[19]);
|
||||
|
||||
err = cahute_mcs_decode_data(
|
||||
datap,
|
||||
&buf[19],
|
||||
group_size,
|
||||
NULL, /* MCS1 packet does not present a directory. */
|
||||
0,
|
||||
&buf[11],
|
||||
name_size,
|
||||
&buf[40],
|
||||
link->data_buffer_size - 40,
|
||||
buf[10]
|
||||
);
|
||||
if (err != CAHUTE_ERROR_IMPL)
|
||||
return err;
|
||||
}
|
||||
/* TODO */
|
||||
break;
|
||||
}
|
||||
|
||||
/* If the data was final, we still need to break here. */
|
||||
|
@ -1036,3 +1308,71 @@ cahute_casiolink_receive_screen(
|
|||
|
||||
return CAHUTE_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce generic device information using the optionally stored
|
||||
* device information.
|
||||
*
|
||||
* @param link Link from which to get the cached EACK response.
|
||||
* @param infop Pointer to set to the allocated device information structure.
|
||||
* @return Cahute error, or 0 if no error has occurred.
|
||||
*/
|
||||
CAHUTE_EXTERN(int)
|
||||
cahute_casiolink_make_device_info(
|
||||
cahute_link *link,
|
||||
cahute_device_info **infop
|
||||
) {
|
||||
cahute_device_info *info = NULL;
|
||||
char *buf;
|
||||
cahute_u8 const *raw_info;
|
||||
|
||||
if (~link->protocol_state.casiolink.flags
|
||||
& CASIOLINK_FLAG_DEVICE_INFO_OBTAINED) {
|
||||
/* We don't have a 'generic device information'. */
|
||||
CAHUTE_RETURN_IMPL("No generic device with CASIOLINK.");
|
||||
}
|
||||
|
||||
info = malloc(sizeof(cahute_device_info) + 20);
|
||||
if (!info)
|
||||
return CAHUTE_ERROR_ALLOC;
|
||||
|
||||
buf = (void *)(&info[1]);
|
||||
raw_info = link->protocol_state.seven.raw_device_info;
|
||||
|
||||
info->cahute_device_info_flags = CAHUTE_DEVICE_INFO_FLAG_OS;
|
||||
info->cahute_device_info_rom_capacity = 0;
|
||||
info->cahute_device_info_rom_version = "";
|
||||
|
||||
info->cahute_device_info_flash_rom_capacity =
|
||||
(raw_info[20] << 24) | (raw_info[19] << 16) | (raw_info[18] << 8)
|
||||
| raw_info[17];
|
||||
info->cahute_device_info_ram_capacity =
|
||||
(raw_info[24] << 24) | (raw_info[23] << 16) | (raw_info[22] << 8)
|
||||
| raw_info[21];
|
||||
|
||||
info->cahute_device_info_bootcode_version = "";
|
||||
info->cahute_device_info_bootcode_offset = 0;
|
||||
info->cahute_device_info_bootcode_size = 0;
|
||||
|
||||
memcpy(buf, &raw_info[13], 4);
|
||||
buf[4] = 0;
|
||||
info->cahute_device_info_os_version = buf;
|
||||
buf += 5;
|
||||
|
||||
info->cahute_device_info_os_offset = 0;
|
||||
info->cahute_device_info_os_size = 0;
|
||||
|
||||
info->cahute_device_info_product_id = "";
|
||||
info->cahute_device_info_username = "";
|
||||
info->cahute_device_info_organisation = "";
|
||||
|
||||
memcpy(buf, raw_info, 6);
|
||||
buf[6] = 0;
|
||||
info->cahute_device_info_hwid = buf;
|
||||
buf += 7;
|
||||
|
||||
info->cahute_device_info_cpuid = "";
|
||||
|
||||
*infop = info;
|
||||
return CAHUTE_OK;
|
||||
}
|
||||
|
|
|
@ -295,6 +295,13 @@ union cahute_link_medium_state {
|
|||
/* Absolute minimum buffer size for CASIOLINK. */
|
||||
#define CASIOLINK_MINIMUM_BUFFER_SIZE 50
|
||||
|
||||
/* Raw device information size for CASIOLINK, most specifically the
|
||||
* CAS100 variant. */
|
||||
#define CASIOLINK_RAW_DEVICE_INFO_BUFFER_SIZE 33
|
||||
|
||||
/* Flags to describe whether device information was obtained or not. */
|
||||
#define CASIOLINK_FLAG_DEVICE_INFO_OBTAINED 0x00000001
|
||||
|
||||
/* Maximum size of raw data that can come from an extended packet.
|
||||
* Calculators support data packets with up to 256 raw bytes (512 encoded
|
||||
* bytes), but fxRemote uses payloads that go up to 1028 raw bytes
|
||||
|
@ -314,12 +321,19 @@ union cahute_link_medium_state {
|
|||
/**
|
||||
* CASIOLINK peer state.
|
||||
*
|
||||
* @property flags Flags for the CASIOLINK peer state.
|
||||
* @property variant Variant with which to force data frame interpretation.
|
||||
* @property last_variant Variant for the last data frame.
|
||||
* @property raw_device_info Raw device information buffer, so that data
|
||||
* can be extracted later if actual device information is requested.
|
||||
*/
|
||||
struct cahute_casiolink_state {
|
||||
unsigned long flags;
|
||||
|
||||
int variant;
|
||||
int last_variant;
|
||||
|
||||
cahute_u8 raw_device_info[CASIOLINK_RAW_DEVICE_INFO_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -535,6 +549,12 @@ cahute_casiolink_receive_screen(
|
|||
unsigned long timeout
|
||||
);
|
||||
|
||||
CAHUTE_EXTERN(int)
|
||||
cahute_casiolink_make_device_info(
|
||||
cahute_link *link,
|
||||
cahute_device_info **infop
|
||||
);
|
||||
|
||||
/* ---
|
||||
* Protocol 7.00 functions, defined in seven.c
|
||||
* --- */
|
||||
|
|
34
lib/link.c
34
lib/link.c
|
@ -30,6 +30,17 @@
|
|||
#define CHECK_SENDER 0x00000001 /* Check that a link is not a receiver. */
|
||||
#define CHECK_RECEIVER 0x00000002 /* Check that a link is a receiver. */
|
||||
|
||||
/* On some platforms, for directories, ftell() returns an insanely high
|
||||
* number that may be platform-specific, e.g. 9223372036854775807 (2^63 - 1),
|
||||
* which would correspond to the highest positive value of a long if defined
|
||||
* on 64 bits. In order to detect such cases in a reasonably
|
||||
* platform-independent manner, we want to cap the size of any file that
|
||||
* gets stored into memory.
|
||||
*
|
||||
* See ``read_file_contents()`` for more details on the usage of this
|
||||
* constant. */
|
||||
#define REASONABLE_FILE_CONTENT_LIMIT 134217728 /* 128 MiB */
|
||||
|
||||
/**
|
||||
* Check a link's state.
|
||||
*
|
||||
|
@ -218,9 +229,6 @@ cahute_negotiate_serial_params(
|
|||
CAHUTE_RETURN_IMPL("Operation not supported by the link protocol.");
|
||||
}
|
||||
|
||||
link->serial_flags = new_serial_flags;
|
||||
link->serial_speed = speed;
|
||||
|
||||
err = cahute_set_serial_params_to_link(link, new_serial_flags, speed);
|
||||
if (err) {
|
||||
/* We have successfully negociated with the device to switch
|
||||
|
@ -267,6 +275,18 @@ cahute_get_device_info(cahute_link *link, cahute_device_info **infop) {
|
|||
return err;
|
||||
|
||||
switch (link->protocol) {
|
||||
case CAHUTE_LINK_PROTOCOL_SERIAL_CASIOLINK:
|
||||
/* With CASIOLINK, we may have received device information at
|
||||
* some point. */
|
||||
err = cahute_casiolink_make_device_info(
|
||||
link,
|
||||
&link->cached_device_info
|
||||
);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
break;
|
||||
|
||||
case CAHUTE_LINK_PROTOCOL_SERIAL_SEVEN:
|
||||
case CAHUTE_LINK_PROTOCOL_USB_SEVEN:
|
||||
/* With Protocol 7.00, we may already have device information
|
||||
|
@ -276,6 +296,7 @@ cahute_get_device_info(cahute_link *link, cahute_device_info **infop) {
|
|||
cahute_seven_make_device_info(link, &link->cached_device_info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -374,6 +395,13 @@ cahute_send_file_to_storage(
|
|||
}
|
||||
|
||||
file_size = (size_t)ftell(filep);
|
||||
if (file_size > REASONABLE_FILE_CONTENT_LIMIT) {
|
||||
msg(ll_error,
|
||||
"file too big (over 128MiB) or unsupported file type (e.g. "
|
||||
"directory)");
|
||||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
rewind(filep);
|
||||
|
||||
/* Send the file using the protocol. */
|
||||
|
|
|
@ -342,6 +342,7 @@ init_link(cahute_link *link, unsigned long flags, int casiolink_variant) {
|
|||
return CAHUTE_ERROR_UNKNOWN;
|
||||
}
|
||||
|
||||
casiolink_state->flags = 0;
|
||||
casiolink_state->variant = casiolink_variant;
|
||||
|
||||
if (~flags & PROTOCOL_FLAG_NOCHECK) {
|
||||
|
@ -1267,11 +1268,8 @@ cahute_open_serial_link(
|
|||
link->medium_state.windows.overlapped.hEvent = overlapped_event_handle;
|
||||
#endif
|
||||
|
||||
link->serial_flags = flags
|
||||
& (CAHUTE_SERIAL_STOP_MASK | CAHUTE_SERIAL_PARITY_MASK
|
||||
| CAHUTE_SERIAL_XONXOFF_MASK
|
||||
| CAHUTE_SERIAL_DTR_MASK | CAHUTE_SERIAL_RTS_MASK);
|
||||
link->serial_speed = speed;
|
||||
link->serial_flags = 0;
|
||||
link->serial_speed = 0;
|
||||
link->protocol = protocol;
|
||||
link->medium_read_start = 0;
|
||||
link->medium_read_size = 0;
|
||||
|
@ -1290,7 +1288,14 @@ cahute_open_serial_link(
|
|||
|
||||
/* The link is now considered opened, with protocol uninitialized.
|
||||
* We want to set the serial parameters to the medium now. */
|
||||
err = cahute_set_serial_params_to_link(link, link->serial_flags, speed);
|
||||
err = cahute_set_serial_params_to_link(
|
||||
link,
|
||||
flags
|
||||
& (CAHUTE_SERIAL_STOP_MASK | CAHUTE_SERIAL_PARITY_MASK
|
||||
| CAHUTE_SERIAL_XONXOFF_MASK | CAHUTE_SERIAL_DTR_MASK
|
||||
| CAHUTE_SERIAL_RTS_MASK),
|
||||
speed
|
||||
);
|
||||
if (err) {
|
||||
cahute_close_link(link);
|
||||
return err;
|
||||
|
|
|
@ -78,7 +78,7 @@ cahute_mcs_decode_data(
|
|||
}
|
||||
|
||||
/* TODO */
|
||||
msg(ll_info, "Data Type: %02X", data_type);
|
||||
msg(ll_info, "Data Type: 0x%02X", data_type);
|
||||
msg(ll_info, "Directory Name: %.*s", directory_size, directory);
|
||||
msg(ll_info, "Data Name: %.*s", name_size, name);
|
||||
msg(ll_info, "Group Name: %.*s", group_size, group);
|
||||
|
|
25
lib/medium.c
25
lib/medium.c
|
@ -778,6 +778,29 @@ cahute_set_serial_params_to_link(
|
|||
unsigned long flags,
|
||||
unsigned long speed
|
||||
) {
|
||||
if (link->serial_flags == flags && link->serial_speed == speed)
|
||||
return CAHUTE_OK;
|
||||
|
||||
msg(ll_info, "Setting the following serial settings:");
|
||||
msg(ll_info,
|
||||
" baud=%lu, parity=%s, stop bits=%d,",
|
||||
speed,
|
||||
(flags & CAHUTE_SERIAL_PARITY_MASK) == CAHUTE_SERIAL_PARITY_ODD ? "odd"
|
||||
: (flags & CAHUTE_SERIAL_PARITY_MASK) == CAHUTE_SERIAL_PARITY_EVEN
|
||||
? "even"
|
||||
: "none",
|
||||
(flags & CAHUTE_SERIAL_STOP_MASK) == CAHUTE_SERIAL_STOP_TWO ? 2 : 1);
|
||||
msg(ll_info,
|
||||
" dtr=%s, rts=%s",
|
||||
(flags & CAHUTE_SERIAL_DTR_MASK) == CAHUTE_SERIAL_DTR_HANDSHAKE
|
||||
? "handshake"
|
||||
: (flags & CAHUTE_SERIAL_DTR_ENABLE) ? "enabled"
|
||||
: "disabled",
|
||||
(flags & CAHUTE_SERIAL_RTS_MASK) == CAHUTE_SERIAL_RTS_HANDSHAKE
|
||||
? "handshake"
|
||||
: (flags & CAHUTE_SERIAL_RTS_ENABLE) ? "enabled"
|
||||
: "disabled");
|
||||
|
||||
switch (link->medium) {
|
||||
#ifdef CAHUTE_LINK_MEDIUM_POSIX_SERIAL
|
||||
case CAHUTE_LINK_MEDIUM_POSIX_SERIAL: {
|
||||
|
@ -1052,6 +1075,8 @@ cahute_set_serial_params_to_link(
|
|||
CAHUTE_RETURN_IMPL("No method available for setting serial params.");
|
||||
}
|
||||
|
||||
link->serial_flags = flags;
|
||||
link->serial_speed = speed;
|
||||
return CAHUTE_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -2147,9 +2147,6 @@ cahute_seven_receive_data(
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
link->serial_flags = new_serial_flags;
|
||||
link->serial_speed = new_serial_speed;
|
||||
|
||||
/* We introduce an artificial sleep to make the device believe
|
||||
* that we may be slow. Otherwise, the transfer may crash right
|
||||
* after we have changed the properties of our link. */
|
||||
|
@ -2170,8 +2167,7 @@ cahute_seven_receive_data(
|
|||
* Therefore, we consider the link to be irrecoverable. */
|
||||
msg(ll_error,
|
||||
"Could not set the serial params; that makes our "
|
||||
"connection "
|
||||
"irrecoverable!");
|
||||
"connection irrecoverable!");
|
||||
link->flags |= CAHUTE_LINK_FLAG_IRRECOVERABLE;
|
||||
return err;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue