jbedo/boogiesync-tablet

Knocking the Sync 9.7 into digitizer mode

Closed this issue · 17 comments

Hey,

Like the other guy, I'm having trouble sending they payload that apparently knocks the device into digitizer mode. Connected by USB on Arch linux. Here's what I can do:

  • Successfully find the device
  • Successfully detach the kernel driver on both interfaces
  • Successfully claim both interfaces

However the ctrl_transfer command to send the payload always returns "usb.core.USBError: [Errno 110] Operation timed out", even if I set the timeout to ridiculously long.

I've a few questions that might help find a fix:

  • I've capped my bt traffic when going to live mode on the android phone app, as you suggested in another reply. When I hexdump and grep it, I can't find the hex sequence in your "usb.py" script "05 05 00 03" anywhere, in either my log or that of the other dude who posted an issue. I can find "05 05 00 03", which is the payload sequence that appears in your "blue.py" file. Is this a bug?
  • How did you arrive at the 4 parameters that precede your payload in the ctrl_transfer request? As I understand it, the norm is for bmRequestType to be 0x40 and bmRequest to be 0x04 for multibyte messages sent to devices over usb. That doesn't work either, so just hoping to get some insight here.

Thanks for your help and for open sourcing this handy script,

MC

jbedo commented

Thanks for the interest! The USB code is pretty rough, I never got that working properly. I was sniffing USB packets using a windows VM and wireshark on the host, however that wasn't working very consistently and I had trouble collecting good dumps. The USB code works intermittently, it'll work perfectly for a while then stop working at all, making me think the device needs to be in a specific state for it to fall into tablet mode. The bmRequestType and bmRequest were also derived from the USB packet sniffing.

The blue.py code is much more reliable (for me), working flawlessly since I posted it on github. From memory, the 05 05 sequence did differ between the USB and BT dumps, which surprises me a lot. The main problem is I couldn't reliably capture USB packets, making it very hard to play with the device and work out the correct sequences and bytes. If you have a way of dumping USB traffic reliably then we should be able to work this out.

I've found something that might help us. Looks like the people behind
the Sync have published an SDK, or rather three
http://www.myboogieboard.com/developers/. The android and IOS SDKs
just have BT protocols, but the chrome one has both BT and USB
connections (see here
https://github.com/kent-displays/boogie-board-sync-sdk-chrome/blob/master/js/sync-sdk/sync-streaming.js).
This solves the 5504 vs 5503 question: 4 is "capture", 3 is "digitizer"
(see lines 55-60). Not sure what the difference between those is, but it
looks like they use "capture" mode for both BT and USB devices (line 145).

The work of putting the Sync into digitizer/capture mode seems to get
done by the setMode function (lines 90-99), which calls chrome's
"chrome.hid.sendFeatureReport()" function. Unfortunately it looks like
chrome abstracts away most of the connection details to achieve a single
interface for both BT and USB, so it's not obvious what's being sent
behind the scenes, and the chrome API isn't especially informative
either https://developer.chrome.com/apps/hid#method-sendFeatureReport.
Still, prima facie it looks like it's just sending the four bytes: 0x5504.

Is there any straightforward way to just send those 4 raw bytes to the
device using pyusb/libusb? That is, without the bmRequestType, bRequest
(etc) headers mandatory in "ctrl_transfer()"?

On 07/22/15 15:33, Justin Bedő wrote:

Thanks for the interest! The USB code is pretty rough, I never got
that working properly. I was sniffing USB packets using a windows VM
and wireshark on the host, however that wasn't working very
consistently and I had trouble collecting good dumps. The USB code
works intermittently, it'll work perfectly for a while then stop
working at all, making me think the device needs to be in a specific
state for it to fall into tablet mode. The bmRequestType and bmRequest
were also derived from the USB packet sniffing.

The blue.py code is much more reliable (for me), working flawlessly
since I posted it on github. From memory, the 05 05 sequence did
differ between the USB and BT dumps, which surprises me a lot. The
main problem is I couldn't reliably capture USB packets, making it
very hard to play with the device and work out the correct sequences
and bytes. If you have a way of dumping USB traffic reliably then we
should be able to work this out.


Reply to this email directly or view it on GitHub
#2 (comment).

jbedo commented

That's really interstering, thanks for the links! It's not possible to send a usb setup packet without the request type and request, that's part of the usb standard. I don't think that packet is the problem though, I'm guessing there's some other setup packet(s) that need to preceed the request to change into digitiser mode. Looking at the code you linked to, we have the payload exactly correct which is encouraging. I will need to dig through that sdk and see if there's any other packets that are sent before the mode request is ever issued.

jbedo commented

OK, dug into this a bit. Our ctrl_transfer is pretty much correct (extra 0x05 byte perhaps). I've modified the code to simply attempt resending the payload. With my boogie board, it now works but can take a large number of retries (like, 20-40) before the payload is actually accepted. No idea why this is yet. Can you try the new code and see if your device enters digitiser mode after a lot of retrying?

Success.

dev.ctrl_transfer(0x21, 0x09, 0x0305, 1, payload) <- required about
50 retries

dev.ctrl_transfer(0x21, 0x09, 0x0200, 0, payload) <- failed

dev.ctrl_transfer(0x21, 0x09, 0x0200, 1, payload) <- consistent
success on first attempt

BB-Sync now works as a digitizer.

Had a play around with drawing things in GIMP. The following things
worked out of the box:

  • Hovering pen over the surface to move the mouse without clicking
  • Tapping the pen on the surface to left click
  • Dragging the pen along the surface to left-drag (e.g., draw a line)
  • The otherwise useless button on the pen is now a right-click (though
    could probably be remapped)

I saw no evidence of GIMP responding to pressure information, even
though I explicitly mapped pressure to line-thickness in the software. I
can draw thick lines and thin lines on the Sync by pushing down harder,
but on the screen they come out a uniform thickness. Not sure if the
Sync isn't sending the info or GIMP isn't using it.

jbedo commented

Awesome! The right mouse button is mapped to the BTN_STYLUS2 key event. This is not necessarily the right mouse button, it depends on your program. Pressure sensitivity is mapped correctly, however you have to instruct GIMP to care about it. Try going to Edit -> Preferences -> Input Devices -> Configure Extended Input Device and setting the BB input to "Screen".

Alright, maybe I was premature declaring success. I looks like it was
working last time because I'd already successfully sent the payload
once, not because I changed the 3rd parameter.

A few more random things:

  • I notice you commented out the "claim_interface" lines. It still seems
    to connect in this case (I suspect ctrl_transfer claims the interface on
    its own), but if the resource is busy (e.g., mounted, etc) then instead
    of throwing an error, the loop on lines 24-32 never terminates and the
    terminal is filled with"payload transfer failed, retrying".
  • The main loop with the ui.write() functions does not terminate in
    response to ^C keyboard interrupt signals in my terminal (update does respond to ctrl + , but not ctrl + break, or ctrl + d. Maybe catch SIGINT and SIGQUIT rather than try-excepting KeyboardInterrupt?). Had to
    background the job and kill it, which left the bb-sync permanently
    claimed by a dead process. Not sure if there's a way of ensuring python
    does clean up in those cases.
  • You might want to catch the exception that's generated by unplugging
    the sync, "usb.core.USBError: [Errno 19] No such device (it may have
    been disconnected)", and terminate gracefully.

