mirror of
https://github.com/bluekitchen/btstack.git
synced 2025-03-31 19:20:26 +00:00
hid_device: extract l2cap handling into classic/hid_device, send events
This commit is contained in:
parent
dd148ddbc5
commit
8eb8d46326
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user