Tuesday, March 10, 2009

Bluetooth Handsfree Profile Support for Windows Mobile 5 - The Series

http://thefiles.macadamian.com/2007/01/bluetooth-handsfree-profil_116786630484523617.html

Bluetooth Handsfree Profile Support for Windows Mobile 5 - The Series (Part 1)

Bluetooth Handsfree Profile Support for Windows Mobile 5 - Part 1

Have you ever run into the question of what mechanisms are there for supporting Bluetooth on your WM5 device? What has Microsoft provided to extend the Handsfree profile functionality? How can I use the Audio Gateway?

Very good questions. In this series of articles about Bluetooth support, I'm going to try to fill in the holes. This series is geared towards developers who have experimented with the Audio Gateway and are scratching their heads as to how to get into this "black box".

Microsoft supports Bluetooth with an architecture known as the "Audio Gateway." It consists of the core itself, the Services interface, AT Command Extension Module, Phone Extension Module, and Network Component. All of these components interact with the Core that signals the Bluetooth stack of commands and state changes. To read up on the Audio Gateway architecture, trickle into the MSDN website and search for "Bluetooth Audio Gateway."

Now, as a developer, you're wondering, "How do I get into this darn thing and make it do what I want it to do?"

There are a few ways to interact with the Audio Gateway (which I'll refer to as the "AG").

Firstly, from a services standpoint, Services.exe and the underlying layer of IOCTL calls the core support call enumerations to control the AG. I've listed the constants below. They are also in the .h file. The constants are pretty self-explanatory, so I won't go into much detail about them. Basically, there is the AG service, which we can start, stop and refresh. If the service has started and a Bluetooth device has been paired with the WM5 device, a service level connection must be established with the device as to signal different commands via AT Commands (to be discussed later), which we can start and stop as well. On top of that, the AG can route audio to the Bluetooth headset.

// You can find these within service.h
// IOCTL_SERVICE_START
// IOCTL_SERVICE_REFRESH
// IOCTL_SERVICE_STOP
// IOCTL_SERVICE_STATUS

#define IOCTL_AG_OPEN_AUDIO 0x01
#define IOCTL_AG_CLOSE_AUDIO 0x02
#define IOCTL_AG_CLOSE_CONTROL 0x03
#define IOCTL_AG_SET_SPEAKER_VOL 0x04
#define IOCTL_AG_SET_MIC_VOL 0x05
#define IOCTL_AG_GET_SPEAKER_VOL 0x06
#define IOCTL_AG_GET_MIC_VOL 0x07
#define IOCTL_AG_GET_POWER_MODE 0x08
#define IOCTL_AG_SET_POWER_MODE 0x09
#define IOCTL_AG_OPEN_CONTROL 0x0A


So, by simply calling these defines within a IOCTL call, you'll be able to achieve basic services support. Note that some defines require you to pass in values. I don't want to go into too much detail about this, since we're just scratching the surface, and I'm hoping as the reader you're asking yourself, "When does the fun really start?"

HANDLE msAGHandle = ::CreateFile( L"BAG0:", 0, 0, 0, OPEN_EXISTING, 0, 0 );
if ( INVALID_HANDLE_VALUE == msAGHandle )
{
// log something with GetLastError();
return;
}

BOOL result = ::DeviceIoControl( msAGHandle, IOCTL_SERVICE_STOP, 0, 0, 0, 0, 0, 0 );
if ( FALSE == result )
{
// log something
}

::CloseHandle( msAGHandle );


Aside from the services layer, it has been hypothesized (however not actually confirmed by me) that we can replace the AT Command Extension Module to override the AG core of AT command reception and AT command processing. What happens in the AG core, once paired and a service level connection has been established, is if an AT command is received, the core will first "ask" the AT Command Extension Module if it would like to handle this command. If the AT Command Extension Module wishes to handle the command, it returns, from the interface call, "true", otherwise "false" to signal that the core does its own processing.

So how do you replace the AT Command Extension Module?

Easy. There are two ways to replace this module. In the Windows directory of your WM5 device, a file called "btagext.dll" exposes an interface (interface description to come) which the core dynamically links to. You can replace this file with your own file to override the AT command processing. Replacing the file can get a little messy if one doesn't have the proper access rights to overwrite Windows dlls.

Another way is to edit the registry so that the AT Command Extension Module path points to your dll. First, copy your AT Command Extension DLL onto the device, edit the registry path "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Bluetooth\\AudioGateway\\BTAGExtModule" with your DLL path, restart the device or restart the AG service and... Voila!

So what does your custom AT Command Extension Module have to override?

The "btagext.dll" exposes these commands:
typedef DWORD (*PFN_SendATCommand) (LPSTR szCommand, DWORD cbCommand);
BOOL BthAGATHandler(LPSTR szCommand, DWORD cbCommand);
void BthAGATSetCallback(PFN_SendATCommand pfn);
DWORD BthAGOnVoiceTag(BOOL fOn);


I leave it up to you to explore how you can replace the AT Command Extension Module. Take a look at the MSDN Website for further details about these methods. Since we are supporting the Handsfree profile, search the Bluetooth website for the .pdf file for a list of AT commands to support... Or stay tuned for a description of these AT commands.

OK, now for the really fun stuff.

I know some of you are in the same boat as I was, saying to yourself, "I've paired and authenticated with the device, so how do I use the AG to route audio? I can route audio via the IOCTL calls but I can't do any other in-call processing. Some libraries offered in WinCE are not exposed to me, e.g. the Network component (for network signalling), so I can't signal different in-call states."

Personally, I've never gotten around to using the AG service, since specific libraries in the WM5 PPC and Smartphone SDK weren't included in my version and, simply put, I had no mechanism to control the AG service besides the service component via IOCTL calls.

What to do, what to do?! Hey lets build our own Audio Gateway! Yeah! That'll be fun!

Bluetooth Handsfree Profile Support for Windows Mobile 5 - The Series (Part 2)

Bluetooth HandsFree Profile Support for Windows Mobile 5 - part 2

In our last article, we explored some of the interfaces offered by the Audio Gateway (AG) for developers.
The problem was that some of the mechanisms offered in WinCE to signal the AG core of specific network events are not present in the PPC and Smartphone SDK. In this article, I'll show you how to create a lightweight replacement for the AG without spending two weeks (or more) figuring out what's inside that little "black box" they call the Audio Gateway. Please be aware I've never worked for Microsoft and everything within this article should be considered "as is".

To start, I'll give you some background information on how the AG core is constructed and then I'll share specific details on creating a connection with the Bluetooth device, processing AT commands, and routing audio to the Bluetooth chip.

The AG core consists of 3 modules:

Parser: Once the AG core has established a socket connection with the Bluetooth Headset device, the interactions between the WM5 device and the Bluetooth Headset are signed via the socket connection with AT Commands. The parser' responsibility is to retrieve these messages from the socket, parse them and return them to the Handler module for processing.

Interface: Exposes methods to signal the network states and initiate commands from the services layer. It also listens for incoming Bluetooth device requests that are trying to create a baseband connection, whether synchronously or asynchronously.

Handler: The core module that receives input from both the Interface module and the Parser module and, based on those stimuli and the internal state of the AG core, will respond accordingly, i.e. send AT command responses, create a service level socket connection, an IOCTL call to the audio driver to route voice...etc.

The Meat and Bones of the Parser
The parser is quite simple. After the Handler creates a socket connection with the Bluetooth Headset (I'll talk more about that in the third installment) it passes the connected socket to the Parser module to listen for and receive incoming AT commands. Listed below are the AT commands that are supported by the Bluetooth HandsFree profile and their corresponding general definitions.

"AT+CKPD=200" - Indicates a Bluetooth button press
"AT+VGM=" - Indicates a microphone volume change
"AT+VGS=" - Indicates a speakerphone volume change
"AT+BRSF=" - The Headset is asking what features are supported
"AT+CIND?" - The Headset is asking about the indicators that are signaled
"AT+CIND=?" - The Headset is asking about the test indicators
"AT+CMER=" - The Headset is asking which indicates are registered for updates
"ATA" - When an incoming call has been answered, usually a Bluetooth button press
"AT+CHUP" - When a call has been hung up, usually a Bluetooth button press
"ATD>" - The Headset is requesting the local device to perform a memory dial
"ATD" - The Headset is requesting to dial the number
"AT+BLDN" - The Headset is requesting to perform last number dialed
"AT+CCWA=" - The Headset has enabled call waiting
"AT+CLIP=" - The Headset has enabled CLI (Calling Line Identification)
"AT+VTS=" - The Headset is asking to send DTMF digits
"AT+CHLD=" - The Headset is asking to put the call on Hold
"AT+BVRA=" - The Headset is requesting voice recognition
"ATH" - Call hang-up

Once the parser receives one of these commands, it parses the received buffer for these character sequences and passes the parsed command back to the Handler (discussed later) as an enumeration that the Handler will be able to identify and process.

The Meat and Bones of the Interface Module
The Interface module exposes methods for signaling the Handler of network connections, listens for incoming connections and calls Handler methods of services requests.

Firstly, the Interface layer must ensure that the Bluetooth stack has been started, otherwise there's no point in processing any other commands for the network layer or the services layer. The mechanism in which the underlying layer signals that the Bluetooth stack is fully functional is via waiting on an Event Handle. Here's some code to support this:
#define BTH_NAMEDEVENT_STACK_INITED L"system/events/bluetooth/StackInitialized"
HANDLE btStack = ::OpenEvent( EVENT_ALL_ACCESS, FALSE,BTH_NAMEDEVENT_STACK_INITED );

// m_closeEvent is a previously Created Event to signal other
// waiting threads to stop waiting (created by the developer)

if( btStack && m_closeEvent )
{
HANDLE waitObjects[] = {btStack, m_closeEvent};
DWORD result = ::WaitForMultipleObjects( 2, &waitObjects[0], FALSE, INFINITE );
::CloseHandle( btStack );
if( result - WAIT_OBJECT_0 == 0 )
{
//The Bluetooth stack is up, signal core module to finish initialization
}
else if( 1 == result - WAIT_OBJECT_0 WAIT_FAILED == result )
{
// We've been signaled to close, Bluetooth stack is down!
// Or our application has signaled us to stop waiting
}
}

Here we wait on the Open Bluetooth Stack Initialized Event or our own pre-created event to indicate either the Bluetooth stack is operational or to exit, since our application is closing too.

This same scheme also works for waiting for a synchronous or asynchronous baseband connection (Which I'll discuss further in the third installment), but instead of waiting on the BTH_NAMEDEVENT_STACK_INITED wstring, we wait on the Opened Event BTH_NAMEDEVENT_CONNECTIONS_CHANGED, as defined below:
#define BTH_NAMEDEVENT_CONNECTIONS_CHANGED L"system/events/bluetooth/ConnectionsChange"

If the Interface module receives a signal of this event, it notifies the Handler module to check its baseband connections for changes.

Aside from the listening for baseband connections, or if the Bluetooth Stack is up, the Interface module also want to process service commands and network layer notifications. So, following the services interface, as mentioned in the previous article, the module needs to support:

Opening audio
Closing audio
Opening a control channel
Closing a control channel
Getting/setting the microphone volume
Getting/setting the speaker volume
Getting/setting the power mode

And these methods simply call the Handler methods for processing (Which I'll talk about more in the third installment).
For reliability, however, a developer can't create a control channel if no Bluetooth device has been paired with the WM5 device (obviously). Also, a developer can't open up a control channel without a fully initialized Interface Module (Bluetooth Stack is operational).
Secondly, you can't open an audio channel if there is no control channel open.

On the Network signaling side, the Interface Module exposes a method call to the network layer, which passes a number to identify the type of network call being signaled. The supported signaling calls are:
#define NETWORK_EVENT_CALL_IN 0x00
#define NETWORK_EVENT_CALL_OUT 0x01
#define NETWORK_EVENT_CALL_CONNECT 0x02
#define NETWORK_EVENT_CALL_DISCONNECT 0x03
#define NETWORK_EVENT_CALL_REJECT 0x04
#define NETWORK_EVENT_CALL_INFO 0x05
#define NETWORK_EVENT_CALL_BUSY 0x06
#define NETWORK_EVENT_RING 0x07

void BthAGOnNetworkEvent( DWORD dwEvent, LPSTR pszParam );

If you're designing the Interface module and the appliction accesses the network layer, you'll have to support these defines and signal your Handler class to handle these events. For more detail on these libraries, visit the MSDN website.

Important Note:
If you're developing the Interface Module alongside Microsoft's Audio Gateway, be sure that you turn off the MS AG in your Interface Module initialization code before doing anything else. If you don't, the Microsoft's AG will interfere with *your* Gateway application. The first article in this series lists code that turn offs the service. Please refer to it for the IOCTL call that corresponds to the service shutdown.

Ok, now we're getting to the really juicy part, The Handler Module. What's ticking underneath that hood, you might ask. In this article, we've explored a system level overview of the AG core and gone into details about the different responsibilities associated with the Parser Module and the Interface Module. But what ties them all together? The Handler module!

Stay tuned for a detailed description of the Handler Module...
Comments:
Internet Explorer may not respond when you click Print or Print Preview in the File menu.When you try to connect to Web folders, you may receive the following error message:The current operation could not be completed because an unexpected error has occurred.
What version were you using?
This is very useful artical, I have developed tiny AG for bluetooth Hands Free (HF) device but I am facing issue to connect with HF when HF is made ON/OFF means connection from HF side.

I am able to make socket connection with BT_ADDRESS from registry to HF but how can I make it possible that HF make connection with me when it made start.
There are a few options you can use to detect that the Headset has either turned on or is trying to communicate with your device.
You can listen to the event "system/events/bluetooth/ConnectionsChange" just call OpenEvent with this string. Otherwise you can build a notification framework around RequestBluetoothNotifications(...). Look on msdn for more information about this. If your bluetooth device is paired and has turned on it will notify with an event
Another problem that you might run into is that MS AG might be interferring with your tiny AG. If that is the case simple do a IOControl call to the "BAG" to stop the control channel
#define IOCTL_AG_CLOSE_CONTROL 0x03
#define IOCTL_AG_OPEN_CONTROL 0x0A
Thanks for your prompt response.I tried both the methods to get event on connection change but I am facing issue like I get disconnect event immediately as I get connect event. I figure out the event type by second method Request......
I have already stoped MS AG (BAG ) service.
How can my AG accept the initiated connection request and get socket handle or should my AG try to connect with the BT_Address what my AG currently doing??
Try this out, don't stop the MS AG service entirely, keep it running. If you receive the disconnection from the headset (e.g. the headset has powered off or gone out of range) close the connection and start the MS AG control channel. When you receive a notification that there is a baseband connection again, stop MS AG control channel and then try to connect to the headset with your tiny AG. And yes connect with the BT_Address from the registry. Does this answer your question? - I'm a little unclear as to your second post.
Hello Quan Nguyen,

I tried your suggested way to connect with HF by stopping MS AG control channel on baseband connection event but the issue is my AG fails to connect with HF if my AG try to connect with HF on base band connection event, means MS AG start to make connection with HF earlier than my AG start on the event.

My AG is able to make connection with HF after few second (around 1)of the event by closing already opened MS AG connection by IOCTL_AG_CLOSE_CONTROL.

How can I stop MS AG to make connection on this connection event? or any other event that I can use for knowing about MS AG made connection with HF after base band connection event.

One thing I also want to know is if I stop entire MS AG service is there any functinality that may be affected like pairing or file transfer or other???

Any help will be very helpful to me.
Yes the MS AG when it receives the baseband notification will connect faster than your AG. And from my experience this is unavoidable unless you turn the MS AG off entirely. If MS AG service is running I do not know of any "clean" way of stopping the MS AG service from trying to connect.
You will be able to see that the headset makes a connection with the system through the RequestBluetoothNotifications notifications and you can equate that if you're not connect to it, it must be the MS AG.

If you turn off MS AG entirely, pairing and file transfer will not be affected (to my knowledge). However functionality related to BT audio while *your* AG has control of the control channel will be affected if you don't support it properly.
Hello Quan Nguyen,

Thanks for your response.
I did it by stopping MS AG entirely.
Again I have one issue related to connect() API with blocking mode. When HF is already OFF and I am trying to make connection by connect() it blocks for ever and it hangs my application. and also normally when HF is ON connect() takes 7-10 seconds to return. Can you please give me some guide line or how to tackle with such situation.
Well there a few things you can do to get around this. You can always run a multi-threaded application. One thread would call the connect() and on another thread you can set a timer which will closesocket() and stop the connect() from blocking. You can also use the select() method with a timeout. Drop by MSDN and take a look at the methods for socket programming. When the Headset is ON and it takes 7-10 seconds to connect, I think this is unavoidable. This is the latency it takes for the bluetooth connection to negotiate properly. From my experience it shouldn't take that long. If you analyse your code and it's not because of something you're doing in your application then from your perspective there is really nothing you can do (I believe - from an application level implementation).
Hello Quan Nguyen,

One time more I need your help.
I already created complete service for bluetooth AG and it works fine.

Issue I am facing in DOPOD 810 and HTC TyTN is Createfile("PFX0",0,0,NULL,OPEN_EXISTING,0,NULL);

But it fails every time I try to call createfile() and return invalid handle (0xFFFFFFFFF).

It is called in multithreaded application.
Hi,
A few things here,
1# For your CreateFile(...) make sure that it is L"PFX0" and not "PFX0". I would imagine this is important. The compiler should catch this
2# Make sure that the index is correct, possibly it could be PFX1..etc
3# Make sure that you can find this within the active drivers list [HKEY_LOCAL_MACHINE\Drivers\Active] within the registry of the device
4# If createFile(...) returns an error what does GetLastError() return, this could reveal some possible hints.
Hello Quan Nguyen,

One hint I want, is service I made for tiny bluetooth gateway is working correctly with HTC Touch. I am facing issue with TyTN device is event socket gets connected with Hands Free it doesn't start to send AT commands to my service. Also some time I start to connect and it doesn't gets connected forever. Is there any thing missing to say bluetooth stack after socket gets closed??

Socket connection is not smooth as HTC Touch device I am getting.

I try to connect with hands free when STACK_UP, connect event from stack.
Hello Quan Nguyen,

I want to close connection on removing pairing from settings on PDA.

Can you please suggest me the way how can I get notification of removing perticular pairing??

One thing I found with my AG is once my AG gets connected with HF and I remove pairing from PDA, still registry entry for the same HF is there and not removed while in case of not connected with HF via my AG it removes the registry on removing pairing.

Any thing left to inform stack to take that action after closing socket??
Hello Quan Nguyen,

I really want help from you,

My service for BTAG works fine but I am facing issue for making pairing.
Pairing logic I am using from default , When device gets paired my service will try to make connection.

I handled BTE_KEY_REVOKED event to remove connection on removing pairing. Problem I am facing with Jabra BT135 is after removing pairing if I try to make pairing again then Jabra device continuously asking for passkey and give incorrect passkey msg.

On removing pairing my service just close the socket and tear down the connection. I think any thing is missed out during closing connection hence again pairing create issue.

Can you please tell me for closing connection with BTHF just close socket in any condition like active voice or active call? or anything need to take care with BTHF or stack.

After removing pairing if I try it again, device found successfully and asking for passkey again and again.

How can I tell BTHF or stack to remove previous connection completely sp I can make fresh pairing and new connection again??


Please respond atleast.

Thanks in advance.
Hello Quan Nguyen,

I didn't receive any reply for earlier post.

Can you please reply for this one atlest?

Proble I am facing is receiving AT commands.

After making BT radio ON when my AG try to connect with BTHF and gets connected and negotiated with proper At commends and responses successfully but after that my AG didn't receive any AT commands for call operating like ATA,CHUP or BLDN and none. When my AG sends AT commands like RING it operates correctly and play ring but my AG didn't receive any At commands from HF.


This problem happens only when I break active connection by making BT radio OFF. It doesn't create any problem if I break active connection by making BTHF OFF. Why my AG didn't get any AT commands from BTHF?????

Is there anything to say to BT stack or BTHF???

Any registry to cahnge?
When I make BTHF off and try to make connection after makinf ON, it works fine but not working when BT radio is made OFF and then ON, it gets negotiated correctly but after that not responding well and At commands are not received.

Please respond at lest, even have no answer.
Hi Paresh,
My apologies for not responding, I'm not a part of the Macadamian Files any longer and hence have not checked this blog site for quite some time.

My apologies again

Regards,

Quan Nguyen
How can I control the AG to connect HF by my application?
I tried to use the bluetooth api, suck as: BthCreateACLConnection, BthCreateScoConnection, or via socket directly. But all did work.
Because the AG didn't know the connection status. Maybe i have to use the AG to connect HF in the application.

Bluetooth HandsFree Profile Support for Windows Mobile 5 - The Series (part 3)

In the last two articles, we talked about the different ways to interact with Microsoft's Audio Gateway (AG). We then focused on how to start writing your own light-weight Audio Gateway, since some versions of the PPC and Smartphone SDK do not include mechanisms to support control of the AG. In the second article, we took a system level view of the AG core and gave enough detail to create a Parser and Interface Module.

Today, we'll get to the real guts, and this is what heroes are made of: the Handler Module. However, be warned that this is not for the timid, nor the faint of heart. We'll dive into the deep end right away!

This third and last installment will discuss how to create the Handler Module, which includes processing the services and network commands from the Interface module; processing the AT Commands from the Parser Module, starting the service/control level socket connection with the Bluetooth Device; and routing audio to the Bluetooth chip.

Yeah, that's right--what you've been waiting for! With this information, you can do loads of cool things like streaming mp3 music from your mobile device to your bluetooth headset. Wow! Imagine what else you can do with a little imagination!

The Guts of the Handler Module


The guts of the Handler Module contains four small sub-modules, the connection processing, the AT command processing, network notification processing and the services processing. We'll break down these four sub-modules in this article.

Creating a Socket Connection with the Bluetooth HandsFree device:


First, we assume that the Bluetooth device has already been paired with WM5 device, if not then do so right now! Start->Settings->Connections->Bluetooth... you know the rest, if not consult your WM5 Device User's Manual on how to pair with a Bluetooth device.

Once the pair has been completed and Microsoft's AG has been turned off (See the previous article), we're ready to create a service level connection with the Bluetooth device.

The pairing of the Bluetooth device will create an entry in the registry, under [HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Bluetooth\\AudioGateway\\Devices]. In this registry folder will be numbered folders for the different Bluetooth devices that have been paired with the WM5 device. These folders consist of a socket address and the associated service value which the connection offers, in our case, the Handsfree profile service. To create a service/control-level socket connection, first iterate through the numbered folders to get the right address and service that corresponds to the device that you want to create a connection with. Then, simply connect with the Bluetooth device by using the standard winsock socket connection scheme as shown in the code below:

// make sure to include these
#include
#include

// in a function to create the connection, do this
// create a Socket
m_socket = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
if (INVALID_SOCKET == m_socket)
{
// Failed to create socket: Log something here with GetLastError();
return;
}

// Fill the socket addr structure with the bluetooth device information
SOCKADDR_BTH socketAddr = {0};
socketAddr.serviceClassId = /*Service value from the registry goes here*/;
socketAddr.addressFamily = AF_BTH;
socketAddr.btAddr = /*Address from the registry goes here*/;

// connect to the device
if( SOCKET_ERROR == connect(m_socket, (SOCKADDR *)&m_addr, sizeof(m_addr)) )
{
// Failed to connect: Log something here with GetLastError();
return;
}
Simply put, get the registry value, create the socket, fill the SOCKADDR_BTH structure with the registry value and try to connect to the Bluetooth device.

Parse AT Command Processing and Initialization Hand-shaking


So, we've created a service/control level connection with the Bluetooth device, now what? Well, once the Bluetooth device has detected a connection, it will send initialization codes as AT Commands. It's the Parser's job to parse these initialization codes and send them to us to send to the Handler Module for processing.

To receive these commands, we'll need to receive, from the socket, i.e.
// create an array of char with a size of RECEIVE_BUFFER_SIZE (that's your define)
// with s(the socket that was just connected)
int result = recv(s, buffer, RECEIVE_BUFFER_SIZE, 0);

And subsequently send out responses through the same socket the AT Command responses, i.e.
// "command" is a std::string and "s" is the socket
// that has the connection to the Bluetooth device
::send(s, command.c_str(), command.size(), 0);


The initialization hand-shaking is outlined below. However, for a detailed overview of this call flow go to the Bluetooth website .
        Headset                 AG
| |
| AT+BRSF= |
|--------------------->| 1.
| |
| +BRSF: |
|<---------------------|
| OK |
|<---------------------|
| |
| AT+CIND=? |
|--------------------->| 2.
| |
| +CIND:... |
|<---------------------|
| OK |
|<---------------------|
| |
| AT+CIND |
|--------------------->| 3.
| |
| +CIND:... |
|<---------------------|
| OK |
|<---------------------|
| |
| AT+CMER= |
|--------------------->| 4.
| OK |
|<---------------------|
| |
| |

Note that the OK command send string as follows: "\r\nOK\r\n"

1. The Headset first sends the AG the features that it supports and in turn the AG sends back features that are supported.

Listed below are constants to define the end of the AT command (after the "=" sign)

#define HF_FEATURE_EC_ANDOR_NR                   0x01
#define HF_FEATURE_CALL_WAITING_THREEWAY_CALLING 0x02
#define HF_FEATURE_CLI_PRESENTATION 0x04
#define HF_FEATURE_VOICE_RECOGNITION_ACTIVATION 0x08
#define HF_FEATURE_REMOTE_VOLUME_CONTROL 0x10

#define AG_FEATURE_THREE_WAY_CALLING 0x01
#define AG_FEATURE_EC_ANDOR_NR 0x02
#define AG_FEATURE_VOICE_RECOGNITION 0x04
#define AG_FEATURE_INBAND_RINGTONE 0x08
#define AG_FEATURE_ATTACH_NUMBER_TO_VOICETAG 0x10
#define AG_FEATURE_REJECT_A_CALL 0x20

These values can be OR'ed together to create the support value, e.g. if we only want to support three-way calling and reject a call features the value would be 0x21. Subsequently, if the headset only supports voice recognition activation and remote volume control, it would send to the AG 0x18. Hence, the string which would be sent out from the AG to the headset would be "\r\n+BRSF:%d\r\n", where %d is equal to the features supported.

Note the new line and carriage return before and after the command. These are classic AT Command delimiters. See the Bluetooth Spec for details.

2. The next message sent from the Headset to the AG is a request by the Headset to the AG to indicate which indicators are supported by the AG. An example of the response string is as follows: "\r\n+CIND:

(\"service\",(0,1)),(\"call\",(0,1)),(\"callsetup\",(0-3))\r\n"

3. The headset then requests the status of those indicators. In our example we respond with the string "\r\n+CIND: %d,%d,%d\r\n", where the %d are defined:

first %d indicates that we have cellular service (from the listed values this could be 0-1)
second %d indicates that we're in a call: 0 for false (from the listed values this could be 0-1)
third %d indicates the current call set-up: 0 for idle (from the listed values this could be 0-3)

4. Finally, the headset sends a request to enable the indicator status update in the AG.

NOTE: From time to time, in my experience, what follows with some devices that the microphone volume information is sent after step 4.

ALSO NOTE: In the last article, we discussed the different AT commands which can be expected to be received by the AG, the additional commands not mentioned in this article are out of scope, i.e. "AT+BLDN" - last number dialed, which requires the developer to have some grasp of the RIL layer. However, from a purely simple view, sending back an "\r\nOK\r\n" to the headset for these commands will be sufficient. But, I've given enough information that if the developer does understand the RIL and has implemented telephony functionality, they will be able to piece together how processing of these AT commands can be done. Keep a look out for additional articles about RIL if interested.

OK, we're done with the initialization! WOW! that was a lot of information. Let's move on to network signaling.

Network Signaling of In-call States:


As mentioned in the second article from the Interface layer, there are a few messages which will be signaled by the Interface Module to the Handler module that need to be supported: Incoming Call, Outgoing Call, Call Connected, Call Disconnected, Call Rejected, Call Busy, Call Waiting and Call Ringing. Listed below are the AT Commands sent to the headset for the corresponding Network event to signal the Headset into a different state

Incoming Call
"\r\n+BSIR:0\r\n"
"\r\n+CIEV:3,1\r\n"

Outgoing Call
"\r\n+CIEV:3,3\r\n"

Call Connected
"\r\n+CIEV:2,1\r\n"
"\r\n+CIEV:3,0\r\n"

Call Disconnected and Call Rejected
"\r\n+CIEV:2,0\r\n"

Call Waiting
"\r\n+CCWA:\"0\",0,1\r\n"

Call Ringing
"\r\nRING\r\n"

Almost done! Let’s move to the final section--voice routing.

Services Processing:


In the last article, we talked about the Interface Module and how the Handler Module needs to support method calls from the Interface Layer. In this section, we'll go deeper into these calls to see how we can accurately process them. The commands are listed as:

Opening Audio
Closing audio
Opening a control channel
Closing a control channel
Getting/setting the mic volume
Getting/setting the speaker volume
Getting/setting the power mode

I leave it up to the reader to be able to handle the start and stop of your own AG service and also the mic/speaker and power getting and setting methods, since they are trivial.

Open/Close Control Channel: from the previous section, we learned how to connect to the Bluetooth socket to create a service level connection with the remote Bluetooth device. Hence, to handle the Open Control Channel command, we'll need to create some reliability and error checking around this socket connection. To close the control channel, all that is needed is to close the connected socket with a Winsock call. I leave it up to the reader to choose an appropriate method to shut down the socket connection.

Finally, the last piece is to open and close the audio, and this is not a trivial task.

First, we need to create a synchronous connection with the Bluetooth device. If you're lucky enough, you'll already have the libraries for creating this connection. Unfortunately, this is an article that describes a work-around if you don't have this library.

In the Windows directory, you'll find the DLL "btdrt.dll." You'll need to dynamically link to a few methods to get the audio connection. Listed below is example code to call the create synchronous connection and shut down the synchronous connection (which we'll need to route audio):

//******Note that the developer needs to call
//before these calls call ::LoadLibrary(...)
//once finished with these calls call ::FreeLibrary(...)

// Code for creating a SCO connection
typedef int (*BthCreateSCOConnectionWrapper)( BT_ADDR*, unsigned short* );
BthCreateSCOConnectionWrapper proc;
HMODULE hMod;
if(NULL == (hMod = ::GetModuleHandle(L"\\Windows\\btdrt.dll")))
{
// Log something since we can't find the module
return;
}

if(NULL == (proc = (BthCreateSCOConnectionWrapper)::GetProcAddress(hMod,
L"BthCreateSCOConnection")))
{
// Log something since we can't find the method
return;
}
//ptrBTAddr is a pointer to the Bluetooth Address--the same address we created the service level connection with
//ptrHandle is a pointer to an unsigned short which will be filled with the handle to the SCO connection
proc(ptrBTAddr, ptrHandle);


// Code for closing the SCO connection
typedef int (*BthCloseConnectionWrapper)( unsigned short );
BthCloseConnectionWrapper proc;
HMODULE hMod;
if(NULL == (hMod = ::GetModuleHandle(L"\\Windows\\btdrt.dll")))
{
// Log something since we can't find the module
return;
}

if(NULL == (proc = (BthCloseConnectionWrapper)::GetProcAddress(hMod,
L"BthCloseConnection")))
{
// Log something since we can't find the method
return;
}
// handle is the pointer to the unsigned short value that was passed in to create the SCO connection
return proc( handle );


Once we have a service level connection, a SCO connection with the Bluetooth device,
we can call the audio driver to route to the Bluetooth chip.

#define WODM_BT_SCO_AUDIO_CONTROL 500
waveOutMessage(0, WODM_BT_SCO_AUDIO_CONTROL, 0, TRUE);

To close the audio channel, first signal the audio driver to stop routing to the Bluetooth chip and close the SCO connection.
waveOutMessage(0, WODM_BT_SCO_AUDIO_CONTROL, 0, FALSE);

And that's it!

I hope that this has been an adventure. Play around with what has been shown; contact me if there is anything missing. I've omitted the getting and setting of the registry values with regards to different call states, volume settings and feature supports. However, if you're interested, take a look on the MSDN websites for registry settings related to the Audio Gateway and apply them into your architecture (and AT Command processing).

Best of luck in your Bluetooth Handsfree Adventures!

If you've read this series in its entirety drop me a line, I would love to hear what you've thought about the article.

Look for more articles related to Bluetooth and Telephony in the near feature by me!

Labels: , ,


Comments:
Hello Quan,

great article :dumpup:
I've enjoyed reading a lot :)

Could you give a hint how to redirect a2dp to wiress stereo headset?
Hi,
Thanks for the comments. A2DP is quite involved and possibly would require an additional article(s) ;-) to describe. I would like to note that A2DP streaming audio is not done through the narrow band voice on the SCO channel. In any case it's not a trivial task to undertake. What exactly are you having trouble with? Have you referred to the bluetooth spec for A2DP profile? A2DP Profile Link
Hi Quan,

thanks for the link. Great info.
I tried to set up a a2dp connection and wasn't successful so far.

I would love to read an a2dp article by you :)
Hi Quan,

It is very useful to understand , how to switch audio between device and bluetooth hands free. I am facing issue here in DOPOD 810 is I am able to route audio from device to bluetooth hands free as your suggested way, but routing from hands free to device is something funny. I just close SCO connection and send 500 (FALSE) to driver and I loose audio from either side, what it require is stop/start audio after starting it again I am able to route. Same thing is done in default phone hands free usage, it can route audio anywhere without stop/start. Only difference I am getting here is I am able to route audio from hands free to device for cell call only but If I try same for music or audio played with WaveOut API then above problem occurs. Any idea will help me lot I stuck with it.
This post has been removed by the author.
Hi Paresh,
So after closing the SCO conneciton calling
waveOutMessage(0, 500, 0, FALSE); gives you no audio?
Does the call fail or is it successful, waveOutMessage(...) usually comes with a return code. If it is successful quite possibly you need to call the RIL audio set audio method, i.e. call RIL_SetAudioDevices(HRIL, RILAUDIODEVICEINFO* ); which is from the ril.dll within the Windows system folder.
If you're having a hard time finding the definition of this method try finding the ril.h file on google.

Basically this translates to a IOControl call to the WAV driver to route audio
Hello Quan,

I am trying to switch audio from HF to device with DeviceIOControl() with IOCTL IOCTL_WAV_MESSAGE and
suggested msg value 500 with dwParam2 -> TRUE/FALSE in MMDRV_MESSAGE_PARAMS structure. I am able to route cell call audio only without STOP/START but not other audio.
hi this is good article but it would have been better for me if i could have it for linux. such as handfree profile implememntation on linuc system..

thanks but..
good one
thanks a lot
please give me some guidance for the same for linux
Hello QUAN,

It is really very good article.
I am quite n ew to this Bluetooth technology. However I am in need of Bluetooth API's for Application level on Linux so that with those API's I should be able to call driver level functions.Can you suggest me something.

Regards
Nisha

3 comments:

Zhang said...

Bluetooth Handsfree AG profiles are implemented on almost all Bluetooth Enabled portable devices to make connection with in-car handsfree unit. Upto now, I couldn't see the Bluetooth Handsfree in handsfree unit role is available.

Since you can implement a lightweight AG on WM5, I'm sure you can do it easily.

The full specifications of HFP 1.5 is online. Isn't it fun to do MSN/Skype with mobile device when at home or office?

Ramchand said...
This comment has been removed by the author.
Ramchand said...

Good article.Can I know how to get notifications from BT handsfree device to answer and dial calls from bluetooth device.