Thanks for follow up on this. I'm gonna have some fun drawing!

Works as advertised. Now have pressure sensitivity in GIMP. Thanks :-)

On 07/27/15 20:32, Justin Bedő wrote:

Awesome! The right mouse button is mapped to the BTN_STYLUS2 key
event. This is not necessarily the right mouse button, it depends on
your program. Pressure sensitivity is mapped correctly, however you
have to instruct GIMP to care about it. Try going to Edit ->
Preferences -> Input Devices -> Configure Extended Input Device and
setting the BB input to "Screen".


Reply to this email directly or view it on GitHub
#2 (comment).

jbedo commented

I've lowered the timeout and explicitly catch timout & pipe errors when retrying, hopefully this makes it faster to connect and resolves the busy error loop.

As for ^C, I've modified it to timeout on read so that it can register ^C and terminate. You should be able to ^C it and quit it without giving it futher pen input now.

Nice work mate, thanks.

On 07/27/15 22:21, Justin Bedő wrote:

I've lowered the timeout and explicitly catch timout & pipe errors
when retrying, hopefully this makes it faster to connect and resolves
the busy error loop.

As for ^C, I've modified it to timeout on read so that it can register
^C and terminate. You should be able to ^C it and quit it without
giving it futher pen input now.


Reply to this email directly or view it on GitHub
#2 (comment).

Is it possible to make this work with a proper USB driver with
https://github.com/signal11/hidapi or other python HID modules?

usb-driver-easyhid.txt

A simple reader using easyhid (still does not report my stylus movements)

jbedo commented

It doesn't report stylus movements probably because it didn't get set to digitiser mode, i.e., what the payload is supposed to do. What do you hope to gain from using hidapi over pyusb?

By using hidapi , we could simply port part of the the behaviour of https://github.com/kent-displays/boogie-board-sync-sdk-chrome to Python instead of reverse engineering other drivers. And evidently , cleaner code.

After my last comment though, when I was working on it, I found something interesting . The device filter is set to CAPTURE_USAGE_PAGE = 0xFF00 (among other attributes) in the SDK code by Kent . But the capture_usage_page of my device is 0 (as reported by easyhid enumeration) . Am I having a different device ? (The vendor id and product_id seem to be same!)

Totally forgot to mention!!!! It does not report the stylus movement. But when the payload is '\x05\x00\x03' or '\x05\x00\x04' , it does report the pressing of erase and save buttons! That is what I found strange.

When in capture mode (payload '\x05\x00\x04')

Save Button Down
bytearray(b'\x03\x00\x00\x00\x00\x00\x00@')
Save Button Up
bytearray(b'\x03\x00\x00\x00\x00\x00\x00\x00')
Erase Button Down
bytearray(b'\x03\x00\x00\x00\x00\x00\x00\x80')
bytearray(b'\x03\x00\x00\x00\x00\x00\x00\xa0')
Erase Button Up
bytearray(b'\x03\x00\x00\x00\x00\x00\x00\x00')

Ok, a sad revelation .. The digitizer component of my BB is dead :(

At least it forced me to learn about USB , HID and Python HID modules :)

jbedo commented

Ah that sucks :(. Thanks for your code snippets though! I need to test on my BB and if it works it's definitely much cleaner than the current pyusb code.