It will take some reverse engineering, but once you get a grasp of what you're doing it gets easier. Plantronics devices as well as other USB device manufacturers use the HID descriptors as a standard way to inform host devices (such as your PC or slate) of the capabilities of the USB device being plugged to it.

 

Plantronics headsets often use the consumer (0x0c) and the telephony (0x08) pages in order to exchange events and commands with the host device. That is the same way that other vendors also use the same usage pages and usages.

 

Here's more or less where the reverse engineering starts. Although the usages and usage pages are the same, the way the information is presented may differ, since vendors can chose to have the information organized in reports or not.

 

For example, my other reference headset is an older Microsoft LifeChat LX-3000 and the Plantronics headset I'm using for this blog post is a Blackwire 320.

 

Since each vendor may present the usages organized within reports or not, the devices present their HID descriptors to describe how they have the data organized, so the host device knows/learns how to parse the information.

 

Here's the descriptor (taken from the lsusb command in a linux box, trimmed down for clarity) for Microsoft's LX-3000:

 

Bus 005 Device 003: ID 045e:070f Microsoft Corp.
Device Descriptor:
  idVendor                0x045e Microsoft Corp.
  idProduct               0x070f
  iProduct                1 Microsoft LifeChat LX-3000
Interface Descriptor:
  bInterfaceClass         3 Human Interface Device
HID Device Descriptor:
  Report Descriptor:
    Item(Global): Usage Page, data= [ 0x0c ] 12        Consumer
    Item(Local ): Usage, data= [ 0x01 ] 1              Consumer Control
    Item(Local ): Usage, data= [ 0xe9 ] 233            Volume Increment
    Item(Local ): Usage, data= [ 0xea ] 234            Volume Decrement
    Item(Local ): Usage, data= [ 0xe2 ] 226            Mute
And, here's the descriptor for the BW-320:
Bus 005 Device 002: ID 047f:c01f Plantronics, Inc.
Device Descriptor:
  idVendor               0x047f Plantronics, Inc.
  idProduct              0xc01f
  iProduct               2 Plantronics C320-M
Interface Descriptor:
  bInterfaceClass        3 Human Interface Device
HID Device Descriptor:
  Report Descriptor:
    Item(Global): Usage Page, data= [ 0x0c ] 12                         Consumer
    Item(Local ): Usage, data= [ 0x01 ] 1                               Consumer Control
    Item(Global): Report ID, data= [ 0x01 ] 1 
    Item(Local ): Usage, data= [ 0xe9 ] 233                             Volume Increment 
    Item(Local ): Usage, data= [ 0xea ] 234                             Volume Decrement 
    Item(Global): Usage Page, data= [ 0x0b ] 11                         Telephony 
    Item(Local ): Usage, data= [ 0x05 ] 5                               Headset 
    Item(Global): Report ID, data= [ 0x08 ] 8 
    Item(Local ): Usage, data= [ 0x2f ] 47                              Phone Mute 
    Item(Local ): Usage, data= [ 0x20 ] 32                              Hook Switch 
    Item(Local ): Usage, data= [ 0x21 ] 33                              Flash 
Note that while Microsoft presents no reports ids within its descriptor and Plantronics organizes the data within reports, the usages presented and the usage pages are the same, and if you look at the USB HID spec for their description, you get a better understanding of what they actually mean.I have omitted the details on the types and size of the data as well as some other usages present in the descriptors. If you run a USB tracer tool such as USBTrace or USBLyzer, you can plug the device and monitor the data exchanged as you press buttons on the headset (or any other USB device for that matter).I also created the python code below to help me in the development phase:
import time
import usb
dev=usb.core.find(idVendor=0x045e)   #vendor id 0x45e = Microsoft; 047f=Plantronics
intf=dev[0][3,0]
ep=intf[0]
if dev.is_kernel_driver_active(intf):
    dev.detach_kernel_driver(intf)  #libusb organizes the USB devices in configurations, interfaces (with alternates) and endpoints
eps=[] #list of HID endpoints in the device
for c in dev:
    for i in c:
        if i.bInterfaceClass == 3:  #find HID interfaces' endpoints
            print vars(i)
            eps.append(i[0])  #HACK: grab the first endpoint from this interface
for x in range(500):
    time.sleep(.5)
    try:
        for ep in eps:
            d = ep.read(64)
            for b in d: print hex(b),
            print
    except:
        pass
