On Mi, 2011-01-19 at 01:46 +0000, Tony wrote:
> I am excited about hearing the good news.
> Could you show me when and where i can get the published code?

The code is public now. We decided to publish the whole development
history of it instead of just the final state. The history contains
useful information about why things were done and some code which did
not end up in the final revision, but might still be useful in the
future.

Because of the number of patches, please get the code from:
http://meego.gitorious.org/~pohly/meego-middleware/pohlys-buteo-mtp

The "gadgetfs" branch was rebased against current "master". I verified
that the tip of the branch compiles and passes some simple sanity checks
(see below), but intermediate versions do not necessarily compile.
Therefore I squashed all patches into one commit on the "master" branch
of the repo above and suggest the following steps to update the official
repo - if you agree with the changes, of course:
     1. git checkout master
     2. git remote add gadgetfs
        git://gitorious.org/~pohly/meego-middleware/pohlys-buteo-mtp.git
     3. git fetch gadgetfs
     4. git merge gadgetfs/master
     5. git push origin master gadgetfs/gadgetfs 

Paul has written an extensive documentation for his work, see
mts/transport/gadgetfs/README (also attached). There's obviously quite a
bit of work still left to do, but it takes Buteo MTP to the level of
"almost works in MeeGo", and therefore I suggest merging it right away.

I've replicated Paul's setup with "mtp_test" running as root in a MeeGo
chroot, talking via dummy_hcd to a normal Linux host (in my case, Debian
Testing with a recompiled kernel for gadgetfs/dummy_hcd). gphoto and
Nautilus detect the virtual MeeGo device and I was able to view a photo
via MTP.

However, there was a long delay between "click on photo in nautilus" and
"mtp_test starts sending data", with debug output of mtp_test pointing
towards some kind of communication problem during that delay. Paul also
mentions running mtp-detect and that he was seeing failures; I haven't
tried that myself yet.

Does mtp-detect work in the setup that Nokia is using to test? Are there
known failures?

Here are some possible next steps (not necessarily in this order):
     1. get the code merged
     2. clean up debug logging: replace printf with Qt mechanism
     3. change the gadgetfs transport code to always send ZLP (see
        discussion between Deepak and Paul)
     4. make fsstorageplugin more configurable, or at least adapt to
        MeeGo paths
     5. decide about kernel config: enable gadgetfs and dummy_hcd by
        default in MeeGo so that development can be done in MeeGo?
     6. get mtp-detect working with mtp_test
     7. fix permission problems when running as normal user inside
        msyncd

A word of caution: I'll continue to work on this in the background, but
like Paul I also got other things to do which are considered more
important. If someone has spare cycles (inside our outside of Nokia
+Intel), then this would be a good opportunity to get involved.

-- 
Best Regards

Patrick Ohly
Senior Software Engineer

Intel GmbH
Open Source Technology Center               
Pützstr. 5                              Phone: +49-228-2493652
53129 Bonn
Germany
Gadgetfs-based USB transport for buteo-mtp:

-----------------------------------------------------------------------
Description:

These files comprise a (new) subclass of the MTPTransporter super-class.  The
new subclass is named "MTPTransporterGadgetfs".  It implements the "device" end
of USB transport completely in user-space by taking advantage of the "gadgetfs"
interface supplied by the linux kernel (with suitable configuration option(s)
selected).

Briefly, gadgetfs makes an interface to the (singleton) USB device-controller
chip driver appear as a name in a pseudo filesystem.  Opening this device and
writing suitable descriptor records causes bulk and interrupt endpoints to
appear and become configured.  Opening some of these and writing some more
descriptors prepares these for I/O as USB logical pipes.  The original opened
usb-controller device remains used as "endpoint 0" for various overall control
and status operations.  All subsequent I/O operations and their meanings are
subject to the protocol definition of whatever USB device class is being
portrayed.

Although a strong effort has been made to make these files readily re-usable
for different USB device classes, these files are _not_ generic for just any
old device class.  They are specific to Media Transfer Protocol (MTP), which is
a compatible extension of Picture Transfer Protocol (PTP).  Class specific
details show up in several ways:

