2013年9月22日 星期日

Client HID of Bluez for Bluetooth Qualification

Download client HID sample code
http://anselm.hoffmeister.be/computer/hidclient/hidclient-20120728.tar.bz2

HID report descriptor sample
http://www.instructables.com/id/USB-Wii-Classic-Controller/step13/Bonus-Keyboard-and-Mouse

Register record to SDP sample
http://blog.csdn.net/huipengzhao/article/details/18268201

1.
//////////////////////// close host hid service ////////////////////////
###undefined those lines in (a.) and (b.)###
(a.)
gedit /mnt/projects/victor_mnt/myandroid/external/bluetooth/bluez/android/client/halbt.c

const struct interface *interfaces[] =
{
//    &audio_if,    // class will show 0x0800000
    &bluetooth_if,
//    &av_if,
    &rc_if,
    &gatt_if,
    &gatt_client_if,
    &gatt_server_if,
//    &hf_if,        // disable handsfree
//    &hh_if,       // disable hidhost
//    &pan_if,    // class will show 0x0200000
    &sock_if,
    NULL
};
...
...
...
static void init(void)
{
    static const char * const inames[] =
    {
#if 0
        BT_PROFILE_HANDSFREE_ID,
        BT_PROFILE_ADVANCED_AUDIO_ID,
        BT_PROFILE_AV_RC_ID,
        BT_PROFILE_HEALTH_ID,
        BT_PROFILE_HIDHOST_ID,
        BT_PROFILE_PAN_ID,
#endif
        BT_PROFILE_HEALTH_ID,
        BT_PROFILE_GATT_ID,
        BT_PROFILE_SOCKETS_ID
    };
    const struct method *m;
    const char *argv[4];
    char init_audio[] = "audio init";
    char init_bt[] = "bluetooth init";
    uint32_t i;

    process_line(init_audio);
    process_line(init_bt);

    m = get_interface_method("bluetooth", "get_profile_interface");

    for (i = 0; i < NELEM(inames); ++i) {
        argv[2] = inames[i];
        m->func(3, argv);
    }

    /* Init what is available to init */
    for (i = 2; i < NELEM(interfaces) - 1; ++i)
    {
        m = get_interface_method(interfaces[i]->name, "init");
        if (m != NULL)
            m->func(2, argv);
    }
}

(b.)
gedit /mnt/projects/victor_mnt/myandroid/external/bluetooth/bluez/android/hal-bluetooth.c

static const void *get_profile_interface(const char *profile_id)
{
    DBG("%s", profile_id);

    if (!interface_ready())
        return NULL;

    if (!strcmp(profile_id, BT_PROFILE_SOCKETS_ID))
        return bt_get_sock_interface();

#if 0 // disable we don't need socket server.
    if (!strcmp(profile_id, BT_PROFILE_HIDHOST_ID))
        return bt_get_hidhost_interface();

    if (!strcmp(profile_id, BT_PROFILE_PAN_ID))
        return bt_get_pan_interface();

    if (!strcmp(profile_id, BT_PROFILE_ADVANCED_AUDIO_ID))
        return bt_get_a2dp_interface();

    if (!strcmp(profile_id, BT_PROFILE_AV_RC_ID))
        return bt_get_avrcp_interface();

    if (!strcmp(profile_id, BT_PROFILE_HANDSFREE_ID))
        return bt_get_handsfree_interface();
#endif
    return NULL;
}

2.
//////////////////////// Register record to client SDP ////////////////////////
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <bluetooth/sdp.h>
#include <sdp_lib.h>

#define    L2CAP_PSM_HIDP_CTRL    0x11
#define    L2CAP_PSM_HIDP_INTR    0x13

#define    HIDINFO_NAME    "Bluetooth HID Test"
#define    HIDINFO_PROV    "Huipeng Zhao"
#define    HIDINFO_DESC    "Test Bluetooth HID"

