Checkpoint work on MTP and PTP investigation.

This change includes work in progress on a C++ library for both host and device
MTP and PTP support.
Currently the makefile builds two test programs:

mtptest - a command line test program that implements a small subset of device side MTP.
Requires a kernel driver that has not been checked in yet.

ptptest - a host tool to test USB host support for detecting and communicating with
digital cameras over PTP.  Runs on Linux host.

Later this will be reformulated as a native library that will be used in the media process.

Change-Id: I81aab279975b600b59d99013ab97f9adf0b58da7
Signed-off-by: Mike Lockwood <lockwood@android.com>
This commit is contained in:
Mike Lockwood
2010-05-11 17:16:59 -04:00
parent f6860f1979
commit 56118b5b38
31 changed files with 3656 additions and 0 deletions

69
media/mtp/Android.mk Normal file
View File

@@ -0,0 +1,69 @@
#
# Copyright (C) 2010 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
mtptest.cpp \
MtpDatabase.cpp \
MtpDataPacket.cpp \
MtpDebug.cpp \
MtpPacket.cpp \
MtpRequestPacket.cpp \
MtpResponsePacket.cpp \
MtpServer.cpp \
MtpStringBuffer.cpp \
MtpStorage.cpp \
MtpUtils.cpp \
SqliteDatabase.cpp \
SqliteStatement.cpp \
LOCAL_MODULE:= mtptest
LOCAL_C_INCLUDES := external/sqlite/dist
LOCAL_CFLAGS := -DMTP_DEVICE
LOCAL_SHARED_LIBRARIES := libutils libsqlite
include $(BUILD_EXECUTABLE)
ifeq ($(HOST_OS),linux)
include $(CLEAR_VARS)
LOCAL_MODULE := ptptest
LOCAL_SRC_FILES:= \
ptptest.cpp \
MtpClient.cpp \
MtpDataPacket.cpp \
MtpDebug.cpp \
MtpPacket.cpp \
MtpRequestPacket.cpp \
MtpResponsePacket.cpp \
MtpStringBuffer.cpp \
LOCAL_STATIC_LIBRARIES := libusbhost
LOCAL_LDLIBS := -lpthread
LOCAL_CFLAGS := -g -DMTP_HOST
LOCAL_LDFLAGS := -g
include $(BUILD_HOST_EXECUTABLE)
endif

136
media/mtp/MtpClient.cpp Normal file
View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <usbhost/usbhost.h>
#include "MtpClient.h"
#include "MtpDebug.h"
#include "MtpStringBuffer.h"
MtpClient::MtpClient(struct usb_endpoint *ep_in, struct usb_endpoint *ep_out,
struct usb_endpoint *ep_intr)
: mEndpointIn(ep_in),
mEndpointOut(ep_out),
mEndpointIntr(ep_intr),
mSessionID(0),
mTransactionID(0)
{
}
MtpClient::~MtpClient() {
}
bool MtpClient::openSession() {
printf("openSession\n");
mSessionID = 0;
mTransactionID = 0;
MtpSessionID newSession = 1;
mRequest.reset();
mRequest.setParameter(1, newSession);
if (!sendRequest(MTP_OPERATION_OPEN_SESSION))
return false;
MtpResponseCode ret = readResponse();
if (ret == MTP_RESPONSE_SESSION_ALREADY_OPEN)
newSession = mResponse.getParameter(1);
else if (ret != MTP_RESPONSE_OK)
return false;
mSessionID = newSession;
mTransactionID = 1;
return true;
}
bool MtpClient::getDeviceInfo() {
mRequest.reset();
if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO))
return false;
if (!readData())
return false;
MtpResponseCode ret = readResponse();
if (ret == MTP_RESPONSE_OK) {
MtpStringBuffer string;
// fill in device info
printf("MTP standard version: %d\n", mData.getUInt16());
printf("MTP Vendor Extension ID: %d\n", mData.getUInt32());
printf("MTP vendor extension version: %d\n", mData.getUInt16());
mData.getString(string);
printf("vendor extension desc %s\n", (const char *)string);
return true;
}
return false;
}
bool MtpClient::closeSession() {
return true;
}
bool MtpClient::sendRequest(MtpOperationCode operation) {
printf("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
mRequest.setOperationCode(operation);
if (mTransactionID > 0)
mRequest.setTransactionID(mTransactionID++);
int ret = mRequest.write(mEndpointOut);
mRequest.dump();
return (ret > 0);
}
bool MtpClient::sendData(MtpOperationCode operation) {
printf("sendData\n");
mData.setOperationCode(mRequest.getOperationCode());
mData.setTransactionID(mRequest.getTransactionID());
int ret = mData.write(mEndpointOut);
mData.dump();
return (ret > 0);
}
bool MtpClient::readData() {
int ret = mData.read(mEndpointIn);
printf("readData returned %d\n", ret);
if (ret >= MTP_CONTAINER_HEADER_SIZE) {
mData.dump();
return true;
}
else {
printf("readResponse failed\n");
return false;
}
}
MtpResponseCode MtpClient::readResponse() {
printf("readResponse\n");
int ret = mResponse.read(mEndpointIn);
if (ret >= MTP_CONTAINER_HEADER_SIZE) {
mResponse.dump();
return mResponse.getResponseCode();
}
else {
printf("readResponse failed\n");
return -1;
}
}

59
media/mtp/MtpClient.h Normal file
View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _MTP_CLIENT_H
#define _MTP_CLIENT_H
#include "MtpRequestPacket.h"
#include "MtpDataPacket.h"
#include "MtpResponsePacket.h"
#include "mtp.h"
#include "MtpUtils.h"
class MtpClient {
private:
struct usb_endpoint *mEndpointIn;
struct usb_endpoint *mEndpointOut;
struct usb_endpoint *mEndpointIntr;
// current session ID
MtpSessionID mSessionID;
// current transaction ID
MtpTransactionID mTransactionID;
MtpRequestPacket mRequest;
MtpDataPacket mData;
MtpResponsePacket mResponse;
public:
MtpClient(struct usb_endpoint *ep_in, struct usb_endpoint *ep_out,
struct usb_endpoint *ep_intr);
virtual ~MtpClient();
bool openSession();
bool getDeviceInfo();
bool closeSession();
private:
bool sendRequest(MtpOperationCode operation);
bool sendData(MtpOperationCode operation);
bool readData();
MtpResponseCode readResponse();
};
#endif // _MTP_CLIENT_H

296
media/mtp/MtpDataPacket.cpp Normal file
View File

@@ -0,0 +1,296 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include "MtpDataPacket.h"
#include "MtpStringBuffer.h"
MtpDataPacket::MtpDataPacket()
: MtpPacket(512),
mOffset(MTP_CONTAINER_HEADER_SIZE)
{
}
MtpDataPacket::~MtpDataPacket() {
}
void MtpDataPacket::reset() {
MtpPacket::reset();
mOffset = MTP_CONTAINER_HEADER_SIZE;
}
void MtpDataPacket::setOperationCode(MtpOperationCode code) {
MtpPacket::putUInt16(MTP_CONTAINER_CODE_OFFSET, code);
}
void MtpDataPacket::setTransactionID(MtpTransactionID id) {
MtpPacket::putUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET, id);
}
uint16_t MtpDataPacket::getUInt16() {
int offset = mOffset;
uint16_t result = (uint16_t)mBuffer[offset] | ((uint16_t)mBuffer[offset + 1] << 8);
mOffset += 2;
return result;
}
uint32_t MtpDataPacket::getUInt32() {
int offset = mOffset;
uint32_t result = (uint32_t)mBuffer[offset] | ((uint32_t)mBuffer[offset + 1] << 8) |
((uint32_t)mBuffer[offset + 2] << 16) | ((uint32_t)mBuffer[offset + 3] << 24);
mOffset += 4;
return result;
}
uint64_t MtpDataPacket::getUInt64() {
int offset = mOffset;
uint64_t result = (uint64_t)mBuffer[offset] | ((uint64_t)mBuffer[offset + 1] << 8) |
((uint64_t)mBuffer[offset + 2] << 16) | ((uint64_t)mBuffer[offset + 3] << 24) |
((uint64_t)mBuffer[offset + 4] << 32) | ((uint64_t)mBuffer[offset + 5] << 40) |
((uint64_t)mBuffer[offset + 6] << 48) | ((uint64_t)mBuffer[offset + 7] << 56);
mOffset += 8;
return result;
}
void MtpDataPacket::getString(MtpStringBuffer& string)
{
string.readFromPacket(this);
}
void MtpDataPacket::putInt8(int8_t value) {
allocate(mOffset + 1);
mBuffer[mOffset++] = (uint8_t)value;
if (mPacketSize < mOffset)
mPacketSize = mOffset;
}
void MtpDataPacket::putUInt8(uint8_t value) {
allocate(mOffset + 1);
mBuffer[mOffset++] = (uint8_t)value;
if (mPacketSize < mOffset)
mPacketSize = mOffset;
}
void MtpDataPacket::putInt16(int16_t value) {
allocate(mOffset + 2);
mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
if (mPacketSize < mOffset)
mPacketSize = mOffset;
}
void MtpDataPacket::putUInt16(uint16_t value) {
allocate(mOffset + 2);
mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
if (mPacketSize < mOffset)
mPacketSize = mOffset;
}
void MtpDataPacket::putInt32(int32_t value) {
allocate(mOffset + 4);
mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF);
if (mPacketSize < mOffset)
mPacketSize = mOffset;
}
void MtpDataPacket::putUInt32(uint32_t value) {
allocate(mOffset + 4);
mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF);
if (mPacketSize < mOffset)
mPacketSize = mOffset;
}
void MtpDataPacket::putInt64(int64_t value) {
allocate(mOffset + 8);
mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 32) & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 40) & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 48) & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 56) & 0xFF);
if (mPacketSize < mOffset)
mPacketSize = mOffset;
}
void MtpDataPacket::putUInt64(uint64_t value) {
allocate(mOffset + 8);
mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 32) & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 40) & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 48) & 0xFF);
mBuffer[mOffset++] = (uint8_t)((value >> 56) & 0xFF);
if (mPacketSize < mOffset)
mPacketSize = mOffset;
}
void MtpDataPacket::putAInt8(const int8_t* values, int count) {
putUInt32(count);
for (int i = 0; i < count; i++)
putInt8(*values++);
}
void MtpDataPacket::putAUInt8(const uint8_t* values, int count) {
putUInt32(count);
for (int i = 0; i < count; i++)
putUInt8(*values++);
}
void MtpDataPacket::putAInt16(const int16_t* values, int count) {
putUInt32(count);
for (int i = 0; i < count; i++)
putInt16(*values++);
}
void MtpDataPacket::putAUInt16(const uint16_t* values, int count) {
putUInt32(count);
for (int i = 0; i < count; i++)
putUInt16(*values++);
}
void MtpDataPacket::putAInt32(const int32_t* values, int count) {
putUInt32(count);
for (int i = 0; i < count; i++)
putInt32(*values++);
}
void MtpDataPacket::putAUInt32(const uint32_t* values, int count) {
putUInt32(count);
for (int i = 0; i < count; i++)
putUInt32(*values++);
}
void MtpDataPacket::putAUInt32(const UInt32List* list) {
if (!list) {
putEmptyArray();
} else {
size_t size = list->size();
putUInt32(size);
for (size_t i = 0; i < size; i++)
putUInt32((*list)[i]);
}
}
void MtpDataPacket::putAInt64(const int64_t* values, int count) {
putUInt32(count);
for (int i = 0; i < count; i++)
putInt64(*values++);
}
void MtpDataPacket::putAUInt64(const uint64_t* values, int count) {
putUInt32(count);
for (int i = 0; i < count; i++)
putUInt64(*values++);
}
void MtpDataPacket::putString(const MtpStringBuffer& string)
{
string.writeToPacket(this);
}
void MtpDataPacket::putString(const char* s)
{
MtpStringBuffer string(s);
string.writeToPacket(this);
}
#ifdef MTP_DEVICE
int MtpDataPacket::read(int fd) {
// first read the header
int ret = ::read(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE);
printf("MtpDataPacket::read 1 returned %d\n", ret);
if (ret != MTP_CONTAINER_HEADER_SIZE)
return -1;
// then the following data
int total = MtpPacket::getUInt32(MTP_CONTAINER_LENGTH_OFFSET);
int remaining = total - MTP_CONTAINER_HEADER_SIZE;
printf("total: %d, remaining: %d\n", total, remaining);
ret = ::read(fd, &mBuffer[0] + MTP_CONTAINER_HEADER_SIZE, remaining);
printf("MtpDataPacket::read 2 returned %d\n", ret);
if (ret != remaining)
return -1;
mPacketSize = total;
mOffset = MTP_CONTAINER_HEADER_SIZE;
return total;
}
int MtpDataPacket::readDataHeader(int fd) {
int ret = ::read(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE);
if (ret > 0)
mPacketSize = ret;
else
mPacketSize = 0;
return ret;
}
int MtpDataPacket::write(int fd) {
MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
// send header separately from data
int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE);
if (ret == MTP_CONTAINER_HEADER_SIZE)
ret = ::write(fd, mBuffer + MTP_CONTAINER_HEADER_SIZE,
mPacketSize - MTP_CONTAINER_HEADER_SIZE);
return (ret < 0 ? ret : 0);
}
int MtpDataPacket::writeDataHeader(int fd, uint32_t length) {
MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length);
MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
int ret = ::write(fd, mBuffer, MTP_CONTAINER_HEADER_SIZE);
return (ret < 0 ? ret : 0);
}
#endif // MTP_DEVICE
#ifdef MTP_HOST
int MtpDataPacket::read(struct usb_endpoint *ep) {
// first read the header
int ret = transfer(ep, mBuffer, mBufferSize);
printf("MtpDataPacket::transfer returned %d\n", ret);
if (ret >= 0)
mPacketSize = ret;
return ret;
}
int MtpDataPacket::write(struct usb_endpoint *ep) {
MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
// send header separately from data
int ret = transfer(ep, mBuffer, MTP_CONTAINER_HEADER_SIZE);
if (ret == MTP_CONTAINER_HEADER_SIZE)
ret = transfer(ep, mBuffer + MTP_CONTAINER_HEADER_SIZE,
mPacketSize - MTP_CONTAINER_HEADER_SIZE);
return (ret < 0 ? ret : 0);
}
#endif // MTP_HOST

