MTP Responder in a composite USB gadget

Backstory:

I recently worked on an embedded device running Buildroot Linux that would be plugged into another mobile device or computer. For this, I needed to create a composite USB gadget to implement several different functions over the same USB connection. In my configuration, I needed the following functionalities exposed:

  • Serial port
  • Read/Write storage
  • Read only storage

The sore spot for me was the read/write storage interface. This could not be a mass storage interface as writing from the system side and the remote connection side would easily corrupt the media.

I began looking at Android as an example. Android migrated from a separate flash partition and mass storage interface to use Media Transfer Protocol. MTP is a protocol that is implemented in user land as a translation layer when accessing a file system. This meant they no longer had to have a secondary FAT partition and juggle which side has write access.

I found a good amount of info on MTP protocol, but no ready to use implementations. It seems that most big players end up writing their own. There are 3 main open source implementations as noted in this stack overflow post:

  • http://stackoverflow.com/questions/35829070/is-there-an-open-source-mtp-media-tranfer-protocol-responder-implementation-fo

Recently Tizen added their own open source version. I thought I could build off one of the available choices.

Options:

  • ANDROID MTP:
    • https://android.googlesource.com/platform/frameworks/base/+/master/media/jni/
    • Apache 2.0 license.
    • Requires Android configfs MTP gadget module which is not in the Linux kernel.
    • Seems to be very closely tied to Android media code and require many other android libraries that I did not want to merge.
  • TIZEN MTP:
    • https://build.tizen.org/package/show?package=mtp-responder&project=Tizen%3AIVI
    • Apache 2.0 license.
    • Requires GLIB 2.0 and some capi that would inflate our image size by a good bit.
  • BUTEO MTP:
    • Current repo: https://git.merproject.org/mer-core/buteo-mtp/tree/master
    • Repo I used: https://github.com/pcercuei/buteo-mtp (Pcercuei already separated it from buteo-syncfw here, see next bullet)
    • Newer version of buteo is intrinsically tied to buteo-syncfw. buteo-syncfw requires many more platform code packages to be pulled in from the Meego or Mer code base. Since this added code handles config and networking etc, it was not very useful to me.
    • Buteo was originally written for gadgetfs and alot of the documentation still shows this. However, Buteo now uses functionfs gadget module with configfs. This adds to the confusion a bit.
  • UBUNTU MTP:
    • http://bazaar.launchpad.net/~phablet-team/mtp/trunk/files
    • Requires Boost library with certain packages, dbus-cpp, libglog. Boost library will inflate our image size a good bit.
    • Also requires bazaar installed on the dev machine to pull from the Ubuntu repo.
    • Requires Android configfs MTP gadget module which is not in the Linux kernel.
  • uMTP-Responder
    • https://github.com/viveris/uMTP-Responder
    • Thanks to Jean-François DEL NERO for the update on this.
    • Recently released by Viveris under GPLv3. (This is newer than the instructions here)
    • Implemented in C, in user space and use the GadgetFs layer.
    • This is most likely a better option than the instructions below. Give it a try!

Implementation:

Note: There may be a better and more proper Linux way to implement MTP, but in the time frame I have, this was the fastest working way I found with existing code. It seems that good details are hard to find on MTP responders when you don't want to write your own.

I first began looking at buteo since it used functionfs which is already in the kernel. I was able to pull the old version and build it. Running the binary seemed to work, however attaching the gadget configuration to the UDC caused a kernel oops every time.

Ubuntu was the second flavor I tried and ultimately found success. Thank you Android and Canonical phablet team! Here's the steps and files necessary to pull Ubuntu's MTP daemon into a buildroot system:

1. Pull down the kernel patch for the MTP gadget driver that was never accepted into the kernel. Add this to your build.

  1. https://patches.linaro.org/patch/52411/
  2. Download from the patch link
  3. cp RFC-1-2-usb-gadget-configfs-add-MTP-function.patch <your linux patches directory>

