| /* |
| * 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 "MtpDataPacket" |
| |
| #include "MtpDataPacket.h" |
| |
| #include <algorithm> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <usbhost/usbhost.h> |
| #include "MtpStringBuffer.h" |
| #include "IMtpHandle.h" |
| |
| namespace android { |
| |
| namespace { |
| // Reads the exact |count| bytes from |fd| to |buf|. |
| // Returns |count| if it succeed to read the bytes. Otherwise returns -1. If it reaches EOF, the |
| // function regards it as an error. |
| ssize_t readExactBytes(int fd, void* buf, size_t count) { |
| if (count > SSIZE_MAX) { |
| return -1; |
| } |
| size_t read_count = 0; |
| while (read_count < count) { |
| int result = read(fd, static_cast<int8_t*>(buf) + read_count, count - read_count); |
| // Assume that EOF is error. |
| if (result <= 0) { |
| return -1; |
| } |
| read_count += result; |
| } |
| return read_count == count ? count : -1; |
| } |
| } // namespace |
| |
| MtpDataPacket::MtpDataPacket() |
| : MtpPacket(MTP_BUFFER_SIZE), // MAX_USBFS_BUFFER_SIZE |
| 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); |
| } |
| |
| bool MtpDataPacket::getUInt8(uint8_t& value) { |
| if ((mPacketSize - mOffset < sizeof(value)) || (mOffset >= mBufferSize)) |
| return false; |
| value = mBuffer[mOffset++]; |
| return true; |
| } |
| |
| bool MtpDataPacket::getUInt16(uint16_t& value) { |
| if ((mPacketSize - mOffset < sizeof(value)) || ((mOffset+1) >= mBufferSize)) |
| return false; |
| int offset = mOffset; |
| value = (uint16_t)mBuffer[offset] | ((uint16_t)mBuffer[offset + 1] << 8); |
| mOffset += sizeof(value); |
| return true; |
| } |
| |
| bool MtpDataPacket::getUInt32(uint32_t& value) { |
| if ((mPacketSize - mOffset < sizeof(value)) || ((mOffset+3) >= mBufferSize)) |
| return false; |
| int offset = mOffset; |
| value = (uint32_t)mBuffer[offset] | ((uint32_t)mBuffer[offset + 1] << 8) | |
| ((uint32_t)mBuffer[offset + 2] << 16) | ((uint32_t)mBuffer[offset + 3] << 24); |
| mOffset += sizeof(value); |
| return true; |
| } |
| |
| bool MtpDataPacket::getUInt64(uint64_t& value) { |
| if ((mPacketSize - mOffset < sizeof(value)) || ((mOffset+7) >= mBufferSize)) |
| return false; |
| int offset = mOffset; |
| value = (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 += sizeof(value); |
| return true; |
| } |
| |
| bool MtpDataPacket::getUInt128(uint128_t& value) { |
| return getUInt32(value[0]) && getUInt32(value[1]) && getUInt32(value[2]) && getUInt32(value[3]); |
| } |
| |
| bool MtpDataPacket::getString(MtpStringBuffer& string) |
| { |
| return string.readFromPacket(this); |
| } |
| |
| Int8List* MtpDataPacket::getAInt8() { |
| uint32_t count; |
| if (!getUInt32(count)) |
| return NULL; |
| Int8List* result = new Int8List; |
| for (uint32_t i = 0; i < count; i++) { |
| int8_t value; |
| if (!getInt8(value)) { |
| delete result; |
| return NULL; |
| } |
| result->push_back(value); |
| } |
| return result; |
| } |
| |
| UInt8List* MtpDataPacket::getAUInt8() { |
| uint32_t count; |
| if (!getUInt32(count)) |
| return NULL; |
| UInt8List* result = new UInt8List; |
| for (uint32_t i = 0; i < count; i++) { |
| uint8_t value; |
| if (!getUInt8(value)) { |
| delete result; |
| return NULL; |
| } |
| result->push_back(value); |
| } |
| return result; |
| } |
| |
| Int16List* MtpDataPacket::getAInt16() { |
| uint32_t count; |
| if (!getUInt32(count)) |
| return NULL; |
| Int16List* result = new Int16List; |
| for (uint32_t i = 0; i < count; i++) { |
| int16_t value; |
| if (!getInt16(value)) { |
| delete result; |
| return NULL; |
| } |
| result->push_back(value); |
| } |
| return result; |
| } |
| |
| UInt16List* MtpDataPacket::getAUInt16() { |
| uint32_t count; |
| if (!getUInt32(count)) |
| return NULL; |
| UInt16List* result = new UInt16List; |
| for (uint32_t i = 0; i < count; i++) { |
| uint16_t value; |
| if (!getUInt16(value)) { |
| delete result; |
| return NULL; |
| } |
| result->push_back(value); |
| } |
| return result; |
| } |
| |
| Int32List* MtpDataPacket::getAInt32() { |
| uint32_t count; |
| if (!getUInt32(count)) |
| return NULL; |
| Int32List* result = new Int32List; |
| for (uint32_t i = 0; i < count; i++) { |
| int32_t value; |
| if (!getInt32(value)) { |
| delete result; |
| return NULL; |
| } |
| result->push_back(value); |
| } |
| return result; |
| } |
| |
| UInt32List* MtpDataPacket::getAUInt32() { |
| uint32_t count; |
| if (!getUInt32(count)) |
| return NULL; |
| UInt32List* result = new UInt32List; |
| for (uint32_t i = 0; i < count; i++) { |
| uint32_t value; |
| if (!getUInt32(value)) { |
| delete result; |
| return NULL; |
| } |
| result->push_back(value); |
| } |
| return result; |
| } |
| |
| Int64List* MtpDataPacket::getAInt64() { |
| uint32_t count; |
| if (!getUInt32(count)) |
| return NULL; |
| Int64List* result = new Int64List; |
| for (uint32_t i = 0; i < count; i++) { |
| int64_t value; |
| if (!getInt64(value)) { |
| delete result; |
| return NULL; |
| } |
| result->push_back(value); |
| } |
| return result; |
| } |
| |
| UInt64List* MtpDataPacket::getAUInt64() { |
| uint32_t count; |
| if (!getUInt32(count)) |
| return NULL; |
| UInt64List* result = new UInt64List; |
| for (uint32_t i = 0; i < count; i++) { |
| uint64_t value; |
| if (!getUInt64(value)) { |
| delete result; |
| return NULL; |
| } |
| result->push_back(value); |
| } |
| return result; |
| } |
| |
| 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::putInt128(const int128_t& value) { |
| putInt32(value[0]); |
| putInt32(value[1]); |
| putInt32(value[2]); |
| putInt32(value[3]); |
| } |
| |
| void MtpDataPacket::putUInt128(const uint128_t& value) { |
| putUInt32(value[0]); |
| putUInt32(value[1]); |
| putUInt32(value[2]); |
| putUInt32(value[3]); |
| } |
| |
| void MtpDataPacket::putInt128(int64_t value) { |
| putInt64(value); |
| putInt64(value < 0 ? -1 : 0); |
| } |
| |
| void MtpDataPacket::putUInt128(uint64_t value) { |
| putUInt64(value); |
| putUInt64(0); |
| } |
| |
| 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::putAUInt16(const UInt16List* values) { |
| size_t count = (values ? values->size() : 0); |
| putUInt32(count); |
| for (size_t i = 0; i < count; i++) |
| putUInt16((*values)[i]); |
| } |
| |
| 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) { |
| if (s != NULL) { |
| MtpStringBuffer string(s); |
| string.writeToPacket(this); |
| } |
| } |
| |
| void MtpDataPacket::putString(const uint16_t* string) { |
| if (string == NULL) { |
| return; |
| } |
| int count = 0; |
| for (int i = 0; i <= MTP_STRING_MAX_CHARACTER_NUMBER; i++) { |
| if (string[i]) |
| count++; |
| else |
| break; |
| } |
| putUInt8(count > 0 ? count + 1 : 0); |
| for (int i = 0; i < count; i++) |
| putUInt16(string[i]); |
| // only terminate with zero if string is not empty |
| if (count > 0) |
| putUInt16(0); |
| } |
| |
| #ifdef MTP_DEVICE |
| int MtpDataPacket::read(IMtpHandle *h) { |
| int ret = h->read(mBuffer, MTP_BUFFER_SIZE); |
| if (ret < MTP_CONTAINER_HEADER_SIZE) |
| return -1; |
| mPacketSize = ret; |
| mOffset = MTP_CONTAINER_HEADER_SIZE; |
| return ret; |
| } |
| |
| int MtpDataPacket::write(IMtpHandle *h) { |
| MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize); |
| MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA); |
| int ret = h->write(mBuffer, mPacketSize); |
| return (ret < 0 ? ret : 0); |
| } |
| |
| int MtpDataPacket::writeData(IMtpHandle *h, void* data, uint32_t length) { |
| allocate(length + MTP_CONTAINER_HEADER_SIZE); |
| memcpy(mBuffer + MTP_CONTAINER_HEADER_SIZE, data, length); |
| length += MTP_CONTAINER_HEADER_SIZE; |
| MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length); |
| MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA); |
| int ret = h->write(mBuffer, length); |
| return (ret < 0 ? ret : 0); |
| } |
| |
| #endif // MTP_DEVICE |
| |
| #ifdef MTP_HOST |
| int MtpDataPacket::read(struct usb_request *request) { |
| // first read the header |
| request->buffer = mBuffer; |
| request->buffer_length = mBufferSize; |
| int length = transfer(request); |
| if (length >= MTP_CONTAINER_HEADER_SIZE) { |
| // look at the length field to see if the data spans multiple packets |
| uint32_t totalLength = MtpPacket::getUInt32(MTP_CONTAINER_LENGTH_OFFSET); |
| allocate(totalLength); |
| while (totalLength > static_cast<uint32_t>(length)) { |
| request->buffer = mBuffer + length; |
| request->buffer_length = totalLength - length; |
| int ret = transfer(request); |
| if (ret >= 0) |
| length += ret; |
| else { |
| length = ret; |
| break; |
| } |
| } |
| } |
| if (length >= 0) |
| mPacketSize = length; |
| return length; |
| } |
| |
| int MtpDataPacket::readData(struct usb_request *request, void* buffer, int length) { |
| int read = 0; |
| while (read < length) { |
| request->buffer = (char *)buffer + read; |
| request->buffer_length = length - read; |
| int ret = transfer(request); |
| if (ret < 0) { |
| return ret; |
| } |
| read += ret; |
| } |
| return read; |
| } |
| |
| // Queue a read request. Call readDataWait to wait for result |
| int MtpDataPacket::readDataAsync(struct usb_request *req) { |
| if (usb_request_queue(req)) { |
| ALOGE("usb_endpoint_queue failed, errno: %d", errno); |
| return -1; |
| } |
| return 0; |
| } |
| |
| // Wait for result of readDataAsync |
| int MtpDataPacket::readDataWait(struct usb_device *device) { |
| struct usb_request *req = usb_request_wait(device, -1); |
| return (req ? req->actual_length : -1); |
| } |
| |
| int MtpDataPacket::readDataHeader(struct usb_request *request) { |
| request->buffer = mBuffer; |
| request->buffer_length = request->max_packet_size; |
| int length = transfer(request); |
| if (length >= 0) |
| mPacketSize = length; |
| return length; |
| } |
| |
| int MtpDataPacket::write(struct usb_request *request, UrbPacketDivisionMode divisionMode) { |
| if (mPacketSize < MTP_CONTAINER_HEADER_SIZE || mPacketSize > MTP_BUFFER_SIZE) { |
| ALOGE("Illegal packet size."); |
| return -1; |
| } |
| |
| MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize); |
| MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA); |
| |
| size_t processedBytes = 0; |
| while (processedBytes < mPacketSize) { |
| const size_t write_size = |
| processedBytes == 0 && divisionMode == FIRST_PACKET_ONLY_HEADER ? |
| MTP_CONTAINER_HEADER_SIZE : mPacketSize - processedBytes; |
| request->buffer = mBuffer + processedBytes; |
| request->buffer_length = write_size; |
| const int result = transfer(request); |
| if (result < 0) { |
| ALOGE("Failed to write bytes to the device."); |
| return -1; |
| } |
| processedBytes += result; |
| } |
| |
| return processedBytes == mPacketSize ? processedBytes : -1; |
| } |
| |
| int64_t MtpDataPacket::write(struct usb_request *request, |
| UrbPacketDivisionMode divisionMode, |
| int fd, |
| size_t payloadSize) { |
| // Obtain the greatest multiple of minimum packet size that is not greater than |
| // MTP_BUFFER_SIZE. |
| if (request->max_packet_size <= 0) { |
| ALOGE("Cannot determine bulk transfer size due to illegal max packet size %d.", |
| request->max_packet_size); |
| return -1; |
| } |
| const size_t maxBulkTransferSize = |
| MTP_BUFFER_SIZE - (MTP_BUFFER_SIZE % request->max_packet_size); |
| const size_t containerLength = payloadSize + MTP_CONTAINER_HEADER_SIZE; |
| size_t processedBytes = 0; |
| bool readError = false; |
| |
| // Bind the packet with given request. |
| request->buffer = mBuffer; |
| allocate(maxBulkTransferSize); |
| |
| while (processedBytes < containerLength) { |
| size_t bulkTransferSize = 0; |
| |
| // prepare header. |
| const bool headerSent = processedBytes != 0; |
| if (!headerSent) { |
| MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, containerLength); |
| MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA); |
| bulkTransferSize += MTP_CONTAINER_HEADER_SIZE; |
| } |
| |
| // Prepare payload. |
| if (headerSent || divisionMode == FIRST_PACKET_HAS_PAYLOAD) { |
| const size_t processedPayloadBytes = |
| headerSent ? processedBytes - MTP_CONTAINER_HEADER_SIZE : 0; |
| const size_t maxRead = payloadSize - processedPayloadBytes; |
| const size_t maxWrite = maxBulkTransferSize - bulkTransferSize; |
| const size_t bulkTransferPayloadSize = std::min(maxRead, maxWrite); |
| // prepare payload. |
| if (!readError) { |
| const ssize_t result = readExactBytes( |
| fd, |
| mBuffer + bulkTransferSize, |
| bulkTransferPayloadSize); |
| if (result < 0) { |
| ALOGE("Found an error while reading data from FD. Send 0 data instead."); |
| readError = true; |
| } |
| } |
| if (readError) { |
| memset(mBuffer + bulkTransferSize, 0, bulkTransferPayloadSize); |
| } |
| bulkTransferSize += bulkTransferPayloadSize; |
| } |
| |
| // Bulk transfer. |
| mPacketSize = bulkTransferSize; |
| request->buffer_length = bulkTransferSize; |
| const int result = transfer(request); |
| if (result != static_cast<ssize_t>(bulkTransferSize)) { |
| // Cannot recover writing error. |
| ALOGE("Found an error while write data to MtpDevice."); |
| return -1; |
| } |
| |
| // Update variables. |
| processedBytes += bulkTransferSize; |
| } |
| |
| return readError ? -1 : processedBytes; |
| } |
| |
| #endif // MTP_HOST |
| |
| void* MtpDataPacket::getData(int* outLength) const { |
| int length = mPacketSize - MTP_CONTAINER_HEADER_SIZE; |
| if (length > 0) { |
| void* result = malloc(length); |
| if (result) { |
| memcpy(result, mBuffer + MTP_CONTAINER_HEADER_SIZE, length); |
| *outLength = length; |
| return result; |
| } |
| } |
| *outLength = 0; |
| return NULL; |
| } |
| |
| } // namespace android |