static void msg_read_tiny(const uint8_t *msg, size_t len) {
    if (len != 64)
        return;

    uint8_t buf[64];
    memcpy(buf, msg, sizeof(buf));

    if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') {
        (*msg_failure)(FailureType_Failure_UnexpectedMessage, "Malformed tiny packet");
        return;
    }

    uint16_t msgId = buf[4] | ((uint16_t)buf[3]) << 8;
    uint32_t msgSize = buf[8]        |
            ((uint32_t)buf[7]) <<  8 |
            ((uint32_t)buf[6]) << 16 |
            ((uint32_t)buf[5]) << 24;

    if (msgSize > 64 - 9) {
        (*msg_failure)(FailureType_Failure_UnexpectedMessage, "Malformed tiny packet");
        return;
    }

    const pb_field_t *fields = NULL;
    pb_istream_t stream = pb_istream_from_buffer(buf + 9, msgSize);

    switch (msgId) {
    case MessageType_MessageType_PinMatrixAck:
        fields = PinMatrixAck_fields;
        break;
    case MessageType_MessageType_ButtonAck:
        fields = ButtonAck_fields;
        break;
    case MessageType_MessageType_PassphraseAck:
        fields = PassphraseAck_fields;
        break;
    case MessageType_MessageType_Cancel:
        fields = Cancel_fields;
        break;
    case MessageType_MessageType_Initialize:
        fields = Initialize_fields;
        break;
#if DEBUG_LINK
    case MessageType_MessageType_DebugLinkDecision:
        fields = DebugLinkDecision_fields;
        break;
    case MessageType_MessageType_DebugLinkGetState:
        fields = DebugLinkGetState_fields;
        break;
#endif
    }

    if (fields) {
        bool status = pb_decode(&stream, fields, msg_tiny);
        if (status) {
            msg_tiny_id = msgId;
        } else {
            (*msg_failure)(FailureType_Failure_SyntaxError, stream.errmsg);
            msg_tiny_id = 0xffff;
        }
    } else {
        (*msg_failure)(FailureType_Failure_UnexpectedMessage, "Unknown message");
        msg_tiny_id = 0xffff;
    }
}
static void msg_read_tiny(const uint8_t *msg, size_t len) {
    if (len != 64)
        return;

    uint8_t buf[64];
    memcpy(buf, msg, sizeof(buf));

    if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') {
        (*msg_failure)(FailureType_Failure_UnexpectedMessage, "Malformed tiny packet");
        return;
    }

    uint16_t msgId = buf[4] | ((uint16_t)buf[3]) << 8;
    uint32_t msgSize = buf[8]        |
            ((uint32_t)buf[7]) <<  8 |
            ((uint32_t)buf[6]) << 16 |
            ((uint32_t)buf[5]) << 24;

    if (msgSize > 64 - 9) {
        (*msg_failure)(FailureType_Failure_UnexpectedMessage, "Malformed tiny packet");
        return;
    }

    const pb_field_t *fields = NULL;
    pb_istream_t stream = pb_istream_from_buffer(buf + 9, msgSize);

    switch (msgId) {
    case MessageType_MessageType_PinMatrixAck:
        fields = PinMatrixAck_fields;
        break;
    case MessageType_MessageType_ButtonAck:
        fields = ButtonAck_fields;
        break;
    case MessageType_MessageType_PassphraseAck:
        fields = PassphraseAck_fields;
        break;
    case MessageType_MessageType_Cancel:
        fields = Cancel_fields;
        break;
    case MessageType_MessageType_Initialize:
        fields = Initialize_fields;
        break;
#if DEBUG_LINK
    case MessageType_MessageType_DebugLinkDecision:
        fields = DebugLinkDecision_fields;
        break;
    case MessageType_MessageType_DebugLinkGetState:
        fields = DebugLinkGetState_fields;
        break;
#endif
    }

    if (fields) {
        bool status = pb_decode(&stream, fields, msg_tiny);
        if (status) {
            msg_tiny_id = msgId;
        } else {
            (*msg_failure)(FailureType_Failure_SyntaxError, stream.errmsg);
            msg_tiny_id = 0xffff;
        }
    } else {
        (*msg_failure)(FailureType_Failure_UnexpectedMessage, "Unknown message");
        msg_tiny_id = 0xffff;
    }
}