2. Select the newly added MTP gadget option in the kernel config

  1. make linux-menuconfig
  2. Device Drivers -> USB Support -> USB Gadget Support -> USB Gadget Drivers -> USB functions configurable through configfs
  3. Select MTP gadget
  4. Also select any others you want to use. I'm also selecting ACM serial and Mass Storage.

3. Save the configuration to your default config (optional)

  1. make linux-savedefconfig
  2. cp output/build/linux-linux4sam_5.3/defconfig <your defconfig location>

4. Setup ubuntu-mtp package in buildroot

  1. cd <path to your package directory>
  2. mkdir ubuntu-mtp
  3. Add Config.in:

config BR2_PACKAGE_UBUNTU_MTP
    bool "ubuntu-mtp"
    select BR2_PACKAGE_BOOST
    select BR2_PACKAGE_BOOST_THREAD
    select BR2_PACKAGE_BOOST_SYSTEM
    select BR2_PACKAGE_BOOST_FILESYSTEM
    select BR2_PACKAGE_BOOST_TEST
    select BR2_PACKAGE_DBUS_CPP
    select BR2_PACKAGE_GLOG

    help
      Media Transfer Protocol (MTP) stack from Ubuntu

      http://bazaar.launchpad.net/~phablet-team/mtp

5. Add ubuntu-mtp.mk. Note that I am using sed to replace tags added in the patch file.

#############################################################
#
# Ubuntu MTP responder
# source http://bazaar.launchpad.net/~phablet-team/mtp/trunk
# Adding as a local package to avoid baazar requirement
#
#############################################################
UBUNTU_MTP_VERSION = 71
UBUNTU_MTP_SITE = file://$(TOPDIR)/Company/package/ubuntu-mtp
UBUNTU_MTP_DEPENDENCIES = boost dbus-cpp
UBUNTU_MTP_LICENSE = GPLv3
UBUNTU_MTP_LICENSE_FILES = COPYING
UBUNTU_MTP_INSTALL_STAGING = yes

define UBUNTU_MTP_SET_PROPERTIES
    $(SED) "s/---MANUFACTURER---/Company Name/" $(@D)/src/MtpServer.cpp
    $(SED) "s/---MODEL---/Product Name/" $(@D)/src/MtpServer.cpp
    $(SED) "s/---SERIAL---/Serial Number/" $(@D)/src/MtpServer.cpp
endef

UBUNTU_MTP_PRE_CONFIGURE_HOOKS += UBUNTU_MTP_SET_PROPERTIES

$(eval $(cmake-package))

6. Add patches to package directory. These patches strip out code that ties it to the ubuntu phablet and hard codes some config options. they also modify some build files to include libraries. Some assembly required here. Note you may want to modify these patch files to expose the proper disk locations. Currently, i'm exposing the /root partition for testing.

  1. 0001-CMake-remove-android-libraries-and-fix-module-names.patch
  2. 0002-remove-android-properties-and-ubuntu-phablet-specific.patch
  3. 0003-convert-android-properties-to-hardcoded-strings.patch

7. Pull down the MTP responder from the ubuntu bzr repo. Note: I saved the tgz to our repo so that bazaar is not a requirement for the build

  1. Download the tarball here: http://bazaar.launchpad.net/~phablet-team/mtp/trunk/revision/71?start_revid=71
  2. Add this to your ubuntu-mtp package directory

8. Package is done, add it to the top level Config.in

  1. vim ../../Config.in

9. Select it in the menuconfig along with the dependencies and hope it builds

  1. make menuconfig
  2. User Provided Options
  3. select ubuntu-mtp

10. After it builds and installs, create the gadget startup script. Simple example shown here. Also, get your own vid/pid ya bum!.

  1. vim /etc/init.d/S99-gadget

#!/bin/sh

CONFIGFS="/sys/kernel/config"
GADGET="$CONFIGFS/usb_gadget"
VID="0x0000"
PID="0x0000"
SERIAL="0123456789"
MANUF="Me"
PRODUCT="Radget"