89
media/mtp/MtpDataPacket.h Normal file
View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _MTP_DATA_PACKET_H
#define _MTP_DATA_PACKET_H
#include "MtpPacket.h"
#include "mtp.h"
class MtpDataPacket : public MtpPacket {
private:
// current offset for get/put methods
int mOffset;
public:
MtpDataPacket();
virtual ~MtpDataPacket();
virtual void reset();
void setOperationCode(MtpOperationCode code);
void setTransactionID(MtpTransactionID id);
inline uint8_t getUInt8() { return (uint8_t)mBuffer[mOffset++]; }
inline int8_t getInt8() { return (int8_t)mBuffer[mOffset++]; }
uint16_t getUInt16();
inline int16_t getInt16() { return (int16_t)getUInt16(); }
uint32_t getUInt32();
inline int32_t getInt32() { return (int32_t)getUInt32(); }
uint64_t getUInt64();
inline int64_t getInt64() { return (int64_t)getUInt64(); }
void getString(MtpStringBuffer& string);
void putInt8(int8_t value);
void putUInt8(uint8_t value);
void putInt16(int16_t value);
void putUInt16(uint16_t value);
void putInt32(int32_t value);
void putUInt32(uint32_t value);
void putInt64(int64_t value);
void putUInt64(uint64_t value);
void putAInt8(const int8_t* values, int count);
void putAUInt8(const uint8_t* values, int count);
void putAInt16(const int16_t* values, int count);
void putAUInt16(const uint16_t* values, int count);
void putAInt32(const int32_t* values, int count);
void putAUInt32(const uint32_t* values, int count);
void putAUInt32(const UInt32List* list);
void putAInt64(const int64_t* values, int count);
void putAUInt64(const uint64_t* values, int count);
void putString(const MtpStringBuffer& string);
void putString(const char* string);
inline void putEmptyString() { putUInt16(0); }
inline void putEmptyArray() { putUInt32(0); }
#ifdef MTP_DEVICE
// fill our buffer with data from the given file descriptor
int read(int fd);
int readDataHeader(int fd);
// write our data to the given file descriptor
int write(int fd);
int writeDataHeader(int fd, uint32_t length);
#endif
#ifdef MTP_HOST
int read(struct usb_endpoint *ep);
int write(struct usb_endpoint *ep);
#endif
inline bool hasData() const { return mPacketSize > MTP_CONTAINER_HEADER_SIZE; }
};
#endif // _MTP_DATA_PACKET_H

386
media/mtp/MtpDatabase.cpp Normal file
View File

@@ -0,0 +1,386 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "MtpDatabase.h"
#include "MtpDataPacket.h"
#include "SqliteDatabase.h"
#include "SqliteStatement.h"
#include <stdio.h>
#include <sqlite3.h>
#define ID_COLUMN 1
#define PATH_COLUMN 2
#define FORMAT_COLUMN 3
#define PARENT_COLUMN 4
#define STORAGE_COLUMN 5
#define SIZE_COLUMN 6
#define CREATED_COLUMN 7
#define MODIFIED_COLUMN 8
#define TABLE_CREATE "CREATE TABLE IF NOT EXISTS files (" \
"_id INTEGER PRIMARY KEY," \
"path TEXT," \
"format INTEGER," \
"parent INTEGER," \
"storage INTEGER," \
"size INTEGER," \
"date_created INTEGER," \
"date_modified INTEGER" \
");"
#define PATH_INDEX_CREATE "CREATE INDEX IF NOT EXISTS path_index on files(path);"
#define FILE_ID_QUERY "SELECT _id FROM files WHERE path = ?;"
#define FILE_PATH_QUERY "SELECT path,size FROM files WHERE _id = ?"
#define GET_OBJECT_INFO_QUERY "SELECT storage,format,parent,path,size,date_created,date_modified FROM files WHERE _id = ?;"
#define FILE_INSERT "INSERT INTO files VALUES(?,?,?,?,?,?,?,?);"
#define FILE_DELETE "DELETE FROM files WHERE path = ?;"
struct PropertyTableEntry {
MtpObjectProperty property;
int type;
const char* columnName;
};
static const PropertyTableEntry kPropertyTable[] = {
{ MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32, "parent" },
{ MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32, "storage" },
{ MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT32, "format" },
{ MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR, "path" },
{ MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64, "size" },
{ MTP_PROPERTY_DATE_CREATED, MTP_TYPE_STR, "date_created" },
{ MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR, "date_modified" },
};
static bool getPropertyInfo(MtpObjectProperty property, int& type, const char*& columnName) {
int count = sizeof(kPropertyTable) / sizeof(kPropertyTable[0]);
const PropertyTableEntry* entry = kPropertyTable;
for (int i = 0; i < count; i++, entry++) {
if (entry->property == property) {
type = entry->type;
columnName = entry->columnName;
return true;
}
}
return false;
}
MtpDatabase::MtpDatabase()
: mFileIdQuery(NULL),
mObjectInfoQuery(NULL),
mFileInserter(NULL),
mFileDeleter(NULL)
{
}
MtpDatabase::~MtpDatabase() {
}
bool MtpDatabase::open(const char* path, bool create) {
if (!SqliteDatabase::open(path, create))
return false;
// create the table if necessary
if (!exec(TABLE_CREATE)) {
fprintf(stderr, "could not create table\n");
return false;
}
if (!exec(PATH_INDEX_CREATE)) {
fprintf(stderr, "could not path index\n");
return false;
}
return true;
}
MtpObjectHandle MtpDatabase::addFile(const char* path,
MtpObjectFormat format,
MtpObjectHandle parent,
MtpStorageID storage,
uint64_t size,
time_t created,
time_t modified) {
// first check to see if the file exists
if (mFileIdQuery)
mFileIdQuery->reset();
else {
mFileIdQuery = new SqliteStatement(this);
if (!mFileIdQuery->prepare(FILE_ID_QUERY)) {
fprintf(stderr, "could not compile FILE_ID_QUERY\n");
delete mFileIdQuery;
mFileIdQuery = NULL;
return kInvalidObjectHandle;
}
}
mFileIdQuery->bind(1, path);
if (mFileIdQuery->step()) {
int row = mFileIdQuery->getColumnInt(0);
if (row > 0)
return row;
}
if (!mFileInserter) {
mFileInserter = new SqliteStatement(this);
if (!mFileInserter->prepare(FILE_INSERT)) {
fprintf(stderr, "could not compile FILE_INSERT\n");
delete mFileInserter;
mFileInserter = NULL;
return kInvalidObjectHandle;
}
}
mFileInserter->bind(PATH_COLUMN, path);
mFileInserter->bind(FORMAT_COLUMN, format);
mFileInserter->bind(PARENT_COLUMN, parent);
mFileInserter->bind(STORAGE_COLUMN, storage);
mFileInserter->bind(SIZE_COLUMN, size);
mFileInserter->bind(CREATED_COLUMN, created);
mFileInserter->bind(MODIFIED_COLUMN, modified);
mFileInserter->step();
mFileInserter->reset();
int row = lastInsertedRow();
return (row > 0 ? row : kInvalidObjectHandle);
}
MtpObjectHandleList* MtpDatabase::getObjectList(MtpStorageID storageID,
MtpObjectFormat format,
MtpObjectHandle parent) {
bool whereStorage = (storageID != 0xFFFFFFFF);
bool whereFormat = (format != 0);
bool whereParent = (parent != 0);
char intBuffer[20];
MtpString query("SELECT _id FROM files");
if (whereStorage || whereFormat || whereParent)
query += " WHERE";
if (whereStorage) {
snprintf(intBuffer, sizeof(intBuffer), "%d", storageID);
query += " storage = ";
query += intBuffer;
}
if (whereFormat) {
snprintf(intBuffer, sizeof(intBuffer), "%d", format);
if (whereStorage)
query += " AND";
query += " format = ";
query += intBuffer;
}
if (whereParent) {
snprintf(intBuffer, sizeof(intBuffer), "%d", parent);
if (whereStorage || whereFormat)
query += " AND";
query += " parent = ";
query += intBuffer;
}
query += ";";
SqliteStatement stmt(this);
printf("%s\n", (const char *)query);
stmt.prepare(query);
MtpObjectHandleList* list = new MtpObjectHandleList();
while (!stmt.isDone()) {
if (stmt.step()) {
int index = stmt.getColumnInt(0);
printf("stmt.getColumnInt returned %d\n", index);
if (index > 0)
list->push(index);
}
}
printf("list size: %d\n", list->size());
return list;
}
MtpResponseCode MtpDatabase::getObjectProperty(MtpObjectHandle handle,
MtpObjectProperty property,
MtpDataPacket& packet) {
int type;
const char* columnName;
char intBuffer[20];
if (!getPropertyInfo(property, type, columnName))
return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE;
snprintf(intBuffer, sizeof(intBuffer), "%d", handle);
MtpString query("SELECT ");
query += columnName;
query += " FROM files WHERE _id = ";
query += intBuffer;
query += ";";
SqliteStatement stmt(this);
printf("%s\n", (const char *)query);
stmt.prepare(query);
if (!stmt.step())
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
switch (type) {
case MTP_TYPE_INT8:
packet.putInt8(stmt.getColumnInt(0));
break;
case MTP_TYPE_UINT8:
packet.putUInt8(stmt.getColumnInt(0));
break;
case MTP_TYPE_INT16:
packet.putInt16(stmt.getColumnInt(0));
break;
case MTP_TYPE_UINT16:
packet.putUInt16(stmt.getColumnInt(0));
break;
case MTP_TYPE_INT32:
packet.putInt32(stmt.getColumnInt(0));
break;
case MTP_TYPE_UINT32:
packet.putUInt32(stmt.getColumnInt(0));
break;
case MTP_TYPE_INT64:
packet.putInt64(stmt.getColumnInt64(0));
break;
case MTP_TYPE_UINT64:
packet.putUInt64(stmt.getColumnInt64(0));
break;
case MTP_TYPE_STR:
packet.putString(stmt.getColumnString(0));
break;
default:
fprintf(stderr, "unsupported object type\n");
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
}
return MTP_RESPONSE_OK;
}
MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle,
MtpDataPacket& packet) {
char date[20];
if (mObjectInfoQuery)
mObjectInfoQuery->reset();
else {
mObjectInfoQuery = new SqliteStatement(this);
if (!mObjectInfoQuery->prepare(GET_OBJECT_INFO_QUERY)) {
fprintf(stderr, "could not compile FILE_ID_QUERY\n");
delete mObjectInfoQuery;
mObjectInfoQuery = NULL;
return MTP_RESPONSE_GENERAL_ERROR;
}
}
mObjectInfoQuery->bind(1, handle);
if (!mObjectInfoQuery->step())
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
MtpStorageID storageID = mObjectInfoQuery->getColumnInt(0);
MtpObjectFormat format = mObjectInfoQuery->getColumnInt(1);
MtpObjectHandle parent = mObjectInfoQuery->getColumnInt(2);
// extract name from path. do we want a separate database entry for this?
const char* name = mObjectInfoQuery->getColumnString(3);
const char* lastSlash = strrchr(name, '/');
if (lastSlash)
name = lastSlash + 1;
int64_t size = mObjectInfoQuery->getColumnInt64(4);
time_t created = mObjectInfoQuery->getColumnInt(5);
time_t modified = mObjectInfoQuery->getColumnInt(6);
int associationType = (format == MTP_FORMAT_ASSOCIATION ?
MTP_ASSOCIATION_TYPE_GENERIC_FOLDER :
MTP_ASSOCIATION_TYPE_UNDEFINED);
printf("storageID: %d, format: %d, parent: %d\n", storageID, format, parent);
packet.putUInt32(storageID);
packet.putUInt16(format);
packet.putUInt16(0); // protection status
packet.putUInt32((size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size));
packet.putUInt16(0); // thumb format
packet.putUInt32(0); // thumb compressed size
packet.putUInt32(0); // thumb pix width
packet.putUInt32(0); // thumb pix height
packet.putUInt32(0); // image pix width
packet.putUInt32(0); // image pix height
packet.putUInt32(0); // image bit depth
packet.putUInt32(parent);
packet.putUInt16(associationType);
packet.putUInt32(0); // association desc
packet.putUInt32(0); // sequence number
packet.putString(name); // file name
formatDateTime(created, date, sizeof(date));
packet.putString(date); // date created
formatDateTime(modified, date, sizeof(date));
packet.putString(date); // date modified
packet.putEmptyString(); // keywords
return MTP_RESPONSE_OK;
}
bool MtpDatabase::getObjectFilePath(MtpObjectHandle handle,
MtpString& filePath,
int64_t& fileLength) {
if (mFilePathQuery)
mFilePathQuery->reset();
else {
mFilePathQuery = new SqliteStatement(this);
if (!mFilePathQuery->prepare(FILE_PATH_QUERY)) {
fprintf(stderr, "could not compile FILE_ID_QUERY\n");
delete mFilePathQuery;
mFilePathQuery = NULL;
return kInvalidObjectHandle;
}
}
mFilePathQuery->bind(1, handle);
if (!mFilePathQuery->step())
return false;
const char* path = mFilePathQuery->getColumnString(0);
if (!path)
return false;
filePath = path;
fileLength = mFilePathQuery->getColumnInt64(1);
return true;
}
bool MtpDatabase::deleteFile(MtpObjectHandle handle) {
if (!mFileDeleter) {
mFileDeleter = new SqliteStatement(this);
if (!mFileDeleter->prepare(FILE_DELETE)) {
fprintf(stderr, "could not compile FILE_DELETE\n");
delete mFileDeleter;
mFileDeleter = NULL;
return false;
}
}
printf("deleteFile %d\n", handle);
mFileDeleter->bind(1, handle);
mFileDeleter->step();
mFileDeleter->reset();
return true;
}
/*
for getObjectPropDesc
packet.putUInt16(property);
packet.putUInt16(dataType);
packet.putUInt8(getSet);
// default value DTS
packet.putUInt32(groupCode);
packet.putUInt8(formFlag);
// form, variable
*/

