1 /* 2 This file is part of Android File Transfer For Linux. 3 Copyright (C) 2015-2020 Vladimir Menshakov 4 5 This library is free software; you can redistribute it and/or modify it 6 under the terms of the GNU Lesser General Public License as published by 7 the Free Software Foundation; either version 2.1 of the License, 8 or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public License 16 along with this library; if not, write to the Free Software Foundation, 17 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 */ 19 20 #include <mtp/ptp/PipePacketer.h> 21 #include <mtp/ptp/Response.h> 22 #include <mtp/ptp/EventCode.h> 23 #include <mtp/ptp/InputStream.h> 24 #include <mtp/ptp/ByteArrayObjectStream.h> 25 #include <mtp/ptp/JoinedObjectStream.h> 26 #include <mtp/ptp/OutputStream.h> 27 #include <mtp/ptp/OperationRequest.h> 28 #include <mtp/usb/Request.h> 29 #include <usb/Device.h> 30 #include <usb/Interface.h> 31 #include <mtp/log.h> 32 33 34 namespace mtp 35 { 36 Write(const IObjectInputStreamPtr & inputStream,int timeout)37 void PipePacketer::Write(const IObjectInputStreamPtr &inputStream, int timeout) 38 { _pipe->Write(inputStream, timeout); } 39 Write(const ByteArray & data,int timeout)40 void PipePacketer::Write(const ByteArray &data, int timeout) 41 { Write(std::make_shared<ByteArrayObjectInputStream>(data), timeout); } 42 43 namespace 44 { 45 class MessageParsingStream final: public JoinedObjectOutputStreamBase 46 { 47 FixedSizeByteArrayObjectOutputStreamPtr _header; 48 IObjectOutputStreamPtr _stream; 49 u64 _offset; 50 u64 _size; 51 52 private: GetStream1() const53 IObjectOutputStreamPtr GetStream1() const override 54 { return _header; } 55 GetStream2() const56 IObjectOutputStreamPtr GetStream2() const override 57 { return _stream; } 58 59 public: MessageParsingStream(IObjectOutputStreamPtr stream)60 MessageParsingStream(IObjectOutputStreamPtr stream): 61 _header(new FixedSizeByteArrayObjectOutputStream(4)), 62 _stream(stream), _offset(0), _size(4) 63 { } 64 Finished() const65 bool Finished() const 66 { return _offset >= _size; } 67 OnStream1Exhausted()68 void OnStream1Exhausted() override 69 { 70 InputStream is(_header->GetData()); 71 u32 size; 72 is >> size; 73 if (size < 4) 74 throw std::runtime_error("invalid size/malformed message"); 75 _size = size; 76 } 77 Write(const u8 * data,size_t size)78 size_t Write(const u8 *data, size_t size) override 79 { 80 size_t r = JoinedObjectOutputStreamBase::Write(data, size); 81 _offset += r; 82 if (!_stream1Exhausted && _offset >= _header->GetData().size()) { 83 _stream1Exhausted = true; 84 OnStream1Exhausted(); 85 } 86 return r; 87 } 88 }; 89 DECLARE_PTR(MessageParsingStream); 90 } 91 ReadMessage(const IObjectOutputStreamPtr & outputStream,int timeout)92 void PipePacketer::ReadMessage(const IObjectOutputStreamPtr &outputStream, int timeout) 93 { _pipe->Read(outputStream, timeout); } 94 PollEvent(int timeout)95 void PipePacketer::PollEvent(int timeout) 96 { 97 ByteArray interruptData = _pipe->ReadInterrupt(timeout); 98 if (interruptData.empty()) 99 return; 100 101 HexDump("interrupt", interruptData); 102 InputStream stream(interruptData); 103 ContainerType containerType; 104 u32 size; 105 EventCode eventCode; 106 u32 sessionId; 107 u32 transactionId; 108 stream >> size; 109 stream >> containerType; 110 stream >> eventCode; 111 stream >> sessionId; 112 stream >> transactionId; 113 if (containerType != ContainerType::Event) 114 throw std::runtime_error("not an event"); 115 debug("event ", hex(eventCode, 8)); 116 } 117 118 namespace 119 { 120 struct DummyOutputStream final: IObjectOutputStream, public CancellableStream 121 { Writemtp::__anon0f4940360211::DummyOutputStream122 size_t Write(const u8 *data, size_t size) override 123 { return size; } 124 }; 125 126 class HeaderParserObjectOutputStream final: public JoinedObjectOutputStreamBase 127 { 128 size_t _offset; 129 u32 _transaction; 130 FixedSizeByteArrayObjectOutputStreamPtr _header; 131 ByteArrayObjectOutputStreamPtr _response; 132 IObjectOutputStreamPtr _dataOutput; 133 IObjectOutputStreamPtr _output; 134 bool _valid; 135 bool _finished; 136 ResponseType _responseCode; 137 138 private: GetStream1() const139 virtual IObjectOutputStreamPtr GetStream1() const override 140 { return _header; } GetStream2() const141 virtual IObjectOutputStreamPtr GetStream2() const override 142 { if (!_output) throw std::runtime_error("no data stream"); return _output; } 143 OnStream1Exhausted()144 void OnStream1Exhausted() override 145 { 146 InputStream stream(_header->GetData()); 147 Response header; 148 header.Read(stream); 149 if (_transaction && _transaction != header.Transaction) 150 { 151 error("drop message ", hex(header.ContainerType, 4), ", response: ", hex(header.ResponseType, 4), ", transaction: ", hex(header.Transaction, 8), ", transaction: ", hex(_transaction, 8)); 152 _valid = false; 153 _output = std::make_shared<DummyOutputStream>(); 154 return; 155 } 156 157 switch(header.ContainerType) 158 { 159 case ContainerType::Data: 160 _output = _dataOutput; 161 break; 162 case ContainerType::Response: 163 _output = _response; 164 _responseCode = header.ResponseType; 165 _finished = true; 166 break; 167 default: 168 _valid = false; 169 _output = std::make_shared<DummyOutputStream>(); 170 } 171 } 172 173 public: HeaderParserObjectOutputStream(u32 transaction,IObjectOutputStreamPtr dataOutput)174 HeaderParserObjectOutputStream(u32 transaction, IObjectOutputStreamPtr dataOutput): 175 _offset(0), _transaction(transaction), 176 _header(new FixedSizeByteArrayObjectOutputStream(Response::Size)), 177 _response(new ByteArrayObjectOutputStream), 178 _dataOutput(dataOutput), 179 _valid(true), _finished(false) { } 180 GetResponseCode() const181 ResponseType GetResponseCode() const 182 { return _responseCode; } 183 GetResponse() const184 const ByteArray &GetResponse() const 185 { return _response->GetData(); } 186 Valid() const187 bool Valid() const 188 { return _valid; } Finished() const189 bool Finished() const 190 { return _finished; } 191 Write(const u8 * data,size_t size)192 size_t Write(const u8 *data, size_t size) override 193 { 194 size_t r = JoinedObjectOutputStreamBase::Write(data, size); 195 _offset += r; 196 if (!_stream1Exhausted && _offset >= _header->GetData().size()) { 197 _stream1Exhausted = true; 198 OnStream1Exhausted(); 199 } 200 return r; 201 } 202 }; 203 DECLARE_PTR(HeaderParserObjectOutputStream); 204 } 205 Read(u32 transaction,const IObjectOutputStreamPtr & object,ResponseType & code,ByteArray & response,int timeout)206 void PipePacketer::Read(u32 transaction, const IObjectOutputStreamPtr &object, ResponseType &code, ByteArray &response, int timeout) 207 { 208 response.clear(); 209 210 HeaderParserObjectOutputStreamPtr parser; 211 MessageParsingStreamPtr output; 212 213 while(true) 214 { 215 if (!parser) 216 parser.reset(new HeaderParserObjectOutputStream(transaction, object)); 217 if (!output) 218 output.reset(new MessageParsingStream(parser)); 219 220 ReadMessage(output, timeout); 221 if (parser->Finished()) 222 { 223 response = parser->GetResponse(); 224 code = parser->GetResponseCode(); 225 break; 226 } 227 228 if (output->Finished()) { 229 parser.reset(); 230 output.reset(); 231 } 232 } 233 234 //HexDump("response", response); 235 } 236 Read(u32 transaction,ByteArray & data,ResponseType & code,ByteArray & response,int timeout)237 void PipePacketer::Read(u32 transaction, ByteArray &data, ResponseType &code, ByteArray &response, int timeout) 238 { 239 ByteArrayObjectOutputStreamPtr stream(new ByteArrayObjectOutputStream); 240 Read(transaction, stream, code, response, timeout); 241 data = stream->GetData(); 242 } 243 Abort(u32 transaction,int timeout)244 void PipePacketer::Abort(u32 transaction, int timeout) 245 { 246 _pipe->Cancel(); 247 OperationRequest req(OperationCode::CancelTransaction, transaction); 248 HexDump("abort control message", req.Data); 249 /* 0x21: host-to-device, class specific, recipient - interface, 0x64: cancel request */ 250 _pipe->GetDevice()->WriteControl( 251 (u8)(usb::RequestType::HostToDevice | usb::RequestType::Class | usb::RequestType::Interface), 252 0x64, 253 0, _pipe->GetInterface()->GetIndex(), req.Data, timeout); 254 } 255 256 257 } 258