static unsigned char s_hid_report_desc_keyboard[] =
{
    0x05, 0x8c,                  // USAGE_PAGE (barcode scanner page)
    0x09, 0x02,                 // USAGE (barcode scanner device)
    0xa1, 0x01,                 // COLLECTION (application)
    0x09, 0x12,                 // USAGE (scanned data report)
    0xa1, 0x02,                 // COLLECTION (Logical)
    0x85, 0x01,                   // REPORT_ID (1)
    0x15, 0x00,                 // LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,         // LOGICAL_MAXIMUM (255)
    0x75, 0x08,                 // REPORT_SIZE (8)
    0x95, 0x03,                 // REPORT_COUNT (3)
    0x09, 0xfb,                     //usage symbology 1
    0x09, 0xfc,                     //usage symbology 2
    0x09, 0xfd,                     //usage symbology 3
    0x81, 0x02,                     //input data var abs
    0x95, 0x20,                     //count 32 bytes
    0x09, 0xfe,                     //usage decoded data
    0x82, 0x02, 0x01,         //input data var abs buf
    0x25, 0x01,                     //logical max,
    0x75, 0x08,                  // REPORT_SIZE (8)
    0x95, 0x01,                  // REPORT_COUNT (1)
    0x09, 0xff,                     //usage data continued
    0x81, 0x02,                     //input data var abs
    0xc0,                            //    END COLLECTION
    0xc0,                            //    END COLLECTION

    0x05, 0x01,   // USAGE_PAGE (Generic Desktop)
    0x09, 0x06,   // USAGE (Keyboard)
    0xa1, 0x01,   // COLLECTION (Application)
    0x85, 0x02,   //   REPORT_ID (2)
    0x05, 0x07,   //   USAGE_PAGE (Keyboard)
    0x19, 0xe0,   //   USAGE_MINIMUM (Keyboard LeftControl)
    0x29, 0xe7,   //   USAGE_MAXIMUM (Keyboard Right GUI)
    0x15, 0x00,   //   LOGICAL_MINIMUM (0)
    0x25, 0x01,   //   LOGICAL_MAXIMUM (1)
    0x75, 0x01,   //   REPORT_SIZE (1)
    0x95, 0x08,   //   REPORT_COUNT (8)
    0x81, 0x02,   //   INPUT (Data,Var,Abs)
    0x95, 0x01,   //   REPORT_COUNT (1)
    0x75, 0x08,   //   REPORT_SIZE (8)
    0x81, 0x03,   //   INPUT (Cnst,Var,Abs)
    0x95, 0x05,   //   REPORT_COUNT (5)
    0x75, 0x01,   //   REPORT_SIZE (1)
    0x05, 0x08,   //   USAGE_PAGE (LEDs)
    0x19, 0x01,   //   USAGE_MINIMUM (Num Lock)
    0x29, 0x05,   //   USAGE_MAXIMUM (Kana)
    0x91, 0x02,   //   OUTPUT (Data,Var,Abs)
    0x95, 0x01,   //   REPORT_COUNT (1)
    0x75, 0x03,   //   REPORT_SIZE (3)
    0x91, 0x03,   //   OUTPUT (Cnst,Var,Abs)
    0x95, 0x06,   //   REPORT_COUNT (6)
    0x75, 0x08,   //   REPORT_SIZE (8)
    0x15, 0x00,   //   LOGICAL_MINIMUM (0)
    0x25, 0x65,   //   LOGICAL_MAXIMUM (101)
    0x05, 0x07,   //   USAGE_PAGE (Keyboard)
    0x19, 0x00,   //   USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0x65,   //   USAGE_MAXIMUM (Keyboard Application)
    0x81, 0x00,   //   INPUT (Data,Ary,Abs)
    0xc0,         // END_COLLECTION

    /*0x05, 0x01,       // USAGE_PAGE (Generic Desktop)
    0x09, 0x02,       // USAGE (Mouse)
    0xa1, 0x01,       // COLLECTION (Application)
    0x09, 0x01,       //   USAGE (Pointer)
    0xa1, 0x00,       //   COLLECTION (Physical)
    0x85, 0x03,       //   REPORT_ID (3)
    0x05, 0x09,       //     USAGE_PAGE (Button)
    0x19, 0x01,       //     USAGE_MINIMUM (Button 1)
    0x29, 0x03,       //     USAGE_MAXIMUM (Button 3)
    0x15, 0x00,       //     LOGICAL_MINIMUM (0)
    0x25, 0x01,       //     LOGICAL_MAXIMUM (1)
    0x95, 0x03,       //     REPORT_COUNT (3)
    0x75, 0x01,       //     REPORT_SIZE (1)
    0x81, 0x02,       //     INPUT (Data,Var,Abs)
    0x95, 0x01,       //     REPORT_COUNT (1)
    0x75, 0x05,       //     REPORT_SIZE (5)
    0x81, 0x03,       //     INPUT (Cnst,Var,Abs)
    0x05, 0x01,       //     USAGE_PAGE (Generic Desktop)
    0x09, 0x30,       //     USAGE (X)
    0x09, 0x31,       //     USAGE (Y)
    0x09, 0x38,       //     USAGE (Wheel)
    0x15, 0x81,       //     LOGICAL_MINIMUM (-127)
    0x25, 0x7f,       //     LOGICAL_MAXIMUM (127)
    0x75, 0x08,       //     REPORT_SIZE (8)
    0x95, 0x03,       //     REPORT_COUNT (3)
    0x81, 0x06,       //     INPUT (Data,Var,Rel)
    0x05, 0x0c,       //     USAGE_PAGE (Consumer Devices)
    0x0a, 0x38, 0x02, //     USAGE (Undefined)
    0x95, 0x01,       //     REPORT_COUNT (1)
    0x81, 0x06,       //     INPUT (Data,Var,Rel)
    0xc0,          //   END_COLLECTION
    0xc0,          // END_COLLECTION*/
};

struct hidc_info_t
{
    char *       service_name;
    char *       description;
    char *       provider;
    char *       desc_data; //USB HID Report Descriptor data
    unsigned int desc_len;
    unsigned int sub_class;
};

