hid_device: extract l2cap handling into classic/hid_device, send events

This commit is contained in:
Matthias Ringwald 2017-04-27 14:57:02 +02:00
parent dd148ddbc5
commit 8eb8d46326
3 changed files with 268 additions and 89 deletions

View File

@ -39,6 +39,8 @@
// *****************************************************************************
/* EXAMPLE_START(hid_device_demo): HID Device (Server) Demo
*
* Status: Basic implementation. HID Request from Host are not answered yet. Works with iOS.
*
* @text This HID Device example demonstrates how to implement
* an HID keyboard. Without a HAVE_POSIX_STDIN, a fixed demo text is sent
@ -56,19 +58,17 @@
#include "btstack.h"
#undef HAVE_POSIX_STDIN
#ifdef HAVE_POSIX_STDIN
#include "stdin_support.h"
#endif
uint8_t hid_service_buffer[250];
const char hid_device_name[] = "BTstack HID Keyboard";
static btstack_packet_callback_registration_t hci_event_callback_registration;
// to enable demo text on POSIX systems
// #undef HAVE_POSIX_STDIN
// hid device state
static uint16_t hid_control_cid;
static uint16_t hid_interrupt_cid;
static uint8_t hid_service_buffer[250];
static const char hid_device_name[] = "BTstack HID Keyboard";
static btstack_packet_callback_registration_t hci_event_callback_registration;
static uint16_t hid_cid;
// from USB HID Specification 1.1, Appendix B.1
const uint8_t hid_descriptor_keyboard_boot_mode[] = {
@ -204,34 +204,19 @@ static int send_modifier;
static void send_key(int modifier, int keycode){
send_keycode = keycode;
send_modifier = modifier;
l2cap_request_can_send_now_event(hid_interrupt_cid);
hid_device_request_can_send_now_event(hid_cid);
}
static void send_report(int modifier, int keycode){
uint8_t report[] = { 0xa1, modifier, 0, 0, keycode, 0, 0, 0, 0, 0};
l2cap_send(hid_interrupt_cid, &report[0], sizeof(report));
hid_device_send_interrupt_message(hid_cid, &report[0], sizeof(report));
}
static int hid_connected(void){
return hid_control_cid && hid_interrupt_cid;
}
// Demo Application
#ifdef HAVE_POSIX_STDIN
// prototypes
static void show_usage(void);
// Testig User Interface
static void show_usage(void){
bd_addr_t iut_address;
gap_local_bd_addr(iut_address);
printf("\n--- Bluetooth HID Device Test Console %s ---\n", bd_addr_to_str(iut_address));
printf("\n");
printf("---\n");
printf("Ctrl-c - exit\n");
printf("---\n");
}
// On systems with STDIN, we can directly type on the console
static void stdin_process(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type){
UNUSED(ds);
@ -246,7 +231,6 @@ static void stdin_process(btstack_data_source_t *ds, btstack_data_source_callbac
send_key(modifier, keycode);
return;
}
show_usage();
}
#else
@ -261,7 +245,7 @@ static btstack_timer_source_t typing_timer;
static void typing_timer_handler(btstack_timer_source_t * ts){
// abort if not connected
if (!hid_connected()) return;
if (!hid_cid) return;
// get next character
uint8_t character = demo_text[demo_pos++];
@ -282,7 +266,7 @@ static void typing_timer_handler(btstack_timer_source_t * ts){
btstack_run_loop_add_timer(ts);
}
static void hid_start_typing(void){
static void hid_embedded_start_typing(void){
demo_pos = 0;
// set one-shot timer
typing_timer.process = &typing_timer_handler;
@ -295,7 +279,6 @@ static void hid_start_typing(void){
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t packet_size){
UNUSED(channel);
UNUSED(packet_size);
int connected_before;
switch (packet_type){
case HCI_EVENT_PACKET:
switch (packet[0]){
@ -305,63 +288,36 @@ static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * pack
log_info("SSP User Confirmation Auto accept\n");
break;
// into HID Device/Server
case L2CAP_EVENT_INCOMING_CONNECTION:
switch (l2cap_event_incoming_connection_get_psm(packet)){
case PSM_HID_CONTROL:
case PSM_HID_INTERRUPT:
l2cap_accept_connection(channel);
break;
default:
l2cap_decline_connection(channel);
break;
}
break;
case L2CAP_EVENT_CHANNEL_OPENED:
if (packet[2]) return;
connected_before = hid_connected();
switch (l2cap_event_channel_opened_get_psm(packet)){
case PSM_HID_CONTROL:
hid_control_cid = l2cap_event_channel_opened_get_local_cid(packet);
log_info("HID Control opened, cid 0x%02x", hid_control_cid);
break;
case PSM_HID_INTERRUPT:
hid_interrupt_cid = l2cap_event_channel_opened_get_local_cid(packet);
log_info("HID Interrupt opened, cid 0x%02x", hid_interrupt_cid);
break;
default:
break;
}
if (!connected_before && hid_connected()){
printf("HID Connected\n");
#ifndef HAVE_POSIX_STDIN
hid_start_typing();
case HCI_EVENT_HID_META:
switch (hci_event_hid_meta_get_subevent_code(packet)){
case HID_SUBEVENT_CONNECTION_OPENED:
if (hid_subevent_connection_opened_get_status(packet)) return;
hid_cid = hid_subevent_connection_opened_get_hid_cid(packet);
#ifdef HAVE_POSIX_STDIN
printf("HID Connected, please start typing...\n");
#else
printf("HID Connected, sending demo text...\n");
hid_embedded_start_typing();
#endif
break;
case HID_SUBEVENT_CONNECTION_CLOSED:
printf("HID Disconnected\n");
hid_cid = 0;
break;
case HID_SUBEVENT_CAN_SEND_NOW:
if (send_keycode){
send_report(send_modifier, send_keycode);
send_keycode = 0;
send_modifier = 0;
hid_device_request_can_send_now_event(hid_cid);
} else {
send_report(0, 0);
}
break;
default:
break;
}
break;
case L2CAP_EVENT_CHANNEL_CLOSED:
connected_before = hid_connected();
if (l2cap_event_channel_closed_get_local_cid(packet) == hid_control_cid){
log_info("HID Control closed");
hid_control_cid = 0;
}
if (l2cap_event_channel_closed_get_local_cid(packet) == hid_interrupt_cid){
log_info("HID Interrupt closed");
hid_interrupt_cid = 0;
}
if (connected_before && !hid_connected()){
printf("HID Disconnected\n");
}
break;
case L2CAP_EVENT_CAN_SEND_NOW:
if (send_keycode){
send_report(send_modifier, send_keycode);
send_keycode = 0;
send_modifier = 0;
l2cap_request_can_send_now_event(hid_interrupt_cid);
} else {
send_report(0, 0);
}
default:
break;
}
@ -396,8 +352,6 @@ int btstack_main(int argc, const char * argv[]){
// L2CAP
l2cap_init();
l2cap_register_service(packet_handler, PSM_HID_INTERRUPT, 100, LEVEL_0);
l2cap_register_service(packet_handler, PSM_HID_CONTROL, 100, LEVEL_0);
// SDP Server
sdp_init();
@ -407,6 +361,10 @@ int btstack_main(int argc, const char * argv[]){
printf("SDP service record size: %u\n", de_get_len( hid_service_buffer));
sdp_register_service(hid_service_buffer);
// HID Device
hid_device_init();
hid_device_register_packet_handler(&packet_handler);
#ifdef HAVE_POSIX_STDIN
btstack_stdin_setup(stdin_process);
#endif

View File

@ -38,11 +38,29 @@
#define __BTSTACK_FILE__ "hid_device.c"
#include <string.h>
#include "classic/hid_device.h"
#include "classic/sdp_util.h"
#include "bluetooth.h"
#include "bluetooth_sdp.h"
#include "l2cap.h"
#include "btstack_event.h"
#include "btstack_debug.h"
// hid device state
typedef struct hid_device {
uint16_t cid;
bd_addr_t bd_addr;
hci_con_handle_t con_handle;
uint16_t control_cid;
uint16_t interrupt_cid;
uint8_t incoming;
} hid_device_t;
static hid_device_t _hid_device;
static hid_device_t * hid_device = &_hid_device;
static btstack_packet_handler_t hid_callback;
void hid_create_sdp_record(
uint8_t *service,
@ -167,4 +185,176 @@ void hid_create_sdp_record(
de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_HID_BOOT_DEVICE);
de_add_number(service, DE_BOOL, DE_SIZE_8, hid_boot_device);
}
static inline void hid_device_emit_connected_event(hid_device_t * context, uint8_t status){
uint8_t event[15];
int pos = 0;
event[pos++] = HCI_EVENT_HID_META;
pos++; // skip len
event[pos++] = HID_SUBEVENT_CONNECTION_OPENED;
little_endian_store_16(event,pos,context->cid);
pos+=2;
event[pos++] = status;
memcpy(&event[pos], context->bd_addr, 6);
pos += 6;
little_endian_store_16(event,pos,context->con_handle);
pos += 2;
event[pos++] = context->incoming;
event[1] = pos - 2;
if (pos != sizeof(event)) log_error("hid_device_emit_connected_event size %u", pos);
hid_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
}
static inline void hid_device_emit_connection_closed_event(hid_device_t * context){
uint8_t event[5];
int pos = 0;
event[pos++] = HCI_EVENT_HID_META;
pos++; // skip len
event[pos++] = HID_SUBEVENT_CONNECTION_CLOSED;
little_endian_store_16(event,pos,context->cid);
pos+=2;
event[1] = pos - 2;
if (pos != sizeof(event)) log_error("hid_device_emit_connection_closed_event size %u", pos);
hid_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
}
static inline void hid_device_emit_can_send_now_event(hid_device_t * context){
uint8_t event[5];
int pos = 0;
event[pos++] = HCI_EVENT_HID_META;
pos++; // skip len
event[pos++] = HID_SUBEVENT_CAN_SEND_NOW;
little_endian_store_16(event,pos,context->cid);
pos+=2;
event[1] = pos - 2;
if (pos != sizeof(event)) log_error("hid_device_emit_can_send_now_event size %u", pos);
hid_callback(HCI_EVENT_PACKET, context->cid, &event[0], pos);
}
static int hid_connected(void){
return hid_device->control_cid && hid_device->interrupt_cid;
}
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * packet, uint16_t packet_size){
UNUSED(channel);
UNUSED(packet_size);
int connected_before;
switch (packet_type){
case HCI_EVENT_PACKET:
switch (packet[0]){
case L2CAP_EVENT_INCOMING_CONNECTION:
switch (l2cap_event_incoming_connection_get_psm(packet)){
case PSM_HID_CONTROL:
case PSM_HID_INTERRUPT:
if (hid_device->con_handle == 0 || l2cap_event_incoming_connection_get_handle(packet) == hid_device->con_handle){
hid_device->con_handle = l2cap_event_incoming_connection_get_handle(packet);
l2cap_accept_connection(channel);
} else {
l2cap_decline_connection(channel);
}
break;
default:
l2cap_decline_connection(channel);
break;
}
break;
case L2CAP_EVENT_CHANNEL_OPENED:
if (l2cap_event_channel_opened_get_status(packet)) return;
connected_before = hid_connected();
switch (l2cap_event_channel_opened_get_psm(packet)){
case PSM_HID_CONTROL:
hid_device->control_cid = l2cap_event_channel_opened_get_local_cid(packet);
log_info("HID Control opened, cid 0x%02x", hid_device->control_cid);
break;
case PSM_HID_INTERRUPT:
hid_device->interrupt_cid = l2cap_event_channel_opened_get_local_cid(packet);
log_info("HID Interrupt opened, cid 0x%02x", hid_device->interrupt_cid);
break;
default:
break;
}
if (!connected_before && hid_connected()){
hid_device->incoming = 1;
log_info("HID Connected");
hid_device_emit_connected_event(hid_device, 0);
}
break;
case L2CAP_EVENT_CHANNEL_CLOSED:
connected_before = hid_connected();
if (l2cap_event_channel_closed_get_local_cid(packet) == hid_device->control_cid){
log_info("HID Control closed");
hid_device->control_cid = 0;
}
if (l2cap_event_channel_closed_get_local_cid(packet) == hid_device->interrupt_cid){
log_info("HID Interrupt closed");
hid_device->interrupt_cid = 0;
}
if (connected_before && !hid_connected()){
hid_device->con_handle = 0;
log_info("HID Disconnected");
hid_device_emit_connection_closed_event(hid_device);
}
break;
case L2CAP_EVENT_CAN_SEND_NOW:
log_info("HID Can send now, emit event");
hid_device_emit_can_send_now_event(hid_device);
break;
default:
break;
}
break;
default:
break;
}
}
/**
* @brief Set up HID Device
*/
void hid_device_init(void){
memset(hid_device, 0, sizeof(hid_device_t));
hid_device->cid = 1;
l2cap_register_service(packet_handler, PSM_HID_INTERRUPT, 100, LEVEL_0);
l2cap_register_service(packet_handler, PSM_HID_CONTROL, 100, LEVEL_0);
}
/**
* @brief Register callback for the HID Device client.
* @param callback
*/
void hid_device_register_packet_handler(btstack_packet_handler_t callback){
hid_callback = callback;
}
/**
* @brief Request can send now event to send HID Report
* @param hid_cid
*/
void hid_device_request_can_send_now_event(uint16_t hid_cid){
UNUSED(hid_cid);
if (!hid_device->control_cid) return;
l2cap_request_can_send_now_event(hid_device->control_cid);
}
/**
* @brief Send HID messageon interrupt channel
* @param hid_cid
*/
void hid_device_send_interrupt_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){
UNUSED(hid_cid);
if (!hid_device->interrupt_cid) return;
l2cap_send(hid_device->interrupt_cid, (uint8_t*) message, message_len);
}
/**
* @brief Send HID messageon control channel
* @param hid_cid
*/
void hid_device_send_contro_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len){
UNUSED(hid_cid);
if (!hid_device->control_cid) return;
l2cap_send(hid_device->control_cid, (uint8_t*) message, message_len);
}

View File

@ -36,7 +36,7 @@
*/
#include <stdint.h>
#include "btstack_defines.h"
/**
* @brief Create HID Device SDP service record.
* @param service Empty buffer in which a new service record will be stored.
@ -64,3 +64,34 @@ void hid_create_sdp_record(
uint16_t hid_descriptor_size,
const char * device_name);
/**
* @brief Set up HID Device
*/
void hid_device_init(void);
/**
* @brief Register callback for the HID Device client.
* @param callback
*/
void hid_device_register_packet_handler(btstack_packet_handler_t callback);
/**
* @brief Request can send now event to send HID Report
* Generates an HID_SUBEVENT_CAN_SEND_NOW subevent
* @param hid_cid
*/
void hid_device_request_can_send_now_event(uint16_t hid_cid);
/**
* @brief Send HID messageon interrupt channel
* @param hid_cid
*/
void hid_device_send_interrupt_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len);
/**
* @brief Send HID messageon control channel
* @param hid_cid
*/
void hid_device_send_contro_message(uint16_t hid_cid, const uint8_t * message, uint16_t message_len);