() A PTP/MTP specific constellation of endpoints is configured and opened when
   the device becomes connected:  a bulk-out, a bulk-in, and an interrupt-in
   endpoint.  The PTP/MTP protocol is defined to operate over these endpoints.
   This code simply forwards requests, responses, and events back and forth
   between the USB endpoints and higher software layers that interpret the
   PTP/MTP protocol.
() PTP/MTP also defines several class-specific operations over the "control"
   endpoint (endpoint 0).  The code interprets these directly.

Adapting this code for a different USB device class would be a matter of
opening and configuring a different constellation of bulk and interrupt
endpoints as defined by that device class, and replacing with control-endpoint
class-specific operation handler procedure with one appropriate for your new
device class.  Of course, you would also need to embed the class in a
higher-level framework that implements your protocol over the bulk and
interrupt endpoints.

-----------------------------------------------------------------------
Design Notes:

The QT-based high-level MTP framework in which this is embedded uses an
"application event-loop" model to drive all its actions:  the application
consists of a single main thread that normally sits idly waiting for an event.
When one comes along (from any of a variety of sources), it wakes up, handles
it, and goes back to goes back to the central idle/wait point.

This application event loop has to be triggered to wake up and handle USB
packets when they arrive.  Ordinarily the QT framework would provide convenient
support for this.  A QSocketNotifier could be created and applied to a
file-descriptor such as a socket or one of the file-descriptors embodying the
USB endpoints.  Then two things (are supposed to) happen:

() QSocketNotifier internally runs something like select(), poll(), or epoll()
   at its idle/wait point and dispatch the event-loop thread to a callback of
   your choice when I/O becomes available, and

() Your file-descriptor is set to non-blocking (O_NONBLOCK) so that you can do
   a read or write without risk of blocking and hanging up the single
   event-loop process.

However, as of kernel version 2.6.36, the file-descriptors handed out by the
gadgetfs pseudo-filesystem for endpoints do _not_ handle select() or O_NONBLOCK
properly (not really a bug, just a not-implemented-yet).  Select() fires
immediately and continuously regardless of the actual I/O readiness state, and
a read() or write() will merrily block regardless of the setting of O_NONBLOCK.
So QSocketNotifier doesn't work here.

Accordingly, the MTPTransporterGadgetfs uses asynchronous I/O (which _is_
supported by gadgetfs) to implement non-blocking I/O and notifications.  This
is currently done only for "read" operations, since "write" operations are all
expected to be responses that will be delivered promptly.

The design is as follows:

() When any endpoint is opened for reading, MTPTransporterGadgetfs posts one or
   more asynchronous IO control blocks with "read" opcodes at the file
   descriptor.

() MTPTransporterGadgetfs uses one of the available asynchronous I/O interface
   mechanisms to get notified whenever an asynchronous I/O control block
   reaches the "completed" state.  This happens independently of the main
   event-loop thread.  When notified, the control-block is enqueued at a queue
   of completed reads for that endpoint and the main event-loop thread is
   triggered to activate a "ReadReady" callback.  This activation happens
   through the signal/slot connection mechanism using the "QueuedConnection"
   type of connection, which can cross thread boundaries for later delivery.
   See the QObject::connect()-->Qt::ConnectionType documentation for details.

() When the "ReadReady" callback is invoked in the normal event-loop thread, it
   dequeues the completed asynchronous I/O control block just as if it were
   doing a normal read and handles the read data as usual.  When done with the
   data, and before returning to the main-loop, it re-posts the asynchronous IO
   control block again for another read just as at the beginning of time.

There are two asynchronous I/O interface families as candidates for the
underlying asynchronous I/O (both are ultimately built on the same underlying
Linux syscall operations).  Linux-native Asynchronous I/O (libaio.h and -laio)
is very close to the underlying kernel interfaces, and would have been a better
choice due to its more robust, efficient, scalable notification mechanism.
However, libaio was not available in the mainstream MeeGo release at the time.
The fall-back that is used as of 12-30-2010 is Posix Asynchronous I/O (aio.h
and lrt).  Posting asynchronous I/O blocks is straightforward as described, but
notification posed some problems.  As of this time, one of three mechanisms can
be selected in the code by the setting of an enumeration variable (initialized
at compile-time to COMPLETION_SUSPEND, the best of the three).  The methods and
their evaluation is as follows:

() COMPLETION_SUSPEND:  A thread is created when an endpoint is opened for
   "read".  It normally sits in "aio_suspend()" waiting for the oldest asynch
   I/O block to complete.  When it gets it, it enqueues it, causes the trigger
   to the main-loop thread, and waits on the next-oldest asynch I/O block.
   This works reliably, and costs on the order of a thread-context-switch for
   each delivered read.  I have not had any problem with dropped/missed
   notifications.  Some complexity is involved in bookkeeping the list of I/O
   blocks that are pending and cleaning up the thread and objects it will touch
   when it is time to stop/close endpoints.

() COMPLETION_CALLBACK:  The Asynch I/O control blocks are marked to have a
   particular callback function called in an independent thread context when
   the indicated I/O completes.  This is completely reliable and is the
   simplest to implement and use.  However, the underlying implementation
   spawns an entirely new thread for _every_ callback.  This is a huge and
   undesirable amount of thread creation and destruction.  It also raises the
   possibility that depending on scheduling, notifications all the way to the
   main-loop thread could be delivered out of order.  This may or may not be
   possible or problematic depending on the protocol.

() COMPLETION_SIGNAL:  The Async I/O control blocks are marked to send a
   user-configurable Linux signal when the indicated I/O completes.  Signals
   are handled by a callback in some unspecified thread context.  The cost is
   on the order of a thread-context-switch for each delivered notification.
   There are some limits to what you can do in the "special" signal-handler
   thread context.  Triggering the main-loop thread is ok.  Blocking is not a
   good idea, nor is taking a long time.  Unfortunately this method has a
   couple of big problems.  Signals tend to interrupt system calls, especially
   reads.  Interruption can be detected and the read retried, but this adds
   complexity and it would have to be done throughout the software layers.  The
   worse problem is that signals have an intrinsic queue-depth limit in their
   implementation.  When the limit is hit, signals are dropped (signals do not
   guarantee delivery).  In practice this happens quite often since
   signal-generating events tend to cascade together.  Using this method led to
   chronic hangs due to lost signals and missing I/O completion notification.



-----------------------------------------------------------------------
Running/Testing it:

The kernel you use should be built with "gadgetfs" enabled (this does _not_
seem to be the usual default).  In addition, it is very helpful at least
initially to select the "dummy_hcd" host-controller driver instead of a driver
for a "real" USB device-controller.  The "dummy_hcd" driver presents itself as
both a host-controller interface and a device-controller interface.  Internally
these are looped back and forth to each other.  This lets you test entirely on
a single system without even any (rare) device-controller hardware present.
Subsequent instructions assume dummy_hcd, although only small changes are
needed to use something else.

Although not required, the "libmtp" family of packages is useful for testing.
I had to modify these (as described below) to know about the "idVendor" and
"idProduct" values in its list of MTP devices it knows about.  Include the
following packages (currently available in MeeGo:1.1:Core):

libmtp
libmtp-debuginfo (not strictly required)
libmtp-devel
libmtp-examples (important, contains all the test programs)

The "buteo-mtp" family of packages (with MTPTransporterGadgetfs integrated) can
be built into a MeeGo image or uploaded and installed separately.  Include the
following:

buteo-mtp
buteo-mtp-debuginfo
buteo-mtp-devel
buteo-mtp-tests (important, contains mtp_test: the MTP responder)

One-time preparation of the system after installing buteo-mtp rpms:

() mkdir /home/user
() cp /usr/share/mtp/deviceinfo.xml /home/user/.mtpdeviceinfo.xml
() mkdir /home/user/MyDocs
() cp /usr/lib/mtp/libfsstorage.so /usr/share/mtp

The need for that last is probably a packaging bug (?  library
installed/expected in different places), but it's outside the scope of this
MTP/USB/gadgetfs transport project.  By the way, MyDocs is where your MTP
content lives.

