Part of my self-set midterm goal is bringing basic support for hardware devices in OSX banshee, in particular to USB mass storage devices. Mass storage devices are very common: From the first generation of mp3 players, the mass storage usb driver was used to put mp3 files into the players flash memory and it is still widely used today: All recent Android phones support it, as well as low-cost consumer mp3 player sticks and more sophisticated “feature phones” are also often loadable via usb mass storage. That makes having mass storage support in OS X banshee very important.
OS X disk arbitration library
After little research, I found there are several methods to access usb devices on OS X. There is the IOKit framework, that can be used to handle everything usb related, as far as low level device driver programming. Luckily, there is another Framework that is a little higher on the device layer while not being bound too strictly to USB and fits perfectly for our purposes: the DiskArbitration framework. You can use it to hook callbacks whenever a disk device (being a USB/Firewire mountable volume like a usb stick or harddrive, network shares or .dmg images) appears or disappears to the system. So I chose to go for it and started research.
Unfortunately, neither the IOKit nor the DiskArbitration framework are yet bound in MonoMac. That meant I had a number of choices:
- writing myself a C based glue layer to interface with the frameworks
- bind the needed function calls myself with DllImport P/Invokes and wrap the datatypes
The first way is usually an easier one from a programmers perspective, as I could do everything in C and just write a very basic public interface that is marshaled by the mono runtime. Future changes in the framework’s public ABI can be spotted very easily, as the C compiler will complain about missing functions or changes datatypes.
However, I chose the second way as usually prefer keeping one language in a project. Banshee uses quite some C gluelayers at the moment, bug there are efforts to remove them and someday have a completely C# based codebase.
Although MonoMac did not bind the IOKit/DiskArbitration framework, there is limited support for the CoreFoundation framework which helped doing a managed-only solution. I did never work on low-level OS X APIs before, neither did I work with MonoMac, so I spent nearly a full-time week digging into the Header files (which turned out to be the best documentation for the API) and finding out about MonoMac, and how I could handle the native CFObject types and marhsal them into C#.
If I had more knowledge about MonoMac and OS X internals, I’d have tried to contribute them to MonoMac by binding the missing frameworks. I am however glad I didn’t chose that in the first place, as it turned out I only needed a handful of funtions wrapped, and especially IOKit is a huge beast with far more complexity needed for my purposes. So right now, there are two small static IOKit and DiskArbitation classes that expose the needed library calls via the public static extern DllImport mechanism.
Hooking into disk arbitration wasn’t the difficult part. Turned out, for device recognition I needed the usb vendorId/productId pairs, and those were not provided by disk arbtitration (as its not bound to the USB bus). It took me days to figure out how to do that, and I had a chance to catch up with my rusty C abilities, as I did the prototype completely in C, and then rebuild it in C#.
I managed to get USB device recognition working quite well (as far as I could test). In the latest alpha build (see below), you can plugin usb mass storage devices at will, before or during banshee runs, and we hook into all necessary events (device unmounting, hard usb removal because life is to short to remove usb safely) and banshee will refresh the devices list.
The usb vendor/product ID pairs are exposed, so lots of device abilities are detected automatically. My Samsung Galaxy S (with CM9) is correctly detected as an android device, and I tested a simple USB stick with an .is_audio_player file to pretend it being a MP3 player. There might be glitches with devices having more than one partition on them, I have yet to test that.
As I only have a limited number of devices, I invite everyone to test with your devices, and give me feedback so I can further improve the mass storage support.
The alpha3 build I put together includes, besides the very large hardware-support patch (that is not yet in upstream as it will likely change in the next weeks), enabled UPnP/DLNA client support . There was not much to do, just create a bockbuild package dependency and it worked out of the box, same way as it does on Linux.
As the folks from xamarin pointed out to me in IRC, there are known problems with Glib 2.32 on OS X which severely impact performance. The (quite old) gcc Apple ships seems to have problem with gcc interlocked/atomic intrinsic (compare-and-swap and alike). I downgraded to Glib 2.30 by reusing the xamarin package and to my great surprise I could actually feel it. The UI is a little more responsive, but the great change is the playback: While I often had sound stuttering when the system was under load, that is completely gone now. That change is also included in the latest alpha build.
New alpha build release
As I had done with the two progress reports before, I’ve bundled a new Banshee.app (this time alpha3) ready for everyone to download and test. Get it from here and do not forget to read the instructions provided. Feedback is greatly welcome, you could use the banshee mailing list for that.