#include "h2transactions.h"
#include <bluetooth/HCI/btHCI.h>
#include <bluetooth/HCI/btHCI_event.h>
#include <bluetooth/HCI/btHCI_acl.h>
#include <ByteOrder.h>
#include <kernel.h>
#include <string.h>
#include "h2debug.h"
#include "h2generic.h"
#include "h2upper.h"
#include "h2util.h"
void acl_tx_complete(void* cookie, status_t status, void* data, size_t actual_len);
void acl_rx_complete(void* cookie, status_t status, void* data, size_t actual_len);
void command_complete(void* cookie, status_t status, void* data, size_t actual_len);
void event_complete(void* cookie, status_t status, void* data, size_t actual_len);
static status_t
assembly_rx(bt_usb_dev* bdev, bt_packet_t type, void* data, int count)
{
bdev->stat.bytesRX += count;
return btDevices->PostTransportPacket(bdev->hdev, type, data, count);
}
#if 0
#pragma mark --- RX Complete ---
#endif
void
event_complete(void* cookie, status_t status, void* data, size_t actual_len)
{
bt_usb_dev* bdev = (bt_usb_dev*)cookie;
status_t error;
TRACE("%s: cookie@%p status=%s len=%" B_PRIuSIZE "\n", __func__, cookie,
strerror(status), actual_len);
if (bdev == NULL)
return;
if (status == B_CANCELED || status == B_DEV_CRC_ERROR)
return;
if (status != B_OK || actual_len == 0)
goto resubmit;
if (assembly_rx(bdev, BT_EVENT, data, actual_len) == B_OK) {
bdev->stat.successfulTX++;
} else {
bdev->stat.errorRX++;
}
resubmit:
error = usb->queue_interrupt(bdev->intr_in_ep->handle, data,
max_c(HCI_MAX_EVENT_SIZE, bdev->max_packet_size_intr_in),
event_complete, bdev);
if (error != B_OK) {
reuse_room(&bdev->eventRoom, data);
bdev->stat.rejectedRX++;
ERROR("%s: RX event resubmittion failed %s\n", __func__,
strerror(error));
} else {
bdev->stat.acceptedRX++;
}
}
void
acl_rx_complete(void* cookie, status_t status, void* data, size_t actual_len)
{
bt_usb_dev* bdev = (bt_usb_dev*)cookie;
status_t error;
if (bdev == NULL)
return;
if (status == B_CANCELED || status == B_DEV_CRC_ERROR)
return;
if (status != B_OK || actual_len == 0)
goto resubmit;
if (assembly_rx(bdev, BT_ACL, data, actual_len) == B_OK) {
bdev->stat.successfulRX++;
} else {
bdev->stat.errorRX++;
}
resubmit:
error = usb->queue_bulk(bdev->bulk_in_ep->handle, data,
max_c(HCI_MAX_FRAME_SIZE, bdev->max_packet_size_bulk_in),
acl_rx_complete, (void*) bdev);
if (error != B_OK) {
reuse_room(&bdev->aclRoom, data);
bdev->stat.rejectedRX++;
ERROR("%s: RX acl resubmittion failed %s\n", __func__, strerror(error));
} else {
bdev->stat.acceptedRX++;
}
}
#if 0
#pragma mark --- RX ---
#endif
status_t
submit_rx_event(bt_usb_dev* bdev)
{
size_t size = max_c(HCI_MAX_EVENT_SIZE, bdev->max_packet_size_intr_in);
void* buf = alloc_room(&bdev->eventRoom, size);
status_t status;
if (buf == NULL)
return ENOMEM;
status = usb->queue_interrupt(bdev->intr_in_ep->handle, buf, size,
event_complete, (void*)bdev);
if (status != B_OK) {
reuse_room(&bdev->eventRoom, buf);
bdev->stat.rejectedRX++;
} else {
bdev->stat.acceptedRX++;
TRACE("%s: Accepted RX Event %d\n", __func__, bdev->stat.acceptedRX);
}
return status;
}
status_t
submit_rx_acl(bt_usb_dev* bdev)
{
size_t size = max_c(HCI_MAX_FRAME_SIZE, bdev->max_packet_size_bulk_in);
void* buf = alloc_room(&bdev->aclRoom, size);
status_t status;
if (buf == NULL)
return ENOMEM;
status = usb->queue_bulk(bdev->bulk_in_ep->handle, buf, size,
acl_rx_complete, bdev);
if (status != B_OK) {
reuse_room(&bdev->aclRoom, buf);
bdev->stat.rejectedRX++;
} else {
bdev->stat.acceptedRX++;
}
return status;
}
status_t
submit_rx_sco(bt_usb_dev* bdev)
{
return B_ERROR;
}
#if 0
#pragma mark --- TX Complete ---
#endif
void
command_complete(void* cookie, status_t status, void* data, size_t actual_len)
{
snet_buffer* snbuf = (snet_buffer*)cookie;
bt_usb_dev* bdev = (bt_usb_dev*)snb_cookie(snbuf);
TRACE("%s: len = %" B_PRIuSIZE " @%p\n", __func__, actual_len, data);
if (status == B_OK) {
bdev->stat.successfulTX++;
bdev->stat.bytesTX += actual_len;
} else {
bdev->stat.errorTX++;
}
snb_park(&bdev->snetBufferRecycleTrash, snbuf);
#ifdef BT_RESCHEDULING_AFTER_COMPLETITIONS
schedTxProcessing(bdev);
#endif
}
void
acl_tx_complete(void* cookie, status_t status, void* data, size_t actual_len)
{
net_buffer* nbuf = (net_buffer*)cookie;
bt_usb_dev* bdev = GET_DEVICE(nbuf);
if (status == B_OK) {
bdev->stat.successfulTX++;
bdev->stat.bytesTX += actual_len;
} else {
bdev->stat.errorTX++;
}
nb_destroy(nbuf);
#ifdef BT_RESCHEDULING_AFTER_COMPLETITIONS
schedTxProcessing(bdev);
#endif
}
#if 0
#pragma mark --- TX ---
#endif
status_t
submit_tx_command(bt_usb_dev* bdev, snet_buffer* snbuf)
{
uint8 bRequestType = bdev->ctrl_req;
uint8 bRequest = 0;
uint16 wIndex = 0;
uint16 value = 0;
uint16 wLength = B_HOST_TO_LENDIAN_INT16(snb_size(snbuf));
status_t error;
if (!GET_BIT(bdev->state, RUNNING)) {
return B_DEV_NOT_READY;
}
snb_set_cookie(snbuf, bdev);
TRACE("%s: @%p\n", __func__, snb_get(snbuf));
error = usb->queue_request(bdev->dev, bRequestType, bRequest,
value, wIndex, wLength, snb_get(snbuf),
command_complete, (void*) snbuf);
if (error != B_OK) {
bdev->stat.rejectedTX++;
} else {
bdev->stat.acceptedTX++;
}
return error;
}
status_t
submit_tx_acl(bt_usb_dev* bdev, net_buffer* nbuf)
{
status_t error;
SET_DEVICE(nbuf, bdev->hdev);
if (!GET_BIT(bdev->state, RUNNING)) {
return B_DEV_NOT_READY;
}
error = usb->queue_bulk(bdev->bulk_out_ep->handle, nb_get_whole_buffer(nbuf),
nbuf->size, acl_tx_complete, (void*)nbuf);
if (error != B_OK) {
bdev->stat.rejectedTX++;
} else {
bdev->stat.acceptedTX++;
}
return error;
}
status_t
submit_tx_sco(bt_usb_dev* bdev)
{
if (!GET_BIT(bdev->state, RUNNING)) {
return B_DEV_NOT_READY;
}
return B_ERROR;
}