struct hidc_info_t s_hidc_info =
{
    .service_name = HIDINFO_NAME,
    .description  = HIDINFO_DESC,
    .provider     = HIDINFO_PROV,
    .desc_data    = (char*)&s_hid_report_desc_keyboard,
    .desc_len     = sizeof(s_hid_report_desc_keyboard),
    .sub_class    = 0x80,
};

static void add_lang_attr(sdp_record_t *r)
{
    sdp_lang_attr_t base_lang;
    sdp_list_t *langs = 0;
    /* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
    base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
    base_lang.encoding = 106;
    base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
    langs = sdp_list_append(0, &base_lang);
    sdp_set_lang_attr(r, langs);
    sdp_list_free(langs, 0);
}

//==============================================================================
// Sample code start
//==============================================================================
//ATTR 0x0001
static void Inno_sdp_set_ServiceClassIDList(sdp_record_t *record) {
    uuid_t hidsvcls_uuid;
    sdp_list_t *svclass_id;
    sdp_uuid16_create(&hidsvcls_uuid, HID_SVCLASS_ID);
    svclass_id = sdp_list_append(0, &hidsvcls_uuid);
    sdp_set_service_classes(record, svclass_id);
}

//ATTR 0x0004
static void Inno_sdp_set_ProtocolDescriptorList(sdp_record_t *record) {
    uuid_t hidp_uuid, l2cap_uuid;
    sdp_list_t *proto_l2cap, *proto_hidp, *apseq, *aproto;
    sdp_data_t *psm;
    unsigned short ctrl = L2CAP_PSM_HIDP_CTRL;

    sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
    proto_l2cap = sdp_list_append(0, &l2cap_uuid);
    psm = sdp_data_alloc(SDP_UINT16, &ctrl);
    proto_l2cap = sdp_list_append(proto_l2cap, psm);

    sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
    proto_hidp = sdp_list_append(0, &hidp_uuid);

    apseq = sdp_list_append(0, proto_l2cap);
    apseq = sdp_list_append(apseq, proto_hidp);
    aproto = sdp_list_append(0, apseq);
    sdp_set_access_protos(record, aproto);
}

//ATTR 0x0005
static void Inno_sdp_set_BrowseGroupList(sdp_record_t *record) {
    uuid_t browsegroup_uuid;
    sdp_list_t *browsegroup_seq;
    sdp_uuid16_create(&browsegroup_uuid, PUBLIC_BROWSE_GROUP);
    browsegroup_seq = sdp_list_append(0, &browsegroup_uuid);
    sdp_set_browse_groups(record, browsegroup_seq);
}

//ATTR 0x0006
static void Inno_sdp_set_LanguageBaseAttributeIDList(sdp_record_t *record) {
    sdp_lang_attr_t base_lang;
    sdp_list_t *langs = 0;
    base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
    base_lang.encoding = 106;
    base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
    langs = sdp_list_append(0, &base_lang);
    sdp_set_lang_attr(record, langs);
    sdp_list_free(langs, 0);
}

//ATTR 0x0009
static void Inno_sdp_set_BluetoothProfileDescriptorList(sdp_record_t *record) {
    sdp_profile_desc_t profile;
    sdp_list_t *pfseq;
    sdp_uuid16_create(&profile.uuid, HID_PROFILE_ID);
    profile.version = 0x0100;
    pfseq = sdp_list_append(0, &profile);
    sdp_set_profile_descs(record, pfseq);
}

//ATTR 0x000d
static void Inno_sdp_set_AdditionalProtocolDescriptorLists(sdp_record_t *record) {
    uuid_t hidp_uuid, l2cap_uuid;
    sdp_list_t *proto_l2cap, *proto_hidp, *apseq, *aproto;
    sdp_data_t *psm;
    unsigned short intr = L2CAP_PSM_HIDP_INTR;

    sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
    proto_l2cap = sdp_list_append(0, &l2cap_uuid);
    psm = sdp_data_alloc(SDP_UINT16, &intr);
    proto_l2cap = sdp_list_append(proto_l2cap, psm);

    sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
    proto_hidp = sdp_list_append(0, &hidp_uuid);

    apseq = sdp_list_append(0, proto_l2cap);
    apseq = sdp_list_append(apseq, proto_hidp);
    aproto = sdp_list_append(0, apseq);
    sdp_set_add_access_protos(record, aproto);
}

//ATTR 0x0100
static void Inno_sdp_set_ServiceName(sdp_record_t *record, const char *name) {
    sdp_attr_add_new(record, SDP_ATTR_SVCNAME_PRIMARY, SDP_TEXT_STR8, name);
}

//ATTR 0x0101
static void Inno_sdp_set_ServiceDescription(sdp_record_t *record, const char *desc) {
    sdp_attr_add_new(record, SDP_ATTR_SVCDESC_PRIMARY, SDP_TEXT_STR8, desc);
}

//ATTR 0x0102
static void Inno_sdp_set_ProviderName(sdp_record_t *record, const char *prov) {
    sdp_attr_add_new(record, SDP_ATTR_PROVNAME_PRIMARY, SDP_TEXT_STR8, prov);
}

//ATTR 0x0200
static void Inno_sdp_set_HID_DeviceReleaseNumber(sdp_record_t *record, unsigned int val) {
    sdp_attr_add_new(record, SDP_ATTR_HID_DEVICE_RELEASE_NUMBER, SDP_UINT16, &val);
}

//ATTR 0x0201
static void Inno_sdp_set_HID_ParserVersion(sdp_record_t *record, unsigned int val) {
    sdp_attr_add_new(record, SDP_ATTR_HID_PARSER_VERSION, SDP_UINT16, &val);
}

//ATTR 0x0202
static void Inno_sdp_set_HID_DeviceSubClass(sdp_record_t *record, unsigned int val) {
    sdp_attr_add_new(record, SDP_ATTR_HID_DEVICE_SUBCLASS, SDP_UINT8, &val);
}

//ATTR 0x0203
static void Inno_sdp_set_HID_CountryCode(sdp_record_t *record, unsigned int val) {
    sdp_attr_add_new(record, SDP_ATTR_HID_COUNTRY_CODE, SDP_UINT8, &val);
}

//ATTR 0x0204
static void Inno_sdp_set_HID_VirtualCable(sdp_record_t *record, unsigned int val) {
    sdp_attr_add_new(record, SDP_ATTR_HID_VIRTUAL_CABLE, SDP_BOOL, &val);
}

//ATTR 0x0205
static void Inno_sdp_set_HID_ReconnectInitiate(sdp_record_t *record, unsigned int val) {
    sdp_attr_add_new(record, SDP_ATTR_HID_RECONNECT_INITIATE, SDP_BOOL, &val);
}

//ATTR 0x0206
static void Inno_sdp_set_HID_DescriptorList(sdp_record_t *record, void *data, int len) {
    void *dtds[2];
    void *values[2];
    unsigned char dtd2=SDP_UINT8;
    unsigned char hid_spec_type=0x22;
    unsigned char dtd_data=SDP_TEXT_STR8;
    int leng[2];
    sdp_data_t *hid_spec_lst;
    sdp_data_t *hid_spec_lst2;

    dtds[0] = &dtd2;
    values[0] = &hid_spec_type;
    dtds[1] = &dtd_data;
    values[1] = data;
    leng[0] = 0;
    leng[1] = len;
    hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2);
    hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst);
    sdp_attr_add(record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2);
}

/*
hid_attr_langs - array of uint16.
          size - how many elements in the array.
*/
//ATTR 0x0207
static void Inno_sdp_set_HID_LangIdBaseList(sdp_record_t *record, unsigned short *hid_attr_langs, int size)
{
    void *dtds2[2];
    unsigned char dtd = SDP_UINT16;
    void *values2[2];
    sdp_data_t *lang_lst;
    sdp_data_t *lang_lst2;
    int i;

    for (i = 0; i < size; i++) {
        dtds2[i] = &dtd;
        values2[i] = &hid_attr_langs[i];
    }
    lang_lst = sdp_seq_alloc(dtds2, values2, size);
    lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst);
    sdp_attr_add(record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);
}

