Class: MTP::Protocol

Inherits:
Object
  • Object
show all
Defined in:
lib/mtp/protocol.rb

Defined Under Namespace

Classes: EndPoints, Transaction

Constant Summary collapse

@@logger =
Logger.new(STDERR)

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(device, usb_device) ⇒ Protocol



135
136
137
138
139
140
141
142
143
# File 'lib/mtp/protocol.rb', line 135

def initialize(device, usb_device)
  @device = device
  @transaction_id = 0
  @split_data_packets = false
  @usb_handle = nil
  @usb_device = usb_device
  @timeout = 10000
  logger.info("new device #{@usb_device.product}")
end

Instance Attribute Details

#deviceObject (readonly)

Returns the value of attribute device.



134
135
136
# File 'lib/mtp/protocol.rb', line 134

def device
  @device
end

Class Method Details

.loggerObject



9
10
11
# File 'lib/mtp/protocol.rb', line 9

def self.logger
  @@logger
end

.mtp?(usb_device, timeout = 1000) ⇒ Boolean



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/mtp/protocol.rb', line 99

def self.mtp?(usb_device, timeout = 1000)
  ret = true
  usb_device.open do |usb|
    # check for MS OS descriptors
    mod = usb.get_string_simple(238)
    
    if mod.nil? or mod[0,4] != "MSFT"
      logger.debug("Microsoft OS Descriptor invalid")
      ret = false 
      break
    end
    bVendorCode = mod[7]
    result = "\0" * 0x20
    expd = usb.usb_control_msg(USB::USB_ENDPOINT_IN|USB::USB_RECIP_DEVICE|USB::USB_TYPE_VENDOR, bVendorCode, 0, 4, result, timeout)
    unless expd > 0x15 and result[0x12,3] == "MTP"
      logger.debug("Microsoft Extended Descriptor invalid")
      ret = false
      break
    end
    
    result = "\0" * 0x20
    expd = usb.usb_control_msg(USB::USB_ENDPOINT_IN|USB::USB_RECIP_DEVICE|USB::USB_TYPE_VENDOR, bVendorCode, 0, 5, result, timeout)
    unless expd > 0x15 and result[0x12,3] == "MTP"
      logger.debug("Microsoft Extended Descriptor invalid")
      ret = false
      break
    end
  end
  ret
end

Instance Method Details

#closeObject



285
286
287
288
289
290
291
# File 'lib/mtp/protocol.rb', line 285

def close
  logger.debug("low level close")
  reset_end_points
  @usb_handle.release_interface(interface.settings.first)
  @usb_handle.usb_reset
  @usb_handle.usb_close
end

#configurationObject



149
150
151
# File 'lib/mtp/protocol.rb', line 149

def configuration
  @usb_device.configurations.first
end

#info {|data| ... } ⇒ Object

do not use normal procedure as we need to determine if we use splitted header / data

Yields:

  • (data)

Raises:



249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/mtp/protocol.rb', line 249

def info
  request = Request.for("GetDeviceInfo")
  request.transaction_id = 0
  send(request)
  data = receive

  raise CommandError.new(self, request, data, "expected a data packet") unless data.is_a?(Data)
  
  if data.payload.empty?
    logger.debug("device split packet headers from payload")
    @split_data_packets = true 
    data.payload = raw_read
  end
  response = receive
  yield data if block_given?
end

#interfaceObject



153
154
155
# File 'lib/mtp/protocol.rb', line 153

def interface
  @usb_device.interfaces.first
end

#loggerObject



130
131
132
# File 'lib/mtp/protocol.rb', line 130

def logger
  Protocol.logger
end

#openObject



266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/mtp/protocol.rb', line 266

def open
  logger.debug("low level open")
  @usb_handle = @usb_device.usb_open
  begin
    @usb_handle.set_configuration(configuration)
    @usb_handle.claim_interface(interface.settings.first)
    @end_points = EndPoints.new(self)
  rescue Exception => e
    close
  end
  self
end

#raw_readObject



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/mtp/protocol.rb', line 204

