<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>day50 &amp;mdash; Gerrit Niezen</title>
    <link>https://gerritniezen.com/tag:day50</link>
    <description>Maker of open-source software and hardware.</description>
    <pubDate>Thu, 30 Apr 2026 03:35:08 +0000</pubDate>
    <image>
      <url>https://i.snap.as/aMPXpIot.png</url>
      <title>day50 &amp;mdash; Gerrit Niezen</title>
      <link>https://gerritniezen.com/tag:day50</link>
    </image>
    <item>
      <title>Taking a deep dive into MTP: Part 2</title>
      <link>https://gerritniezen.com/taking-a-deep-dive-into-mtp-part-2?pk_campaign=rss-feed</link>
      <description>&lt;![CDATA[Last time I started looking at how to implement a subset of the MTP specification, in order to read files from Android devices. Let&#39;s start by writing some code to talk to the device over WebUSB:&#xA;&#xA;const device = await usb.requestDevice({&#xA;  filters: [&#xA;    {&#xA;      vendorId: 3725,&#xA;      productId: 8221,&#xA;    }&#xA;  ]&#xA;});&#xA;&#xA;await device.open();&#xA;&#xA;if (device.configuration === null) {&#xA;  console.log(&#39;selectConfiguration&#39;);&#xA;  await device.selectConfiguration(1);&#xA;}&#xA;await device.claimInterface(0);&#xA;MTP uses containers to structure what the various commands and responses look like. Here is how to build a container packet (and note that struct.pack() is part of a convenience library):&#xA;&#xA;const buildContainerPacket = (container, payloadLength) =  {&#xA;  const packetLength = 12 + payloadLength;&#xA;  const buf = new ArrayBuffer(packetLength);&#xA;  const bytes = new Uint8Array(buf);&#xA;  let ctr = struct.pack(bytes, 0, &#39;issi&#39;, packetLength, container.type, container.code, container.transactionID);&#xA;  if (payloadLength   0) {&#xA;    ctr += struct.copyBytes(bytes, ctr, container.payload, payloadLength);&#xA;  }&#xA;&#xA;  return buf;&#xA;};&#xA;&#xA;To open a new session, we fill the container object with the right info and send it off via the bulk pipe:&#xA;&#xA;  const openSession = {&#xA;    type: 1, // command block&#xA;    code: 0x1002, // open session&#xA;    transactionID: 0,&#xA;    payload: 1, // session ID&#xA;  };&#xA;  let data = buildContainerPacket(openSession, 4);&#xA;  let result = await device.transferOut(0x01, data);&#xA;&#xA;To read the response, I&#39;m creating a receiveData() function:&#xA;&#xA;  const receiveData = async () =  {&#xA;    const timeoutID = setTimeout(async() =  {&#xA;      console.warn(&#39;Device not connected&#39;);&#xA;    }, 5000);&#xA;&#xA;    console.log(&#39;Receiving...&#39;);&#xA;&#xA;    let incoming = await device.transferIn(0x01, 1024);&#xA;&#xA;    if (incoming.data.byteLength   0) {&#xA;      clearTimeout(timeoutID);&#xA;&#xA;      const [length] = new Uint32Array(incoming.data.buffer, 0, 1);&#xA;      console.log(&#39;Length:&#39;, length);&#xA;&#xA;      // TODO: if length !== incoming.data.byteLength, read more data&#xA;    }&#xA;  };&#xA;&#xA;To actually receive some data, we call &#xA;&#xA;await receiveData();&#xA;&#xA;and then parse the response (0c 00 00 00 03 00 01 20 01 00 00 00), which is also in the same container format:&#xA;&#xA;Length: 0x0C000000 - 12 (in little-endian format)&#xA;Type: 0x0300 - Response block&#xA;0x0120, or 0x2001 in big-endian - response code OK&#xA;0x0000000000 - transaction ID&#xA;&#xA;So, we&#39;re able to successfully open a session!&#xA;&#xA;PS: Hey, I&#39;m halfway through #100DaysToOffload already! 🎉️&#xA;&#xA;---&#xA;I’m publishing this as part of 100 Days To Offload. You can join in yourself by visiting https://100daystooffload.com.&#xA;&#xA;#100DaysToOffload #day50 #mtp&#xA;&#xA;iComment on this post/i&#xD;&#xA;div id=&#34;cusdis_thread&#34;/div]]&gt;</description>
      <content:encoded><![CDATA[<p><a href="https://gerritniezen.com/taking-a-deep-dive-into-mtp">Last time</a> I started looking at how to implement a subset of the MTP specification, in order to read files from Android devices. Let&#39;s start by writing some code to talk to the device over WebUSB:</p>

<pre><code class="language-js">const device = await usb.requestDevice({
  filters: [
    {
      vendorId: 3725,
      productId: 8221,
    }
  ]
});

await device.open();

if (device.configuration === null) {
  console.log(&#39;selectConfiguration&#39;);
  await device.selectConfiguration(1);
}
await device.claimInterface(0);
</code></pre>

<p>MTP uses <em>containers</em> to structure what the various commands and responses look like. Here is how to build a container packet (and note that <code>struct.pack()</code> is part of a <a href="https://github.com/tidepool-org/uploader/blob/master/lib/struct.js">convenience library</a>):</p>

<pre><code class="language-js">const buildContainerPacket = (container, payloadLength) =&gt; {
  const packetLength = 12 + payloadLength;
  const buf = new ArrayBuffer(packetLength);
  const bytes = new Uint8Array(buf);
  let ctr = struct.pack(bytes, 0, &#39;issi&#39;, packetLength, container.type, container.code, container.transactionID);
  if (payloadLength &gt; 0) {
    ctr += struct.copyBytes(bytes, ctr, container.payload, payloadLength);
  }

  return buf;
};
</code></pre>

<p>To open a new session, we fill the <code>container</code> object with the right info and send it off via the bulk pipe:</p>

<pre><code class="language-js">  const openSession = {
    type: 1, // command block
    code: 0x1002, // open session
    transactionID: 0,
    payload: 1, // session ID
  };
  let data = buildContainerPacket(openSession, 4);
  let result = await device.transferOut(0x01, data);
</code></pre>

<p>To read the response, I&#39;m creating a <code>receiveData()</code> function:</p>

<pre><code class="language-js">  const receiveData = async () =&gt; {
    const timeoutID = setTimeout(async() =&gt; {
      console.warn(&#39;Device not connected&#39;);
    }, 5000);

    console.log(&#39;Receiving...&#39;);

    let incoming = await device.transferIn(0x01, 1024);

    if (incoming.data.byteLength &gt; 0) {
      clearTimeout(timeoutID);

      const [length] = new Uint32Array(incoming.data.buffer, 0, 1);
      console.log(&#39;Length:&#39;, length);

      // TODO: if length !== incoming.data.byteLength, read more data
    }
  };
</code></pre>

<p>To actually receive some data, we call</p>

<pre><code class="language-js">await receiveData();
</code></pre>

<p>and then parse the response (<code>0c 00 00 00 03 00 01 20 01 00 00 00</code>), which is also in the same container format:</p>
<ul><li>Length: <code>0x0C000000</code> – 12 (in little-endian format)</li>
<li>Type: <code>0x0300</code> – Response block</li>
<li><code>0x0120</code>, or <code>0x2001</code> in big-endian – response code <code>OK</code></li>
<li><code>0x0000000000</code> – transaction ID</li></ul>

<p>So, we&#39;re able to successfully open a session!</p>

<p>PS: Hey, I&#39;m halfway through <a href="https://gerritniezen.com/tag:100DaysToOffload" class="hashtag"><span>#</span><span class="p-category">100DaysToOffload</span></a> already! 🎉️</p>

<hr/>

<p>I’m publishing this as part of 100 Days To Offload. You can join in yourself by visiting <a href="https://100daystooffload.com">https://100daystooffload.com</a>.</p>

<p><a href="https://gerritniezen.com/tag:100DaysToOffload" class="hashtag"><span>#</span><span class="p-category">100DaysToOffload</span></a> <a href="https://gerritniezen.com/tag:day50" class="hashtag"><span>#</span><span class="p-category">day50</span></a> <a href="https://gerritniezen.com/tag:mtp" class="hashtag"><span>#</span><span class="p-category">mtp</span></a></p>

<p><i>Comment on this post</i>
<div id="cusdis_thread" id="cusdis_thread"></div></p>
]]></content:encoded>
      <guid>https://gerritniezen.com/taking-a-deep-dive-into-mtp-part-2</guid>
      <pubDate>Mon, 22 Jun 2020 15:24:08 +0000</pubDate>
    </item>
  </channel>
</rss>