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

Returns a new instance of 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

Returns:

  • (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

Returns:

  • (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