Then each time you boot the system and want to run an mtp responder:

() mkdir /dev/gadget
() mount -t gadgetfs none /dev/gadget
() /usr/bin/mtp_test

By this time MTPTransporterGadgetfs is rugged enough that it usually does not
leave endpoints "in-use" so it can be run again without a re-boot after crash
or killing it.  Do "ls /dev/gadget" to check.  If you see just "dummy_hcd" (or
your chosen driver) you're probably ready to re-run.  If not, try "killall
mtp_test".  If all else fails, reboot and start over from "mkdir /dev/gadget".

Depending on what you included in your system build, there are a couple of
daemons that will recognize your new "device" when you run mtp_test and grab it
to interact with it.  These can be helpful or an annoyance.  If you want to
keep these out of the way, do "ps aux" and then "killall <prog>" where <prog>
is anything that looks like gvfsd or gvfs-gphoto2-<something>.

The "Banshee" media player seems to recognize MTP and try to interact with it.

The best quick-and-dirty go/no-go test is as follows:
() mtp_test
in another window:
() lsusb
You should see a device with the fake device ID I picked: "dead:f00d".
() lsusb -v
The mtp_test window should print a bunch of lines reporting string retrieval.
The output of the command should show an expanded listing for this device ID,
with strings such as "iManufacturer" filled in with text ("Linux Community" in
this case).

A much more extensive test:
() mtp_test
in another window:
() mtp-detect
The mtp_test window shows a huge amount of activity about zillions of commands
executed.  The mtp-detect window shows reams and reams of information about
what options are supported.  Note that this reports some problems, some of
which I believe to be in buteo-mtp, some of which I believe to be in
mtp-detect.

Note that libmtp uses two semi-bogus strategies to recognize MTP devices:
() They have an idVendor:idProduct pair that's in its list, or
() One of the property strings retrieved matches a (patented?) pattern that
   Microsoft was using for MTP before MTP got granted its own class identifier
   by the USB standards body.

They are _not_ using what should be a reliable test:
() Make sure the device class is PTP
() Make sure the "vendor extension ID" is FFFFFFFF, as defined for the MTP
   extension.
Moreover, as of now the buteo-mtp MTP responder uses "6" as the vendor
extension ID, so this wouldn't work anyway.  The result of all this is that the
easiest way to get libmtp's tests (such as mtp-detect) to work with this for
now was to patch it to know about our fake idVendor:idProduct pair.  Briefly,
the relevant part of this patch is:

>From 66f35645c27df84fa8ed9122012b1a5577cfb2d8 Mon Sep 17 00:00:00 2001
From: Paul Drews <paul.dr...@intel.com>
Date: Tue, 28 Dec 2010 11:24:16 -0800
Subject: [PATCH] Add experimental buteo-mtp stack


diff --git a/src/music-players.h b/src/music-players.h
index ce5c4de..3ea0596 100644
--- a/src/music-players.h
+++ b/src/music-players.h
@@ -805,3 +805,8 @@
    * Other strange stuff.
    */
   { "Isabella", 0x0b20, "Her Prototype", 0xddee, DEVICE_FLAG_NONE }
+
+  /*
+   * Experimental USB MTP stack for buteo-mtp
+   */
+  { "Intel", 0xdead, "Buteo-MTP stack", 0xf00d, DEVICE_FLAG_NONE }

>From 5c7c097b5dd1a65555fb268a64c03df404326383 Mon Sep 17 00:00:00 2001
From: Paul Drews <paul.dr...@intel.com>
Date: Tue, 28 Dec 2010 11:27:27 -0800
Subject: [PATCH] Fix comma separaters in list


diff --git a/src/music-players.h b/src/music-players.h
index 3ea0596..7caa8fb 100644
--- a/src/music-players.h
+++ b/src/music-players.h
@@ -804,9 +804,9 @@
   /*
    * Other strange stuff.
    */