65
media/mtp/MtpDatabase.h Normal file
View File

@@ -0,0 +1,65 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _MTP_DATABASE_H
#define _MTP_DATABASE_H
#include "MtpUtils.h"
#include "SqliteDatabase.h"
#include "mtp.h"
class MtpDataPacket;
class SqliteStatement;
class MtpDatabase : public SqliteDatabase {
private:
SqliteStatement* mFileIdQuery;
SqliteStatement* mFilePathQuery;
SqliteStatement* mObjectInfoQuery;
SqliteStatement* mFileInserter;
SqliteStatement* mFileDeleter;
public:
MtpDatabase();
virtual ~MtpDatabase();
bool open(const char* path, bool create);
MtpObjectHandle addFile(const char* path,
MtpObjectFormat format,
MtpObjectHandle parent,
MtpStorageID storage,
uint64_t size,
time_t created,
time_t modified);
MtpObjectHandleList* getObjectList(MtpStorageID storageID,
MtpObjectFormat format,
MtpObjectHandle parent);
MtpResponseCode getObjectProperty(MtpObjectHandle handle,
MtpObjectProperty property,
MtpDataPacket& packet);
MtpResponseCode getObjectInfo(MtpObjectHandle handle,
MtpDataPacket& packet);
bool getObjectFilePath(MtpObjectHandle handle,
MtpString& filePath,
int64_t& fileLength);
bool deleteFile(MtpObjectHandle handle);
};
#endif // _MTP_DATABASE_H

73
media/mtp/MtpDebug.cpp Normal file
View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "MtpDebug.h"
struct OperationCodeEntry {
const char* name;
MtpOperationCode code;
};
static const OperationCodeEntry sOperationCodes[] = {
{ "MTP_OPERATION_GET_DEVICE_INFO", 0x1001 },
{ "MTP_OPERATION_OPEN_SESSION", 0x1002 },
{ "MTP_OPERATION_CLOSE_SESSION", 0x1003 },
{ "MTP_OPERATION_GET_STORAGE_IDS", 0x1004 },
{ "MTP_OPERATION_GET_STORAGE_INFO", 0x1005 },
{ "MTP_OPERATION_GET_NUM_OBJECTS", 0x1006 },
{ "MTP_OPERATION_GET_OBJECT_HANDLES", 0x1007 },
{ "MTP_OPERATION_GET_OBJECT_INFO", 0x1008 },
{ "MTP_OPERATION_GET_OBJECT", 0x1009 },
{ "MTP_OPERATION_GET_THUMB", 0x100A },
{ "MTP_OPERATION_DELETE_OBJECT", 0x100B },
{ "MTP_OPERATION_SEND_OBJECT_INFO", 0x100C },
{ "MTP_OPERATION_SEND_OBJECT", 0x100D },
{ "MTP_OPERATION_INITIATE_CAPTURE", 0x100E },
{ "MTP_OPERATION_FORMAT_STORE", 0x100F },
{ "MTP_OPERATION_RESET_DEVICE", 0x1010 },
{ "MTP_OPERATION_SELF_TEST", 0x1011 },
{ "MTP_OPERATION_SET_OBJECT_PROTECTION", 0x1012 },
{ "MTP_OPERATION_POWER_DOWN", 0x1013 },
{ "MTP_OPERATION_GET_DEVICE_PROP_DESC", 0x1014 },
{ "MTP_OPERATION_GET_DEVICE_PROP_VALUE", 0x1015 },
{ "MTP_OPERATION_SET_DEVICE_PROP_VALUE", 0x1016 },
{ "MTP_OPERATION_RESET_DEVICE_PROP_VALUE", 0x1017 },
{ "MTP_OPERATION_TERMINATE_OPEN_CAPTURE", 0x1018 },
{ "MTP_OPERATION_MOVE_OBJECT", 0x1019 },
{ "MTP_OPERATION_COPY_OBJECT", 0x101A },
{ "MTP_OPERATION_GET_PARTIAL_OBJECT", 0x101B },
{ "MTP_OPERATION_INITIATE_OPEN_CAPTURE", 0x101C },
{ "MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED", 0x9801 },
{ "MTP_OPERATION_GET_OBJECT_PROP_DESC", 0x9802 },
{ "MTP_OPERATION_GET_OBJECT_PROP_VALUE", 0x9803 },
{ "MTP_OPERATION_SET_OBJECT_PROP_VALUE", 0x9804 },
{ "MTP_OPERATION_GET_OBJECT_REFERENCES", 0x9810 },
{ "MTP_OPERATION_SET_OBJECT_REFERENCES", 0x9811 },
{ "MTP_OPERATION_SKIP", 0x9820 },
{ 0, 0 },
};
const char* MtpDebug::getOperationCodeName(MtpOperationCode code) {
const OperationCodeEntry* entry = sOperationCodes;
while (entry->name) {
if (entry->code == code)
return entry->name;
entry++;
}
return "*** UNKNOWN OPERATION ***";
}

27
media/mtp/MtpDebug.h Normal file
View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _MTP_DEBUG_H
#define _MTP_DEBUG_H
#include "mtp.h"
class MtpDebug {
public:
static const char* getOperationCodeName(MtpOperationCode code);
};
#endif // _MTP_DEBUG_H

136
media/mtp/MtpPacket.cpp Normal file
View File

@@ -0,0 +1,136 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <usbhost/usbhost.h>
#include "MtpPacket.h"
MtpPacket::MtpPacket(int bufferSize)
: mBuffer(NULL),
mBufferSize(bufferSize),
mAllocationIncrement(bufferSize),
mPacketSize(0)
{
mBuffer = (uint8_t *)malloc(bufferSize);
if (!mBuffer) {
fprintf(stderr, "out of memory!\n");
abort();
}
}
MtpPacket::~MtpPacket() {
if (mBuffer)
free(mBuffer);
}
void MtpPacket::reset() {
allocate(MTP_CONTAINER_HEADER_SIZE);
mPacketSize = MTP_CONTAINER_HEADER_SIZE;
memset(mBuffer, 0, mBufferSize);
}
void MtpPacket::allocate(int length) {
if (length > mBufferSize) {
int newLength = length + mAllocationIncrement;
mBuffer = (uint8_t *)realloc(mBuffer, newLength);
if (!mBuffer) {
fprintf(stderr, "out of memory!\n");
abort();
}
mBufferSize = newLength;
}
}
void MtpPacket::dump() {
for (int i = 0; i < mPacketSize; i++) {
printf("%02X ", mBuffer[i]);
if (i % 16 == 15)
printf("\n");
}
printf("\n\n");
}
uint16_t MtpPacket::getUInt16(int offset) const {
return ((uint16_t)mBuffer[offset + 1] << 8) | (uint16_t)mBuffer[offset];
}
uint32_t MtpPacket::getUInt32(int offset) const {
return ((uint32_t)mBuffer[offset + 3] << 24) | ((uint32_t)mBuffer[offset + 2] << 16) |
((uint32_t)mBuffer[offset + 1] << 8) | (uint32_t)mBuffer[offset];
}
void MtpPacket::putUInt16(int offset, uint16_t value) {
mBuffer[offset++] = (uint8_t)(value & 0xFF);
mBuffer[offset++] = (uint8_t)((value >> 8) & 0xFF);
}
void MtpPacket::putUInt32(int offset, uint32_t value) {
mBuffer[offset++] = (uint8_t)(value & 0xFF);
mBuffer[offset++] = (uint8_t)((value >> 8) & 0xFF);
mBuffer[offset++] = (uint8_t)((value >> 16) & 0xFF);
mBuffer[offset++] = (uint8_t)((value >> 24) & 0xFF);
}
uint16_t MtpPacket::getContainerCode() const {
return getUInt16(MTP_CONTAINER_CODE_OFFSET);
}
void MtpPacket::setContainerCode(uint16_t code) {
putUInt16(MTP_CONTAINER_CODE_OFFSET, code);
}
MtpTransactionID MtpPacket::getTransactionID() const {
return getUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET);
}
void MtpPacket::setTransactionID(MtpTransactionID id) {
putUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET, id);
}
uint32_t MtpPacket::getParameter(int index) const {
if (index < 1 || index > 5) {
fprintf(stderr, "index %d out of range in MtpRequestPacket::getParameter\n", index);
return 0;
}
return getUInt32(MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t));
}
void MtpPacket::setParameter(int index, uint32_t value) {
if (index < 1 || index > 5) {
fprintf(stderr, "index %d out of range in MtpResponsePacket::setParameter\n", index);
return;
}
int offset = MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t);
if (mPacketSize < offset + sizeof(uint32_t))
mPacketSize = offset + sizeof(uint32_t);
putUInt32(offset, value);
}
#ifdef MTP_HOST
int MtpPacket::transfer(struct usb_endpoint *ep, void* buffer, int length) {
printf("MtpPacket::transfer length: %d\n", length);
if (usb_endpoint_queue(ep, buffer, length)) {
printf("usb_endpoint_queue failed, errno: %d\n", errno);
return -1;
}
int ep_num;
return usb_endpoint_wait(usb_endpoint_get_device(ep), &ep_num);
}
#endif

68
media/mtp/MtpPacket.h Normal file
View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _MTP_PACKET_H
#define _MTP_PACKET_H
class MtpStringBuffer;
#include "mtp.h"
#include "MtpUtils.h"
#include <stdint.h>
class MtpPacket {
protected:
uint8_t* mBuffer;
// current size of the buffer
int mBufferSize;
// number of bytes to add when resizing the buffer
int mAllocationIncrement;
// size of the data in the packet
int mPacketSize;
public:
MtpPacket(int bufferSize);
virtual ~MtpPacket();
// sets packet size to the default container size and sets buffer to zero
virtual void reset();
void allocate(int length);
void dump();
uint16_t getContainerCode() const;
void setContainerCode(uint16_t code);
MtpTransactionID getTransactionID() const;
void setTransactionID(MtpTransactionID id);
uint32_t getParameter(int index) const;
void setParameter(int index, uint32_t value);
#ifdef MTP_HOST
int transfer(struct usb_endpoint *ep, void* buffer, int length);
#endif
protected:
uint16_t getUInt16(int offset) const;
uint32_t getUInt32(int offset) const;
void putUInt16(int offset, uint16_t value);
void putUInt32(int offset, uint32_t value);
};
#endif // _MTP_PACKET_H

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include "MtpRequestPacket.h"
MtpRequestPacket::MtpRequestPacket()
: MtpPacket(512)
{
}
MtpRequestPacket::~MtpRequestPacket() {
}
#ifdef MTP_DEVICE
int MtpRequestPacket::read(int fd) {
int ret = ::read(fd, mBuffer, mBufferSize);
if (ret >= 0)
mPacketSize = ret;
else
mPacketSize = 0;
return ret;
}
#endif
#ifdef MTP_HOST
// write our buffer to the given endpoint (host mode)
int MtpRequestPacket::write(struct usb_endpoint *ep)
{
putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_COMMAND);
return transfer(ep, mBuffer, mPacketSize);
}
#endif

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _MTP_REQUEST_PACKET_H
#define _MTP_REQUEST_PACKET_H
#include "MtpPacket.h"
#include "mtp.h"
class MtpRequestPacket : public MtpPacket {
public:
MtpRequestPacket();
virtual ~MtpRequestPacket();
#ifdef MTP_DEVICE
// fill our buffer with data from the given file descriptor
int read(int fd);
#endif
#ifdef MTP_HOST
// write our buffer to the given endpoint
int write(struct usb_endpoint *ep);
#endif
inline MtpOperationCode getOperationCode() const { return getContainerCode(); }
inline void setOperationCode(MtpOperationCode code)
{ return setContainerCode(code); }
};
#endif // _MTP_REQUEST_PACKET_H

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include "MtpResponsePacket.h"
MtpResponsePacket::MtpResponsePacket()
: MtpPacket(512)
{
}
MtpResponsePacket::~MtpResponsePacket() {
}
#ifdef MTP_DEVICE
int MtpResponsePacket::write(int fd) {
putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_RESPONSE);
int ret = ::write(fd, mBuffer, mPacketSize);
return (ret < 0 ? ret : 0);
}
#endif
#ifdef MTP_HOST
// read our buffer from the given endpoint
int MtpResponsePacket::read(struct usb_endpoint *ep) {
int ret = transfer(ep, mBuffer, mBufferSize);
if (ret >= 0)
mPacketSize = ret;
else
mPacketSize = 0;
return ret;
}
#endif

View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _MTP_RESPONSE_PACKET_H
#define _MTP_RESPONSE_PACKET_H
#include "MtpPacket.h"
#include "mtp.h"
class MtpResponsePacket : public MtpPacket {
public:
MtpResponsePacket();
virtual ~MtpResponsePacket();
#ifdef MTP_DEVICE
// write our data to the given file descriptor
int write(int fd);
#endif
#ifdef MTP_HOST
// read our buffer from the given endpoint
int read(struct usb_endpoint *ep);
#endif
inline MtpResponseCode getResponseCode() const { return getContainerCode(); }
inline void setResponseCode(MtpResponseCode code)
{ return setContainerCode(code); }
};
#endif // _MTP_RESPONSE_PACKET_H

516
media/mtp/MtpServer.cpp Normal file
View File

