void network_dispatch_response(Packet *response) { EnumerateCallback *enumerate_callback; char packet_signature[PACKET_MAX_SIGNATURE_LENGTH]; int i; Client *client; Node *pending_request_global_node; PendingRequest *pending_request; if (packet_header_get_sequence_number(&response->header) == 0) { if (response->header.function_id == CALLBACK_ENUMERATE) { enumerate_callback = (EnumerateCallback *)response; if (enumerate_callback->enumeration_type == ENUMERATION_TYPE_CONNECTED || enumerate_callback->enumeration_type == ENUMERATION_TYPE_DISCONNECTED) { network_drop_pending_requests(response->header.uid); } } if (_clients.count == 0) { log_packet_debug("No clients connected, dropping %s (%s)", packet_get_response_type(response), packet_get_response_signature(packet_signature, response)); return; } log_packet_debug("Broadcasting %s (%s) to %d client(s)", packet_get_response_type(response), packet_get_response_signature(packet_signature, response), _clients.count); for (i = 0; i < _clients.count; ++i) { client = array_get(&_clients, i); client_dispatch_response(client, NULL, response, true, false); } } else if (_clients.count + _zombies.count > 0) { log_packet_debug("Dispatching response (%s) to %d client(s) and %d zombies(s)", packet_get_response_signature(packet_signature, response), _clients.count, _zombies.count); pending_request_global_node = _pending_request_sentinel.next; while (pending_request_global_node != &_pending_request_sentinel) { pending_request = containerof(pending_request_global_node, PendingRequest, global_node); if (packet_is_matching_response(response, &pending_request->header)) { if (pending_request->client != NULL) { client_dispatch_response(pending_request->client, pending_request, response, false, false); } else { zombie_dispatch_response(pending_request->zombie, pending_request); } return; } pending_request_global_node = pending_request_global_node->next; } log_warn("Broadcasting response (%s) because no client/zombie has a matching pending request", packet_get_response_signature(packet_signature, response)); for (i = 0; i < _clients.count; ++i) { client = array_get(&_clients, i); client_dispatch_response(client, NULL, response, true, false); } } else { log_packet_debug("No clients/zombies connected, dropping response (%s)", packet_get_response_signature(packet_signature, response)); } }
// If data should just be polled, set packet_send to NULL. // // If no packet is received from slave the length in packet_recv will be set to 0, // the exact reason for that is encoded in the return value. // // For the return value see RED_STACK_TRANSCEIVE_RESULT_* at the top of this file. static int red_stack_spi_transceive_message(REDStackPacket *packet_send, Packet *packet_recv, REDStackSlave *slave) { int retval = 0; uint8_t length, length_send; uint8_t checksum; int rc; uint8_t sequence_number_master = 0xFF; uint8_t sequence_number_slave = 0xFF; uint8_t tx[RED_STACK_SPI_PACKET_SIZE] = {0}; uint8_t rx[RED_STACK_SPI_PACKET_SIZE] = {0}; // We assume that we don't receive anything. If we receive a packet the // length will be overwritten again packet_recv->header.length = 0; // Preamble is always the same tx[RED_STACK_SPI_PREAMBLE] = RED_STACK_SPI_PREAMBLE_VALUE; if (packet_send == NULL) { // If packet_send is NULL // we send a message with empty payload (4 byte) tx[RED_STACK_SPI_LENGTH] = RED_STACK_SPI_PACKET_EMPTY_SIZE; retval = RED_STACK_TRANSCEIVE_RESULT_SEND_NONE; } else if (slave->status == RED_STACK_SLAVE_STATUS_AVAILABLE) { length = packet_send->packet.header.length; if (length > sizeof(Packet)) { retval |= RED_STACK_TRANSCEIVE_RESULT_SEND_ERROR; log_error("Send length is greater then allowed (actual: %d > maximum: %d)", length, (int)sizeof(Packet)); goto ret; } retval = RED_STACK_TRANSCEIVE_DATA_SEND; tx[RED_STACK_SPI_LENGTH] = length + RED_STACK_SPI_PACKET_EMPTY_SIZE; memcpy(tx+2, &packet_send->packet, length); } else { retval = RED_STACK_TRANSCEIVE_RESULT_SEND_ERROR; log_error("Slave with stack address %d is not present in stack", slave->stack_address); goto ret; } length = tx[RED_STACK_SPI_LENGTH]; // Set master and slave sequence number tx[RED_STACK_SPI_INFO(length)] = slave->sequence_number_master | slave->sequence_number_slave; // Calculate checksum tx[RED_STACK_SPI_CHECKSUM(length)] = red_stack_spi_calculate_pearson_hash(tx, length-1); struct spi_ioc_transfer spi_transfer = { .tx_buf = (unsigned long)&tx, .rx_buf = (unsigned long)&rx, .len = RED_STACK_SPI_PACKET_SIZE, }; red_stack_spi_select(slave); rc = ioctl(_red_stack_spi_fd, SPI_IOC_MESSAGE(1), &spi_transfer); red_stack_spi_deselect(slave); if (rc < 0) { // Overwrite current return status with error, // it seems ioctl itself didn't work. retval = RED_STACK_TRANSCEIVE_RESULT_SEND_ERROR | RED_STACK_TRANSCEIVE_RESULT_READ_ERROR; if(packet_send == NULL) { slave->next_packet_empty = true; } log_error("ioctl failed: %s (%d)", get_errno_name(errno), errno); goto ret; } length_send = rc; if (length_send != RED_STACK_SPI_PACKET_SIZE) { // Overwrite current return status with error, // it seems ioctl itself didn't work. retval = RED_STACK_TRANSCEIVE_RESULT_SEND_ERROR | RED_STACK_TRANSCEIVE_RESULT_READ_ERROR; if(packet_send == NULL) { slave->next_packet_empty = true; } log_error("ioctl has unexpected result (actual: %d != expected: %d)", length_send, RED_STACK_SPI_PACKET_SIZE); goto ret; } if (rx[RED_STACK_SPI_PREAMBLE] != RED_STACK_SPI_PREAMBLE_VALUE) { // Do not log by default, an "unproper preamble" is part of the protocol // if the slave is too busy to fill the DMA buffers fast enough // log_error("Received packet without proper preamble (actual: %d != expected: %d)", // rx[RED_STACK_SPI_PREAMBLE], RED_STACK_SPI_PREAMBLE_VALUE); retval = (retval & (~RED_STACK_TRANSCEIVE_RESULT_MASK_READ)) | RED_STACK_TRANSCEIVE_RESULT_READ_ERROR; if(packet_send == NULL) { slave->next_packet_empty = true; } goto ret; } // Check length length = rx[RED_STACK_SPI_LENGTH]; if ((length != RED_STACK_SPI_PACKET_EMPTY_SIZE) && ((length < (RED_STACK_SPI_PACKET_EMPTY_SIZE + sizeof(PacketHeader))) || (length > RED_STACK_SPI_PACKET_SIZE))) { log_error("Received packet with malformed length: %d", length); retval = (retval & (~RED_STACK_TRANSCEIVE_RESULT_MASK_READ)) | RED_STACK_TRANSCEIVE_RESULT_READ_ERROR; if(packet_send == NULL) { slave->next_packet_empty = true; } goto ret; } // Calculate and check checksum checksum = red_stack_spi_calculate_pearson_hash(rx, length-1); if (checksum != rx[RED_STACK_SPI_CHECKSUM(length)]) { log_error("Received packet with wrong checksum (actual: %x != expected: %x)", checksum, rx[RED_STACK_SPI_CHECKSUM(length)]); retval = (retval & (~RED_STACK_TRANSCEIVE_RESULT_MASK_READ)) | RED_STACK_TRANSCEIVE_RESULT_READ_ERROR; if(packet_send == NULL) { slave->next_packet_empty = true; } goto ret; } // If we send data and the master sequence number matches to the one // set in the packet we know that the slave received the packet! if ((packet_send != NULL) /*&& (packet_send->status == RED_STACK_PACKET_STATUS_SEQUENCE_NUMBER_SET)*/) { sequence_number_master = rx[RED_STACK_SPI_INFO(length)] & RED_STACK_SPI_INFO_SEQUENCE_MASTER_MASK; if (sequence_number_master == slave->sequence_number_master) { retval = (retval & (~RED_STACK_TRANSCEIVE_RESULT_MASK_SEND)) | RED_STACK_TRANSCEIVE_RESULT_SEND_OK; // Increase sequence number for next packet red_stack_increase_master_sequence_number(slave); } } else { // If we didn't send anything we can always increase the sequence number, // it doesn't matter if the slave actually received it. red_stack_increase_master_sequence_number(slave); } // If the slave sequence number matches we already processed this packet sequence_number_slave = rx[RED_STACK_SPI_INFO(length)] & RED_STACK_SPI_INFO_SEQUENCE_SLAVE_MASK; if (sequence_number_slave == slave->sequence_number_slave) { retval = (retval & (~RED_STACK_TRANSCEIVE_RESULT_MASK_READ)) | RED_STACK_TRANSCEIVE_RESULT_READ_NONE; } else { // Otherwise we save the new sequence number slave->sequence_number_slave = sequence_number_slave; if (length == RED_STACK_SPI_PACKET_EMPTY_SIZE) { // Do not log by default, will produce 2000 log entries per second // log_packet_debug("Received empty packet over SPI (w/ header)"); retval = (retval & (~RED_STACK_TRANSCEIVE_RESULT_MASK_READ)) | RED_STACK_TRANSCEIVE_RESULT_READ_NONE; } else { // Everything seems OK, we can copy to buffer memcpy(packet_recv, rx+2, length - RED_STACK_SPI_PACKET_EMPTY_SIZE); log_packet_debug("Received packet over SPI (%s)", packet_get_response_signature(packet_signature, packet_recv)); retval = (retval & (~RED_STACK_TRANSCEIVE_RESULT_MASK_READ)) | RED_STACK_TRANSCEIVE_RESULT_READ_OK; retval |= RED_STACK_TRANSCEIVE_DATA_RECEIVED; } } ret: return retval; } // Creates the "routing table", which is just the // array of REDStackSlave structures. static void red_stack_spi_create_routing_table(void) { char base58[BASE58_MAX_LENGTH]; int tries, ret, i; uint8_t stack_address = 0; uint8_t uid_counter = 0; log_debug("Starting to discover SPI stack slaves"); while (stack_address < RED_STACK_SPI_MAX_SLAVES) { REDStackSlave *slave = &_red_stack.slaves[stack_address]; Packet packet; StackEnumerateResponse *response; REDStackPacket red_stack_packet = { slave, {{ 0, // UID 0 sizeof(StackEnumerateRequest), FUNCTION_STACK_ENUMERATE, 0x08, // Return expected 0 }, {0}, {0}}, RED_STACK_PACKET_STATUS_ADDED, }; // We have to assume that the slave is available slave->status = RED_STACK_SLAVE_STATUS_AVAILABLE; // Send stack enumerate request for (tries = 0; tries < RED_STACK_SPI_ROUTING_TRIES; tries++) { ret = red_stack_spi_transceive_message(&red_stack_packet, &packet, slave); if ((ret & RED_STACK_TRANSCEIVE_RESULT_MASK_SEND) == RED_STACK_TRANSCEIVE_RESULT_SEND_OK) { break; } SLEEP_NS(0, RED_STACK_SPI_ROUTING_WAIT); // Give slave some more time } if (tries == RED_STACK_SPI_ROUTING_TRIES) { // Slave does not seem to be available, slave->status = RED_STACK_SLAVE_STATUS_ABSENT; // This means that there can't be any more slaves above // and we are actually done here already! break; } // Receive stack enumerate response for (tries = 0; tries < RED_STACK_SPI_ROUTING_TRIES; tries++) { // We first check if we already received an answer before we try again if ((ret & RED_STACK_TRANSCEIVE_RESULT_MASK_READ) == RED_STACK_TRANSCEIVE_RESULT_READ_OK) { break; } // Here we sleep before transceive so that there is some time // between the sending of stack enumerate and the receiving // of the answer SLEEP_NS(0, RED_STACK_SPI_ROUTING_WAIT); // Give slave some more time ret = red_stack_spi_transceive_message(NULL, &packet, slave); } if (tries == RED_STACK_SPI_ROUTING_TRIES) { // Slave does not seem to be available, slave->status = RED_STACK_SLAVE_STATUS_ABSENT; // This means that there can't be any more slaves above // and we are actually done here already! break; } response = (StackEnumerateResponse *)&packet; for (i = 0; i < PACKET_MAX_STACK_ENUMERATE_UIDS; i++) { if (response->uids[i] != 0) { uid_counter++; stack_add_recipient(&_red_stack.base, response->uids[i], stack_address); log_debug("Found UID number %d of slave %d with UID %s", i, stack_address, base58_encode(base58, uint32_from_le(response->uids[i]))); } else { break; } } stack_address++; } _red_stack.slave_num = stack_address; log_info("SPI stack slave discovery done. Found %d slave(s) with %d UID(s) in total", stack_address, uid_counter); }