-  { "Isabella", 0x0b20, "Her Prototype", 0xddee, DEVICE_FLAG_NONE }
+  { "Isabella", 0x0b20, "Her Prototype", 0xddee, DEVICE_FLAG_NONE },

   /*
    * Experimental USB MTP stack for buteo-mtp
    */
-  { "Intel", 0xdead, "Buteo-MTP stack", 0xf00d, DEVICE_FLAG_NONE }
+  { "Intel", 0xdead, "Buteo-MTP stack", 0xf00d, DEVICE_FLAG_NONE },


WARNING:  DO NOT use this idVendor:idProduct pair.  You have to get real ones
assigned by the USB compliance testing process.

-----------------------------------------------------------------------
Status:

() Working with MeeGo:1.1:Core->buteo-mtp version 0.0.37 as of 12-30-2010

() Not heavily tested but seems rugged enough, alpha-ish quality.

() Not pushed in to build.meego.com at all anywhere yet.  I need to run it past
   the open-source committee and legal first.


-----------------------------------------------------------------------
Known Issues:

()
   It makes zillions of printfs, both from the MTPTransporterGadgetfs and
   higher layers.  Most of the ones in this layer are hard-coded in and can't
   simply be turned off.

() idVendor:idProduct is something I made up.  A real one needs to get assigned
   through the USB compliance community.

() Need to get this through open-source release process and push it upstream.

() According to my reading of the MTP spec, "Vendor Extension ID" should be
   FFFFFFFF to be flagged as an MTP extension of PTP (currently 6).
   MTPResponder issue.

() GetObjectHandles with handle=ffffffff, depth=0 implementation wrong (?)
   should reply with root handle, not ffffffff.  MTPResponder issue.  Also a
   libmtp issue, they should ask for depth=ffffffff if they want all-depth.

() Sending events (e.g., create file in /home/user/MyDocs) caused a hang in
   earlier tests, needs to be re-tested with more-robust async IO completion.

() mtp_test gets a segfault on control-C late in destructor chain, within
   malloc somewhere, probably lurking stack-corruption problem from earlier.
   mtp_test catches SIGINT and tries to shutdown gracefully.

() Responder doesn't support retrieval of device certificate.  This is
   optional, but mtp-detect warns about it.

() All three asynch io completion options are still in the code.  Could clean
   out the bad ones.

() Native linux libaio would be better than (currently used) Posix aio.  Also,
   this would be cleaner to factor into a separate class that wraps around a
   file descriptor.

() Mount location of "/dev/gadget" is hardcoded several places, should be
   factored and configurable.

() Currently only tested with "dummy_hcd" device-controller driver.  Several
   others are included, but untested, and many more were not included for
   expediency.  Need to know the device-controller chip of the real hardware
   and put in support for that, top priority, also retrofit all other known
   device-controllers, bottom priority.

() sendData() semantics of "isLastPacket" need to get clarified and documented.
   Probably this means that, if true, the function should not return until all
   data is delivered, even if doing buffering otherwise.

() sendData() and possibly others, need to establish and meet a design-rule
   regarding handling of zero-length packets.  Preferred:  caller must know if
   a ZLP is required and must call sendData() with 0 bytes, and sendData() must
   really call the underlying write().

() sendDeviceOk(), sendDeviceBusy(), and sendDeviceTxCancelled() are called at
   times that have nothing to do with the protocol, and seriously break stuff
   if allowed to do any I/O.  I turned these into no-ops.  Need at least
   clarification of documentation of what these are for.

() I can't find anywhere in the specifications that says how you choose
   endpoint address numbers.  As far as I can tell, you just make them up
   (except for endpoint 0, which gets 0).

() The definition of cancelTransaction in the MTPTransporter superclass is
   basically flawed since it doesn't carry the cancellation data:
   () 2-byte identifier for cancellation
   () 4-byte PIMA 15740 TransactionID
   The MTPResponder can't possibly cancel the transaction properly without this
   information.

() Why are we doing 64Kb reads from bulk-out endpoint?  Is anything ever going
   to be bigger than a packet?
_______________________________________________
MeeGo-dev mailing list
MeeGo-dev@meego.com
http://lists.meego.com/listinfo/meego-dev

Reply via email to