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