/** * ui_interface.c * * Packet-based communication interface. * * Copyright (c) 2022 Bigscreen, Inc. */ #include #include #include #include "ui_interface.h" #include "vxr_interface.h" Data_Packet_T response_packet; uint32_t ui_user_signature_buffer[512/sizeof(uint32_t)]; uint8_t ui_user_signature_block_num; bool ui_signature_needs_updating = true; /* * Checks a buffer for a valid packet. Only processes the first packet found. * Packet data is populated into the Data_Packet_T pointer passed to this function. * Arguments: * buf: the data buffer which may contain a packet * buf_length: the length of valid data the buffer * received_packet: a pointer to a Data_Packet_T struct which will * be filled with data from the received packet. * Return: * uint8_t*: * If valid start byte found: pointer to the location of the first packet start byte, * e.g. the beginning of the packet * NOTE: If there is no valid packet found, the return value will still contain * the location of the first start byte, if it exists. * * If no packet is found, the received_packet "Type" will be "UI_NO_PACKET". This should * be used as the final check to determine whether or not a valid packet is in the buffer. */ uint8_t* UI_check_packet(uint8_t* buf, uint32_t buf_length, Data_Packet_T* received_packet) { uint8_t* start_byte; uint8_t packet_type; uint16_t packet_length; // 1. Check for the start bytes start_byte = memchr(buf, USART_START_BYTE0, buf_length); uint32_t start_place = ((uint32_t)start_byte) - ((uint32_t)buf); // Make sure the start byte was found and that enough bytes are in the buffer to complete // at least a zero data packet if((start_byte == NULL) || ((buf_length - start_place) < USART_DATA_OFFSET)) { received_packet->ErrorCode = UI_ERR_NO_PACKET; } else { // 1b. check for second start byte if(USART_START_BYTE1 != start_byte[USART_START_BYTE1_OFFSET]) { received_packet->ErrorCode = UI_ERR_BAD_START_SEQ; } else { // 2. get the packet type. two bytes, second is bit inverted version of first if((start_byte[USART_PACKET_TYPE_OFFSET] ^ start_byte[USART_INV_PACKET_TYPE_OFFSET]) != 0xFFu) { received_packet->ErrorCode = UI_ERR_BAD_TYPE; } else { packet_type = start_byte[USART_PACKET_TYPE_OFFSET]; // 3. get the length packet_length = start_byte[USART_DATA_LENGTH_LSB_OFFSET]; packet_length += ((uint16_t)start_byte[USART_DATA_LENGTH_MSB_OFFSET]) << 8; if(packet_length > MAX_PACKET_DATA_LENGTH) { received_packet->ErrorCode = UI_ERR_INVALID_LENGTH; } else { // check that enough bytes have been received for all the declared data uint32_t end_place = start_place + USART_DATA_OFFSET + packet_length; // end_place is actually 1 after the last data byte. It's the place just after the packet ends. if(buf_length >= end_place) { // 4. get the data memcpy(received_packet->Data, &(start_byte[USART_DATA_OFFSET]), packet_length); // populate the other members of the packet received_packet->Length = packet_length; received_packet->Type = packet_type; received_packet->ErrorCode = UI_ERR_SUCCESS; } else { // Not enough data for this packet, but more could be coming later. // Caller shouldn't delete this partial packet from the buffer yet received_packet->ErrorCode = UI_ERR_NO_PACKET; } } } } } return start_byte; } uint8_t UI_build_packet(uint8_t* buf, uint32_t buf_length, Data_Packet_T* pkt) { if((buf_length < (USART_DATA_OFFSET + (pkt->Length))) || (MAX_PACKET_DATA_LENGTH < (pkt->Length))) { // not enough buffer space to load the packet, or length too long, fail. return pdFAIL; } buf[USART_START_BYTE0_OFFSET] = USART_START_BYTE0; buf[USART_START_BYTE1_OFFSET] = USART_START_BYTE1; buf[USART_PACKET_TYPE_OFFSET] = pkt->Type; buf[USART_INV_PACKET_TYPE_OFFSET] = ~(pkt->Type); buf[USART_DATA_LENGTH_LSB_OFFSET] = (uint8_t)((pkt->Length) & 0x00FFu); buf[USART_DATA_LENGTH_MSB_OFFSET] = (uint8_t)(((pkt->Length) & 0xFF00u) >> 8); memcpy(&(buf[USART_DATA_OFFSET]), pkt->Data, pkt->Length); return pdPASS; } void UI_process_command(Data_Packet_T* req_pkt) { switch(req_pkt->Type) { case HID_CODE_FOR_SW_VER: // Reply with the software version string response_packet.Type = HID_CODE_FOR_SW_VER_REPLY; response_packet.Length = get_sw_ver_len(); memcpy(response_packet.Data, get_sw_ver(), get_sw_ver_len()); break; case HID_CODE_FOR_SERIAL_NUM: // Reply with the serial number response_packet.Length = sigv2_tag_length(SigTag_Serial); if(0 == response_packet.Length) { response_packet.Type = UI_NACK; // no serial number entry found in user signature } else { if(sigv2_find_tag(SigTag_Serial, response_packet.Data)) { response_packet.Type = HID_CODE_FOR_SERIAL_NUM_REPLY; } else { // Really shouldn't fail to here. But if it does, respond with nack response_packet.Type = UI_NACK; } } break; case HID_CODE_FOR_RGB_LED: // Next three bytes are R, G, B respectively if(req_pkt->Length == 3) { RGB_Color_t newcolor; newcolor.r = req_pkt->Data[0]; newcolor.g = req_pkt->Data[1]; newcolor.b = req_pkt->Data[2]; rgb_led_set_color(MCU_I2C(), &newcolor); response_packet.Length = 0; response_packet.Type = UI_ACK; } else { // respond with NACK since there's something wrong with the command response_packet.Length = 0; response_packet.Type = UI_NACK; } break; case HID_CODE_FOR_READ_SIG: // Next byte is block. Signature is read in 32 bytes blocks (total of 512 bytes will // be read out in 16 blocks) if(req_pkt->Length == 1) { if((req_pkt->Data[0]) <= 15) { const uint8_t* sig = (const uint8_t*) get_signature_raw(); response_packet.Type = HID_CODE_FOR_SIG_REPLY; response_packet.Length = HID_SIG_XFER_LENGTH; memcpy(response_packet.Data, &(sig[(req_pkt->Data[0])*HID_SIG_XFER_LENGTH]),HID_SIG_XFER_LENGTH); } else { response_packet.Length = 0; response_packet.Type = UI_NACK; } } else { response_packet.Length = 0; response_packet.Type = UI_NACK; } break; case HID_CODE_FOR_WRITE_SIG: // Should be taking in 33 bytes - first one is the block (0 to 15), next 32 are the data to be // written to that block if(req_pkt->Length == (HID_SIG_XFER_LENGTH + 1u)){ if((req_pkt->Data[0]) <= 15) { if(pdPASS == update_signature_raw(&(req_pkt->Data[1]), (req_pkt->Data[0])*HID_SIG_XFER_LENGTH, HID_SIG_XFER_LENGTH)) { response_packet.Length = 0; response_packet.Type = UI_ACK; } else { response_packet.Length = 0; response_packet.Type = UI_NACK; } } else { response_packet.Length = 0; response_packet.Type = UI_NACK; } } else { response_packet.Length = 0; response_packet.Type = UI_NACK; } break; case HID_CODE_FOR_SAVE_SIG: if(req_pkt->Length == 0) { if(pdPASS == save_signature_raw()) { response_packet.Length = 0; response_packet.Type = UI_ACK; } else { response_packet.Length = 0; response_packet.Type = UI_NACK; } } else { response_packet.Length = 0; response_packet.Type = UI_NACK; } break; case HID_CODE_FOR_CHECK_VXR_CONNECTION: if(req_pkt->Length == 0) { if(RC_Success == VXR_Check_Connection()) { response_packet.Length = 0; response_packet.Type = UI_ACK; } else { response_packet.Length = 0; response_packet.Type = UI_NACK; } } else { response_packet.Length = 0; response_packet.Type = UI_NACK; } break; case HID_CODE_FOR_VXR_DELETE: // Deletes a block of Flash memory on the VXR7200. // There are 8 blocks - so only values 0->7 are allowed if(req_pkt->Length == 1) { if(RC_Success == VXR_Erase_Firmware_Bank(req_pkt->Data[0])) { response_packet.Length = 0; response_packet.Type = UI_ACK; } else { response_packet.Length = 0; response_packet.Type = UI_NACK; } } else { response_packet.Length = 0; response_packet.Type = UI_NACK; } break; case HID_CODE_FOR_VXR_CHECKSUM: // Calculates the checksum of installed firmware on the VXR7200 chip. // requires a length and starting address // Both are 4 byte values, most significant byte last // for example, 0x01020304 is sent as 0x04, 0x03, 0x02, 0x01 // Data[0:3] = address to start calculating checksum // Data[4:7] = length (in bytes) to sum up for the checksum if(req_pkt->Length == 8) { uint32_t chksum_addr = ((uint32_t)req_pkt->Data[0]); chksum_addr += (((uint32_t)req_pkt->Data[1]) << 8); chksum_addr += (((uint32_t)req_pkt->Data[2]) << 16); chksum_addr += (((uint32_t)req_pkt->Data[3]) << 24); uint32_t chksum_len = ((uint32_t)req_pkt->Data[4]); chksum_len += (((uint32_t)req_pkt->Data[5]) << 8); chksum_len += (((uint32_t)req_pkt->Data[6]) << 16); chksum_len += (((uint32_t)req_pkt->Data[7]) << 24); uint32_t chksum_result; if(RC_Success == VXR_Calc_Checksum(chksum_addr, chksum_len, &chksum_result)) { memcpy(response_packet.Data, &(chksum_result), VXR_CHECKSUM_LENGTH); response_packet.Type = HID_CODE_FOR_VXR_CHECKSUM_REPLY; response_packet.Length = VXR_CHECKSUM_LENGTH; } } else { response_packet.Length = 0; response_packet.Type = UI_NACK; } break; case HID_CODE_FOR_VXR_PROGRAM: // programs 1-32 bytes in the VXR7200 Flash memory // Data[0] = length in bytes (1 to 32) // Data[1:4] = starting address where to program // Data[5:5+length-1] = bytes to program if( (req_pkt->Length >= 6 ) && (req_pkt->Length <= 37) ) { // Allows 1 to 32 bytes to be programmed per packet // Length of 6: 1 byte for length, 4 byte address, 1 data byte // Length of 37: 1 byte for length, 4 byte address, 32 data bytes uint32_t prog_len = req_pkt->Data[0]; uint32_t prog_addr = ((uint32_t)req_pkt->Data[1]); prog_addr += (((uint32_t)req_pkt->Data[2]) << 8); prog_addr += (((uint32_t)req_pkt->Data[3]) << 16); prog_addr += (((uint32_t)req_pkt->Data[4]) << 24); if(RC_Success == VXR_Program_Firmware(prog_addr, prog_len, &(req_pkt->Data[5]))) { response_packet.Length = 0; response_packet.Type = UI_ACK; } else { response_packet.Length = 0; response_packet.Type = UI_NACK; } } else { response_packet.Length = 0; response_packet.Type = UI_NACK; } break; case HID_CODE_FOR_VXR_TAGS: if(req_pkt->Length == 0) { response_packet.Data[0] = 0; if(VXR_Tag_Valid(Vxr_Tag_Config_0)) response_packet.Data[0] |= 0x01; if(VXR_Tag_Valid(Vxr_Tag_Config_1)) response_packet.Data[0] |= 0x02; if(VXR_Tag_Valid(Vxr_Tag_Firmware_0)) response_packet.Data[0] |= 0x04; if(VXR_Tag_Valid(Vxr_Tag_Firmware_1)) response_packet.Data[0] |= 0x08; response_packet.Length = 1; response_packet.Type = HID_CODE_FOR_VXR_TAGS_REPLY; } else { response_packet.Length = 0; response_packet.Type = UI_NACK; } break; case HID_CODE_FOR_HW_TEST: if(req_pkt->Length == 1) { // One byte for the test ID switch( (HW_Test_T)(req_pkt->Data[0]) ) { case HWTest_USB_Hub: response_packet.Data[0] = test_usb_hub(); response_packet.Type = HID_CODE_FOR_HW_TEST_REPLY; response_packet.Length = 1; break; case HWTest_RGB_LED: response_packet.Data[0] = test_rgb_led(); response_packet.Type = HID_CODE_FOR_HW_TEST_REPLY; response_packet.Length = 1; break; case HWTest_USBC_Switch: response_packet.Data[0] = test_usbc_switch(); response_packet.Type = HID_CODE_FOR_HW_TEST_REPLY; response_packet.Length = 1; break; case HWTest_VXR: response_packet.Data[0] = test_vxr(); response_packet.Type = HID_CODE_FOR_HW_TEST_REPLY; response_packet.Length = 1; break; case HWTest_Prox: response_packet.Data[0] = test_prox(); response_packet.Type = HID_CODE_FOR_HW_TEST_REPLY; response_packet.Length = 1; break; case HWTest_OLED: response_packet.Data[0] = test_oled_panels(); response_packet.Type = HID_CODE_FOR_HW_TEST_REPLY; response_packet.Length = 1; break; case HWTest_Fan: response_packet.Data[0] = test_fan(); response_packet.Type = HID_CODE_FOR_HW_TEST_REPLY; response_packet.Length = 1; break; default: response_packet.Type = UI_NACK; response_packet.Length = 0; break; } } else { response_packet.Length = 0; response_packet.Type = UI_NACK; } break; default: response_packet.Length = 0; response_packet.Type = UI_NACK; break; } } Data_Packet_T* UI_get_response(void) { return &response_packet; }