//ATTR 0x0208
static void Inno_sdp_set_HID_SdpDisable(sdp_record_t *record, unsigned int val) {
    sdp_attr_add_new(record, SDP_ATTR_HID_SDP_DISABLE, SDP_BOOL, &val);
}

//ATTR 0x0209
static void Inno_sdp_set_HID_BatteryPower(sdp_record_t *record, unsigned int val) {
    sdp_attr_add_new(record, SDP_ATTR_HID_BATTERY_POWER, SDP_BOOL, &val);
}

//ATTR 0x020a
static void Inno_sdp_set_HID_RemoteWakeUp(sdp_record_t *record, unsigned int val) {
    sdp_attr_add_new(record, SDP_ATTR_HID_REMOTE_WAKEUP, SDP_BOOL, &val);
}

//ATTR 0x020b
static void Inno_sdp_set_HID_ProfileVersion(sdp_record_t *record, unsigned int val) {
    sdp_attr_add_new(record, SDP_ATTR_HID_PROFILE_VERSION, SDP_UINT16, &val);
}

//ATTR 0x020c
static void Inno_sdp_set_HID_SuperVersionTimeout(sdp_record_t *record, unsigned int val) {
    sdp_attr_add_new(record, SDP_ATTR_HID_SUPERVISION_TIMEOUT, SDP_UINT16, &val);
}

//ATTR 0x020d
static void Inno_sdp_set_HID_NormallyConnectable(sdp_record_t *record, unsigned int val) {
    sdp_attr_add_new(record, SDP_ATTR_HID_NORMALLY_CONNECTABLE, SDP_BOOL, &val);
}

//ATTR 0x020e
static void Inno_sdp_set_HID_BootDevice(sdp_record_t *record, unsigned int val) {
    sdp_attr_add_new(record, SDP_ATTR_HID_BOOT_DEVICE, SDP_BOOL, &val);
}

// With 0xffffffff, we get assigned the first free record >= 0x10000
// Make HID service visible (add to PUBLIC BROWSE GROUP)
sdp_record_t *SdpCreateRecord(unsigned int handle) {
    sdp_record_t *record = calloc(1, sizeof(sdp_record_t));
    record->handle = handle;
    return record;
}

