diff options
author | Mike Lockwood <lockwood@android.com> | 2010-05-11 17:16:59 -0400 |
---|---|---|
committer | Mike Lockwood <lockwood@android.com> | 2010-05-13 15:36:47 -0400 |
commit | 56118b5b385adad0ff301f67be2cf601432d94ba (patch) | |
tree | 80002c10be6033927fd94799954c0d4394bc7bde | |
parent | f6860f1979b30646e57d41c214b3a60e401abcd1 (diff) | |
download | frameworks_base-56118b5b385adad0ff301f67be2cf601432d94ba.zip frameworks_base-56118b5b385adad0ff301f67be2cf601432d94ba.tar.gz frameworks_base-56118b5b385adad0ff301f67be2cf601432d94ba.tar.bz2 |
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>
31 files changed, 3656 insertions, 0 deletions
diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk new file mode 100644 index 0000000..b9da15f --- /dev/null +++ b/media/mtp/Android.mk @@ -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
\ No newline at end of file diff --git a/media/mtp/MtpClient.cpp b/media/mtp/MtpClient.cpp new file mode 100644 index 0000000..5e4adb4 --- /dev/null +++ b/media/mtp/MtpClient.cpp @@ -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; + } +} + diff --git a/media/mtp/MtpClient.h b/media/mtp/MtpClient.h new file mode 100644 index 0000000..fbbd388 --- /dev/null +++ b/media/mtp/MtpClient.h @@ -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 diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp new file mode 100644 index 0000000..8e7ea6e --- /dev/null +++ b/media/mtp/MtpDataPacket.cpp @@ -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 diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h new file mode 100644 index 0000000..3b18e4e --- /dev/null +++ b/media/mtp/MtpDataPacket.h @@ -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 diff --git a/media/mtp/MtpDatabase.cpp b/media/mtp/MtpDatabase.cpp new file mode 100644 index 0000000..bb44ab6 --- /dev/null +++ b/media/mtp/MtpDatabase.cpp @@ -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 +*/ diff --git a/media/mtp/MtpDatabase.h b/media/mtp/MtpDatabase.h new file mode 100644 index 0000000..66f70bd --- /dev/null +++ b/media/mtp/MtpDatabase.h @@ -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 diff --git a/media/mtp/MtpDebug.cpp b/media/mtp/MtpDebug.cpp new file mode 100644 index 0000000..6d0273d --- /dev/null +++ b/media/mtp/MtpDebug.cpp @@ -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 ***"; +} diff --git a/media/mtp/MtpDebug.h b/media/mtp/MtpDebug.h new file mode 100644 index 0000000..bab79e9 --- /dev/null +++ b/media/mtp/MtpDebug.h @@ -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 diff --git a/media/mtp/MtpPacket.cpp b/media/mtp/MtpPacket.cpp new file mode 100644 index 0000000..26a9460 --- /dev/null +++ b/media/mtp/MtpPacket.cpp @@ -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 diff --git a/media/mtp/MtpPacket.h b/media/mtp/MtpPacket.h new file mode 100644 index 0000000..26a3c24 --- /dev/null +++ b/media/mtp/MtpPacket.h @@ -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 diff --git a/media/mtp/MtpRequestPacket.cpp b/media/mtp/MtpRequestPacket.cpp new file mode 100644 index 0000000..ed4cc9d --- /dev/null +++ b/media/mtp/MtpRequestPacket.cpp @@ -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 diff --git a/media/mtp/MtpRequestPacket.h b/media/mtp/MtpRequestPacket.h new file mode 100644 index 0000000..d44d1dc --- /dev/null +++ b/media/mtp/MtpRequestPacket.h @@ -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 diff --git a/media/mtp/MtpResponsePacket.cpp b/media/mtp/MtpResponsePacket.cpp new file mode 100644 index 0000000..6ebac9e --- /dev/null +++ b/media/mtp/MtpResponsePacket.cpp @@ -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 + + diff --git a/media/mtp/MtpResponsePacket.h b/media/mtp/MtpResponsePacket.h new file mode 100644 index 0000000..84c3024 --- /dev/null +++ b/media/mtp/MtpResponsePacket.h @@ -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 diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp new file mode 100644 index 0000000..57b84ac --- /dev/null +++ b/media/mtp/MtpServer.cpp @@ -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; +} diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h new file mode 100644 index 0000000..81b1c81 --- /dev/null +++ b/media/mtp/MtpServer.h @@ -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 diff --git a/media/mtp/MtpStorage.cpp b/media/mtp/MtpStorage.cpp new file mode 100644 index 0000000..bbdef51 --- /dev/null +++ b/media/mtp/MtpStorage.cpp @@ -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; +} diff --git a/media/mtp/MtpStorage.h b/media/mtp/MtpStorage.h new file mode 100644 index 0000000..011cb81 --- /dev/null +++ b/media/mtp/MtpStorage.h @@ -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 diff --git a/media/mtp/MtpStringBuffer.cpp b/media/mtp/MtpStringBuffer.cpp new file mode 100644 index 0000000..6b55c44 --- /dev/null +++ b/media/mtp/MtpStringBuffer.cpp @@ -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); + } +} diff --git a/media/mtp/MtpStringBuffer.h b/media/mtp/MtpStringBuffer.h new file mode 100644 index 0000000..46138d2 --- /dev/null +++ b/media/mtp/MtpStringBuffer.h @@ -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 diff --git a/media/mtp/MtpUtils.cpp b/media/mtp/MtpUtils.cpp new file mode 100644 index 0000000..a472781 --- /dev/null +++ b/media/mtp/MtpUtils.cpp @@ -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); +} + + diff --git a/media/mtp/MtpUtils.h b/media/mtp/MtpUtils.h new file mode 100644 index 0000000..2e80762 --- /dev/null +++ b/media/mtp/MtpUtils.h @@ -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 diff --git a/media/mtp/SqliteDatabase.cpp b/media/mtp/SqliteDatabase.cpp new file mode 100644 index 0000000..5a70dfe --- /dev/null +++ b/media/mtp/SqliteDatabase.cpp @@ -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); +} diff --git a/media/mtp/SqliteDatabase.h b/media/mtp/SqliteDatabase.h new file mode 100644 index 0000000..841b2b7 --- /dev/null +++ b/media/mtp/SqliteDatabase.h @@ -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 diff --git a/media/mtp/SqliteStatement.cpp b/media/mtp/SqliteStatement.cpp new file mode 100644 index 0000000..e568928 --- /dev/null +++ b/media/mtp/SqliteStatement.cpp @@ -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); +} diff --git a/media/mtp/SqliteStatement.h b/media/mtp/SqliteStatement.h new file mode 100644 index 0000000..0fd8a1e --- /dev/null +++ b/media/mtp/SqliteStatement.h @@ -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 diff --git a/media/mtp/f_mtp.h b/media/mtp/f_mtp.h new file mode 100644 index 0000000..7307d74 --- /dev/null +++ b/media/mtp/f_mtp.h @@ -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 */ diff --git a/media/mtp/mtp.h b/media/mtp/mtp.h new file mode 100644 index 0000000..27abaa7 --- /dev/null +++ b/media/mtp/mtp.h @@ -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 diff --git a/media/mtp/mtptest.cpp b/media/mtp/mtptest.cpp new file mode 100644 index 0000000..8996458 --- /dev/null +++ b/media/mtp/mtptest.cpp @@ -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; +} + diff --git a/media/mtp/ptptest.cpp b/media/mtp/ptptest.cpp new file mode 100644 index 0000000..1218fa6e6 --- /dev/null +++ b/media/mtp/ptptest.cpp @@ -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; +}
\ No newline at end of file |