@@ -0,0 +1,516 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include "MtpDebug.h"
#include "MtpServer.h"
#include "MtpStorage.h"
#include "MtpStringBuffer.h"
#include "MtpDatabase.h"
#include "f_mtp.h"
static const MtpOperationCode kSupportedOperationCodes[] = {
MTP_OPERATION_GET_DEVICE_INFO,
MTP_OPERATION_OPEN_SESSION,
MTP_OPERATION_CLOSE_SESSION,
MTP_OPERATION_GET_STORAGE_IDS,
MTP_OPERATION_GET_STORAGE_INFO,
MTP_OPERATION_GET_NUM_OBJECTS,
MTP_OPERATION_GET_OBJECT_HANDLES,
MTP_OPERATION_GET_OBJECT_INFO,
MTP_OPERATION_GET_OBJECT,
// MTP_OPERATION_GET_THUMB,
MTP_OPERATION_DELETE_OBJECT,
MTP_OPERATION_SEND_OBJECT_INFO,
MTP_OPERATION_SEND_OBJECT,
// MTP_OPERATION_INITIATE_CAPTURE,
// MTP_OPERATION_FORMAT_STORE,
// MTP_OPERATION_RESET_DEVICE,
// MTP_OPERATION_SELF_TEST,
// MTP_OPERATION_SET_OBJECT_PROTECTION,
// MTP_OPERATION_POWER_DOWN,
MTP_OPERATION_GET_DEVICE_PROP_DESC,
MTP_OPERATION_GET_DEVICE_PROP_VALUE,
MTP_OPERATION_SET_DEVICE_PROP_VALUE,
MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
// MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
// MTP_OPERATION_MOVE_OBJECT,
// MTP_OPERATION_COPY_OBJECT,
// MTP_OPERATION_GET_PARTIAL_OBJECT,
// MTP_OPERATION_INITIATE_OPEN_CAPTURE,
MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
// MTP_OPERATION_GET_OBJECT_PROP_DESC,
MTP_OPERATION_GET_OBJECT_PROP_VALUE,
MTP_OPERATION_SET_OBJECT_PROP_VALUE,
// MTP_OPERATION_GET_OBJECT_REFERENCES,
// MTP_OPERATION_SET_OBJECT_REFERENCES,
// MTP_OPERATION_SKIP,
};
static const MtpObjectProperty kSupportedObjectProperties[] = {
MTP_PROPERTY_STORAGE_ID,
MTP_PROPERTY_OBJECT_FORMAT,
MTP_PROPERTY_OBJECT_SIZE,
MTP_PROPERTY_OBJECT_FILE_NAME,
MTP_PROPERTY_PARENT_OBJECT,
};
static const MtpObjectFormat kSupportedPlaybackFormats[] = {
// FIXME - fill this out later
MTP_FORMAT_ASSOCIATION,
MTP_FORMAT_MP3,
};
MtpServer::MtpServer(int fd, const char* databasePath)
: mFD(fd),
mDatabasePath(databasePath),
mDatabase(NULL),
mSessionID(0),
mSessionOpen(false),
mSendObjectHandle(kInvalidObjectHandle),
mSendObjectFileSize(0)
{
mDatabase = new MtpDatabase();
mDatabase->open(databasePath, true);
}
MtpServer::~MtpServer() {
}
void MtpServer::addStorage(const char* filePath) {
int index = mStorages.size() + 1;
index |= index << 16; // set high and low part to our index
MtpStorage* storage = new MtpStorage(index, filePath, mDatabase);
addStorage(storage);
}
MtpStorage* MtpServer::getStorage(MtpStorageID id) {
for (int i = 0; i < mStorages.size(); i++) {
MtpStorage* storage = mStorages[i];
if (storage->getStorageID() == id)
return storage;
}
return NULL;
}
void MtpServer::scanStorage() {
for (int i = 0; i < mStorages.size(); i++) {
MtpStorage* storage = mStorages[i];
storage->scanFiles();
}
}
void MtpServer::run() {
int fd = mFD;
printf("MtpServer::run fd: %d\n", fd);
while (1) {
int ret = mRequest.read(fd);
if (ret < 0) {
fprintf(stderr, "request read returned %d, errno: %d\n", ret, errno);
break;
}
MtpOperationCode operation = mRequest.getOperationCode();
MtpTransactionID transaction = mRequest.getTransactionID();
printf("operation: %s\n", MtpDebug::getOperationCodeName(operation));
mRequest.dump();
// FIXME need to generalize this
bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO);
if (dataIn) {
int ret = mData.read(fd);
if (ret < 0) {
fprintf(stderr, "data read returned %d, errno: %d\n", ret, errno);
break;
}
printf("received data:\n");
mData.dump();
} else {
mData.reset();
}
handleRequest();
if (!dataIn && mData.hasData()) {
mData.setOperationCode(operation);
mData.setTransactionID(transaction);
printf("sending data:\n");
mData.dump();
ret = mData.write(fd);
if (ret < 0) {
fprintf(stderr, "request write returned %d, errno: %d\n", ret, errno);
break;
}
}
mResponse.setTransactionID(transaction);
ret = mResponse.write(fd);
if (ret < 0) {
fprintf(stderr, "request write returned %d, errno: %d\n", ret, errno);
break;
}
}
}
void MtpServer::handleRequest() {
MtpOperationCode operation = mRequest.getOperationCode();
MtpResponseCode response;
mResponse.reset();
if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
// FIXME - need to delete mSendObjectHandle from the database
fprintf(stderr, "expected SendObject after SendObjectInfo\n");
mSendObjectHandle = kInvalidObjectHandle;
}
switch (operation) {
case MTP_OPERATION_GET_DEVICE_INFO:
response = doGetDeviceInfo();
break;
case MTP_OPERATION_OPEN_SESSION:
response = doOpenSession();
break;
case MTP_OPERATION_CLOSE_SESSION:
response = doCloseSession();
break;
case MTP_OPERATION_GET_STORAGE_IDS:
response = doGetStorageIDs();
break;
case MTP_OPERATION_GET_STORAGE_INFO:
response = doGetStorageInfo();
break;
case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
response = doGetObjectPropsSupported();
break;
case MTP_OPERATION_GET_OBJECT_HANDLES:
response = doGetObjectHandles();
break;
case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
response = doGetObjectPropValue();
break;
case MTP_OPERATION_GET_OBJECT_INFO:
response = doGetObjectInfo();
break;
case MTP_OPERATION_GET_OBJECT:
response = doGetObject();
break;
case MTP_OPERATION_SEND_OBJECT_INFO:
response = doSendObjectInfo();
break;
case MTP_OPERATION_SEND_OBJECT:
response = doSendObject();
break;
case MTP_OPERATION_DELETE_OBJECT:
response = doDeleteObject();
break;
case MTP_OPERATION_GET_OBJECT_PROP_DESC:
default:
response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
break;
}
mResponse.setResponseCode(response);
}
MtpResponseCode MtpServer::doGetDeviceInfo() {
MtpStringBuffer string;
// fill in device info
mData.putUInt16(MTP_STANDARD_VERSION);
mData.putUInt32(6); // MTP Vendor Extension ID
mData.putUInt16(MTP_STANDARD_VERSION);
string.set("microsoft.com: 1.0;");
mData.putString(string); // MTP Extensions
mData.putUInt16(0); //Functional Mode
mData.putAUInt16(kSupportedOperationCodes,
sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
mData.putEmptyArray(); // Events Supported
mData.putEmptyArray(); // Device Properties Supported
mData.putEmptyArray(); // Capture Formats
mData.putAUInt16(kSupportedPlaybackFormats,
sizeof(kSupportedPlaybackFormats) / sizeof(uint16_t)); // Playback Formats
// FIXME
string.set("Google, Inc.");
mData.putString(string); // Manufacturer
string.set("Just an Ordinary MTP Device");
mData.putString(string); // Model
string.set("1.0");
mData.putString(string); // Device Version
string.set("123456789012345678AA");
mData.putString(string); // Serial Number
return MTP_RESPONSE_OK;
}
MtpResponseCode MtpServer::doOpenSession() {
if (mSessionOpen) {
mResponse.setParameter(1, mSessionID);
return MTP_RESPONSE_SESSION_ALREADY_OPEN;
}
mSessionID = mRequest.getParameter(1);
mSessionOpen = true;
return MTP_RESPONSE_OK;
}
MtpResponseCode MtpServer::doCloseSession() {
if (!mSessionOpen)
return MTP_RESPONSE_SESSION_NOT_OPEN;
mSessionID = 0;
mSessionOpen = false;
return MTP_RESPONSE_OK;
}
MtpResponseCode MtpServer::doGetStorageIDs() {
if (!mSessionOpen)
return MTP_RESPONSE_SESSION_NOT_OPEN;
int count = mStorages.size();
mData.putUInt32(count);
for (int i = 0; i < count; i++)
mData.putUInt32(mStorages[i]->getStorageID());
return MTP_RESPONSE_OK;
}
MtpResponseCode MtpServer::doGetStorageInfo() {
MtpStringBuffer string;
if (!mSessionOpen)
return MTP_RESPONSE_SESSION_NOT_OPEN;
MtpStorageID id = mRequest.getParameter(1);
MtpStorage* storage = getStorage(id);
if (!storage)
return MTP_RESPONSE_INVALID_STORAGE_ID;
mData.putUInt16(storage->getType());
mData.putUInt16(storage->getFileSystemType());
mData.putUInt16(storage->getAccessCapability());
mData.putUInt64(storage->getMaxCapacity());
mData.putUInt64(storage->getFreeSpace());
mData.putUInt32(1024*1024*1024); // Free Space in Objects
string.set(storage->getDescription());
mData.putString(string);
mData.putEmptyString(); // Volume Identifier
return MTP_RESPONSE_OK;
}
MtpResponseCode MtpServer::doGetObjectPropsSupported() {
if (!mSessionOpen)
return MTP_RESPONSE_SESSION_NOT_OPEN;
MtpObjectFormat format = mRequest.getParameter(1);
mData.putAUInt16(kSupportedObjectProperties,
sizeof(kSupportedObjectProperties) / sizeof(uint16_t));
return MTP_RESPONSE_OK;
}
MtpResponseCode MtpServer::doGetObjectHandles() {
if (!mSessionOpen)
return MTP_RESPONSE_SESSION_NOT_OPEN;
MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
MtpObjectFormat format = mRequest.getParameter(2); // 0x00000000 for all formats
MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
// 0x00000000 for all objects?
MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
mData.putAUInt32(handles);
delete handles;
return MTP_RESPONSE_OK;
}
MtpResponseCode MtpServer::doGetObjectPropValue() {
MtpObjectHandle handle = mRequest.getParameter(1);
MtpObjectProperty property = mRequest.getParameter(2);
return mDatabase->getObjectProperty(handle, property, mData);
}
MtpResponseCode MtpServer::doGetObjectInfo() {
MtpObjectHandle handle = mRequest.getParameter(1);
return mDatabase->getObjectInfo(handle, mData);
}
MtpResponseCode MtpServer::doGetObject() {
MtpObjectHandle handle = mRequest.getParameter(1);
MtpString filePath;
int64_t fileLength;
if (!mDatabase->getObjectFilePath(handle, filePath, fileLength))
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
mtp_file_range mfr;
mfr.path = filePath;
mfr.path_length = strlen(mfr.path);
mfr.offset = 0;
mfr.length = fileLength;
// send data header
mData.setOperationCode(mRequest.getOperationCode());
mData.setTransactionID(mRequest.getTransactionID());
mData.writeDataHeader(mFD, fileLength);
// then transfer the file
int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
// FIXME - check for errors here
printf("MTP_SEND_FILE returned %d\n", ret);
return MTP_RESPONSE_OK;
}
MtpResponseCode MtpServer::doSendObjectInfo() {
MtpString path;
MtpStorageID storageID = mRequest.getParameter(1);
MtpStorage* storage = getStorage(storageID);
MtpObjectHandle parent = mRequest.getParameter(2);
if (!storage)
return MTP_RESPONSE_INVALID_STORAGE_ID;
// special case the root
if (parent == MTP_PARENT_ROOT)
path = storage->getPath();
else {
int64_t dummy;
if (!mDatabase->getObjectFilePath(parent, path, dummy))
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
}
// read only the fields we need
mData.getUInt32(); // storage ID
MtpObjectFormat format = mData.getUInt16();
mData.getUInt16(); // protection status
mSendObjectFileSize = mData.getUInt32();
mData.getUInt16(); // thumb format
mData.getUInt32(); // thumb compressed size
mData.getUInt32(); // thumb pix width
mData.getUInt32(); // thumb pix height
mData.getUInt32(); // image pix width
mData.getUInt32(); // image pix height
mData.getUInt32(); // image bit depth
mData.getUInt32(); // parent
uint16_t associationType = mData.getUInt16();
uint32_t associationDesc = mData.getUInt32(); // association desc
mData.getUInt32(); // sequence number
MtpStringBuffer name, created, modified;
mData.getString(name); // file name
mData.getString(created); // date created
mData.getString(modified); // date modified
// keywords follow
time_t createdTime, modifiedTime;
if (!parseDateTime(created, createdTime))
createdTime = 0;
if (!parseDateTime(modified, modifiedTime))
modifiedTime = 0;
printf("SendObjectInfo format: %04X size: %d name: %s, created: %s, modified: %s\n",
format, mSendObjectFileSize, (const char*)name, (const char*)created, (const char*)modified);
if (path[path.size() - 1] != '/')
path += "/";
path += (const char *)name;
MtpObjectHandle handle = mDatabase->addFile((const char*)path,
format, parent, storageID, mSendObjectFileSize,
createdTime, modifiedTime);
if (handle == kInvalidObjectHandle)
return MTP_RESPONSE_GENERAL_ERROR;
if (format == MTP_FORMAT_ASSOCIATION) {
mode_t mask = umask(0);
int ret = mkdir((const char *)path, S_IRWXU | S_IRWXG | S_IRWXO);
umask(mask);
if (ret && ret != -EEXIST)
return MTP_RESPONSE_GENERAL_ERROR;
} else {
mSendObjectFilePath = path;
// save the handle for the SendObject call, which should follow
mSendObjectHandle = handle;
}
mResponse.setParameter(1, storageID);
mResponse.setParameter(2, parent);
mResponse.setParameter(3, handle);
return MTP_RESPONSE_OK;
}
MtpResponseCode MtpServer::doSendObject() {
if (mSendObjectHandle == kInvalidObjectHandle) {
fprintf(stderr, "Expected SendObjectInfo before SendObject\n");
return MTP_RESPONSE_NO_VALID_OBJECT_INFO;
}
// read the header
int ret = mData.readDataHeader(mFD);
// FIXME - check for errors here.
// reset so we don't attempt to send this back
mData.reset();
mtp_file_range mfr;
mfr.path = (const char*)mSendObjectFilePath;
mfr.path_length = strlen(mfr.path);
mfr.offset = 0;
mfr.length = mSendObjectFileSize;
// transfer the file
ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
// FIXME - check for errors here.
// we need to return a reasonable response and delete
// mSendObjectHandle from the database if this fails.
printf("MTP_RECEIVE_FILE returned %d\n", ret);
mSendObjectHandle = kInvalidObjectHandle;
return MTP_RESPONSE_OK;
}
MtpResponseCode MtpServer::doDeleteObject() {
MtpObjectHandle handle = mRequest.getParameter(1);
MtpObjectFormat format = mRequest.getParameter(1);
// FIXME - support deleting all objects if handle is 0xFFFFFFFF
// FIXME - implement deleting objects by format
// FIXME - handle non-empty directories
MtpString filePath;
int64_t fileLength;
if (!mDatabase->getObjectFilePath(handle, filePath, fileLength))
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
printf("deleting %s\n", (const char *)filePath);
// one of these should work
rmdir((const char *)filePath);
unlink((const char *)filePath);
mDatabase->deleteFile(handle);
return MTP_RESPONSE_OK;
}
MtpResponseCode MtpServer::doGetObjectPropDesc() {
MtpObjectProperty property = mRequest.getParameter(1);
MtpObjectFormat format = mRequest.getParameter(2);
return -1;
}

86
media/mtp/MtpServer.h Normal file
View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _MTP_SERVER_H
#define _MTP_SERVER_H
#include "MtpRequestPacket.h"
#include "MtpDataPacket.h"
#include "MtpResponsePacket.h"
#include "mtp.h"
#include "MtpUtils.h"
class MtpStorage;
class MtpDatabase;
class MtpServer {
private:
// file descriptor for MTP kernel driver
int mFD;
// path to our sqlite3 database
const char* mDatabasePath;
MtpDatabase* mDatabase;
// current session ID
MtpSessionID mSessionID;
// true if we have an open session and mSessionID is valid
bool mSessionOpen;
MtpRequestPacket mRequest;
MtpDataPacket mData;
MtpResponsePacket mResponse;
MtpStorageList mStorages;
// handle for new object, set by SendObjectInfo and used by SendObject
MtpObjectHandle mSendObjectHandle;
MtpString mSendObjectFilePath;
size_t mSendObjectFileSize;
public:
MtpServer(int fd, const char* databasePath);
virtual ~MtpServer();
void addStorage(const char* filePath);
inline void addStorage(MtpStorage* storage) { mStorages.push(storage); }
MtpStorage* getStorage(MtpStorageID id);
void scanStorage();
void run();
private:
void handleRequest();
MtpResponseCode doGetDeviceInfo();
MtpResponseCode doOpenSession();
MtpResponseCode doCloseSession();
MtpResponseCode doGetStorageIDs();
MtpResponseCode doGetStorageInfo();
MtpResponseCode doGetObjectPropsSupported();
MtpResponseCode doGetObjectHandles();
MtpResponseCode doGetObjectPropValue();
MtpResponseCode doGetObjectInfo();
MtpResponseCode doGetObject();
MtpResponseCode doSendObjectInfo();
MtpResponseCode doSendObject();
MtpResponseCode doDeleteObject();
MtpResponseCode doGetObjectPropDesc();
};
#endif // _MTP_SERVER_H

135
media/mtp/MtpStorage.cpp Normal file
View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "MtpDatabase.h"
#include "MtpStorage.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
MtpStorage::MtpStorage(MtpStorageID id, const char* filePath, MtpDatabase* db)
: mStorageID(id),
mFilePath(filePath),
mDatabase(db),
mMaxCapacity(0)
{
}
MtpStorage::~MtpStorage() {
}
int MtpStorage::getType() const {
return MTP_STORAGE_FIXED_RAM;
}
int MtpStorage::getFileSystemType() const {
return MTP_STORAGE_FILESYSTEM_HIERARCHICAL;
}
int MtpStorage::getAccessCapability() const {
return MTP_STORAGE_READ_WRITE;
}
uint64_t MtpStorage::getMaxCapacity() {
if (mMaxCapacity == 0) {
struct statfs stat;
if (statfs(mFilePath, &stat))
return -1;
mMaxCapacity = (uint64_t)stat.f_blocks * (uint64_t)stat.f_bsize;
}
return mMaxCapacity;
}
uint64_t MtpStorage::getFreeSpace() {
struct statfs stat;
if (statfs(mFilePath, &stat))
return -1;
return (uint64_t)stat.f_bavail * (uint64_t)stat.f_bsize;
}
const char* MtpStorage::getDescription() const {
return "Phone Storage";
}
bool MtpStorage::scanFiles() {
mDatabase->beginTransaction();
int ret = scanDirectory(mFilePath, MTP_PARENT_ROOT);
mDatabase->commitTransaction();
return (ret == 0);
}
int MtpStorage::scanDirectory(const char* path, MtpObjectHandle parent)
{
char buffer[PATH_MAX];
struct dirent* entry;
int length = strlen(path);
if (length > sizeof(buffer) + 2) {
fprintf(stderr, "path too long: %s\n", path);
}
DIR* dir = opendir(path);
if (!dir) {
fprintf(stderr, "opendir %s failed, errno: %d", path, errno);
return -1;
}
strncpy(buffer, path, sizeof(buffer));
char* fileStart = buffer + length;
// make sure we have a trailing slash
if (fileStart[-1] != '/') {
*(fileStart++) = '/';
}
int fileNameLength = sizeof(buffer) + fileStart - buffer;
while ((entry = readdir(dir))) {
const char* name = entry->d_name;
// ignore "." and "..", as well as any files or directories staring with dot
if (name[0] == '.') {
continue;
}
if (strlen(name) + 1 > fileNameLength) {
fprintf(stderr, "path too long for %s\n", name);
continue;
}
strcpy(fileStart, name);
struct stat statbuf;
memset(&statbuf, 0, sizeof(statbuf));
stat(buffer, &statbuf);
if (entry->d_type == DT_DIR) {
MtpObjectHandle handle = mDatabase->addFile(buffer, MTP_FORMAT_ASSOCIATION,
parent, mStorageID, 0, 0, statbuf.st_mtime);
scanDirectory(buffer, handle);
} else if (entry->d_type == DT_REG) {
mDatabase->addFile(buffer, MTP_FORMAT_UNDEFINED, parent, mStorageID,
statbuf.st_size, 0, statbuf.st_mtime);
}
}
closedir(dir);
return 0;
}

52
media/mtp/MtpStorage.h Normal file
View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _MTP_STORAGE_H
#define _MTP_STORAGE_H
#include "mtp.h"
class MtpDatabase;
class SqliteStatement;
class MtpStorage {
private:
MtpStorageID mStorageID;
const char* mFilePath;
MtpDatabase* mDatabase;
uint64_t mMaxCapacity;
public:
MtpStorage(MtpStorageID id, const char* filePath, MtpDatabase* db);
virtual ~MtpStorage();
inline MtpStorageID getStorageID() const { return mStorageID; }
int getType() const;
int getFileSystemType() const;
int getAccessCapability() const;
uint64_t getMaxCapacity();
uint64_t getFreeSpace();
const char* getDescription() const;
inline const char* getPath() const { return mFilePath; }
bool scanFiles();
private:
int scanDirectory(const char* path, MtpObjectHandle parent);
};
#endif // _MTP_STORAGE_H

View File

@@ -0,0 +1,133 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include "MtpDataPacket.h"
#include "MtpStringBuffer.h"
MtpStringBuffer::MtpStringBuffer()
: mCharCount(0),
mByteCount(1)
{
mBuffer[0] = 0;
}
MtpStringBuffer::MtpStringBuffer(const char* src)
: mCharCount(0),
mByteCount(1)
{
set(src);
}
MtpStringBuffer::MtpStringBuffer(const MtpStringBuffer& src)
: mCharCount(src.mCharCount),
mByteCount(src.mByteCount)
{
memcpy(mBuffer, src.mBuffer, mByteCount);
}
MtpStringBuffer::~MtpStringBuffer() {
}
void MtpStringBuffer::set(const char* src) {
int length = strlen(src);
if (length >= sizeof(mBuffer))
length = sizeof(mBuffer) - 1;
memcpy(mBuffer, src, length);
// count the characters
int count = 0;
char ch;
while ((ch = *src++) != 0) {
if ((ch & 0x80) == 0) {
// single byte character
} else if ((ch & 0xE0) == 0xC0) {
// two byte character
if (! *src++) {
// last character was truncated, so ignore last byte
length--;
break;
}
} else if ((ch & 0xF0) == 0xE0) {
// 3 byte char
if (! *src++) {
// last character was truncated, so ignore last byte
length--;
break;
}
if (! *src++) {
// last character was truncated, so ignore last two bytes
length -= 2;
break;
}
}
count++;
}
mByteCount = length + 1;
mBuffer[length] = 0;
mCharCount = count;
}
void MtpStringBuffer::readFromPacket(MtpDataPacket* packet) {
int count = packet->getUInt8();
uint8_t* dest = mBuffer;
for (int i = 0; i < count; i++) {
uint16_t ch = packet->getUInt16();
if (ch >= 0x0800) {
*dest++ = (uint8_t)(0xE0 | (ch >> 12));
*dest++ = (uint8_t)(0x80 | ((ch >> 6) & 0x3F));
*dest++ = (uint8_t)(0x80 | (ch & 0x3F));
} else if (ch >= 0x80) {
*dest++ = (uint8_t)(0xC0 | (ch >> 6));
*dest++ = (uint8_t)(0x80 | (ch & 0x3F));
} else {
*dest++ = ch;
}
}
*dest++ = 0;
mCharCount = count;
mByteCount = dest - mBuffer;
}
void MtpStringBuffer::writeToPacket(MtpDataPacket* packet) const {
int count = mCharCount;
const uint8_t* src = mBuffer;
packet->putUInt8(count);
// expand utf8 to 16 bit chars
for (int i = 0; i < count; i++) {
uint16_t ch;
uint16_t ch1 = *src++;
if ((ch1 & 0x80) == 0) {
// single byte character
ch = ch1;
} else if ((ch1 & 0xE0) == 0xC0) {
// two byte character
uint16_t ch2 = *src++;
ch = ((ch1 & 0x1F) << 6) | (ch2 & 0x3F);
} else {
// three byte character
uint16_t ch2 = *src++;
uint16_t ch3 = *src++;
ch = ((ch1 & 0x0F) << 12) | ((ch2 & 0x3F) << 6) | (ch3 & 0x3F);
}
packet->putUInt16(ch);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _MTP_STRING_BUFFER_H
#define _MTP_STRING_BUFFER_H
#include <stdint.h>
class MtpDataPacket;
// Represents a utf8 string, with a maximum of 255 characters
class MtpStringBuffer {
private:
// maximum 3 bytes/character, with 1 extra for zero termination
uint8_t mBuffer[255 * 3 + 1];
int mCharCount;
int mByteCount;
public:
MtpStringBuffer();
MtpStringBuffer(const char* src);
MtpStringBuffer(const MtpStringBuffer& src);
virtual ~MtpStringBuffer();
void set(const char* src);
void readFromPacket(MtpDataPacket* packet);
void writeToPacket(MtpDataPacket* packet) const;
inline int getCharCount() const { return mCharCount; }
inline int getByteCount() const { return mByteCount; }
inline operator const char*() const { return (const char *)mBuffer; }
};
#endif // _MTP_STRING_BUFFER_H

73
media/mtp/MtpUtils.cpp Normal file
View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <time.h>
#include <cutils/tztime.h>
#include "MtpUtils.h"
/*
DateTime strings follow a compatible subset of the definition found in ISO 8601, and
take the form of a Unicode string formatted as: "YYYYMMDDThhmmss.s". In this
representation, YYYY shall be replaced by the year, MM replaced by the month (01-12),
DD replaced by the day (01-31), T is a constant character 'T' delimiting time from date,
hh is replaced by the hour (00-23), mm is replaced by the minute (00-59), and ss by the
second (00-59). The ".s" is optional, and represents tenths of a second.
*/
bool parseDateTime(const char* dateTime, time_t& outSeconds) {
int year, month, day, hour, minute, second;
struct tm tm;
if (sscanf(dateTime, "%04d%02d%02dT%02d%02d%02d",
&year, &month, &day, &hour, &minute, &second) != 6)
return false;
const char* tail = dateTime + 15;
// skip optional tenth of second
if (tail[0] == '.' && tail[1])
tail += 2;
//FIXME - support +/-hhmm
bool useUTC = (tail[0] == 'Z');
// hack to compute timezone
time_t dummy;
localtime_r(&dummy, &tm);
tm.tm_sec = second;
tm.tm_min = minute;
tm.tm_hour = hour;
tm.tm_mday = day;
tm.tm_mon = month;
tm.tm_year = year - 1900;
tm.tm_wday = 0;
tm.tm_isdst = -1;
if (useUTC)
outSeconds = mktime(&tm);
else
outSeconds = mktime_tz(&tm, tm.tm_zone);
return true;
}
void formatDateTime(time_t seconds, char* buffer, int bufferLength) {
struct tm tm;
localtime_r(&seconds, &tm);
snprintf(buffer, bufferLength, "%04d%02d%02dT%02d%02d%02d",
tm.tm_year + 1900, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
}

34
media/mtp/MtpUtils.h Normal file
View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _MTP_UTILS_H
#define _MTP_UTILS_H
#include "utils/String8.h"
#include "utils/Vector.h"
class MtpStorage;
typedef android::Vector<MtpStorage *> MtpStorageList;
typedef android::Vector<uint32_t> UInt32List;
typedef UInt32List MtpObjectHandleList;
typedef android::String8 MtpString;
bool parseDateTime(const char* dateTime, time_t& outSeconds);
void formatDateTime(time_t seconds, char* buffer, int bufferLength);
#endif // _MTP_UTILS_H

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "SqliteDatabase.h"
#include "SqliteStatement.h"
#include <stdio.h>
#include <sqlite3.h>
SqliteDatabase::SqliteDatabase()
: mDatabaseHandle(NULL)
{
}
SqliteDatabase::~SqliteDatabase() {
close();
}
bool SqliteDatabase::open(const char* path, bool create) {
int flags = SQLITE_OPEN_READWRITE;
if (create) flags |= SQLITE_OPEN_CREATE;
// SQLITE_OPEN_NOMUTEX?
int ret = sqlite3_open_v2(path, &mDatabaseHandle, flags, NULL);
if (ret) {
fprintf(stderr, "could not open database\n");
return false;
}
return true;
}
void SqliteDatabase::close() {
if (mDatabaseHandle) {
sqlite3_close(mDatabaseHandle);
mDatabaseHandle = NULL;
}
}
bool SqliteDatabase::exec(const char* sql) {
return (sqlite3_exec(mDatabaseHandle, sql, NULL, NULL, NULL) == 0);
}
int SqliteDatabase::lastInsertedRow() {
return sqlite3_last_insert_rowid(mDatabaseHandle);
}
void SqliteDatabase::beginTransaction() {
exec("BEGIN TRANSACTION");
}
void SqliteDatabase::commitTransaction() {
exec("COMMIT TRANSACTION");
}
void SqliteDatabase::rollbackTransaction() {
exec("ROLLBACK TRANSACTION");
}
int SqliteDatabase::getVersion() {
SqliteStatement stmt(this);
stmt.prepare("PRAGMA user_version;");
stmt.step();
return stmt.getColumnInt(0);
}
void SqliteDatabase::setVersion(int version) {
char buffer[40];
snprintf(buffer, sizeof(buffer), "PRAGMA user_version = %d", version);
exec(buffer);
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _SQLITE_DATABASE_H
#define _SQLITE_DATABASE_H
typedef struct sqlite3 sqlite3;
class SqliteDatabase {
private:
sqlite3* mDatabaseHandle;
public:
SqliteDatabase();
virtual ~SqliteDatabase();
virtual bool open(const char* path, bool create);
virtual void close();
bool exec(const char* sql);
int lastInsertedRow();
void beginTransaction();
void commitTransaction();
void rollbackTransaction();
int getVersion();
void setVersion(int version);
inline sqlite3* getDatabaseHandle() const { return mDatabaseHandle; }
};
#endif // _SQLITE_DATABASE_H

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "SqliteStatement.h"
#include "SqliteDatabase.h"
#include <stdio.h>
#include <sqlite3.h>
SqliteStatement::SqliteStatement(SqliteDatabase* db)
: mDatabaseHandle(db->getDatabaseHandle()),
mStatement(NULL),
mDone(false)
{
}
SqliteStatement::~SqliteStatement() {
finalize();
}
bool SqliteStatement::prepare(const char* sql) {
return (sqlite3_prepare_v2(mDatabaseHandle, sql, -1, &mStatement, NULL) == 0);
}
bool SqliteStatement::step() {
int ret = sqlite3_step(mStatement);
if (ret == SQLITE_DONE) {
mDone = true;
return true;
}
return (ret == SQLITE_OK || ret == SQLITE_ROW);
}
void SqliteStatement::reset() {
sqlite3_reset(mStatement);
mDone = false;
}
void SqliteStatement::finalize() {
if (mStatement) {
sqlite3_finalize(mStatement);
mStatement = NULL;
}
}
void SqliteStatement::bind(int column, int value) {
sqlite3_bind_int(mStatement, column, value);
}
void SqliteStatement::bind(int column, const char* value) {
sqlite3_bind_text(mStatement, column, value, -1, SQLITE_TRANSIENT);
}
int SqliteStatement::getColumnInt(int column) {
return sqlite3_column_int(mStatement, column);
}
int64_t SqliteStatement::getColumnInt64(int column) {
return sqlite3_column_int64(mStatement, column);
}
const char* SqliteStatement::getColumnString(int column) {
return (const char *)sqlite3_column_text(mStatement, column);
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _SQLITE_STATEMENT_H
#define _SQLITE_STATEMENT_H
typedef struct sqlite3 sqlite3;
typedef struct sqlite3_stmt sqlite3_stmt;
class SqliteDatabase;
#include <stdint.h>
class SqliteStatement {
private:
sqlite3* mDatabaseHandle;
sqlite3_stmt* mStatement;
bool mDone;
public:
SqliteStatement(SqliteDatabase* db);
virtual ~SqliteStatement();
bool prepare(const char* sql);
bool step();
void reset();
void finalize();
void bind(int column, int value);
void bind(int column, const char* value);
int getColumnInt(int column);
int64_t getColumnInt64(int column);
const char* getColumnString(int column);
inline bool isDone() const { return mDone; }
};
#endif // _SQLITE_STATEMENT_H

39
media/mtp/f_mtp.h Normal file
View File

@@ -0,0 +1,39 @@
/*
* Gadget Function Driver for MTP
*
* Copyright (C) 2010 Google, Inc.
* Author: Mike Lockwood <lockwood@android.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __LINUX_USB_F_MTP_H
#define __LINUX_USB_F_MTP_H
struct mtp_file_range {
/* path for file to transfer */
const char *path;
/* strlen(path) */
int path_length;
/* offset in file for start of transfer */
loff_t offset;
/* number of bytes to transfer */
size_t length;
};
/* Sends the specified file range to the host */
#define MTP_SEND_FILE _IOW('M', 0, struct mtp_file_range)
/* Receives data from the host and writes it to a file.
* The file is created if it does not exist.
*/
#define MTP_RECEIVE_FILE _IOW('M', 1, struct mtp_file_range)
#endif /* __LINUX_USB_F_MTP_H */

492
media/mtp/mtp.h Normal file
View File

@@ -0,0 +1,492 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _MTP_H
#define _MTP_H
#include <stdint.h>
typedef uint16_t MtpOperationCode;
typedef uint16_t MtpResponseCode;
typedef uint32_t MtpSessionID;
typedef uint32_t MtpStorageID;
typedef uint32_t MtpTransactionID;
typedef uint16_t MtpObjectFormat;
typedef uint16_t MtpObjectProperty;
// object handles are unique across all storage but only within a single session.
// object handles cannot be reused after an object is deleted.
// values 0x00000000 and 0xFFFFFFFF are reserved for special purposes.
typedef uint32_t MtpObjectHandle;
#define kInvalidObjectHandle 0xFFFFFFFF
#define MTP_STANDARD_VERSION 100
// Container Types
#define MTP_CONTAINER_TYPE_UNDEFINED 0
#define MTP_CONTAINER_TYPE_COMMAND 1
#define MTP_CONTAINER_TYPE_DATA 2
#define MTP_CONTAINER_TYPE_RESPONSE 3
#define MTP_CONTAINER_TYPE_EVENT 4
// Container Offsets
#define MTP_CONTAINER_LENGTH_OFFSET 0
#define MTP_CONTAINER_TYPE_OFFSET 4
#define MTP_CONTAINER_CODE_OFFSET 6
#define MTP_CONTAINER_TRANSACTION_ID_OFFSET 8
#define MTP_CONTAINER_PARAMETER_OFFSET 12
#define MTP_CONTAINER_HEADER_SIZE 12
// Special values
#define MTP_PARENT_ROOT 0xFFFFFFFF // parent is root of the storage
// MTP Types
#define MTP_TYPE_UNDEFINED 0x0000 // Undefined
#define MTP_TYPE_INT8 0x0001 // Signed 8-bit integer
#define MTP_TYPE_UINT8 0x0002 // Unsigned 8-bit integer
#define MTP_TYPE_INT16 0x0003 // Signed 16-bit integer
#define MTP_TYPE_UINT16 0x0004 // Unsigned 16-bit integer
#define MTP_TYPE_INT32 0x0005 // Signed 32-bit integer
#define MTP_TYPE_UINT32 0x0006 // Unsigned 32-bit integer
#define MTP_TYPE_INT64 0x0007 // Signed 64-bit integer
#define MTP_TYPE_UINT64 0x0008 // Unsigned 64-bit integer
#define MTP_TYPE_INT128 0x0009 // Signed 128-bit integer
#define MTP_TYPE_UINT128 0x000A // Unsigned 128-bit integer
#define MTP_TYPE_AINT8 0x4001 // Array of signed 8-bit integers
#define MTP_TYPE_AUINT8 0x4002 // Array of unsigned 8-bit integers
#define MTP_TYPE_AINT16 0x4003 // Array of signed 16-bit integers
#define MTP_TYPE_AUINT16 0x4004 // Array of unsigned 16-bit integers
#define MTP_TYPE_AINT32 0x4005 // Array of signed 32-bit integers
#define MTP_TYPE_AUINT32 0x4006 // Array of unsigned 32-bit integers
#define MTP_TYPE_AINT64 0x4007 // Array of signed 64-bit integers
#define MTP_TYPE_AUINT64 0x4008 // Array of unsigned 64-bit integers
#define MTP_TYPE_AINT128 0x4009 // Array of signed 128-bit integers
#define MTP_TYPE_AUINT128 0x400A // Array of unsigned 128-bit integers
#define MTP_TYPE_STR 0xFFFF // Variable-length Unicode string
// MTP Format Codes
#define MTP_FORMAT_UNDEFINED 0x3000 // Undefined object
#define MTP_FORMAT_ASSOCIATION 0x3001 // Association (for example, a folder)
#define MTP_FORMAT_SCRIPT 0x3002 // Device model-specific script
#define MTP_FORMAT_EXECUTABLE 0x3003 // Device model-specific binary executable
#define MTP_FORMAT_TEXT 0x3004 // Text file
#define MTP_FORMAT_HTML 0x3005 // Hypertext Markup Language file (text)
#define MTP_FORMAT_DPOF 0x3006 // Digital Print Order Format file (text)
#define MTP_FORMAT_AIFF 0x3007 // Audio clip
#define MTP_FORMAT_WAV 0x3008 // Audio clip
#define MTP_FORMAT_MP3 0x3009 // Audio clip
#define MTP_FORMAT_AVI 0x300A // Video clip
#define MTP_FORMAT_MPEG 0x300B // Video clip
#define MTP_FORMAT_ASF 0x300C // Microsoft Advanced Streaming Format (video)
#define MTP_FORMAT_DEFINED 0x3800 // Unknown image object
#define MTP_FORMAT_EXIF_JPEG 0x3801 // Exchangeable File Format, JEIDA standard
#define MTP_FORMAT_TIFF_EP 0x3802 // Tag Image File Format for Electronic Photography
#define MTP_FORMAT_FLASHPIX 0x3803 // Structured Storage Image Format
#define MTP_FORMAT_BMP 0x3804 // Microsoft Windows Bitmap file
#define MTP_FORMAT_CIFF 0x3805 // Canon Camera Image File Format
#define MTP_FORMAT_GIF 0x3807 // Graphics Interchange Format
#define MTP_FORMAT_JFIF 0x3808 // JPEG File Interchange Format
#define MTP_FORMAT_CD 0x3809 // PhotoCD Image Pac
#define MTP_FORMAT_PICT 0x380A // Quickdraw Image Format
#define MTP_FORMAT_PNG 0x380B // Portable Network Graphics
#define MTP_FORMAT_TIFF 0x380D // Tag Image File Format
#define MTP_FORMAT_TIFF_IT 0x380E // Tag Image File Format for Information Technology (graphic arts)
#define MTP_FORMAT_JP2 0x380F // JPEG2000 Baseline File Format
#define MTP_FORMAT_JPX 0x3810 // JPEG2000 Extended File Format
#define MTP_FORMAT_UNDEFINED_FIRMWARE 0xB802
#define MTP_FORMAT_WINDOWS_IMAGE_FORMAT 0xB881
#define MTP_FORMAT_UNDEFINED_AUDIO 0xB900
#define MTP_FORMAT_WMA 0xB901
#define MTP_FORMAT_OGG 0xB902
#define MTP_FORMAT_AAC 0xB903
#define MTP_FORMAT_AUDIBLE 0xB904
#define MTP_FORMAT_FLAC 0xB906
#define MTP_FORMAT_UNDEFINED_VIDEO 0xB980
#define MTP_FORMAT_WMV 0xB981
#define MTP_FORMAT_MP4_CONTAINER 0xB982 // ISO 14496-1
#define MTP_FORMAT_MP2 0xB983
#define MTP_FORMAT_3GP_CONTAINER 0xB984 // 3GPP file format. Details: http://www.3gpp.org/ftp/Specs/html-info/26244.htm (page title - \u201cTransparent end-to-end packet switched streaming service, 3GPP file format\u201d).
#define MTP_FORMAT_UNDEFINED_COLLECTION 0xBA00
#define MTP_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM 0xBA01
#define MTP_FORMAT_ABSTRACT_IMAGE_ALBUM 0xBA02
#define MTP_FORMAT_ABSTRACT_AUDIO_ALBUM 0xBA03
#define MTP_FORMAT_ABSTRACT_VIDEO_ALBUM 0xBA04
#define MTP_FORMAT_ABSTRACT_AV_PLAYLIST 0xBA05
#define MTP_FORMAT_ABSTRACT_CONTACT_GROUP 0xBA06
#define MTP_FORMAT_ABSTRACT_MESSAGE_FOLDER 0xBA07
#define MTP_FORMAT_ABSTRACT_CHAPTERED_PRODUCTION 0xBA08
#define MTP_FORMAT_ABSTRACT_AUDIO_PLAYLIST 0xBA09
#define MTP_FORMAT_ABSTRACT_VIDEO_PLAYLIST 0xBA0A
#define MTP_FORMAT_ABSTRACT_MEDIACAST 0xBA0B // For use with mediacasts; references multimedia enclosures of RSS feeds or episodic content
#define MTP_FORMAT_WPL_PLAYLIST 0xBA10
#define MTP_FORMAT_M3U_PLAYLIST 0xBA11
#define MTP_FORMAT_MPL_PLAYLIST 0xBA12
#define MTP_FORMAT_ASX_PLAYLIST 0xBA13
#define MTP_FORMAT_PLS_PLAYLIST 0xBA14
#define MTP_FORMAT_UNDEFINED_DOCUMENT 0xBA80
#define MTP_FORMAT_ABSTRACT_DOCUMENT 0xBA81
#define MTP_FORMAT_XML_DOCUMENT 0xBA82
#define MTP_FORMAT_MS_WORD_DOCUMENT 0xBA83
#define MTP_FORMAT_MHT_COMPILED_HTML_DOCUMENT 0xBA84
#define MTP_FORMAT_MS_EXCEL_SPREADSHEET 0xBA85
#define MTP_FORMAT_MS_POWERPOINT_PRESENTATION 0xBA86
#define MTP_FORMAT_UNDEFINED_MESSAGE 0xBB00
#define MTP_FORMAT_ABSTRACT_MESSSAGE 0xBB01
#define MTP_FORMAT_UNDEFINED_CONTACT 0xBB80
#define MTP_FORMAT_ABSTRACT_CONTACT 0xBB81
#define MTP_FORMAT_VCARD_2 0xBB82
// MTP Object Property Codes
#define MTP_PROPERTY_STORAGE_ID 0xDC01
#define MTP_PROPERTY_OBJECT_FORMAT 0xDC02
#define MTP_PROPERTY_PROTECTION_STATUS 0xDC03
#define MTP_PROPERTY_OBJECT_SIZE 0xDC04
#define MTP_PROPERTY_ASSOCIATION_TYPE 0xDC05
#define MTP_PROPERTY_ASSOCIATION_DESC 0xDC06
#define MTP_PROPERTY_OBJECT_FILE_NAME 0xDC07
#define MTP_PROPERTY_DATE_CREATED 0xDC08
#define MTP_PROPERTY_DATE_MODIFIED 0xDC09
#define MTP_PROPERTY_KEYWORDS 0xDC0A
#define MTP_PROPERTY_PARENT_OBJECT 0xDC0B
#define MTP_PROPERTY_ALLOWED_FOLDER_CONTENTS 0xDC0C
#define MTP_PROPERTY_HIDDEN 0xDC0D
#define MTP_PROPERTY_SYSTEM_OBJECT 0xDC0E
#define MTP_PROPERTY_PERSISTENT_UID 0xDC41
#define MTP_PROPERTY_SYNC_ID 0xDC42
#define MTP_PROPERTY_PROPERTY_BAG 0xDC43
#define MTP_PROPERTY_NAME 0xDC44
#define MTP_PROPERTY_CREATED_BY 0xDC45
#define MTP_PROPERTY_ARTIST 0xDC46
#define MTP_PROPERTY_DATE_AUTHORED 0xDC47
#define MTP_PROPERTY_DESCRIPTION 0xDC48
#define MTP_PROPERTY_URL_REFERENCE 0xDC49
#define MTP_PROPERTY_LANGUAGE_LOCALE 0xDC4A
#define MTP_PROPERTY_COPYRIGHT_INFORMATION 0xDC4B
#define MTP_PROPERTY_SOURCE 0xDC4C
#define MTP_PROPERTY_ORIGIN_LOCATION 0xDC4D
#define MTP_PROPERTY_DATE_ADDED 0xDC4E
#define MTP_PROPERTY_NON_CONSUMABLE 0xDC4F
#define MTP_PROPERTY_CORRUPT_UNPLAYABLE 0xDC50
#define MTP_PROPERTY_PRODUCER_SERIAL_NUMBER 0xDC51
#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_FORMAT 0xDC81
#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_SIZE 0xDC82
#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_HEIGHT 0xDC83
#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_WIDTH 0xDC84
#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DURATION 0xDC85
#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DATA 0xDC86
#define MTP_PROPERTY_WIDTH 0xDC87
#define MTP_PROPERTY_HEIGHT 0xDC88
#define MTP_PROPERTY_DURATION 0xDC89
#define MTP_PROPERTY_RATING 0xDC8A
#define MTP_PROPERTY_TRACK 0xDC8B
#define MTP_PROPERTY_GENRE 0xDC8C
#define MTP_PROPERTY_CREDITS 0xDC8D
#define MTP_PROPERTY_LYRICS 0xDC8E
#define MTP_PROPERTY_SUBSCRIPTION_CONTENT_ID 0xDC8F
#define MTP_PROPERTY_PRODUCED_BY 0xDC90
#define MTP_PROPERTY_USE_COUNT 0xDC91
#define MTP_PROPERTY_SKIP_COUNT 0xDC92
#define MTP_PROPERTY_LAST_ACCESSED 0xDC93
#define MTP_PROPERTY_PARENTAL_RATING 0xDC94
#define MTP_PROPERTY_META_GENRE 0xDC95
#define MTP_PROPERTY_COMPOSER 0xDC96
#define MTP_PROPERTY_EFFECTIVE_RATING 0xDC97
#define MTP_PROPERTY_SUBTITLE 0xDC98
#define MTP_PROPERTY_ORIGINAL_RELEASE_DATE 0xDC99
#define MTP_PROPERTY_ALBUM_NAME 0xDC9A
#define MTP_PROPERTY_ALBUM_ARTIST 0xDC9B
#define MTP_PROPERTY_MOOD 0xDC9C
#define MTP_PROPERTY_DRM_STATUS 0xDC9D
#define MTP_PROPERTY_SUB_DESCRIPTION 0xDC9E
#define MTP_PROPERTY_IS_CROPPED 0xDCD1
#define MTP_PROPERTY_IS_COLOUR_CORRECTED 0xDCD2
#define MTP_PROPERTY_IMAGE_BIT_DEPTH 0xDCD3
#define MTP_PROPERTY_F_NUMBER 0xDCD4
#define MTP_PROPERTY_EXPOSURE_TIME 0xDCD5
#define MTP_PROPERTY_EXPOSURE_INDEX 0xDCD6
#define MTP_PROPERTY_TOTAL_BITRATE 0xDE91
#define MTP_PROPERTY_BITRATE_TYPE 0xDE92
#define MTP_PROPERTY_SAMPLE_RATE 0xDE93
#define MTP_PROPERTY_NUMBER_OF_CHANNELS 0xDE94
#define MTP_PROPERTY_AUDIO_BIT_DEPTH 0xDE95
#define MTP_PROPERTY_SCAN_TYPE 0xDE97
#define MTP_PROPERTY_AUDIO_WAVE_CODEC 0xDE99
#define MTP_PROPERTY_AUDIO_BITRATE 0xDE9A
#define MTP_PROPERTY_VIDEO_FOURCC_CODEC 0xDE9B
#define MTP_PROPERTY_VIDEO_BITRATE 0xDE9C
#define MTP_PROPERTY_FRAMES_PER_THOUSAND_SECONDS 0xDE9D
#define MTP_PROPERTY_KEYFRAME_DISTANCE 0xDE9E
#define MTP_PROPERTY_BUFFER_SIZE 0xDE9F
#define MTP_PROPERTY_ENCODING_QUALITY 0xDEA0
#define MTP_PROPERTY_ENCODING_PROFILE 0xDEA1
#define MTP_PROPERTY_DISPLAY_NAME 0xDCE0
#define MTP_PROPERTY_BODY_TEXT 0xDCE1
#define MTP_PROPERTY_SUBJECT 0xDCE2
#define MTP_PROPERTY_PRIORITY 0xDCE3
#define MTP_PROPERTY_GIVEN_NAME 0xDD00
#define MTP_PROPERTY_MIDDLE_NAMES 0xDD01
#define MTP_PROPERTY_FAMILY_NAME 0xDD02
#define MTP_PROPERTY_PREFIX 0xDD03
#define MTP_PROPERTY_SUFFIX 0xDD04
#define MTP_PROPERTY_PHONETIC_GIVEN_NAME 0xDD05
#define MTP_PROPERTY_PHONETIC_FAMILY_NAME 0xDD06
#define MTP_PROPERTY_EMAIL_PRIMARY 0xDD07
#define MTP_PROPERTY_EMAIL_PERSONAL_1 0xDD08
#define MTP_PROPERTY_EMAIL_PERSONAL_2 0xDD09
#define MTP_PROPERTY_EMAIL_BUSINESS_1 0xDD0A
#define MTP_PROPERTY_EMAIL_BUSINESS_2 0xDD0B
#define MTP_PROPERTY_EMAIL_OTHERS 0xDD0C
#define MTP_PROPERTY_PHONE_NUMBER_PRIMARY 0xDD0D
#define MTP_PROPERTY_PHONE_NUMBER_PERSONAL 0xDD0E
#define MTP_PROPERTY_PHONE_NUMBER_PERSONAL_2 0xDD0F
#define MTP_PROPERTY_PHONE_NUMBER_BUSINESS 0xDD10
#define MTP_PROPERTY_PHONE_NUMBER_BUSINESS_2 0xDD11
#define MTP_PROPERTY_PHONE_NUMBER_MOBILE 0xDD12
#define MTP_PROPERTY_PHONE_NUMBER_MOBILE_2 0xDD13
#define MTP_PROPERTY_FAX_NUMBER_PRIMARY 0xDD14
#define MTP_PROPERTY_FAX_NUMBER_PERSONAL 0xDD15
#define MTP_PROPERTY_FAX_NUMBER_BUSINESS 0xDD16
#define MTP_PROPERTY_PAGER_NUMBER 0xDD17
#define MTP_PROPERTY_PHONE_NUMBER_OTHERS 0xDD18
#define MTP_PROPERTY_PRIMARY_WEB_ADDRESS 0xDD19
#define MTP_PROPERTY_PERSONAL_WEB_ADDRESS 0xDD1A
#define MTP_PROPERTY_BUSINESS_WEB_ADDRESS 0xDD1B
#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS 0xDD1C
#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_2 0xDD1D
#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_3 0xDD1E
#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_FULL 0xDD1F
#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_1 0xDD20
#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_2 0xDD21
#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_CITY 0xDD22
#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_REGION 0xDD23
#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_POSTAL_CODE 0xDD24
#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_COUNTRY 0xDD25
#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_FULL 0xDD26
#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_1 0xDD27
#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_2 0xDD28
#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_CITY 0xDD29
#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_REGION 0xDD2A
#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_POSTAL_CODE 0xDD2B
#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_COUNTRY 0xDD2C
#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_FULL 0xDD2D
#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_1 0xDD2E
#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_2 0xDD2F
#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_CITY 0xDD30
#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_REGION 0xDD31
#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_POSTAL_CODE 0xDD32
#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_COUNTRY 0xDD33
#define MTP_PROPERTY_ORGANIZATION_NAME 0xDD34
#define MTP_PROPERTY_PHONETIC_ORGANIZATION_NAME 0xDD35
#define MTP_PROPERTY_ROLE 0xDD36
#define MTP_PROPERTY_BIRTHDATE 0xDD37
#define MTP_PROPERTY_MESSAGE_TO 0xDD40
#define MTP_PROPERTY_MESSAGE_CC 0xDD41
#define MTP_PROPERTY_MESSAGE_BCC 0xDD42
#define MTP_PROPERTY_MESSAGE_READ 0xDD43
#define MTP_PROPERTY_MESSAGE_RECEIVED_TIME 0xDD44
#define MTP_PROPERTY_MESSAGE_SENDER 0xDD45
#define MTP_PROPERTY_ACTIVITY_BEGIN_TIME 0xDD50
#define MTP_PROPERTY_ACTIVITY_END_TIME 0xDD51
#define MTP_PROPERTY_ACTIVITY_LOCATION 0xDD52
#define MTP_PROPERTY_ACTIVITY_REQUIRED_ATTENDEES 0xDD54
#define MTP_PROPERTY_ACTIVITY_OPTIONAL_ATTENDEES 0xDD55
#define MTP_PROPERTY_ACTIVITY_RESOURCES 0xDD56
#define MTP_PROPERTY_ACTIVITY_ACCEPTED 0xDD57
#define MTP_PROPERTY_ACTIVITY_TENTATIVE 0xDD58
#define MTP_PROPERTY_ACTIVITY_DECLINED 0xDD59
#define MTP_PROPERTY_ACTIVITY_REMAINDER_TIME 0xDD5A
#define MTP_PROPERTY_ACTIVITY_OWNER 0xDD5B
#define MTP_PROPERTY_ACTIVITY_STATUS 0xDD5C
#define MTP_PROPERTY_OWNER 0xDD5D
#define MTP_PROPERTY_EDITOR 0xDD5E
#define MTP_PROPERTY_WEBMASTER 0xDD5F
#define MTP_PROPERTY_URL_SOURCE 0xDD60
#define MTP_PROPERTY_URL_DESTINATION 0xDD61
#define MTP_PROPERTY_TIME_BOOKMARK 0xDD62
#define MTP_PROPERTY_OBJECT_BOOKMARK 0xDD63
#define MTP_PROPERTY_BYTE_BOOKMARK 0xDD64
#define MTP_PROPERTY_LAST_BUILD_DATE 0xDD70
#define MTP_PROPERTY_TIME_TO_LIVE 0xDD71
#define MTP_PROPERTY_MEDIA_GUID 0xDD72
// MTP Device Property Codes
#define MTP_DEVICE_PROPERTY_UNDEFINED 0x5000
#define MTP_DEVICE_PROPERTY_BATTERY_LEVEL 0x5001
#define MTP_DEVICE_PROPERTY_FUNCTIONAL_MODE 0x5002
#define MTP_DEVICE_PROPERTY_IMAGE_SIZE 0x5003
#define MTP_DEVICE_PROPERTY_COMPRESSION_SETTING 0x5004
#define MTP_DEVICE_PROPERTY_WHITE_BALANCE 0x5005
#define MTP_DEVICE_PROPERTY_RGB_GAIN 0x5006
#define MTP_DEVICE_PROPERTY_F_NUMBER 0x5007
#define MTP_DEVICE_PROPERTY_FOCAL_LENGTH 0x5008
#define MTP_DEVICE_PROPERTY_FOCUS_DISTANCE 0x5009
#define MTP_DEVICE_PROPERTY_FOCUS_MODE 0x500A
#define MTP_DEVICE_PROPERTY_EXPOSURE_METERING_MODE 0x500B
#define MTP_DEVICE_PROPERTY_FLASH_MODE 0x500C
#define MTP_DEVICE_PROPERTY_EXPOSURE_TIME 0x500D
#define MTP_DEVICE_PROPERTY_EXPOSURE_PROGRAM_MODE 0x500E
#define MTP_DEVICE_PROPERTY_EXPOSURE_INDEX 0x500F
#define MTP_DEVICE_PROPERTY_EXPOSURE_BIAS_COMPENSATION 0x5010
#define MTP_DEVICE_PROPERTY_DATETIME 0x5011
#define MTP_DEVICE_PROPERTY_CAPTURE_DELAY 0x5012
#define MTP_DEVICE_PROPERTY_STILL_CAPTURE_MODE 0x5013
#define MTP_DEVICE_PROPERTY_CONTRAST 0x5014
#define MTP_DEVICE_PROPERTY_SHARPNESS 0x5015
#define MTP_DEVICE_PROPERTY_DIGITAL_ZOOM 0x5016
#define MTP_DEVICE_PROPERTY_EFFECT_MODE 0x5017
#define MTP_DEVICE_PROPERTY_BURST_NUMBER 0x5018
#define MTP_DEVICE_PROPERTY_BURST_INTERVAL 0x5019
#define MTP_DEVICE_PROPERTY_TIMELAPSE_NUMBER 0x501A
#define MTP_DEVICE_PROPERTY_TIMELAPSE_INTERVAL 0x501B
#define MTP_DEVICE_PROPERTY_FOCUS_METERING_MODE 0x501C
#define MTP_DEVICE_PROPERTY_UPLOAD_URL 0x501D
#define MTP_DEVICE_PROPERTY_ARTIST 0x501E
#define MTP_DEVICE_PROPERTY_COPYRIGHT_INFO 0x501F
#define MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER 0xD401
#define MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME 0xD402
#define MTP_DEVICE_PROPERTY_VOLUME 0xD403
#define MTP_DEVICE_PROPERTY_SUPPORTED_FORMATS_ORDERED 0xD404
#define MTP_DEVICE_PROPERTY_DEVICE_ICON 0xD405
#define MTP_DEVICE_PROPERTY_PLAYBACK_RATE 0xD410
#define MTP_DEVICE_PROPERTY_PLAYBACK_OBJECT 0xD411
#define MTP_DEVICE_PROPERTY_PLAYBACK_CONTAINER_INDEX 0xD412
#define MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO 0xD406
#define MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE 0xD407
// MTP Operation Codes
#define MTP_OPERATION_GET_DEVICE_INFO 0x1001
#define MTP_OPERATION_OPEN_SESSION 0x1002
#define MTP_OPERATION_CLOSE_SESSION 0x1003
#define MTP_OPERATION_GET_STORAGE_IDS 0x1004
#define MTP_OPERATION_GET_STORAGE_INFO 0x1005
#define MTP_OPERATION_GET_NUM_OBJECTS 0x1006
#define MTP_OPERATION_GET_OBJECT_HANDLES 0x1007
#define MTP_OPERATION_GET_OBJECT_INFO 0x1008
#define MTP_OPERATION_GET_OBJECT 0x1009
#define MTP_OPERATION_GET_THUMB 0x100A
#define MTP_OPERATION_DELETE_OBJECT 0x100B
#define MTP_OPERATION_SEND_OBJECT_INFO 0x100C
#define MTP_OPERATION_SEND_OBJECT 0x100D
#define MTP_OPERATION_INITIATE_CAPTURE 0x100E
#define MTP_OPERATION_FORMAT_STORE 0x100F
#define MTP_OPERATION_RESET_DEVICE 0x1010
#define MTP_OPERATION_SELF_TEST 0x1011
#define MTP_OPERATION_SET_OBJECT_PROTECTION 0x1012
#define MTP_OPERATION_POWER_DOWN 0x1013
#define MTP_OPERATION_GET_DEVICE_PROP_DESC 0x1014
#define MTP_OPERATION_GET_DEVICE_PROP_VALUE 0x1015
#define MTP_OPERATION_SET_DEVICE_PROP_VALUE 0x1016
#define MTP_OPERATION_RESET_DEVICE_PROP_VALUE 0x1017
#define MTP_OPERATION_TERMINATE_OPEN_CAPTURE 0x1018
#define MTP_OPERATION_MOVE_OBJECT 0x1019
#define MTP_OPERATION_COPY_OBJECT 0x101A
#define MTP_OPERATION_GET_PARTIAL_OBJECT 0x101B
#define MTP_OPERATION_INITIATE_OPEN_CAPTURE 0x101C
#define MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED 0x9801
#define MTP_OPERATION_GET_OBJECT_PROP_DESC 0x9802
#define MTP_OPERATION_GET_OBJECT_PROP_VALUE 0x9803
#define MTP_OPERATION_SET_OBJECT_PROP_VALUE 0x9804
#define MTP_OPERATION_GET_OBJECT_REFERENCES 0x9810
#define MTP_OPERATION_SET_OBJECT_REFERENCES 0x9811
#define MTP_OPERATION_SKIP 0x9820
// MTP Response Codes
#define MTP_RESPONSE_UNDEFINED 0x2000
#define MTP_RESPONSE_OK 0x2001
#define MTP_RESPONSE_GENERAL_ERROR 0x2002
#define MTP_RESPONSE_SESSION_NOT_OPEN 0x2003
#define MTP_RESPONSE_INVALID_TRANSACTION_ID 0x2004
#define MTP_RESPONSE_OPERATION_NOT_SUPPORTED 0x2005
#define MTP_RESPONSE_PARAMETER_NOT_SUPPORTED 0x2006
#define MTP_RESPONSE_INCOMPLETE_TRANSFER 0x2007
#define MTP_RESPONSE_INVALID_STORAGE_ID 0x2008
#define MTP_RESPONSE_INVALID_OBJECT_HANDLE 0x2009
#define MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED 0x200A
#define MTP_RESPONSE_INVALID_OBJECT_FORMAT_CODE 0x200B
#define MTP_RESPONSE_STORAGE_FULL 0x200C
#define MTP_RESPONSE_OBJECT_WRITE_PROTECTED 0x200D
#define MTP_RESPONSE_STORE_READ_ONLY 0x200E
#define MTP_RESPONSE_ACCESS_DENIED 0x200F
#define MTP_RESPONSE_NO_THUMBNAIL_PRESENT 0x2010
#define MTP_RESPONSE_SELF_TEST_FAILED 0x2011
#define MTP_RESPONSE_PARTIAL_DELETION 0x2012
#define MTP_RESPONSE_STORE_NOT_AVAILABLE 0x2013
#define MTP_RESPONSE_SPECIFICATION_BY_FORMAT_UNSUPPORTED 0x2014
#define MTP_RESPONSE_NO_VALID_OBJECT_INFO 0x2015
#define MTP_RESPONSE_INVALID_CODE_FORMAT 0x2016
#define MTP_RESPONSE_UNKNOWN_VENDOR_CODE 0x2017
#define MTP_RESPONSE_CAPTURE_ALREADY_TERMINATED 0x2018
#define MTP_RESPONSE_DEVICE_BUSY 0x2019
#define MTP_RESPONSE_INVALID_PARENT_OBJECT 0x201A
#define MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT 0x201B
#define MTP_RESPONSE_INVALID_DEVICE_PROP_VALUE 0x201C
#define MTP_RESPONSE_INVALID_PARAMETER 0x201D
#define MTP_RESPONSE_SESSION_ALREADY_OPEN 0x201E
#define MTP_RESPONSE_TRANSACTION_CANCELLED 0x201F
#define MTP_RESPONSE_SPECIFICATION_OF_DESTINATION_UNSUPPORTED 0x2020
#define MTP_RESPONSE_INVALID_OBJECT_PROP_CODE 0xA801
#define MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT 0xA802
#define MTP_RESPONSE_INVALID_OBJECT_PROP_VALUE 0xA803
#define MTP_RESPONSE_INVALID_OBJECT_REFERENCE 0xA804
#define MTP_RESPONSE_GROUP_NOT_SUPPORTED 0xA805
#define MTP_RESPONSE_INVALID_DATASET 0xA806
#define MTP_RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED 0xA807
#define MTP_RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED 0xA808
#define MTP_RESPONSE_OBJECT_TOO_LARGE 0xA809
#define MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED 0xA80A
// MTP Event Codes
#define MTP_EVENT_UNDEFINED 0x4000
#define MTP_EVENT_CANCEL_TRANSACTION 0x4001
#define MTP_EVENT_OBJECT_ADDED 0x4002
#define MTP_EVENT_OBJECT_REMOVED 0x4003
#define MTP_EVENT_STORE_ADDED 0x4004
#define MTP_EVENT_STORE_REMOVED 0x4005
#define MTP_EVENT_DEVICE_PROP_CHANGED 0x4006
#define MTP_EVENT_OBJECT_INFO_CHANGED 0x4007
#define MTP_EVENT_DEVICE_INFO_CHANGED 0x4008
#define MTP_EVENT_REQUEST_OBJECT_TRANSFER 0x4009
#define MTP_EVENT_STORE_FULL 0x400A
#define MTP_EVENT_DEVICE_RESET 0x400B
#define MTP_EVENT_STORAGE_INFO_CHANGED 0x400C
#define MTP_EVENT_CAPTURE_COMPLETE 0x400D
#define MTP_EVENT_UNREPORTED_STATUS 0x400E
#define MTP_EVENT_OBJECT_PROP_CHANGED 0xC801
#define MTP_EVENT_OBJECT_PROP_DESC_CHANGED 0xC802
#define MTP_EVENT_OBJECT_REFERENCES_CHANGED 0xC803
// Storage Type
#define MTP_STORAGE_FIXED_ROM 0x0001
#define MTP_STORAGE_REMOVABLE_ROM 0x0002
#define MTP_STORAGE_FIXED_RAM 0x0003
#define MTP_STORAGE_REMOVABLE_RAM 0x0004
// Storage File System
#define MTP_STORAGE_FILESYSTEM_FLAT 0x0001
#define MTP_STORAGE_FILESYSTEM_HIERARCHICAL 0x0002
#define MTP_STORAGE_FILESYSTEM_DCF 0x0003
// Storage Access Capability
#define MTP_STORAGE_READ_WRITE 0x0000
#define MTP_STORAGE_READ_ONLY_WITHOUT_DELETE 0x0000
#define MTP_STORAGE_READ_ONLY_WITH_DELETE 0x0000
// Association Type
#define MTP_ASSOCIATION_TYPE_UNDEFINED 0x0000
#define MTP_ASSOCIATION_TYPE_GENERIC_FOLDER 0x0001
#endif // _MTP_H

62
media/mtp/mtptest.cpp Normal file
View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "mtp_usb"
#include "cutils/log.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include "MtpServer.h"
#include "MtpStorage.h"
static void enable_usb_function(const char* name, bool enable) {
char path[PATH_MAX];
snprintf(path, sizeof(path), "/sys/class/usb_composite/%s/enable", name);
int fd = open(path, O_RDWR);
if (fd < 0) {
fprintf(stderr, "could not open %s in enable_usb_function\n", path);
exit(1);
}
write(fd, enable ? "1" : "0", 2);
close(fd);
}
int main() {
// first disable UMS and enable MTP USB functions
enable_usb_function("usb_mass_storage", false);
enable_usb_function("mtp", true);
int fd = open("/dev/mtp_usb", O_RDWR);
printf("open returned %d\n", fd);
if (fd < 0) {
fprintf(stderr, "could not open MTP driver\n");
return -1;
}
MtpServer server(fd, "/data/data/mtp/mtp.db");
server.addStorage("/sdcard");
server.scanStorage();
server.run();
close(fd);
return 0;
}

134
media/mtp/ptptest.cpp Normal file
View File

@@ -0,0 +1,134 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <usbhost/usbhost.h>
#include <linux/usb/ch9.h>
#include "MtpClient.h"
static struct usb_device *sCameraDevice = NULL;
static int sCameraInterface = 0;
static MtpClient *sClient = NULL;
static void start_session(struct usb_endpoint *ep_in, struct usb_endpoint *ep_out,
struct usb_endpoint *ep_intr)
{
if (sClient)
delete sClient;
sClient = new MtpClient(ep_in, ep_out, ep_intr);
sClient->openSession();
sClient->getDeviceInfo();
}
static void usb_device_added(const char *devname)
{
struct usb_descriptor_header* desc;
struct usb_descriptor_iter iter;
struct usb_device *device = usb_device_open(devname);
if (!device) return;
usb_descriptor_iter_init(device, &iter);
while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
if (desc->bDescriptorType == USB_DT_INTERFACE) {
struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
interface->bInterfaceSubClass == 1 && // Still Image Capture
interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470)
{
printf("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
usb_device_get_product_name(device));
// interface should be followed by three endpoints
struct usb_endpoint_descriptor *ep, *ep_in_desc = NULL, *ep_out_desc = NULL, *ep_intr_desc = NULL;
for (int i = 0; i < 3; i++) {
ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
fprintf(stderr, "endpoints not found\n");
return;
}
if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
ep_in_desc = ep;
else
ep_out_desc = ep;
} else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
ep_intr_desc = ep;
}
}
if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
fprintf(stderr, "endpoints not found\n");
return;
}
struct usb_endpoint *ep_in = usb_endpoint_open(device, ep_in_desc);
struct usb_endpoint *ep_out = usb_endpoint_open(device, ep_out_desc);
struct usb_endpoint *ep_intr = usb_endpoint_open(device, ep_intr_desc);
if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
fprintf(stderr, "usb_device_claim_interface failed\n");
usb_endpoint_close(ep_in);
usb_endpoint_close(ep_out);
usb_endpoint_close(ep_intr);
return;
}
if (sCameraDevice) {
usb_device_release_interface(sCameraDevice, sCameraInterface);
usb_device_close(sCameraDevice);
}
sCameraDevice = device;
start_session(ep_in, ep_out, ep_intr);
}
}
}
if (device != sCameraDevice)
usb_device_close(device);
}
static void usb_device_removed(const char *devname)
{
if (sCameraDevice && !strcmp(devname, usb_device_get_name(sCameraDevice))) {
delete sClient;
printf("Camera removed!\n");
usb_device_release_interface(sCameraDevice, sCameraInterface);
usb_device_close(sCameraDevice);
sCameraDevice = NULL;
}
}
int main(int argc, char* argv[])
{
if (usb_host_init(usb_device_added, usb_device_removed)) {
fprintf(stderr, "usb_host_init failed\n");
return -1;
}
while (1) {
sleep(1);
}
return 0;
}