Friday, December 31, 2010

A.R. Drone USB (Updated)

A word of warning: You should not do anything like the following if you do not know what you do. You may ruin your drone.

As A.R. Drone has an USB OTG Port, it should be possible to use this port for extending the functionality of A.R. Drone.

But sadly Parrot disabled this port and only allows to use it for flashing purpose. Luckily they provide the source code for the driver [1].

So... let's have a closer look to this USB port..

First, Parrot defined the port as device only.

arch/arm/parrot6/mykonos.c, line 109:
static dwc_otg_info_t usb0_info = {
 .ctrl_mode = 2,
 .sof_filter = 7,
 .reset_pin = -1,
 .vbus_detection = 0,
 .fiq_enable = 0,
};

According to dwc_otg driver:
drivers/parrot/usb/dwc_otg/dwc_otg_cil.h
 /**
  * Controller mode
  * 0 - OTG
  * 1 - host only
  * 2 - device only
  */
 int32_t ctrl_mode;
#define DWC_OTG_HOST_DEVICE 0
#define DWC_OTG_HOST_ONLY   1
#define DWC_OTG_DEVICE_ONLY 2

In order to enable the USB host mode, it is required to overwrite/ignore the usb0_info defined in mykonos.c.

This can be achieved by patching dwc_otg_driver.c where the platform data is copied to the local parameter data. This is done by function "dwc_otg_set_specific_param". Also, by default (if not overwritten by platform data) the local parameters are configured as DWC_OTG_HOST_DEVICE. So simply not copying this parameter would prevent the driver from going to DEVICE_ONLY mode.

Thus the driver is modified and the assignment "params->ctrl_mode = info->ctrl_mode" is removed:
drivers/parrot/usb/dwc_otg/dwc_otg_driver.c, line 224:

/**
 * This function is called to set specific configuration parameters
 */
static void 
dwc_otg_set_specific_param
(
dwc_otg_info_t *info,
dwc_otg_core_params_t *params,
int port
)
{
 params->sof_filter = info->sof_filter;
 params->reset_pin = info->reset_pin;
 params->speed = info->speed;
 if (port == 0) {
  //params->ctrl_mode = info->ctrl_mode;
  //params->vbus_detection = info->vbus_detection;
 }
 if (port == 1 && !usb1_disable_fiq) {
  params->fiq_enable = info->fiq_enable;
 }
}

[Update]
As noted by MAPGPS [3], there is still an issue with the driver causing Oops. To fix this issue, it is necessary to change the overcurrent_pin from 89 to -1:

drivers/parrot/usb/dwc_otg/dwc_otg_driver.c, line 135:

.overcurrent_pin = -1, /* default */
[/Update]



After compiling, ftp-ing the module to the drone and calling

# insmod dwc_otg.ko

Oh no! dmesg shows that something gone wrong.. also VBus is still +0 V :-(

[   39.862534] dwc_otg: version 2.70a-parrot 22/03/2009
[   39.862703] dwc_otg dwc_otg.0: dwc_otg_driver_probe(c034d578)
[   39.862736] dwc_otg dwc_otg.0: start=0xc0400000
[   39.862851] dwc_otg dwc_otg.0: base=0xc8e00000
[   39.862883] dwc_otg dwc_otg.0: specific configuration
[   39.862914] dwc_otg dwc_otg.0: dwc_otg_device=0xc7a0b6e0
[   39.873574] DWC_otg: dwc_otg_core_reset() HANG! Soft Reset
 GRSTCTL=80000001
[   39.980233] DWC_otg: dwc_otg_core_reset() HANG! Soft Reset
 GRSTCTL=80000001

Looking at the drivers code, it seems like this HANG! message indicates a communication problem with the PHY. Checking the voltage levels of the PHY, it seems like almost all pins are tri-stated.

Having a look at the PCB, a SMSC USB3317 can be identified. According to its datasheet there is a pin named RESETB. When this pin is set to low the PHY is suspended and all I/O are tri-stated.

By monitoring RESETB it can be observed that RESETB is toggled by program.elf. So dumping all GPIO registers before and after starting program.elf the pin can be found.

And voilá: GPIO_127 is used to enable/disable the PHY. Program.elf sets it to output a high level which then switches a transistor and pulls RESETB to low.

In order to activate the USB as host GPIO_127 needs to be set to input level. This can be done either by ioctl or by the gpio command:

# gpio 127 -d i

After re-inserting dwc_otg.ko, dmesg no longer shows the HANG! message and VBUS has +5V :-)

Let's try to connect an USB device. The pinout for the USB connector is described in [2]. I use an old USB thumb drive (128MB) for testing.

dmesg shows:

[ 1675.054378] usb-storage: USB Mass Storage device detected
[ 1675.054650] usb-storage: -- associate_dev
[ 1675.054680] usb-storage: Vendor: 0x0ea0, Product: 0x2168,
 Revision: 0x0200
[ 1675.054707] usb-storage: Interface Subclass: 0x06, Protocol:
 0x50
[ 1675.054741] usb-storage: Transport: Bulk
[ 1675.054761] usb-storage: Protocol: Transparent SCSI
[ 1675.055439] scsi1 : SCSI emulation for USB Mass Storage devices
[...]
[ 1680.071311] scsi 1:0:0:0: Direct-Access              128MB       
     2.00 PQ: 0 ANSI: 2

Seems to work :-)


[1] https://projects.ardrone.org/documents/show/19
[2] https://projects.ardrone.org/attachments/167/ARDrone-USB-Cable.png
[3] http://www.ardrone-flyers.com/forum/viewtopic.php?p=5823#p5823

Wednesday, December 29, 2010

PLF File Format