case "$1" in
    start)
        echo "Creating the USB gadget"

        echo "Loading composite module"
        modprobe libcomposite

        echo "Mounting ConfigFS"
        mount -t configfs none $CONFIGFS
        cd $GADGET
        if [ $? -ne 0 ]; then
            echo "Error setting up configfs"
            exit 1;
        fi

        echo "Creating gadget directory"
        mkdir gadget
        cd gadget
        if [ $? -ne 0 ]; then
            echo "Error creating usb gadget in configfs"
            exit 1;
        fi

        echo "Setting Vendor and Product ID's"
        echo $VID > idVendor
        echo $PID > idProduct

        echo "Setting English strings"
        mkdir strings/0x409
        echo $SERIAL > strings/0x409/serialnumber
        echo $MANUF > strings/0x409/manufacturer
        echo $PRODUCT > strings/0x409/product

        echo "Setting configuration"
        mkdir configs/c.1
        mkdir configs/c.1/strings/0x409
        echo "CDC ACM + MTP + Mass Storage" > configs/c.1/strings/0x409/configuration
        echo 120 > configs/c.1/MaxPower

        echo "Creating ACM interface"
        mkdir functions/acm.GS0
        ln -s functions/acm.GS0 configs/c.1

        echo "Creating MTP interface"
        mkdir functions/mtp.mtp
        ln -s functions/mtp.mtp configs/c.1
        mkdir /dev/mtp
        mount -t functionfs mtp /dev/mtp

        echo "Creating Mass Storage interface"  
        mkdir functions/mass_storage.ms0
        echo "/dev/mmcblk0" > functions/mass_storage.ms0/lun.0/file
        echo "1" > functions/mass_storage.ms0/lun.0/removable
        ln -s functions/mass_storage.ms0 configs/c.1/mass_storage.ms0

        echo "Binding USB Device Controller"
        echo `ls /sys/class/udc` > UDC

        echo "Starting the MTP responder daemon"
        mtp-server &
        ;;
    stop)
        echo "Stopping the USB gadget"

        echo "Killing MTP responder daemon"
        killall mtp-server

        cd $GADGET/gadget

        if [ $? -ne 0 ]; then
            echo "Error: no configfs gadget found" 
            exit 1;
        fi

        echo "Unbinding USB Device Controller"
        echo "" > UDC

        echo "Removing Mass Storage interface"
        rm configs/c.1/mass_storage.ms0
        rmdir functions/mass_storage.ms0

        echo "Removing MTP interface"
        umount /dev/mtp
        rmdir /dev/mtp
        rm configs/c.1/mtp.mtp
        rmdir functions/mtp.mtp

        echo "Removing ACM interface"
        rm configs/c.1/acm.GS0
        rmdir functions/acm.GS0

        echo "Clearing English strings"
        rmdir strings/0x409

        echo "Cleaning up configuration"
        rmdir configs/c.1/strings/0x409
        rmdir configs/c.1

        echo "Removing gadget directory"
        cd $GADGET
        rmdir gadget

        cd /

        echo "Unmounting ConfigFS"
        umount $CONFIGFS
        ;;
esac

11. The gadget should show up now when plugged in. Here's a quick screen cap of the directory structure showing the /root partition that i'm exposing.

Asus RT-AC87U Kernel Module Bug

My internet has been dropping a few times a week lately. I've been chalking it up to Comcast and shaking my fist angrily in the air while resetting the router and modem. Today I took a bit of extra time to drill into it and found something surprising. It seems it was the AC87U's fault this time. 

The Setup.

A kernel module named wred seems to be causing this problem. wred (weighted early random detection) in Linux seems to be a network scheduling module, although I'm not sure if this is a problem inherited from Linux or if Asus has modified this module.

Since there's still no Open-WRT option, I'll just send in feedback for now and hope its fixed in the next update. At least this only seems to happen a few times a week... How lucky can a fella get?

Symptoms:

  • No internet access
  • After disconnecting from the WI-FI, could not reconnect

Router Log Snippet:

May 19 10:48:11 kernel: BUG: scheduling while atomic: wred/893/0xffffff00
May 19 10:48:11 kernel: module: ct_notification bf7d7000 1684
May 19 10:48:11 kernel: module: bw_forward bf7b7000 93892
May 19 10:48:11 kernel: module: IDP bf72b000 529318
May 19 10:48:11 kernel: module: nf_nat_sip bf718000 5031
May 19 10:48:11 kernel: module: nf_conntrack_sip bf70f000 15713
May 19 10:48:11 kernel: module: nf_nat_h323 bf708000 4761
May 19 10:48:11 kernel: module: nf_conntrack_h323 bf6fa000 33807
May 19 10:48:11 kernel: module: nf_nat_rtsp bf6f4000 3202
May 19 10:48:11 kernel: module: nf_conntrack_rtsp bf6ee000 4067
May 19 10:48:11 kernel: module: nf_nat_ftp bf6e8000 1144
May 19 10:48:11 kernel: module: nf_conntrack_ftp bf6e1000 4909
May 19 10:48:11 kernel: module: ip6table_mangle bf6db000 934
May 19 10:48:11 kernel: module: xt_length bf6d5000 769
May 19 10:48:11 kernel: module: cdc_mbim bf6c3000 3129
May 19 10:48:11 kernel: module: qmi_wwan bf6bc000 5780
May 19 10:48:11 kernel: module: cdc_wdm bf6b5000 7248
May 19 10:48:11 kernel: module: cdc_ncm bf6ad000 8750
May 19 10:48:11 kernel: module: rndis_host bf6a6000 4936
May 19 10:48:11 kernel: module: cdc_ether bf6a0000 3187
May 19 10:48:11 kernel: module: asix bf698000 10832
May 19 10:48:11 kernel: module: usbnet bf68f000 11161
May 19 10:48:11 kernel: module: mii bf689000 3367
May 19 10:48:11 kernel: module: usblp bf681000 10317
May 19 10:48:11 kernel: module: ohci_hcd bf677000 17918
May 19 10:48:11 kernel: module: ehci_hcd bf66a000 31565
May 19 10:48:11 kernel: module: xhci_hcd bf657000 50352
May 19 10:48:11 kernel: module: thfsplus bf63a000 82029
May 19 10:48:11 kernel: module: tntfs bf5c0000 443780
May 19 10:48:11 kernel: module: tfat bf58b000 174489
May 19 10:48:11 kernel: module: ext2 bf577000 52768
May 19 10:48:11 kernel: module: ext4 bf536000 221662
May 19 10:48:11 kernel: module: jbd2 bf523000 48989
May 19 10:48:11 kernel: module: crc16 bf51d000 1007
May 19 10:48:11 kernel: module: ext3 bf4fb000 106401
May 19 10:48:11 kernel: module: jbd bf4ea000 42367
May 19 10:48:11 kernel: module: mbcache bf4e3000 4599
May 19 10:48:11 kernel: module: usb_storage bf4d4000 34110
May 19 10:48:11 kernel: module: sg bf4c9000 19823
May 19 10:48:11 kernel: module: sd_mod bf4bd000 21983
May 19 10:48:11 kernel: module: scsi_wait_scan bf4b7000 416
May 19 10:48:11 kernel: module: scsi_mod bf492000 108730
May 19 10:48:11 kernel: module: usbcore bf46f000 101794
May 19 10:48:11 kernel: module: ip6t_LOG bf468000 4494
May 19 10:48:11 kernel: module: ip6table_filter bf462000 750
May 19 10:48:11 kernel: module: jffs2 bf444000 91266
May 19 10:48:11 kernel: module: zlib_deflate bf43a000 19489
May 19 10:48:11 kernel: module: wl bf031000 3978595
May 19 10:48:11 kernel: module: igs bf029000 11887
May 19 10:48:11 kernel: module: emf bf020000 15145
May 19 10:48:11 kernel: module: et bf00a000 61221
May 19 10:48:11 kernel: module: ctf bf000000 20227
May 19 10:48:11 kernel: Modules linked in: ct_notification bw_forward(P) IDP(P) nf_nat_sip nf_conntrack_sip nf_nat_h323 nf_conntrack_h323 nf_nat_rtsp nf_conntrack_rtsp nf_nat_ftp nf_conntrack_ftp ip6table_mangle xt_length cdc_mbim qmi_wwan cdc_wdm cdc_ncm rndis_host cdc_ether asix usbnet mii usblp ohci_hcd ehci_hcd xhci_hcd thfsplus tntfs(P) tfat(P) ext2 ext4 jbd2 crc16 ext3 jbd mbcache usb_storage sg sd_mod scsi_wait_scan scsi_mod usbcore ip6t_LOG ip6table_filter jffs2 zlib_deflate wl(P) igs(P) emf(P)
May 19 10:48:11 kernel: [] (unwind_backtrace+0x0/0xf8) from [] (schedule+0x434/0x75c)
May 19 10:48:11 kernel: [] (schedule+0x434/0x75c) from [] (sys_rt_sigsuspend+0xcc/0x108)
May 19 10:48:11 kernel: [] (sys_rt_sigsuspend+0xcc/0x108) from [] (ret_fast_syscall+0x0/0x30)

 

 

