diff --git a/src/servers/bluetooth/BPortNot.cpp b/src/servers/bluetooth/BPortNot.cpp new file mode 100644 index 0000000000..7a12f4ebd2 --- /dev/null +++ b/src/servers/bluetooth/BPortNot.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com + * + * All rights reserved. Distributed under the terms of the MIT License. + * + */ + +#include "BPortNot.h" +#include "Output.h" + +#include "BluetoothServer.h" +#include "LocalDeviceImpl.h" + +#include +#include + +#include + +BPortNot::BPortNot(BluetoothServer* app, const char* name) { + + ourapp = (BluetoothServer*) app; + + fPort = find_port(name); + if ( fPort == B_NAME_NOT_FOUND ) + { + fPort = create_port(256, name); + Output::Instance()->Post("Event Listener Port Created\n",BLACKBOARD_GENERAL); + } +} + +void +BPortNot::loop() +{ + size_t size; + size_t ssize; + int32 code; + + struct hci_event_header *hdr; + + LocalDeviceImpl* ld; + + while (code != HCI_HAIKU_EVENT_SERVER_QUITTING) { + + size = port_buffer_size(fPort); + + /* TODO: Consider using snb_buff like structure here or + max event size to prevent mallocs & frees */ + hdr = (struct hci_event_header *) malloc(size); + ssize = read_port(fPort, &code, hdr, size); + + if (size != ssize) { + Output::Instance()->Post("Event size not matching", BLACKBOARD_GENERAL); + continue; + } + + if (size <= 0) { + Output::Instance()->Post("Suspicious empty event", BLACKBOARD_GENERAL); + continue; + } + + // we only handle events + if (GET_PORTCODE_TYPE(code)!= BT_EVENT) { + Output::Instance()->Post("Wrong type frame code", BLACKBOARD_GENERAL); + continue; + } + +#if 0 + for (int i=0 ; iLocateLocalDeviceImpl(GET_PORTCODE_HID(code)); + if (ld == NULL) { + Output::Instance()->Post("LocalDevice could not be fetched", BLACKBOARD_EVENTS); + continue; + } + + + ld->HandleEvent(hdr); + + + // free the event + free(hdr); + } + +} diff --git a/src/servers/bluetooth/BPortNot.h b/src/servers/bluetooth/BPortNot.h new file mode 100644 index 0000000000..3a4330cabf --- /dev/null +++ b/src/servers/bluetooth/BPortNot.h @@ -0,0 +1,28 @@ +/* + * Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com + * + * All rights reserved. Distributed under the terms of the MIT License. + * + */ + +#ifndef _BT_PORT_NOTIFICATION +#define _BT_PORT_NOTIFICATION + +#include + +class BluetoothServer; + +class BPortNot { + +public: + BPortNot(BluetoothServer* app, const char* name) ; + void loop(); + + port_id fPort; + port_id sdp_request_port; + thread_id sdp_server_thread; + + BluetoothServer* ourapp; +}; + +#endif diff --git a/src/servers/bluetooth/BluetoothServer.cpp b/src/servers/bluetooth/BluetoothServer.cpp new file mode 100644 index 0000000000..ea263c7264 --- /dev/null +++ b/src/servers/bluetooth/BluetoothServer.cpp @@ -0,0 +1,559 @@ +/* + * Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com + * + * All rights reserved. Distributed under the terms of the MIT License. + * + */ + +#include +#include +#include + + +#include +#include +#include +#include +#include +#include + + +#include +#include + +#include +#include +#include + +#include "LocalDeviceImpl.h" +#include "BluetoothServer.h" +#include "Output.h" + + +BluetoothServer::BluetoothServer() : BApplication(BLUETOOTH_SIGNATURE) +{ + Output::Instance()->Run(); + Output::Instance()->SetTitle("Bluetooth message gathering"); + + Output::Instance()->AddTab("General", BLACKBOARD_GENERAL); + Output::Instance()->AddTab("Device Manager", BLACKBOARD_DEVICEMANAGER); + Output::Instance()->AddTab("Events", BLACKBOARD_EVENTS); + Output::Instance()->AddTab("Kit", BLACKBOARD_KIT); + + + ShowWindow(Output::Instance()); + + fDeviceManager = new DeviceManager(); + fLocalDevicesList.MakeEmpty(); + + fEventListener = spawn_thread(notification_Thread, "BT port listener" , B_DISPLAY_PRIORITY , this); + + +} + +bool BluetoothServer::QuitRequested(void) +{ + // Finish quitting + Output::Instance()->Lock(); + Output::Instance()->Quit(); + + +/* HCIDelegate *hd = NULL; + while ((hd = (HCIDelegate *)fDelegatesList.RemoveItem((int32)0)) !=NULL) + delete hd; +*/ + + printf("Accepting quitting of the application\n"); + return BApplication::QuitRequested(); +} + +void BluetoothServer::ArgvReceived(int32 argc, char **argv) +{ + if (argc>1) { + if (strcmp(argv[1], "--finish") == 0) + PostMessage(B_QUIT_REQUESTED); + + else { + + + } + } + +} + + +void BluetoothServer::ReadyToRun(void) { + + fDeviceManager->StartMonitoringDevice("bluetooth/h2generic"); + + + // Launch the notifier thread + if ( resume_thread(fEventListener) != B_OK ) + { + Output::Instance()->Post("Bluetooth port listener failed\n", BLACKBOARD_GENERAL); + } + + Output::Instance()->Post("Bluetooth server Ready\n", BLACKBOARD_GENERAL); +} + +void BluetoothServer::AppActivated(bool act) { + + printf("Activated %d\n",act); + +} + + + +void BluetoothServer::MessageReceived(BMessage *message) +{ + BMessage reply; + status_t status = B_WOULD_BLOCK; // mark somehow.. do not reply anything + + switch(message->what) + { + + case BT_MSG_ADD_DEVICE: + { + BString str; + message->FindString("name", &str); + BPath path(str.String()); + + (Output::Instance()->Post( str.String(), BLACKBOARD_GENERAL)); + (Output::Instance()->Post(" requested LocalDevice\n", BLACKBOARD_GENERAL)); + + LocalDeviceImpl* ldi = LocalDeviceImpl::CreateTransportAccessor(&path); + + if (ldi->GetID() >= 0) { + fLocalDevicesList.AddItem(ldi); + Output::Instance()->AddTab("Local Device", BLACKBOARD_LD_OFFSET + ldi->GetID()); + (Output::Instance()->Post( str.String(), BLACKBOARD_LD_OFFSET + ldi->GetID())); + (Output::Instance()->Post(" LocalDevice added\n", BLACKBOARD_LD_OFFSET + ldi->GetID())); + + + } else { + (Output::Instance()->Post("Adding LocalDevice failed\n", BLACKBOARD_GENERAL)); + } + + status = B_WOULD_BLOCK; + } + + case BT_MSG_COUNT_LOCAL_DEVICES: + status = HandleLocalDevicesCount(message, &reply); + break; + + case BT_MSG_ADQUIRE_LOCAL_DEVICE: + status = HandleAcquireLocalDevice(message, &reply); + break; + + case BT_MSG_GET_FRIENDLY_NAME: + status = HandleGetFriendlyName(message, &reply); + break; + + case BT_MSG_GET_ADDRESS: + status = HandleGetAddress(message, &reply); + break; + + case BT_MSG_HANDLE_SIMPLE_REQUEST: + status = HandleSimpleRequest(message, &reply); + break; + + + /* Handle if the bluetooth preferences is running */ + case B_SOME_APP_LAUNCHED: + { + const char *signature; + // TODO: what's this for? + if (message->FindString("be:signature", &signature)==B_OK) { + printf("input_server : %s\n", signature); + if (strcmp(signature, "application/x-vnd.Be-TSKB")==0) { + + } + } + return; + } + + + default: + BApplication::MessageReceived(message); + break; + } + + /* Can we reply right now? */ + if (status != B_WOULD_BLOCK) { + reply.AddInt32("status", status); + message->SendReply(&reply); + printf("Sending reply message\n"); + } + +} + +#if 0 +#pragma mark - +#endif + +LocalDeviceImpl* +BluetoothServer::LocateDelegateFromMessage(BMessage* message) +{ + LocalDeviceImpl* ldi = NULL; + hci_id hid; + + if (message->FindInt32("hci_id", &hid) == B_OK) + { + /* Try to find out when a ID was specified */ + int index; + + for (index = 0; index < fLocalDevicesList.CountItems() ; index ++) { + + ldi = fLocalDevicesList.ItemAt(index); + if (ldi->GetID() == hid) { + break; + } + } + } + + return ldi; +} + +LocalDeviceImpl* +BluetoothServer::LocateLocalDeviceImpl(hci_id hid) +{ + + /* Try to find out when a ID was specified */ + int index; + + for (index = 0; index < fLocalDevicesList.CountItems() ; index ++) { + + LocalDeviceImpl* ldi = fLocalDevicesList.ItemAt(index); + if (ldi->GetID() == hid) { + return ldi; + } + } + + return NULL; +} + + +#if 0 +#pragma - Messages reply +#endif + +status_t +BluetoothServer::HandleLocalDevicesCount(BMessage* message, BMessage* reply) +{ + return reply->AddInt32("count", fLocalDevicesList.CountItems()); +} + + +status_t +BluetoothServer::HandleAcquireLocalDevice(BMessage* message, BMessage* reply) +{ + hci_id hid; + ssize_t size; + bdaddr_t bdaddr; + LocalDeviceImpl* ldi = NULL; + int32 index = 0; + + if (message->FindInt32("hci_id", &hid) == B_OK) + { + Output::Instance()->Post("GetLocalDevice requested with id\n", BLACKBOARD_KIT); + ldi = LocateDelegateFromMessage(message); + + } else if (message->FindData("bdaddr", B_ANY_TYPE, (const void**)&bdaddr, &size ) == B_OK) + { + /* Try to find out when the user specified the address */ + Output::Instance()->Post("GetLocalDevice requested with bdaddr\n", BLACKBOARD_KIT); + for (index = 0; index < fLocalDevicesList.CountItems() ; index ++) { + + bdaddr_t local; + ldi = fLocalDevicesList.ItemAt(index); + if ((ldi->GetAddress(&local, message) == B_OK) && bacmp(&local, &bdaddr)) { + break; + } + } + + } else + { + /* Careless, any device not performing operations will be fine */ + Output::Instance()->Post("GetLocalDevice requested\n", BLACKBOARD_KIT); + for (index = 0; index < fLocalDevicesList.CountItems() ; index ++) { + + ldi = fLocalDevicesList.ItemAt(index); + printf("Requesting local device %ld\n", ldi->GetID()); + if (ldi != NULL && ldi->Available()) + { + printf("dev ours %ld\n", ldi->GetID()); + break; + } + + } + } + + if (index <= fLocalDevicesList.CountItems() && ldi != NULL && ldi->Available()) + { + Output::Instance()->Post("Device acquired\n", BLACKBOARD_KIT); + ldi->Acquire(); + return reply->AddInt32("hci_id", hid); + } + + return B_ERROR; + +} + + +status_t +BluetoothServer::HandleGetFriendlyName(BMessage* message, BMessage* reply) +{ + LocalDeviceImpl* ldi = LocateDelegateFromMessage(message); + BString name; + + if (ldi == NULL) + return B_ERROR; + + /* If the device was ocupied... Autlock?->LocalDeviceImpl will decide */ + if (ldi->GetFriendlyName(name, DetachCurrentMessage()) == B_OK) { + + return reply->AddString("friendlyname", name); + } + + return B_WOULD_BLOCK; +} + + +status_t +BluetoothServer::HandleGetAddress(BMessage* message, BMessage* reply) +{ + LocalDeviceImpl* ldi = LocateDelegateFromMessage(message); + bdaddr_t bdaddr; + + if (ldi == NULL) + return B_ERROR; + + /* If the device was ocupied... Autlock?->LocalDeviceImpl will decide */ + status_t status = ldi->GetAddress(&bdaddr, DetachCurrentMessage()); + if ( status == B_OK) { + + return reply->AddData("bdaddr", B_ANY_TYPE, &bdaddr, sizeof(bdaddr_t)); + } + + return status; +} + + +status_t +BluetoothServer::HandleSimpleRequest(BMessage* message, BMessage* reply) +{ + + LocalDeviceImpl* ldi = LocateDelegateFromMessage(message); + BString propertyRequested; + + // Find out if there is a property being requested, + if (message->FindString("property", &propertyRequested) == B_OK) { + // Check if the property has been already retrieved + + if (ldi->IsPropertyAvailable(propertyRequested)) { + + // Dump everything + reply->AddMessage("properties",ldi->GetPropertiesMessage()); + return B_OK; + } + + } + + // we are gonna need issue the command ... + if (ldi->ProcessSimpleRequest(DetachCurrentMessage()) == B_OK) + return B_WOULD_BLOCK; + else + return B_ERROR; + +} + + +#if 0 +#pragma mark - +#endif + + +int32 +BluetoothServer::notification_Thread(void* data) +{ + BPortNot notifierd( (BluetoothServer*) data , BT_USERLAND_PORT_NAME); + + notifierd.loop(); + return B_NO_ERROR; +} + +int32 +BluetoothServer::sdp_server_Thread(void* data) +{ + + return B_NO_ERROR; +} + +#if 0 + +void BluetoothServer::DevicesWatching(void) { + + BDirectory* hdoses; + BEntry entrada; + BPath path; + + //status_t err; + int fd1 = -1; + + // only cheks the actual driver which we have + hdoses = new BDirectory("/dev/bus/bluetooth/h2/"); +// hdoses = new BDirectory("/dev/bus/bluetooth/h3/..."); +// hdoses = new BDirectory("/dev/bus/bluetooth/h4/..."); + Output::Instance()->Post("Exploring present devices ...\n",1); + + while (hdoses->GetNextEntry(&entrada,true) == B_OK) { + Output::Instance()->Post((char*)path.Path(), 1); + entrada.GetPath(&path); + if (entrada.IsDirectory()) { + Output::Instance()->Post((char*)path.Path(),1); + BDirectory* driver_directory = new BDirectory(path.Path()); + BEntry driver_entry; + + syslog(LOG_ALERT, "Bluetooth driver %s\n",path.Path()); + fprintf(stderr, "Bluetooth driver %s\n",path.Path()); + + while (driver_directory->GetNextEntry(&driver_entry,true) == B_OK) { + Output::Instance()->Post((char*)path.Path(),1); + driver_entry.GetPath(&path); + fd1 = open(path.Path(), O_RDWR); + + + + // TODO: Watching all folders under and set some kind of internal structure that hold all + // LocalDevices. + // devloop = new DeviceLooper(); + // devloop->StartMonitoringDevice("bus/bluetooth/h2"); + + if (fd1 < 0) { + syslog(LOG_ALERT,BT "Error opening device %s\n",path.Path()); + fprintf(stderr, "Error opening device %s\n",path.Path()); + } + else + { + struct { + size_t size; + struct hci_command_header header; + //struct hci_rp_read_bd_addr body; + } __attribute__ ((packed)) cm1; + + struct { + size_t size; + struct hci_command_header header; + struct hci_cp_inquiry body; + } __attribute__ ((packed)) cm2; + + struct { + size_t size; + struct hci_command_header header; + struct hci_remote_name_request body; + } __attribute__ ((packed)) cm3; + + struct { + size_t size; + struct hci_command_header header; + struct hci_cp_create_conn body; + } __attribute__ ((packed)) cm4; + + + syslog(LOG_ALERT,BT "Registering device %s\n",path.Path()); + fprintf(stderr, "Opening %s\n",path.Path()); + + cm1.size = sizeof(struct hci_command_header); + cm1.header.opcode = B_HOST_TO_LENDIAN_INT16(hci_opcode_pack(OGF_CONTROL_BASEBAND, OCF_RESET)); + cm1.header.clen = 0; + ioctl(fd1, ISSUE_BT_COMMAND, &cm1, sizeof(cm1)); + + /*cm1.size = sizeof(struct hci_command_header); + cm1.header.opcode = B_HOST_TO_LENDIAN_INT16(hci_opcode_pack(OGF_CONTROL_BASEBAND, OCF_READ_LOCAL_NAME)); + cm1.header.clen = 0; + ioctl(fd1, ISSUE_BT_COMMAND, &cm1, sizeof(cm1)); + + cm1.size = sizeof(struct hci_command_header); + cm1.header.opcode = B_HOST_TO_LENDIAN_INT16(hci_opcode_pack(OGF_INFORMATIONAL_PARAM, OCF_READ_BD_ADDR)); + cm1.header.clen = 0; + ioctl(fd1, ISSUE_BT_COMMAND, &cm1, sizeof(cm1)); + + cm2.size = sizeof(struct hci_command_header)+sizeof(struct hci_cp_inquiry); + cm2.header.opcode = B_HOST_TO_LENDIAN_INT16(hci_opcode_pack(OGF_LINK_CTL, OCF_INQUIRY)); + cm2.body.lap[0] = ((B_BT_GIAC & 0x000000FF)); + cm2.body.lap[1] = ((B_BT_GIAC & 0x0000FF00) >> 8); + cm2.body.lap[2] = ((B_BT_GIAC & 0x00FF0000) >> 16); + cm2.body.length = 0x15; + cm2.body.num_rsp = 8; + cm2.header.clen = 5; + ioctl(fd1, ISSUE_BT_COMMAND, &cm2, sizeof(cm1)); + snooze(60*1000*1000);*/ + cm3.size = sizeof(struct hci_command_header)+sizeof(struct hci_remote_name_request); + cm3.header.opcode = B_HOST_TO_LENDIAN_INT16(hci_opcode_pack(OGF_LINK_CONTROL, OCF_REMOTE_NAME_REQUEST)); + cm3.body.bdaddr.b[0] = 0x92; + cm3.body.bdaddr.b[1] = 0xd3; + cm3.body.bdaddr.b[2] = 0xaf; + cm3.body.bdaddr.b[3] = 0xd9; + cm3.body.bdaddr.b[4] = 0x0a; + cm3.body.bdaddr.b[5] = 0x00; + cm3.body.pscan_rep_mode = 1; + cm3.body.clock_offset = 0xc7; + cm3.header.clen = 10; + ioctl(fd1, ISSUE_BT_COMMAND, &cm3, sizeof(cm3)); + /* + cm4.size = sizeof(struct hci_command_header)+sizeof(struct hci_cp_create_conn); + cm4.header.opcode = B_HOST_TO_LENDIAN_INT16(hci_opcode_pack(OGF_LINK_CTL, OCF_CREATE_CONN)); + cm4.body.bdaddr.b[0] = 0x92; + cm4.body.bdaddr.b[1] = 0xd3; + cm4.body.bdaddr.b[2] = 0xaf; + cm4.body.bdaddr.b[3] = 0xd9; + cm4.body.bdaddr.b[4] = 0x0a; + cm4.body.bdaddr.b[5] = 0x00; + cm4.body.pkt_type = 0xFFFF; + cm4.body.pscan_rep_mode = 1; + cm4.body.pscan_mode = 0; + cm4.body.clock_offset = 0xc7; + cm4.body.role_switch = 1; + cm4.header.clen = 13; + ioctl(fd1, ISSUE_BT_COMMAND, &cm4, sizeof(cm4)); + */ + } + } + } + } + + syslog(LOG_ALERT,BT "All devices registered\n"); + fprintf(stderr, "Waiting with opened devices\n"); + +} +#endif + +void +BluetoothServer::ShowWindow(BWindow* pWindow) +{ + pWindow->Lock(); + if (pWindow->IsHidden()) + pWindow->Show(); + else + pWindow->Activate(); + pWindow->Unlock(); +} + + +#if 0 +#pragma mark - +#endif + +int +main(int /*argc*/, char** /*argv*/) +{ + setbuf(stdout,NULL); + + BluetoothServer* bluetoothServer = new BluetoothServer; + + bluetoothServer->Run(); + delete bluetoothServer; + + return 0; +} + diff --git a/src/servers/bluetooth/BluetoothServer.h b/src/servers/bluetooth/BluetoothServer.h new file mode 100644 index 0000000000..35d8be5f1c --- /dev/null +++ b/src/servers/bluetooth/BluetoothServer.h @@ -0,0 +1,83 @@ +/* + * Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com + * + * All rights reserved. Distributed under the terms of the MIT License. + * + */ + +#ifndef _BLUETOOTH_SERVER_APP_H +#define _BLUETOOTH_SERVER_APP_H + +#include +#include +#include +#include + +#include "BPortNot.h" +#include "HCIDelegate.h" +#include "DeviceManager.h" +#include "LocalDeviceImpl.h" + +#include +#include +#include +#include + + +#define BT "bluetooth_server: " + +#define BLACKBOARD_GENERAL 0 +#define BLACKBOARD_DEVICEMANAGER 1 +#define BLACKBOARD_EVENTS 2 +#define BLACKBOARD_KIT 3 +#define BLACKBOARD_LD_OFFSET 4 + +typedef BObjectList LocalDevicesList; + + +class BluetoothServer : public BApplication +{ +public: + + BluetoothServer(); + + virtual bool QuitRequested(void); + virtual void ArgvReceived(int32 argc, char **argv); + virtual void ReadyToRun(void); + + + virtual void AppActivated(bool act); + virtual void MessageReceived(BMessage *message); + + + static int32 notification_Thread(void* data); + static int32 sdp_server_Thread(void* data); + + /* Messages reply */ + status_t HandleLocalDevicesCount(BMessage* message, BMessage* reply); + status_t HandleAcquireLocalDevice(BMessage* message, BMessage* reply); + status_t HandleGetFriendlyName(BMessage* message, BMessage* reply); + status_t HandleGetAddress(BMessage* message, BMessage* reply); + + status_t HandleSimpleRequest(BMessage* message, BMessage* reply); + + + LocalDeviceImpl* LocateLocalDeviceImpl(hci_id hid); + +private: + + LocalDeviceImpl* LocateDelegateFromMessage(BMessage* message); + + void ShowWindow(BWindow* pWindow); + + LocalDevicesList fLocalDevicesList; + // UI + + // Notification system + thread_id fEventListener; + DeviceManager* fDeviceManager; + + BPoint fCenter; +}; + +#endif diff --git a/src/servers/bluetooth/CommandManager.cpp b/src/servers/bluetooth/CommandManager.cpp new file mode 100644 index 0000000000..a0b19e4883 --- /dev/null +++ b/src/servers/bluetooth/CommandManager.cpp @@ -0,0 +1,109 @@ +/* + * Copyright 2008 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com + * + * All rights reserved. Distributed under the terms of the MIT License. + * + */ + + +#include +#include + +#include + +#include "CommandManager.h" + + +inline void* buildCommand(uint8 ogf, uint8 ocf, void** param, size_t psize, size_t* outsize) +{ + struct hci_command_header* header; + +#ifdef BT_IOCTLS_PASS_SIZE + header = (struct hci_command_header*) malloc(psize + sizeof(struct hci_command_header)); + *outsize = psize + sizeof(struct hci_command_header); +#else + size_t* size = (size_t*)malloc(psize + sizeof(struct hci_command_header) + sizeof(size_t)); + *outsize = psize + sizeof(struct hci_command_header) + sizeof(size_t); + + *size = psize + sizeof(struct hci_command_header); + header = (struct hci_command_header*) (((uint8*)size)+4); +#endif + + + if (header != NULL) { + + header->opcode = B_HOST_TO_LENDIAN_INT16(PACK_OPCODE(ogf, ocf)); + header->clen = psize; + + if (param != NULL && psize != 0) { + *param = ((uint8*)header) + sizeof(struct hci_command_header); + } + } +#ifdef BT_IOCTLS_PASS_SIZE + return header; +#else + return (void*)size; +#endif +} + + +/* CONTROL BASEBAND */ +void* buildReset(size_t* outsize) +{ + return buildCommand(OGF_CONTROL_BASEBAND, OCF_RESET, NULL, 0, outsize); +} + + +void* buildReadLocalName(size_t* outsize) +{ + return buildCommand(OGF_CONTROL_BASEBAND, OCF_READ_LOCAL_NAME, NULL, 0, outsize); +} + + +/* LINK CONTROL */ +void* buildRemoteNameRequest(bdaddr_t bdaddr,uint8 pscan_rep_mode, uint16 clock_offset, size_t* outsize) +{ + + struct hci_remote_name_request* param; + void* command = buildCommand(OGF_LINK_CONTROL, OCF_REMOTE_NAME_REQUEST, (void**) ¶m, sizeof(struct hci_remote_name_request), outsize); + + if (command != NULL) { + param->bdaddr = bdaddr; + param->pscan_rep_mode = pscan_rep_mode; + param->clock_offset = clock_offset; + } + + return command; +} + + +void* buildInquiry(uint32 lap, uint8 length, uint8 num_rsp, size_t* outsize) +{ + + struct hci_cp_inquiry* param; + void* command = buildCommand(OGF_LINK_CONTROL, OCF_INQUIRY, (void**) ¶m, sizeof(struct hci_cp_inquiry), outsize); + + if (command != NULL) { + + param->lap[2] = (lap >> 16) & 0xFF; + param->lap[1] = (lap >> 8) & 0xFF; + param->lap[0] = (lap >> 0) & 0xFF; + param->length = length; + param->num_rsp = num_rsp; + } + + return command; +} + + +/* OGF_INFORMATIONAL_PARAM */ +void* buildReadBufferSize(size_t* outsize) +{ + return buildCommand(OGF_INFORMATIONAL_PARAM, OCF_READ_BUFFER_SIZE, NULL, 0, outsize); +} + + +void* buildReadBdAddr(size_t* outsize) +{ + return buildCommand(OGF_INFORMATIONAL_PARAM, OCF_READ_BD_ADDR, NULL, 0, outsize); +} diff --git a/src/servers/bluetooth/CommandManager.h b/src/servers/bluetooth/CommandManager.h new file mode 100644 index 0000000000..223b9d6892 --- /dev/null +++ b/src/servers/bluetooth/CommandManager.h @@ -0,0 +1,25 @@ +/* + * Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com + * + * All rights reserved. Distributed under the terms of the MIT License. + * + */ + +#ifndef _COMMAND_MANAGER_H +#define _COMMAND_MANAGER_H + +#include + +/* CONTROL BASEBAND */ +void* buildReset(size_t* outsize); +void* buildReadLocalName(size_t* outsize); + +/* LINK CONTROL */ +void* buildRemoteNameRequest(bdaddr_t bdaddr,uint8 pscan_rep_mode, uint16 clock_offset, size_t* outsize); +void* buildInquiry(uint32 lap, uint8 length, uint8 num_rsp, size_t* outsize); + +/* OGF_INFORMATIONAL_PARAM */ +void* buildReadBufferSize(size_t* outsize); +void* buildReadBdAddr(size_t* outsize); + +#endif \ No newline at end of file diff --git a/src/servers/bluetooth/DeviceManager.cpp b/src/servers/bluetooth/DeviceManager.cpp new file mode 100644 index 0000000000..e6279e5a94 --- /dev/null +++ b/src/servers/bluetooth/DeviceManager.cpp @@ -0,0 +1,329 @@ +/* +** Copyright 2004, the Haiku project. All rights reserved. +** Distributed under the terms of the Haiku License. +** +** +** Author : Jérôme Duval +** Original authors: Marcus Overhagen, Axel Dörfler +** +** Bluetooth adaptation: Oliver Ruiz Dorantes +** +*/ + + +#include +#include +#include + +#include +#include +#include +#include +#include + + +#include +#include +#include + +#include "DeviceManager.h" +#include "LocalDeviceImpl.h" + +#include "Output.h" +#include "BluetoothServer.h" + +#include + + +void +DeviceManager::MessageReceived(BMessage * msg) +{ + if (msg->what == B_NODE_MONITOR) { + int32 opcode; + if (msg->FindInt32("opcode", &opcode) == B_OK) { + switch (opcode) { + case B_ENTRY_CREATED: + case B_ENTRY_REMOVED: + case B_ENTRY_MOVED: + { + node_ref dir_nref; + const char *name; + BDirectory dir; + + (Output::Instance()->Post("Bus changing ...\n", BLACKBOARD_DEVICEMANAGER)); + + if ((msg->FindInt32("device", &dir_nref.device)!=B_OK) + || (msg->FindInt64("directory", &dir_nref.node)!=B_OK) + || (msg->FindString("name", &name) != B_OK)) + return; + + // Check if the entry is a File or a directory + if (dir.SetTo(&dir_nref) == B_OK) { + (Output::Instance()->Post(name, BLACKBOARD_DEVICEMANAGER)); + (Output::Instance()->Post(" adding folder...\n", BLACKBOARD_DEVICEMANAGER)); + AddDirectory(&dir_nref); + } else { + // Then it is a device! + // post it to server +// msg->what = BT_MSG_ADD_DEVICE; +// be_app_messenger.SendMessage(msg); +// (Output::Instance()->Post("New bluetooth device on bus ...\n", BLACKBOARD_DEVICEMANAGER)); + } + } + case B_STAT_CHANGED: + case B_ATTR_CHANGED: + case B_DEVICE_MOUNTED: + case B_DEVICE_UNMOUNTED: + default: + BLooper::MessageReceived(msg); + break; + } + } + } +} + + +status_t +DeviceManager::AddDirectory(node_ref *nref) +{ + BDirectory directory(nref); + status_t status = directory.InitCheck(); + if (status != B_OK) { + (Output::Instance()->Post("AddDirectory::Initcheck Failed\n", BLACKBOARD_DEVICEMANAGER)); + return status; + } + + status = watch_node(nref, B_WATCH_DIRECTORY, this); + if (status != B_OK) { + (Output::Instance()->Post("AddDirectory::watch_node Failed\n", BLACKBOARD_DEVICEMANAGER)); + return status; + } + + BEntry entry; + while (directory.GetNextEntry(&entry, true) == B_OK) { + + // its suposed to be devices ... + entry_ref ref; + entry.GetRef(&ref); + BPath path(new BEntry(&ref)); + BString* str = new BString(path.Path()); + + BMessage* msg = new BMessage(BT_MSG_ADD_DEVICE); + msg->AddInt32("opcode", B_ENTRY_CREATED); + msg->AddInt32("device", nref->device); + msg->AddInt64("directory", nref->node); + msg->AddString("name", *str ); + + be_app_messenger.SendMessage(msg); + + + (Output::Instance()->Post( path.Path(), BLACKBOARD_DEVICEMANAGER)); + (Output::Instance()->Post(" Entry added\n", BLACKBOARD_DEVICEMANAGER)); + + } + (Output::Instance()->Post("Finished exploring entries\n", BLACKBOARD_DEVICEMANAGER)); + + return B_OK; +} + + +status_t +DeviceManager::RemoveDirectory(node_ref* nref) +{ + BDirectory directory(nref); + status_t status = directory.InitCheck(); + if (status != B_OK) + return status; + + status = watch_node(nref, B_STOP_WATCHING, this); + if (status != B_OK) + return status; + + BEntry entry; + while (directory.GetNextEntry(&entry, true) == B_OK) { + entry_ref ref; + entry.GetRef(&ref); + BMessage msg(B_NODE_MONITOR); + msg.AddInt32("opcode", B_ENTRY_REMOVED); + msg.AddInt32("device", nref->device); + msg.AddInt64("directory", nref->node); + msg.AddString("name", ref.name); + //addon->fDevice->Control(NULL, NULL, msg.what, &msg); + } + + return B_OK; +} + + +DeviceManager::DeviceManager() + : + fLock("device manager") +{ +} + + +DeviceManager::~DeviceManager() +{ + +} + + +void +DeviceManager::LoadState() +{ + + if (!Lock()) + return; + Run(); + Unlock(); +} + + +void +DeviceManager::SaveState() +{ +} + + +status_t +DeviceManager::StartMonitoringDevice(const char *device) +{ + + status_t err; + node_ref nref; + BDirectory directory; + BPath path("/dev"); + + /* Build the path */ + if ((err = path.Append(device)) != B_OK) { + printf("DeviceManager::StartMonitoringDevice BPath::Append() error %s: %s\n", path.Path(), strerror(err)); + return err; + } + + /* Check the path */ + if ((err = directory.SetTo(path.Path())) != B_OK) { + /* Entry not there ... */ + if (err != B_ENTRY_NOT_FOUND) { // something else we cannot handle + printf("DeviceManager::StartMonitoringDevice SetTo error %s: %s\n", path.Path(), strerror(err)); + return err; + } + /* Create it */ + if ((err = create_directory(path.Path(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) != B_OK + || (err = directory.SetTo(path.Path())) != B_OK) { + printf("DeviceManager::StartMonitoringDevice CreateDirectory error %s: %s\n", path.Path(), strerror(err)); + return err; + } + } + + /* get noderef */ + if ((err = directory.GetNodeRef(&nref)) != B_OK) { + printf("DeviceManager::StartMonitoringDevice GetNodeRef error %s: %s\n", path.Path(), strerror(err)); + return err; + } + + // test if already monitored in any add-on + + (Output::Instance()->Post(device, BLACKBOARD_DEVICEMANAGER)); + (Output::Instance()->Post(" Being monitorized\n", BLACKBOARD_DEVICEMANAGER)); + + bool alreadyMonitored = false; +#if 0 + HCIDelegate *tmphd = NULL; + int32 i = 0; + + // TODO!! ask the server if this needs to be monitored + + while ((tmphd = (HCIDelegate *)fDelegatesList.ItemAt(i++)) !=NULL) { + + /* Find out the reference*/ + node_ref *dnref = (node_ref *)tmphd->fMonitoredRefs ; + if (*dnref == nref) { + printf("StartMonitoringDevice already monitored\n"); + alreadyMonitored = true; + break; + } + + } +#endif + + // monitor if needed + if (!alreadyMonitored) { + if ((err = AddDirectory(&nref)) != B_OK) + return err; + } + +/* // add addon in list + if (!fDeviceAddons.HasItem(addon)) + fDeviceAddons.AddItem(addon); + + // add dir ref in list + int32 j=0; + node_ref *dnref = NULL; + alreadyMonitored = false; // why rechecking? + while ((dnref = (node_ref *)addon->fMonitoredRefs.ItemAt(j++)) != NULL) { + if (*dnref == nref) { + alreadyMonitored = true; + break; + } + } + + if (!alreadyMonitored) { + addon->fMonitoredRefs.AddItem(new node_ref(nref)); + } +*/ + return B_OK; +} + + +status_t +DeviceManager::StopMonitoringDevice(const char *device) +{ + status_t err; + node_ref nref; + BDirectory directory; + BPath path("/dev"); + if (((err = path.Append(device)) != B_OK) + || ((err = directory.SetTo(path.Path())) != B_OK) + || ((err = directory.GetNodeRef(&nref)) != B_OK)) + return err; + + // test if still monitored + bool stillMonitored = false; +/* + int32 i = 0; + while ((tmpaddon = (_BDeviceAddOn_ *)fDeviceAddons.ItemAt(i++)) !=NULL) { + if (addon == tmpaddon) + continue; + + int32 j=0; + node_ref *dnref = NULL; + while ((dnref = (node_ref *)tmpaddon->fMonitoredRefs.ItemAt(j++)) != NULL) { + if (*dnref == nref) { + stillMonitored = true; + break; + } + } + if (stillMonitored) + break; + } + + // remove from list + node_ref *dnref = NULL; + int32 j=0; + while ((dnref = (node_ref *)addon->fMonitoredRefs.ItemAt(j)) != NULL) { + if (*dnref == nref) { + addon->fMonitoredRefs.RemoveItem(j); + delete dnref; + break; + } + j++; + } + + // stop monitoring if needed + if (!stillMonitored) { + if ((err = RemoveDirectory(&nref, addon)) != B_OK) + return err; + } +*/ + return B_OK; +} diff --git a/src/servers/bluetooth/DeviceManager.h b/src/servers/bluetooth/DeviceManager.h new file mode 100644 index 0000000000..8f73eaa382 --- /dev/null +++ b/src/servers/bluetooth/DeviceManager.h @@ -0,0 +1,43 @@ +/* +** Copyright 2004, the Haiku project. All rights reserved. +** Distributed under the terms of the Haiku License. +** +** Author : Jérôme Duval +** Original authors: Marcus Overhagen, Axel Dörfler +*/ +#ifndef _DEVICE_MANAGER_H +#define _DEVICE_MANAGER_H + +// Manager for devices monitoring + +#include +#include +#include +#include +/*#include +#include +#include +#include "TList.h" +*/ + +class DeviceManager : public BLooper { + public: + DeviceManager(); + ~DeviceManager(); + + void LoadState(); + void SaveState(); + + status_t StartMonitoringDevice(const char *device); + status_t StopMonitoringDevice(const char *device); + + void MessageReceived(BMessage *msg); + + private: + status_t AddDirectory(node_ref *nref); + status_t RemoveDirectory(node_ref *nref); + + BLocker fLock; +}; + +#endif // _DEVICE_MANAGER_H diff --git a/src/servers/bluetooth/HCIControllerAccessor.cpp b/src/servers/bluetooth/HCIControllerAccessor.cpp new file mode 100644 index 0000000000..39f83709a2 --- /dev/null +++ b/src/servers/bluetooth/HCIControllerAccessor.cpp @@ -0,0 +1,19 @@ + +#include "HCIControllerAccessor.h" + +HCIControllerAccessor::HCIControllerAccessor(BPath* path) : HCIDelegate(path) +{ + + +} + +status_t +HCIControllerAccessor::IssueCommand(raw_command* rc, size_t size) +{ + + if (GetID() < 0) + return B_ERROR; + + + return B_ERROR; +} diff --git a/src/servers/bluetooth/HCIControllerAccessor.h b/src/servers/bluetooth/HCIControllerAccessor.h new file mode 100644 index 0000000000..cfe5032e04 --- /dev/null +++ b/src/servers/bluetooth/HCIControllerAccessor.h @@ -0,0 +1,16 @@ +/* */ + +#ifndef _HCICONTROLLER_ACCESSOR_H_ +#define _HCICONTROLLER_ACCESSOR_H_ + +#include "HCIDelegate.h" + + +class HCIControllerAccessor : public HCIDelegate { + + public: + HCIControllerAccessor(BPath* path); + status_t IssueCommand(raw_command* rc, size_t size); +}; + +#endif diff --git a/src/servers/bluetooth/HCIDelegate.h b/src/servers/bluetooth/HCIDelegate.h new file mode 100644 index 0000000000..2bac212323 --- /dev/null +++ b/src/servers/bluetooth/HCIDelegate.h @@ -0,0 +1,59 @@ +#ifndef _HCIDELEGATE_H_ +#define _HCIDELEGATE_H_ + +#include +#include +#include +#include + +#include + + +typedef void* raw_command; + + +class HCIDelegate { + + public: + HCIDelegate(BPath* path) + { + status_t status; + + fFD = open (path->Path(), O_RDWR); + printf("## fdesc %d\n", fFD); + if (fFD > 0) { + // find out which ID was assigned + status = ioctl(fFD, GET_HCI_ID, &fHID, 0); + printf("## id fdesc %ld ### %ld\n", fHID, status); + + } + else { + fHID = B_ERROR; + } + + + + } + + hci_id GetID(void) + { + return fHID; + } + + virtual status_t IssueCommand(raw_command rc, size_t size) + { + return B_ERROR; + } + + protected: + + + hci_id fHID; + int fFD; + + private: + + +}; + +#endif diff --git a/src/servers/bluetooth/HCITransportAccessor.cpp b/src/servers/bluetooth/HCITransportAccessor.cpp new file mode 100644 index 0000000000..ccdc099af3 --- /dev/null +++ b/src/servers/bluetooth/HCITransportAccessor.cpp @@ -0,0 +1,28 @@ + +#include + +#include "BluetoothServer.h" +#include "HCITransportAccessor.h" +#include "Output.h" + +HCITransportAccessor::HCITransportAccessor(BPath* path) : HCIDelegate(path) +{ + + +} + +status_t +HCITransportAccessor::IssueCommand(raw_command rc, size_t size) +{ + if (GetID() < 0 || fFD < 0) + return B_ERROR; + +printf("Command going: len = %d\n", size); +for (int16 index = 0 ; index < size; index++ ) { + printf("%x:",((uint8*)rc)[index]); +} +printf("\n"); + + + return ioctl(fFD, ISSUE_BT_COMMAND, rc, size); +} diff --git a/src/servers/bluetooth/HCITransportAccessor.h b/src/servers/bluetooth/HCITransportAccessor.h new file mode 100644 index 0000000000..370364ee97 --- /dev/null +++ b/src/servers/bluetooth/HCITransportAccessor.h @@ -0,0 +1,16 @@ +/* */ + +#ifndef _HCITRANSPORT_ACCESSOR_H_ +#define _HCITRANSPORT_ACCESSOR_H_ + +#include "HCIDelegate.h" + + +class HCITransportAccessor : public HCIDelegate { + + public: + HCITransportAccessor(BPath* path); + status_t IssueCommand(raw_command rc, size_t size); +}; + +#endif diff --git a/src/servers/bluetooth/Jamfile b/src/servers/bluetooth/Jamfile new file mode 100644 index 0000000000..a3321c79c1 --- /dev/null +++ b/src/servers/bluetooth/Jamfile @@ -0,0 +1,22 @@ +SubDir HAIKU_TOP src servers bluetooth ; + +SetSubDirSupportedPlatformsBeOSCompatible ; + +UsePrivateHeaders shared bluetooth net kernel ; + +AddResources bluetooth_server : server-bluetooth.rdef ; + +Server bluetooth_server + : + BPortNot.cpp + BluetoothServer.cpp + CommandManager.cpp + DeviceManager.cpp + HCIControllerAccessor.cpp + HCITransportAccessor.cpp + LocalDeviceHandler.cpp + LocalDeviceImpl.cpp + Output.cpp + : be + $(TARGET_LIBSTDC++) +; diff --git a/src/servers/bluetooth/LocalDeviceHandler.cpp b/src/servers/bluetooth/LocalDeviceHandler.cpp new file mode 100644 index 0000000000..52624f9ca5 --- /dev/null +++ b/src/servers/bluetooth/LocalDeviceHandler.cpp @@ -0,0 +1,161 @@ +/* + * Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com + * + * All rights reserved. Distributed under the terms of the MIT License. + * + */ + + +#include "LocalDeviceHandler.h" + + + +LocalDeviceHandler::LocalDeviceHandler(HCIDelegate* hd) +{ + fHCIDelegate = hd; + fProperties = new BMessage(); + +} + +LocalDeviceHandler::~LocalDeviceHandler() +{ + +} + + +hci_id +LocalDeviceHandler::GetID() +{ + return fHCIDelegate->GetID(); +} + + +bool +LocalDeviceHandler::Available() { + + return true; +} + + +void +LocalDeviceHandler::Acquire(void) { + +} + + +bool +LocalDeviceHandler::IsPropertyAvailable(const BString& property) { + + type_code typeFound; + int32 countFound; + + return (fProperties->GetInfo(property.String(), &typeFound, &countFound) == B_OK ); + +} + + +void +LocalDeviceHandler::AddWantedEvent(BMessage* msg) +{ + fEventsWanted.Lock(); + // TODO: review why it is neede to replicate the msg + fEventsWanted.AddMessage(msg); + fEventsWanted.Unlock(); +} + + +void +LocalDeviceHandler::ClearWantedEvent(BMessage* msg, uint16 event = 0, uint16 opcode = 0) +{ + // Remove the whole petition from queue + fEventsWanted.Lock(); +/* + if (event == 0) { + fEventsWanted.RemoveMessage(msg); + goto bail; + } + + int16 eventFound; + int16 opcodeFound; + int32 eventIndex; + + for (int32 index = 0 ; index < fEventsWanted.CountMessages() ; index++) { + + BMessage* msg = fEventsWanted.FindMessage(index); + + eventIndex = 0; + // for each Event + while (msg->FindInt16("eventExpected", eventIndex, &eventFound) == B_OK ) { + if (eventFound == event) { + + // there is an opcode specified + if (opcde != 0) + + // The opcode matches + if ( (msg->FindInt16("opcodeExpected", eventIndex, &opcodeFound) == B_OK) && + ((uint16)opcodeFound != opcode) ) { + + fEventsWanted.RemoveMessage(msg); + goto bail; + } + } else { + // Event matches so far + fEventsWanted.RemoveMessage(msg); + goto bail; + } + + } + eventIndex++; + } + } + +bail: */ + fEventsWanted.Unlock(); + + fEventsWanted.RemoveMessage(msg); +} + + +BMessage* +LocalDeviceHandler::FindPetition(uint16 event, uint16 opcode = 0) +{ + int16 eventFound; + int16 opcodeFound; + int32 eventIndex; + + fEventsWanted.Lock(); + // for each Petition + for (int32 index = 0 ; index < fEventsWanted.CountMessages() ; index++) { + BMessage* msg = fEventsWanted.FindMessage(index); + + printf("Petition %ld ... of %ld msg #%p\n", index, fEventsWanted.CountMessages(), msg); + msg->PrintToStream(); + eventIndex = 0; + // for each Event + while (msg->FindInt16("eventExpected", eventIndex, &eventFound) == B_OK ) { + if (eventFound == event) { + + printf("Event found %ld\n", eventIndex); + // there is an opcode specified.. + if (msg->FindInt16("opcodeExpected", eventIndex, &opcodeFound) == B_OK) { + + // ensure the opcode + if ((uint16)opcodeFound != opcode) { + printf("opcode does not match %d\n", opcode); + break; + } + printf("Opcdodes match %d %d \n", opcode , opcodeFound); + } + + fEventsWanted.Unlock(); + return msg; + + + } + eventIndex++; + } + } + + fEventsWanted.Unlock(); + return NULL; +} diff --git a/src/servers/bluetooth/LocalDeviceHandler.h b/src/servers/bluetooth/LocalDeviceHandler.h new file mode 100644 index 0000000000..ea0135f4ee --- /dev/null +++ b/src/servers/bluetooth/LocalDeviceHandler.h @@ -0,0 +1,50 @@ +/* + * Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com + * + * All rights reserved. Distributed under the terms of the MIT License. + * + */ + +#ifndef _LOCALDEVICE_HANDLER_H_ +#define _LOCALDEVICE_HANDLER_H_ + +#include + +#include + +#include + +#include "HCIDelegate.h" + +class LocalDeviceHandler { + +public: + + virtual hci_id GetID(); + + virtual bool Available(); + void Acquire(void); + + BMessage* GetPropertiesMessage(void) { return fProperties; } + bool IsPropertyAvailable(const BString& property); + + +protected: + LocalDeviceHandler (HCIDelegate* hd); + virtual ~LocalDeviceHandler(); + + HCIDelegate* fHCIDelegate; + BMessage* fProperties; + + void AddWantedEvent(BMessage* msg); + void ClearWantedEvent(BMessage* msg, uint16 code, uint16 opcode = 0); + BMessage* FindPetition(uint16 event, uint16 opcode = 0); + +private: + + BMessageQueue fEventsWanted; + + +}; + +#endif diff --git a/src/servers/bluetooth/LocalDeviceImpl.cpp b/src/servers/bluetooth/LocalDeviceImpl.cpp new file mode 100644 index 0000000000..8cb19f63ef --- /dev/null +++ b/src/servers/bluetooth/LocalDeviceImpl.cpp @@ -0,0 +1,396 @@ +/* + * Copyright 2008 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com + * + * All rights reserved. Distributed under the terms of the MIT License. + * + */ + + +#include "BluetoothServer.h" + +#include "LocalDeviceImpl.h" +#include "CommandManager.h" +#include "Output.h" + +#include +#include + + +#include + +// Factory methods +LocalDeviceImpl* +LocalDeviceImpl::CreateControllerAccessor(BPath* path) +{ + HCIDelegate* hd = new HCIControllerAccessor(path); + + if ( hd != NULL) + return new LocalDeviceImpl(hd); + else + return NULL; +} + + +LocalDeviceImpl* +LocalDeviceImpl::CreateTransportAccessor(BPath* path) +{ + HCIDelegate* hd = new HCITransportAccessor(path); + + if ( hd != NULL) + return new LocalDeviceImpl(hd); + else + return NULL; +} + + +LocalDeviceImpl::LocalDeviceImpl(HCIDelegate* hd) : LocalDeviceHandler(hd) +{ + +} + +#if 0 +#pragma mark - Class methods - +#endif + +void +LocalDeviceImpl::HandleEvent(struct hci_event_header* event) +{ + // Check if it was a non requested events + switch (event->ecode) { + case HCI_EVENT_HARDWARE_ERROR: + //HardwareError(event); + return; + default: + // lets go on + break; + } + +printf("Event comming: len = %d\n", event->elen); +for (int16 index = 0 ; index < event->elen + 2; index++ ) { + printf("%x:",((uint8*)event)[index]); +} +printf("\n"); + + + BMessage* request = NULL; + + // Check if its a requested one + if ( event->ecode == HCI_EVENT_CMD_COMPLETE ) { + + (Output::Instance()->Post("Incoming Command Complete\n", BLACKBOARD_EVENTS)); + request = FindPetition(event->ecode, ((struct hci_ev_cmd_complete*)(event+1))->opcode ); + + } else + // TODO: Command status should also be considered + { + request = FindPetition(event->ecode); + } + + if ( request == NULL) { + (Output::Instance()->Post("Event could not be understood or delivered\n", BLACKBOARD_EVENTS)); + return; + } + + // we are waiting for a reply + switch (event->ecode) { + case HCI_EVENT_INQUIRY_COMPLETE: + break; + + case HCI_EVENT_INQUIRY_RESULT: + break; + + case HCI_EVENT_CONN_COMPLETE: + break; + + case HCI_EVENT_CONN_REQUEST: + break; + + case HCI_EVENT_DISCONNECTION_COMPLETE: + break; + + case HCI_EVENT_AUTH_COMPLETE: + break; + + case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE: + break; + + case HCI_EVENT_ENCRYPT_CHANGE: + break; + + case HCI_EVENT_CHANGE_CONN_LINK_KEY_COMPLETE: + break; + + case HCI_EVENT_MASTER_LINK_KEY_COMPL: + break; + + case HCI_EVENT_RMT_FEATURES: + break; + + case HCI_EVENT_RMT_VERSION: + break; + + case HCI_EVENT_QOS_SETUP_COMPLETE: + break; + + case HCI_EVENT_CMD_COMPLETE: + CommandComplete((struct hci_ev_cmd_complete*)(event+1), request); + break; + + case HCI_EVENT_CMD_STATUS: + break; + + case HCI_EVENT_FLUSH_OCCUR: + break; + + case HCI_EVENT_ROLE_CHANGE: + break; + + case HCI_EVENT_NUM_COMP_PKTS: + break; + + case HCI_EVENT_MODE_CHANGE: + break; + + case HCI_EVENT_RETURN_LINK_KEYS: + break; + + case HCI_EVENT_PIN_CODE_REQ: + break; + + case HCI_EVENT_LINK_KEY_REQ: + break; + + case HCI_EVENT_LINK_KEY_NOTIFY: + break; + + case HCI_EVENT_LOOPBACK_COMMAND: + break; + + case HCI_EVENT_DATA_BUFFER_OVERFLOW: + break; + + case HCI_EVENT_MAX_SLOT_CHANGE: + break; + + case HCI_EVENT_READ_CLOCK_OFFSET_COMPL: + break; + + case HCI_EVENT_CON_PKT_TYPE_CHANGED: + break; + + case HCI_EVENT_QOS_VIOLATION: + break; + + case HCI_EVENT_PAGE_SCAN_REP_MODE_CHANGE: + break; + + case HCI_EVENT_FLOW_SPECIFICATION: + break; + + case HCI_EVENT_INQUIRY_RESULT_WITH_RSSI: + break; + + case HCI_EVENT_REMOTE_EXTENDED_FEATURES: + break; + + case HCI_EVENT_SYNCHRONOUS_CONNECTION_COMPLETED: + break; + + case HCI_EVENT_SYNCHRONOUS_CONNECTION_CHANGED: + + break; + } + +} + + +void +LocalDeviceImpl::CommandComplete(struct hci_ev_cmd_complete* event, BMessage* request) { + + int16 opcodeExpected; + BMessage reply; + + Output::Instance()->Post(__FUNCTION__, BLACKBOARD_LD_OFFSET + GetID()); + Output::Instance()->Post("\n", BLACKBOARD_LD_OFFSET + GetID()); + + // Handle command complete information + request->FindInt16("opcodeExpected", 0 /*REVIEW!*/, &opcodeExpected); + +printf("Command complete ...%p\n",event); +for (int16 index = 0 ; index < 10; index++ ) { + printf("%x:",((uint8*)event)[index]); +} +printf("\n"); + + + if (request->IsSourceWaiting() == false) + Output::Instance()->Post("nobody waiting\n", BLACKBOARD_KIT); + + + switch (opcodeExpected) { + + case PACK_OPCODE(OGF_INFORMATIONAL_PARAM, OCF_READ_BD_ADDR): + { + struct hci_rp_read_bd_addr* readbdaddr = (struct hci_rp_read_bd_addr*)(event+1); + + printf("read bdaddr ...%p\n", readbdaddr); + for (int16 index = 0 ; index < 10; index++ ) { + printf("%x:",((uint8*)readbdaddr)[index]); + } + printf("\n"); + + + if (readbdaddr->status == BT_OK) { + + + reply.AddData("bdaddr", B_ANY_TYPE, &readbdaddr->bdaddr, sizeof(bdaddr_t)); + reply.AddInt32("status", readbdaddr->status); + + printf("Sending reply ... %ld\n",request->SendReply(&reply)); + reply.PrintToStream(); + + + Output::Instance()->Post("Positive reply for getAdress\n", BLACKBOARD_KIT); + + } else { + reply.AddInt8("status", readbdaddr->status); + request->SendReply(&reply); + Output::Instance()->Post("Negative reply for getAdress\n", BLACKBOARD_KIT); + } + + ClearWantedEvent(request, PACK_OPCODE(OGF_INFORMATIONAL_PARAM, OCF_READ_BD_ADDR)); + } + break; + + case PACK_OPCODE(OGF_CONTROL_BASEBAND, OCF_READ_LOCAL_NAME): + { + struct hci_rp_read_local_name* readLocalName = (struct hci_rp_read_local_name*)(event+1); + + printf("read bdaddr ...%p\n", readLocalName); + for (int16 index = 0 ; index < 10; index++ ) { + printf("%x:",((uint8*)readLocalName)[index]); + } + printf("\n"); + + reply.AddInt8("status", readLocalName->status); + + if (readLocalName->status == BT_OK) { + + reply.AddString("friendlyname", (const char*)readLocalName->local_name ); + Output::Instance()->Post("Positive reply for friendly name\n", BLACKBOARD_KIT); + + } else { + + Output::Instance()->Post("Negative reply for friendly name\n", BLACKBOARD_KIT); + + } + + printf("Sending reply ... %ld\n",request->SendReply(&reply)); + reply.PrintToStream(); + + ClearWantedEvent(request, PACK_OPCODE(OGF_CONTROL_BASEBAND, OCF_READ_LOCAL_NAME)); + } + break; + + default: + Output::Instance()->Post("Command Complete not handled\n", BLACKBOARD_KIT); + break; + + + } +} + + +#if 0 +#pragma mark - Request Methods - +#endif + + +status_t +LocalDeviceImpl::GetAddress(bdaddr_t* bdaddr, BMessage* request) +{ + ssize_t size; + + if (fProperties->FindData("bdaddr", B_ANY_TYPE, 0, (const void **)bdaddr, &size) == B_OK) { + + (Output::Instance()->Post("BDADDR already present in server\n", BLACKBOARD_EVENTS)); + /* We have this info, returning back */ + return B_OK; + + } else { + size_t size; + + void* command = buildReadBdAddr(&size); + + /* Adding a wanted event in the queue */ + request->AddInt16("eventExpected", HCI_EVENT_CMD_COMPLETE); + request->AddInt16("opcodeExpected", PACK_OPCODE(OGF_INFORMATIONAL_PARAM, OCF_READ_BD_ADDR)); + + printf("Adding request... %p\n", request); + AddWantedEvent(request); + request->PrintToStream(); + + if (((HCITransportAccessor*)fHCIDelegate)->IssueCommand(command, size) == B_ERROR) + (Output::Instance()->Post("Command issue error\n", BLACKBOARD_EVENTS)); + + (Output::Instance()->Post("Command issued for GetAddress\n", BLACKBOARD_EVENTS)); + return B_WOULD_BLOCK; + } + +} + + +status_t +LocalDeviceImpl::GetFriendlyName(BString str, BMessage* request) +{ + + if (fProperties->FindString("friendlyname", &str) == B_OK) { + + (Output::Instance()->Post("Friendly name already present in server\n", BLACKBOARD_EVENTS)); + /* We have this info, returning back */ + return B_OK; + + } else { + size_t size; + + void* command = buildReadLocalName(&size); + + /* Adding a wanted event in the queue */ + request->AddInt16("eventExpected", HCI_EVENT_CMD_COMPLETE); + request->AddInt16("opcodeExpected", PACK_OPCODE(OGF_CONTROL_BASEBAND, OCF_READ_LOCAL_NAME)); + + printf("Adding request... %p\n", request); + AddWantedEvent(request); + request->PrintToStream(); + + if (((HCITransportAccessor*)fHCIDelegate)->IssueCommand(command, size) == B_ERROR) + (Output::Instance()->Post("Command issue error\n", BLACKBOARD_EVENTS)); + + (Output::Instance()->Post("Command issued for GetFriendlyname\n", BLACKBOARD_EVENTS)); + + return B_WOULD_BLOCK; + } + +} + + +status_t +LocalDeviceImpl::ProcessSimpleRequest(BMessage* request) +{ + ssize_t size; + void* command = NULL; + + if (request->FindData("raw command", B_ANY_TYPE, 0, (const void **)command, &size) == B_OK) + if (((HCITransportAccessor*)fHCIDelegate)->IssueCommand(command, size) == B_ERROR) + (Output::Instance()->Post("Command issue error\n", BLACKBOARD_EVENTS)); + else + { + AddWantedEvent(request); + return B_OK; + } + else { + (Output::Instance()->Post("No command specified for simple request\n", BLACKBOARD_KIT)); + } + + return B_ERROR; +} diff --git a/src/servers/bluetooth/LocalDeviceImpl.h b/src/servers/bluetooth/LocalDeviceImpl.h new file mode 100644 index 0000000000..a693fbf2ab --- /dev/null +++ b/src/servers/bluetooth/LocalDeviceImpl.h @@ -0,0 +1,44 @@ +/* + * Copyright 2007 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com + * + * All rights reserved. Distributed under the terms of the MIT License. + * + */ + +#ifndef _LOCALDEVICE_IMPL_H_ +#define _LOCALDEVICE_IMPL_H_ + +#include + +#include + +#include "LocalDeviceHandler.h" + +#include "HCIDelegate.h" +#include "HCIControllerAccessor.h" +#include "HCITransportAccessor.h" + +class LocalDeviceImpl : public LocalDeviceHandler { + +private: + LocalDeviceImpl(HCIDelegate* hd); + +public: + + // Factory methods + static LocalDeviceImpl* CreateControllerAccessor(BPath* path); + static LocalDeviceImpl* CreateTransportAccessor(BPath* path); + + void HandleEvent(struct hci_event_header* event); + + /* Request handling */ + status_t GetAddress(bdaddr_t* bdaddr, BMessage* request); + status_t GetFriendlyName(BString str, BMessage* request); + status_t ProcessSimpleRequest(BMessage* request); + + /* Events handling */ + void CommandComplete(struct hci_ev_cmd_complete* event, BMessage* request); + +}; + +#endif diff --git a/src/servers/bluetooth/Output.cpp b/src/servers/bluetooth/Output.cpp new file mode 100644 index 0000000000..fd5d5ed4cb --- /dev/null +++ b/src/servers/bluetooth/Output.cpp @@ -0,0 +1,179 @@ +#ifndef _Output_h +#include "Output.h" +#endif + +#include +#include + +/* +#ifndef _Preferences_h +#include "Preferences.h" +#endif +*/ + +/****************************************************************/ +/* OutputView */ +/****************************************************************/ + +OutputView::OutputView(BRect frame) : + BView(frame, "OutputView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW) +{ + SetViewColor(216,216,216); + rgb_color color = {255,255,255}; + + BRect b = Bounds(); + AddChild(m_pTextView = new BTextView(BRect(b.left+5,b.top+5,b.right-B_V_SCROLL_BAR_WIDTH-6,b.bottom-5), "Output", BRect(b.left+5,b.top+5,b.right-B_V_SCROLL_BAR_WIDTH-6,b.bottom-5), NULL, &color, B_FOLLOW_ALL_SIDES, B_WILL_DRAW)); + m_pTextView->SetViewColor(0,0,100); + m_pTextView->MakeEditable(false); + + AddChild(m_pScrollBar = new BScrollBar(BRect(b.right-B_V_SCROLL_BAR_WIDTH-5,b.top+5,b.right-5, b.bottom-5), "outputScroll", m_pTextView, 0, 0, B_VERTICAL)); +} + + +void +OutputView::FrameResized(float width, float height) +{ + BView::FrameResized(width, height); + m_pTextView->SetTextRect(BRect(0,0,width,height)); +} + + +/****************************************************************/ +/* Output */ +/****************************************************************/ + +// Singleton implementation +Output* Output::m_instance = 0; + +Output::Output() : + BWindow(BRect(200,200,600,600), "Output", B_TITLED_WINDOW, B_NOT_RESIZABLE) +{ + BRect b = Bounds(); + + fTabsList = new BList(20); + fOutputViewsList = new BList(20); + + BView* resetView = new BView(BRect(b.left,b.bottom-25,b.right,b.bottom), "resetView", B_FOLLOW_ALL_SIDES, B_WILL_DRAW); + resetView->SetViewColor(216,216,216); + resetView->AddChild(m_pReset = new BButton(BRect(1,1,61,20), "reset all", "Clear", new BMessage(MSG_OUTPUT_RESET), B_FOLLOW_BOTTOM)); + resetView->AddChild(m_pResetAll = new BButton(BRect(70,1,130,20), "reset", "Clear all", new BMessage(MSG_OUTPUT_RESET_ALL), B_FOLLOW_BOTTOM)); + AddChild(resetView); + + fTabView = new BTabView(BRect(b.left,b.top,b.right,b.bottom-25), "tab_view", B_WIDTH_FROM_LABEL /*,B_FOLLOW_ALL_SIDES, B_FULL_UPDATE_ON_RESIZE*/); + fTabView->SetViewColor(216,216,216); + + fBounds = fTabView->Bounds(); + fBounds.bottom -= fTabView->TabHeight(); + + fTabView->AddTab(m_pAll = new OutputView(fBounds), m_pAllTab = new BTab()); + m_pAllTab->SetLabel("All"); + + AddChild(fTabView); + /* + MoveTo( Preferences::Instance()->OutputX(), + Preferences::Instance()->OutputY()); + */ +} + +void +Output::AddTab(const char* text, int32 index) +{ + OutputView* customOutput; + BTab* customTab; + + Lock(); + fTabView->AddTab(customOutput = new OutputView(fBounds), customTab = new BTab()); + customTab->SetLabel( text ); + fTabView->Invalidate(); + Unlock(); + + fTabsList->AddItem(customTab, index); + fOutputViewsList->AddItem(customOutput, index); + +} + +Output::~Output() +{ +/* BWindow::~BWindow();*/ + + delete fTabsList; + delete fOutputViewsList; +} + + +Output* +Output::Instance() +{ + if (!m_instance) + m_instance = new Output; + return m_instance; +} + + +bool +Output::QuitRequested() +{ + Hide(); + return false; +} + + +void +Output::MessageReceived(BMessage* msg) +{ + switch(msg->what) + { + case MSG_OUTPUT_RESET: + + for (int32 index = 0 ; indexCountItems() ; index++) { + if (fTabsList->ItemAt(index) != NULL && ((BTab*)fTabsList->ItemAt(index))->IsSelected() ) + ((OutputView*)fOutputViewsList->ItemAt(index))->Clear(); + + } + + break; + case MSG_OUTPUT_RESET_ALL: + m_pAll->Clear(); + + for (int32 index = 0 ; indexCountItems() ; index++) { + if (fTabsList->ItemAt(index) != NULL ) + ((OutputView*)fOutputViewsList->ItemAt(index))->Clear(); + } + break; + default: + BWindow::MessageReceived(msg); + break; + } +} + + +void +Output::FrameMoved(BPoint point) +{ +/* Preferences::Instance()->OutputX(point.x); + Preferences::Instance()->OutputY(point.y); +*/ +} + +void +Output::Post(const char* text, uint32 index) +{ + Lock(); + OutputView* view = (OutputView*) fOutputViewsList->ItemAt(index); + + if (view != NULL) + Add(text, view ); + else + // Note that the view should be added before this! + // Dropping twice to the main + Add(text, m_pAll ); + + Unlock(); +} + +void +Output::Add(const char* text, OutputView* view) +{ + view->Add(text); + m_pAll->Add(text); +} diff --git a/src/servers/bluetooth/Output.h b/src/servers/bluetooth/Output.h new file mode 100644 index 0000000000..26605c6188 --- /dev/null +++ b/src/servers/bluetooth/Output.h @@ -0,0 +1,63 @@ +#ifndef _Output_h +#define _Output_h + +#include +#include +#include +#include +#include + +const uint32 MSG_OUTPUT_RESET = 'outr'; +const uint32 MSG_OUTPUT_RESET_ALL = 'opra'; + +class OutputView : public BView +{ +public: + OutputView(BRect frame); + virtual void FrameResized(float width, float height); + + void Add(const char* text) {m_pTextView->Insert(text);} + void Clear() {m_pTextView->Delete(0,m_pTextView->TextLength());} + +private: + BTextView* m_pTextView; + BScrollBar* m_pScrollBar; +}; + + + +class Output : public BWindow +{ +public: + static Output* Instance(); + ~Output(); + virtual bool QuitRequested(); + virtual void MessageReceived(BMessage* msg); + virtual void FrameMoved(BPoint point); + + void AddTab(const char* text, int32 index); + + void Post(const char* text, uint32 index); + +private: // functions + Output(); + void Add(const char* text, OutputView* view); + +private: // data + static Output* m_instance; + BTab* m_pAllTab; + + OutputView* m_pAll; + + BButton* m_pReset; + BButton* m_pResetAll; + + BTabView* fTabView; + + BList* fTabsList; + BList* fOutputViewsList; + BRect fBounds; // Bounds for tabs +}; + + +#endif // _Output_h diff --git a/src/servers/bluetooth/server-bluetooth.rdef b/src/servers/bluetooth/server-bluetooth.rdef new file mode 100644 index 0000000000..b75644e4a0 --- /dev/null +++ b/src/servers/bluetooth/server-bluetooth.rdef @@ -0,0 +1,139 @@ +/* + * bluetooth_server.rdef + */ + +resource app_signature "application/x-vnd.Be-bluetooth_server"; + +resource app_flags B_SINGLE_LAUNCH | B_BACKGROUND_APP; + +resource app_version { + major = 0, + middle = 1, + minor = 0, + + variety = B_APPV_ALPHA, + internal = 0, + + short_info = "bluetooth_server", + long_info = "bluetooth_server ©2007-2008 Haiku" +}; + +resource large_icon array { + $"6E6369661204004C03FFCD9A01CD9966B204FFF2050004FFB204002B02000602" + $"B430A6B1179CB1179C3430A6494C3D4865F300345E7EFF18334E02000602B3DD" + $"36B09384B0938433DD3649544C4A60FF00345E7EFF18334E02000602B8E37133" + $"395F33395F38E3714843E64A164600D5E0E6FF4977950200060832F6423E45F2" + $"BE45F232F642C6D4714ACE2600FFFFFF1FFAFBFC46EBF0F36FD3DEE59BB1C5D2" + $"C985A4B8F8517D99FF49779502000602394A6239E1E439E1E4B94A624AAE1A45" + $"37F800497795FF18334E0200060238C213394110394110B8C2134AA1CD496B12" + $"00345E7EFF18334E02000602B430D02C104F2C104F3430D04A410F4905520034" + $"5E7EFF18334E02000602B35905319AC6319AC63359054A22224AB4AC00345E7E" + $"FF18334E020006053ABE7DB70C89370C893ABE7D4A249047970000AAC7D62BA2" + $"C0D0708BADC1C5668FA8FF49779502000602B62927B6FCA8B6FCA8362927483B" + $"8347620500608DACFF345E7E0318334E22021CCC1DBE91CC33BE50CC1DBE91CB" + $"92BF87CB92BF87CB92BF87CB60C01CCB60C01CCB79C093CB53C11CCB53C0DACB" + $"53C16ECAFCC21DCB3AC1B0CA8DC2DEC665C719C786C5F7C665C719C0ACCCAFC0" + $"ACCCAFBFCFCD9BBE36CD9CBECFCDC2BDDECD86BD91CD1CBDA1CD57BD7FCCD8BD" + $"49CC0BBCEFCC62BCA0CCD9BAE0CCD9BB94CCFABA4CCCBFB912CBC4B92FCC19B9" + $"0D5EB909CB8EB909CB8EB7E3CC8CB605CBCBB685CC55B5E2CBA6B549CB22B549" + $"CB4DB549CAE9B62CC9D3B62CC9D3B5BDCA41BD28C018BD28C018BD28C018BD9B" + $"BED8BD9BBED8BD9BBDFCBC41BDE2BCB2BDEABC41BDE2B858BECDB858BECDB800" + $"BECDB792BE87B7BDBEB5B763BE55B759BDE4B759BE13B759BD3CB87BBC80B7D5" + $"BC80B87BBC80C5F0BB43C5F0BB43C5F0BB43C5F8BB3CC5F8BB3CC5FEBB37C784" + $"BAEDC68CBAB9CA15BB78CC20BD0CCBA2BC2FCC41BD46CC4DBDB5CC4DBD7ECC4D" + $"BE050617EBFFBEFFBE3FC6DFB96BCD02BAB6C5BBB92EC513B9CBC3B6BA7AC27C" + $"BA11C30CBA0BC27CBA11B7FCBA11B7FCBA11B67CBA11B7D9BCEEB5CEBCEEB7D9" + $"BCEEBB88BCEEBB88BCEEBC18BCF9BC9FBD9DBC9FBD11BC9FBD9DBC36BEB4B3C2" + $"C6E1B3C2C6E1B243C861B5ABC8CAB4A5C9D0B5ABC8CAB5B0C8CBB542C933B542" + $"C933B3C3CAB3B889CA84B67ECC4AB889CA84B8E3CA25B8E3CA25B7F0CBCDBCC7" + $"CB03BB5ACCC2BCC7CB03BD11CABABD19CABCBD19CABCBB5CCC6EC02ACBA7BE42" + $"63C02ACBA7C5E4C610CA86C105C9FBC1F9CB12C011CAA9BE6ECAEFBFCBCAA9BE" + $"6ECB9EBD9CCB9EBD9CCBE8BCAD0609DFFB03C3A9BAC1C3A9BAC1C3E4BACDC533" + $"BBFAC533BB67C533BBFABC4AC583BC42C583BC42C71FBC16C9AABE0EC9AABCA1" + $"C9AABE0EC97DBCE7C9E0BCE5C9E0BCE5C931BBD7C58EBBE5C72EBBD0C545BAFC" + $"C3C2BA33C3E9BA3AC3C2BA330608BEBFB76FBC5ABCEDBC0EBCEDBC0EBDEABD9C" + $"C099BD53BE7BBE08C257BCBEC38FBC9FC0E1BE74C226BDE4BF9CBF05BBF0BF4D" + $"BE0EBF29BC0EBE51BC5DBD0BBD31BD28B9C7BCB2B7D9BCEE0609FEBF03C074C4" + $"AAC105C5A7C14DC53BC0BDC614BE9FC7C5BEC3C735BDC6C856BCA5CA08BCEDC9" + $"77BC5CCA98BA3ECAE0BACFCAE0B9AECAE0BA1ACBDDB98ACBDDBAABCBDDBCC7CB" + $"03BC10CBB6BCC7CB03C1B9C614C2DAC5A7C26EC55FC226C55F0609FEBF03BC38" + $"C3D2BCC9C4CFBD11C462BC81C53BBA6352BA87C65CB98AC77DB869C92FB8B1C8" + $"9EB820C9BFB602CA08B693CA08B572CA08B5DECB04B55ACACBB6B6CB63B88BCA" + $"2AB7D4CADDB88BCA2ABD7DC53BBE9FC4CEBE32C486BDEAC4860609FEBF03BA63" + $"C1B4BAF3C2B1BB3BC244BAABC31DB88DC4CFB8B1C43EB7B4C55FB693C711B6DB" + $"C680B64BC7A1B42DC7EAB535C869B3AAC7ABB409C8E6B378C8E6B499C8E6B6B5" + $"C80CB5FEC8BFB6B5C80CBBA8C31DBCC9C2B0BC5CC268BC14C2680205BD35BFBA" + $"BD35BFBABE32BFDEC0E1C123C050C0B7C171C190C3FCC268C3B3C190C323C1D8" + $"C129C1D8C22BC238C008C16BBE7BC0DBBF0BC123BDEAC0930202BE0EBF29BF9C" + $"41BF53BF29C3FCBE2CC323BEE1C2B7BF710203C0E1C28CC14DC2B0C226C2D5C3" + $"8FC2F9C323C2B1C3FCC341C420C3F6C420C3F6C347C3890608FFAAB40BC7B5B3" + $"D0C75DB481C77BB5A5C63DB56BC6B3B5E0C5C7B6AEC517B61BC56FB741C4BEB8" + $"FBC322B8C0C398B936C2ACBA04C1DEBAB2C189BB7CBFBEB427C6B30608FFAAB5" + $"D7C9C8B59CC96FB64DC98DB772C84FB737C8C5B7ADC7D9B87BC729B7E8C781B9" + $"0EC6D1BAC7C534BA8DC5AABB02C4BFBBD0C3F1BCDBC3BFBC0BC2E8B5F3C8C506" + $"08FFEAB96DCAC8B8EBCA51B9E3CA8DBB0858BACDC9C5BB42C8DABC10C829BB7D" + $"C882BC7FC7E7BDFDC6A8BD72C731BE2CC67BBDB3C6A8BC09C7EBBB73C7E7B989" + $"C9C5B989C9C5B989C9C50606FF0EBD54CBBBBC54CB85BDC9CB80BEEECA43BEB3" + $"CAB8BF29C9CDBFF7C91CBF64C975C059C8E2C1AFC7CDC122C84DC1F7C78BC090" + $"C7ACBD70CAB8BD70CAB8BD70CAB8021ECC2ABC78CC2ABCE0CC2ABC2DCBEABB89" + $"CC19BBDCCB54BA82C6EEB924C9B3B9BAC5C4B8E5C4ECB98EC50FB970C4E2B993" + $"C3B8BA28C3F0BA0CC37ABA05C279B9C9C2F4B9C4C279B9C9B7FCB9C9B7FCB9C9" + $"2AB9C9B64ABBBDB64ABAD0B64ABC25B6AABCC3B66BBC80B6DBBCF8B7DABD36B7" + $"38BD36B7DABD36BB88BD36BB88BD36BC2CBD43BC55BD94BC51BD61BC4ABDB0BB" + $"F8BE8BBC02BE72BBE1BEA1B390C6AEB390C6AEB327C71620C7DD20C77F20C843" + $"B36EC8DBB32FC89DB3C3C931B4C0C968B442C965B472C9EFB4E7CAF2B47DCA80" + $"B59FCBB8B876CAEBB72CCBDAB879CAF9B880CB12B87CCB06B8AECB9DBA12CC2C" + $"B945CC06BAB3CC49BC49CBCCBB8FCC39BC4ACBE2BC51CC06BC4DCBF6BC6FCC75" + $"BD5FCCECBCD2CCC9BE1ECD1CC05FCBD83FCCF0C05DCBDAC616C644C616C644C6" + $"42C618CAC5C129CA36C223CB12C0A1CB2FBFE0CB2FC045CB2FBF8ECB03BEBDCB" + $"1CBF37CB03BEBDCBDCBDC6CBDCBDC6CBDCBDC60635FBFFBFFABFAFFEBFEAEFFF" + $"BFFE03CB69BD56CB7FBD14CB69BD56CA5ABE53CA76BEDACA76BEDACA8EBF51CA" + $"9EBFE0CA9EBF9ECA9EC033CA48C0E1CA86C074C9D9C1A3C5B1C5DDC6D2C4BCC5" + $"B1C5DDBFF7CB73BFF7CB73BF1BCC5FBD82CC60BE1BCC86BD2ACC4ABCDDCBE0BC" + $"ECCC1BBCCACB9DBD4BCAF0BCF1CB47BD4BCAF0BD46CAEBBD46CAEBBE62C9D7C2" + $"6EC5E5C164C5B3C19BC5E9BC94CACFBC94CACFBBECCB9DBA2BCB9EBAE0CBBEB9" + $"98CB83B909CAE4B926CB3AB903CAD4B901CAB5B901CAC5B901CA92B91ECA50B9" + $"0DCA70B96CC9FFB9C9C99DB9C3C997B9C3C997BAC1C8A0BDF9C580BD52C622BE" + $"12C568BE98C52CBD7DC4B2BD93C519B8F6C995B8F6C995B8D7C9B7B8ADC9F6B8" + $"C0C9D7B8ADC9F6B855CA52B855CA52B72FCB50B551CA90B5D1CB19B52ECA6AB5" + $"1CCA16B51CCA41B51CC9DDB575C966B53AC9A1B576C966BBFFC345BB6FC27CBB" + $"46C346B57CC89BB578C897B578C897B509C906B3D4C875B43FC8E0B36FC810B3" + $"F6C715B37BC78FB3F6C715BC74BEDCBCE7BD9CBCE7BD9CBCE7BCC1BB8DBCA6BB" + $"FEBCAFBB8DBCA6B7DABCA6B7DABCA6B781BCA6B713BC60B73FBC8EB6E4BC2EB6" + $"DABBBDB6DABBECB6DABB15B7FCBA59B757BA59B7FCBA59C27CBA59C27CBA59C2" + $"E1BA55C38EBAB6C364BA9BC38EBAB6C3B1BACDC53CBA07C544BA00C544BA00C5" + $"4AB9FBC6D0B9B2C5D8B97DC961BA3CCB6CBBD1CAEEBAF3CB8DBC0ACB99BC79CB" + $"99BC43CB99BCC90207C3EFBDBFC49EBCD1C46EBD50C55CBC72C49EBC92C61ABC" + $"52C904BD01C826BC23C855BD01C6F8BE6DC708BDDFC6E9BEFCC65ABEADC699BE" + $"DCC61ABE7DC5EBBD9FC5EBBDEFC5EBBD50C6C9BCD1C65ABCE1C5ABBCF10610FF" + $"ABBAFFCAA9BE6ECAA9BE6ECB9EBD9CCA7ABAC9CCBCBBA2CAE6BBA2C97DBCE7CA" + $"0DBCE7C97DBD78C880C0B7CA0DBEE1C82CC11AC796C1C9C7DEC175C62CC1FFC4" + $"25C1A2C434C3A8C408C393C407C51CC051C7B1C283C7C5C283C7C5C19CC8BCC0" + $"99C9BFBE32CBDDBEC3CBDDBDA2CBDDBDA2CC92BCEDCC92BE57CC92C202C9BFBF" + $"9CCC25C468C759CAE6BFDECAC2C18FCB0ABE2C0612BAAEEBBA0ABFF4B304BB6C" + $"B4F1BB6CB596BB6CB56EBB3AB5A8B990B63DB823B729BB08BCC6BAE7BC85BACE" + $"BCEBB83EBE93B9ABC156BB6BC07EBB0FC0AABB6BC0F5BB6CC4E4BCF5C6B4C166" + $"C44FC166C44FC169C44EC515C1F1C534C1DCC273BC63C293BCA3C2ACBC3DC548" + $"BA89C17FB32CBFFE200203BD4BB80EBD4BB8EFBDFFB96BBEDBBB15BE79BA59BE" + $"A7BB37BD4BBC18BDF9BBA7BD4BBA2D0203BD4BBFB8BD4BC182BDECC0F0BEC5C2" + $"95BE94C237BE67C2D2BD4BC38ABDF7C31ABD4BC2B5060EAEEBE90ABC14B560BC" + $"14BB25BC14B86EBB0DB929B9DAB6D5B8FEB763BBE3BD00BA72BA35BA82BDE4B9" + $"19BECDB9F5C078BC14BF19BB1FBFB7BC14C1ABC4A6BD1DC5E0C07DC3B2BD9BBE" + $"1CBEC7C062BED6BD51C093BC32BD1DB57D0203BD4BB80EBD4BB8EFBDFFB96BBE" + $"DBBB15BE79BA59BEA7BB37BD4BBC18BDF9BBA7BD4BBA2D0203BD4BBFB8BD4BC1" + $"82BDECC0F0BEC5C295BE94C237BE67C2D2BD4BC38ABDF7C31ABD4BC2B5060EBE" + $"EFEF0FBC6CBC86B9BAB74DBA02B7DAB996B765B96BB780B98CB76BB9ABB7FBBC" + $"51BD1DB986BEEBB9F0BEA6B9ACBF34BA15BFFFB9E9BFA9BA87BFB6BC67BE7FBC" + $"67C487BC67C44DBC83C4A8BD2EC571BCF3C52DBD90C532C010C395BFAAC3D6BF" + $"D0C31ABD2EBDFFC025BC15BFBABC5ABFE7BB9BBCE7B5CCBD0DB615BCC3B5C8BC" + $"67B5BEBCA2B5C4BC67B6620A03BCF7B6C5BF48BB33BCF7BCB20A03BCFCBE5CBF" + $"32C2B3BCF7C4240A04BD1DB57DC112B3C8C46EBA4FC093BC320A04BD9BBE1CC1" + $"9ABC28C45BC1A1C07DC3B20A04BD9BBE1CC093BC32C46EBA4FC19ABC280A04BD" + $"1DC5E0C07DC3B2C45BC1A1C112C3BE0A04BC14B560BD1DB57DC112B3C8C017B3" + $"AB0A03B9DAB6D5BC14BB25BC14B60D0A03B9F5C078BC13BF74BC14BF191E0A00" + $"0100000A010101000A020102000A020103000A020104000A020105000A020106" + $"000A020107000A020108000A020109000A03010A000A03010B000A03010C000A" + $"03010D000A04020E0F000A050110000A020111000A0600000A040112000A0701" + $"13000A080114000A0903151617000A0A0318191A000A0B011B000A0C011C000A" + $"0D011D000A0E011E000A0F011F000A100120000A11012100" +};