/* Includes ------------------------------------------------------------------*/ #include #include #include #include /* Private defines -----------------------------------------------------------*/ /* Private macros ------------------------------------------------------------*/ /* Private typedef -----------------------------------------------------------*/ /* Global constant data ------------------------------------------------------*/ /* Global variables ----------------------------------------------------------*/ Endpoint** endpoint_list_ = nullptr; // initialized by calling fibre_publish size_t n_endpoints_ = 0; // initialized by calling fibre_publish uint16_t json_crc_; // initialized by calling fibre_publish JSONDescriptorEndpoint json_file_endpoint_ = JSONDescriptorEndpoint(); EndpointProvider* application_endpoints_; /* Private constant data -----------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ /* Private function prototypes -----------------------------------------------*/ static void hexdump(const uint8_t* buf, size_t len); static inline int write_string(const char* str, StreamSink* output); /* Function implementations --------------------------------------------------*/ #if 0 void hexdump(const uint8_t* buf, size_t len) { for (size_t pos = 0; pos < len; ++pos) { printf(" %02x", buf[pos]); if ((((pos + 1) % 16) == 0) || ((pos + 1) == len)) printf("\r\n"); osDelay(2); } } #else void hexdump(const uint8_t* buf, size_t len) { (void) buf; (void) len; } #endif int StreamToPacketSegmenter::process_bytes(const uint8_t *buffer, size_t length, size_t* processed_bytes) { int result = 0; while (length--) { if (header_index_ < sizeof(header_buffer_)) { // Process header byte header_buffer_[header_index_++] = *buffer; if (header_index_ == 1 && header_buffer_[0] != CANONICAL_PREFIX) { header_index_ = 0; } else if (header_index_ == 2 && (header_buffer_[1] & 0x80)) { header_index_ = 0; // TODO: support packets larger than 128 bytes } else if (header_index_ == 3 && calc_crc8(CANONICAL_CRC8_INIT, header_buffer_, 3)) { header_index_ = 0; } else if (header_index_ == 3) { packet_length_ = header_buffer_[1] + 2; } } else if (packet_index_ < sizeof(packet_buffer_)) { // Process payload byte packet_buffer_[packet_index_++] = *buffer; } // If both header and packet are fully received, hand it on to the packet processor if (header_index_ == 3 && packet_index_ == packet_length_) { if (calc_crc16(CANONICAL_CRC16_INIT, packet_buffer_, packet_length_) == 0) { result |= output_.process_packet(packet_buffer_, packet_length_ - 2); } header_index_ = packet_index_ = packet_length_ = 0; } buffer++; if (processed_bytes) (*processed_bytes)++; } return result; } int StreamBasedPacketSink::process_packet(const uint8_t *buffer, size_t length) { // TODO: support buffer size >= 128 if (length >= 128) return -1; LOG_FIBRE("send header\r\n"); uint8_t header[] = { CANONICAL_PREFIX, static_cast(length), 0 }; header[2] = calc_crc8(CANONICAL_CRC8_INIT, header, 2); if (output_.process_bytes(header, sizeof(header), nullptr)) return -1; LOG_FIBRE("send payload:\r\n"); hexdump(buffer, length); if (output_.process_bytes(buffer, length, nullptr)) return -1; LOG_FIBRE("send crc16\r\n"); uint16_t crc16 = calc_crc16(CANONICAL_CRC16_INIT, buffer, length); uint8_t crc16_buffer[] = { (uint8_t)((crc16 >> 8) & 0xff), (uint8_t)((crc16 >> 0) & 0xff) }; if (output_.process_bytes(crc16_buffer, 2, nullptr)) return -1; LOG_FIBRE("sent!\r\n"); return 0; } void JSONDescriptorEndpoint::write_json(size_t id, StreamSink* output) { write_string("{\"name\":\"\",", output); // write endpoint ID write_string("\"id\":", output); char id_buf[10]; snprintf(id_buf, sizeof(id_buf), "%u", (unsigned)id); // TODO: get rid of printf write_string(id_buf, output); write_string(",\"type\":\"json\",\"access\":\"r\"}", output); } void JSONDescriptorEndpoint::register_endpoints(Endpoint** list, size_t id, size_t length) { if (id < length) list[id] = this; } // Returns part of the JSON interface definition. void JSONDescriptorEndpoint::handle(const uint8_t* input, size_t input_length, StreamSink* output) { // The request must contain a 32 bit integer to specify an offset if (input_length < 4) return; uint32_t offset = 0; read_le(&offset, input); NullStreamSink output_with_offset = NullStreamSink(offset, *output); size_t id = 0; write_string("[", &output_with_offset); json_file_endpoint_.write_json(id, &output_with_offset); id += decltype(json_file_endpoint_)::endpoint_count; write_string(",", &output_with_offset); application_endpoints_->write_json(id, &output_with_offset); write_string("]", &output_with_offset); } int BidirectionalPacketBasedChannel::process_packet(const uint8_t* buffer, size_t length) { LOG_FIBRE("got packet of length %d: \r\n", length); hexdump(buffer, length); if (length < 4) return -1; uint16_t seq_no = read_le(&buffer, &length); if (seq_no & 0x8000) { // TODO: ack handling } else { // TODO: think about some kind of ordering guarantees // currently the seq_no is just used to associate a response with a request uint16_t endpoint_id = read_le(&buffer, &length); bool expect_response = endpoint_id & 0x8000; endpoint_id &= 0x7fff; if (endpoint_id >= n_endpoints_) return -1; Endpoint* endpoint = endpoint_list_[endpoint_id]; if (!endpoint) { LOG_FIBRE("critical: no endpoint at %d", endpoint_id); return -1; } // Verify packet trailer. The expected trailer value depends on the selected endpoint. // For endpoint 0 this is just the protocol version, for all other endpoints it's a // CRC over the entire JSON descriptor tree (this may change in future versions). uint16_t expected_trailer = endpoint_id ? json_crc_ : PROTOCOL_VERSION; uint16_t actual_trailer = buffer[length - 2] | (buffer[length - 1] << 8); if (expected_trailer != actual_trailer) { LOG_FIBRE("trailer mismatch for endpoint %d: expected %04x, got %04x\r\n", endpoint_id, expected_trailer, actual_trailer); return -1; } LOG_FIBRE("trailer ok for endpoint %d\r\n", endpoint_id); // TODO: if more bytes than the MTU were requested, should we abort or just return as much as possible? uint16_t expected_response_length = read_le(&buffer, &length); // Limit response length according to our local TX buffer size if (expected_response_length > sizeof(tx_buf_) - 2) expected_response_length = sizeof(tx_buf_) - 2; MemoryStreamSink output(tx_buf_ + 2, expected_response_length); endpoint->handle(buffer, length - 2, &output); // Send response if (expect_response) { size_t actual_response_length = expected_response_length - output.get_free_space() + 2; write_le(seq_no | 0x8000, tx_buf_); LOG_FIBRE("send packet:\r\n"); hexdump(tx_buf_, actual_response_length); output_.process_packet(tx_buf_, actual_response_length); } } return 0; }