Remaining part of the USB-BT driver. Events are commands are tested and working. ACLcode is untested and needs review. Lacks some general headers to be able to compile it, as I am still considering making some org. changes. Commiting for possible reviews or suggestions.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23034 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
|
||||
*
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _H2CFG_H_
|
||||
#define _H2CFG_H_
|
||||
|
||||
/* TODO: move exclusive header for drivers*/
|
||||
#define BT_DRIVER_SUPPORTS_CMD 1
|
||||
#define BT_DRIVER_SUPPORTS_EVT 1
|
||||
#define BT_DRIVER_SUPPORTS_ESCO 0
|
||||
#define BT_DRIVER_SUPPORTS_SCO 0
|
||||
#define BT_DRIVER_SUPPORTS_ACL 0
|
||||
|
||||
#define BT_DRIVER_RXCOVERAGE (BT_DRIVER_SUPPORTS_EVT+BT_DRIVER_SUPPORTS_ACL+BT_DRIVER_SUPPORTS_SCO+BT_DRIVER_SUPPORTS_ESCO)
|
||||
#define BT_DRIVER_TXCOVERAGE (BT_DRIVER_SUPPORTS_CMD+BT_DRIVER_SUPPORTS_ACL+BT_DRIVER_SUPPORTS_SCO+BT_DRIVER_SUPPORTS_ESCO)
|
||||
|
||||
#if BT_DRIVER_RXCOVERAGE<1 || BT_DRIVER_TXCOVERAGE<1
|
||||
#error incomplete Bluetooth driver Commands and Events should be implemented
|
||||
#endif
|
||||
|
||||
|
||||
////////////////////////////////////
|
||||
|
||||
#define BT_SURVIVE_WITHOUT_HCI
|
||||
#define BT_SURVIVE_WITHOUT_NET_BUFFERS
|
||||
//#define BT_IOCTLS_PASS_SIZE
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,824 @@
|
||||
/*
|
||||
* Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
|
||||
*
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <ByteOrder.h>
|
||||
#include <Drivers.h>
|
||||
#include <USB_spec.h>
|
||||
#include <USB.h>
|
||||
|
||||
#include <net/net_buffer.h>
|
||||
#include "snet_buffer.h"
|
||||
|
||||
#include <bluetooth_util.h>
|
||||
#include <btHCI.h>
|
||||
|
||||
#define BT_DEBUG_THIS_MODULE
|
||||
#include <btDebug.h>
|
||||
|
||||
#include "h2generic.h"
|
||||
#include "h2transactions.h"
|
||||
#include "h2util.h"
|
||||
|
||||
#include "h2cfg.h"
|
||||
|
||||
int32 api_version = B_CUR_DRIVER_API_VERSION;
|
||||
|
||||
/* Modules */
|
||||
static char* usb_name = B_USB_MODULE_NAME;
|
||||
static char* hci_name = B_BT_HCI_MODULE_NAME;
|
||||
|
||||
usb_module_info *usb = NULL;
|
||||
bt_hci_module_info *hci = NULL;
|
||||
struct net_buffer_module_info *nb = NULL;
|
||||
|
||||
/* Driver Global data */
|
||||
static char *publish_names[MAX_BT_GENERIC_USB_DEVICES];
|
||||
|
||||
int32 dev_count = 0; /* number of connected devices */
|
||||
static bt_usb_dev* bt_usb_devices[MAX_BT_GENERIC_USB_DEVICES];
|
||||
sem_id dev_table_sem = -1; /* sem to synchronize access to device table */
|
||||
|
||||
usb_support_descriptor supported_devices[] =
|
||||
{
|
||||
/* Generic Bluetooth USB device */
|
||||
/* Class, SubClass, and Protocol codes that describe a Bluetooth device */
|
||||
{ UDCLASS_WIRELESS, UDSUBCLASS_RF, UDPROTO_BLUETOOTH , 0 , 0 },
|
||||
|
||||
/* Generic devices */
|
||||
/* Broadcom BCM2035 */
|
||||
{ 0, 0, 0, 0x0a5c, 0x200a },
|
||||
{ 0, 0, 0, 0x0a5c, 0x2009 },
|
||||
|
||||
/* Devices taken from the linux Driver */
|
||||
/* AVM BlueFRITZ! USB v2.0 */
|
||||
{ 0, 0, 0, 0x057c , 0x3800 },
|
||||
/* Bluetooth Ultraport Module from IBM */
|
||||
{ 0, 0, 0, 0x04bf , 0x030a },
|
||||
/* ALPS Modules with non-standard id */
|
||||
{ 0, 0, 0, 0x044e , 0x3001 },
|
||||
{ 0, 0, 0, 0x044e , 0x3002 },
|
||||
/* Ericsson with non-standard id */
|
||||
{ 0, 0, 0, 0x0bdb , 0x1002 }
|
||||
};
|
||||
|
||||
/* add a device to the list of connected devices */
|
||||
static bt_usb_dev*
|
||||
spawn_device(const usb_device* usb_dev)
|
||||
{
|
||||
int32 i;
|
||||
status_t err = B_OK;
|
||||
bt_usb_dev* new_bt_dev = NULL;
|
||||
|
||||
flowf("add_device()\n");
|
||||
|
||||
/* 16 usb dongles... u are unsane */
|
||||
if (dev_count >= MAX_BT_GENERIC_USB_DEVICES) {
|
||||
flowf("device table full\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* try the allocation */
|
||||
new_bt_dev = (bt_usb_dev*)malloc(sizeof(bt_usb_dev));
|
||||
if ( new_bt_dev == NULL ) {
|
||||
flowf("no memoery allocating\n");
|
||||
goto exit;
|
||||
}
|
||||
memset(new_bt_dev, 0, sizeof(bt_usb_dev) );
|
||||
|
||||
/* We will need this sem for some flow control */
|
||||
new_bt_dev->cmd_complete = create_sem(1, ID "cmd_complete");
|
||||
if (new_bt_dev->cmd_complete < 0) {
|
||||
err = new_bt_dev->cmd_complete;
|
||||
goto bail0;
|
||||
}
|
||||
|
||||
/* and this for something else */
|
||||
new_bt_dev->lock = create_sem(1, ID "lock");
|
||||
if (new_bt_dev->lock < 0) {
|
||||
err = new_bt_dev->lock;
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
/* find a free slot and fill out the name */
|
||||
acquire_sem(dev_table_sem);
|
||||
for (i = 0; i < MAX_BT_GENERIC_USB_DEVICES; i++) {
|
||||
if (bt_usb_devices[i] == NULL) {
|
||||
bt_usb_devices[i] = new_bt_dev;
|
||||
sprintf(new_bt_dev->name, "%s/%ld", DEVICE_PATH, i);
|
||||
new_bt_dev->num = i;
|
||||
debugf("added device %p %ld %s\n", bt_usb_devices[i] ,new_bt_dev->num,new_bt_dev->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
release_sem_etc(dev_table_sem, 1, B_DO_NOT_RESCHEDULE);
|
||||
|
||||
/* In the case we cannot us */
|
||||
if (bt_usb_devices[i] != new_bt_dev) {
|
||||
flowf("Device could not be added\n");
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
new_bt_dev->dev = usb_dev;
|
||||
/* TODO am i actually gonna use this? */
|
||||
new_bt_dev->open_count = 0;
|
||||
|
||||
dev_count++;
|
||||
return new_bt_dev;
|
||||
|
||||
bail2:
|
||||
delete_sem(new_bt_dev->lock);
|
||||
bail1:
|
||||
delete_sem(new_bt_dev->cmd_complete);
|
||||
bail0:
|
||||
free(new_bt_dev);
|
||||
exit:
|
||||
return new_bt_dev;
|
||||
}
|
||||
|
||||
|
||||
/* remove a device from the list of connected devices */
|
||||
static void
|
||||
kill_device(bt_usb_dev* dev)
|
||||
{
|
||||
uint16 i;
|
||||
debugf("remove_device(%p)\n", dev);
|
||||
|
||||
delete_sem(dev->lock);
|
||||
delete_sem(dev->cmd_complete);
|
||||
|
||||
free(dev);
|
||||
dev_count--;
|
||||
}
|
||||
|
||||
|
||||
bt_usb_dev*
|
||||
fetch_device(bt_usb_dev* dev, hci_id hid)
|
||||
{
|
||||
int i;
|
||||
|
||||
debugf("(%p)\n", dev);
|
||||
|
||||
acquire_sem(dev_table_sem);
|
||||
if (dev != NULL)
|
||||
for (i = 0; i < MAX_BT_GENERIC_USB_DEVICES; i++) {
|
||||
/* somehow the device is still around */
|
||||
if (bt_usb_devices[i] == dev) {
|
||||
release_sem_etc(dev_table_sem, 1, B_DO_NOT_RESCHEDULE);
|
||||
return bt_usb_devices[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
for (i = 0; i < MAX_BT_GENERIC_USB_DEVICES; i++) {
|
||||
/* somehow the device is still around */
|
||||
if (bt_usb_devices[i] != NULL && bt_usb_devices[i]->hdev == hid) {
|
||||
release_sem_etc(dev_table_sem, 1, B_DO_NOT_RESCHEDULE);
|
||||
return bt_usb_devices[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
release_sem_etc(dev_table_sem, 1, B_DO_NOT_RESCHEDULE);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
#pragma mark -
|
||||
#endif
|
||||
|
||||
static bt_hci_transport bt_usb_hooks =
|
||||
{
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
H2,
|
||||
"H2 Bluetooth Device"
|
||||
};
|
||||
|
||||
/* called by USB Manager when device is added to the USB */
|
||||
static status_t
|
||||
device_added(const usb_device* dev, void** cookie)
|
||||
{
|
||||
const usb_interface_info* interface;
|
||||
const usb_device_descriptor* desc;
|
||||
const usb_configuration_info* config;
|
||||
const usb_interface_info* uif;
|
||||
const usb_endpoint_info* ep;
|
||||
|
||||
status_t err = B_ERROR;
|
||||
bt_usb_dev* new_bt_dev = spawn_device(dev);
|
||||
int e, i;
|
||||
|
||||
debugf("device_added(%ld, %p)\n", dev, new_bt_dev);
|
||||
|
||||
if (new_bt_dev == NULL) {
|
||||
flowf("Couldn't allocate device record.\n");
|
||||
err = ENOMEM;
|
||||
goto bail_no_mem;
|
||||
}
|
||||
|
||||
/* we only have 1 configuration number 0 */
|
||||
config = usb->get_nth_configuration(dev, 0);
|
||||
//dump_usb_configuration_info(config);
|
||||
if (config == NULL) {
|
||||
flowf("couldn't get default config.\n");
|
||||
err = B_ERROR;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
debugf("found %ld alt interfaces.\n", config->interface->alt_count);
|
||||
|
||||
/* set first interface */
|
||||
interface = &config->interface->alt[0];
|
||||
err = usb->set_alt_interface(new_bt_dev->dev, interface);
|
||||
|
||||
if (err != B_OK) {
|
||||
debugf("set_alt_interface() returned %ld.\n", err);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* call set_configuration() only after calling set_alt_interface()*/
|
||||
err = usb->set_configuration(dev, config);
|
||||
if (err != B_OK) {
|
||||
debugf("set_configuration() returned %ld.\n", err);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Place to find out whats our concrete device and set up some special info to our driver */
|
||||
/* TODO: if this code increases too much reconsider this implementation*/
|
||||
desc = usb->get_device_descriptor(dev);
|
||||
if ( desc->vendor_id == 0x0a5c && desc->product_id == 0x200a && desc->product_id == 0x2009) {
|
||||
// Tecom Device VENDOR_ID 0x0a5c PRODUCT_ID 0x2035
|
||||
new_bt_dev->driver_info = BT_WILL_NEED_A_RESET | BT_SCO_NOT_WORKING;
|
||||
}
|
||||
/*
|
||||
else if ( desc->vendor_id == YOUR_VENDOR_HERE && desc->product_id == YOUR_PRODUCT_HERE ) {
|
||||
YOUR_SPECIAL_FLAGS_HERE
|
||||
}
|
||||
*/
|
||||
|
||||
if (new_bt_dev->driver_info & BT_IGNORE_THIS_DEVICE){
|
||||
err = ENODEV;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
// security check
|
||||
if (config->interface->active->descr->interface_number > 0){
|
||||
debugf("Strange condition happened %d\n", config->interface->active->descr->interface_number);
|
||||
err = B_ERROR;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
debugf("Found %ld interfaces. Expected 3\n", config->interface_count);
|
||||
/* Find endpoints that we need */
|
||||
uif = config->interface->active;
|
||||
for (e = 0; e < uif->descr->num_endpoints; e++) {
|
||||
|
||||
ep = &uif->endpoint[e];
|
||||
switch (ep->descr->attributes & USB_ENDPOINT_ATTR_MASK)
|
||||
{
|
||||
case USB_ENDPOINT_ATTR_INTERRUPT:
|
||||
if (ep->descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)
|
||||
{
|
||||
new_bt_dev->intr_in_ep = ep;
|
||||
new_bt_dev->max_packet_size_intr_in = ep->descr->max_packet_size;
|
||||
flowf("INT in\n");
|
||||
} else
|
||||
{
|
||||
;
|
||||
flowf("INT out\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_ENDPOINT_ATTR_BULK:
|
||||
if (ep->descr->endpoint_address & USB_ENDPOINT_ADDR_DIR_IN)
|
||||
{
|
||||
new_bt_dev->bulk_in_ep = ep;
|
||||
new_bt_dev->max_packet_size_bulk_in = ep->descr->max_packet_size;;
|
||||
flowf("BULK int\n");
|
||||
} else
|
||||
{
|
||||
new_bt_dev->bulk_out_ep = ep;
|
||||
new_bt_dev->max_packet_size_bulk_out = ep->descr->max_packet_size;;
|
||||
flowf("BULK out\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!new_bt_dev->bulk_in_ep || !new_bt_dev->bulk_out_ep || !new_bt_dev->intr_in_ep) {
|
||||
flowf("Minimal # endpoints for BT not found\n");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
// Look into the devices suported to understand this
|
||||
if (new_bt_dev->driver_info & BT_DIGIANSWER)
|
||||
new_bt_dev->ctrl_req = USB_TYPE_VENDOR;
|
||||
else
|
||||
new_bt_dev->ctrl_req = USB_TYPE_CLASS;
|
||||
|
||||
new_bt_dev->connected = true;
|
||||
|
||||
/* set the cookie that will be passed to other USB
|
||||
hook functions (currently device_removed() is the only other) */
|
||||
*cookie = new_bt_dev;
|
||||
debugf("Ok %p\n",bt_usb_devices[0]);
|
||||
return B_OK;
|
||||
|
||||
bail:
|
||||
kill_device(new_bt_dev);
|
||||
bail_no_mem:
|
||||
*cookie = NULL;
|
||||
done:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* called by USB Manager when device is removed from the USB */
|
||||
static status_t
|
||||
device_removed(void* cookie)
|
||||
{
|
||||
int32 i;
|
||||
void* item;
|
||||
bt_usb_dev* bdev = (bt_usb_dev*) fetch_device(cookie, 0);
|
||||
|
||||
debugf("device_removed(%p)\n", bdev);
|
||||
|
||||
if (bdev == NULL) {
|
||||
flowf("Weird condition...\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
// TODO: Consider some other place
|
||||
// TX
|
||||
for (i = 0; i < BT_DRIVER_TXCOVERAGE; i++) {
|
||||
if (i = BT_COMMAND)
|
||||
while ((item = list_remove_head_item(&bdev->nbuffersTx[i])) != NULL) {
|
||||
snb_free(item);
|
||||
}
|
||||
else
|
||||
while ((item = list_remove_head_item(&bdev->nbuffersTx[i])) != NULL) {
|
||||
nb_destroy(item);
|
||||
}
|
||||
|
||||
}
|
||||
// RX
|
||||
for (i = 0; i < BT_DRIVER_RXCOVERAGE; i++) {
|
||||
nb_destroy(bdev->nbufferRx[i]);
|
||||
}
|
||||
snb_free(bdev->eventRx);
|
||||
|
||||
purge_room(&bdev->eventRoom);
|
||||
purge_room(&bdev->aclRoom);
|
||||
// TODO: Consider some other place
|
||||
|
||||
if (hci != NULL)
|
||||
hci->UnregisterDriver(bdev->hdev);
|
||||
|
||||
bdev->connected = false;
|
||||
|
||||
/* TODO: maybe we still need this struct for close and free hooks */
|
||||
kill_device(bdev);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static usb_notify_hooks notify_hooks =
|
||||
{
|
||||
&device_added,
|
||||
&device_removed
|
||||
};
|
||||
|
||||
#if 0
|
||||
#pragma mark -
|
||||
#endif
|
||||
|
||||
/* implements the POSIX open() */
|
||||
static status_t
|
||||
device_open(const char *name, uint32 flags, void **cookie)
|
||||
{
|
||||
status_t err = ENODEV;
|
||||
bt_usb_dev* bdev = NULL;
|
||||
hci_id hdev;
|
||||
int i;
|
||||
|
||||
flowf("device_open()\n");
|
||||
|
||||
acquire_sem(dev_table_sem);
|
||||
for (i = 0; i < MAX_BT_GENERIC_USB_DEVICES; i++) {
|
||||
if (bt_usb_devices[i] && !strcmp(name, bt_usb_devices[i]->name)) {
|
||||
bdev = bt_usb_devices[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
release_sem_etc(dev_table_sem, 1, B_DO_NOT_RESCHEDULE);
|
||||
|
||||
if (bdev == NULL) {
|
||||
flowf("Device not found in the open list!");
|
||||
*cookie = NULL;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
acquire_sem(bdev->lock);
|
||||
// Set HCI_RUNNING
|
||||
if ( TEST_AND_SET(&bdev->state, RUNNING) ) {
|
||||
flowf("dev already running! - reOpened device!\n");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// TX structures
|
||||
for (i = 0; i < BT_DRIVER_TXCOVERAGE; i++) {
|
||||
list_init(&bdev->nbuffersTx[i]);
|
||||
bdev->nbuffersPendingTx[i] = 0;
|
||||
}
|
||||
|
||||
// RX structures
|
||||
bdev->eventRx = NULL;
|
||||
for (i = 0; i < BT_DRIVER_RXCOVERAGE; i++) {
|
||||
bdev->nbufferRx[i] = NULL;
|
||||
}
|
||||
|
||||
|
||||
// dumping the USB frames
|
||||
init_room(&bdev->eventRoom);
|
||||
init_room(&bdev->aclRoom);
|
||||
//Init_room(new_bt_dev->scoRoom);
|
||||
|
||||
list_init(&bdev->snetBufferRecycleTrash);
|
||||
|
||||
// Allocate set and register the HCI device
|
||||
if (hci != NULL) {
|
||||
// TODO: Fill the transport descriptor
|
||||
hci->RegisterDriver(&bt_usb_hooks, &hdev, (void*)bdev);
|
||||
|
||||
if ( err != B_OK )
|
||||
{
|
||||
flowf("Impossible to register a hci device.\n");
|
||||
hdev = bdev->num; /* XXX: Lets try to go on*/
|
||||
}
|
||||
}
|
||||
else {
|
||||
hdev = bdev->num;
|
||||
}
|
||||
bdev->hdev = hdev;
|
||||
|
||||
// H: set the special flags
|
||||
|
||||
// EVENTS
|
||||
err = submit_rx_event(bdev);
|
||||
if (err != B_OK)
|
||||
goto unrun;
|
||||
#if BT_DRIVER_SUPPORTS_ACL
|
||||
// ACL
|
||||
for (i = 0; i < MAX_ACL_IN_WINDOW; i++) {
|
||||
err = submit_rx_acl(bdev);
|
||||
if (err != B_OK && i == 0 )
|
||||
goto unrun;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if BT_DRIVER_SUPPORTS_SCO
|
||||
// TODO: SCO / eSCO
|
||||
#endif
|
||||
|
||||
*cookie = bdev;
|
||||
release_sem(bdev->lock);
|
||||
|
||||
return B_OK;
|
||||
|
||||
unrun:
|
||||
CLEAR_BIT(bdev->state, RUNNING); // Set the flaq in the HCI world
|
||||
flowf("Queuing failed device stops running\n");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* called when a client calls POSIX close() on the driver, but I/O
|
||||
** requests may still be pending */
|
||||
static status_t
|
||||
device_close(void *cookie)
|
||||
{
|
||||
bt_usb_dev* bdev = (bt_usb_dev*)cookie;
|
||||
|
||||
if (bdev == NULL)
|
||||
panic("bad cookie");
|
||||
|
||||
debugf("device_close() called on %s\n", DEVICE_PATH, bdev->hdev );
|
||||
|
||||
|
||||
if (!TEST_AND_CLEAR(&bdev->state, RUNNING) ) {
|
||||
flowf("Device wasnt running!!!\n");
|
||||
}
|
||||
|
||||
flowf("Stopping device and cancelling queues...\n");
|
||||
|
||||
if ( bdev->intr_in_ep != NULL ) {
|
||||
usb->cancel_queued_transfers(bdev->intr_in_ep->handle);
|
||||
|
||||
} else {
|
||||
flowf("Cancelling impossible EVENTS\n");
|
||||
}
|
||||
|
||||
if (bdev->bulk_in_ep!=NULL) {
|
||||
usb->cancel_queued_transfers(bdev->bulk_in_ep->handle);
|
||||
} else {
|
||||
flowf("Cancelling impossible ACL in\n");
|
||||
}
|
||||
|
||||
if (bdev->bulk_out_ep!=NULL) {
|
||||
usb->cancel_queued_transfers(bdev->bulk_out_ep->handle);
|
||||
} else {
|
||||
flowf("Cancelling impossible ACL out\n");
|
||||
}
|
||||
|
||||
// TODO: Kill if its not connected?
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* called after device_close(), when all pending I/O requests have
|
||||
* returned */
|
||||
static status_t
|
||||
device_free (void *cookie)
|
||||
{
|
||||
status_t err = B_OK;
|
||||
bt_usb_dev* dev = (bt_usb_dev*)cookie;
|
||||
|
||||
debugf("device_free() called on \"%s %ld\"\n",DEVICE_PATH, dev->num);
|
||||
|
||||
|
||||
if (--dev->open_count == 0) {
|
||||
|
||||
/* GotoLowPower */
|
||||
// interesting .....
|
||||
}
|
||||
else {
|
||||
/* The last client has closed, and the device is no longer
|
||||
connected, so remove it from the list. */
|
||||
|
||||
}
|
||||
|
||||
// TODO: Kill if its not connected?
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* implements the POSIX ioctl() */
|
||||
static status_t
|
||||
device_control(void *cookie, uint32 msg, void *params, size_t size)
|
||||
{
|
||||
status_t err = B_ERROR;
|
||||
bt_usb_dev* dev = (bt_usb_dev*)cookie;
|
||||
snet_buffer* snbuf;
|
||||
TOUCH(size);
|
||||
|
||||
debugf("ioctl() opcode %ld size %ld.\n", msg, size);
|
||||
|
||||
if (dev == NULL) {
|
||||
flowf("Bad cookie\n");
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
if (params == NULL) {
|
||||
flowf("Invalid pointer control\n");
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
acquire_sem(dev->lock);
|
||||
|
||||
switch (msg) {
|
||||
case ISSUE_BT_COMMAND:
|
||||
#ifdef BT_IOCTLS_PASS_SIZE
|
||||
if (size == 0) {
|
||||
flowf("Invalid size control\n");
|
||||
err = B_BAD_VALUE;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
size = (*((size_t*)params));
|
||||
((size_t*)params)++;
|
||||
#endif
|
||||
|
||||
// TODO: Reuse from some TXcompleted queue
|
||||
snbuf = snb_create(size);
|
||||
snb_put(snbuf, params, size);
|
||||
|
||||
err = send_command(dev->hdev, snbuf);
|
||||
|
||||
break;
|
||||
|
||||
case ISSUE_STATICS:
|
||||
memcpy(params, &dev->stat, sizeof(bt_hci_statistics));
|
||||
err = B_OK;
|
||||
break;
|
||||
|
||||
default:
|
||||
debugf("Invalid opcode %ld.\n", msg);
|
||||
err = B_DEV_INVALID_IOCTL;
|
||||
break;
|
||||
}
|
||||
|
||||
release_sem(dev->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* implements the POSIX read() */
|
||||
static status_t
|
||||
device_read(void *cookie, off_t pos, void *buf, size_t *count)
|
||||
{
|
||||
debugf("Reading... pos = %ld || count = %ld\n", pos, *count);
|
||||
|
||||
*count = 0;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/* implements the POSIX write() */
|
||||
static status_t
|
||||
device_write(void *cookie, off_t pos, const void *buf, size_t *count)
|
||||
{
|
||||
flowf("device_write()\n");
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
#if 0
|
||||
#pragma mark -
|
||||
#endif
|
||||
|
||||
/* called each time the driver is loaded by the kernel */
|
||||
status_t
|
||||
init_driver(void)
|
||||
{
|
||||
int j;
|
||||
flowf("init_driver()\n");
|
||||
|
||||
// HCI MODULE INITS
|
||||
if (get_module(hci_name,(module_info**)&hci) != B_OK) {
|
||||
debugf("cannot get module \"%s\"\n", hci_name);
|
||||
#ifndef BT_SURVIVE_WITHOUT_HCI
|
||||
return B_ERROR;
|
||||
#endif
|
||||
}
|
||||
debugf("hci module at %p\n", hci);
|
||||
|
||||
// USB MODULE INITS
|
||||
if (get_module(usb_name,(module_info**)&usb) != B_OK) {
|
||||
debugf("cannot get module \"%s\"\n", usb_name);
|
||||
goto err_release;
|
||||
}
|
||||
debugf("usb module at %p\n", usb);
|
||||
|
||||
|
||||
if (get_module(NET_BUFFER_MODULE_NAME,(module_info**)&nb) != B_OK) {
|
||||
debugf("cannot get module \"%s\"\n", NET_BUFFER_MODULE_NAME);
|
||||
#ifndef BT_SURVIVE_WITHOUT_NET_BUFFERS
|
||||
goto err_release;
|
||||
#endif
|
||||
}
|
||||
debugf("nb module at %p\n", nb);
|
||||
|
||||
// GENERAL INITS
|
||||
dev_table_sem = create_sem(1, ID "dev_table_lock");
|
||||
if (dev_table_sem < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (j = 0; j < MAX_BT_GENERIC_USB_DEVICES; j++) {
|
||||
bt_usb_devices[j] = NULL;
|
||||
}
|
||||
|
||||
/* After here device_added and publish devices hooks are called
|
||||
be carefull USB devs */
|
||||
usb->register_driver(DEVICE_NAME, supported_devices, 1, NULL);
|
||||
usb->install_notify(DEVICE_NAME, ¬ify_hooks);
|
||||
|
||||
return B_OK;
|
||||
|
||||
err: // Releasing
|
||||
put_module(usb_name);
|
||||
err_release:
|
||||
put_module(hci_name);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/* called just before the kernel unloads the driver */
|
||||
void
|
||||
uninit_driver(void)
|
||||
{
|
||||
int32 j;
|
||||
|
||||
flowf("uninit_driver()\n");
|
||||
|
||||
for (j = 0; j < MAX_BT_GENERIC_USB_DEVICES; j++) {
|
||||
|
||||
if (publish_names[j] != NULL)
|
||||
free(publish_names[j]);
|
||||
|
||||
if (bt_usb_devices[j] != NULL) {
|
||||
// if (connected_dev != NULL) {
|
||||
// debugf("Device %p still exists.\n", connected_dev);
|
||||
// }
|
||||
kill_device(bt_usb_devices[j]);
|
||||
bt_usb_devices[j] = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
usb->uninstall_notify(DEVICE_NAME);
|
||||
usb->register_driver(DEVICE_NAME, supported_devices, 1, NULL);
|
||||
|
||||
/* Releasing modules */
|
||||
put_module(usb_name);
|
||||
put_module(hci_name);
|
||||
// TODO: netbuffers
|
||||
|
||||
delete_sem(dev_table_sem);
|
||||
}
|
||||
|
||||
|
||||
const char**
|
||||
publish_devices(void)
|
||||
{
|
||||
int32 j;
|
||||
int32 i = 0;
|
||||
|
||||
char* str;
|
||||
|
||||
flowf("publish_devices()\n");
|
||||
|
||||
for (j = 0; j < MAX_BT_GENERIC_USB_DEVICES; j++) {
|
||||
if (publish_names[j]) {
|
||||
free(publish_names[j]);
|
||||
publish_names[j] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
acquire_sem(dev_table_sem);
|
||||
for (j = 0; j < MAX_BT_GENERIC_USB_DEVICES; j++)
|
||||
{
|
||||
if (bt_usb_devices[j] != NULL && bt_usb_devices[j]->connected)
|
||||
{
|
||||
str = strdup(bt_usb_devices[j]->name);
|
||||
if (str) {
|
||||
publish_names[i++] = str;
|
||||
debugf("publishing %s\n", bt_usb_devices[j]->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
release_sem_etc(dev_table_sem, 1, B_DO_NOT_RESCHEDULE);
|
||||
|
||||
publish_names[i] = NULL;
|
||||
debugf("published %ld devices\n", i);
|
||||
|
||||
// TODO: this method might make better memory use
|
||||
// dev_names = (char**)malloc(sizeof (char*) * (dev_count+1));
|
||||
// if (dev_names) {
|
||||
// for (i = 0; i < MAX_NUM_DEVS; i++) {
|
||||
// if ((dev != NULL) &&
|
||||
// (dev_names[i] = (char*)malloc(strlen(DEVICE_PATH)+2/* num + \n */))) {
|
||||
// sprintf(dev_names[i], "%s%ld", DEVICE_PATH, dev->num);
|
||||
// debugf("publishing \"%s\"\n", dev_names[i]);
|
||||
// }
|
||||
// }
|
||||
|
||||
return (const char**)publish_names;
|
||||
}
|
||||
|
||||
|
||||
static device_hooks hooks = {
|
||||
device_open,
|
||||
device_close,
|
||||
device_free,
|
||||
device_control,
|
||||
device_read,
|
||||
device_write,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
device_hooks*
|
||||
find_device(const char* name)
|
||||
{
|
||||
debugf("find_device(%s)\n", name);
|
||||
|
||||
return &hooks;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
|
||||
*
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _H2GENERIC_H_
|
||||
#define _H2GENERIC_H_
|
||||
|
||||
#include <OS.h>
|
||||
#include <USB.h>
|
||||
|
||||
#include <util/list.h>
|
||||
#include <btHCI.h>
|
||||
|
||||
#include <net/net_buffer.h>
|
||||
|
||||
#include "h2cfg.h"
|
||||
#include "snet_buffer.h"
|
||||
|
||||
/* USB definitions for the generic device*/
|
||||
#define UDCLASS_WIRELESS 0xe0
|
||||
#define UDSUBCLASS_RF 0x01
|
||||
#define UDPROTO_BLUETOOTH 0x01
|
||||
|
||||
#define TRANSPORT_NAME "h2"
|
||||
#define DEVICE_NAME "generic"
|
||||
#define DEVICE_PATH "bus/bluetooth/" TRANSPORT_NAME "/" DEVICE_NAME
|
||||
|
||||
#define ID DEVICE_NAME ": " /* prefix for debug messages */
|
||||
|
||||
#define USB_TYPE_CLASS (0x01 << 5) /// Check if it is in some other header
|
||||
#define USB_TYPE_VENDOR (0x02 << 5)
|
||||
|
||||
|
||||
// Expecting nobody is gonna have 16 USB-BT dongles connected in their system
|
||||
#define MAX_BT_GENERIC_USB_DEVICES 16
|
||||
|
||||
extern usb_module_info *usb;
|
||||
extern bt_hci_module_info *hci;
|
||||
extern struct net_buffer_module_info *nb;
|
||||
|
||||
#define MAX_COMMAND_WINDOW 1
|
||||
#define MAX_ACL_OUT_WINDOW 4
|
||||
#define MAX_ACL_IN_WINDOW 1
|
||||
|
||||
#define MAX_NUM_QUEUED_PACKETS 1
|
||||
#define NUM_BUFFERS 1
|
||||
|
||||
typedef struct bt_usb_dev bt_usb_dev;
|
||||
|
||||
struct bt_usb_dev {
|
||||
const usb_device* dev; /* opaque handle */
|
||||
hci_id hdev; /* HCI device id*/
|
||||
|
||||
char name[B_OS_NAME_LENGTH];
|
||||
bool connected; /* is the device plugged into the USB? */
|
||||
int32 open_count; /* number of clients of the device */
|
||||
int32 num; /* instance number of the device */
|
||||
|
||||
sem_id lock; /* synchronize access to the device */
|
||||
sem_id cmd_complete; /* To synchronize completitions */
|
||||
|
||||
size_t actual_len; /* length of data returned by command */
|
||||
status_t cmd_status; /* result of command */
|
||||
|
||||
uint8 ctrl_req;
|
||||
uint8 driver_info;
|
||||
uint32 state;
|
||||
|
||||
bt_hci_statistics stat;
|
||||
|
||||
const usb_endpoint_info *bulk_in_ep;
|
||||
uint16 max_packet_size_bulk_in;
|
||||
const usb_endpoint_info *bulk_out_ep;
|
||||
uint16 max_packet_size_bulk_out;
|
||||
const usb_endpoint_info *intr_in_ep;
|
||||
uint16 max_packet_size_intr_in;
|
||||
|
||||
#ifdef BLUETOOTH_SUPPORTS_SCO
|
||||
const usb_endpoint_info *iso_in_ep;
|
||||
const usb_endpoint_info *iso_out_ep;
|
||||
#endif
|
||||
|
||||
/* This so called rooms, are for dumping the USB RX frames
|
||||
and try to reuse the allocations. see util submodule */
|
||||
struct list eventRoom;
|
||||
struct list aclRoom;
|
||||
|
||||
// Tx buffers: net_buffers for BT_ACL and snet_buffers for BT_COMMAND
|
||||
// in the same array
|
||||
struct list nbuffersTx[BT_DRIVER_TXCOVERAGE];
|
||||
uint32 nbuffersPendingTx[BT_DRIVER_TXCOVERAGE];
|
||||
|
||||
// Rx buffer
|
||||
net_buffer* nbufferRx[BT_DRIVER_RXCOVERAGE]; /* Wasting 1 pointer for BT_EVENT */
|
||||
snet_buffer* eventRx; /* <- which we hold here */
|
||||
|
||||
// for who ever needs preallocated buffers
|
||||
struct list snetBufferRecycleTrash;
|
||||
|
||||
};
|
||||
|
||||
bt_usb_dev* fetch_device(bt_usb_dev* dev, hci_id hid);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
|
||||
*
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "h2generic.h"
|
||||
#include "h2transactions.h"
|
||||
#include "h2upper.h"
|
||||
#include "h2util.h"
|
||||
|
||||
#include <btHCI.h>
|
||||
#include <btHCI_event.h>
|
||||
#include <btHCI_acl.h>
|
||||
|
||||
#include <ByteOrder.h>
|
||||
|
||||
#define BT_DEBUG_THIS_MODULE
|
||||
#include "btDebug.h"
|
||||
|
||||
#if 0
|
||||
#pragma mark --- RX Complete ---
|
||||
#endif
|
||||
|
||||
|
||||
status_t
|
||||
assembly_rx(bt_usb_dev* bdev, bt_packet_t type, void *data, int count)
|
||||
{
|
||||
|
||||
net_buffer* nbuf;
|
||||
snet_buffer* snbuf;
|
||||
|
||||
size_t currentPacketLen;
|
||||
size_t expectedPacketLen;
|
||||
|
||||
bdev->stat.bytesRX += count;
|
||||
|
||||
while (count) {
|
||||
|
||||
if (type == BT_EVENT)
|
||||
snbuf = bdev->eventRx;
|
||||
else
|
||||
nbuf = bdev->nbufferRx[type];
|
||||
|
||||
debugf("count %d %p %p\n",count, nbuf, nb);
|
||||
|
||||
|
||||
if ( (type != BT_EVENT && nbuf == NULL) ||
|
||||
(type == BT_EVENT && snb_completed(snbuf)) ) {
|
||||
|
||||
/* new buffer incoming */
|
||||
switch (type) {
|
||||
case BT_EVENT:
|
||||
if (count >= HCI_EVENT_HDR_SIZE) {
|
||||
|
||||
struct hci_event_header* headerPkt = data;
|
||||
expectedPacketLen = HCI_EVENT_HDR_SIZE + headerPkt->elen;
|
||||
snbuf = bdev->eventRx = snb_fetch(&bdev->snetBufferRecycleTrash, expectedPacketLen);
|
||||
|
||||
} else {
|
||||
flowf("EVENT frame corrupted\n");
|
||||
return -EILSEQ;
|
||||
}
|
||||
break;
|
||||
|
||||
case BT_ACL:
|
||||
if (count >= HCI_ACL_HDR_SIZE) {
|
||||
|
||||
struct hci_acl_header* headerPkt = data;
|
||||
expectedPacketLen = HCI_ACL_HDR_SIZE + B_LENDIAN_TO_HOST_INT16(headerPkt->alen);
|
||||
|
||||
/* Create the buffer */
|
||||
bdev->nbufferRx[type] = nbuf = nb->create(expectedPacketLen);
|
||||
nbuf->protocol = type;
|
||||
|
||||
|
||||
} else {
|
||||
flowf("ACL frame corrupted\n");
|
||||
return -EILSEQ;
|
||||
}
|
||||
break;
|
||||
|
||||
case BT_SCO:
|
||||
break;
|
||||
|
||||
default:
|
||||
panic("unkown packet type in assembly");
|
||||
break;
|
||||
}
|
||||
#if 0
|
||||
if (nb == NULL) {
|
||||
port_id port;
|
||||
/* Coded for test purpose only we should panic here */
|
||||
debugf("net_buffers are not ready post manually %d/%d\n",count,expectedPacketLen);
|
||||
port = find_port(BT_USERLAND_PORT_NAME);
|
||||
if (port != B_NAME_NOT_FOUND) {
|
||||
(void)write_port(port, PACK_HEADER_PORT(bdev->num,type), data, count);
|
||||
return B_OK;
|
||||
}
|
||||
panic("Algorithm just ready to arrive here");
|
||||
return B_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
currentPacketLen = expectedPacketLen;
|
||||
|
||||
}
|
||||
else {
|
||||
/* Continuation */
|
||||
if (type == BT_EVENT)
|
||||
currentPacketLen = get_expected_size(nbuf) - nbuf->size;
|
||||
else
|
||||
currentPacketLen = snb_remaining_to_put(snbuf);
|
||||
}
|
||||
|
||||
currentPacketLen = min(currentPacketLen, count);
|
||||
|
||||
if (type == BT_EVENT)
|
||||
snb_put(snbuf, data, currentPacketLen);
|
||||
else
|
||||
nb->append(nbuf, data, currentPacketLen);
|
||||
|
||||
/* Complete frame? */
|
||||
if (type == BT_EVENT && snb_completed(snbuf)) {
|
||||
|
||||
flowf("Frame goes up!\n");
|
||||
snbuf = bdev->eventRx = NULL;
|
||||
post_packet_up(bdev, type, snbuf);
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (type != BT_EVENT && (get_expected_size(nbuf) - nbuf->size) == 0 ) {
|
||||
|
||||
flowf("Frame goes up!\n");
|
||||
bdev->nbufferRx[type] = nbuf = NULL;
|
||||
post_packet_up(bdev, type, nbuf);
|
||||
}
|
||||
|
||||
/* in case in the pipe there is info about the next buffer ... */
|
||||
count -= currentPacketLen;
|
||||
data += currentPacketLen;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
event_complete(void* cookie, uint32 status, void* data, uint32 actual_len)
|
||||
{
|
||||
|
||||
bt_usb_dev* bdev = cookie;
|
||||
status_t err;
|
||||
|
||||
/* TODO: check are we running? */
|
||||
|
||||
if (status != B_OK || actual_len == 0)
|
||||
goto resubmit;
|
||||
|
||||
if ( assembly_rx(cookie, BT_EVENT, data, actual_len) == B_OK ) {
|
||||
bdev->stat.successfulTX++;
|
||||
} else {
|
||||
bdev->stat.errorRX++;
|
||||
}
|
||||
|
||||
resubmit:
|
||||
|
||||
err = usb->queue_interrupt(bdev->intr_in_ep->handle,
|
||||
data, bdev->max_packet_size_intr_in ,
|
||||
event_complete, bdev);
|
||||
|
||||
if (err != B_OK ) {
|
||||
reuse_room(&bdev->eventRoom, data);
|
||||
bdev->stat.rejectedRX++;
|
||||
debugf("RX event resubmittion failed %s\n",strerror(err));
|
||||
}
|
||||
else {
|
||||
bdev->stat.acceptedRX++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
acl_rx_complete(void* cookie, uint32 status, void* data, uint32 actual_len)
|
||||
{
|
||||
bt_usb_dev* bdev = cookie;
|
||||
status_t err;
|
||||
|
||||
/* TODO: check are we running? */
|
||||
|
||||
if (status != B_OK || actual_len == 0)
|
||||
goto resubmit;
|
||||
|
||||
if ( assembly_rx(cookie, BT_ACL, data, actual_len) == B_OK ) {
|
||||
bdev->stat.successfulRX++;
|
||||
} else {
|
||||
bdev->stat.errorRX++;
|
||||
}
|
||||
|
||||
resubmit:
|
||||
|
||||
err = usb->queue_bulk(bdev->bulk_in_ep->handle, data,
|
||||
max(HCI_MAX_FRAME_SIZE,bdev->max_packet_size_bulk_in),
|
||||
acl_rx_complete, bdev);
|
||||
|
||||
if (err != B_OK ) {
|
||||
reuse_room(&bdev->aclRoom, data);
|
||||
bdev->stat.rejectedRX++;
|
||||
debugf("RX acl resubmittion failed %s\n",strerror(err));
|
||||
}
|
||||
else {
|
||||
bdev->stat.acceptedRX++;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
#pragma mark --- RX ---
|
||||
#endif
|
||||
|
||||
status_t
|
||||
submit_rx_event(bt_usb_dev* bdev)
|
||||
{
|
||||
status_t err;
|
||||
size_t size = bdev->max_packet_size_intr_in;
|
||||
void* buf = alloc_room(&bdev->eventRoom, size);
|
||||
|
||||
if (buf == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
err = usb->queue_interrupt(bdev->intr_in_ep->handle,
|
||||
buf, size ,
|
||||
event_complete, bdev);
|
||||
if (err != B_OK ) {
|
||||
reuse_room(&bdev->eventRoom, buf);
|
||||
bdev->stat.rejectedRX++;
|
||||
}
|
||||
else {
|
||||
bdev->stat.acceptedRX++;
|
||||
debugf("Accepted RX Event\n",bdev->stat.acceptedRX);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
submit_rx_acl(bt_usb_dev* bdev)
|
||||
{
|
||||
|
||||
status_t err;
|
||||
size_t size = max(HCI_MAX_FRAME_SIZE,bdev->max_packet_size_bulk_in);
|
||||
void* buf = alloc_room(&bdev->aclRoom, size);
|
||||
|
||||
if (buf == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
err = usb->queue_bulk(bdev->bulk_in_ep->handle, buf, size ,
|
||||
acl_rx_complete, bdev);
|
||||
|
||||
if (err != B_OK ) {
|
||||
reuse_room(&bdev->aclRoom, buf);
|
||||
bdev->stat.rejectedRX++;
|
||||
}
|
||||
else {
|
||||
bdev->stat.acceptedRX++;
|
||||
}
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
submit_rx_sco(bt_usb_dev* bdev)
|
||||
{
|
||||
|
||||
/* not yet implemented */
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
#pragma mark --- TX Complete ---
|
||||
#endif
|
||||
|
||||
void
|
||||
command_complete(void* cookie, uint32 status, void* data, uint32 actual_len)
|
||||
{
|
||||
snet_buffer* snbuf = (snet_buffer*) cookie;
|
||||
bt_usb_dev* bdev = snb_cookie(snbuf);
|
||||
status_t err;
|
||||
|
||||
debugf("%d %02x:%02x:%02x:\n", actual_len, ((uint8*)data)[0],((uint8*)data)[1],((uint8*)data)[2]);
|
||||
|
||||
if (status != B_OK) {
|
||||
|
||||
bdev->stat.successfulTX++;
|
||||
bdev->stat.bytesTX += actual_len;
|
||||
}
|
||||
else {
|
||||
bdev->stat.errorTX++;
|
||||
/* the packet has been lost */
|
||||
/* too late to requeue it? */
|
||||
}
|
||||
|
||||
snb_park(&bdev->snetBufferRecycleTrash, snbuf);
|
||||
|
||||
#ifdef BT_RESCHEDULING_AFTER_COMPLETITIONS
|
||||
// TODO: check just the empty queues?
|
||||
schedTxProcessing(bdev);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
aclTxComplete(void* cookie, uint32 status, void* data, uint32 actual_len)
|
||||
{
|
||||
|
||||
net_buffer* nbuf = (net_buffer*) cookie;
|
||||
bt_usb_dev* bdev = GET_DEVICE(nbuf);
|
||||
status_t err;
|
||||
|
||||
if (status != B_OK) {
|
||||
|
||||
bdev->stat.successfulTX++;
|
||||
bdev->stat.bytesTX += actual_len;
|
||||
}
|
||||
else {
|
||||
bdev->stat.errorTX++;
|
||||
/* the packet has been lost */
|
||||
/* too late to requeue it? */
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
status_t err;
|
||||
|
||||
uint8 bRequestType = bdev->ctrl_req;
|
||||
uint8 bRequest = 0;
|
||||
uint16 wIndex = 0;
|
||||
uint16 value = 0;
|
||||
uint16 wLength = B_HOST_TO_LENDIAN_INT16(snb_size(snbuf));
|
||||
|
||||
/* set cookie */
|
||||
snb_set_cookie(snbuf, bdev);
|
||||
|
||||
err = usb->queue_request(bdev->dev, bRequestType, bRequest,
|
||||
value, wIndex, wLength,
|
||||
snb_get(snbuf), wLength //???
|
||||
,command_complete, snbuf);
|
||||
|
||||
if (err != B_OK ) {
|
||||
bdev->stat.rejectedTX++;
|
||||
}
|
||||
else {
|
||||
bdev->stat.acceptedTX++;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
status_t
|
||||
submit_tx_acl(bt_usb_dev* bdev, net_buffer* nbuf)
|
||||
{
|
||||
status_t err;
|
||||
|
||||
/* set cookie */
|
||||
SET_DEVICE(nbuf,bdev->hdev);
|
||||
|
||||
err = usb->queue_bulk(bdev->bulk_out_ep->handle,
|
||||
nb_get_whole_buffer(nbuf), nbuf->size,
|
||||
aclTxComplete, nbuf);
|
||||
|
||||
if (err != B_OK ) {
|
||||
bdev->stat.rejectedTX++;
|
||||
}
|
||||
else {
|
||||
bdev->stat.acceptedTX++;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
submit_tx_sco(bt_usb_dev* bdev)
|
||||
{
|
||||
|
||||
/* not yet implemented */
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
|
||||
*
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _H2TRANSACTION_H_
|
||||
#define _H2TRANSACTION_H_
|
||||
|
||||
#include "h2generic.h"
|
||||
|
||||
status_t submit_rx_event(bt_usb_dev* bdev);
|
||||
status_t submit_rx_acl(bt_usb_dev* bdev);
|
||||
status_t submit_rx_sco(bt_usb_dev* bdev);
|
||||
|
||||
status_t submit_tx_command(bt_usb_dev* bdev, snet_buffer* snbuf);
|
||||
status_t submit_tx_acl(bt_usb_dev* bdev, net_buffer* nbuf);
|
||||
status_t submit_tx_sco(bt_usb_dev* bdev);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
|
||||
*
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "h2upper.h"
|
||||
#include "h2transactions.h"
|
||||
|
||||
#define BT_DEBUG_THIS_MODULE
|
||||
#include "btDebug.h"
|
||||
|
||||
#include <btHCITransport.h>
|
||||
#include <bluetooth.h>
|
||||
|
||||
#include "snet_buffer.h"
|
||||
|
||||
/* TODO: split for commands and comunication(ACL&SCO) */
|
||||
void
|
||||
sched_tx_processing(bt_usb_dev* bdev)
|
||||
{
|
||||
|
||||
net_buffer* nbuf;
|
||||
snet_buffer* snbuf;
|
||||
status_t err;
|
||||
flowf("sched\n")
|
||||
if (!TEST_AND_SET(&bdev->state, PROCESSING)) {
|
||||
/* We are not processing in another thread so... START!! */
|
||||
|
||||
do {
|
||||
/* Do while this bit is on... so someone should set it before we stop the iterations*/
|
||||
CLEAR_BIT(bdev->state, SENDING);
|
||||
/* check Commands*/
|
||||
#ifdef EMPTY_COMMAND_QUEUE
|
||||
while (!list_is_empty(&bdev->nbuffersTx[BT_COMMAND]) ) {
|
||||
#else
|
||||
if (!list_is_empty(&bdev->nbuffersTx[BT_COMMAND]) ) {
|
||||
#endif
|
||||
snbuf = list_remove_head_item(&bdev->nbuffersTx[BT_COMMAND]);
|
||||
err = submit_tx_command(bdev, snbuf);
|
||||
if (err != B_OK) {
|
||||
/* re-head it*/
|
||||
list_insert_item_before(&bdev->nbuffersTx[BT_COMMAND],
|
||||
list_get_first_item(&bdev->nbuffersTx[BT_COMMAND]), snbuf);
|
||||
}
|
||||
}
|
||||
|
||||
/* check Acl */
|
||||
#define EMPTY_ACL_QUEUE
|
||||
#ifdef EMPTY_ACL_QUEUE
|
||||
while (!list_is_empty(&bdev->nbuffersTx[BT_ACL]) ) {
|
||||
#else
|
||||
if (!list_is_empty(&bdev->nbuffersTx[BT_ACL]) ) {
|
||||
#endif
|
||||
nbuf = list_remove_head_item(&bdev->nbuffersTx[BT_ACL]);
|
||||
err = submit_tx_acl(bdev, nbuf);
|
||||
if (err != B_OK) {
|
||||
/* re-head it*/
|
||||
list_insert_item_before(&bdev->nbuffersTx[BT_ACL],
|
||||
list_get_first_item(&bdev->nbuffersTx[BT_ACL]), nbuf);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!list_is_empty(&bdev->nbuffersTx[BT_SCO]) ) {
|
||||
/* TODO to be implemented */
|
||||
|
||||
}
|
||||
|
||||
|
||||
} while (GET_BIT(bdev->state, SENDING));
|
||||
|
||||
CLEAR_BIT(bdev->state, PROCESSING);
|
||||
|
||||
} else {
|
||||
/* We are processing so MARK that we need to still go on with that ... */
|
||||
SET_BIT(bdev->state, SENDING);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
status_t
|
||||
post_packet_up(bt_usb_dev* bdev, bt_packet_t type, void* buf)
|
||||
{
|
||||
|
||||
status_t err;
|
||||
port_id port;
|
||||
|
||||
if (hci == NULL) {
|
||||
|
||||
err = B_ERROR;
|
||||
|
||||
// ERROR but we will try to send if its a event!
|
||||
if (type != BT_EVENT) {
|
||||
err = B_ERROR;
|
||||
} else {
|
||||
snet_buffer* snbuf = (snet_buffer*)buf;
|
||||
|
||||
flowf("HCI not present, Posting to userland\n");
|
||||
port = find_port(BT_USERLAND_PORT_NAME);
|
||||
if (port != B_NAME_NOT_FOUND) {
|
||||
|
||||
|
||||
err = write_port_etc(port, PACK_HEADER_PORT(bdev->num,type),
|
||||
snb_get(snbuf), snb_size(snbuf), B_TIMEOUT, 1*1000*1000);
|
||||
if (err != B_OK)
|
||||
debugf("Error posting userland %s\n",strerror(err));
|
||||
|
||||
}
|
||||
else {
|
||||
flowf("ERROR:bluetooth_server not found for posting\n");
|
||||
err = B_NAME_NOT_FOUND;
|
||||
}
|
||||
/* No need to free the buffer at allocation is gonna be reused */
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO: Upper layer comunication
|
||||
/* Not freeing because is being used by upper layers*/
|
||||
}
|
||||
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
status_t
|
||||
send_packet(hci_id hid, bt_packet_t type, net_buffer* nbuf)
|
||||
{
|
||||
bt_usb_dev* bdev = fetch_device(NULL, hid);
|
||||
status_t err = B_OK;
|
||||
|
||||
if (bdev == NULL)
|
||||
return B_ERROR;
|
||||
|
||||
// TODO: check if device is actually ready for this
|
||||
// TODO: Lock Device
|
||||
|
||||
if (nbuf != NULL) {
|
||||
if (type != nbuf->protocol) // a bit strict maybe?
|
||||
panic("Upper layer has not filled correctly a packet");
|
||||
|
||||
switch (type) {
|
||||
case BT_COMMAND:
|
||||
case BT_ACL:
|
||||
case BT_SCO:
|
||||
|
||||
list_add_item(&bdev->nbuffersTx[type],nbuf);
|
||||
bdev->nbuffersPendingTx[type]++;
|
||||
break;
|
||||
default:
|
||||
debugf("Unkown packet type for sending %d\n",type);
|
||||
// TODO: free the net_buffer -> no, allow upper layer
|
||||
// handle it with the given error
|
||||
err = B_BAD_VALUE;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
flowf("tx sched provoked");
|
||||
}
|
||||
|
||||
// TODO: check if device is actually ready for this
|
||||
// TODO: unLock device
|
||||
|
||||
/* sched in All cases even if nbuf is null (hidden way to provoke re-scheduling)*/
|
||||
sched_tx_processing(bdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
send_command(hci_id hid, snet_buffer* snbuf)
|
||||
{
|
||||
|
||||
bt_usb_dev* bdev = fetch_device(NULL, hid);
|
||||
status_t err = B_OK;
|
||||
|
||||
if (bdev == NULL)
|
||||
return B_ERROR;
|
||||
|
||||
// TODO: check if device is actually ready for this
|
||||
// TODO: mutex?
|
||||
|
||||
if (snbuf != NULL) {
|
||||
|
||||
list_add_item(&bdev->nbuffersTx[BT_COMMAND],snbuf);
|
||||
bdev->nbuffersPendingTx[BT_COMMAND]++;
|
||||
} else {
|
||||
err = B_BAD_VALUE;
|
||||
flowf("tx sched provoked");
|
||||
}
|
||||
|
||||
// TODO: check if device is actually ready for this
|
||||
// TODO: mutex?
|
||||
|
||||
/* sched in All cases even if nbuf is null (hidden way to provoke re-scheduling)*/
|
||||
sched_tx_processing(bdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
|
||||
*
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _H2UPPER_H_
|
||||
#define _H2UPPER_H_
|
||||
|
||||
#include <util/list.h>
|
||||
|
||||
#include "h2generic.h"
|
||||
|
||||
#define PACK_HEADER_PORT(x,y) (x<<24|y<<16)
|
||||
|
||||
status_t post_packet_up(bt_usb_dev* bdev, bt_packet_t type, void* buf);
|
||||
status_t send_packet(hci_id hid, bt_packet_t type, net_buffer* nbuf);
|
||||
|
||||
void sched_tx_processing(bt_usb_dev* bdev);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
|
||||
*
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "h2upper.h"
|
||||
#include "h2util.h"
|
||||
#include "h2transactions.h"
|
||||
|
||||
#define BT_DEBUG_THIS_MODULE
|
||||
#include "btDebug.h"
|
||||
|
||||
#include <btHCITransport.h>
|
||||
#include <btHCI_acl.h>
|
||||
#include <btHCI_command.h>
|
||||
#include <btHCI_event.h>
|
||||
|
||||
void*
|
||||
nb_get_whole_buffer(net_buffer* nbuf)
|
||||
{
|
||||
|
||||
void* conPointer;
|
||||
status_t err;
|
||||
|
||||
/* the job could be already done */
|
||||
// !!! it could be trash from other upper protocols...
|
||||
if (nbuf->COOKIEFIELD != NULL)
|
||||
return (void*)nbuf->COOKIEFIELD;
|
||||
|
||||
if (nb == NULL)
|
||||
goto fail;
|
||||
|
||||
err = nb->direct_access(nbuf, 0, nbuf->size, &conPointer);
|
||||
|
||||
if (err != B_OK) {
|
||||
|
||||
/* pity, we are gonna need a realocation */
|
||||
nbuf->COOKIEFIELD = (uint32) malloc(nbuf->size);
|
||||
if (nbuf->COOKIEFIELD == NULL)
|
||||
goto fail;
|
||||
|
||||
err = nb->write(nbuf, 0, (void*) nbuf->COOKIEFIELD, nbuf->size);
|
||||
if (err != B_OK)
|
||||
goto free;
|
||||
|
||||
conPointer = (void*)nbuf->COOKIEFIELD;
|
||||
|
||||
}
|
||||
|
||||
return conPointer;
|
||||
|
||||
free:
|
||||
free(nbuf->COOKIEFIELD);
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nb_destroy(net_buffer* nbuf)
|
||||
{
|
||||
|
||||
/* Free possible allocated */
|
||||
if (nbuf->COOKIEFIELD != NULL)
|
||||
free((void*)nbuf->COOKIEFIELD);
|
||||
|
||||
// TODO check for survivers...
|
||||
if (nb != NULL)
|
||||
nb->free(nbuf);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Check from the completition if the queue is empty */
|
||||
size_t
|
||||
get_expected_size(net_buffer* nbuf)
|
||||
{
|
||||
|
||||
if (nbuf == NULL)
|
||||
panic("Analizing NULL packet");
|
||||
|
||||
switch (nbuf->protocol) {
|
||||
|
||||
case BT_ACL: {
|
||||
struct hci_acl_header* header = nb_get_whole_buffer(nbuf);
|
||||
return header->alen;
|
||||
}
|
||||
|
||||
case BT_COMMAND: {
|
||||
struct hci_command_header* header = nb_get_whole_buffer(nbuf);
|
||||
return header->clen;
|
||||
}
|
||||
|
||||
|
||||
case BT_EVENT: {
|
||||
struct hci_event_header* header = nb_get_whole_buffer(nbuf);
|
||||
return header->elen;
|
||||
}
|
||||
default:
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
#pragma mark - room util -
|
||||
#endif
|
||||
|
||||
inline void
|
||||
init_room(struct list* l)
|
||||
{
|
||||
list_init(l);
|
||||
}
|
||||
|
||||
|
||||
void*
|
||||
alloc_room(struct list* l, size_t size)
|
||||
{
|
||||
|
||||
void* item = list_get_first_item(l);
|
||||
|
||||
if (item == NULL)
|
||||
item = (void*) malloc(size);
|
||||
|
||||
return item;
|
||||
|
||||
}
|
||||
|
||||
|
||||
inline void
|
||||
reuse_room(struct list* l, void* room)
|
||||
{
|
||||
list_add_item(l, room);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
purge_room(struct list* l)
|
||||
{
|
||||
void* item;
|
||||
|
||||
while ((item = list_remove_head_item(l)) != NULL) {
|
||||
free(item);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
|
||||
*
|
||||
* All rights reserved. Distributed under the terms of the MIT License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _H2UTIL_H_
|
||||
#define _H2UTIL_H_
|
||||
|
||||
#include <util/list.h>
|
||||
|
||||
#include "h2generic.h"
|
||||
|
||||
/* net buffer utils for ACL, to be reviwed */
|
||||
#define DEVICEFIELD type
|
||||
#define SET_DEVICE(nbuf,hid) (nbuf->DEVICEFIELD=(nbuf->DEVICEFIELD&0xFFF0)|(hid&0xF))
|
||||
#define GET_DEVICE(nbuf) fetch_device(NULL,(nbuf->DEVICEFIELD&0x0F))
|
||||
|
||||
#define COOKIEFIELD flags
|
||||
void* nb_get_whole_buffer(net_buffer* nbuf);
|
||||
void nb_destroy(net_buffer* nbuf);
|
||||
size_t get_expected_size(net_buffer* nbuf);
|
||||
|
||||
/* Room utils */
|
||||
inline void init_room(struct list* l);
|
||||
void* alloc_room(struct list* l, size_t size);
|
||||
inline void reuse_room(struct list* l, void* room);
|
||||
void purge_room(struct list* l);
|
||||
|
||||
/* list utils */
|
||||
#define list_purge(x) purge_room(x)
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user