]> git.karo-electronics.de Git - oswald.git/blobdiff - metawatch/bt_hci.c
Here we are! MetaWatch support in Oswald!
[oswald.git] / metawatch / bt_hci.c
diff --git a/metawatch/bt_hci.c b/metawatch/bt_hci.c
new file mode 100644 (file)
index 0000000..265b00c
--- /dev/null
@@ -0,0 +1,477 @@
+#include <msp430.h>
+#include <msp430xgeneric.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "mw_main.h"
+
+#include "mw_uart.h"
+#include "mw_bt.h"
+#include "bt_hci.h"
+#include "bt_l2cap.h"
+
+
+static uint8_t local_bdaddr[6];
+
+uint8_t *bt_hci_get_local_bdaddr(void)
+{
+       return local_bdaddr;
+}
+
+static void bt_print_bd_addr(uint8_t *bd_addr)
+{
+#if defined MW_DEVBOARD_V2
+       int i;
+       char nums[4];
+
+       for (i=5; i>=0; i--) {
+               snprintf(nums, 4, "%02x", bd_addr[i]);
+               debug_uart_tx(nums);
+               if (i>0)
+                       debug_uart_tx_char(':');
+       }
+#endif
+}
+
+void bt_hci_process_acl_packet(unsigned char *packet)
+{
+#if defined MW_DEVBOARD_V2
+       char tstr[32];
+#endif
+       uint16_t dlen;
+       uint16_t handle;
+       L2CAP_PB_FLAG PB;
+       L2CAP_BC_FLAG BC;
+       uint16_t channel;
+       uint16_t mlen;
+
+//     debug_uart_tx("ACL packet, ");
+       handle = packet[0] | ((packet[1] & 0x0f) << 8);
+//     snprintf(tstr, 32, "handle 0x%04x ", handle);
+//     debug_uart_tx(tstr);
+       PB = (packet[1] >> 4) & 0x03;
+       BC = (packet[1] >> 6) & 0x03;
+//     snprintf(tstr, 32, "PB 0x%02x BC 0x%02x ", PB, BC);
+//     debug_uart_tx(tstr);
+
+       dlen = packet[2] | ((uint16_t)packet[3] << 8);
+//     snprintf(tstr, 32, "len 0x%04x \n", dlen);
+//     debug_uart_tx(tstr);
+//     debug_dump_hex(dlen+4, packet);
+
+       channel = packet[4+2] | (packet[4+3] << 8);
+       mlen = packet[4] | (packet[4+1] << 8);
+       switch (channel) {
+               case L2CAP_CID_SIGNALING:
+                       bt_l2cap_proc_signalling(handle, (packet+8), mlen);
+                       break;
+               case L2CAP_CID_CONNECTIONLESS_CHANNEL:
+                       break;
+               case L2CAP_CID_ATTRIBUTE_PROTOCOL:
+                       break;
+               case L2CAP_CID_SIGNALING_LE:
+                       break;
+               case L2CAP_CID_SECURITY_MANAGER_PROTOCOL:
+                       break;
+               default:
+                       if (channel > 0x3f) {
+                               // just for sure, add a 0 as string delimiter
+                               packet[mlen+8] = 0x00;
+                               bt_l2cap_proc_dyn_channel(channel, handle, (packet+8), mlen);
+                       } else {
+#if defined MW_DEVBOARD_V2
+                               snprintf(tstr, 32, "L2CAP unhandled CID 0x%04x\n", channel);
+                               debug_uart_tx(tstr);
+#endif
+                       }
+                       break;
+       }
+}
+
+void bt_hci_process_event_packet(unsigned char *packet)
+{
+#if defined MW_DEVBOARD_V2
+       char tstr[32];
+#endif
+       int i;
+       // uint8_t bd_addr[6];
+       uint32_t dev_type;
+
+//     debug_uart_tx("Event packet, ");
+//     snprintf(tstr, 32, "evt 0x%02x: ", packet[0]);
+//     debug_uart_tx(tstr);
+
+       switch (packet[0]) {
+               case HCI_EVENT_INQUIRY_COMPLETE:
+                       debug_uart_tx("inq complete\n");
+                       break;
+               case HCI_EVENT_INQUIRY_RESULT:
+                       debug_uart_tx("inq result\n");
+                       break;
+               case HCI_EVENT_CONNECTION_COMPLETE:
+                       debug_uart_tx("con complete from ");
+#if defined MW_DEVBOARD_V2
+                       bt_print_bd_addr((packet+5));
+                       snprintf(tstr, 32, " status 0x%02x handle 0x%02x", packet[2], packet[3]);
+                       debug_uart_tx(tstr);
+                       snprintf(tstr, 32, " type 0x%02x enc 0x%02x\n", packet[11], packet[12]);
+                       debug_uart_tx(tstr);
+                       if (packet[2] == 0x00)
+                               debug_uart_tx("connection established\n");
+                       else
+                               debug_uart_tx("connection failed\n");
+#endif
+                       break;
+               case HCI_EVENT_CONNECTION_REQUEST: {
+                       uint8_t bd_addr[7];
+
+                       switch (packet[11]) {
+                               case HCI_LINK_TYPE_SCO:
+                                       debug_uart_tx("SCO");
+                                       break;
+                               case HCI_LINK_TYPE_ACL:
+                                       debug_uart_tx("ACL");
+                                       break;
+                               case HCI_LINK_TYPE_ESCO:
+                                       debug_uart_tx("eSCO");
+                                       break;
+                               default:
+                                       debug_uart_tx("unknown type");
+                                       break;
+                       }
+                       debug_uart_tx(" con req from ");
+                       for (i=0; i<6; i++) {
+                               bd_addr[i] = packet[i+2];
+                       }
+                       bt_print_bd_addr(bd_addr);
+                       dev_type = (uint32_t)packet[8] << 16;
+                       dev_type |= (uint32_t)packet[9] << 8;
+                       dev_type |= packet[10];
+#if defined MW_DEVBOARD_V2
+                       snprintf(tstr, 32, " rem. dtype 0x%06lx\n", dev_type);
+                       debug_uart_tx(tstr);
+#endif
+                       //memcpy(tstr, bd_addr, 6);
+                       //tstr[6] = 0x01; /* remain slave */
+                       bd_addr[6] = 0x01; /* remain slave */
+                       bt_hci_cmd(HCI_LINK_CTRL_OGF, HCI_ACCEPT_CONN_REQ_OCF, 7, bd_addr);
+                       } break;
+               case HCI_EVENT_DISCONNECTION_COMPLETE:
+                       debug_uart_tx("discon complete\n");
+                       break;
+               case HCI_EVENT_AUTHENTICATION_COMPLETE_EVENT:
+                       debug_uart_tx("auth complete\n");
+                       break;
+               case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE:
+                       debug_uart_tx("rem name req complete\n");
+                       break;
+               case HCI_EVENT_ENCRYPTION_CHANGE:
+                       debug_uart_tx("enc change\n");
+                       break;
+               case HCI_EVENT_CHANGE_CONNECTION_LINK_KEY_COMPLETE:
+                       debug_uart_tx("change con link key complete\n");
+                       break;
+               case HCI_EVENT_MASTER_LINK_KEY_COMPLETE:
+                       debug_uart_tx("master link key complete\n");
+                       break;
+               case HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE:
+                       debug_uart_tx("read rem feat. complete\n");
+                       break;
+               case HCI_EVENT_READ_REMOTE_VERSION_INFORMATION_COMPLETE:
+                       debug_uart_tx("read rem version complete\n");
+                       break;
+               case HCI_EVENT_QOS_SETUP_COMPLETE:
+                       debug_uart_tx("qos setup complete\n");
+                       break;
+               case HCI_EVENT_COMMAND_COMPLETE:
+                       // snprintf(tstr, 32, "%d cmd(s) complete: 0x%02x%02x=%d", packet[2], packet[3], packet[4], packet[5]);
+                       // debug_uart_tx(tstr);
+                       if (packet[2] > 0 &&
+                           packet[3] == ((HCI_R_BD_ADDR_OCF | (HCI_INFO_PARAM_OGF << 10)) & 0xff) &&
+                           packet[4] == (((HCI_R_BD_ADDR_OCF | (HCI_INFO_PARAM_OGF << 10)) & 0xff00) >> 8)) { // read local bdaddr
+                               memcpy(local_bdaddr, (packet+6), 6);
+#if defined MW_DEVBOARD_V2
+                               debug_uart_tx("local bdaddr = ");
+                               bt_print_bd_addr((uint8_t *)(packet+6));
+                               debug_uart_tx("\n");
+#endif
+                       }
+                       break;
+               case HCI_EVENT_COMMAND_STATUS:
+                       debug_uart_tx("cmd status\n");
+                       break;
+               case HCI_EVENT_HARDWARE_ERROR:
+#if defined MW_DEVBOARD_V2
+                       debug_uart_tx("hardw err");
+                       snprintf(tstr, 32, " 0x%02x\n", packet[2]);
+                       debug_uart_tx(tstr);
+#endif
+                       break;
+               case HCI_EVENT_FLUSH_OCCURED:
+                       debug_uart_tx("flush occured\n");
+                       break;
+               case HCI_EVENT_ROLE_CHANGE:
+                       debug_uart_tx("role change\n");
+                       break;
+               case HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS:
+                       debug_uart_tx("numb compl. packets\n");
+                       break;
+               case HCI_EVENT_MODE_CHANGE_EVENT:
+                       debug_uart_tx("mode change\n");
+                       break;
+               case HCI_EVENT_RETURN_LINK_KEYS:
+                       debug_uart_tx("return link keys\n");
+                       break;
+               case HCI_EVENT_PIN_CODE_REQUEST:
+                       debug_uart_tx("pin code request\n");
+#if defined MW_DEVBOARD_V2
+                       debug_uart_tx("from ");
+                       bt_print_bd_addr((uint8_t *)(packet+2));
+                       debug_uart_tx("\n");
+#endif
+                       memmove(packet, (packet+2), 6);
+                       packet[6] = 0x04; // PIN has length of 4
+                       packet[7] = '4';
+                       packet[8] = '3';
+                       packet[9] = '1';
+                       packet[10] = '2';
+                       bt_hci_cmd(HCI_LINK_CTRL_OGF, HCI_PIN_CODE_REQ_REP_OCF, 11, packet);
+                       break;
+               case HCI_EVENT_LINK_KEY_REQUEST:
+                       debug_uart_tx("link key request\n");
+                       break;
+               case HCI_EVENT_LINK_KEY_NOTIFICATION:
+                       debug_uart_tx("link key notify\n");
+                       break;
+               case HCI_EVENT_DATA_BUFFER_OVERFLOW:
+                       debug_uart_tx("evt data buf overflow\n");
+                       break;
+               case HCI_EVENT_MAX_SLOTS_CHANGED:
+                       debug_uart_tx("max slots changed\n");
+                       break;
+               case HCI_EVENT_READ_CLOCK_OFFSET_COMPLETE:
+                       debug_uart_tx("read clock offset compl.\n");
+                       break;
+               case HCI_EVENT_PACKET_TYPE_CHANGED:
+                       debug_uart_tx("packet type changed\n");
+                       break;
+               case HCI_EVENT_PAGE_SCAN_REPETION_MODE_CHANGE:
+                       debug_uart_tx("page scan repetition mode changed\n");
+                       break;
+               case HCI_EVENT_INQUIRY_RESULT_WITH_RSSI:
+                       debug_uart_tx("inq result with RSSI\n");
+                       break;
+               case HCI_EVENT_EXTENDED_INQUIRY_RESPONSE:
+                       debug_uart_tx("ext. inq. resp.\n");
+                       break;
+               case HCI_EVENT_LE_META:
+                       debug_uart_tx("LE meta\n");
+                       break;
+               case HCI_EVENT_VENDOR_SPECIFIC:
+                       debug_uart_tx("vend. spec.\n");
+                       break;
+               default:
+                       debug_uart_tx("unknown\n");
+                       break;
+       }
+//     debug_uart_tx("\n");
+//     for (i=2; i<=(packet[1]+1); i++) {
+//             snprintf(tstr, 32, " 0x%02x", packet[i]);
+//             debug_uart_tx(tstr);
+//     }
+//     debug_uart_tx("\n");
+}
+
+typedef enum {
+       HCI_PACKET_START = 0,
+       HCI_CMD_PACKET,
+       HCI_ACL_PACKET_HEADER,
+       HCI_ACL_PACKET_DATA,
+       HCI_SCO_PACKET,
+       HCI_EVENT_PACKET_HEADER,
+       HCI_EVENT_PACKET_DATA,
+       HCI_EHCILL_PACKET,
+       EHCILL_SLEEPING
+} bt_hci_state_t;
+
+static bt_hci_state_t state = HCI_PACKET_START;
+
+unsigned char bt_feed_packet_data(unsigned char pdata)
+{
+       char tstr[32];
+       static unsigned char packet[255];
+       static uint16_t bytes_left = 0;
+       static uint16_t pdata_pos = 0;
+
+//     snprintf(tstr, 32, "bt 0x%02x ", pdata);
+//     debug_uart_tx(tstr);
+       switch (state) {
+               case HCI_PACKET_START:
+                       switch (pdata) {
+                               case HCI_EVENT_PACKET:
+                                       state = HCI_EVENT_PACKET_HEADER;
+                                       bytes_left = 1;
+                                       pdata_pos = 0;
+                                       memset(packet, 0, 64);
+                                       break;
+                               case HCI_ACL_DATA_PACKET:
+                                       state = HCI_ACL_PACKET_HEADER;
+                                       bytes_left = 3;
+                                       pdata_pos = 0;
+                                       memset(packet, 0, 64);
+                                       break;
+                               case EHCILL_GO_TO_SLEEP_IND:
+                                       debug_uart_tx("eHCILL go to sleep ind\n");
+                                       state = HCI_PACKET_START;
+                                       // disable BT UART?
+                                       // mabye UCA1CTL1 = UCSWRST ?
+                                       pdata = EHCILL_GO_TO_SLEEP_ACK;
+                                       mw_bt_uart_tx(&pdata, 0x01);
+                                       BT_IO_POUT |= BT_IO_RTS; // pull RTS -> stop data
+
+                                       P1IFG &= ~BT_IO_CTS;
+                                       P1IE |= BT_IO_CTS;
+
+                                       state = EHCILL_SLEEPING;
+                                       break;
+                               case EHCILL_GO_TO_SLEEP_ACK:
+                                       debug_uart_tx("eHCILL go to sleep ack\n");
+                                       state = HCI_PACKET_START;
+                                       break;
+                               case EHCILL_WAKE_UP_IND:
+                                       debug_uart_tx("eHCILL wake up ind\n");
+                                       state = HCI_PACKET_START;
+                                       break;
+                               case EHCILL_WAKE_UP_ACK:
+                                       debug_uart_tx("eHCILL wake up ack\n");
+                                       state = HCI_PACKET_START;
+                                       break;
+                               default:
+                                       debug_uart_tx("unexpected packet start\n");
+                                       break;
+                       }
+                       break;
+               case HCI_EVENT_PACKET_HEADER:
+                       if (bytes_left != 0) {
+                               packet[pdata_pos++] = pdata;
+                               bytes_left--;
+                       } else {
+                               state = HCI_EVENT_PACKET_DATA;
+                               packet[pdata_pos++] = pdata;
+                               bytes_left = pdata;
+                       }
+                       break;
+               case HCI_EVENT_PACKET_DATA:
+                       packet[pdata_pos++] = pdata;
+                       bytes_left--;
+                       if (bytes_left == 0) {
+                               state = HCI_PACKET_START;
+                               bt_hci_process_event_packet(packet);
+                       }
+                       break;
+               case HCI_ACL_PACKET_HEADER:
+                       if (bytes_left != 0) {
+                               packet[pdata_pos++] = pdata;
+                               bytes_left--;
+                       } else {
+                               state = HCI_ACL_PACKET_DATA;
+                               packet[pdata_pos] = pdata;
+                               bytes_left = (packet[pdata_pos-1] | (packet[pdata_pos] << 8));
+//                             snprintf(tstr, 32, "ACL data len 0x%04x\n", bytes_left);
+//                             debug_uart_tx(tstr);
+//                             snprintf(tstr, 32, "%d (0x%02x 0x%02x)\n", pdata_pos, packet[pdata_pos-1], packet[pdata_pos]);
+//                             debug_uart_tx(tstr);
+                               pdata_pos++;
+                       }
+                       break;
+               case HCI_ACL_PACKET_DATA:
+//                     snprintf(tstr, 32, "%02x ", pdata);
+//                     debug_uart_tx(tstr);
+                       packet[pdata_pos++] = pdata;
+                       bytes_left--;
+                       if (bytes_left == 0) {
+//                             debug_uart_tx("\n");
+                               state = HCI_PACKET_START;
+                               bt_hci_process_acl_packet(packet);
+                       }
+                       break;
+               default:
+                       debug_uart_tx("hosed HCI state!\n");
+                       snprintf(tstr, 32, "  state = %d\n", state);
+                       debug_uart_tx(tstr);
+                       break;
+       };
+
+       // one byte consumed
+       return 1;
+}
+
+typedef struct {
+       uint8_t type;
+       uint16_t cmd;
+       uint8_t length;
+} __attribute__((packed)) bt_hci_cmd_t;
+
+void bt_hci_cmd(const uint8_t OGF, const uint8_t OCF, const uint8_t data_len, const void *data)
+{
+       bt_hci_cmd_t packet;
+
+       if (state == EHCILL_SLEEPING) {
+               uint8_t ehcill_p = EHCILL_WAKE_UP_IND;
+
+               debug_uart_tx("HCILL wakeup\n");
+               state = HCI_PACKET_START;
+               mw_bt_uart_tx(&ehcill_p, 1);
+               __delay_cycles(300000);
+               BT_IO_POUT &= ~BT_IO_RTS; // drop RTS -> start data
+       }
+       packet.type = HCI_COMMAND_PACKET;
+       packet.cmd = OCF | (OGF<<10);
+       packet.length = data_len;
+       mw_bt_uart_tx(&packet, sizeof(bt_hci_cmd_t));
+       if (data_len > 0 && data != NULL)
+               mw_bt_uart_tx(data, data_len);
+}
+
+typedef struct {
+       uint8_t type;
+       uint16_t handle;
+       uint16_t total_length;
+       uint16_t data_length;
+       uint16_t channel;
+} __attribute__((packed)) bt_hci_acl_t;
+
+void bt_acl_send(const uint16_t handle, const uint8_t PB, const uint8_t BC, const uint16_t channel, const uint16_t dlen, const void *dat)
+{
+       bt_hci_acl_t packet;
+
+       packet.type = HCI_ACL_DATA_PACKET;
+       packet.handle = handle | ((PB & 0x03) << 12) | ((BC & 0x03) << 14);
+       packet.total_length = dlen + 4;
+       packet.data_length = dlen;
+       packet.channel = channel;
+       mw_bt_uart_tx(&packet, sizeof(bt_hci_acl_t));
+       mw_bt_uart_tx(dat, dlen);
+}
+
+void bt_hci_init(void)
+{
+       state = HCI_PACKET_START;
+}
+
+void bt_hci_ehcill_wake(void)
+{
+       uint8_t ehcill_p = EHCILL_WAKE_UP_ACK;
+
+       debug_uart_tx("HCILL wakeup\n");
+
+       P1IE &= ~BT_IO_CTS;
+       state = HCI_PACKET_START;
+
+       BT_IO_POUT &= ~BT_IO_RTS; // drop RTS -> start data
+
+       mw_bt_uart_tx(&ehcill_p, 1);
+       //__delay_cycles(160000);
+}
+