void SdpDestroyRecord(sdp_record_t *record) {
    if (record) free(record);
}

sdp_record_t*     SdpMakeUpHidRecord(sdp_record_t *record, struct hidc_info_t *hidc_info)
{
    unsigned short hid_attr_lang[] = {0x0409, 0x0100};

    Inno_sdp_set_ServiceClassIDList(record);
    Inno_sdp_set_ProtocolDescriptorList(record);
    Inno_sdp_set_BrowseGroupList(record);
    Inno_sdp_set_LanguageBaseAttributeIDList(record);
    Inno_sdp_set_BluetoothProfileDescriptorList(record);
    Inno_sdp_set_AdditionalProtocolDescriptorLists(record);
    Inno_sdp_set_HID_DeviceReleaseNumber(record, 0x0100);
    Inno_sdp_set_HID_ParserVersion(record, 0x0111);
    Inno_sdp_set_HID_CountryCode(record, 0x21);
    Inno_sdp_set_HID_VirtualCable(record, 1);
    Inno_sdp_set_HID_ReconnectInitiate(record, 1);
    Inno_sdp_set_HID_LangIdBaseList(record, hid_attr_lang,sizeof(hid_attr_lang)/sizeof(hid_attr_lang[0]));
    Inno_sdp_set_HID_SdpDisable(record, 0);
    Inno_sdp_set_HID_BatteryPower(record, 1);
    Inno_sdp_set_HID_RemoteWakeUp(record, 1);
    Inno_sdp_set_HID_ProfileVersion(record, 0x0100);
    Inno_sdp_set_HID_SuperVersionTimeout(record, 0x0c80);
    Inno_sdp_set_HID_NormallyConnectable(record, 0);
    Inno_sdp_set_HID_BootDevice(record, 1);

    if (hidc_info->service_name)
        Inno_sdp_set_ServiceName(record, hidc_info->service_name);

    if (hidc_info->description)
        Inno_sdp_set_ServiceDescription(record, hidc_info->description);

    if (hidc_info->provider)
        Inno_sdp_set_ProviderName(record, hidc_info->provider);

    Inno_sdp_set_HID_DeviceSubClass(record, hidc_info->sub_class);
    Inno_sdp_set_HID_DescriptorList(record, hidc_info->desc_data, hidc_info->desc_len);

    return record;
}

int SdpRegisterRecord(sdp_record_t *record) {
    sdp_session_t *session;

    // Connect to SDP server on localhost, to publish service information
    session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
    if (!session) {
        fprintf(stderr, "Failed to connect to SDP server: %s\n", strerror(errno));
        return 1;
    }

    // Submit our IDEA of a SDP record to the "sdpd"
    if (sdp_record_register(session, record, SDP_RECORD_PERSIST) < 0) {
        fprintf (stderr, "Service Record registration failed\n");
        return -1;
    }
    fprintf (stdout, "HID keyboard service registered. handle=0x%x\n", record->handle);

    sdphandle_hid = record->handle;

    sdp_close(session);

    return 0;
}

void SdpUnregisterRecord(unsigned int handle) {
    unsigned int    range=0x0000ffff;
    sdp_list_t    *    attr;
    sdp_session_t *    session;
    sdp_record_t  *    record;

    // Connect to the local SDP server
    session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
    if (!session) return;

    attr = sdp_list_append(0, &range);
    record = sdp_service_attr_req(session, handle, SDP_ATTR_REQ_RANGE, attr);
    sdp_list_free(attr, 0);
    if (!record) {
        sdp_close(session);
        return;
    }
    sdp_device_record_unregister(session, BDADDR_ANY, record);
    sdp_close(session);
    return;
}

int DoSdpRegistration(void)
{
    sdp_record_t* record;
    record = (sdp_record_t *)SdpCreateRecord(0xffffffff);
    SdpMakeUpHidRecord(record, &s_hidc_info);
    SdpRegisterRecord(record);
    SdpDestroyRecord(record);
    return 0;
}

3.
//////////////////////// client hid response and report ////////////////////////
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/socket.h>

#include <linux/input.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h>
#include <hidp.h>
#include <android/log.h>
 
#define    L2CAP_PSM_HIDP_CTRL    0x11
#define    L2CAP_PSM_HIDP_INTR    0x13

// These numbers must also be used in the HID descriptor binary file
#define    REPORTID_BARCODE    1
#define    REPORTID_KEYBD        2
//#define    REPORTID_MOUSE    3

#define HID_VIRTUAL_CABLE_UNPLUG                0x15
#define HID_MSG_RESERVED                                0x20
#define HID_MSG_GET_REPORT                            0x41
#define HID_MSG_GET_REPORT_INVALID            0x42
#define HID_MSG_SET_REPORT                            0x52
#define HID_MSG_GET_PROTOCOL                        0x60
#define HID_MSG_SET_PROTOCOL_BOOT            0x70
#define HID_MSG_SET_PROTOCOL_REPORT        0x71
#define HID_MSG_GET_IDLE                                0x80
#define HID_MSG_SET_IDLE                                    0x90

