diff -uNr linux-2.6.10-rc2-bk13/net/bluetooth/hidp/core.c linux-2.6.10-rc2-bk13-bthid/net/bluetooth/hidp/core.c --- linux-2.6.10-rc2-bk13/net/bluetooth/hidp/core.c 2004-10-19 10:54:55.000000000 +1300 +++ linux-2.6.10-rc2-bk13-bthid/net/bluetooth/hidp/core.c 2004-12-02 08:27:41.045629872 +1300 @@ -45,7 +45,7 @@ #include "hidp.h" -#ifndef CONFIG_BT_HIDP_DEBUG +#ifndef CONFIG_BT_HIDP_DEBUG_BT #undef BT_DBG #define BT_DBG(D...) #endif @@ -130,8 +130,8 @@ struct sk_buff *skb; unsigned char newleds; - BT_DBG("session %p hid %p data %p size %d", session, device, data, size); - + BT_DBG(""); + if (type != EV_LED) return -1; @@ -151,7 +151,7 @@ return -ENOMEM; } - *skb_put(skb, 1) = 0xa2; + *skb_put(skb, 1) = HIDP_TRANS_DATA|HIDP_DATA_RTYPE_OUPUT; *skb_put(skb, 1) = 0x01; *skb_put(skb, 1) = newleds; @@ -232,7 +232,36 @@ del_timer(&session->timer); } -static inline void hidp_send_message(struct hidp_session *session, unsigned char hdr) +static inline int hidp_send_ctrl_message(struct hidp_session *session, + unsigned char hdr, + unsigned char *data, int size) +{ + struct sk_buff *skb; + + BT_DBG("session %p data %p size %d", session, data, size); + + if (!(skb = alloc_skb(size + 1, GFP_ATOMIC))) { + BT_ERR("Can't allocate memory for new frame"); + return -ENOMEM; + } + + *skb_put(skb, 1) = hdr; + if (size > 0) + memcpy(skb_put(skb, size), data, size); + + skb_queue_tail(&session->ctrl_transmit, skb); + + hidp_schedule(session); + + return 0; +} + +/* Send a 1 byte control message. + * Schedule flag has to be set if calling from outside HID session process + */ +static inline void hidp_send_ctrl_byte(struct hidp_session *session, + unsigned char hdr, + int schedule) { struct sk_buff *skb; @@ -247,29 +276,199 @@ skb_queue_tail(&session->ctrl_transmit, skb); - hidp_schedule(session); + if (schedule) + hidp_schedule(session); +} + + +static inline void hidp_process_handshake(struct hidp_session *session, __u8 param){ + switch (param) { + case HIDP_HSHK_SUCCESSFUL: + /* Call into SET_ GET_ handlers here */ + break; + case HIDP_HSHK_NOT_READY: + case HIDP_HSHK_ERR_INVALID_REPORT_ID: + case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: + case HIDP_HSHK_ERR_INVALID_PARAMETER: + /* Call into SET_ GET_ handlers here */ + break; + case HIDP_HSHK_ERR_UNKNOWN: + BT_INFO("HANDSHAKE parameter ERR_UNKNOWN seen."); + break; + case HIDP_HSHK_ERR_FATAL: + /* Device requests a reboot, as this is the only way this error + * can be recovered. + */ + hidp_send_ctrl_byte(session, + HIDP_TRANS_HID_CONTROL|HIDP_CTRL_SOFT_RESET, + 0); + break; + default: + hidp_send_ctrl_byte(session, + HIDP_TRANS_HANDSHAKE|HIDP_HSHK_ERR_INVALID_PARAMETER, + 0); + break; + } + +} + +static inline void hidp_process_hid_control (struct hidp_session *session, __u8 param) +{ + switch (param) { + case HIDP_CTRL_NOP: + break; + case HIDP_CTRL_VIRTUAL_CABLE_UNPLUG: + /* Flush the transmit queues */ + skb_queue_purge(&session->ctrl_transmit); + skb_queue_purge(&session->intr_transmit); + + /* Kill session thread */ + atomic_inc(&session->terminate); + + /* Do some funky HCI stuff here to delete pairing on dongle? */ + break; + case HIDP_CTRL_HARD_RESET: + case HIDP_CTRL_SOFT_RESET: + case HIDP_CTRL_SUSPEND: + case HIDP_CTRL_EXIT_SUSPEND: + /* We have to parse these and return no error */ + break; + default: + hidp_send_ctrl_byte(session, + HIDP_TRANS_HANDSHAKE|HIDP_HSHK_ERR_INVALID_PARAMETER, + 0); + break; + } +} + +static inline int hidp_process_data(struct hidp_session *session, struct sk_buff *skb, __u8 param) +{ + int result = 0; + + BT_INFO("DATA packet on control channel, GET_, SET_ not implemented"); + + switch (param) { + case HIDP_DATA_RTYPE_INPUT: + hidp_set_timer(session); + + if (session->input) + hidp_input_report(session, skb); + + if (session->hid) { + result = hid_recv_report(session->hid, + HID_INPUT_REPORT, + skb->data, + skb->len); + switch (result) { + case -EPROTOTYPE: + hidp_send_ctrl_byte(session, + HIDP_TRANS_SET_PROTOCOL|HIDP_PROTO_REPORT, + 0); + break; + case -EBADF: + hidp_send_ctrl_byte(session, + HIDP_TRANS_HANDSHAKE|HIDP_HSHK_ERR_INVALID_REPORT_ID, + 0); + break; + default: + break; + } + } + break; + case HIDP_DATA_RTYPE_OTHER: + case HIDP_DATA_RTYPE_OUPUT: + case HIDP_DATA_RTYPE_FEATURE: + BT_DBG("Unimplemented DATA parameter 0x%01x", param); + break; + default: + BT_DBG("Invalid DATA parameter 0x%01x", param); + hidp_send_ctrl_byte(session, + HIDP_TRANS_HANDSHAKE|HIDP_HSHK_ERR_INVALID_PARAMETER, + 0); + } + + return 0; } -static inline int hidp_recv_frame(struct hidp_session *session, struct sk_buff *skb) + +static inline int hidp_recv_ctrl_frame(struct hidp_session *session, struct sk_buff *skb) { __u8 hdr; + __u8 type; + __u8 param; + int retval = 0; BT_DBG("session %p skb %p len %d", session, skb, skb->len); hdr = skb->data[0]; skb_pull(skb, 1); - if (hdr == 0xa1) { - hidp_set_timer(session); + type = hdr & HIDP_THDR_TRANS_MASK; + param = hdr & HIDP_THDR_PARAM_MASK; - if (session->input) - hidp_input_report(session, skb); - } else { + switch (type) { + case HIDP_TRANS_HANDSHAKE: + hidp_process_handshake(session, param); + break; + case HIDP_TRANS_HID_CONTROL: + hidp_process_hid_control(session, param); + break; + case HIDP_TRANS_DATA: + retval = hidp_process_data(session, skb, param); + break; + default: BT_DBG("Unsupported protocol header 0x%02x", hdr); + hidp_send_ctrl_byte(session, + HIDP_TRANS_HANDSHAKE|HIDP_HSHK_ERR_UNSUPPORTED_REQUEST, + 0); + break; } + + kfree_skb(skb); + return retval; +} + +/* This needs to be separate as there are only DATA and DATC packets on the + * interrupt channel. MAG + */ +static inline int hidp_recv_intr_frame(struct hidp_session *session, struct sk_buff *skb) +{ + __u8 hdr; + int retval = 0; + int result = 0; + + BT_DBG("session %p skb %p len %d", session, skb, skb->len); + + hdr = skb->data[0]; + skb_pull(skb, 1); + + if ( hdr == (HIDP_TRANS_DATA|HIDP_DATA_RTYPE_INPUT) ) { + hidp_set_timer(session); + if (session->input) + hidp_input_report(session, skb); + if (session->hid) + result = hid_recv_report(session->hid, + HID_INPUT_REPORT, + skb->data, + skb->len); + switch (result) { + case -EPROTOTYPE: + hidp_send_ctrl_byte(session, + HIDP_TRANS_SET_PROTOCOL|HIDP_PROTO_REPORT, + 0); + break; + default: + break; + } + retval = result; + } + else { + BT_INFO("Unsupported protocol header 0x%02x", hdr); + } + kfree_skb(skb); - return 0; + return retval; } static int hidp_send_frame(struct socket *sock, unsigned char *data, int len) @@ -350,12 +549,12 @@ while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) { skb_orphan(skb); - hidp_recv_frame(session, skb); + hidp_recv_ctrl_frame(session, skb); } while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) { skb_orphan(skb); - hidp_recv_frame(session, skb); + hidp_recv_intr_frame(session, skb); } hidp_process_transmit(session); @@ -514,7 +713,9 @@ goto unlink; if (session->input) { - hidp_send_message(session, 0x70); + hidp_send_ctrl_byte(session, + HIDP_TRANS_SET_PROTOCOL|HIDP_PROTO_BOOT, + 1); session->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE); session->leds = 0xff; @@ -554,14 +755,16 @@ session = __hidp_get_session(&req->bdaddr); if (session) { if (req->flags & (1 << HIDP_VIRTUAL_CABLE_UNPLUG)) { - hidp_send_message(session, 0x15); + hidp_send_ctrl_byte(session, + HIDP_TRANS_HID_CONTROL|HIDP_CTRL_VIRTUAL_CABLE_UNPLUG, + 1); } else { /* Flush the transmit queues */ skb_queue_purge(&session->ctrl_transmit); skb_queue_purge(&session->intr_transmit); /* Kill session thread */ - atomic_inc(&session->terminate); + atomic_inc(&session->terminate); hidp_schedule(session); } } else diff -uNr linux-2.6.10-rc2-bk13/net/bluetooth/hidp/hidp.h linux-2.6.10-rc2-bk13-bthid/net/bluetooth/hidp/hidp.h --- linux-2.6.10-rc2-bk13/net/bluetooth/hidp/hidp.h 2004-10-19 10:53:50.000000000 +1300 +++ linux-2.6.10-rc2-bk13-bthid/net/bluetooth/hidp/hidp.h 2004-12-02 08:03:57.857987280 +1300 @@ -26,6 +26,71 @@ #include #include +/* + * Blue Tooth HID packet defines + */ + +/* + * HID Transaction Types, and Transaction header stuff + */ +#define HIDP_THDR_TRANS_MASK 0xF0 +#define HIDP_THDR_PARAM_MASK 0x0F + +#define HIDP_TRANS_HANDSHAKE 0x00 +#define HIDP_TRANS_HID_CONTROL 0x10 +#define HIDP_TRANS_RSRVD_2 0x20 +#define HIDP_TRANS_RSRVD_3 0x30 +#define HIDP_TRANS_GET_REPORT 0x40 +#define HIDP_TRANS_SET_REPORT 0x50 +#define HIDP_TRANS_GET_PROTOCOL 0x60 +#define HIDP_TRANS_SET_PROTOCOL 0x70 +#define HIDP_TRANS_GET_IDLE 0x80 +#define HIDP_TRANS_SET_IDLE 0x90 +#define HIDP_TRANS_DATA 0xA0 +#define HIDP_TRANS_DATC 0xB0 +#define HIDP_TRANS_RSRVD_C 0xC0 +#define HIDP_TRANS_RSRVD_D 0xD0 +#define HIDP_TRANS_RSVRD_E 0xE0 +#define HIDP_TRANS_RSVRD_F 0xF0 + +/* + * HID Handshake results returned in the result parameter of the handshake + * transaction HID packet + */ +#define HIDP_HSHK_SUCCESSFUL 0x00 +#define HIDP_HSHK_NOT_READY 0x01 +#define HIDP_HSHK_ERR_INVALID_REPORT_ID 0x02 +#define HIDP_HSHK_ERR_UNSUPPORTED_REQUEST 0x03 +#define HIDP_HSHK_ERR_INVALID_PARAMETER 0x04 +#define HIDP_HSHK_ERR_UNKNOWN 0x0E +#define HIDP_HSHK_ERR_FATAL 0x0F + +/* + * HID HID_CONTROL operation parameter + */ +#define HIDP_CTRL_NOP 0x00 /* No operation */ +#define HIDP_CTRL_HARD_RESET 0x01 /* Request hard reset */ +#define HIDP_CTRL_SOFT_RESET 0x02 /* Request soft reset */ +#define HIDP_CTRL_SUSPEND 0x03 /* Request device to suspend */ +#define HIDP_CTRL_EXIT_SUSPEND 0x04 /* request exit suspend */ +#define HIDP_CTRL_VIRTUAL_CABLE_UNPLUG 0x05 /* only one Mouse/kbd can send */ + +/* + * HID DATA Transaction header parameter nibble + */ +#define HIDP_DATA_RTYPE_MASK 0x03 +#define HIDP_DATA_RSRVD_MASK 0x0C +#define HIDP_DATA_RTYPE_OTHER 0x00 +#define HIDP_DATA_RTYPE_INPUT 0x01 +#define HIDP_DATA_RTYPE_OUPUT 0x02 +#define HIDP_DATA_RTYPE_FEATURE 0x03 + +/* + * HID SET_PROTOCOL header parameter nibble + */ +#define HIDP_PROTO_BOOT 0x00 +#define HIDP_PROTO_REPORT 0x01 + /* HIDP ioctl defines */ #define HIDPCONNADD _IOW('H', 200, int) #define HIDPCONNDEL _IOW('H', 201, int) diff -uNr linux-2.6.10-rc2-bk13/net/bluetooth/hidp/Kconfig linux-2.6.10-rc2-bk13-bthid/net/bluetooth/hidp/Kconfig --- linux-2.6.10-rc2-bk13/net/bluetooth/hidp/Kconfig 2004-10-19 10:54:38.000000000 +1300 +++ linux-2.6.10-rc2-bk13-bthid/net/bluetooth/hidp/Kconfig 2004-12-02 08:03:55.595331256 +1300 @@ -10,3 +10,27 @@ Say Y here to compile HIDP support into the kernel or say M to compile it as module (hidp). +config BT_HIDP_DEBUG_BT + bool "HIDP BT Debug code" + depends on BT_HIDP + select INPUT + help + Turn this on if you have problems and want to see what the Blue Tooth + side of the code is doing. + +config BT_HIDP_DEBUG_REPORT + bool "HIDP Report mode Debug code" + depends on BT_HIDP + select INPUT + help + Turn this on if you have problems and want to see what the HID Report + part of the code is doing. + + +config BT_HIDP_DEBUG_DATA + bool "HIDP Debug of Data" + depends on BT_HIDP + select INPUT + help + Turn this on for debug printout of BT HIDP data. +