I'm looking at the patched version resulting from applying the pc-ble-driver patch, but this might be present in the original as well.
675 case APP_USBD_CDC_ACM_USER_EVT_RX_DONE: 676 { 677 ret_code_t ret_code; 678 do 679 { 680 ser_phi_hci_rx_byte(m_rx_byte); 681 682 ret_code = app_usbd_cdc_acm_read(p_cdc_acm, &m_rx_byte, 1); 683 } while (ret_code == NRF_SUCCESS); 684 } 685 break; 686 687 default: 688 break; 689 } 690 }
For the 2nd APP_USBD_CDC_ACM_USER_EVT_RX_DONE event this seems to be repeating the last byte from the last event into the next packet. Note that the earlier APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN event handler handles this in a more logical pattern.
do { ret_code = app_usbd_cdc_acm_read(p_cdc_acm, &m_rx_byte, 1); if (ret_code == NRF_SUCCESS) { ser_phi_hci_rx_byte(m_rx_byte); } else if (ret_code != NRF_ERROR_IO_PENDING) { APP_ERROR_CHECK(ret_code); } } while (ret_code == NRF_SUCCESS);
I would think something like this is what is intended:
while (NRF_SUCCESS == app_usbd_cdc_acm_read(p_cdc_acm, &m_rx_byte, 1)) { ser_phi_hci_rx_byte(m_rx_byte); }
or just copy the implementation from the APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN event.
The reason that this still works is because copying the END slip byte just resets the packet state to the beginning but this only works AS LONG AS ONE READ RETURNS A COMPLETE PACKET.
Here is what the data stream to the slip decoder looks like (one line per RX_DONE event).
i> ser_conn_dec: Command process start
i> ser_conn_dec: Tx buffer allocated
C0 18 00 00 E8 C0
C0 D8 4E 01 D9 00 62 01 FB 34 9B 5F 80 00 00 80 00 10 00 00 00 CB 00 00 01 D2 0A C0
i> ser_conn_dec: Command process start
i> ser_conn_dec: Tx buffer allocated
C0 20 00 00 E0 C0
C0 E1 4E 01 D0 00 62 01 FB 34 9B 5F 80 00 00 80 00 10 00 00 01 CB 00 00 01 CC E5 C0
i> ser_conn_dec: Command process start
i> ser_conn_dec: Tx buffer allocated
C0 28 00 00 D8 C0
C0 EA 4E 01 C7 00 62 01 24 D1 BC EA 5F 78 23 15 DE EF 12 12 23 15 00 00 01 F7 66 C0
i> ser_conn_dec: Command process start
i> ser_conn_dec: Tx buffer allocated
C0 30 00 00 D0 C0
C0 F3 4E 01 BE 00 62 01 24 D1 BC EA 5F 78 23 15 DE EF 12 12 25 15 00 00 01 9B F6 C0
i> ser_conn_dec: Command process start
i> ser_conn_dec: Tx buffer allocated
C0 38 00 00 C8 C0
C0 FC 4E 01 B5 00 62 01 24 D1 BC EA 5F 78 23 15 DE EF 12 12 24 15 00 00 01 B7 D0 C0
I've highlighted the repeated byte (in the case END). It looks like should a single slip packet ever span one USB read this will break horribly.
Please advise.
FOLLOW UP:
I applied the fix I proposed and ser_phi_hci_rx_byte() dies horribly. It should be evident that the 1st packet is lost because there is no END until the 2nd packet, but I fixed this by injecting one in the OPEN event handler. Still dies horribly. Ser_phi_hci_rx_byte needs the two ENDs in a row, the 1st one closes the packet and sends it, the second resets rx_sync. So Ser_phi_hci_rx_byte is relying on this duplication. Note that if data becomes available at the OPEN event, which doesn't have this bug, it won't be readable because of the above.
I'm going to need to fix up ser_phi_hci_rx_byte() as well to use my fix, relying on all the data in packet to come in on one USB read is just being way too chummy with the host USB implementation in assuming that it won't break a write up across separate transfers.
NB: It is considered "good practice" in a slip implementation to precede each packet by an END as well. If you get END END that is ignored, it is not a zero-length packet. Superficially it looks like the tx_buf_fill() (which sends to the PC) does do this, but I didn't poke at it to verify.
FURTHER EXPLANATION:
What the original code is relying on is that a write(serial_port, the_complete_packet, sizeof(the_complete_packet)) on the PC side results in exactly one read on the device side of exactly one packet, ie, read(serial_port, input_buffer) always returns either nothing or exactly sizeof(the_complete_packet) but never the first half of a packet followed by the second half. This sort of guarantee doesn't exist on the host side, it would be entirely reasonable for the host to split up the write into multiple bulk packets (especially considering single packets can be almost 800 bytes).
If the guarantee you are relying on does in fact exist THEN YOU DON'T NEED A SLIP LAYER. You just write the complete raw packets to the virtual serial port on the PC side and they will magically come out as packets on the device side.
I have a complete set of patches now that fixes this, the changes required to fix ser_phi_hci_rx_byte() were trivial.