A few weeks ago i bought an A.R. Drone from Parrot. This quadcopter is controlled over WLAN and uses an embedded Linux on an ARM chipset.

Altough it is possible via FTP / Telnet to modify the file system, i was intreseted in the flash file format. So I analyzed the available firmware.

I was quite successful athough there are still some unkown fields left.

Fileheader:
typedef struct sPLFFile
{
  u32 dwMagic; 
  u32 dwHdrVersion;
  u32 dwHeaderSize;
  u32 dwEntryHeaderSize;
  u32 uk_0x10;
  u32 uk_0x14;
  u32 uk_0x18;
  u32 uk_0x1C;
  u32 uk_0x20;
  u32 dwVersionMajor;
  u32 dwVersionMinor;
  u32 dwVersionBugfix;
  u32 uk_0x30;
  u32 dwFileSize;
}

The fields have the following meaning:

  • dwMagic: MagicCode of the file. Must be 0x21464C50
  • dwHdrVersion: Version of the header struct. Only seen with 0xB or 0xA
  • dwHeaderSize: Size of the file header. Must be 0x38.
  • dwEntryHeaderSize: Size of the section header. Must be 0x14.
  • uk_0x10: Currently unkown. Maybe some Type. Seen with 0x2
  • uk_0x14: Currently unkown. Maybe an EntryPoint. Seen with 0x0
  • uk_0x18: Currently unkown. Maybe the Device Type. Seen with 0x4
  • uk_0x1C: Currently unkown. Maybe the Model Number. Seen with 0x4E
  • uk_0x20: Currenlty unkown. Seen with 0x20
  • dwVersionMajor: Major version of this firmware
  • dwVersionMinor: Minor version of this firmware
  • dwVersionBugfix: Bugfix version of this firmware
  • uk_0x30: Curently unkown
  • dwFileSize: Size of the file.



After the header the sections follow. Each section starts with the following header.
typedef struct sPLFEntryTag
{
  u32 dwSectionType;
  u32 dwEntrySize;
  u32 dwCRC32;
  u32 uk_0x0C;
  u32 dwUncompressedSize;
}

The fields have the following meaning:
  • dwSectionType: Type of the section.

    0x00: unkown
    0x03: probably the bootloader
    0x07: unkown
    0x09: Filesystem action/content
    0x0b: Configuration Data (Mounting, creation of partitions)
    0x0c: Installer (contains another PLF file)
  • dwEntrySize: Size of the section (excluding header size)
  • dwCRC32: CRC-32 of the content and EntrySize
  • uk_0x0C: Currently unkown. Seems to be the address where to load the section to
  • dwUncompressedSize: If > 0, the content of this entry is compressed with gzip format. The value represents the uncompressed size of the entry.
The meaning of each section depends on dwSectionType.

0x09 - File system content
Each section represents one entry on the file system. It first starts with the null-terminated filename (variable length). After this name, the following header is placed:

typedef struct sFileEntryTag
{
  u32 dwFlags;
  u32 uk_0x04;
  u32 uk_0x08;
} sFileEntry;

  • dwFlags: Flags of the file entry. Encodes the type of the entry and the access rights.

    bit00-11: Rights of the file. Its the same as you would use for chmod. E.g. 0755 -> 0x1ED
    bit12-15: Type of the file entry:
    • 0x04: mkdir. No more data follows after the header.
    • 0x08: write. The file content follows after the header.
    • 0x0A: symlink. The target of the link follows after the header.
  • uk_0x04: Currently unkown. Seems to be either the UID or GID.
  • uk_0x08: Currently unkown. Seems to be either the UID or GID.
0x0b - Configuration data
This section contains informations about mounting, truncating and creating partitions. There is an old and a new version.

Old version (starts with "/mnt"):
32 bytes unkown. Then a u32 with some unkown_count follows. Then unkown_count*8 bytes follows. Then a u32 with the number of entries follows. Afterwards, the entries follow.

New version (does not start with "/mnt"):
typedef struct sPartitionSectionTag
{
  u32 dwTblVersion;
  u32 dwVersionMajor;
  u32 dwVersionMinor;
  u32 dwVersionBugfix;
  u32 uk_0x10;
  u32 uk_0x14;
  u32 uk_0x18;
  u32 uk_0x1C;
  u32 uk_0x20;
  u32 dwNumEntries;
} sPartitionSection;

  • dwTblVersion: Seems to be the version of this "partition table". 
  • dwVersionMajor: Major version
  • dwVersionMinor: Minor version
  • dwVersionBugfix: Bugfix version
  • uk_0x10 - uk_0x20: Unkown
  • dwNumEntries number of partition entries
For both, new and old version, the entries follow directly after the header. Each entries had the following format:

typedef struct sPartitionEntryTag
{
  u16 wDevice;
  u16 wVolumeType;
  u16 wVolume;
  u16 uk_0x06;
  u32 dwVolumeSize;
  u32 wVolumeAction;
  u8  cVolumeName[32];
  u8  cMountName[32];
} sPartitionEntry;

  • wDevice: Device number
  • wVolumeType: Volume type.
    • 0x0: RAW - no file system is available on this device
    • 0x1: STATIC - A filesystem is available but will not change (e.g. for the boot partitions)
    • 0x2: DYNAMIC - A filesystem is availalbe and can be changed 
  • wVolume: Volume number
  • uk_0x06: Unkown
  • dwVolumeSize: Size of the volume. Only to be used for wVolumeAction=2
  • wVolumeAction
    • 0x0: mount. Only for wVolumeType=2! -> mount /dev/ubi__
    • 0x1: truncate and mount -> Truncates the partition and mounts it afterwards
    • 0x2: create partition -> will call ubimkvol

... to be continued ...

Feel free to leave your comments!