#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 "Errors.h"
#include "h2debug.h"
#include "h2generic.h"
#include "h2upper.h"
#include "h2util.h"
void sco_tx_complete(void* cookie, status_t status, void* data, size_t actual_len);
void sco_rx_complete(void* cookie, status_t status, void* data, size_t actual_len);
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++;
}
}
void
sco_rx_complete(void* cookie, status_t status, void* data, size_t actual_len)
{
sco_rx_transfer_t* ctx = (sco_rx_transfer_t*)cookie;
if (ctx == NULL)
return;
bt_usb_dev* bdev = ctx->bdev;
status_t error;
if (status == B_CANCELED)
return;
size_t packetSize = bdev->max_packet_size_iso_in;
if (status != B_OK || actual_len == 0)
goto resubmit;
{
bool frame_errors = false;
uint8* frame_ptr = (uint8*)data;
for (uint32 i = 0; i < SCO_PACKETS_PER_BUFFER; i++) {
if (ctx->descriptors[i].status == B_OK && ctx->descriptors[i].actual_length > 0) {
if (assembly_rx(bdev, BT_SCO, frame_ptr, ctx->descriptors[i].actual_length) != B_OK)
frame_errors = true;
}
frame_ptr += packetSize;
}
if (!frame_errors)
bdev->stat.successfulRX++;
else
bdev->stat.errorRX++;
}
resubmit:
for (uint32 i = 0; i < SCO_PACKETS_PER_BUFFER; i++) {
ctx->descriptors[i].request_length = packetSize;
ctx->descriptors[i].actual_length = 0;
ctx->descriptors[i].status = B_OK;
}
uint32 startingFrame = 0;
error = usb->queue_isochronous(bdev->iso_in_ep->handle, data,
SCO_PACKETS_PER_BUFFER * packetSize, ctx->descriptors, SCO_PACKETS_PER_BUFFER,
&startingFrame, USB_ISO_ASAP, sco_rx_complete, (void*)ctx);
if (error != B_OK) {
reuse_room(&bdev->scoRoom, ctx);
bdev->stat.rejectedRX++;
ERROR("%s: RX sco 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)
{
size_t packetSize = bdev->max_packet_size_iso_in;
if (packetSize == 0)
return B_ERROR;
size_t dataSize = SCO_PACKETS_PER_BUFFER * packetSize;
size_t roomSize = sizeof(sco_rx_transfer_t) + dataSize;
sco_rx_transfer_t* ctx = (sco_rx_transfer_t*)alloc_room(&bdev->scoRoom, roomSize);
if (ctx == NULL)
return B_NO_MEMORY;
ctx->bdev = bdev;
for (uint32 i = 0; i < SCO_PACKETS_PER_BUFFER; i++) {
ctx->descriptors[i].request_length = packetSize;
ctx->descriptors[i].actual_length = 0;
ctx->descriptors[i].status = B_OK;
}
void* dataBuffer = (uint8*)ctx + sizeof(sco_rx_transfer_t);
uint32 startingFrame = 0;
status_t status
= usb->queue_isochronous(bdev->iso_in_ep->handle, dataBuffer, dataSize, ctx->descriptors,
SCO_PACKETS_PER_BUFFER, &startingFrame, USB_ISO_ASAP, sco_rx_complete, ctx);
if (status != B_OK) {
reuse_room(&bdev->scoRoom, ctx);
bdev->stat.rejectedRX++;
} else {
bdev->stat.acceptedRX++;
}
return status;
}
#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
}
void
sco_tx_complete(void* cookie, status_t status, void* data, size_t actual_len)
{
sco_tx_transfer_t* transfer = (sco_tx_transfer_t*)cookie;
bt_usb_dev* bdev = transfer->bdev;
if (status == B_OK) {
bdev->stat.successfulTX++;
bdev->stat.bytesTX += actual_len;
} else {
bdev->stat.errorTX++;
}
nb_destroy(transfer->nbuf);
free(transfer->packet_descriptors);
free(transfer);
#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 ((bdev->state & RUNNING) == 0) {
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 ((bdev->state & RUNNING) == 0) {
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, net_buffer* nbuf)
{
status_t error;
if ((bdev->state & RUNNING) == 0)
return B_DEV_NOT_READY;
SET_DEVICE(nbuf, bdev->hdev);
size_t maxPacketSize = bdev->max_packet_size_iso_out;
if (maxPacketSize == 0)
return B_ERROR;
uint32 packetCount = (nbuf->size + maxPacketSize - 1) / maxPacketSize;
sco_tx_transfer_t* transfer = (sco_tx_transfer_t*)malloc(sizeof(sco_tx_transfer_t));
usb_iso_packet_descriptor* descriptors
= (usb_iso_packet_descriptor*)malloc(sizeof(usb_iso_packet_descriptor) * packetCount);
if (transfer == NULL || descriptors == NULL) {
free(transfer);
free(descriptors);
return B_NO_MEMORY;
}
size_t remaining = nbuf->size;
for (uint32 i = 0; i < packetCount; i++) {
descriptors[i].request_length = (remaining > maxPacketSize) ? maxPacketSize : remaining;
descriptors[i].actual_length = 0;
descriptors[i].status = B_OK;
remaining -= descriptors[i].request_length;
}
transfer->bdev = bdev;
transfer->nbuf = nbuf;
transfer->packet_descriptors = descriptors;
uint32 startingFrameNumber = 0;
error = usb->queue_isochronous(bdev->iso_out_ep->handle, nb_get_whole_buffer(nbuf), nbuf->size,
descriptors, packetCount, &startingFrameNumber, USB_ISO_ASAP, sco_tx_complete,
(void*)transfer);
if (error != B_OK) {
free(descriptors);
free(transfer);
bdev->stat.rejectedTX++;
} else {
bdev->stat.acceptedTX++;
}
return error;
}