Android Preference Fragment with Long Click Listener

Numero Uno. 

In the great tradition of half-assedness and incompleteness, this post starts where I am. My current predicament. A problem relating to Android preferences and fragments.

Android preference classes are used to add settings to an android app.  These classes are used in place of the typical view when creating the settings user interface.

 

These settings pages allow programmers to easily implement a configuration page without worrying about storage or modification of the underlying settings.

http://developer.android.com/guide/topics/ui/settings.html

Android preference implementations come in 2 major flavors: Activities and Fragments. Activities and Fragments are fundamental to android. A description of these could be a topic in itself. For now, lets shorten it to the following over-simplification:

Activity - An entire displayable page.
Fragment - A reusable building block or sub section of a page.

So what this boils down to is whether you show the preferences over the whole screen or inside another view, such as a tabbed display.

I am currently working to shoe-horn a preference activity from older code into the sleek new form factor of a fragment! This is quite a bit more cumbersome than it first appears. As the preference activity I am modifying is very dynamic.

The most simplistic android preference page looks something like the following:


package com.your.name.android.fragments;

import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceFragment;

public class SettingsFragment extends PreferenceFragment
{
    private static final String TAG = "SettingsFragment";

    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);

        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.preferences);
    }
}

However, I'm looking to do a bit more than this, such as click listeners and long click listeners to add and delete dynamic fields. This was previously done in the settings activity, however the fragment doesn't have quite the same access. 

To enable a long click listener in the settings fragment, we must first get the ListView that is used to display the settings. As luck would have it, there is no direct access. So instead we must find the view that is being used and ensure that it is a ListVew. This can only be done by overriding the onCreateView function and "borrowing" the view. From there we can cast it as a ListView and use it:


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 
{
  View result = super.onCreateView(inflater, container, savedInstanceState);
  if (result != null) 
  {
    View lv = result.findViewById (android.R.id.list);
    if (lv instanceof ListView) 
    {
      ((ListView)lv).setOnItemLongClickListener(this);
    }
    else 
    {
      //The view created is not a list view!
    }
  }
  return result;
}

Last, just override the onItemLongClick listener.


@Override
public boolean onItemLongClick(AdapterView<?> parent, 
                               View view, 
                               int pos, 
                               long id )
{
  ListView listView = (ListView)parent;
  ListAdapter listAdapter = listView.getAdapter();
  Object obj = listAdapter.getItem( pos );

  if ( obj instanceof Preference )
  {
    Preference p = (Preference)obj;
    if ( p.getKey().matches( Main.REPORT ) ) 
    {
      showDeletePrompt( "Delete Report",
      String.format( "Do you want to delete the report '%s'?", p.getTitle() ),
      p.getKey(),
      pos );
      return true;
    }
  }/*if*/

  return false;
}

And Bob is your father's brother.