def raw_read
  read = packet_size = @end_points.in.wMaxPacketSize
  raw_packet = ""
  
  # packet is received when we receive an emtpy packet
  # or a packet whose size is smaller than the packet size
  while read == packet_size
    buffer = "\0" * packet_size
    read = @usb_handle.usb_bulk_read(@end_points.in.bEndpointAddress, buffer, @timeout)
    if read.zero?
      logger.warn("packet match max packet size, need to send NULL packet")
    end
    raw_packet << buffer[0, read]
  end
  raw_packet
end

#raw_read_io(io, size, &block) ⇒ Object



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/mtp/protocol.rb', line 221

def raw_read_io(io, size, &block)
  read = packet_size = @end_points.in.wMaxPacketSize

  total = 0
  # packet is received when we receive an emtpy packet
  # or a packet whose size is smaller than the packet size
  while read == packet_size
    buffer = "\0" * packet_size
    read = @usb_handle.usb_bulk_read(@end_points.in.bEndpointAddress, buffer, @timeout)
    if read.zero?
      logger.warn("packet match max packet size, need to send NULL packet")
    end
    io.write(buffer[0, read])
    total += read
    yield total, size if block_given?
  end
end

#raw_send(raw_packet, &block) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/mtp/protocol.rb', line 163

def raw_send(raw_packet, &block)
  raw_packet = raw_packet.clone
  if (raw_packet.length % @end_points.out.wMaxPacketSize).zero?
    send_empty_packet = true
    logger.warn("packet match max packet size, need to send NULL packet")
  end
  written, total = 0, raw_packet.length
  until raw_packet.empty?
    if (wrt = @usb_handle.usb_bulk_write(@end_points.out.bEndpointAddress, 
                                  raw_packet.slice!(0, @end_points.out.wMaxPacketSize), @timeout)) < 0
      raise WriteError.new(self)
    end
    written += wrt
    yield written, total if block_given?
  end
end

#raw_send_io(io, size, &block) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
# File 'lib/mtp/protocol.rb', line 180

def raw_send_io(io, size, &block)
  packet_size = @end_points.out.wMaxPacketSize
  written = 0
  until (str = io.read(packet_size)).nil?
    if (wrt = @usb_handle.usb_bulk_write(@end_points.out.bEndpointAddress, str, @timeout)) < 0
      raise WriteError.new(self)
    end
    written += wrt
    yield written, size if block_given?
  end
end

#receiveObject



239
240
241
242
243
244
245
# File 'lib/mtp/protocol.rb', line 239

def receive
  raw_packet = raw_read
  response = Container.parse(raw_packet)
  logger.debug(sprintf("<= %-8s: 0x%08x, 0x%04x - %s", (response.is_a?(Data) ? "DATA" : "RESPONSE"), 
                           response.transaction_id, response.code, response.code.name))
  response
end

#reset_end_pointsObject



279
280
281
282
283
# File 'lib/mtp/protocol.rb', line 279

def reset_end_points
  @usb_handle.usb_clear_halt(@end_points.out.bEndpointAddress)
  @usb_handle.usb_clear_halt(@end_points.in.bEndpointAddress)
  @usb_handle.usb_clear_halt(@end_points.interrupt.bEndpointAddress)
end

#send(request) ⇒ Object



192
193
194
195
196
197
198
199
200
201
202
# File 'lib/mtp/protocol.rb', line 192

def send(request)
  unless request.is_a?(Data) or request.code.name == "GetDeviceInfo"
    raise UnsupportedRequest.new(self, request) unless @device.support?(request.code)
    request.transaction_id = transaction_id 
  end
  
  raw_send(request.pack)

  logger.debug(sprintf("=> %-8s: 0x%08x, 0x%04x - %s", (request.is_a?(Data) ? "DATA" : "REQUEST"), request.transaction_id, request.code, request.code.name))
  request
end

#split_data_packets?Boolean



145
146
147
# File 'lib/mtp/protocol.rb', line 145

def split_data_packets?
  @split_data_packets
end

#transactionObject



293
294
295
# File 'lib/mtp/protocol.rb', line 293

def transaction
  Transaction.new(self)
end

#transaction_idObject



157
158
159
160
161
# File 'lib/mtp/protocol.rb', line 157

def transaction_id
  tid = @transaction_id
  @transaction_id = (tid % 0xffffffff) + 1
  tid
end