Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Bluetooth: hci_h5: implement CRC data integrity

The UART-based H5 protocol supports CRC data integrity checks for
reliable packets. The host sets bit 5 in the configuration field of the
CONFIG link control message to indicate that CRC is supported. The
controller sets the same bit in the CONFIG RESPONSE message to indicate
that CRC may be used from then on.

Tested on a MangoPi MQ-Pro with a Realtek RTL8723DS Bluetooth controller
using the tip of the bluetooth-next tree.

Signed-off-by: Javier Nieto <jgnieto@cs.stanford.edu>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

authored by

Javier Nieto and committed by
Luiz Augusto von Dentz
97fdb2ea 01622e9a

+39 -4
+1
drivers/bluetooth/Kconfig
··· 188 188 bool "Three-wire UART (H5) protocol support" 189 189 depends on BT_HCIUART 190 190 depends on BT_HCIUART_SERDEV 191 + select CRC_CCITT 191 192 help 192 193 The HCI Three-wire UART Transport Layer makes it possible to 193 194 user the Bluetooth HCI over a serial port interface. The HCI
+38 -4
drivers/bluetooth/hci_h5.c
··· 7 7 */ 8 8 9 9 #include <linux/acpi.h> 10 + #include <linux/bitrev.h> 11 + #include <linux/crc-ccitt.h> 10 12 #include <linux/errno.h> 11 13 #include <linux/gpio/consumer.h> 12 14 #include <linux/kernel.h> ··· 60 58 H5_TX_ACK_REQ, /* Pending ack to send */ 61 59 H5_WAKEUP_DISABLE, /* Device cannot wake host */ 62 60 H5_HW_FLOW_CONTROL, /* Use HW flow control */ 61 + H5_CRC, /* Use CRC */ 63 62 }; 64 63 65 64 struct h5 { ··· 144 141 145 142 static u8 h5_cfg_field(struct h5 *h5) 146 143 { 147 - /* Sliding window size (first 3 bits) */ 148 - return h5->tx_win & 0x07; 144 + /* Sliding window size (first 3 bits) and CRC request (fifth bit). */ 145 + return (h5->tx_win & 0x07) | 0x10; 149 146 } 150 147 151 148 static void h5_timed_event(struct timer_list *t) ··· 364 361 h5_link_control(hu, conf_rsp, 2); 365 362 h5_link_control(hu, conf_req, 3); 366 363 } else if (memcmp(data, conf_rsp, 2) == 0) { 367 - if (H5_HDR_LEN(hdr) > 2) 364 + if (H5_HDR_LEN(hdr) > 2) { 368 365 h5->tx_win = (data[2] & 0x07); 366 + assign_bit(H5_CRC, &h5->flags, data[2] & 0x10); 367 + } 369 368 BT_DBG("Three-wire init complete. tx_win %u", h5->tx_win); 370 369 h5->state = H5_ACTIVE; 371 370 hci_uart_init_ready(hu); ··· 431 426 432 427 static int h5_rx_crc(struct hci_uart *hu, unsigned char c) 433 428 { 434 - h5_complete_rx_pkt(hu); 429 + struct h5 *h5 = hu->priv; 430 + const unsigned char *hdr = h5->rx_skb->data; 431 + u16 crc; 432 + __be16 crc_be; 433 + 434 + crc = crc_ccitt(0xffff, hdr, 4 + H5_HDR_LEN(hdr)); 435 + crc = bitrev16(crc); 436 + 437 + crc_be = cpu_to_be16(crc); 438 + 439 + if (memcmp(&crc_be, hdr + 4 + H5_HDR_LEN(hdr), 2) != 0) { 440 + bt_dev_err(hu->hdev, "Received packet with invalid CRC"); 441 + h5_reset_rx(h5); 442 + } else { 443 + /* Remove CRC bytes */ 444 + skb_trim(h5->rx_skb, 4 + H5_HDR_LEN(hdr)); 445 + h5_complete_rx_pkt(hu); 446 + } 435 447 436 448 return 0; 437 449 } ··· 579 557 h5->rx_func = h5_rx_delimiter; 580 558 h5->rx_pending = 0; 581 559 clear_bit(H5_RX_ESC, &h5->flags); 560 + clear_bit(H5_CRC, &h5->flags); 582 561 } 583 562 584 563 static int h5_recv(struct hci_uart *hu, const void *data, int count) ··· 710 687 struct h5 *h5 = hu->priv; 711 688 struct sk_buff *nskb; 712 689 u8 hdr[4]; 690 + u16 crc; 713 691 int i; 714 692 715 693 if (!valid_packet_type(pkt_type)) { ··· 738 714 /* Reliable packet? */ 739 715 if (pkt_type == HCI_ACLDATA_PKT || pkt_type == HCI_COMMAND_PKT) { 740 716 hdr[0] |= 1 << 7; 717 + hdr[0] |= (test_bit(H5_CRC, &h5->flags) && 1) << 6; 741 718 hdr[0] |= h5->tx_seq; 742 719 h5->tx_seq = (h5->tx_seq + 1) % 8; 743 720 } ··· 757 732 758 733 for (i = 0; i < len; i++) 759 734 h5_slip_one_byte(nskb, data[i]); 735 + 736 + if (H5_HDR_CRC(hdr)) { 737 + crc = crc_ccitt(0xffff, hdr, 4); 738 + crc = crc_ccitt(crc, data, len); 739 + crc = bitrev16(crc); 740 + 741 + h5_slip_one_byte(nskb, (crc >> 8) & 0xff); 742 + h5_slip_one_byte(nskb, crc & 0xff); 743 + } 760 744 761 745 h5_slip_delim(nskb); 762 746