//***************** Global variables
static volatile int s_innohid_loop;

volatile int  g_hid_ctrl_sock;      // For the one-session-only
volatile int  g_hid_intr_sock;
volatile int  g_hid_connection    = 0;
static volatile int  s_hidsend            = 0;
static volatile int  s_hid_conn_flag    = 0;

//***************** Data structures
typedef struct _hidrep_barcode_t
{
    unsigned char    act_type;    // Fixed value for "Data Frame": 0xA1
    unsigned char    rep_id; // Will be set to REPORTID_KEYBD for "keyboard"
    unsigned char    symbology1; // Modifier keys (shift, alt, the like)
    unsigned char    symbology2; // Modifier keys (shift, alt, the like)
    unsigned char    symbology3; // Modifier keys (shift, alt, the like)
    unsigned char    data[32]; //0x20
    unsigned char    reserved; // useless for now
}hidrep_barcode_t;

//http://www.instructables.com/id/USB-Wii-Classic-Controller/step13/Bonus-Keyboard-and-Mouse
typedef struct _hidrep_keyb_t
{
    unsigned char    act_type;    // Fixed value for "Data Frame": 0xA1
    unsigned char    rep_id; // Will be set to REPORTID_KEYBD for "keyboard"
    unsigned char    modify; // Modifier keys (shift, alt, the like)
    unsigned char    reserved; // useless for now
    unsigned char    key[6]; // Currently pressed keys, max 6 at once
}hidrep_keyb_t;

/*typedef struct _hidrep_mouse_t
 {
    unsigned char    act_type;    // Fixed value for "Data Frame": 0xA1
    unsigned char    rep_id; // Will be set to REPORTID_MOUSE for "mouse"
    unsigned char    button;    // bits 0..2 for left,right,middle, others 0
    char x;        // mouse x movement
    char y;        // mouse y movement
    char v_wheel;  // mouse wheel movement
    char h_wheel;  // mouse wheel movement
}hidrep_mouse_t;*/

typedef struct _hidrep_protocol_t
 {
    unsigned char    act_type;    // Fixed value for "Data Frame": 0xA1
    unsigned char    reserved_bits; // useless for now
}hidrep_protocol_t;

void* Thread_KeyEvent(void* pparam)
{
    int                            rlen;
    hidrep_keyb_t            evkeyb;
    char                        ev_buf[256];
    int                            fd_local;

    fd_local = *(int*)pparam;

    while( s_innohid_loop )
    {
        memset(ev_buf,0,sizeof(ev_buf));
        rlen = read(fd_local, (void*)ev_buf, sizeof(ev_buf));

        if( rlen > 0 )
        {
            memset(&evkeyb, 0, sizeof(struct _hidrep_keyb_t));
            evkeyb.act_type    = 0xa1; // always this constant value.
            evkeyb.rep_id     = REPORTID_KEYBD;

            if( ev_buf[28] )
            {
                switch(ev_buf[12])
                {
                    case 0x10: //1 0x10 + 0x0e
                        evkeyb.key[1]    = 0x1e;
                        break;

                    case 0x11: //2
                        evkeyb.key[1]    = 0x1f;
                        break;

                    case 0x12:
                        evkeyb.key[1]    = 0x20;
                        break;

                    case 0x18:
                        evkeyb.key[1]    = 0x21;
                        break;

                    case 0x19:
                        evkeyb.key[1]    = 0x22;
                        break;

                    case 0x1a:
                        evkeyb.key[1]    = 0x23;
                        break;

                    case 0x20:
                        evkeyb.key[1]    = 0x24;
                        break;

                    case 0x21:
                        evkeyb.key[1]    = 0x25;
                        break;

                    case 0x22:
                        evkeyb.key[1]    = 0x26;
                        break;

                    case 0x29:
                        evkeyb.key[1]    = 0x27;
                        break;

                    default:
                        evkeyb.key[1] = ev_buf[12];
                        break;
                }
            }

            if( g_hid_intr_sock )
                send(g_hid_intr_sock, &evkeyb, sizeof(struct _hidrep_keyb_t), 0);
        }
    }

    return NULL;
}

