[banshee] OSX: Add hardware support for USB mass storage devices (bgo#682087)



commit 9b5f892fd83019179d6f8de1e905ae2551afc8cf
Author: Timo DÃrr <timo latecrew de>
Date:   Sun Aug 19 16:01:10 2012 +0200

    OSX: Add hardware support for USB mass storage devices (bgo#682087)
    
    This commit adds an implementation of an OSX-specific HardwareManager.
    It supports USB mass storage devices, which can then be used through
    Banshee.Dap. This enables device/playlists syncing over USB, like i.e.
    Android devices, or other USB mass storage devices with an
    .is_audio_player file present.
    
    Signed-off-by: Bertrand Lorentz <bertrand lorentz gmail com>

 .../Banshee.Hardware.Osx/CdromDevice.cs            |  108 ++++++
 .../Banshee.Osx/Banshee.Hardware.Osx/Device.cs     |  167 +++++++++
 .../Banshee.Osx/Banshee.Hardware.Osx/DiscVolume.cs |   78 +++++
 .../LowLevel/CoreFoundation.cs                     |   74 ++++
 .../LowLevel/DiskArbitration.cs                    |   74 ++++
 .../Banshee.Hardware.Osx/LowLevel/IOKit.cs         |  107 ++++++
 .../LowLevel/OsxDiskArbiter.cs                     |  354 ++++++++++++++++++++
 .../Banshee.Hardware.Osx/LowLevel/OsxUsbData.cs    |  107 ++++++
 .../Banshee.Osx/Banshee.Hardware.Osx/UsbDevice.cs  |   70 ++++
 .../Banshee.Osx/Banshee.Hardware.Osx/UsbVolume.cs  |  101 ++++++
 .../Banshee.Osx/Banshee.Hardware.Osx/Volume.cs     |  179 ++++++++++
 src/Backends/Banshee.Osx/Banshee.Osx.addin.xml     |    2 +-
 src/Backends/Banshee.Osx/Banshee.Osx.csproj        |   16 +-
 .../Banshee.OsxBackend/HardwareManager.cs          |  140 +++++++-
 .../Banshee.Osx/Banshee.OsxBackend/OsxService.cs   |   25 ++-
 src/Backends/Banshee.Osx/Makefile.am               |   11 +
 16 files changed, 1597 insertions(+), 16 deletions(-)
---
diff --git a/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/CdromDevice.cs b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/CdromDevice.cs
new file mode 100644
index 0000000..8b91f48
--- /dev/null
+++ b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/CdromDevice.cs
@@ -0,0 +1,108 @@
+//
+// CdromDevice.cs
+//
+// Author:
+//   Timo DÃrr <timo latecrew de>
+//
+// Copyright 2012 Timo DÃrr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+using MonoMac.Foundation;
+
+using Banshee.Hardware;
+using Banshee.Hardware.Osx.LowLevel;
+
+namespace Banshee.Hardware.Osx
+{
+    public class CdromDevice : BlockDevice, ICdromDevice
+    {
+        public CdromDevice (DeviceArguments arguments) : base (arguments)
+        {
+        }
+
+        #region ICdromDevice implementation
+        public bool LockDoor ()
+        {
+            return true;
+        }
+
+        public bool UnlockDoor ()
+        {
+            return true;
+        }
+
+        public bool IsDoorLocked {
+            get {
+                return false;
+            }
+        }
+        #endregion
+    }
+    public class BlockDevice : Device, IBlockDevice
+    {
+        private IVolume v;
+
+        public BlockDevice (DeviceArguments arguments) : base (arguments)
+        {
+            this.v = new DiscVolume (arguments, this);
+        }
+
+        #region IEnumerable implementation
+        IEnumerator<IVolume> IEnumerable<IVolume>.GetEnumerator ()
+        {
+            yield return v;
+        }
+        #endregion
+
+        #region IBlockDevice implementation
+        public string DeviceNode {
+            get {
+                return "/dev/disk3";
+            }
+        }
+
+        public IEnumerable<IVolume> Volumes {
+            get {
+                List<IVolume> l = new List<IVolume> ();
+                l.Add (v);
+                return l;
+            }
+        }
+
+        public bool IsRemovable {
+            get {
+                return true;
+            }
+        }
+        #endregion
+
+        #region IEnumerable implementation
+        public IEnumerator GetEnumerator ()
+        {
+            throw new System.NotImplementedException ();
+        }
+        #endregion
+    }
+}
+
diff --git a/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/Device.cs b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/Device.cs
new file mode 100644
index 0000000..b304612
--- /dev/null
+++ b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/Device.cs
@@ -0,0 +1,167 @@
+//
+// Device.cs
+//
+// Author:
+//   Timo DÃrr <timo latecrew de>
+//
+// Copyright 2012 Timo DÃrr 
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Security.Cryptography;
+
+using MonoMac.Foundation;
+
+using Banshee.Hardware;
+using Banshee.Hardware.Osx.LowLevel;
+
+namespace Banshee.Hardware.Osx
+{
+    public class Device : IDevice, IComparable, IDisposable
+    {
+        // this is a low-level NSDictionary the OS X DiskArbitration framework
+        // gives us back for any disk devices or volumes and holds ALL information
+        // we need for a given device
+        protected DeviceArguments deviceArguments;
+
+        public Device (DeviceArguments arguments)
+        {
+            this.deviceArguments = arguments;
+
+            // copy values from the NSDictionary so we don't rely on it later
+            this.vendor = deviceArguments.DeviceProperties.GetStringValue("DADeviceVendor");
+            this.uuid = GetUUIDFromProperties (deviceArguments.DeviceProperties);
+
+            this.name = deviceArguments.DeviceProperties.GetStringValue ("DAVolumeName");
+            if (string.IsNullOrEmpty (this.name)) {
+                this.name = deviceArguments.DeviceProperties.GetStringValue ("DAMediaName");
+            }
+   
+            this.product = deviceArguments.DeviceProperties.GetStringValue("DADeviceModel");
+        }
+
+        #region IDevice implementation
+        public IUsbDevice ResolveRootUsbDevice ()
+        {
+            // TODO this should be refactored - devices don't need to be usb devices
+            // There's also firewire, thunderbolt, etc.
+            if ((this as IUsbDevice) != null)
+                return (IUsbDevice) this;
+            else {
+                // return a fake usb device
+                return new UsbDevice (deviceArguments) as IUsbDevice;
+            }
+        }
+
+        public IUsbPortInfo ResolveUsbPortInfo ()
+        {
+            return null;
+        }
+
+        /// <summary>
+        /// Dumps the device details. Mainly usefull for debugging.
+        /// </summary>
+        public void DumpDeviceDetails ()
+        {
+            foreach (var key in deviceArguments.DeviceProperties.Keys)
+                Console.WriteLine ("{0} => {1}",
+                    key.ToString (),
+                    deviceArguments.DeviceProperties.GetStringValue (key.ToString ())
+                );
+        }
+
+        public void Dispose ()
+        {}
+
+        protected static string GetUUIDFromProperties (NSDictionary properties)
+        {
+             // this is somewhat troublesome
+             // some devices have a filesystem UUID (i.e. HFS+ formated ones), but most other devices don't.
+             // As the different devices/volumes have not really always a key in common, we use different keys
+             // depending on the device type, and generate a UUID conforming 16byte value out of it
+
+             string uuid_src = 
+                properties.GetStringValue ("DAMediaBSDName") ??
+                properties.GetStringValue ("DADevicePath")  ??
+                properties.GetStringValue ("DAVolumePath");
+
+            if (string.IsNullOrEmpty (uuid_src)) {
+                Hyena.Log.ErrorFormat ("Tried to create a device for which we can't determine a UUID");
+                throw new ApplicationException ("Could not determine a UUID for the device");
+            }
+
+            // TODO actually transform into a real UUID
+            return uuid_src;
+        }
+
+        protected string uuid;
+        public string Uuid {
+            get {
+                return uuid;
+            }
+        }
+
+        protected string serial;
+        public string Serial {
+            get {
+                return "123456789";
+            }
+        }
+
+        protected string name;
+        public string Name {
+            get {
+                return name;
+            }
+        }
+
+        protected string product;
+        public string Product {
+            get {
+                return product;
+            }
+        }
+
+        protected string vendor;
+        public string Vendor {
+            get {
+                return vendor;
+            }
+        }
+
+        public IDeviceMediaCapabilities MediaCapabilities {
+            get {
+                return null;
+            }
+        }
+        #endregion
+
+        #region IComparable implementation
+        public int CompareTo (object device)
+        {
+            if (device is IDevice) {
+                return this.Uuid.CompareTo (((IDevice) device).Uuid);
+            } else {
+                throw new ArgumentException ("object is not an IDevice");
+            }
+        }
+        #endregion
+    }
+}
diff --git a/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/DiscVolume.cs b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/DiscVolume.cs
new file mode 100644
index 0000000..08b136f
--- /dev/null
+++ b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/DiscVolume.cs
@@ -0,0 +1,78 @@
+// 
+// DiscVolume.cs
+// 
+// Author:
+//   Timo DÃrr <timo latecrew de>
+// 
+// Copyright 2012 Timo DÃrr 
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using MonoMac.Foundation;
+using Banshee.Hardware.Osx.LowLevel;
+
+
+namespace Banshee.Hardware.Osx
+{
+
+    public class DiscVolume : Volume, IDiscVolume
+    {
+        public DiscVolume (DeviceArguments arguments, IBlockDevice b) : base(arguments, b)
+        {
+        }
+        #region IDiscVolume implementation
+        public bool HasAudio {
+            get {
+                return true;
+            }
+        }
+
+        public bool HasData {
+            get {
+                return false;
+            }
+        }
+
+        public bool HasVideo {
+            get {
+                return false;
+            }
+        }
+
+        public bool IsRewritable {
+            get {
+                return false;
+            }
+        }
+
+        public bool IsBlank {
+            get {
+                return false;
+            }
+        }
+
+        public ulong MediaCapacity {
+            get {
+                return 128338384858;
+            }
+        }
+        #endregion
+    }
+}
diff --git a/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/LowLevel/CoreFoundation.cs b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/LowLevel/CoreFoundation.cs
new file mode 100644
index 0000000..6c048ca
--- /dev/null
+++ b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/LowLevel/CoreFoundation.cs
@@ -0,0 +1,74 @@
+//
+// CoreFoundation.cs
+//
+// Author:
+//   Timo DÃrr <timo latecrew de>
+//
+// Copyright (C) 2012 Timo DÃrr
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+using MonoMac;
+using MonoMac.CoreFoundation;
+using MonoMac.Foundation;
+using MonoMac.AppKit;
+using MonoMac.ObjCRuntime;
+
+namespace Banshee.Hardware.Osx.LowLevel
+{
+    // Missing pieces that are not present in MonoMac.CoreFoundation
+    internal class CoreFoundation
+    {
+        [DllImport (MonoMac.Constants.CoreFoundationLibrary)]
+        public static extern IntPtr CFAllocatorGetDefault ();
+
+        [DllImport (MonoMac.Constants.CoreFoundationLibrary)]
+        public static extern IntPtr CFRunLoopGetCurrent ();
+
+        [DllImport (MonoMac.Constants.CoreFoundationLibrary)]
+        public static extern IntPtr CFRunLoopCopyCurrentMode (IntPtr runloop);
+
+        [DllImport (MonoMac.Constants.CoreFoundationLibrary)]
+        public static extern void CFRunLoopRun ();
+
+        [DllImport (MonoMac.Constants.CoreFoundationLibrary)]
+        public static extern void CFRunLoopStop (IntPtr runloop);
+
+        [DllImport (MonoMac.Constants.CoreFoundationLibrary)]
+        public static extern IntPtr CFURLCopyFileSystemPath (IntPtr url, uint style);
+
+        [DllImport (MonoMac.Constants.CoreFoundationLibrary)]
+        public static extern void CFRelease (IntPtr ptr);
+
+        [DllImport (MonoMac.Constants.CoreFoundationLibrary)]
+        public static extern bool CFNumberGetValue (IntPtr number, int numberType, out Int32 val);
+
+        [DllImport (MonoMac.Constants.CoreFoundationLibrary)]
+        public static extern void CFShow (IntPtr obj);
+
+        [DllImport (MonoMac.Constants.CoreFoundationLibrary)]
+        public static extern IntPtr CFStringCreateWithCString (IntPtr number, string str, int encoding);
+    }
+}
diff --git a/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/LowLevel/DiskArbitration.cs b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/LowLevel/DiskArbitration.cs
new file mode 100644
index 0000000..1df7549
--- /dev/null
+++ b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/LowLevel/DiskArbitration.cs
@@ -0,0 +1,74 @@
+//
+// DiskArbitration.cs
+//
+// Author:
+//   Timo DÃrr <timo latecrew de>
+//
+// Copyright 2012 Timo DÃrr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Banshee.Hardware.Osx.LowLevel
+{
+    public static class DiskArbitration
+    {
+        private const string DiskArbitrationLibrary = "/Systems/Library/Frameworks/DiskArbitration.framework/DiskArbitration";
+
+        [DllImport (DiskArbitrationLibrary)]
+        public static extern IntPtr DASessionCreate (IntPtr allocator);
+    
+        [DllImport (DiskArbitrationLibrary)]
+        public static extern IntPtr DARegisterDiskAppearedCallback (IntPtr session, IntPtr match, IntPtr callback, IntPtr context);
+    
+        [DllImport (DiskArbitrationLibrary)]
+        public static extern IntPtr DARegisterDiskDescriptionChangedCallback (IntPtr session, IntPtr match, IntPtr watch, IntPtr callback, IntPtr context);
+    
+        [DllImport (DiskArbitrationLibrary)]
+        public static extern IntPtr DARegisterDiskDisappearedCallback (IntPtr session, IntPtr match, IntPtr callback, IntPtr context);
+        
+        [DllImport (DiskArbitrationLibrary)]
+        public static extern IntPtr DAUnregisterCallback (IntPtr session, IntPtr callback, IntPtr context);
+
+        [DllImport (DiskArbitrationLibrary)]
+        public static extern IntPtr DASessionScheduleWithRunLoop (IntPtr session , IntPtr runLoop , IntPtr runloopMode);
+    
+        [DllImport (DiskArbitrationLibrary)]
+        public static extern IntPtr DASessionUnscheduleFromRunLoop (IntPtr session , IntPtr runLoop , IntPtr runloopMode);
+
+        [DllImport (DiskArbitrationLibrary)]
+        public static extern IntPtr DADiskCopyDescription (IntPtr disk);
+
+        [DllImport (DiskArbitrationLibrary)]
+        public static extern IntPtr DADiskCopyIOMedia (IntPtr disk);
+
+        [DllImport (DiskArbitrationLibrary)]
+        public static extern void DADiskUnmount (IntPtr disk, int unmountOptions, UnmountCallback callback, IntPtr context);
+
+        [DllImport (DiskArbitrationLibrary)]
+        public static extern IntPtr DADiskCreateFromBSDName (IntPtr allocator, IntPtr da_session_ref, string name);
+
+        [DllImport (DiskArbitrationLibrary)]
+        public static extern IntPtr DADiskCreateFromVolumePath (IntPtr allocator, IntPtr da_session_ref, IntPtr urlref);
+
+        public delegate void UnmountCallback (IntPtr disk, IntPtr dissenter, IntPtr context);
+    }
+}
diff --git a/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/LowLevel/IOKit.cs b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/LowLevel/IOKit.cs
new file mode 100644
index 0000000..2584a15
--- /dev/null
+++ b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/LowLevel/IOKit.cs
@@ -0,0 +1,107 @@
+//
+// IOKit.cs
+//
+// Author:
+//   Timo DÃrr <timo latecrew de>
+//
+// Copyright (C) 2012 Timo DÃrr
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+using MonoMac;
+using MonoMac.CoreFoundation;
+using MonoMac.Foundation;
+using MonoMac.AppKit;
+using MonoMac.ObjCRuntime;
+
+namespace Banshee.Hardware.Osx.LowLevel
+{
+    /// <summary>
+    /// Wrapper against the OS X IOKit framework.
+    /// Especially helpful for is the "IORegistryExplorer" program that ships with Xcode to browse
+    /// connected devices and review their properties.
+    /// </summary>
+    internal class IOKit
+    {
+        private const string IOKitLibrary = "/SystemS/Library/Frameworks/IOKit.framework/IOKit";
+
+        public static IntPtr FindInParent (IntPtr entry , CFString field)
+        {
+            // the field we search for, i.e. idVendor as the usb vendor id
+            IntPtr key = field.Handle;
+
+            IntPtr ptr = IORegistryEntryCreateCFProperty (entry, key, IntPtr.Zero, 0);
+            if (ptr == IntPtr.Zero) {
+                // key does not exist, go up one level
+                IntPtr parent;
+
+                // we search in the IOService plane - other planes might be IOUSB or IODeviceTree etc.
+                // see IORegistryExplorer program that ships with OS X Xcode.
+                IORegistryEntryGetParentEntry (entry , "IOService", out parent);
+                if (parent != IntPtr.Zero) {
+                    return FindInParent (parent, field);
+                } else {
+                    return IntPtr.Zero;
+                }
+            } else {
+                return entry;
+            }
+        }
+
+        public static IntPtr GetUsbProperty (IntPtr registry_entry, CFString key)
+        {
+            if (registry_entry == IntPtr.Zero || key.Handle == IntPtr.Zero) {
+                return IntPtr.Zero;
+            }
+
+            IntPtr parent_entry = IOKit.FindInParent (registry_entry, key);
+            if (parent_entry == IntPtr.Zero) {
+                return IntPtr.Zero;
+            }
+
+            IntPtr ptr = IORegistryEntryCreateCFProperty (parent_entry, key.Handle, IntPtr.Zero, 0);
+            //CFShow (ptr);
+            return ptr;
+        }
+
+        [DllImport (IOKitLibrary)]
+        public static extern void IOObjectRelease (IntPtr obj);
+
+        [DllImport (IOKitLibrary)]
+        public static extern IntPtr IORegistryEntryCreateCFProperty (IntPtr entry, IntPtr key, IntPtr allocator, uint options);
+
+        [DllImport (IOKitLibrary)]
+        public static extern void IORegistryEntryGetParentIterator (IntPtr iterator, IntPtr plane, out IntPtr parent);
+
+        [DllImport (IOKitLibrary)]
+        public static extern void IORegistryEntryGetParentEntry (IntPtr entry, string plane, out IntPtr parent);
+
+        [DllImport (IOKitLibrary)]
+        public static extern IntPtr IORegistryEntryFromPath (IntPtr master_port, string path);
+
+        [DllImport (IOKitLibrary)]
+        public static extern IntPtr IORegistryEntryGetPath (IntPtr entry, string plane, string path);
+    }
+}
diff --git a/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/LowLevel/OsxDiskArbiter.cs b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/LowLevel/OsxDiskArbiter.cs
new file mode 100644
index 0000000..ddcd25a
--- /dev/null
+++ b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/LowLevel/OsxDiskArbiter.cs
@@ -0,0 +1,354 @@
+//
+// OsxDiskArbiter.cs
+//
+// Author:
+//   Timo DÃrr <timo latecrew de>
+//
+// Copyright (C) 2012 Timo DÃrr
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+using MonoMac;
+using MonoMac.CoreFoundation;
+using MonoMac.Foundation;
+using MonoMac.AppKit;
+using MonoMac.ObjCRuntime;
+
+namespace Banshee.Hardware.Osx.LowLevel
+{
+    public delegate void DiskAppearedHandler (object o, DeviceArguments args);
+    public delegate void DiskDisappearedHandler (object o, DeviceArguments args);
+    public delegate void DiskDescriptionChangedHandler (object o, DeviceArguments args);
+
+    public class DeviceArguments
+    {
+        public DeviceArguments (NSDictionary properties)
+        {
+            DeviceProperties = properties;
+        }
+
+        public DeviceArguments (NSDictionary properties, OsxDiskArbiter arbiter) : this (properties)
+        {
+            DiskArbiter = arbiter;
+        }
+
+        public DeviceArguments (NSDictionary properties, OsxUsbData usbdata) : this (properties)
+        {
+            UsbInfo = usbdata;
+        }
+
+        public NSDictionary DeviceProperties;
+        public OsxUsbData UsbInfo;
+        public OsxDiskArbiter DiskArbiter;
+    }
+
+    /// <summary>
+    /// Wrapper against the OS X DiskArbitation framework. Some very useful links for a better understanding:
+    /// <see href="http://www.thoughtstuff.com/rme/weblog/?p=3";>This blog</see>,
+    /// <see href="http://www.cocoaintheshell.com/2011/03/dadiskmountapprovalcallback-double-callback/";>this</see>,
+    /// <see href="http://joubert.posterous.com/notification-when-usb-storage-device-is-conne";>this</see> as well as
+    /// <see href="http://developer.apple.com/library/mac/#documentation/Darwin/Reference/DiscArbitrationFramework/DiskArbitration_h/index.html";>
+    /// Apples DiskArbitation framework documentation</see>,
+    /// <see href="http://developer.apple.com/library/mac/#samplecode/USBPrivateDataSample/Introduction/Intro.html";>This C sample</see>,
+    /// and your local copy of /System/Library/Framework/DiskArbitration/DiskArbitration.h.
+    /// Especially helpful for is the "IORegistryExplorer" program that ships with Xcode.
+    /// </summary>
+    public class OsxDiskArbiter : IDisposable
+    {
+        public NSAutoreleasePool pool;
+
+        public OsxDiskArbiter ()
+        {
+            pool = new NSAutoreleasePool ();
+        }
+
+        /// <summary>
+        /// Called when a disk/volume "appears" to the system. This can be an USB Drive plugged in, as well as network volumes, FUSE filesystems etc.
+        /// Note that this callback is triggered for the main disk (i.e. The USB Drive itself) as well as all Volumes on it (i.e. all partitions on an USB
+        /// drive).
+        /// </summary>
+        /// <remarks>This function should not be used to retrieve the mountpoint of a newly plugged disk/volume.
+        /// It is totally indeterministic whether at the time the callback fires the disk already is mounted
+        /// in the system, and the DAVolumePath field may be null. Use <see cref='DiskDescriptionChanged'/>
+        /// instead.
+        /// </remarks>
+        public event DiskAppearedHandler DeviceAppeared;
+
+        /// <summary>
+        /// Occurs when device disappeared, i.e. is disconnected or unmounted.
+        /// </summary>
+        public event DiskDisappearedHandler DeviceDisappeared;
+
+        /// <summary>
+        /// Occurs when device description changed. This event should be used to watch for newly added usb sticks/drives, as it will have the DAVolumePath
+        /// (=mountpoint) in it.
+        /// </summary>
+        public event DiskDescriptionChangedHandler DeviceChanged;
+
+        private delegate void diskAppearedCallback (IntPtr diskRef, IntPtr context);
+        private delegate void diskDisappearedCallback (IntPtr diskRef, IntPtr context);
+        private delegate void diskChangedCallback (IntPtr diskRef, IntPtr keys, IntPtr context);
+
+        private Thread listenThread;
+        private IntPtr da_session;
+        private IntPtr runloop;
+
+        private IntPtr callback_appeared;
+        private IntPtr callback_disappeared;
+        private IntPtr callback_changed;
+
+        public void StartListening ()
+        {
+            listenThread = new Thread( () => {
+                using (var arp = new NSAutoreleasePool ()) {
+                    startArbiter ();
+                }
+            });
+            listenThread.Start ();
+        }
+
+        /// <summary>
+        /// Called when a disk/volume "appears" to the system. This can be an USB Drive plugged in, as well as network volumes, FUSE filesystems etc.
+        /// Note that this callback is triggered for the main disk (i.e. The USB Drive itself) as well as all Volumes on it (i.e. all partitions on an USB
+        /// drive).
+        /// </summary>
+        /// 
+        /// <param name='disk'>
+        ///  A reference of type DADiskRef
+        /// </param>
+        /// <param name='context'>
+        /// Application-specific context. Currently not in use.
+        /// </param>
+        /// 
+        /// <remarks>This function should not be used to retrieve the mountpoint of a newly plugged disk/volume.
+        /// It is totally indeterministic whether at the time the /// callback fires the disk already is mounted
+        /// in the system, and the DAVolumePath field may be null. Use <see cref='DiskDescriptionChanged'/>
+        /// instead.
+        /// </remarks>
+        private void NativeDiskAppeared (IntPtr disk, IntPtr context)
+        {
+            if (this.DeviceAppeared == null) {
+                // if no-one subscribed to this event, do nothing
+                return;
+            }
+
+            IntPtr device = DiskArbitration.DADiskCopyIOMedia (disk);
+            IntPtr propertiesRef = DiskArbitration.DADiskCopyDescription (disk);
+
+            // using MonoMac we can get a managed NSDictionary from the pointer
+            NSDictionary properties = new NSDictionary (propertiesRef);
+            DeviceArguments deviceArguments = new DeviceArguments (properties, this);
+
+            // get usb data
+            if (properties.HasKey ("DADeviceProtocol") && properties.GetStringValue ("DADeviceProtocol") == "USB") {
+                OsxUsbData usb = new OsxUsbData (device);
+                deviceArguments.UsbInfo = usb;
+            }
+            IOKit.IOObjectRelease (device);
+
+            // trigger the public event for any subscribers
+            this.DeviceAppeared (this, deviceArguments);
+
+            GC.KeepAlive (this);
+        }
+
+        private void NativeDiskChanged (IntPtr disk, IntPtr keys, IntPtr context)
+        {
+            if (this.DeviceChanged == null) {
+                // if no-one subscribed to this event, do nothing
+                return;
+            }
+
+            IntPtr device = DiskArbitration.DADiskCopyIOMedia (disk);
+            IntPtr propertiesRef = DiskArbitration.DADiskCopyDescription (disk);
+
+            // using MonoMac we can get a managed NSDictionary from the pointer
+            NSDictionary properties = new NSDictionary (propertiesRef);
+            DeviceArguments deviceArguments = new DeviceArguments (properties, this);
+
+            if (properties.HasKey ("DADeviceProtocol") && properties.GetStringValue ("DADeviceProtocol") == "USB") {
+                OsxUsbData usb = new OsxUsbData (device);
+                deviceArguments.UsbInfo = usb;
+            }
+
+            IOKit.IOObjectRelease (device);
+
+            // trigger the public event for any subscribers
+            this.DeviceChanged (this, deviceArguments);
+            GC.KeepAlive (this);
+        }
+
+        private void NativeDiskDisappeared (IntPtr disk, IntPtr context)
+        {
+             if (this.DeviceDisappeared == null) {
+                // if no-one subscribed to this event, do nothing
+                return;
+            }
+
+            IntPtr device = DiskArbitration.DADiskCopyIOMedia (disk);
+            IntPtr propertiesRef = DiskArbitration.DADiskCopyDescription (disk);
+
+            NSDictionary properties = new NSDictionary (propertiesRef);
+
+            DeviceArguments deviceArguments = new DeviceArguments (properties, this);
+
+            if (properties.HasKey ("DADeviceProtocol") && properties.GetStringValue ("DADeviceProtocol") == "USB") {
+                OsxUsbData usb = new OsxUsbData (device);
+                deviceArguments.UsbInfo = usb;
+            }
+
+            IOKit.IOObjectRelease (device);
+
+            this.DeviceDisappeared (this, deviceArguments);
+            GC.KeepAlive (this);
+        }
+
+        private void startArbiter ()
+        {
+            diskAppearedCallback disk_appeared_callback = new diskAppearedCallback (NativeDiskAppeared);
+            diskChangedCallback disk_changed_callback = new diskChangedCallback (NativeDiskChanged);
+            diskDisappearedCallback disk_disappeared_callback = new diskDisappearedCallback (NativeDiskDisappeared);
+
+            // create a DiskArbitration session
+            IntPtr allocator = CoreFoundation.CFAllocatorGetDefault ();
+            da_session = DiskArbitration.DASessionCreate (allocator);
+
+            this.callback_appeared = Marshal.GetFunctionPointerForDelegate (disk_appeared_callback);
+            this.callback_changed = Marshal.GetFunctionPointerForDelegate (disk_changed_callback);
+            this.callback_disappeared = Marshal.GetFunctionPointerForDelegate (disk_disappeared_callback);
+
+            DiskArbitration.DARegisterDiskAppearedCallback (da_session, IntPtr.Zero, callback_appeared, IntPtr.Zero);
+            DiskArbitration.DARegisterDiskDescriptionChangedCallback (da_session, IntPtr.Zero, IntPtr.Zero, callback_changed, IntPtr.Zero);
+            DiskArbitration.DARegisterDiskDisappearedCallback (da_session, IntPtr.Zero, callback_disappeared, IntPtr.Zero);
+
+            //IntPtr runloop = CFRunLoopGetCurrent ();
+            runloop = MonoMac.CoreFoundation.CFRunLoop.Current.Handle;
+
+            var mode = MonoMac.CoreFoundation.CFRunLoop.CFDefaultRunLoopMode.Handle;
+            DiskArbitration.DASessionScheduleWithRunLoop (da_session, runloop, mode);
+
+            // this blocks the thread
+            CoreFoundation.CFRunLoopRun ();
+
+            // this code is actually never run, but keeps our native references
+            // and callbacks alive to prevent the GC from removing it
+            GC.KeepAlive (allocator);
+            GC.KeepAlive (da_session);
+            GC.KeepAlive (callback_changed);
+            GC.KeepAlive (callback_appeared);
+            GC.KeepAlive (callback_disappeared);
+            GC.KeepAlive (disk_appeared_callback);
+            GC.KeepAlive (disk_changed_callback);
+            GC.KeepAlive (disk_disappeared_callback);
+        }
+
+        public void Dispose ()
+        {
+            // unregister our callbacks
+            DiskArbitration.DAUnregisterCallback (da_session, callback_appeared, IntPtr.Zero);
+            DiskArbitration.DAUnregisterCallback (da_session, callback_changed, IntPtr.Zero);
+            DiskArbitration.DAUnregisterCallback (da_session, callback_disappeared, IntPtr.Zero);
+
+            var mode = MonoMac.CoreFoundation.CFRunLoop.CFDefaultRunLoopMode.Handle;
+            DiskArbitration.DASessionUnscheduleFromRunLoop (da_session, runloop, mode);
+            CoreFoundation.CFRelease (da_session);
+
+            // stop the main run loop which blocks the thread
+            CoreFoundation.CFRunLoopStop (runloop);
+            listenThread.Join ();
+            GC.SuppressFinalize (this);
+        }
+
+        // we need to map the volumeUrl's to pathes
+        // like file://localhost/Volumes/Mountpoint -> /Volumes/MountPoint
+        public static string UrlToFileSystemPath (string url, uint mode = 0) {
+            if (url == null) {
+                throw new ArgumentException ("url cannot be null");
+            }
+
+            using (var arp = new NSAutoreleasePool ()) {
+                NSUrl nsurl =  new NSUrl (url);
+                NSString path = new NSString (CoreFoundation.CFURLCopyFileSystemPath (nsurl.Handle, mode));
+                return path.ToString ();
+            }
+        }
+
+        /// <summary>
+        /// Gets the name of the disk by its BSD Name.
+        /// </summary>
+        /// <returns>
+        /// The native IntPtr (corresponds to native DADiskRef) to the disk.
+        /// </returns>
+        /// <param name='bsdName'>
+        /// BSDName. A BSDName is the name of the device in the /dev device tree. Example of a valid BSDName is 'disk5s1'
+        /// </param>
+        public static IntPtr GetDiskByBSDName (string bsdName)
+        {
+            // TODO
+            return IntPtr.Zero;
+        }
+
+        public void UnmountDisk (string volumePath)
+        {
+            CFUrl url = MonoMac.CoreFoundation.CFUrl.FromUrlString (volumePath, null);
+            IntPtr disk = DiskArbitration.DADiskCreateFromVolumePath (IntPtr.Zero, da_session, url.Handle);
+
+            if (disk == IntPtr.Zero) {
+                return;
+            }
+
+            DiskArbitration.UnmountCallback cb = (IntPtr unmounted_disk, IntPtr dissenter, IntPtr context) => {
+                Hyena.Log.DebugFormat ("successfully unmounted {0}", volumePath);
+            };
+            DiskArbitration.DADiskUnmount (disk, 0, cb, IntPtr.Zero);
+        }
+    }
+
+    /// <summary>
+    /// NS dictionary helper. Allows easier access of keys in the semi-native NSDictionary
+    /// </summary>
+    public static class NSDictionaryHelper
+    {
+        public static string GetStringValue (this NSDictionary dict, string key)
+        {
+            using (var arp = new NSAutoreleasePool ()) {
+                var searchKey = dict.Keys.Where (k => k.ToString () == key).FirstOrDefault ();
+                if (searchKey == null) {
+                    return null;
+                } else {
+                    return dict[searchKey].ToString ();
+                }
+            }
+        }
+
+        public static bool HasKey (this NSDictionary dict, string key)
+        {
+            using (var arp = new NSAutoreleasePool ()) {
+                var searchKey = dict.Keys.Where (k => k.ToString () == key).FirstOrDefault ();
+                return (searchKey != null) ? true : false;
+            }
+        }
+    }
+}
diff --git a/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/LowLevel/OsxUsbData.cs b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/LowLevel/OsxUsbData.cs
new file mode 100644
index 0000000..00cc1a9
--- /dev/null
+++ b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/LowLevel/OsxUsbData.cs
@@ -0,0 +1,107 @@
+//
+// OsxUsbData.cs
+//
+// Author:
+//   Timo DÃrr <timo latecrew de>
+//
+// Copyright (C) 2012 Timo DÃrr
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+using MonoMac;
+using MonoMac.CoreFoundation;
+using MonoMac.Foundation;
+using MonoMac.AppKit;
+using MonoMac.ObjCRuntime;
+
+namespace Banshee.Hardware.Osx.LowLevel
+{
+    public class OsxUsbData
+    {
+        public uint VendorId;
+        public uint ProductId;
+        public string ProductName;
+        public string VendorName;
+
+        // not to be confused with the iSerialNumber
+        public string UsbSerial;
+
+        public ulong LocationID;
+
+        public OsxUsbData ()
+        {}
+
+        internal OsxUsbData (IntPtr registry_entry)
+        {
+            // 1st approach - get IODeviceTree's parent locationID, then find by location ID
+            /*string path = properties.GetStringValue ("DAMediaPath");
+            IntPtr entry = IORegistryEntryFromPath (IntPtr.Zero, path);
+            CFString s = new CFString ("locationID");
+            IntPtr plane = new CFString ("IODeviceTree").Handle;
+            IntPtr parent = IntPtr.Zero;
+            IORegistryEntryGetParentEntry (entry, "IODeviceTree", out parent);
+            if (parent != IntPtr.Zero) {
+                IntPtr ptr = IORegistryEntryCreateCFProperty (parent, s.Handle, IntPtr.Zero, 0);
+                CFShow (ptr);
+            }*/
+            // TODO recursive find
+
+            // 2nd approach - walk the tree (which one?) up until we find
+            // a idVendor - at worst, up to the root
+            IntPtr cf_ref;
+
+            // populate properties from the usb device info
+
+            cf_ref = IOKit.GetUsbProperty (registry_entry, "idVendor");
+            if (cf_ref != IntPtr.Zero) {
+                Int32 num;
+                CoreFoundation.CFNumberGetValue (cf_ref, 3, out num);
+                VendorId = (uint) num;
+            }
+
+            cf_ref = IOKit.GetUsbProperty (registry_entry, "idProduct");
+            if (cf_ref != IntPtr.Zero) {
+                Int32 num;
+                CoreFoundation.CFNumberGetValue (cf_ref, 3, out num);
+                ProductId = (uint) num;
+            }
+
+            cf_ref = IOKit.GetUsbProperty (registry_entry, "USB Vendor Name");
+            if (cf_ref != IntPtr.Zero) {
+                VendorName = new CFString (cf_ref).ToString ();
+            }
+
+            cf_ref = IOKit.GetUsbProperty (registry_entry, "USB Product Name");
+            if (cf_ref != IntPtr.Zero) {
+                ProductName = new CFString (cf_ref).ToString ();
+            }
+
+            cf_ref = IOKit.GetUsbProperty (registry_entry, "USB Serial Number");
+            if (cf_ref != IntPtr.Zero) {
+                UsbSerial = new CFString (cf_ref).ToString ();
+            }
+        }
+    }
+}
diff --git a/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/UsbDevice.cs b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/UsbDevice.cs
new file mode 100644
index 0000000..6321c91
--- /dev/null
+++ b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/UsbDevice.cs
@@ -0,0 +1,70 @@
+//
+// UsbDevice.cs
+//
+// Author:
+//   Timo DÃrr <timo latecrew de>
+//
+// Copyright 2012 Timo DÃrr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+
+using MonoMac.Foundation;
+
+using Banshee.Hardware;
+using Banshee.Hardware.Osx.LowLevel;
+
+namespace Banshee.Hardware.Osx
+{
+    public class UsbDevice : Device, IUsbDevice
+    {
+        public UsbDevice (DeviceArguments arguments) : base (arguments)
+        {
+        }
+
+        #region IUsbDevice implementation
+        public int ProductId {
+            get {
+                if (deviceArguments.UsbInfo != null)
+                    return (int) deviceArguments.UsbInfo.ProductId;
+                else
+                    return 0;
+            }
+        }
+
+        public int VendorId {
+            get {
+                if (deviceArguments.UsbInfo != null)
+                    return (int) deviceArguments.UsbInfo.VendorId;
+                else
+                    return 0;
+            }
+        }
+
+        public double Speed {
+            get { return 2.0; }
+        }
+
+        public double Version {
+            get { return 2.0; }
+        }
+        #endregion
+    }
+}
diff --git a/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/UsbVolume.cs b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/UsbVolume.cs
new file mode 100644
index 0000000..12b4929
--- /dev/null
+++ b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/UsbVolume.cs
@@ -0,0 +1,101 @@
+//
+// UsbVolume.cs
+//
+// Author:
+//   Timo DÃrr <timo latecrew de>
+//
+// Copyright 2012 Timo DÃrr
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+
+using MonoMac.Foundation;
+
+using Banshee.Hardware;
+using Banshee.Hardware.Osx.LowLevel;
+
+namespace Banshee.Hardware.Osx
+{
+    public class UsbVolume : Volume, IUsbDevice, IBlockDevice
+    {
+        public UsbVolume (DeviceArguments arguments) : base (arguments)
+        {
+            return;
+        }
+
+        #region IUsbDevice implementation
+        public int ProductId {
+            get {
+                if (deviceArguments.UsbInfo != null)
+                    return (int) deviceArguments.UsbInfo.ProductId;
+                else
+                    return 0;
+            }
+        }
+
+        public int VendorId {
+            get {
+                if (deviceArguments.UsbInfo != null)
+                    return (int) deviceArguments.UsbInfo.VendorId;
+                else
+                    return 0;
+            }
+        }
+
+        public double Speed {
+            get { return 2.0; }
+        }
+
+        public double Version {
+            get { return 1.0; }
+        }
+        #endregion
+
+        #region IEnumerable implementation
+        public System.Collections.IEnumerator GetEnumerator ()
+        {
+            throw new System.NotImplementedException ();
+        }
+        #endregion
+
+        #region IEnumerable implementation
+        IEnumerator<IVolume> IEnumerable<IVolume>.GetEnumerator ()
+        {
+            yield return this;
+            throw new System.NotImplementedException ();
+        }
+        #endregion
+
+        #region IBlockDevice implementation
+        public IEnumerable<IVolume> Volumes {
+            get {
+                yield return this;
+            }
+        }
+
+        public bool IsRemovable {
+            get {
+                throw new System.NotImplementedException ();
+            }
+        }
+        #endregion
+    }
+}
diff --git a/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/Volume.cs b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/Volume.cs
new file mode 100644
index 0000000..c11c22b
--- /dev/null
+++ b/src/Backends/Banshee.Osx/Banshee.Hardware.Osx/Volume.cs
@@ -0,0 +1,179 @@
+// 
+// Volume.cs
+// 
+// Author:
+//   Timo DÃrr <timo latecrew de>
+// 
+// Copyright 2012 Timo DÃrr 
+// 
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.IO;
+
+using MonoMac.Foundation;
+
+using Banshee.Hardware.Osx.LowLevel;
+
+namespace Banshee.Hardware.Osx
+{
+    public class Volume : Device, IVolume
+    {
+        private IBlockDevice block_parent;
+        public Volume (DeviceArguments arguments):base (arguments)
+        {
+        }
+
+        public Volume (DeviceArguments arguments, IBlockDevice blockdevice) : base (arguments)
+        {
+            block_parent = blockdevice;
+        }
+
+        #region IVolume implementation
+        public void Eject ()
+        {
+            Unmount ();
+        }
+
+        public void Mount ()
+        {
+            return;
+        }
+
+        public void Unmount ()
+        {
+            string volume_url = deviceArguments.DeviceProperties.GetStringValue ("DAVolumePath");
+            if (string.IsNullOrEmpty (volume_url)) {
+                return;
+            }
+            deviceArguments.DiskArbiter.UnmountDisk (volume_url);
+
+            // remove this from the devices list
+        }
+
+        public string DeviceNode {
+            get {
+                if (deviceArguments.DeviceProperties.HasKey ("DAMediaBSDName")) {
+                    return "/dev/" + deviceArguments.DeviceProperties.GetStringValue ("DAMediaBSDName");
+                } else {
+                    return null;
+                }
+            }
+        }
+
+        public string MountPoint {
+            get {
+                var volume_url = deviceArguments.DeviceProperties.GetStringValue ("DAVolumePath");
+                if (volume_url == null) {
+                    Hyena.Log.Error ("Trying to access device without valid DAVolumePath, aborting!");
+                    throw new Exception ();
+                }
+                var mountpoint = OsxDiskArbiter.UrlToFileSystemPath (volume_url);
+                return mountpoint;
+            }
+        }
+
+        public bool IsMounted {
+            get {
+                // when a device is unmounted, it triggers a DiskDisappear so we
+                // will never see unmounted devices
+                return true;
+            }
+        }
+
+        public bool IsReadOnly {
+            get {
+                bool isWriteable;
+                if (deviceArguments.DeviceProperties.HasKey ("DAMediaWritable") &&
+                    bool.TryParse (deviceArguments.DeviceProperties.GetStringValue ("DAMediaWritable"), out isWriteable)) {
+
+                    return isWriteable;
+                }
+                return false;
+            }
+        }
+
+        public ulong Capacity {
+            get {
+                ulong capacity;
+                var size = deviceArguments.DeviceProperties.GetStringValue ("DAMediaSize");
+                if (size != null && ulong.TryParse (size, out capacity)) {
+                    return capacity;
+                } else {
+                    // try the .NET  way
+                    DriveInfo info = new DriveInfo (MountPoint);
+                    return (ulong)info.TotalSize;
+                }
+            }
+        }
+
+        public long Available {
+            get {
+                // this is unreliable
+                DriveInfo info = new DriveInfo (MountPoint);
+                return info.AvailableFreeSpace;
+            }
+        }
+
+        public IBlockDevice Parent {
+            get { return block_parent; }
+        }
+
+        public bool ShouldIgnore {
+            get { return false; }
+        }
+
+        public string FileSystem {
+            get {
+                string fs = deviceArguments.DeviceProperties.GetStringValue ("DAMediaContent");
+                if (!string.IsNullOrEmpty (fs)) {
+                    return fs;
+                }
+
+                // Defaulting to MSDOS
+                return "MSDOS";
+            }
+        }
+
+        public bool CanEject {
+            get {
+                // for now, we can determine if it's ejectable, but no code to actually eject it
+                // so return false because pressing the eject button wont work
+                return true;
+                /*
+                bool isEjectable;
+                if (deviceProperties.HasKey ("DAMediaEjectable"))
+                    if (bool.TryParse (deviceProperties.GetStringValue("DAMediaEjectable"), out isEjectable))
+                        return isEjectable;
+                // default - not ejectable
+                return false;
+                */
+            }
+        }
+
+        public bool CanMount {
+            get { return false; }
+        }
+
+        public bool CanUnmount {
+            get { return true; }
+        }
+        #endregion
+    }
+}
diff --git a/src/Backends/Banshee.Osx/Banshee.Osx.addin.xml b/src/Backends/Banshee.Osx/Banshee.Osx.addin.xml
index 51129b7..6043926 100644
--- a/src/Backends/Banshee.Osx/Banshee.Osx.addin.xml
+++ b/src/Backends/Banshee.Osx/Banshee.Osx.addin.xml
@@ -18,6 +18,6 @@
   </Extension>
 
   <Extension path="/Banshee/Platform/HardwareManager">
-    <HardwareManager class="Banshee.OsxBackend.HardwareManager"/>
+    <HardwareManager class="Banshee.OsxBackend.HardwareManager" id="Banshee.OsxBackend.HardwareManager" />
   </Extension>
 </Addin>
diff --git a/src/Backends/Banshee.Osx/Banshee.Osx.csproj b/src/Backends/Banshee.Osx/Banshee.Osx.csproj
index 4e58bf9..fb1e52c 100644
--- a/src/Backends/Banshee.Osx/Banshee.Osx.csproj
+++ b/src/Backends/Banshee.Osx/Banshee.Osx.csproj
@@ -69,11 +69,23 @@
     <Reference Include="glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
       <Private>False</Private>
     </Reference>
+    <Reference Include="System.Core" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Banshee.OsxBackend\HardwareManager.cs" />
     <Compile Include="Banshee.OsxBackend\OsxService.cs" />
-    <Compile Include="OsxIntegration.GtkOsxApplication\GtkOsxApplication.cs" />
+    <Compile Include="OsxIntegration.GtkOsxApplication\GtkOSxApplication.cs" />
+    <Compile Include="Banshee.Hardware.Osx\UsbDevice.cs" />
+    <Compile Include="Banshee.Hardware.Osx\CdromDevice.cs" />
+    <Compile Include="Banshee.Hardware.Osx\Device.cs" />
+    <Compile Include="Banshee.Hardware.Osx\Volume.cs" />
+    <Compile Include="Banshee.Hardware.Osx\DiscVolume.cs" />
+    <Compile Include="Banshee.Hardware.Osx\UsbVolume.cs" />
+    <Compile Include="Banshee.Hardware.Osx\LowLevel\IOKit.cs" />
+    <Compile Include="Banshee.Hardware.Osx\LowLevel\OsxDiskArbiter.cs" />
+    <Compile Include="Banshee.Hardware.Osx\LowLevel\OsxUsbData.cs" />
+    <Compile Include="Banshee.Hardware.Osx\LowLevel\DiskArbitration.cs" />
+    <Compile Include="Banshee.Hardware.Osx\LowLevel\CoreFoundation.cs" />
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="Banshee.Osx.addin.xml">
@@ -102,5 +114,7 @@
   <ItemGroup>
     <Folder Include="OsxIntegration.GtkOsxApplication\" />
     <Folder Include="Resources\" />
+    <Folder Include="Banshee.Hardware.Osx\" />
+    <Folder Include="Banshee.Hardware.Osx\LowLevel\" />
   </ItemGroup>
 </Project>
diff --git a/src/Backends/Banshee.Osx/Banshee.OsxBackend/HardwareManager.cs b/src/Backends/Banshee.Osx/Banshee.OsxBackend/HardwareManager.cs
index 054ba57..db8871b 100644
--- a/src/Backends/Banshee.Osx/Banshee.OsxBackend/HardwareManager.cs
+++ b/src/Backends/Banshee.Osx/Banshee.OsxBackend/HardwareManager.cs
@@ -2,9 +2,9 @@
 // HardwareManager.cs
 //
 // Author:
-//   Eoin Hennessy <eoin randomrules org>
+//   Timo DÃrr <timo latecrew de>
 //
-// Copyright (C) 2008 Eoin Hennessy
+// Copyright (C) 2012 Timo DÃrr
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -24,49 +24,163 @@
 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
 
 using System;
 using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+
+using MonoMac.AppKit;
+using MonoMac.Foundation;
 
 using Hyena;
 using Banshee.Hardware;
+using Banshee.Hardware.Osx;
+using Banshee.Hardware.Osx.LowLevel;
+using Banshee.ServiceStack;
 
 namespace Banshee.OsxBackend
 {
-    public sealed class HardwareManager : IHardwareManager
+    public sealed class HardwareManager : IHardwareManager, IService
     {
         public event DeviceAddedHandler DeviceAdded;
         public event DeviceRemovedHandler DeviceRemoved;
 
-        public void Dispose ()
+        private List<IDevice> devices = new List<IDevice> ();
+
+        private OsxDiskArbiter diskArbiter;
+
+        public HardwareManager ()
         {
+            OsxService.GlobalInit ();
+            this.diskArbiter = new OsxDiskArbiter ();
+            diskArbiter.DeviceAppeared += DeviceAppeared;
+            diskArbiter.DeviceChanged += DeviceChanged;
+            diskArbiter.DeviceDisappeared += DeviceDisappeared;
+            diskArbiter.StartListening ();
         }
 
-        public IEnumerable<IDevice> GetAllDevices ()
+        private void DeviceAppeared (object o, DeviceArguments args)
         {
-            yield break;
+            Device device = new Device (args);
+
+            Hyena.Log.DebugFormat ("device appeared: {0}, path: {1}", device.Uuid,
+                args.DeviceProperties.GetStringValue ("DAVolumePath"));
+
+            lock (this) {
+                // only handle devices  which have a VolumePath (=MountPoint)
+                if (!args.DeviceProperties.HasKey ("DAVolumePath")) {
+                    return;
+                }
+
+                var protocol = args.DeviceProperties.GetStringValue ("DADeviceProtocol");
+
+                IDevice new_device = null;
+                if (!string.IsNullOrEmpty (protocol) && protocol == "USB") {
+                    new_device = new UsbVolume (args);
+                } else {
+                   new_device = new Volume (args, null);
+                }
+
+                // avoid adding a device twice - might happen since DeviceAppeared and DeviceChanged both fire
+                var old_device = devices.Where (v => { return v.Uuid == new_device.Uuid; }).FirstOrDefault ();
+                if (old_device != null) {
+                    return;
+                }
+                if (new_device != null) {
+                    devices.Add (new_device);
+
+                    // Notify that a device was added (i.e. to refresh device list)
+                    DeviceAdded (this, new DeviceAddedArgs ((IDevice) new_device));
+                }
+            }
         }
 
-        private IEnumerable<T> GetAllBlockDevices<T> () where T : IBlockDevice
+        private void DeviceChanged (object o, DeviceArguments args)
         {
-            yield break;
+            Device device = new Device (args);
+
+            Hyena.Log.DebugFormat ("device changed: {0}, path: {1}", device.Uuid,
+                args.DeviceProperties.GetStringValue ("DAVolumePath"));
+
+            lock (this) {
+                var old_device = devices.Where (d => d.Uuid == device.Uuid).FirstOrDefault ();
+                if (old_device != null) {
+                    // a device that was currently attached has changed 
+                    // remove the device and immediately re-add it
+                    devices.Remove (old_device);
+                    DeviceRemoved (old_device, new DeviceRemovedArgs (old_device.Uuid));
+                }
+
+                // do not add device without a VolumePath (=MountPoint)
+                if (!args.DeviceProperties.HasKey ("DAVolumePath")) {
+                    return;
+                }
+
+                IDevice new_device = null;
+                var protocol = args.DeviceProperties.GetStringValue ("DADeviceProtocol");
+                if (!string.IsNullOrEmpty (protocol) && protocol == "USB") {
+                    new_device = new UsbVolume (args);
+                } else {
+                    new_device = new Volume (args);
+                }
+                devices.Add (new_device);
+                DeviceAdded (this, new DeviceAddedArgs ((IDevice) new_device));
+            }
+        }
+
+        private void DeviceDisappeared (object o, DeviceArguments args)
+        {
+            Device device = new Device (args);
+
+            Hyena.Log.InformationFormat ("device disappeared: {0}, path: {1}", device.Uuid,
+                args.DeviceProperties.GetStringValue ("DAVolumePath"));
+
+            lock (this) {
+                var old_device = devices.Where (d => d.Uuid == device.Uuid).FirstOrDefault ();
+                if (old_device != null) {
+                    devices.Remove (old_device);
+                    DeviceRemoved (this, new DeviceRemovedArgs (old_device.Uuid));
+                }
+            }
+        }
+
+        public void Dispose ()
+        {
+            if (diskArbiter != null) {
+                diskArbiter.Dispose ();
+            }
+        }
+
+        public IEnumerable<IDevice> GetAllDevices ()
+        {
+            var l = devices.Where (v => { return v is Volume ; }).Select (v => v as IDevice);
+            
+            return l;
         }
 
         public IEnumerable<IBlockDevice> GetAllBlockDevices ()
         {
-            return GetAllBlockDevices<IBlockDevice> ();
+            var l = devices.Where (v => { return v is Volume; }).Select (v => v as IBlockDevice);
+
+            return l;
         }
 
         public IEnumerable<ICdromDevice> GetAllCdromDevices ()
         {
-            return GetAllBlockDevices<ICdromDevice> ();
+            yield break;
         }
 
         public IEnumerable<IDiskDevice> GetAllDiskDevices ()
         {
-            return GetAllBlockDevices<IDiskDevice> ();
+            // cdrom / dvdrom currently not supported
+            return null;
+        }
+
+        #region IService implementation
+        public string ServiceName {
+            get { return "OS X HardwareManager"; }
         }
+        #endregion
     }
 }
-
diff --git a/src/Backends/Banshee.Osx/Banshee.OsxBackend/OsxService.cs b/src/Backends/Banshee.Osx/Banshee.OsxBackend/OsxService.cs
index 57ae1cd..97bb32e 100644
--- a/src/Backends/Banshee.Osx/Banshee.OsxBackend/OsxService.cs
+++ b/src/Backends/Banshee.Osx/Banshee.OsxBackend/OsxService.cs
@@ -4,7 +4,9 @@
 // Authors:
 //   Aaron Bockover <abockover novell com>
 //   Eoin Hennessy <eoin randomrules org>
+//   Timo DÃrr <timo latecrew de>
 //
+// Copyright 2012 Timo DÃrr
 // Copyright 2009-2010 Novell, Inc.
 // Copyright 2008 Eoin Hennessy
 //
@@ -32,6 +34,7 @@ using System;
 using System.Collections;
 using System.IO;
 using System.Reflection;
+using System.Threading;
 using Gtk;
 using Mono.Unix;
 
@@ -41,6 +44,11 @@ using Banshee.Gui;
 using OsxIntegration.GtkOsxApplication;
 using Hyena;
 
+using MonoMac.CoreFoundation;
+using MonoMac.Foundation;
+using MonoMac.AppKit;
+using MonoMac.CoreWlan;
+
 namespace Banshee.OsxBackend
 {
     public class OsxService : IExtensionService, IDisposable
@@ -49,6 +57,20 @@ namespace Banshee.OsxBackend
         private InterfaceActionService interface_action_service;
         private string accel_map_filename = "osx_accel_map";
 
+        private static bool is_nsapplication_initialized = false;
+        public static void GlobalInit ()
+        {
+            // Nearly all MonoMac related functions require that NSApplication.Init () is called
+            // before usage, however other Addins (like HardwareManager) might launch before
+            // OsxService got started
+            lock (typeof (OsxService)) {
+                if (!is_nsapplication_initialized) {
+                    NSApplication.Init ();
+                    is_nsapplication_initialized = true;
+                }
+            }
+        }
+
         void IExtensionService.Initialize ()
         {
             elements_service = ServiceManager.Get<GtkElementsService> ();
@@ -81,9 +103,10 @@ namespace Banshee.OsxBackend
 
             return true;
         }
-
         private void Initialize ()
         {
+            GlobalInit ();
+
             // load OS X specific key mappings, possibly overriding default mappings
             // set in GlobalActions or $HOME/.config/banshee-1/gtk_accel_map
             string accel_map = Paths.Combine (Paths.ApplicationData, accel_map_filename);
diff --git a/src/Backends/Banshee.Osx/Makefile.am b/src/Backends/Banshee.Osx/Makefile.am
index 7ed14a1..b898ec8 100644
--- a/src/Backends/Banshee.Osx/Makefile.am
+++ b/src/Backends/Banshee.Osx/Makefile.am
@@ -5,6 +5,17 @@ LINK = $(REF_BACKEND_OSX)
 INSTALL_DIR = $(BACKENDS_INSTALL_DIR)
 
 SOURCES =  \
+	Banshee.Hardware.Osx/CdromDevice.cs \
+	Banshee.Hardware.Osx/Device.cs \
+	Banshee.Hardware.Osx/DiscVolume.cs \
+	Banshee.Hardware.Osx/LowLevel/CoreFoundation.cs \
+	Banshee.Hardware.Osx/LowLevel/DiskArbitration.cs \
+	Banshee.Hardware.Osx/LowLevel/IOKit.cs \
+	Banshee.Hardware.Osx/LowLevel/OsxDiskArbiter.cs \
+	Banshee.Hardware.Osx/LowLevel/OsxUsbData.cs \
+	Banshee.Hardware.Osx/UsbDevice.cs \
+	Banshee.Hardware.Osx/UsbVolume.cs \
+	Banshee.Hardware.Osx/Volume.cs \
 	Banshee.OsxBackend/HardwareManager.cs \
 	Banshee.OsxBackend/OsxService.cs \
 	OsxIntegration.GtkOsxApplication/GtkOsxApplication.cs



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]