Once you run the code above (assuming all the dependencies from libusb 1.0 and pyusb are installed), you'll start getting printouts of the events received from the devices when you press buttons.For example, here's the sequence of events received for pressing the following buttons: volume up/volume down/talk/mute, in this order:
LX-3000
volume up:
0x1 0x0 0x0 0x0 0x0 0x0 0x0 0x0
volume down:
0x2 0x0 0x0 0x0 0x0 0x0 0x0 0x0
talk:
0x0 0x1 0x0 0x0 0x0 0x0 0x0 0x0
mute:
0x8 0x4 0x0 0x0
0x8 0x0 0x0 0x0
BW-320
volume up:
0x1 0x1
volume down:
0x1 0x2
talk:
0x8 0x2
mute:
0x8 0x1
For the cases where no report ID is present in the HID descriptor, the very first byte is already data, if a report ID is present, the first byte is the report ID itself.The data is presented as a bit-field, starting at the least significant bit. For example, for a descriptor with usages presented as usage 1, u2, u3, etc, the data is organized within the bytes as:
bit 7 6 5 4 3 2 1 0
usage 8 u7 u6 u5 u4 u3 u2 u1

 

Now, if we take the volume up and down scenarios, you'll see that the usages align with the bits reported in the data received.Removing the report ID, from the plantronics headset, we're left with exactly the same data, 0x2 for volume down and 0x1 for volume down, which happens to be 00000010 and 00000001 in binary respectively , or usage 2 and usage 1 in the descriptor, or per the HID specs, usages 234 and 233.Ok, so how can you use this all?There are lots of great things that can be built once you have the USB layer built and figured out. If it seems like a lot, it is because it is, and that's exactly why we offer an SDK. If you are the adventurous type and would like to build your own, then you could use the information here to get a jump start.Just as a simple example and to try to make it a bit more fun, I have created a small scenario using a USB toy Missile Launcher.I used the exact same thought process that I used for the headsets to reverse engineer the missile launcher and created my own code to control it in python as well. If you plan to use the same code, make sure you have the right device as there are many different option out there and may not all work the same.My launcher displays the following under lsusb:
Bus 005 Device 004: ID 2123:1010
Device Descriptor:
  idVendor               0x2123
  idProduct              0x1010
  iManufacturer          1 Syntek
  iProduct               2 USB Missile Launcher

 

Here's an abbreviated version of the missile launcher code:
class MissileLauncher:
    def move_left(self):
        self._send_cmd(MissileLauncher.LEFT)
    def move_right(self):
        self._send_cmd(MissileLauncher.RIGHT)
    def move_up(self):
        self._send_cmd(MissileLauncher.UP)
    def move_down(self):
        self._send_cmd(MissileLauncher.DOWN)
    def fire(self):
        self._send_cmd(MissileLauncher.FIRE)
My next step was to decide which buttons from the headset trigger which actions on the toy launcher. I went with the following configuration:
Headset Button Missile Launcher Action
Talk Fire/Shoot
Mute Up/Down
Volume Up Right
Volume Down Left

 

The reason I assigned the volume up/down to the horizontal rotation of the launcher is that there's a lot more travel horizontally than vertically. Since there were no more buttons to have separate buttons assigned for both directions, I figured I could reuse the mute button for the vertical movement. Once the launcher reaches the limit in one direction, it will revert to the other, for example, if you are pressing mute and it's going up, once it reaches its max height, it'll start to lower until it reaches its lowest position and revert again.And here's how it looks like when I take the headset input and plugin with the missile launcher commands:
#### read events
for loop in range(1,500):
   data=None
   while data == None:
        try:
            data = ep.read(timeout)
            print time.ctime(), "<<<", data
        except:
            #print sys.exc_info()
            continue
    print time.ctime(), "here", data[0], data[1]
    if data[0] == 0x01:
        ml.move_right()
        print "right", data
    elif data[0] == 0x02:
        ml.move_left()
        print "left", data
    elif data[0] == 0x08:
        if ml.limits()&ml.UP or ml.limits()&ml.DOWN:  # check vertical limits
            print "bump"
            going_up = not going_up  #reverse vertical direction
            if going_up:
               ml.move_up()
               print "up", data
        else:
            ml.move_down()
            print "down", data
    elif data[0] == 0x00:
        ml.fire()
        time.sleep(3)
        print "fire", data
For simplicity, I'm not properly parsing the data received from the headset based on its HID descriptors, but rather using the raw data. Also, I'm showing here only the code with the LX-3000 headset data, and only minor modifications would be needed to use the code with the Plantronics headset or any other USB HID device that could send some data back to the host machine.And now to make this even more interesting in a geek way, I have the whole code running on a Raspberry Pi with the headset and the launcher plugged in to its USB ports.To run the app, simply invoke it with python and press the headset buttons to see the launcher react to it.

 

python preview

 

See the video below:

 

 

Have fun!!Ricardo

 

News Articles Classification: 

Comments


I tried make this with Calisto 240 at debian and CentOS. It is impossible, right?

The OS take it as a speaker and i can't read the events of the phone.

Do somebody know if i have reason and it is not possible?

Thanks!


The Calisto 240 doesn't use standard telephony USB HID usages, as it implements additional features (dialpad, caller id display, in call dtmf dialling). Anyone out there needing the details needs to contact us directly.


Could you please provide the complete C-Code for Windows USB for dealing with the Headset Buttons and Headset Ring Feature ? Thanks a lot Samuel