void* innohid_thread_handler(void* pparam)
{
    int                                    j;
    int                                    rlen;
    int                                    sock_ctrl; // For the listening sockets
    int                                     sock_intr;
    int                                     fd_keyevent;

    char                                bt_buf[16];
    char                                act_type;
    char                                report_id;
    char                                evbuf[sizeof(struct input_event)];
    fd_set                                fds;          // fds for listening sockets

    struct timeval                tv;          // Used for "select"
    bluez_reply_t                     reply;
    bluez_reply_callback_t     result;
    struct sockaddr_l2        l2a;
    bluez_send_data_t            buffer = {0};
    socklen_t                            alen = sizeof(l2a);
    pthread_attr_t                 pattr;
    pthread_t                        pthread_id;
    hidrep_barcode_t            evbarcode;
    hidrep_keyb_t                    evkeyb;
    //hidrep_mouse_t                 evmouse;
    hidrep_protocol_t            evprotocol;
    struct input_event*         inevent;

    g_hid_ctrl_sock    = 0;
    g_hid_intr_sock      = 0;

    s_innohid_loop     = 0;

    fd_keyevent            = -1;

    pthread_attr_init(&pattr);
    pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED);

    while (1)
    {
        sock_ctrl =     socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
        sock_intr =    socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);

        if( sock_ctrl < 0 || sock_intr < 0 )
        {
            DBG("Failed to generate bluetooth sockets");
            return    2;
        }

        if( btbind(sock_ctrl, L2CAP_PSM_HIDP_CTRL) || btbind(sock_intr, L2CAP_PSM_HIDP_INTR)  )
        {
            DBG("Failed to bind sockets (%d/%d) "
                "to PSM (%d/%d)", sock_ctrl, sock_intr, L2CAP_PSM_HIDP_CTRL, L2CAP_PSM_HIDP_INTR);

            return    3;
        }

        if( listen(sock_ctrl, 1) || listen(sock_intr, 1) )
        {
            DBG("Failed to listen on int/ctl BT socket");
            close(sock_intr);
            close(sock_ctrl);

            return    4;
        }

        DBG("The HID-Client is now ready to accept connections from another machine");

        while(1)
        {
            tv.tv_sec  = 1;
            tv.tv_usec = 0;
            FD_ZERO(&fds);
            FD_SET(sock_ctrl, &fds);
            switch(select(sock_ctrl + 1, &fds, NULL, NULL, &tv))
            {
                case -1:
                    if(errno == EINTR)
                    {
                        // Ctrl+C ? - handle that elsewhere
                        DBG("Ctrl+C ");
                        continue;
                    }

                    DBG("select() error on BT socket: %s! "
                        "Aborting.", strerror(errno));

                    return    11;

                case 0:
                    continue;

                default:
                    if(FD_ISSET(sock_ctrl, &fds))
                    {
                        hid_ctrl_sock = accept(sock_ctrl, (struct sockaddr *)&l2a, &alen);
                        DBG("hid_ctrl_sock:%d",hid_ctrl_sock);

                        if(hid_ctrl_sock < 0)
                        {
                            if(errno == EAGAIN)
                            {
                                continue;
                            }

                            DBG("Failed to get a control connection:"
                                " %s", strerror(errno));

                            continue;
                        }
                    }
            }

            tv.tv_sec  = 1;
            tv.tv_usec = 0;
            FD_ZERO(&fds);
            FD_SET(sock_intr, &fds);
            switch(select(sock_intr + 1, &fds, NULL, NULL, &tv))
            {
                case -1:
                    if(errno == EINTR)
                    {
                        // Ctrl+C ? - handle that elsewhere
                        DBG("Ctrl+C ");
                        continue;
                    }

                    DBG("select() error on BT socket: %s! "
                        "Aborting.", strerror(errno));

                    return    12;

                case 0:
                    DBG("Interrupt connection failed to "
                        "establish (control connection already"
                        " there), timeout!");
                    close(hid_ctrl_sock);
                    continue;

                default:
                    if(FD_ISSET(sock_intr, &fds))
                    {
                        //DBG( "accept() hid_ctrl_sock %d ", hid_ctrl_sock);
                        hid_intr_sock = accept(sock_intr, (struct sockaddr *)&l2a, &alen);
                        DBG("hid_intr_sock:%d",hid_intr_sock);

                        if(hid_intr_sock < 0)
                        {
                            close(hid_intr_sock);
                            close(hid_ctrl_sock);

                            if(errno == EAGAIN)
                            {
                                continue;
                            }

                            DBG("Failed to get an interrupt "
                                "connection: %s", strerror(errno));

                            continue;
                        }

                    }
            }

            s_innohid_loop = 1;

            fd_keyevent = open("/dev/input/event0", O_RDWR);
            pthread_create(&pthread_id, &pattr, Thread_KeyEvent, &fd_keyevent);

            memset(bt_buf,0,sizeof(bt_buf));
            rlen = recv(g_hid_ctrl_sock, bt_buf, sizeof(bt_buf), 0);
            if( rlen <= 0 )
                bt_buf[0] = HID_VIRTUAL_CABLE_UNPLUG;

            while( s_innohid_loop )
            {
                act_type = bt_buf[0];

                switch(act_type)
                {
                    case HID_VIRTUAL_CABLE_UNPLUG:
                            s_innohid_loop = 0;

                            if( fd_keyevent != -1 )
                            {
                                shutdown(fd_keyevent, SHUT_RDWR);
                                usleep(100000);
                                close(fd_keyevent);
                                usleep(100000);
                                fd_keyevent = -1;
                            }

                            if( g_hid_intr_sock )
                            {
                                close(g_hid_intr_sock);
                                g_hid_intr_sock = 0;
                            }

                            if( g_hid_ctrl_sock )
                            {
                                usleep(200000);
                                close(g_hid_ctrl_sock);
                                g_hid_ctrl_sock = 0;
                            }
                            break;

                    case HID_MSG_RESERVED:
                            report_id = 0x03; //UNSUPPORTED ID
                            send(g_hid_ctrl_sock, &report_id, sizeof(char), 0);
                            break;

                    //GET_REPORT
                    case HID_MSG_GET_REPORT:
                            report_id = bt_buf[1];

                            switch(report_id)
                            {
                                case 0x01:
                                        memset(&evbarcode, 0, sizeof(struct _hidrep_barcode_t));
                                        evbarcode.act_type    = 0xa1; // always this constant value.
                                        evbarcode.rep_id         = REPORTID_BARCODE;
                                        send(g_hid_ctrl_sock, &evbarcode, sizeof(struct _hidrep_barcode_t), 0);
                                        break;

                                case 0x02:
                                        memset(&evbarcode, 0, sizeof(struct _hidrep_keyb_t));
                                        evkeyb.act_type    = 0xa1; // always this constant value.
                                        evkeyb.rep_id     = REPORTID_KEYBD;
                                        send(g_hid_ctrl_sock, &evkeyb, sizeof(struct _hidrep_keyb_t), 0);
                                        break;

                                /*case 0x03:
                                        memset(&evmouse, 0, sizeof(struct _hidrep_mouse_t));
                                        evmouse.act_type = 0xa1; // always this constant value.
                                        evmouse.rep_id = REPORTID_MOUSE;
                                        send(g_hid_ctrl_sock, &evmouse, sizeof(struct _hidrep_mouse_t), 0);
                                        break;*/
                            }
                            break;

                    case HID_MSG_GET_REPORT_INVALID:
                            report_id = bt_buf[1];

                            switch(report_id)
                            {
                                case 0xff:
                                default:
                                        report_id = 0x02; //INVALID ID
                                        send(g_hid_ctrl_sock, &report_id, sizeof(char), 0);
                                        break;
                            }
                            break;

                    case HID_MSG_SET_REPORT:
                            report_id = 0;
                            send(g_hid_ctrl_sock, &report_id, sizeof(char), 0);
                            break;

                    case HID_MSG_GET_PROTOCOL:
                            evprotocol.act_type             = 0xa0;
                            evprotocol.reserved_bits    = 0x00;
                            send(g_hid_ctrl_sock, &evprotocol, sizeof(struct _hidrep_protocol_t), 0);
                            break;

                    case HID_MSG_SET_PROTOCOL_BOOT:
                            report_id = 0;
                            send(g_hid_ctrl_sock, &report_id, sizeof(char), 0);

                            //usleep(3000000);

                            /*memset(&evkeyb, 0, sizeof(struct _hidrep_keyb_t));
                            evkeyb.act_type    = 0xa1; // always this constant value.
                            evkeyb.rep_id     = REPORTID_KEYBD;
                            send(g_hid_intr_sock, &evkeyb, sizeof(struct _hidrep_keyb_t), 0);*/
                            /*memset(&evbarcode, 0, sizeof(struct _hidrep_barcode_t));
                            evbarcode.act_type    = 0xa1; // always this constant value.
                            evbarcode.rep_id         = REPORTID_BARCODE;
                            send(g_hid_intr_sock, &evbarcode, sizeof(struct _hidrep_barcode_t), 0);*/
                            break;

                    case HID_MSG_SET_PROTOCOL_REPORT:
                            report_id = 0;
                            send(g_hid_ctrl_sock, &report_id, sizeof(char), 0);

                            rlen = recv(g_hid_ctrl_sock, bt_buf, sizeof(bt_buf), 0);

                            evprotocol.act_type             = 0xa0;
                            evprotocol.reserved_bits    = 0x01;
                            send(g_hid_ctrl_sock, &evprotocol, sizeof(struct _hidrep_protocol_t), 0);
                            break;

                    case HID_MSG_GET_IDLE:
                            evprotocol.act_type             = 0xa0;
                            evprotocol.reserved_bits    = 0x00;
                            send(g_hid_ctrl_sock, &evprotocol, sizeof(struct _hidrep_protocol_t), 0);
                            break;

                    case HID_MSG_SET_IDLE:
                            report_id = 0;
                            send(g_hid_ctrl_sock, &report_id, sizeof(char), 0);
                            break;
                }

                //recv remote unplug msg
                if( s_innohid_loop )
                {
                    if( g_hid_ctrl_sock )
                        rlen = recv(g_hid_ctrl_sock, bt_buf, sizeof(bt_buf), 0);
                }
            } //while( s_innohid_loop )

            DBG("HID Connection closed");
        }

        close(sock_intr);
        close(sock_ctrl);

        DBG("Stopped hidclient.");
    }//end while

    pthread_attr_destroy(&pattr);

    return NULL;
}

4.
//////////////////////// start thread ////////////////////////
int main(int argc, char ** argv)
{
    int                      err;
    pthread_attr_t     pattr;

    DoSdpRegistration();

    pthread_attr_init(&pattr);
    pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED);

    err = pthread_create(&pInnoHidThread, &pattr, innohid_thread_handler, NULL);

    pthread_attr_destroy(&pattr);

    while(1)
    {
        usleep(10000); //10ms
    }
}

沒有留言:

張貼留言