/*
* QEMU Bluetooth HCI logic.
*
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu-common.h"
#include "qemu-timer.h"
#include "usb.h"
#include "net.h"
#include "bt.h"
struct bt_hci_s {
uint8_t *(*evt_packet)(void *opaque);
void (*evt_submit)(void *opaque, int len);
void *opaque;
uint8_t evt_buf[256];
uint8_t acl_buf[4096];
int acl_len;
uint16_t asb_handle;
uint16_t psb_handle;
int last_cmd; /* Note: Always little-endian */
struct bt_device_s *conn_req_host;
struct {
int inquire;
int periodic;
int responses_left;
int responses;
QEMUTimer *inquiry_done;
QEMUTimer *inquiry_next;
int inquiry_length;
int inquiry_period;
int inquiry_mode;
#define HCI_HANDLE_OFFSET 0x20
#define HCI_HANDLES_MAX 0x10
struct bt_hci_master_link_s {
struct bt_link_s *link;
void (*lmp_acl_data)(struct bt_link_s *link,
const uint8_t *data, int start, int len);
QEMUTimer *acl_mode_timer;
} handle[HCI_HANDLES_MAX];
uint32_t role_bmp;
int last_handle;
int connecting;
bdaddr_t awaiting_bdaddr[HCI_HANDLES_MAX];
} lm;
uint8_t event_mask[8];
uint16_t voice_setting; /* Notw: Always little-endian */
uint16_t conn_accept_tout;
QEMUTimer *conn_accept_timer;
struct HCIInfo info;
struct bt_device_s device;
};
#define DEFAULT_RSSI_DBM 20
#define hci_from_info(ptr) container_of((ptr), struct bt_hci_s, info)
#define hci_from_device(ptr) container_of((ptr), struct bt_hci_s, device)
struct bt_hci_link_s {
struct bt_link_s btlink;
uint16_t handle; /* Local */
};
/* LMP layer emulation */
#if 0
static void bt_submit_lmp(struct bt_device_s *bt, int length, uint8_t *data)
{
int resp, resplen, error, op, tr;
uint8_t respdata[17];
if (length < 1)
return;
tr = *data & 1;
op = *(data ++) >> 1;
resp = LMP_ACCEPTED;
resplen = 2;
respdata[1] = op;
error = 0;
length --;
if (op >= 0x7c) { /* Extended opcode */
op |= *(data ++) << 8;
resp = LMP_ACCEPTED_EXT;
resplen = 4;
respdata[0] = op >> 8;
respdata[1] = op & 0xff;
length --;
}
switch (op) {
case LMP_ACCEPTED:
/* data[0] Op code
*/
if (length < 1) {
error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
goto not_accepted;
}
resp = 0;
break;
case LMP_ACCEPTED_EXT:
/* data[0] Escape op code
* data[1] Extended op code
*/
if (length < 2) {
error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
goto not_accepted;
}
resp = 0;
break;
case LMP_NOT_ACCEPTED:
/* data[0] Op code
* data[1] Error code
*/
if (length < 2) {
error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
goto not_accepted;
}
resp = 0;
break;
case LMP_NOT_ACCEPTED_EXT:
/* data[0] Op code
* data[1] Extended op code
* data[2] Error code
*/
if (length < 3) {
error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
goto not_accepted;
}
resp = 0;
break;
case LMP_HOST_CONNECTION_REQ:
break;
case LMP_SETUP_COMPLETE:
resp = LMP_SETUP_COMPLETE;
resplen = 1;
bt->setup = 1;
break;
case LMP_DETACH:
/* data[0] Error code
*/
if (length < 1) {
error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
goto not_accepted;
}
bt->setup = 0;
resp = 0;
break;
case LMP_SUPERVISION_TIMEOUT:
/* data[0,1] Supervision timeout
*/
if (length < 2) {
error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
goto not_accepted;
}
resp = 0;
break;
case LMP_QUALITY_OF_SERVICE:
resp = 0;
/* Fall through */
case LMP_QOS_REQ:
/* data[0,1] Poll interval
* data[2] N(BC)
*/
if (length < 3) {
error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
goto not_accepted;
}
break;
case LMP_MAX_SLOT:
resp = 0;
/* Fall through */
case LMP_MAX_SLOT_REQ:
/* data[0] Max slots
*/
if (length < 1) {
error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
goto not_accepted;
}
break;
case LMP_AU_RAND:
case LMP_IN_RAND:
case LMP_COMB_KEY:
/* data[0-15] Random number
*/
if (length < 16) {
error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
goto not_accepted;
}
if (op == LMP_AU_RAND) {
if (bt->key_present) {
resp = LMP_SRES;
resplen = 5;
/* XXX: [Part H] Section 6.1 on page 801 */
} else {
error = HCI_PIN_OR_KEY_MISSING;
goto not_accepted;
}
} else if (op == LMP_IN_RAND) {
error = HCI_PAIRING_NOT_ALLOWED;
goto not_accepted;
} else {
/* XXX: [Part H] Section 3.2 on page 779 */
resp = LMP_UNIT_KEY;
resplen = 17;
memcpy(respdata + 1, bt->key, 16);
error = HCI_UNIT_LINK_KEY_USED;
goto not_accepted;
}
break;
case LMP_UNIT_KEY:
/* data[0-15] Key
*/
if (length < 16) {
error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
goto not_accepted;
}
memcpy(bt->key, data, 16);
bt->key_present = 1;
break;
case LMP_SRES:
/* data[0-3] Authentication response
*/
if (length < 4) {
error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
goto not_accepted;
}
break;
case LMP_CLKOFFSET_REQ:
resp = LMP_CLKOFFSET_RES;
resplen = 3;
respdata[1] = 0x33;
respdata[2] = 0x33;
break;
case LMP_CLKOFFSET_RES:
/* data[0,1] Clock offset
* (Slave to master only)
*/
if (length < 2) {
error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
goto not_accepted;
}
break;
case LMP_VERSION_REQ:
case LMP_VERSION_RES:
/* data[0] VersNr
* data[1,2] CompId
* data[3,4] SubVersNr
*/
if (length < 5) {
error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
goto not_accepted;
}
if (op == LMP_VERSION_REQ) {
resp = LMP_VERSION_RES;
resplen = 6;
respdata[1] = 0x20;
respdata[2] = 0xff;
respdata[3] = 0xff;
respdata[4] = 0xff;
respdata[5] = 0xff;
} else
resp = 0;
break;
case LMP_FEATURES_REQ:
case LMP_FEATURES_RES:
/* data[0-7] Features
*/
if (length < 8) {
error = HCI_UNSUPPORTED_LMP_PARAMETER_VALUE;
goto not_accepted;
}
if (op == LMP_FEATURES_REQ) {
resp = LMP_FEATURES_RES;
resplen = 9;
respdata[1] = (bt->lmp_caps >> 0) & 0x