<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>tifan.net</title><link href="https://tifan.net/" rel="alternate"/><link href="https://tifan.net/atom.xml" rel="self"/><id>https://tifan.net/</id><updated>2026-04-24T20:00:00-08:00</updated><entry><title>解码东方红一号</title><link href="https://tifan.net/blog/2026/04/24/dong-fang-hong/" rel="alternate"/><published>2026-04-24T20:00:00-08:00</published><updated>2026-04-24T20:00:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2026-04-24:/blog/2026/04/24/dong-fang-hong/</id><summary type="html">&lt;p&gt;1970 年 4 月 24 日发射的东方红一号卫星，在 20.009 MHz 短波上播送《东方红》前 8 小节， 并在乐曲间隙传回时分复用遥测数据。两位 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;1970 年 4 月 24 日发射的东方红一号卫星，在 20.009 MHz 短波上播送《东方红》前 8 小节， 并在乐曲间隙传回时分复用遥测数据。两位业余无线电爱好者（瑞典 Sven Grahn / 德国 Kurt DF7FU） 在发射后前三天内截获的 206 秒 AM 音频，30 多年公开存档于 dd1us.de。 本文以该录音为唯一数据源，配合公开的轨道要素、地磁偶极模型、Ag-Zn 电池放电曲线等开放资料， 完成遥测系统的技术逆向：帧结构、通道分配、三路 RF 架构、 科学载荷识别、电源/热控/姿态子系统特征——全部公开知识，无需任何保密标定文档。&lt;/p&gt;
&lt;a href="https://tifan.net/blog/2026/04/24/dong-fang-hong/main.html"&gt;Redirect...&lt;/a&gt;

 &lt;script&gt;
   location.replace("https://tifan.net/blog/2026/04/24/dong-fang-hong/main.html")
 &lt;/script&gt;</content><category term="misc"/><category term="tech"/></entry><entry><title>使用 IMB 进行 USPS First Class Mail 跟踪</title><link href="https://tifan.net/blog/2025/07/01/usps-imb-tracking/" rel="alternate"/><published>2025-07-01T02:00:00-07:00</published><updated>2025-07-01T02:00:00-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2025-07-01:/blog/2025/07/01/usps-imb-tracking/</id><summary type="html">&lt;p class="first last"&gt;如何简单的进行 IMB 跟踪&lt;/p&gt;
</summary><content type="html">&lt;p&gt;在美国，即使是自己直接寄信，不贴任何条码，USPS 在邮局里也会喷上 IMB 条码。有些
商业客户寄信的时候，会在信封上面直接印刷 IMB 条码。IMB 条码是有 trace 功能的
（他不提供 tracking 功能，无法确认信件是否确实投妥）。这个 trace 功能可以使用
USPS API 直接进行跟踪，这也是为什么有些银行虽然使用 First Class Mail，但是可以
告诉你信用卡哪天寄到。当然，实现这样的功能的前提是打印信封的时候粘上去条码。&lt;/p&gt;
&lt;p&gt;打印条码本身很简单，连 API 都不需要，只要做好 IMB 的编码工作即可。我自己申请了个 Mailer ID，但是也有免费的工具可以用
他们的 Mail ID。&lt;a class="reference external" href="https://envelopetracker.com/"&gt;https://envelopetracker.com/&lt;/a&gt; 就是一个很好的工具。&lt;/p&gt;
&lt;p&gt;我自己我使用了 Gprinter 写了个小工具，可以自动解码地址，查找邮编后打印信封。由于我的 Mercurial 服务器还没修好，所以在文后附上打印机驱动的源码。等哪天有时间了，我会把自动解码
地址的代码部署一个云服务提供给大家使用。&lt;/p&gt;
&lt;p&gt;获取条码，打印条码都非常简单，麻烦的地方主要是如何方便的在线追踪，以及获得更新后的通知。
&lt;a class="reference external" href="https://envelopetracker.com/"&gt;https://envelopetracker.com/&lt;/a&gt; 已经有了追踪功能，但是没有办法推送。想得到推送通知，
就需要自己注册 Mailer ID 后，登 USPS IV 后台，设置一个 JSON API endpoint，USPS 会定期向这个 endpoint 发送
更新。&lt;/p&gt;
&lt;p&gt;接受更新最方便的方法是 lambda 函数（或者是类似的云函数）。考虑到这是 AI 时代，我就 vibe code 了一个
lambda 函数，凑合也能用。这里的主要挑战是 USPS 根本没写 payload 的格式，我只好
直接 log 了请求。下面附上数据结构，供各位 vibe code 的时候作为 prompt 输入。改改总能改出来凑合能用的东西的。&lt;/p&gt;
&lt;p&gt;另外注意，scanLocaleKey 很有意思，可以通过查询 Excel 表格，找到对应的 Facility 的地址。我把数据转换成了 parquet 文件，方便 lambda 里 load.
这玩意儿实在有点大，用 JSON 扛不住。&lt;/p&gt;
&lt;p&gt;对应的数据字典文件和参考资料有：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://postalpro.usps.com/service-hubs-and-facilities/facilityfile"&gt;FACILITY.xlsx&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://postalpro.usps.com/informedvisibility/DataDictionary"&gt;IVMTR Data Dictionary&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://postalpro.usps.com/informedvisibility"&gt;Informe Visibility&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;除了 lambda 函数，我还做了个 Telegram bot，可以发送 IMB 的更新:
t.me/usps_iv_bot 。这个 Telegram bot 用了 &lt;a class="reference external" href="https://yegle.net/"&gt;&amp;#64;yegle&lt;/a&gt; 负责的 GCP Cloud Run Functions
服务。Cloud Run Functions 的好处主要是它不需要单独开个 VPC 以及对应的 NAT
Gateway，比用 AWS 省一些钱，免费 tier 完全够用。当然，另一个好处是知道挂了以后找谁去喷。&lt;/p&gt;
&lt;p&gt;USPS POST 过来的数据结构如下：&lt;/p&gt;
&lt;pre class="code json literal-block"&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;events&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;mailShapeDescription&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;startTheClockFacilityAddress&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;recipientCridOfMidOnPiece&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;Recipient Crid&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;idTag&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;recipientRoutingCodeAuthorizedCrid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;pieceId&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;machineName&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;DIOSS-006&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;ldeTriggerMethod&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;parentEdocContainerImcb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;routingCodeImbMatchingPortion&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ZIP+9&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;edocSubmitterCrid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;ldeDeliveryMode&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;parentContainerEdocContainerId&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;startTheClockFacilityCity&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;scanFacilityState&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;WA&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;recipientMailOwnerCrid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;scanEventCode&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;Scan Event Code, See IVMTR_OperationCodesList_v9.1_06252025.xlsx&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;scanDatetime&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;2025-01-01 01:00:00-0700&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;impb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;parentEdocTrayImtb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;scanFacilityZip&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;5 digit zip code&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;imbTrackingCode&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;IMB without ZIP+9&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;imbRoutingCode&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;ZIP+9&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;machineId&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;006&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;startTheClockFacilityLocaleKey&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;recipientCridOfMidOnPieceDelegator&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;recipientMailOwnerDelegatorCrid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;startTheClockFacilityZip&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;edocJobId&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;mailClassDescription&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;imb&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;IMB&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;scanLocaleKey&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;Locale Key in FACILITY.xlsx&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;mailPhase&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Phase 2a - Destination MMP Processing&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;parentTrayEdocContainerId&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;edocMailingGroupId&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;predictedDeliveryDate&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;startTheClockFacilityState&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;scanFacilityCity&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;City&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;anticipatedDeliveryDate&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;scannerType&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;imbStid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;270&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;imbSerialNumber&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;IMB Serial Number&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;startTheClockDate&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;expectedDeliveryDate&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;handlingEventTypeDescription&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Actual&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;imbMid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;Mailer ID&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;ldeInventoryMethod&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;handlingEventType&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;A&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;startTheClockFacilityName&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;scanFacilityName&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;City&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;msgGrpId&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&amp;lt;Message Group ID&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;msgSerNbr&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;totMsgCnt&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;recCnt&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;totRecCnt&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Gprinter 打印标签的源代码 (print_label.py) 如下:&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pathlib&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;absl&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;absl&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;absl&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;escpos&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;printer&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;PIL&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;usb.core&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c1"&gt;# --- Flag Definitions ---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;FLAGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FLAGS&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEFINE_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;png_file&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Path to the PNG label file to reprint.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEFINE_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;count&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;Number of copies to print.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEFINE_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;vid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x0471&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;USB Vendor ID of the label printer.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEFINE_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;pid&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x0055&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;USB Product ID of the label printer.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEFINE_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;in_ep&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x82&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;USB IN endpoint for the printer.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEFINE_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;out_ep&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x02&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;USB OUT endpoint for the printer.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c1"&gt;# --- Label Dimension Flags ---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;# These are needed to send the correct SIZE and GAP commands to the printer.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEFINE_float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;paper_width_mm&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;100.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;The width of the label paper in mm.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEFINE_float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;paper_height_mm&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;50.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;The height of the label paper in mm.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEFINE_float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;gap_mm&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;3.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;The gap between labels in mm.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEFINE_integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;direction&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;The print direction (0 or 1).&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mark_flag_as_required&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;png_file&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TSPLPrinterDriver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;A driver to send TSPL commands to a USB label printer.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;in_ep&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out_ep&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Initializes the printer driver.

        Args:
            vid: The USB Vendor ID of the printer.
            pid: The USB Product ID of the printer.
            in_ep: The USB IN endpoint for the printer.
            out_ep: The USB OUT endpoint for the printer.
        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vid&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in_ep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;in_ep&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out_ep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;out_ep&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_device&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_device&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;printer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Usb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Finds and initializes the USB printer device.

        Returns:
            An escpos.printer.Usb object for communication.

        Raises:
            ConnectionError: If the printer cannot be found or connected to.
        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_device&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;printer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Usb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;                &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;in_ep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in_ep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out_ep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out_ep&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Printer found: VID=&lt;/span&gt;&lt;span class="si"&gt;%#06x&lt;/span&gt;&lt;span class="s2"&gt;, PID=&lt;/span&gt;&lt;span class="si"&gt;%#06x&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_device&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;usb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NoBackendError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ConnectionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;libusb backend not found.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;e&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;ConnectionError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Printer not found (VID=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vid&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;#06x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, PID=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;#06x&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;). &amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;                &lt;span class="s2"&gt;&amp;quot;Check connection/permissions.&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;e&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_image_to_bitmap_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Converts a PIL Image to a monochrome bitmap byte array for TSPL.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Ensure monochrome with 1-bit pixels.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="c1"&gt;# Use the standard '1' decoder for MSB-first bitmap data.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tobytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;raw&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;print_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label_params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Constructs and sends the full TSPL payload to print an image.

        Args:
            img: The PIL Image to print.
            count: The number of copies to print.
            label_params: A dictionary with label specs (paper_width_mm, etc.).
        &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;bitmap_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width_dots&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height_dots&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_image_to_bitmap_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="c1"&gt;# Width in bytes for the BITMAP command (must be a multiple of 8).&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;width_bytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width_dots&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;        &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bytearray&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SIZE &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;label_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'paper_width_mm'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; mm, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;label_params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'paper_height_mm'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; mm&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GAP &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;label_params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'gap_mm'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; mm, 0 mm&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;DIRECTION &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;label_params&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'direction'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;CLS&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Clear image buffer before sending new data.&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;        &lt;span class="c1"&gt;# Use mode 0 (OVERWRITE) for the BITMAP command.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;BITMAP 0,0,&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;width_bytes&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;height_dots&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,0,&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bitmap_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;b&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;PRINT &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,1&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;        &lt;span class="n"&gt;printer_device&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_get_device&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Sending &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s2"&gt; bytes to printer.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="n"&gt;printer_device&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Print command sent successfully.&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="n"&gt;printer_device&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Main application entry point.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;  &lt;span class="c1"&gt;# Unused.&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="c1"&gt;# 1. Load the PNG image file.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;png_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FLAGS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;png_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;png_path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;PNG file not found at: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;png_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Loading label from: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;png_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;label_image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;png_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;        &lt;span class="c1"&gt;# 2. Prepare label parameters from flags.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;label_params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="s2"&gt;&amp;quot;paper_width_mm&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FLAGS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paper_width_mm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="s2"&gt;&amp;quot;paper_height_mm&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FLAGS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;paper_height_mm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="s2"&gt;&amp;quot;gap_mm&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FLAGS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gap_mm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="s2"&gt;&amp;quot;direction&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;FLAGS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;        &lt;span class="c1"&gt;# 3. Initialize the printer driver and print.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TSPLPrinterDriver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;            &lt;span class="n"&gt;vid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FLAGS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FLAGS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;in_ep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FLAGS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;in_ep&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;out_ep&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FLAGS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;out_ep&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label_image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FLAGS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ne"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ne"&gt;ConnectionError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ne"&gt;IOError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Reprint operation failed: &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_info&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;__main__&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
</content><category term="misc"/></entry><entry><title>赴华核酸检测的大生意</title><link href="https://tifan.net/blog/2022/02/13/mandatory-covid-test-for-traveling-to-china/" rel="alternate"/><published>2022-02-13T18:00:00-08:00</published><updated>2022-02-13T18:00:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2022-02-13:/blog/2022/02/13/mandatory-covid-test-for-traveling-to-china/</id><summary type="html">&lt;p class="first last"&gt;恭喜发财。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;去中国旅行，除了要被航空公司讹一大笔钱之外，还要额外的被核酸检测讹一大笔钱。&lt;/p&gt;
&lt;p&gt;目前，美国的普通核酸检测是完全免费的，保险公司 100% 报销，无保险的核酸检测费也是由政府完全承担的。洛杉矶地区可以做检测的地方，大概会有几百处，从药店到医院，都可以做检测。&lt;/p&gt;
&lt;p&gt;然而，想去中国，就必须要去中国领馆 &lt;strong&gt;指定&lt;/strong&gt; 的检测机构。核酸以及抗体检测的费用，每套需要 500 到 700 美元（三次核酸检测，两次抗体检测）。&lt;/p&gt;
&lt;p&gt;以洛杉矶为例，平时每周 6 班，可运输大约 2000 人。洛杉矶有 4 家中国领馆指定的检测机构。扣除成本，每个检测机构平均下来可以有 1500 万美元的年利润。这还不考虑很多人都会提前去这些机构测一次，防止假阳性发生（奇怪的是这些机构的假阳性都很常见），也没有考虑很多人真阳性就没有坐上飞机。&lt;/p&gt;
&lt;p&gt;我也想开一家领馆指定的检测机构。&lt;/p&gt;
</content><category term="misc"/></entry><entry><title>Replace ONU with SFP GPON Stick on Frontier/Verizon/Ziply FTTH Network</title><link href="https://tifan.net/blog/2021/12/31/verizon-frontier-fios-sfp-stick/" rel="alternate"/><published>2021-12-31T00:00:00-08:00</published><updated>2021-12-31T00:00:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2021-12-31:/blog/2021/12/31/verizon-frontier-fios-sfp-stick/</id><summary type="html">&lt;p class="first last"&gt;Get rid of the modem altogether!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;WARNING: THIS IS UNSUPPORTED. Verizon/Frontier only allows their own equipment on their own network. They can ban you at any time.&lt;/p&gt;
&lt;p&gt;The experiment was made on a Lantiq FALC ON sfp stick, which shares the same design with Huawei MA5671A and Nokia G-010S-A. Current price is around $20 on Xianyu (闲鱼, Taobao's C2C version) sans shipping. The stick is quite hard to find in the US -- I had to import them from China.&lt;/p&gt;
&lt;p&gt;I bought the stick from &lt;a class="reference external" href="https://item.taobao.com/item.htm?id=666376847051"&gt;https://item.taobao.com/item.htm?id=666376847051&lt;/a&gt; . You may also contact the seller at &lt;a class="reference external" href="mailto:645041413&amp;#64;qq.com"&gt;645041413&amp;#64;qq.com&lt;/a&gt;. The seller is the OEM manufacturer, and can provide technical support (which is super important, unless you have access to Lantiq's NDA documentation).&lt;/p&gt;
&lt;p&gt;Make sure you are using GPON OLT. If you are still using BPON, call your ISP right now to replace it with GPON. XGS-PON is great, but the ONU stick is very expensive (costs ~$200 each).&lt;/p&gt;
&lt;p&gt;Write down the S/N of the ISP's equipment. It should start with 4 characters such as ALCL or FTRO. Change the SN of the stick to the ISP OLT's SN. Do not change anything else. LOID authentication is only used by Chinese ISPs, and PLOAM password is not used.&lt;/p&gt;
&lt;p&gt;After that, your stick should go into O5 state in a few seconds. However, you definitely won't be able to DHCP until you set the correct VLAN. Depending on your area, you might need to change VLAN tagging configuration &lt;em&gt;from within the SFP stick&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To identify the VLAN ID, look at ONU_GPE_EXTENDED_VLAN_TABLE first:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Name:        ONU_GPE_EXTENDED_VLAN_TABLE
ID:          41
no;out tpid;in tpid;vlan rule pointer;dscp pointer
0;0x8100;0x8100;    ;1
1;0x8100;0x8100;  AA;1
&lt;/pre&gt;
&lt;p&gt;VLAN Rule AA points to ONU_GPE_VLAN_RULE_TABLE.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Name:        ONU_GPE_VLAN_RULE_TABLE
ID:          42
;;;enable;;;ethertype filter;;;;;outer;;;;;;;inner;;;;;;
no;end;def;two;one;zero;5;4;3;2;1;de enable;de filter;input tpid enable;vid enable;vid filter;priority enable;priority filter;de enable;de filter;input tpid enable;vid enable;vid filter;priority enable;priority filter
...
AA; ; ; ;1; ; ; ; ; ; ; ; ;1;1;  BB; ; ; ; ; ; ;    ; ;
...
&lt;/pre&gt;
&lt;p&gt;BB is your VLAN ID (decimal, to be tagged on GEM port side). Payload should be tagged with dot1Q &lt;cite&gt;BB&lt;/cite&gt; from the GEM port end. Create a sub interface on your router with VLAN BB and see if you can DHCP. If you can get an address, then great, that's all you need.&lt;/p&gt;
&lt;p&gt;If you can see some packets from the router, but not able to acquire a DHCP lease, try connecting your original router and release the DHCP lease. Some ISPs would only hand out one active lease per account.&lt;/p&gt;
&lt;p&gt;The SFP stick is capable at handshaking at 2.5Gbps (with compatible switches). Unifi devices and a few TP-Link devices were said to be compatible. Alternatively, there are 2.5Gbps SFP to Ethernet media converters available from Taobao.&lt;/p&gt;
</content><category term="misc"/></entry><entry><title>Power Verizon/Frontier ONT with DC supply</title><link href="https://tifan.net/blog/2021/12/25/verizon-frontier-ont-with-dc-battery/" rel="alternate"/><published>2021-12-25T00:00:00-08:00</published><updated>2021-12-25T00:00:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2021-12-25:/blog/2021/12/25/verizon-frontier-ont-with-dc-battery/</id><summary type="html">&lt;p class="first last"&gt;Drop the stupid bulky power supply now.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;If you have a Verizon / Frontier ONT (modem), and would like to solely run it on DC power, or would like to drop the bulky CyberPower CA25U16V2 power supply, you should do the following:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Disconnect the power supply&lt;/li&gt;
&lt;li&gt;Provide your own means of DC power, for example, some sort of DC UPS. Inject power with the 5.5x2.5 barrel connector (if you have the Y cable with mini-DIN on one side, barrel connector and molex connector on the other side).&lt;/li&gt;
&lt;li&gt;Connect a 47 Ohm resistor between grey (BAT) and black (GND). Don't have to be 47 Ohm, but it should be of low resistance.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The ONT does not stricly need 16V. 12V works just fine. Without the resistor, the ONT would shut down WAN port. It only draws around 0.1A at 12V, so any reasonable battery will support it for quite a while...&lt;/p&gt;
</content><category term="misc"/></entry><entry><title>IFITL -- 美国早期光纤接入网的牺牲品</title><link href="https://tifan.net/blog/2021/08/24/ifitl/" rel="alternate"/><published>2021-08-24T00:00:00-08:00</published><updated>2021-08-24T00:00:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2021-08-24:/blog/2021/08/24/ifitl/</id><summary type="html">&lt;p class="first last"&gt;IFITL 是北美早起的高速网络入户的失败尝试。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;IFITL: Integrated Fiber In The Loop 是 Bell South 的早期产品，大约在 2000 年左右推出，是一种 FTTC 的产品。&lt;/p&gt;
&lt;p&gt;技术上，他是多模主动光纤从电话局 CO (Central Office) 直通到路边的信息箱的。信息箱内放置一台 10M 交换机。光口上联，从信息箱到家庭是额外单独布线的户外五类线。端口密度自然是一个光口对应最多 4 个用户。这基本上是类似于 M-Ethernet 的产品。&lt;/p&gt;
&lt;p&gt;听起来这是没什么问题，只是端口密度低了一些。可惜，这个产品死掉了。&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;交换机是北电生产，这种交换机只支持平分带宽，每个板卡上联 10Mbps，四个下联口，每户的最大带宽是 2Mbps，额外 2Mbps 作为控制信令。&lt;/li&gt;
&lt;li&gt;系统是半双工的。&lt;/li&gt;
&lt;li&gt;电口交换机需要外部供电。在设计时，使用了极细的供电线（据说为 22AWG 铜缆），仅仅够对这台特定的交换机供电，无法扩容。&lt;/li&gt;
&lt;li&gt;虽然是主动光纤，但是 1990 年代普遍使用多模光纤。&lt;/li&gt;
&lt;li&gt;接线盒上使用的是 RJ11，以及普通的电话终端。当然，10M 是完全可以用三类线的。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;后来，北电自己放弃了这个技术。SBC 既无法升级交换机（因为电力的限制），又无法将其切换成高速 VDSL（还是电力的限制）。最后，北电倒闭，甚至无法获得备件。在 ADSL 速度可超过 2Mbps 之后，这个技术就被迅速的淘汰了。&lt;/p&gt;
&lt;p&gt;IFITL 产品就这样死掉了。&lt;/p&gt;
&lt;p&gt;如果当时更加标准化一些，那么这至少可以成为一个后期可万兆到信息箱，千兆入户的产品的。&lt;/p&gt;
&lt;p&gt;至于那些光纤呢？因为是多模光纤，自然也无法使用。整个系统甚至没有任何回收价值。&lt;/p&gt;
&lt;p&gt;然而，在当时，谁又能料到光网是 PON 的天下呢？&lt;/p&gt;
</content><category term="misc"/></entry><entry><title>Enable Bridge Mode on Verizon 5G Home Router (model LV55 / LVSKIHP)</title><link href="https://tifan.net/blog/2021/04/01/enable-bridge-mode-on-verizon-5g-home-router-lv55-lvskisp/" rel="alternate"/><published>2021-04-01T02:00:00-07:00</published><updated>2021-04-01T02:00:00-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2021-04-01:/blog/2021/04/01/enable-bridge-mode-on-verizon-5g-home-router-lv55-lvskisp/</id><summary type="html">&lt;p class="first last"&gt;If you are lucky enough to have Verizon 5G Home Internet, there're a few ways to enable bridge mode even if it's hidden from the GUI.&lt;/p&gt;
</summary><content type="html">&lt;div class="section" id="just-use-ipv6"&gt;
&lt;h2&gt;Just Use IPv6&lt;/h2&gt;
&lt;p&gt;This is the most simple solution. Log in to the router, use the following code to enable IPv6, and call it a day. Trust me, you don't want to spend more time on it.&lt;/p&gt;
&lt;pre class="literal-block"&gt;
WNC.DM.set({
  args: {
    &amp;quot;Object&amp;quot;:&amp;quot;Device.DHCPv6.Server.&amp;quot;,
    &amp;quot;Operation&amp;quot;: &amp;quot;Modify&amp;quot;,
    &amp;quot;X_WNC_RA_management&amp;quot;:&amp;quot;Stateless&amp;quot;,
    &amp;quot;Enable&amp;quot;: 1,
  },
  success: function s(objs, status) {
    console.log(&amp;quot;success: &amp;quot;, status, objs)
  },
  error: function s(objs, status) {
    console.log(&amp;quot;error: &amp;quot;, status, objs)
  },
})
&lt;/pre&gt;
&lt;p&gt;Even if you're still behind NAT, having a public IPv6 address is still much better than nothing. Also, you'll get an extra Wi-Fi hotspot for guests. The built-in Wi-Fi is actually quite good.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="enable-dmz"&gt;
&lt;h2&gt;Enable DMZ&lt;/h2&gt;
&lt;p&gt;Unfortunately, IPv4 is still a thing, and pure-IPv6 network doesn't quite work for most people.&lt;/p&gt;
&lt;p&gt;Enable DMZ mode and point that to your router. DMZ can be configured directly from the GUI.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="disable-firewall"&gt;
&lt;h2&gt;Disable Firewall&lt;/h2&gt;
&lt;p&gt;Use the following code:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
WNC.DM.set({
  args: {
    Object: &amp;quot;Device.Firewall.&amp;quot;,
    Operation: &amp;quot;Modify&amp;quot;,
    Config: &amp;quot;Advanced&amp;quot;,
  },
  success: function s(objs, status) {
    console.log(&amp;quot;success: &amp;quot;, status, objs)
  },
  error: function s(objs, status) {
    console.log(&amp;quot;error: &amp;quot;, status, objs)
  },
})
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="enable-bridge-mode"&gt;
&lt;h2&gt;Enable Bridge Mode&lt;/h2&gt;
&lt;p&gt;It is possible to enable bridge mode with some effort. The MTU on cellular network is smaller than 1500, and my test concludes that 1400 is a good number.&lt;/p&gt;
&lt;p&gt;Paste the following to inspector console:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$('#restart-modal').data('action', { url: modeswitch_url, msg: &amp;quot;Click YES&amp;quot;, data: {mode: &amp;quot;Bridge&amp;quot;}, count: 150000, refresh: false }).modal('show');
&lt;/pre&gt;
&lt;p&gt;Click Yes.&lt;/p&gt;
&lt;p&gt;Alternatively, run the following command (replace CSRF Token and Cookie with yours).&lt;/p&gt;
&lt;pre class="literal-block"&gt;
curl 'http://192.168.0.1/cgi-bin/luci/verizon/home/changeidumode' \
  -H 'Accept: */*' \
  -H 'X-CSRF-TOKEN: XXXX' \
  -H 'X-Requested-With: XMLHttpRequest' \
  -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' \
  -H 'Origin: http://192.168.0.1' \
  -H 'Referer: http://192.168.0.1/cgi-bin/luci/' \
  -H 'Cookie: sysauth=XXXX' \
  --data-raw 'mode=Bridge' \
  --insecure \
  --verbose
&lt;/pre&gt;
&lt;p&gt;The router should then reboot. Wait for a few minutes (it might take a few hours, just wait patiently). Eventually, you'll see an IPv4 address DHCP'ed to you. To access the router's consumer facing GUI, visit &lt;a class="reference external" href="http://10.0.0.2"&gt;http://10.0.0.2&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That should be it!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="enabling-bridge-mode-alternative-method"&gt;
&lt;h2&gt;Enabling Bridge Mode (alternative method)&lt;/h2&gt;
&lt;p&gt;The router can be powered by PoE. Pry off the bottom cover, remove the 4 screws, and pry off the bottom panel to remove the window mount. Use PoE to power the previously hidden port. Do NOT use the AC adapter.&lt;/p&gt;
&lt;p&gt;That's probably the easiest way to enable bridge mode, but it's impossible to mount the router on the window with ethernet cable connected, unless if someone 3D prints a bracket.&lt;/p&gt;
&lt;p&gt;The PoE port supports 5GBASE-T, and it's possible to reach to ~3Gbps speed with it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="dumping-tr069-config-files"&gt;
&lt;h2&gt;Dumping TR069 Config Files&lt;/h2&gt;
&lt;p&gt;To dump TR069 config, use the following code:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
WNC.DM.encr_get({
  args: {
    &amp;quot;Object&amp;quot;:&amp;quot;Device.DHCPv4&amp;quot;,
  },
  success: function s(objs, status) {
    console.log(&amp;quot;success: &amp;quot;, status, objs)
  },
  error: function s(objs, status) {
    console.log(&amp;quot;error: &amp;quot;, status, objs)
  },
})
&lt;/pre&gt;
&lt;p&gt;Some objects cannot be dumped directly.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="running-servers"&gt;
&lt;h2&gt;Running Servers&lt;/h2&gt;
&lt;p&gt;Verizon blocks port 22.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"/></entry><entry><title>阅读四通 MS-2401H 图纸得到的一些 hints</title><link href="https://tifan.net/blog/2021/02/15/ms2401h-hardware-design-notes/" rel="alternate"/><published>2021-02-15T16:00:00-08:00</published><updated>2021-02-15T16:00:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2021-02-15:/blog/2021/02/15/ms2401h-hardware-design-notes/</id><summary type="html">&lt;p&gt;阅读 MS-2401H 图纸，配合设计文档，可以知道下面的一些结论：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;MS-2401H 设计时，所有的 IO 设备都是通过 in / out 来进行的，而不是通过访问内 …&lt;/li&gt;&lt;/ul&gt;</summary><content type="html">&lt;p&gt;阅读 MS-2401H 图纸，配合设计文档，可以知道下面的一些结论：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;MS-2401H 设计时，所有的 IO 设备都是通过 in / out 来进行的，而不是通过访问内存地址进行的。原因：没有为访问硬件设备留出专用的地址空间（8088 本来的地址空间就很稀缺了），而且 8088 原本就支持用 AD line 访问设备。&lt;/li&gt;
&lt;li&gt;ISH8 芯片负责内部设备的片选、页切换等，ISH9 芯片负责外部设备的片选。但是一个例外：ISH9 的键盘中断接回了 ISH8。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ISH8 负责下面设备中断（输入）：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;串口 (RXRDY)&lt;/li&gt;
&lt;li&gt;Option board (OPINT, EXPRQ)&lt;/li&gt;
&lt;li&gt;键盘 (KBINT) 接到了 ISH-9 KBINT 输出&lt;/li&gt;
&lt;li&gt;软驱 (FDCINT)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ISH8 负责下面设备的片选（目前确认地址线高 2 位，即位 AD6, AD7 为 ISH8/ISH9 片选信号，AD0:5 为设备地址；AD6、7皆为高位时 ISH9 片选使能，其余时候 ISH8 片选使能。为什么这么说？因为 ISH9 只接了 A0:5, 而且 8088 A/AD 复用，所以 ISH-9 只可能负责 bit 6, 7 固定的部分。配合之前的部分反汇编结果，确认 bit 6 必须为高位, bit 7 必须为低位时片选 ISH-9 设备。）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;SIOST   (Serial IO status)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;LCDCCS  (LCD Controller CS)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;FDCCS   (FD Controller CS)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;SIOCS   (Serial IO CS)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;RTCCS   (RTC)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;BIOSCS  (BIOS EPROM)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;BPRAMCS (Boot PRAM)&lt;/p&gt;
&lt;p&gt;Update Dec 27, 2021: I/O device 0x40 to 0x7f belongs to ISH-9.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ISH9 负责了大部分外部设备的直接控制，例如打印机、键盘等。键盘控制 intr 传递回给 ISH8，软驱根据电路图应为 ISH8 ISH9 共同控制，ISH9 只负责电机&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;打印机所有的 IO 都接到了这里，大概有三十多个信号&lt;/li&gt;
&lt;li&gt;打印机电源开关&lt;/li&gt;
&lt;li&gt;电源管理（34V 打印机电压、电池电压低、主电源掉电信号等）&lt;/li&gt;
&lt;li&gt;软驱电机控制 (但是说明书里并没有提到这个)&lt;/li&gt;
&lt;li&gt;主 CPU NMI&lt;/li&gt;
&lt;li&gt;RX/TX clock (for RS232)&lt;/li&gt;
&lt;li&gt;expansion slot (IRQ)&lt;/li&gt;
&lt;li&gt;键盘 X0:7 Y0:10&lt;/li&gt;
&lt;li&gt;内建时钟（打印机、串口通信用）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一些可供猜测的地方 （和 MS-2401 的数据互相印证，我认为 2401 和 2401H 的很多代码类似，有时间 dump 2401 的话可以猜出更多信息）：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;RTC 需要 16 个连续地址，两个 Bank，每个 IO 地址只可以写四位（接了四根地址线、四根数据线）。不大可能 ISH8 锁存后共享地址。可以继续反汇编，寻找闹钟相关的逻辑，那里需要写入 bank 1, mode D, data bit 3 high。&lt;/li&gt;
&lt;li&gt;根据反汇编的结果，RTC 应该是 0x20 ~ 0x2F 地址&lt;/li&gt;
&lt;li&gt;FDD 只有 A0 地址，所以 8、9 IO 端口，8 对应 status / aux command，9 对应 data.&lt;/li&gt;
&lt;/ul&gt;
</content><category term="misc"/></entry><entry><title>Revealing a Forgotten Chinese Compute History: Stone MS-1300 and MS-1301: Re-branded Victor V86p</title><link href="https://tifan.net/blog/2021/02/04/revealing-a-forgotten-chinese-compute-history-stone-ms1300-ms1301-rebranded-victor-v86p/" rel="alternate"/><published>2021-02-04T20:00:00-08:00</published><updated>2021-02-04T20:00:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2021-02-04:/blog/2021/02/04/revealing-a-forgotten-chinese-compute-history-stone-ms1300-ms1301-rebranded-victor-v86p/</id><summary type="html">&lt;p&gt;So this is really a quick update for some new discoveries. If anyone has a
Victor V86p and found this page -- today is your lucky day.&lt;/p&gt;
&lt;p&gt;Recently, I ordered another set of Stone Chinese Typewriter, Stone MS-1300 and
MS-1301. They're dirty cheap -- 400 RMB for MS-1300 and 300 RMB for …&lt;/p&gt;</summary><content type="html">&lt;p&gt;So this is really a quick update for some new discoveries. If anyone has a
Victor V86p and found this page -- today is your lucky day.&lt;/p&gt;
&lt;p&gt;Recently, I ordered another set of Stone Chinese Typewriter, Stone MS-1300 and
MS-1301. They're dirty cheap -- 400 RMB for MS-1300 and 300 RMB for MS-1301,
and probably another $30 for shipping. MS-1301 came last week, and MS-1300 is
still in China waiting for shipment.&lt;/p&gt;
&lt;p&gt;By looking at the schematics, I discovered something very interesting -- the
machine should supports HDD (26-pin connector), but a model with HDD was never released.
Considering Stone Co was never a hardware company (they heavily outsource
hardware design and reuse machines in the Japanese market whenever possible),
it's not hard to imaging that the machines was based on existing machines.&lt;/p&gt;
&lt;p&gt;While researching the 26-pin HDD connector, someone told me it is a JVC proprietary connector. After digging around the Internet, I found that &lt;a class="reference external" href="https://knm.org.uk/blog/2017/04/the-jvc-26-pin-hard-disk-interface-part-1/"&gt;mattinx&lt;/a&gt; already reverse engineered the protocol to some extend . The machine almost looks identical!&lt;/p&gt;
&lt;p&gt;Well, in fact, they should be the same machine. The motherboard looked pretty much the same, except that MS-1300 has a daughter-board (ISA board), containing Chinese fonts (mask rom) and word processor program.&lt;/p&gt;
&lt;p&gt;The schematics of MS-1300 (or original Victor V86p) can be found under &lt;a class="reference external" href="https://software-archive.tifan.la/Stone-Chinese-TypeWriter/eBook/"&gt;https://software-archive.tifan.la/Stone-Chinese-TypeWriter/eBook/&lt;/a&gt; . &lt;a class="reference external" href="https://software-archive.tifan.la/Stone-Chinese-TypeWriter/eBook/%E5%9B%9B%E9%80%9A%E6%89%93%E5%AD%97%E6%9C%BA%E7%94%B5%E5%8E%9F%E7%90%86%E5%9B%BE.pdf"&gt;四通打字机电原理图&lt;/a&gt; is the schematics (starting from page 232). &lt;a class="reference external" href="https://software-archive.tifan.la/Stone-Chinese-TypeWriter/eBook/%E5%9B%9B%E9%80%9A%E6%89%93%E5%AD%97%E6%9C%BA%E5%8E%9F%E7%90%86%E4%B8%8E%E7%BB%B4%E4%BF%AE-610p.pdf"&gt;四通打字机原理与维修&lt;/a&gt; is the service manual (starting from page 539). If the PSU is missing -- don't worry, the original PSU design is in it.&lt;/p&gt;
&lt;p&gt;And what about the difference between MS-1300 and MS-1301? Well, MS-1301 looks more original. The processor is a NEC V20, the BIOS was written by Stone (Shenzhen8088 BIOS), but is still PC compatible (MS-DOS 3.30). The system is much more integrated, the battery is slightly better (2000mAh Ni-Cd), and supports 1.44MB floppy disks. Unfortunately, it no longer has external video port, nor external ISA expansion port. The keyboard was also slightly redesigned.&lt;/p&gt;
</content><category term="misc"/><category term="tech"/></entry><entry><title>四通打字机模拟器的一些挑战</title><link href="https://tifan.net/blog/2020/09/27/stone-typewriter-emulation-challenges/" rel="alternate"/><published>2020-09-27T18:00:00-08:00</published><updated>2020-09-27T18:00:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2020-09-27:/blog/2020/09/27/stone-typewriter-emulation-challenges/</id><summary type="html">&lt;p&gt;下面简单记录一下目前逆向工程 MS-2401 系列打字机并开发模拟器的一些挑战。&lt;/p&gt;
&lt;div class="section" id="ms-2401h"&gt;
&lt;h2&gt;MS-2401H&lt;/h2&gt;
&lt;p&gt;目前手里能比较正常启动的机器都是 MS-2401H 型 …&lt;/p&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;下面简单记录一下目前逆向工程 MS-2401 系列打字机并开发模拟器的一些挑战。&lt;/p&gt;
&lt;div class="section" id="ms-2401h"&gt;
&lt;h2&gt;MS-2401H&lt;/h2&gt;
&lt;p&gt;目前手里能比较正常启动的机器都是 MS-2401H 型。此型机器有下面的特点：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;无需软盘即可启动进入字处理系统&lt;/li&gt;
&lt;li&gt;CPU 运行在最大模式下&lt;/li&gt;
&lt;li&gt;系统集成度极高（通过两片 NEC 公司制造的 ASIC）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;三条都是机器本身的很大优点，但是后两条对开发模拟器来说有极大的挑战。&lt;/p&gt;
&lt;div class="section" id="i-o"&gt;
&lt;h3&gt;I/O 端口&lt;/h3&gt;
&lt;p&gt;上一篇里已经大概摸索出了部分 IO 端口连接了什么设备，但是对于某个特定端口，目前无法确认读写后应有什么预期结果。&lt;/p&gt;
&lt;p&gt;因此，很难将 BIOS 作为一个黑盒，直接模拟 IO 端口的行为（毕竟你也不知道）。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h3&gt;硬件&lt;/h3&gt;
&lt;p&gt;显然，硬件本身会发起中断，但是由于不知道硬件的具体行为，因此无法知道硬件内部状态机，更无法模拟这一部分。&lt;/p&gt;
&lt;p&gt;实际上，上面两条已经让模拟器的开发停滞了一段时间：有一小段程序是在执行 WP 之前初始化硬件的，但是这段程序始终无法运行成功（会一直等某个内存置为某个特定值，但是并不能确定要运行什么才可以）。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="asic"&gt;
&lt;h3&gt;缺少 ASIC 资料&lt;/h3&gt;
&lt;p&gt;如果有 ASIC 资料的话，上面的工作也会解决一部分，可以参照 datasheet 模拟 ASIC 行为。但是两块 ASIC 的 datasheet 无处可找。&lt;/p&gt;
&lt;p&gt;ASIC 型号为uPD91260GD-5BD (ISH8) 以及 uPD91261PD-5BB (ISH9)。&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="ms-2401"&gt;
&lt;h2&gt;MS-2401&lt;/h2&gt;
&lt;p&gt;MS-2401 运行在最小模式，相对来说资料也齐全很多。但是 MS-2401 有一个致命缺点 —— 它需要软盘才可以启动。目前完全找不到带有软盘的 MS-2401 机器。&lt;/p&gt;
&lt;p&gt;所以，在最好情况下，MS-2401 也只能启动到「请插入软盘」的提示符。&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"/></entry><entry><title>四通打字机的一些资料存档</title><link href="https://tifan.net/blog/2020/09/21/stone-typewriter-resources/" rel="alternate"/><published>2020-09-21T01:00:00-08:00</published><updated>2020-09-21T01:00:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2020-09-21:/blog/2020/09/21/stone-typewriter-resources/</id><summary type="html">&lt;p&gt;一些四通打字机的资料存档，供后人有需要时使用。&lt;/p&gt;
&lt;p&gt;打字机相关的资料全部都在 software-archive 站中可获取: &lt;a class="reference external" href="https://software-archive.tifan.la/Stone-Chinese-TypeWriter/"&gt;https://software-archive.tifan.la/Stone-Chinese-TypeWriter/&lt;/a&gt;&lt;/p&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;电子书&lt;/h2&gt;
&lt;p&gt;因为 Internet …&lt;/p&gt;&lt;/div&gt;</summary><content type="html">&lt;p&gt;一些四通打字机的资料存档，供后人有需要时使用。&lt;/p&gt;
&lt;p&gt;打字机相关的资料全部都在 software-archive 站中可获取: &lt;a class="reference external" href="https://software-archive.tifan.la/Stone-Chinese-TypeWriter/"&gt;https://software-archive.tifan.la/Stone-Chinese-TypeWriter/&lt;/a&gt;&lt;/p&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;电子书&lt;/h2&gt;
&lt;p&gt;因为 Internet Archive 在中国被封，因此在 software-archive 站点中也提供了镜像。&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://software-archive.tifan.la/Stone-Chinese-TypeWriter/eBook/%E5%9B%9B%E9%80%9A%E6%89%93%E5%AD%97%E6%9C%BA%E5%8E%9F%E7%90%86%E4%B8%8E%E7%BB%B4%E4%BF%AE-610p.pdf"&gt;四通打字机原理与维修 (Design and Repair Guide for Stone Typewriters)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://software-archive.tifan.la/Stone-Chinese-TypeWriter/eBook/%E5%9B%9B%E9%80%9A%E6%89%93%E5%AD%97%E6%9C%BA%E7%94%B5%E5%8E%9F%E7%90%86%E5%9B%BE.pdf"&gt;四通打字机电原理图 (Schematics of Stone Typewriters)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;打字机软件&lt;/h2&gt;
&lt;p&gt;四通打字机 MS-2401H 型的 BIOS 文件在这里:&lt;/p&gt;
&lt;blockquote&gt;
&lt;a class="reference external" href="https://software-archive.tifan.la/Stone-Chinese-TypeWriter/MS-2401H/"&gt;https://software-archive.tifan.la/Stone-Chinese-TypeWriter/MS-2401H/&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;注意 MS-2401H 的 firmware 里已经包含了整个字处理软件，因此无需软盘即可启动使用编辑功能。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="ms-2401-i-o"&gt;
&lt;h2&gt;四通 MS-2401 I/O 表&lt;/h2&gt;
&lt;p&gt;目前靠猜测以及找资料，确认了 MS-2401 机型的已知 I/O 端口的对应设备。但是不幸的是仍然有很多端口还没有确认用途。&lt;/p&gt;
&lt;p&gt;MS-2401 和 MS-2401H 的 I/O 设备略有不同，但是还没确定 MS-2401H 究竟多出来了什么。已知 MS-2401H 机使用两块新的 ASIC。&lt;/p&gt;
&lt;p&gt;ASIC 型号为 uPD91260GD-5BD (ISH8) 以及 uPD91261PD-5BB (ISH9)。从名字上看，应为 NEC 公司设计。&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="9%" /&gt;
&lt;col width="91%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Port&lt;/th&gt;
&lt;th class="head"&gt;Device&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;0x00&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;Programmable Timer Counter uPD71054C, 8MHz&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x01&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;Programmable Timer Counter uPD71054C, 8MHz&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x02&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;Programmable Timer Counter uPD71054C, 8MHz&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x03&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;Programmable Timer Counter uPD71054C, 8MHz&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x04&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;LCD MSM6255&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x05&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;LCD MSM6255&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x08&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;FDD uPD72067GC&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x09&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;FDD uPD72067GC&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x0C&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RS232 uPD71051 (8251 compatible)&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x0D&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RS232 uPD71051 (8251 compatible)&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x20&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RTC RP5C15&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x21&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RTC RP5C15&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x22&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RTC RP5C15&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x23&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RTC RP5C15&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x24&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RTC RP5C15&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x25&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RTC RP5C15&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x26&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RTC RP5C15&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x27&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RTC RP5C15&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x28&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RTC RP5C15&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x29&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RTC RP5C15&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x2A&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RTC RP5C15&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x2B&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RTC RP5C15&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x2C&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RTC RP5C15&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x2D&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RTC RP5C15&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x2E&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RTC RP5C15&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x2F&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;RTC RP5C15&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x30&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;Bank Switching Calligraphic Page (0x6000), 16 dot&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x34&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;STATS SIO Status&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x38&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;LED 0&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x39&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;LED 1&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x3A&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;LED 2&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x3B&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;CMT MOTOR &lt;span class="pre"&gt;(MS-2402&lt;/span&gt; only)&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x46&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;CG Bank Switching (0xA0000), 24 dot&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x4B&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;unknown (called by NMI)&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x5E&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;unknown&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x60&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;INT MASK VECTOR&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x62&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;KB INT Timer&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x66&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;KBD Y&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x68&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;KBD X&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x6A&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;MOTOR_6A Head Data 0&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x6C&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;MOTOR_6C Head Data 1&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x6E&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;MOTOR_6E Head Data 2&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x70&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;PRINTER MOTOR / STATUS&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x72&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;COMMAND&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x74&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;STATUS&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x76&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;Bank Switching 24/8 DOTS CG&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x78&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;Bank Switching 16 DOTS CG&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x7A&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;SIO RXCLK&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x7C&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;SIO TXCLK&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x7E&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;MODE SET&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x84&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;unknown&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0x3B4&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;Video adapter?&lt;/tt&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;四通 MS-2401 I/O 表详细情况&lt;/h2&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="12%" /&gt;
&lt;col width="21%" /&gt;
&lt;col width="67%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Port&lt;/th&gt;
&lt;th class="head"&gt;Direction&lt;/th&gt;
&lt;th class="head"&gt;Comments&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;0x60&lt;/td&gt;
&lt;td&gt;out&lt;/td&gt;
&lt;td&gt;&lt;cite&gt;bit 3&lt;/cite&gt;: keyboard (0=unmask)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="section" id="ms-2401h-bios"&gt;
&lt;h2&gt;四通 MS-2401H BIOS 各中断对应的地址&lt;/h2&gt;
&lt;p&gt;注意地址为 &lt;a class="reference external" href="https://software-archive.tifan.la/Stone-Chinese-TypeWriter/MS-2401H/ALPS_BIOS-ST-M27C256B.BIN"&gt;https://software-archive.tifan.la/Stone-Chinese-TypeWriter/MS-2401H/ALPS_BIOS-ST-M27C256B.BIN&lt;/a&gt; 中对应的&lt;/p&gt;
&lt;p&gt;没写地址的表示这个 interrupt 直接调用了 &lt;tt class="docutils literal"&gt;IRET&lt;/tt&gt; (也就是说没实现)。&lt;/p&gt;
&lt;p&gt;没写用途的是因为还没猜出来是什么用途。&lt;/p&gt;
&lt;table border="1" class="docutils"&gt;
&lt;colgroup&gt;
&lt;col width="8%" /&gt;
&lt;col width="15%" /&gt;
&lt;col width="77%" /&gt;
&lt;/colgroup&gt;
&lt;thead valign="bottom"&gt;
&lt;tr&gt;&lt;th class="head"&gt;Int&lt;/th&gt;
&lt;th class="head"&gt;Address&lt;/th&gt;
&lt;th class="head"&gt;Function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td&gt;0h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x010a&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;Divide Error&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1h&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Single Step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x33af&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;NMI (printer, power)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x0000&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;Debug&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;4h&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Overflow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;5h&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;Sys Reserved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;10h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x4fd0&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;11h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x500d&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;ax=0x4041&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;12h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x5011&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;ax=0x0100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;13h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x5015&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;16h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x5189&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;17h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x51c5&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;ah=0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;19h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x51c3&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;run int 3h&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1ah&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x51c8&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;Clear ax, cx, dx&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1eh&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x51d2&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;(I/O with IO port 0x18 to 0x1F)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;43h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x2051&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;Keyboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;50h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x2d40&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;(related with timer)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;51h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x2cb1&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;52h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x2440&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;LCD I/O; al=0: CLS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;54h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x1f20&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;Keyboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;56h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x5670&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;5ah&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x4990&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;Serial Port I/O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;5bh&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x4cc0&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;Serial Port Control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;5ch&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x2940&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;5eh&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x2900&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;LED&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;60h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x4780&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;Show error message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;61h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x484c&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;Display text on LED&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;62h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x1220&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;软盘&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;63h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x1170&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;64h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x117d&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;65h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x0130&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;66h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x1360&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;67h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x14b0&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;68h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x1f10&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;al=2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;6ah&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x4910&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;6bh&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x563f&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;Display Power failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;70h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x51e0&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;此处代码与 INT 1eh 重合，51e0 处看起来不像正常代码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;72h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x5240&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;73h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x5394&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;76h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x5646&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;Display Low Power&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;77h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x564e&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;Display Power Empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;78h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x5664&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;清屏&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;79h&lt;/td&gt;
&lt;td&gt;&lt;tt class="docutils literal"&gt;0x54c0&lt;/tt&gt;&lt;/td&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div class="section" id="ms-2401h"&gt;
&lt;h2&gt;四通 MS-2401H 的内存地址&lt;/h2&gt;
&lt;div class="section" id="bios"&gt;
&lt;h3&gt;BIOS 工作区&lt;/h3&gt;
&lt;p&gt;0000:0000 to 0000:4000&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="with-battery-backup"&gt;
&lt;h3&gt;With battery backup&lt;/h3&gt;
&lt;p&gt;5000:0000 to 5000:000f -- BIOS data&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;打印修正 (0)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;RS232C 线路说明 (1, 2)&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;01h&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;BAUD RATE [7, 6, 5, 4]&lt;/li&gt;
&lt;li&gt;STOP BIT [3 ,2]&lt;/li&gt;
&lt;li&gt;PARITY BIT [1] (0 - odd, 1 - even)&lt;/li&gt;
&lt;li&gt;PARITY ENABLE [0] (0 - disable, 1 - enable)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;02h&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Unused [7, 6, 5, 4, 3, 2]&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Length [1, 0]&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;00: 5 bit&lt;/li&gt;
&lt;li&gt;01: 6 bit&lt;/li&gt;
&lt;li&gt;10: 7 bit&lt;/li&gt;
&lt;li&gt;11: 8 bit&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;打印修正 (3)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;空白 (4 ~ 8)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;日期 (9 ~ D)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Checksum (E ~ F)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;5000:0010 to 5000:1fff -- WP data&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><category term="misc"/></entry><entry><title>Stone MS-240x Typewriter (2): Hardware Design</title><link href="https://tifan.net/blog/2020/09/17/ms240x-chinese-typewriter-2-ms-2401h-hardware-design/" rel="alternate"/><published>2020-09-17T22:00:00-08:00</published><updated>2020-09-17T22:00:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2020-09-17:/blog/2020/09/17/ms240x-chinese-typewriter-2-ms-2401h-hardware-design/</id><summary type="html">&lt;p&gt;In case you misseed it -- I talked about the backgrounds of the MS-240x typewriter in the &lt;a class="reference external" href="https://tifan.net/blog/2020/09/09/revealing-a-forgotten-chinese-compute-history-stone-ms240x-chinese-typewritter-1-background/"&gt;previous article&lt;/a&gt;. In this article, I'm going to discuss the hardware design of the legendary Stone MS-240x Chinese Typewriter (四通 MS-240x 中英文打字机) designed and sold in the mid-1980s …&lt;/p&gt;</summary><content type="html">&lt;p&gt;In case you misseed it -- I talked about the backgrounds of the MS-240x typewriter in the &lt;a class="reference external" href="https://tifan.net/blog/2020/09/09/revealing-a-forgotten-chinese-compute-history-stone-ms240x-chinese-typewritter-1-background/"&gt;previous article&lt;/a&gt;. In this article, I'm going to discuss the hardware design of the legendary Stone MS-240x Chinese Typewriter (四通 MS-240x 中英文打字机) designed and sold in the mid-1980s.&lt;/p&gt;
&lt;p&gt;Both the hardware and the BIOS was designed by ALPS Electric Co. ALPS provided a BIOS reference manual before the development began so that the developers in China could just write an emulator on the PC emulating the ALPS BIOS, and just focus on the development of the word processor.&lt;/p&gt;
&lt;div class="section" id="the-hardware"&gt;
&lt;h2&gt;The Hardware&lt;/h2&gt;
&lt;img alt="Stone MS-2401H 四通 MS-2401H 打字机" class="align-left" src="https://tifan.net/images/20200917-ms-2401h.jpg" style="width: 80%;" /&gt;
&lt;p&gt;(&lt;a class="reference external" href="https://www.lty.me/stone-ms-2401h/"&gt;Picture taken by &amp;#64;lty1993&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;As I mentioned in the previous article, the hardware is just a 8088 machine in its core. In the 80s, the Japanese engineer reverse engineered and implemented Japanese counterparts of almost all popular chips in the west. The ALPS motherboard is not an exception to that.&lt;/p&gt;
&lt;p&gt;I bought the machine on Xianyu (Chinese eBay equivalent) and shipped it to &amp;#64;lty1993 in China for examination, disassembly, and ROM dumps. The machine is quite heavy -- shipping it to the west coast would probably cost 200 USD. Guess there won't be any Stone Chinese Typewriters in the US for a while!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="processor-nec-v20"&gt;
&lt;h2&gt;Processor: NEC V20&lt;/h2&gt;
&lt;p&gt;Instead of using the actual 8088 processor, MS-240x series used the NEC V20 running at different clock frequencies. The original MS-2400 clocks at 4.9125 MHz, the upgraded MS-2401 runs at 8 MHz, and the later MS-2401H model runs at 10 MHz.&lt;/p&gt;
&lt;p&gt;The V20 is 30% faster than the original 8088 running at the same clock speed, providing additional power for the heavy lifting work a Chinese Typewriters needs to do.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="memory-hard-wired-memory-map-with-page-control"&gt;
&lt;h2&gt;Memory: Hard-wired Memory Map with Page Control&lt;/h2&gt;
&lt;p&gt;The RAM itself is not interesting at all. It's just a bunch of Japanese made SRAM connected to the address bus of the processor.&lt;/p&gt;
&lt;p&gt;The BIOS is mapped at &lt;cite&gt;0xF8000&lt;/cite&gt; to &lt;cite&gt;0xFFFF&lt;/cite&gt;, and CPU will execute the instruction at &lt;cite&gt;0xFFFF0&lt;/cite&gt; -- that's the convention for 8088. So naturally, the BIOS was hard wired at that address.&lt;/p&gt;
&lt;p&gt;Remember we talked about the Chinese fonts? It's a mask ROM, and it is quite large -- larger than the address space of 8088 processor if we include high precision Chinese fonts at 24x24 dot (which is still pretty awful in today's standard). To solve this problem, all external ROMs were divided into 32KB pages. To access any page in the ROM, you would send a command to the ASIC to select the page first (bank switching) before reading memory from the hard wired memory location. Sounds like a MMU? Well, this &lt;em&gt;is&lt;/em&gt; a poor man's MMU.&lt;/p&gt;
&lt;p&gt;One thing worth noting is that all models have built in battery backup units. Newer models (such as MS-2401) can even operate with battery with up to 3 hours battery life -- it almost makes the typewritter a laptop with a built-in printer.&lt;/p&gt;
&lt;p&gt;Here's the memory map for various models of the Chinese typewriter.&lt;/p&gt;
&lt;img alt="Memory Map for MS-2400" class="align-left" src="https://tifan.net/images/20200917-ms-2400-memory-map.png" style="width: 60%;" /&gt;
&lt;p&gt;MS-2400 have the Chinese font mapped at &lt;cite&gt;0xA0000&lt;/cite&gt; with 16 pages in total. It can support up to 3 Chinese IMEs (input methods, such as Pinyin, Wubi or Cangjie) -- a standard IME comes with the machine, up to 2 additional IMEs can be purchased as a EPROM chip inserted in the expansion ROM socket. As there's only 1 IME socket, regardless of how many IMEs would you purchase, you'll always get just one 64KB EPROM. The keyboards are mapped at &lt;cite&gt;0x90000&lt;/cite&gt; and have up to 3 pages in total.&lt;/p&gt;
&lt;p&gt;When the machine was designed, there's also an expansion socket at &lt;cite&gt;0xE8000&lt;/cite&gt;. However, the expansion socket was never used.&lt;/p&gt;
&lt;p&gt;As the only display device is a 240x64 LCD, the VRAM is just 2KB in size mapped at &lt;cite&gt;0x80000&lt;/cite&gt;.&lt;/p&gt;
&lt;img alt="Memory Map for MS-2401" class="align-left" src="https://tifan.net/images/20200917-ms-2401-memory-map.png" style="width: 60%;" /&gt;
&lt;p&gt;MS-2401 is significantly more capable with a bigger LCD display, larger RAM, and larger Chinese font ROM. To conserve mask ROM space, all font data in the mask ROM was compressed.&lt;/p&gt;
&lt;img alt="Memory Map for MS-2401H" class="align-left" src="https://tifan.net/images/20200917-ms-2401h-memory-map.png" style="width: 60%;" /&gt;
&lt;p&gt;You might wonder what does &amp;quot;V-RAM (CRT 用)&amp;quot; in MS-2401H/01C mean. MS-2401H/01C is the top of the line model in MS-2401 series featuring ability to attach an external monitor. The graphics chip is &lt;cite&gt;MGP TM6066A&lt;/cite&gt;, a Hercules clone, with MDA output.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="system-devices"&gt;
&lt;h2&gt;System Devices&lt;/h2&gt;
&lt;p&gt;We all know the 8088 is not a very capable machine. ALPS custom made a few ASICs to connect system devices such as printers, keyboards and LCD monitors to the system. That's also what makes it extremely hard to write an emulator -- without knowing exactly how the ASIC works, it's close to impossible to emulate all devices and peripherals. Even with the original designer's help, we still can't be quite sure what is the exact IO address for each device, let alone determining what each command would do.&lt;/p&gt;
&lt;p&gt;But anyway, we do have an rough idea of what the system is doing.&lt;/p&gt;
&lt;div class="section" id="external-storage-device"&gt;
&lt;h3&gt;External Storage Device&lt;/h3&gt;
&lt;p&gt;The first model, MS-2400, have an audio cassette connector running at 1200bps. Each cassette can hold around 500KB of data, or 250k Chinese characters.&lt;/p&gt;
&lt;p&gt;In 1986, when 3 1/2 inch disk just came out, Mr Jizhi Wang chose to use the very new technology in MS-2401. This is a killer function at that time, because digital documents could be finally archived relatively cheaply. Of course you could always use a computer, but that's a big upfront investment.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="keyboard"&gt;
&lt;h3&gt;Keyboard&lt;/h3&gt;
&lt;img alt="Memory Map for MS-2401" class="align-left" src="https://tifan.net/images/20200917-ms-2401-keyboard.jpg" style="width: 80%;" /&gt;
&lt;p&gt;It's not a ANSI keyboard. The design seems to be inspired by JIS keyboard, and was fully translated into Chinese -- you can't even find &amp;quot;Ctrl&amp;quot; on the keyboard, instead, you'll see &amp;quot;控制&amp;quot; (lit. control). This flattens learning curve for the typewriter, as it doesn't feel foreign to the users. Just like we say &amp;quot;it's all Chinese to me&amp;quot; -- the Chinese users would say &amp;quot;it's all English to me&amp;quot; -- because it really is!&lt;/p&gt;
&lt;p&gt;One interesting fact to point out is instead of commonly seem Esc, Tab, Caps Lock, Shift, Ctrl arrangement on the left, the keyboard is actually 半/全 (half width / full width), Tab, Ctrl, Shift, 常用字 (frequently used characters). Of course, it's a Chinese typewriter, Caps Lock isn't that important after all.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="printer"&gt;
&lt;h3&gt;Printer&lt;/h3&gt;
&lt;p&gt;It sees that the printer only accepts low level commands -- or shall we say, the printer itself does not have a controller. According to the reference manual, the printer head and motor are directly controlled by the ASIC. It also needs a few dedicated timers.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="asic-and-fdd-controller"&gt;
&lt;h3&gt;ASIC and FDD Controller&lt;/h3&gt;
&lt;p&gt;In MS-2401H, there are 2 ASICs, each of them contains around 8000 gates. the model is uPD91260GD-5BD and uPD91261GD-5BB.&lt;/p&gt;
&lt;p&gt;The floppy controller for MS-2401 MS-2401H is UPD72067GC.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The MS series machines are classical examples of pushing the hardware to its limits. Most people would simply say it's impossible to use a 8088-equivalent to drive a Chinese typewriter, but the engineers did it. By abusing the system and designing chips around the 8088, they were even able to map memory larger than the actual address space of the machine! Hats off to the hardworking engineers both in Stone Company and ALPS Electric.&lt;/p&gt;
&lt;p&gt;Another thing to point out is Stone Company wrote fabulous documentations. It's really pleasing to read, contains a lot of technical details, and in some occasions, it teaches you electrical engineering! It even contained the layout of the diagnostics program so that you can just disassemble them and add new functionalities should you need them.&lt;/p&gt;
&lt;img alt="manga illustration in technical document" class="align-left" src="https://tifan.net/images/20200917-stone-documentation-manga.png" style="width: 60%;" /&gt;
&lt;p&gt;Plus, the manga illustration is pretty cute. Haven't seen them for a long long time.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="reference-docs"&gt;
&lt;h2&gt;Reference Docs&lt;/h2&gt;
&lt;p&gt;I found two books about the typewriter from 360buy and Kongfuzi. To preserve history, we've digitalized them and uploaded to the Internet Archive.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="https://tifan.la/NZ6vhi"&gt;四通打字机原理与维修 (Design and Repair Guide for Stone Typewriters)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="https://tifan.la/g8qk2D"&gt;四通打字机电原理图 (Schematics of Stone Typewriters)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stay tuned for the next article, where I'm going to talk about the design of the BIOS.&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"/><category term="tech"/></entry><entry><title>Revealing a Forgotten Chinese Compute History: Stone MS-240x Typewriter (1): Background</title><link href="https://tifan.net/blog/2020/09/09/revealing-a-forgotten-chinese-compute-history-stone-ms240x-chinese-typewritter-1-background/" rel="alternate"/><published>2020-09-09T00:30:00-08:00</published><updated>2020-09-09T00:30:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2020-09-09:/blog/2020/09/09/revealing-a-forgotten-chinese-compute-history-stone-ms240x-chinese-typewritter-1-background/</id><summary type="html">&lt;p&gt;While typewriters for European languages was available since the 1800s, Chinese typewriter was always hard to build. In the late 80s in PR China, a company, Stone Company (四通公司), in coordination with Mitsui &amp;amp; Co., Ltd, released a series of revolutionary Chinese typewriters. It was adequately priced, user …&lt;/p&gt;</summary><content type="html">&lt;p&gt;While typewriters for European languages was available since the 1800s, Chinese typewriter was always hard to build. In the late 80s in PR China, a company, Stone Company (四通公司), in coordination with Mitsui &amp;amp; Co., Ltd, released a series of revolutionary Chinese typewriters. It was adequately priced, user friendly, and from a n engineer's point of view, a masterpiece in engineering.&lt;/p&gt;
&lt;p&gt;With help of Mr Wang Jizhi (王缉志), the Chief Scientist at Stone Co and inventor of Stone Chinese typewriter, I was able to obtain some technical documentation on the original Stone typewriter. This series of article discusses about the background, the technical details, and the long term effects of the revolutionary typewriter.&lt;/p&gt;
&lt;div class="section" id="background"&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;CJK computing is hard. Depending which standard to take, there are 4800 (常用國字標準字體表 / List of Frequently used National Character Standard Font) or 4400 (常用漢字表 / List of Jōyō Kanji) or 3500 (通用规范汉字表 / List of Commonly Used Standard Chinese Characters) frequently used characters. Considering not-so-frequently-used-but-necessary characters, we'll be looking at something around 10k characters.&lt;/p&gt;
&lt;p&gt;As we know, there're only 7 bits in ASCII, which means it can hold up to 127 characters. The reason being 7 bits? To reduce cost. Apparently, ASCII is not enough to support anything that's not English. Pioneers in Japan opted to use 8 bit encoding (JIS X 0201) to allow Katakana -- well, that's better than nothing. Unfortunately, Kanji or even Hiragana are not available.&lt;/p&gt;
&lt;p&gt;To solve this problem, a very large amount of internal encodings was released by multiple companies and government entities. But there're one common thing -- all characters were encoded with multiple bytes (2 or 4, depending on amount of characters to support).&lt;/p&gt;
&lt;p&gt;Another problem is CJK font. With so many characters to support and display, font is a big problem. If we use 12x12 pixels to display each CJK character, it would take a whopping amount of 18 bytes of space! For an entire set of Chinese characters included in GB2312, it will take around 122 KB of space. In the 70s or 80s, memory are extremely expensive. The IBM PC comes with just 64KB of RAM and 64 KB of ROM and costs 1565 USD in 1981. Remember, PR China was still in extreme poverty, and take home pay for an average teacher is just 30 RMB (10 USD) per month. Nobody wants to use 13 years worth of salary to buy a IBM PC yet _still_ can't use your language! A solution must be made!&lt;/p&gt;
&lt;p&gt;Well, the solution back then was hardware based. Mask ROM is a really good solution to store large amount of data in the 80s. With a specifically built, IBM PC compatible display adapter, the display adapter is able to display CJK characters if the screen is in text mode. However, the add-on card is not cheap, as it retails at around 2000 USD, more expensive than a PC!&lt;/p&gt;
&lt;p&gt;The most common use of computers in the 80s is word processing. Businesses and government entities literally just buy the computers for typesetting, and schools buy them to print stuff on mimeograph wax papers for the students. So, naturally, the 80-20 rule kicks in: if 80% of the users are just buying the computers for typesetting and printing, why buy an expensive PC, an expensive add-on card, and a printer, why not just release a Chinese typewriter?&lt;/p&gt;
&lt;p&gt;Well, the answer is, there were Chinese typewriters already, but they all suck.&lt;/p&gt;
&lt;p&gt;Most typewriters are imported from Japan and have a built-in thermal printer. Not a good choice in China, as thermal papers has to be imported. Also, thermal paper are just not good for archival purposes. Besides that, you can't use thermal printer to print on wax papers.&lt;/p&gt;
&lt;p&gt;Some other typewriters are not user friendly, and some of them have fonts not suitable for use in China. This might be a surprise to non-CJK users, but the reality is, different culture sometimes use different variants to write the same character, and while still perfectly readable by other Chinese character using cultures, it feels foreign and strange.&lt;/p&gt;
&lt;p&gt;So here comes the Stone Chinese Typewriter, a typewriter that's very user friendly, with localized Chinese fonts, a 24 pin dot matrix printer, and more importantly, only costs around 13500 RMB in total (~ 3k USD), almost 1/5 the price for a complete set of Chinese computer.&lt;/p&gt;
&lt;p&gt;Some interesting fact on the manufacturing process: the electrical boards and parts was imported from Japan costing around 6000 RMB. When arrived at Beijing, SOTEC (Stone Office Terminal Equipment Co, a subsidiary of Stone Co) assembles the machine, put the EPROM containing the software in, and performs QA checks. The machine will then be sold to the Office Automation Department of Stone Co at 9000 RMB. The OA department sells the machine to dealers across the country at around 11000 RMB. The total BOM cost is at around 50% of the retail price, much higher than the standard rate of 30% in consumer electronics industry such as TV sets.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="technical-overview"&gt;
&lt;h2&gt;Technical Overview&lt;/h2&gt;
&lt;p&gt;I'm not going to talk a whole lot about technical details in this article. This will come later when I finish writing the emulator for the typewriter. However, I'd still like to share some high level overview on the machine itself.&lt;/p&gt;
&lt;p&gt;The original MS-2400 typewriter is actually a computer. This should not be a surprise to anyone, as you really needed a serious computer to perform word processing tasks.&lt;/p&gt;
&lt;p&gt;The hardware was designed by ALPS Electric in Japan, and all software was architectured by Mr Wang Jizhi and designed by himself with a small team.&lt;/p&gt;
&lt;p&gt;NEC V20, an Intel 8088 clone, is the heart of the system. However, this is not a IBM PC clone. The similarity with PC really stops here, as BIOS, IO devices, and even the keyboard are not PC compatible. Heck, even the PIT are not compatible!&lt;/p&gt;
&lt;p&gt;This machine featured 64 KB of RAM, 64 KB of EPROM for BIOS and word processor, 576 KB Chinese Character Generator (2 * 256 KB, 1 * 128 KB mask ROM), a 32 KB Chinese dictionary (not for looking up words, but for converting keystrokes to Chinese characters, later commonly known as Input Method), and another optional 32 KB Chinese dictionary for secondary input method.&lt;/p&gt;
&lt;p&gt;There are no video output capability for MS-2400. The display is a 240x64 LCD controlled by OKI MSM6255 LCD controller.&lt;/p&gt;
&lt;p&gt;There's also a Ni-Cd battery in the machine, which could back up the memory for around 30 days.&lt;/p&gt;
&lt;p&gt;To support the printer and keyboard, there are 4 timers with 3 modes (including NMI). The printer can print up to 20 characters per second.&lt;/p&gt;
&lt;p&gt;The external memory is audio cassette. It has standard 3.5 mm audio jacks to interface with an external recording device.&lt;/p&gt;
&lt;div class="section" id="the-os"&gt;
&lt;h3&gt;The OS&lt;/h3&gt;
&lt;p&gt;No, there are no OS. The BIOS itself is the OS. As the machine is not designed to run any PC OS, ALPS simply implemented everything as software interrupts. As the BIOS is mapped to the fixed F000:8000 address, all programs can just execute in place, saving precious memory. Even the Chinese font loading feature was done in the BIOS itself, so that user program (the word processor itself) don't have to worry anything about the hardware at all.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="development"&gt;
&lt;h3&gt;Development&lt;/h3&gt;
&lt;p&gt;The development of word processor software was done on an IBM PC. Mr Shimadzu from ALPS Electric provided Mr Wang a BIOS routine reference manual, and Mr Wang first implemented the same BIOS routine on an IBM PC.&lt;/p&gt;
&lt;p&gt;From there, all the development work are much easier, as the dev team can simply stay in China and develop everything locally to save cost on travel. Remember, China was still poor, and you just can't afford to have an entire team to stay in Japan for months.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="memory-maps"&gt;
&lt;h3&gt;Memory Maps&lt;/h3&gt;
&lt;p&gt;All the flash memory are hard wired to the processor. This enables execution in place. All the software are in the flash memory, as there're no easy way to use external programs. The only external storage interface is a 1200bps audio interface.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="interrupts-as-api"&gt;
&lt;h3&gt;Interrupts as API&lt;/h3&gt;
&lt;p&gt;This shouldn't be a surprise as well. After all, we're talking about a real mode x86 machine. The interrupts itself is the &amp;quot;stable API&amp;quot;, and different team members would solely rely on interrupts to call each other's subroutines.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="event-driven"&gt;
&lt;h3&gt;Event Driven&lt;/h3&gt;
&lt;p&gt;The word processing software itself is event driven. The machine is just looping forever, waiting for a hardware interrupt to come, and the request will be dispatched by the ISR.&lt;/p&gt;
&lt;p&gt;In the next chapter, we'll be discussing about the disassembled BIOS of the machine. Stay tuned!&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><category term="misc"/><category term="tech"/></entry><entry><title>速记一下昨天奇妙遭遇 （a.k.a 水逆）</title><link href="https://tifan.net/blog/2019/11/12/mercury-retrograde/" rel="alternate"/><published>2019-11-12T20:00:00-08:00</published><updated>2019-11-12T20:00:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2019-11-12:/blog/2019/11/12/mercury-retrograde/</id><summary type="html">&lt;p class="first last"&gt;似乎最近和交通工具犯冲。应该拜一下管交通的神 —— 大概是弼马温？&lt;/p&gt;
</summary><content type="html">&lt;p&gt;2019 年双十一，也是从多伦多回家的那一天。原定了 YYZ-SFO-LAX 的机票，衔接时间只有 45 分钟。前一天晚上拿出 United App 抓紧 checkin  以便至少选个 E+ 座位 —— 只发现无法完成 checkin，要求上传护照信息。遂拿出护照进行扫描，上传后 App 自动跳转回上传护照界面，并最终通知无法完成 checkin，请见 agent. 作为生活在美国的外星人，这种国际化支持不够好不支持外国护照的事情也没少见，随他便吧，等去机场人工 CI 便是。&lt;/p&gt;
&lt;p&gt;一早，收到了邮件通知 UA CPU 居然 clear 了，窃喜。&lt;/p&gt;
&lt;p&gt;下午，多伦多的雪越来越大 —— 11 月本来不应该有这么大的雪。于是开了个玩笑 —— 说不定飞机延误，晚上要在三番住一晚。拿出 Uber，决定抓紧打车去机场。只见 App 提示需要 20 分钟才会有车来接。接下来，每 match 到一个司机，过几分钟后司机便会直接取消订单 —— 谁让这雪下的大呢。终于，有一个印度女司机接了单。然而，左看右看，司机从高速下来的路上转错了方向，只能在堵成红色的地图上 U-turn. 正准备放弃 Uber，开车去机场的时候，司机终于慢悠悠的开了过来。搬上行李，司机一脚油门 —— 打滑了，差点在 stop sign 上撞上迎上来的一辆 SUV。&lt;/p&gt;
&lt;p&gt;千叮咛万嘱咐司机一定要开 407ETR，在 427 上堵了二十分钟后，终于到了 YYZ。没想到，这一切只是一系列倒霉事儿的开始。&lt;/p&gt;
&lt;p&gt;找到打航柜台，只见柜台服务人员不紧不慢的一个一个扫护照，用二指禅敲键盘，十分钟处理一个顾客。在等到不耐烦的时候，终于轮到了我打票托运。一路小跑过了安检和美国边境，一看还有十来分钟，于是决定走去 lounge 吃点东西垫垫肚子 —— 毕竟接下来还要飞六个小时，机场也没什么好吃的，垫垫肚子也成。走到 lounge，只见门口摆了一块牌子 ——&lt;/p&gt;
&lt;pre class="literal-block"&gt;
We are currently at
MAXIUM
CAPACITY
and no longer accepting
guests at this time
&lt;/pre&gt;
&lt;p&gt;好在准备离开的时候有几个人离开，于是进去吃了一盘意面。&lt;/p&gt;
&lt;p&gt;到了登机时间，跑到登机口，意想不到的事情发生了 ——因为上一班飞机有人不想下飞机 （！），我们的航班不能登机。&lt;/p&gt;
&lt;p&gt;大概最终这位旅客还是决定了（或被 CBSA 决定）下飞机，我的航班的旅客最终还是上了飞机。&lt;/p&gt;
&lt;p&gt;左等右等，舱门总是不关。乘务员广播称，由于副驾驶不见了（！），航班要延误。衔接并不是问题，因为本来计划飞行时间是 6 小时，实际只需要 5 小时多一点点，只要速度够快，衔接转机的旅客完全不会有问题。由于实在无聊，翻了下下一班飞机的状态，让人心安的是，下一班飞机也延误了，只要两个小时之内这班航班可以起飞，就可以晚上回到洛杉矶。与此同时，打航发来了短信：&lt;/p&gt;
&lt;blockquote&gt;
Delay update: Your 7:00pm flight from Toronto to San Francisco is delayed further because we're working to find a new flight crew.&lt;/blockquote&gt;
&lt;p&gt;于是在飞机上继续装死。&lt;/p&gt;
&lt;p&gt;又一次客舱广播就不是什么好消息了。乘务员说，由于加拿大法律规定乘务员上飞机后 90 分钟内没有飞的话需要重新过海关，由于海关晚 9 点关门，为了保证航班可以正常，乘务员需要现在重新过海关。因此，所有人也需要离开飞机。这个解释令人很不可理解 —— YYZ 明明是个二十四小时工作的机场！&lt;/p&gt;
&lt;p&gt;下飞机以后，检票柜台上的工作人员光速躲到了限制区域后，拒绝回答任何人的问题。没错儿，这很 UA。又过了半个小时，有人躲在玻璃后面进行了广播 ——&lt;/p&gt;
&lt;blockquote&gt;
由于天气原因，本地航班取消，请重新入境加拿大，提取行李，并与地面工作人员联系。&lt;/blockquote&gt;
&lt;p&gt;然后工作人员一溜烟消失了。诸位旅客开始 boo. 同时， UA App 里立即显示，航班因为天气原因取消了航班。显然，这又是一次 UA 的店大欺客操作 —— 反正你也不能怎么样我，我就说是天气原因，不给你酒店，你自己活该咯。&lt;/p&gt;
&lt;p&gt;这时是晚上 8:30，机场最热闹的时候。海关也排起了长队。好在 Nexus 没有队。等了接近一个小时，拿到了行李。跑去了出发层，只见打航柜台只剩下了两个人。因为跑的快，所以在比较早的时候就改到了第二天的加航直飞机票。地勤并不乐意帮我 UA 改 AC，但在我指出 Rule 240 之后，还是给我签转了 Y 舱的纸票。同时，告诉我在另外一队（就是另外一个人）处问是否有任何酒店安排。&lt;/p&gt;
&lt;p&gt;另一队的速度就慢了更多。刷了 Marriott App，发现附近还有一家 Delta 不算贵，最坏情况也是自己定一晚酒店凑合下回家慢慢撕。排队排到了我，我和几乎不讲英语的从 Québec 来的地勤说你们这是 fraud，地勤以最典型的 UA 态度告诉我，你爱怎么办怎么办，反正我们是不会给你酒店的，也不会给你任何形式的 accomodation，爱睡机场自己睡机场去。什么？你想知道我叫什么？就不告诉你啊就不告诉你。&lt;/p&gt;
&lt;p&gt;看来美国文化的影响力还是很大的嘛，这么快就把加拿大人熏陶的和美国人一样了。&lt;/p&gt;
&lt;p&gt;于是花了一百出头美元定了个 Delta，打车去酒店。Uber 司机又是一个印度女司机。累的要命，我就直接在车上发呆装死了。只见司机用颤抖的咖喱英语喊我 ——&lt;/p&gt;
&lt;blockquote&gt;
Sir, our car is not moving at all. Do you know what to do?&lt;/blockquote&gt;
&lt;p&gt;睁眼一看，车陷在了雪里，怎么踩油门都不向前走。但是 —— 有没有搞错？你问一个加州人在雪里怎么开车？&lt;/p&gt;
&lt;p&gt;于是告诉司机，请打开闪灯，不行就倒下坡。。。司机松了下油门，车就向后滑。反正这个坡也爬不上去了，大不了就换个路走。最后，车滑到了坡底，违章调了个头，换了个路，终于到了酒店。&lt;/p&gt;
&lt;p&gt;到了房间，洗了个澡，发现卫生间居然有耳塞 —— 酒店考虑的很周到嘛。戴上耳塞，定好闹钟，充上手机和电脑，睡觉。&lt;/p&gt;
&lt;p&gt;五分钟后，一架飞机降落了。&lt;/p&gt;
&lt;p&gt;三分钟后，另一架飞机降落了。&lt;/p&gt;
&lt;p&gt;四分钟后，下一架飞机。&lt;/p&gt;
&lt;p&gt;………………&lt;/p&gt;
&lt;p&gt;打开窗子，我惊呆了：窗外正是 localizer 天线矩阵……&lt;/p&gt;
&lt;p&gt;好吧，吃褪黑素。一片，两片，睡着了就醒，醒了继续睡，睡了又醒 …… 无数次后，看 YYZ noise abatement procedures —— 全都是废话，毕竟正对着 Runway 24，就算 idle 也能吵死……&lt;/p&gt;
&lt;p&gt;终于到了五点半。上车，checkin. 加航的服务果然比打航好一百倍，排队人少，操作速度也快，就算是纸票换开也一分多钟就给了我登机牌，外加帮我托运好了。&lt;/p&gt;
&lt;p&gt;继续一路狂奔，上了飞机以后累的不行。戴上豌豆射手 AirPods Pro，一分钟后就睡着，什么时候起飞的都不知道。加航 Y 舱管饭，要了份 Mac and Cheese 吃，味道居然还不错。&lt;/p&gt;
&lt;p&gt;回到了第三世界洛杉矶机场，坐大巴去 LAXit。用 Lyft 叫了个车，排到了个黑叔叔司机。车很脏 —— 算了就这样吧，反正是要回家的。没想到，最后这点路也会出幺蛾子。&lt;/p&gt;
&lt;p&gt;上了 405，司机在转 10 的时候，居然开！错！了！路！&lt;/p&gt;
&lt;p&gt;好吧，随便吧。转个圈也能到家。&lt;/p&gt;
&lt;p&gt;开到了家附近，意想不到的事情发生了 —— 在 one way stop 上，这位黑叔叔一脚油门过去，差点撞上了有路权的另一辆车。&lt;/p&gt;
&lt;p&gt;好在没撞上。&lt;/p&gt;
&lt;p&gt;回家，洗澡，吃饭，然后有了这篇文章。&lt;/p&gt;
</content><category term="misc"/></entry><entry><title>适合家用的多合一服务器</title><link href="https://tifan.net/blog/2019/08/31/all-in-one-server-for-homelab/" rel="alternate"/><published>2019-08-31T01:39:00-07:00</published><updated>2019-08-31T01:39:00-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2019-08-31:/blog/2019/08/31/all-in-one-server-for-homelab/</id><summary type="html">&lt;p&gt;最近多次迁移了家用服务器，最后得出的结论是，市场上没有什么值得买的成品家用服务器。&lt;/p&gt;
&lt;p&gt;其实作为家用专业用户来说 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;最近多次迁移了家用服务器，最后得出的结论是，市场上没有什么值得买的成品家用服务器。&lt;/p&gt;
&lt;p&gt;其实作为家用专业用户来说，服务器的需求是相对来说比较简单的，大概满足下面的条件就可以了：&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;噪音不要太大，最好是无风扇设计&lt;/li&gt;
&lt;li&gt;有两个以上的 PCIe 口 （可以期待的板卡有 HBA 卡，10G 网卡，NVMe 卡）&lt;/li&gt;
&lt;li&gt;有 3 个以上的硬盘 bay&lt;/li&gt;
&lt;li&gt;有真正的存储背板，不是用 SATA 线凑合的山寨硬盘架&lt;/li&gt;
&lt;li&gt;可以支持 32G 以上的内存&lt;/li&gt;
&lt;li&gt;可以支持比较正常的处理器 （i7 级别以上），单核频率高，核心数相对少&lt;/li&gt;
&lt;li&gt;带有 BMC&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;只可惜噪音不要太大这一点就把市面上所有的服务器全都筛掉了。关于存储背板 —— 没有存储背板的话没办法用 HBA 卡 （我相信家用用户一般都不想用 RAID 卡）。当然，如果有这么多 PCIe 设备的话，散热还真的可能是个大问题。&lt;/p&gt;
&lt;p&gt;最后的结论是，吵就吵吧，还是继续用 Dell T330 好了。&lt;/p&gt;
</content><category term="misc"/></entry><entry><title>洛杉矶办理中国护照续期的流水账以及边界条件测试</title><link href="https://tifan.net/blog/2019/08/30/chinese-passport-los-angeles/" rel="alternate"/><published>2019-08-30T18:27:23-07:00</published><updated>2019-08-30T18:27:23-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2019-08-30:/blog/2019/08/30/chinese-passport-los-angeles/</id><summary type="html">&lt;div class="section" id="id2"&gt;
&lt;h2&gt;续期护照的手续&lt;/h2&gt;
&lt;p&gt;续期护照需要网上预约。预约的网站默认不跳转 HTTP 到 HTTPS，强迫症患者请自行确认访问了 SSL protected endpoint. 没有网上 …&lt;/p&gt;&lt;/div&gt;</summary><content type="html">&lt;div class="section" id="id2"&gt;
&lt;h2&gt;续期护照的手续&lt;/h2&gt;
&lt;p&gt;续期护照需要网上预约。预约的网站默认不跳转 HTTP 到 HTTPS，强迫症患者请自行确认访问了 SSL protected endpoint. 没有网上预约的时候，原则上无法进入领事馆办理。当然，有原则上也有例外情况。在我上次去领事馆续期时，有一个大约 80 岁的老人未预约即来申请，领事官特事特办也予以受理。&lt;/p&gt;
&lt;p&gt;网上预约时，可能会遇到照片不合格的情况。这很可能是网站的 bug. 如果遇到了这个问题，可以把自己的照片刻录一张光盘带到领馆。注意领馆只收光盘，不接受软盘或优盘。&lt;/p&gt;
&lt;p&gt;洛杉矶领馆的证件部并不在领馆内，附近没有停车场。可以停车到 Wilshire / Vermont 火车站的地下停车场 &lt;a class="reference external" href="https://goo.gl/maps/8RNxUrG2MrhAvDqi6"&gt;Wilshire Vermont Parking Garage&lt;/a&gt; 内， 停车场入口  在 Shatto Pl 靠近 Wilshire 处，十块钱以内可以搞定，很容易停。也可以附近找找停车位。&lt;/p&gt;
&lt;p&gt;到达领事馆后，上电梯三楼，左手边小屋是护照预约处，右手边是签证申请处，不要走错门。进门时，保安会看你的预约表上的预约时间，并给你一个 ticket. 拿到 ticket 以后等叫号即可。大厅内 Verizon 手机信号极烂，也不大可能用电脑办公 （没 Wi-Fi），所以注意带着娱乐用品。&lt;/p&gt;
&lt;p&gt;目前 （2019 年下半年） 洛杉矶领馆办理护照必须存留指纹，对此感到不适的可能需要换家领馆，或者申请政治庇护后申请美国的旅行证件 (Travel document).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;最佳实践：需要很快续期护照怎么办&lt;/h2&gt;
&lt;p&gt;快的方式是在中国续期。需要在户口所在地的省份，出省不行。加急办理护照需要出示有效的证据，例如公司邀请函等。马上开学的用录取通知书一类的也可以。办理速度是三天拿到。美国不保证速度。省外也不保证速度。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;边界条件：需要手里有护照怎么办&lt;/h2&gt;
&lt;p&gt;作为常识，护照续期时，申请人手里是没有护照的，而且在护照续期后，原护照将被剪角注销。存在各种情况，申请人可能需要护照身份证明文件（例如没有其他本地 ID 时），中国领馆允许在续期护照时索取回中国护照。&lt;/p&gt;
&lt;p&gt;需要索取回护照并不需要额外的手续，只需要在领事官面前说明理由、提供证据即可。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;边界条件：申请护照的同时需要出国怎么办&lt;/h2&gt;
&lt;p&gt;答案：最好别这么干。在第一次申请时，过两天我需要去新加坡中国以及香港。在向领事官出示了机票后，领事官表示最好撤回申请，因为不能保证系统什么时候会宣布注销护照，特别是涉及到中国时 —— 中国边检会得到护照注销数据库并把注销的护照作为失效证件处理 (working as intended). 其他国家 &lt;em&gt;可能&lt;/em&gt; 不关心，但是也 &lt;em&gt;可能&lt;/em&gt; 关心。&lt;/p&gt;
&lt;p&gt;中国护照的电子护照是真的基于 PKI 体系构建的， 并真的会发布标准的 CRL 清单。简单的说，别给自己找麻烦 。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id6"&gt;
&lt;h2&gt;边界条件：撤回护照申请&lt;/h2&gt;
&lt;p&gt;可以撤回护照申请。领事官会退还给你护照申请表。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id7"&gt;
&lt;h2&gt;边界条件: 中国护照什么时候会被注销&lt;/h2&gt;
&lt;p&gt;通过中国移民局微信小程序查询发现，在护照已经到达洛杉矶领馆等待 pickup 时，老护照依然显示为有效护照。这是目前的实践，并没有法律/行政手册规定一定会这样实践。另一个可能性是外交部系统与移民局系统并未做到实时同步。&lt;/p&gt;
&lt;p&gt;（移民局小程序的中国国内地区间旅行证件，例如港澳通行证、台湾大通证等的状态是接近实时更新的）&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id8"&gt;
&lt;h2&gt;边界条件：预约的有效期&lt;/h2&gt;
&lt;p&gt;在洛杉矶，当前 （2019 年下半年） 护照预约是一个月有效的。也就是说，如果当天没来得及办理或没办理成需要补材料，可以接下来一个月时间内的任何一天的早晨 9 点到领馆。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id9"&gt;
&lt;h2&gt;边界条件：预约不上怎么办&lt;/h2&gt;
&lt;p&gt;在洛杉矶，当前 （2019 年下半年） 门口的保安的人并没有计算机。前端验证了解一下。剩下的就是各显神通了。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id10"&gt;
&lt;h2&gt;数据点：护照申请的时间线&lt;/h2&gt;
&lt;p&gt;YMMV&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;第一周 周五：领事馆申请&lt;/li&gt;
&lt;li&gt;第二周 周一：您的信息已发往制证中心，待印制新护照。&lt;/li&gt;
&lt;li&gt;第二周 周二：您的护照已印制完毕，正在寄返我馆途中。&lt;/li&gt;
&lt;li&gt;第二周 周五：您的新护照已到达我馆，可以领取。&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</content><category term="misc"/></entry><entry><title>Flashing Stock LSI 2308 IT Mode Firmware on HP H220 HBA (for use on Dell R720)</title><link href="https://tifan.net/blog/2019/01/28/hp-h220-lsi-2308-9207-8i-stock-firmware-on-dell-r720/" rel="alternate"/><published>2019-01-28T00:30:00-08:00</published><updated>2019-01-28T00:30:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2019-01-28:/blog/2019/01/28/hp-h220-lsi-2308-9207-8i-stock-firmware-on-dell-r720/</id><summary type="html">&lt;p class="first last"&gt;Read this if you are unlucky enough to have Dell r720 / r720xd and want to use it as a real storage server and bypass PERC.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;If you are unlucky enough to have a Dell R720xd and wanted to get rid of the PERC RAID card, or just want to have raw disk access -- you are mostly out of luck.&lt;/p&gt;
&lt;p&gt;The official solution is to use PERC H310 Mini, however, H310 Mini has its own problems -- the disk I/O queue is very shallow (25), which is unsuitable for use with SSDs. With H710 or H710p, you have much better performance (still not in par with &amp;quot;normal&amp;quot; HBA cards), but you have to live with various RAID levels. The only viable solution seems to be to use a real LSI HBA card.&lt;/p&gt;
&lt;p&gt;As a result, I purchased a few HP branded H220 HBA cards and standard SAS cables from Taobao.com -- Dell SAS cables are proprietary cables that only work with embedded Mini PERCs. FWIW the Dell P/N of the cable should be &lt;tt class="docutils literal"&gt;C2X59&lt;/tt&gt;. The H220 card itself simply rebranded &lt;tt class="docutils literal"&gt;LSI &lt;span class="pre"&gt;9207-8i&lt;/span&gt;&lt;/tt&gt; with HP firmware. However, with older / HP firmware, your system will NOT boot and stuck at the &amp;quot;Initializing&amp;quot; POST screen forever. The only fix it to disconnect the SAS cable, but without any disks attached, the card would be useless.&lt;/p&gt;
&lt;p&gt;To fix the problem, newer firmware needs to be flashed. The firmware image itself can be downloaded from &lt;a class="reference external" href="ftp://ftp.supermicro.com/driver/sas/lsi/2308/Firmware/IT/"&gt;SuperMico FTP site&lt;/a&gt; (blocked in China), however, the sas2flash.efi P20 binary actually checks OEM data and refuses to flash stock firmware if so. P14 firmware should do it though.&lt;/p&gt;
&lt;p&gt;Take a picture of the card and make sure the S/N on the card (two groups of hexadecimal numbers) is legible. You'll need it shortly. In my case, the yellow sticker on the back of the card has S/N on it.&lt;/p&gt;
&lt;p&gt;Once you obtain the EFI binary (download address follows) and boot into EFI shell, run the following command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;sas2flash.efi -o -e &lt;span class="m"&gt;7&lt;/span&gt;
sas2flash.efi -f 2308T207.ROM
sas2flash.efi -b mptsas2.rom
sas2flash.efi -b x64sas2.rom
sas2flash.efi -o -sasaddhi XXXXXXX
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(If you have trouble entering UEFI shell, use live CD for Arch Linux)&lt;/p&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;-o&lt;/span&gt; &lt;span class="pre"&gt;-e&lt;/span&gt; 7&lt;/tt&gt; command would wipe the entire flash clean. Once you run it, make sure you flash something into the card as the card won't boot again next time.&lt;/p&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;sas2flash.efi &lt;span class="pre"&gt;-o&lt;/span&gt; &lt;span class="pre"&gt;-sasaddhi&lt;/span&gt;&lt;/tt&gt; command writes serial number for the card. Replace &lt;tt class="docutils literal"&gt;XXXXXXX&lt;/tt&gt; with the first 7 digit of the SN (in my case, those are all &lt;tt class="docutils literal"&gt;500605B&lt;/tt&gt;). When asked, type in the last 9 digits of the SN (actual value does not matter that much, since we already broke the warranty). You can use &lt;tt class="docutils literal"&gt;057F1234&lt;/tt&gt; if the SN is unknown.&lt;/p&gt;
&lt;p&gt;After reboot, you should see Avago instead of LSI now on the POST screen.&lt;/p&gt;
&lt;p&gt;Download LSI sas2flash.efi version P14 &lt;a class="reference external" href="https://tifan.la/RPKqu0/JAKpxQXIQO"&gt;from here&lt;/a&gt;. If SuperMicro takes PH20.00.07.00-IT.zip down at some point, download it from &lt;a class="reference external" href="https://tifan.la/rDCrey/qvIgCUAoCN"&gt;https://tifan.la/rDCrey/qvIgCUAoCN&lt;/a&gt;.&lt;/p&gt;
</content><category term="misc"/></entry><entry><title>IoT (屎联网) 设备</title><link href="https://tifan.net/blog/2018/12/06/internet-of-shit/" rel="alternate"/><published>2018-12-06T01:00:00-08:00</published><updated>2018-12-06T01:00:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2018-12-06:/blog/2018/12/06/internet-of-shit/</id><summary type="html">&lt;p class="first last"&gt;我说在坐的诸个厂商，都是垃圾。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;自从 &lt;strong&gt;I&lt;/strong&gt;nternet &lt;strong&gt;o&lt;/strong&gt;f shi&lt;strong&gt;T&lt;/strong&gt; 屎联网设备越来越多，这个世界也越来越不可能好了。&lt;/p&gt;
&lt;p&gt;第一次破解掉 IoT 设备还是 2000 年代，在 Linux 还是 kernel 2.4 的年代。某监控厂还算很先进的，硬盘录像机支持 Web 界面回放。出于好奇 （毕竟那个年代有多少奇怪的硬件可以联网），随手 nmap 一下，发现 —— 这货居然 telnet 大门敞开！再定睛一看， root / 123456 便拿到了 root shell. 进去一看，东西果然是一坨屎 —— 更不用说文件乱放， init 脚本乱写这种小事儿了。简单的说，这玩意儿就是搞土法炼钢，居然还炼出来了东西。大概中国传统文化中的 「&lt;em&gt;差不多就行了&lt;/em&gt;」再次起到了作用。&lt;/p&gt;
&lt;blockquote&gt;
&lt;em&gt;又不是不能用&lt;/em&gt;&lt;/blockquote&gt;
&lt;p&gt;这大概就让我开始注意起来了可以联网的奇怪的盒子 —— 大概率这玩意儿就是一帮野鸡程序员用纸糊出来东西。每次拿到盒子以后，这些野鸡程序员也从来不会让我失望：最多半小时，总能搞出来点什么奇奇怪怪的东西。要不然让它崩溃掉，要不然就拿到 root。反正得折腾点什么东西出来。&lt;/p&gt;
&lt;p&gt;后来呢，突然之间就冒出来了 IoT 这么个概念。东西便宜了， Wi-Fi 普及了，什么阿猫阿狗都可以联网了，于是这个世界变得更差了。&lt;/p&gt;
&lt;p&gt;先是一大堆某康某华的 NVR IPC 被黑，后来又是电脑城三十年精修网络老师傅强力推荐的路由器 XSS 改 DNS，后来又是各种各样的奇葩设备爆出来奇怪漏洞。&lt;/p&gt;
&lt;p&gt;为什么呢？还不是野鸡程序员嘛。举一点近期的例子好了。&lt;/p&gt;
&lt;p&gt;有种东西叫考勤机。这玩意儿的固件可以下得到。拿 John the Ripper 一跑，拿到的密码就能 telnet 进去。&lt;/p&gt;
&lt;p&gt;有种东西叫电话机。拿到的固件发现有后门，敲一下门就可以 telnet 进去。&lt;/p&gt;
&lt;p&gt;有一个厂子以为自己特聪明，固件加了密。可惜这玩意儿有个功能叫 ping。curl 一下，拿到 shell. 进去以后发现升级程序就是个 lua script，第一行是 openssl des. 固件就这么解开了。&lt;/p&gt;
&lt;p&gt;有另一个厂子自以为自己更聪明，还更高端，做了个功能叫 「防火墙」。防火墙是啥呢？当然是 iptables. 于是 curl 一下，卒。&lt;/p&gt;
&lt;p&gt;没办法，我设置了一个独立的 IoT SSID 和 Vlan，并在防火墙规中禁止任何此 Vlan 的设备访问任何内网资源，并只对 Nest 开放了有限的互联网资源 （好吧， 这玩意儿没网真没法用）。国产监控设备，则仅允许来自工作站的入站连接，禁止任何出站连接。&lt;/p&gt;
&lt;p&gt;谁让野鸡程序员统治了这个世界呢？这个世界，迟早是要完蛋的。&lt;/p&gt;
&lt;p&gt;算了，&lt;em&gt;又不是不能用&lt;/em&gt;。&lt;/p&gt;
</content><category term="misc"/></entry><entry><title>Gprinter (佳博) 热敏纸打印机 Mac / Linux CUPS 驱动</title><link href="https://tifan.net/blog/2018/03/27/gprinter-thermal-printer-unix-driver/" rel="alternate"/><published>2018-03-27T00:54:00-07:00</published><updated>2018-03-27T00:54:00-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2018-03-27:/blog/2018/03/27/gprinter-thermal-printer-unix-driver/</id><summary type="html">&lt;p class="first last"&gt;明明可以有 CUPS 驱动的，厂商太懒了。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;身在操蛋的美国，总是有一些操蛋的需求 —— 比方说寄快递要自己打 label. 当然，我也可以选择屁颠屁颠的跑去 FedEx store 在柜台上告诉 amigo 我要寄给谁什么东西，或者我也可以直接拿 letter 纸打印了糊到箱子／快递信封上，但是这不是逼格不够高么。&lt;/p&gt;
&lt;p&gt;于是我买了一部 Gprinter 佳博牌热敏纸打印机，淘宝买的。自带了 NiceLabel 软件以及 Windows printer driver. 但是，这机器两个毛病 —— 第一，它没以太网口，第二，它也没 CUPS 驱动程序。 NiceLabel 软件倒是不错，打个地址标签什么的逼格很高。&lt;/p&gt;
&lt;p&gt;为了解决问题一，基于能从中国买破烂绝对不从美国买正常产品的思路，我买了一些二手 TP-Link TL-WR802N (AR9331) 路由器，插上打印机，自己编译个 openwrt 跑个 &lt;tt class="docutils literal"&gt;p910nd&lt;/tt&gt; 就搞定了。至于问题二，就没那么容易了。&lt;/p&gt;
&lt;p&gt;USB 的通讯协议可以用 WireShark 抓包。于是我就真的抓包了。看了下包内容，是自定义的二进制而不是标准的 PostScript —— 当然，让这种东西支持 PostScript 属于脑子不好使的表现之一。不过凑近处一看，其实是个事实标准协议。当年国产厂商们为了兼容爱普生（！）打印机，使用的都是 ESC 协议。半路又跳出来了个台湾厂 TSC 卖的热敏打印机不错，于是厂商们也纷纷支持了 TSC 协议。&lt;/p&gt;
&lt;p&gt;这些协议倒是挺有意思的 —— 用户甚至可以写 BASIC 程序执行在打印机里，以便节约有限的（当然现在已经基本无所谓）带宽以及可能很受限制的上位机 CPU 资源。不过这属于我们的讨论话题之外了。&lt;/p&gt;
&lt;p&gt;想让 UNIX 用好打印机，自然是用 CUPS 了。CUPS 有一个好，那就是可以把控制和实际的 device specific control 给分开，控制 ppd 文件也很(bu)容易写对。看了下手册， ESC 协议基本上就是把图像二值化以后传给打印机就可以了。从网上一搜，居然还真有其他厂商写的二进制驱动。基于不信任二进制以及安全的考虑，请读者自行搜索 &amp;quot;TSC Mac|Linux driver&amp;quot; 获取二进制。TSC 网站有驱动可以下，自己花点功夫可以解开 copy 到 &lt;tt class="docutils literal"&gt;/usr/libexec/cups/filter/rastertobarcodetspl&lt;/tt&gt; 中。或者，你也可以通过修改 &lt;tt class="docutils literal"&gt;rastertoepson.c&lt;/tt&gt; 的方式实现 &lt;tt class="docutils literal"&gt;rastertogprinter&lt;/tt&gt; 。&lt;/p&gt;
&lt;p&gt;PPD 文件可以在这里下载到: &lt;a class="reference external" href="https://tifan.la/ZM8odX/JAB91NbEFF"&gt;https://tifan.la/ZM8odX/JAB91NbEFF&lt;/a&gt; 请注意，如果你的打印机不是 GP-1324D，那么需要进行一些计算。需要支持不同格式的纸，也需要进行计算。&lt;/p&gt;
&lt;p&gt;计算的方法：打印机分辨率 203 DPI （可以从参数中找到），打印点尺寸 0.125mm*0.125mm。 PostScript 中 1 point = 1/72 inch. 打印面积为最大 104mm * 2286mm. 经过计算，得到 PostScript 中的宽度为 10.4/2.54*72=294.80 高度为 64800.0.&lt;/p&gt;
&lt;p&gt;纸张大小： FedEx 可免费获取的 thermal label 为 4in x 5in. 因此，大小为 4*72 x 5*72 = 288 360. 若你需要加入其他的纸张大小，请修改 PPD 文件砍掉打印机重新安装 ...&lt;/p&gt;
&lt;p&gt;DPI 的设置在 &lt;tt class="docutils literal"&gt;*OpenGroup:Quality/Quality&lt;/tt&gt; 小节中。&lt;/p&gt;
&lt;p&gt;添加打印机时，只需要选择 Software - Other 即可选中 PPD 文件。这个驱动应该可以适用于绝大部分型号的热敏纸打印机。&lt;/p&gt;
</content><category term="misc"/></entry><entry><title>光明网 - 2</title><link href="https://tifan.net/blog/2017/10/25/guang-ming-wang-2/" rel="alternate"/><published>2017-10-25T12:18:00-07:00</published><updated>2017-10-25T12:18:00-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2017-10-25:/blog/2017/10/25/guang-ming-wang-2/</id><summary type="html">&lt;p class="first last"&gt;我上次说什么来着？&lt;/p&gt;
</summary><content type="html">&lt;p&gt;我上次说了光明网。&lt;/p&gt;
&lt;p&gt;下面复制 &lt;a class="reference external" href="http://archive.is/3gBaO"&gt;新闻一则&lt;/a&gt; ：&lt;/p&gt;
&lt;blockquote&gt;
【阿里云】【网络】【故障通告】
故障简述：接到用户反馈于北京时间2017年10月24日 09:15左右开始，国内无法通过ssh远程访问香港及其他海外地域个别ECS服务器（海外客户访问正常）。阿里云已经第一时间向运营商报障，目前运营商尚未反馈恢复信息。&lt;/blockquote&gt;
</content><category term="misc"/></entry><entry><title>光明网 (광명망)</title><link href="https://tifan.net/blog/2017/08/04/guang-ming-wang/" rel="alternate"/><published>2017-08-04T21:06:00-07:00</published><updated>2017-08-04T21:06:00-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2017-08-04:/blog/2017/08/04/guang-ming-wang/</id><summary type="html">&lt;p class="first last"&gt;互联网是健忘的，人也是。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;互联网和人都是健忘的。温水煮青蛙的策略也是成功的。&lt;/p&gt;
&lt;p&gt;大部分人不会回忆起的是，就在二十年前， 169 拨号后得到的网络是没有 Internet 权限的 —— 只有拨更贵的 163 才有因特网权限。这种类似于常识的知识，居然现在我都没想起来。&lt;/p&gt;
&lt;p&gt;前几年，有人说 Gmail 不可能封掉， Google 不可能封掉。这几年纷纷都实现了 —— 因为有几年的缓冲时间，似乎也没有什么抱怨。&lt;/p&gt;
&lt;p&gt;光明网对中国并不是一个陌生的事物，将来也不会是一个陌生的事物 —— 想象一个只有微信、微博的网络 —— 似乎也没有什么不妥？某一天，光明网建成的时候，也只不过会造成一小撮人 —— 不，敌人 —— 的抱怨而已。大部分人，看着微博微信，用着百度地图，也挺开心的。&lt;/p&gt;
</content><category term="misc"/></entry><entry><title>冤枉钱</title><link href="https://tifan.net/blog/2017/07/22/yuan-wang-qian/" rel="alternate"/><published>2017-07-22T19:11:00-07:00</published><updated>2017-07-22T19:11:00-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2017-07-22:/blog/2017/07/22/yuan-wang-qian/</id><summary type="html">&lt;p class="first last"&gt;总结一下在这部电脑上花的冤枉钱&lt;/p&gt;
</summary><content type="html">&lt;p&gt;这几天我的 Mac Mini 频繁死机，并出现文件系统污染问题。最近一两年，机器的 load average 也常年保持在 2 以上。考虑到这台机器连续工作了四年时间，中间几乎没有过停机，应该说还是表现可以的。但是，仔细想想，虽然是买了苹果最廉价的电脑，但用起来一点也不便宜，或者说花了冤枉钱。&lt;/p&gt;
&lt;p&gt;这部电脑购买价格是 563 USD + 53.49 USD tax, 总计 616.49. 为其购买了 2 条 8GB DDR3 内存， 139.99 USD (当年 Amazon 还不收税), 购买了第二块硬盘的支架 34.99 USD. 一块 256GB SSD 227.55。计算下来，主机花了 1019.02 元。&lt;/p&gt;
&lt;p&gt;显示器，两部 HP 显示器，大约花了 700 元。&lt;/p&gt;
&lt;p&gt;存储空间不足，升级两块 512GB SSD，应该价格不菲。由于端口不足，购买了一些端口扩展器。因为没有摄像头以及麦克风，购买了一部罗技摄像头，也约 150 元。键盘鼠标乃外设，这些先不算。&lt;/p&gt;
&lt;p&gt;零零散散的加起来，这一部电脑用了四年，至少花了两千余元 （实际我怀疑花了至少 2500） 的费用以及若干小时的精力才使其可完成日日常任务，且扩展性并不佳 —— 16GiB 内存插满， CPU 也是相对较差的 i5 (因为购买的是乞丐版)。&lt;/p&gt;
&lt;p&gt;总的加起来，购买的穷人的解决方案，但是最后花的钱，与一部高配置的电脑相当；用起来的体验，依然是一部低配置的 Mac Mini. 于此同时，两千余元可以买到一部顶配的 iMac，内存至少可以多插 2 条到 32 GB，一部额外的 24 寸显示器也不过 200 元，但若是这样购买，大概到今天我也不会抱怨机器慢且不稳定。可以说，这两千多块是给我一直以来的 『购买廉价电脑自己升级会比高配的便宜』 的策略一个最佳教训。&lt;/p&gt;
</content><category term="misc"/><category term="life"/></entry><entry><title>用 Mikrotik Simple Queue 救 bufferbloat</title><link href="https://tifan.net/blog/2017/03/03/bufferbloat-mikrotik-simple-queue/" rel="alternate"/><published>2017-03-03T01:38:00-08:00</published><updated>2017-03-03T01:38:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2017-03-03:/blog/2017/03/03/bufferbloat-mikrotik-simple-queue/</id><summary type="html">&lt;p class="first last"&gt;运营商的用户永远没人权。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Bufferbloat 问题依然是一个比较严重的影响网络性能的问题。在 QoS 是个事情之前，如果你和我一样是 Time Warner Cable 的受害者 （或者比我更差，是 Comcast 的受害者，或是几乎任何 Cable network 的受害者），你很可能被 bufferbloat 所困扰。&lt;/p&gt;
&lt;p&gt;Bufferbloat 的表现形式很简单 —— 带宽饱和时，行为不是丢包以整流，而是观察到了更高的延迟。这的本意是好的 —— 基于共享的基础设施，网络设备希望尽可能的减少丢包以避免被 slow start 的性能损失所困扰。但是，更常见的事情其实是带宽用尽。这种情况下，网络设备的 buffer 除了可以增高延迟，并没有什么好处。&lt;/p&gt;
&lt;p&gt;为了避免网络设备的 buffer 被填满，可以通过本地流量整形的方式进行。用 Mikrotik 时，最简单的方式是使用 simple queue。命令如下：&lt;/p&gt;
&lt;pre class="code bash literal-block"&gt;
&lt;span class="o"&gt;[&lt;/span&gt;admin&amp;#64;smo.tifan.net&lt;span class="o"&gt;]&lt;/span&gt; &amp;gt; queue &lt;span class="nb"&gt;export&lt;/span&gt;
/queue simple
add max-limit&lt;span class="o"&gt;=&lt;/span&gt;225M/0 &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;twc-queue &lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ether5
&lt;/pre&gt;
&lt;p&gt;(注：我的网络计划标称 200Mbps/20Mbps，实际大约 235Mbps/24Mbps)&lt;/p&gt;
&lt;p&gt;疗效：在网络空闲时，CMTS 延迟大约 8.6ms。在使用前，流量饱和时从本地到 CMTS 的延迟可达 600ms (可能有 128M 的缓存！)。在使用后，流量饱和时从本地到 CMTS 的延迟稳定在 50ms 左右，完全符合预期。当然，我也有单线程大约 10Mbps 左右的性能损失。不过在极端情况下，可以正常使用网络是更重要的。&lt;/p&gt;
</content><category term="misc"/><category term="networking"/></entry><entry><title>知乎的手机短消息登录是个错误的决定</title><link href="https://tifan.net/blog/2016/11/21/zhihu-sms-login/" rel="alternate"/><published>2016-11-21T17:48:20-07:00</published><updated>2016-11-21T17:48:20-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2016-11-21:/blog/2016/11/21/zhihu-sms-login/</id><summary type="html">&lt;p class="first last"&gt;知乎是最新的一个不懂安全的中国公司的例子。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;这是 2016 年的年末。尽管国家标准技术协会已经在 &lt;a class="reference external" href="https://link.zhihu.com/?target=https%3A//pages.nist.gov/800-63-3/sp800-63b.html"&gt;Publication draft 800-63B&lt;/a&gt; 中将基于 POTS/SMS 的认证方式认为是不安全的，依然有人认为使用手机号作为登录验证的方式是个很好的决策。&lt;/p&gt;
&lt;p&gt;不，手机号登录既不能提供额外的安全性——相反，他极大的 compromised 由强密码带来的额外安全度，而且手机号登录又不能作为一次性密码 (OTP) 使用。&lt;/p&gt;
&lt;div class="section" id="ss7-attack"&gt;
&lt;h2&gt;SS7 Attack&lt;/h2&gt;
&lt;p&gt;SS7 (七号信令) 是美国国内运营商传输控制平面 (control plane) 的协议。在网络内，非语音 (data plane) 的控制讯号使用 SS7 进行传输。运营商的网络系统并非完美的 —— 恰恰相反， &lt;a class="reference external" href="https://link.zhihu.com/?target=http%3A//www.cbsnews.com/news/60-minutes-hacking-your-phone/"&gt;此处就有演示使用 SS7 进行攻击的案例&lt;/a&gt; 。&lt;/p&gt;
&lt;p&gt;这带来的威胁包括运营商网络外未授权访问并注入 SS7 控制信号带来的风险 —— 此类风险将允许入侵者重定向 data plane 之讯息至被入侵者控制的目标机器上，并读取其中所负载得之信息 —— 以及运营商中的内部攻击 (insider attack) 带来的风险 —— 此类风险为内部人员进行未授权访问带来的 —— 以及使用 CALEA (又称之为 lawful intercept 合法监听) 的有关机构 (例如 FBI) 进行侦听带来的风险，甚至包括 NSA/GCHQ 等机构进行 wiretap / snoop 时的风险。&lt;/p&gt;
&lt;p&gt;作为概括， SS7 攻击会带来由于中间链路不受控而导致的信息泄露风险，从而增加了攻击面。此类攻击仅需获知直知乎用户的手机号码。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;端点安全&lt;/h2&gt;
&lt;p&gt;简单的说，手机并非一个可以保证安全的端点 (endpoint)。尽管端点安全对于知乎并无法控制，但以提供手机短消息登录的可能性，知乎将端点被 compromise 而带来的风险扩大了。&lt;/p&gt;
&lt;p&gt;在端点已被 compromise 的前提下，targeted threat 的威胁程度是不同的。&lt;/p&gt;
&lt;p&gt;在仅提供密码认证的情况下，端点被 compromise 时，若设备上并无登录凭据／衍生的认证 ticket (例如 cookies) 时，攻击者只能通过其他办法获取账户的控制权 —— 例如使用 Email reset 密码，若设备上已有 Email 相关的凭据或访问权。&lt;/p&gt;
&lt;p&gt;在仅提供密码认证以及 OTP 校验的情况下 （在这个特定的环境中，知乎不提供 OTP 的能力），攻击者在无法获取 OTP 的情况下，若设备上并无登录凭据／衍生的认证 ticket (例如 cookies) 时，攻击者甚至无法通过 Email reset password 的方式获取账户的访问权。&lt;/p&gt;
&lt;p&gt;但是，在提供 SMS 登录的能力时，攻击者可简单的通过发送短消息的方式，在终端中截获登录凭据，并得到账户的控制权。在中国的特定国情下，此类威胁实际上是持久性的攻击 —— 例如中国厂商 Adups 在为客户制作的固件中植入了可读取短消息等敏感信息的后门。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;端到端加密&lt;/h2&gt;
&lt;p&gt;短消息作为明文传输的协议，其本身不提供端到端 (end to end) 的加密能力。作为敏感信息，从知乎的服务器发出凭据后，到到达用户端点时，有诸多的可被窃听点。除了上述的 SS7 attack 外，若知乎到短消息服务提供商的链路、短消息服务商到运营商的链路并未进行加密且对目标进行校验，则可以发生的是进行 wiretap 或进行 MITM attack.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;评论&lt;/h2&gt;
&lt;p&gt;验证用户的身份永远是困难的事情。但知乎是一个带有 PII/SPII （个人标示信息／敏感个人标示信息） 信息的信息系统。作为此类系统，其登录认证系统进行主动降低安全性的行为，是令人难以理解的。&lt;/p&gt;
&lt;p&gt;这可能意味着知乎的产品设计中，并未进行相应的安全复查 （security review） 便推入了生产环境。这也意味着，有很大的可能知乎团队中并没有真正的理解安全的人员。有理由推断，知乎也没有实现一些基本的安全保护措施，例如 encryption at rest, 数据存取控制及审计等。&lt;/p&gt;
&lt;p&gt;作为一家中国的新型创业公司，知乎本应选择与无视安全的中国互联网公司不同，在产品设计以及实现中，将安全作为检查单中的第一条。但事实上，知乎的做法与其他的中国公司并不不同 —— 其对安全的态度为漠不关心。&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"/><category term="security"/></entry><entry><title>人类的输入输出</title><link href="https://tifan.net/blog/2016/07/10/ren-lei-de-shu-ru-shu-chu/" rel="alternate"/><published>2016-07-10T00:37:00-08:00</published><updated>2016-07-10T00:37:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2016-07-10:/blog/2016/07/10/ren-lei-de-shu-ru-shu-chu/</id><summary type="html">&lt;p&gt;说起来，随着计算机技术的飞速发展， IO 的瓶颈，最终还是在人本身。&lt;/p&gt;
&lt;p&gt;人类的主要输入方式为视觉和听觉。视觉输入抽象数据 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;说起来，随着计算机技术的飞速发展， IO 的瓶颈，最终还是在人本身。&lt;/p&gt;
&lt;p&gt;人类的主要输入方式为视觉和听觉。视觉输入抽象数据的方法为阅读文字，并进行处理。听觉输入抽象数据的方法为听其他人（或计算机）讲话，并进行处理。辅助的，输入的元信息包括且不限于色彩、字体、排版方式、讲话语调语气。辅助输入还包括图像信息以及音乐信息。某些输入中，图像以及音乐是一个整体。&lt;/p&gt;
&lt;p&gt;同样的，人类的主要抽象输出方式包括视觉的输出以及听觉的输出。视觉的输出，主要为文字性的输出以及图像式的输出。听觉的输出，主要为语音性质的输出以及音乐性质的输出。&lt;/p&gt;
&lt;p&gt;在这些输入输出方法中，最有效的输入方式为抽象的文本性输入以及抽象的文本性输出。绘画以及音乐，虽然信息熵更高，但输入过程的细节丢失也更多，且此类输入会受到个人的理解严重影响。作为稳定的输入输出手段，文字性的似乎为最合适的。&lt;/p&gt;
&lt;p&gt;通常，中文发音的速率大约为 180 wpm，约合 3 个汉字每秒。英文大约为 160 wpm。传递的信息量，大约是一致的。此时，折合的字节数，大约为中文 72bps，英文 100bps。英文看似信息量更高，但其实折合起来，并没有这么高。考虑信息熵后，英文的输出速度甚至可能更低 —— 有效的单字信息量，英文比中文的低一些。写字的速度，不论语言，均低于讲话的速度。&lt;/p&gt;
&lt;p&gt;根据调查，阅读速度对于各种语言都在为 180 wpm 左右。也就是说，阅读的输入速率与发音的输出速率大致一致，都在百字节/秒以内。&lt;/p&gt;
&lt;p&gt;因此，对于原生的语音和文字来说，人类的处理速率是几乎一致的。当然，由于人进行 context switching 的成本很高，因此进行输入的 multiplexing 是不经济的。&lt;/p&gt;
&lt;p&gt;进入信息时代，阅读的输入并没有产生本质的变化。键盘的输出速率毫无疑问低于语音输出速率。触摸选择的速率，更是低于，或在最好情况下与手写持平。这也导致了一个本质的问题 —— 人机界面的 I/O 速率在最好情况下依然低于人类习惯的 I/O 速率。&lt;/p&gt;
&lt;p&gt;理想中，最高效率的 HCI 方法即为语音 I/O，辅助文本 I/O。然而，语音文本 I/O 的缺陷也是显著的。我相信大家都对车载 GPS 的语音输入深恶痛绝 —— 下面是个绝佳的例子&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You: Send me to the gas station along my way to downtown LA before I hit the freeway&lt;/li&gt;
&lt;li&gt;GPS: Sorry, I can't understand that. Say a command, or help.&lt;/li&gt;
&lt;li&gt;You: Navigate to nearest gas station&lt;/li&gt;
&lt;li&gt;GPS: I've found 16 gas stations near you. Which one do you want?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;looking at GPS screen. rear ended on another car&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此类语音控制的 epic fail 随处可见。 Siri 听起来不错，甚至会给你讲段子，但是对于这种人类理解起来非常简单的语音，计算机目前却无计可施。人类对上面的回答会很不一样：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你：我要去洛杉矶市区，在我上高速之前指给我最近的加油站&lt;/li&gt;
&lt;li&gt;你女朋友：我看看地图。你继续开&lt;/li&gt;
&lt;li&gt;（过了半分钟）&lt;/li&gt;
&lt;li&gt;你女朋友：上高速之前右拐一下有一家加油站&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;人类的思考过程，在很大程度上依然是未知的。但是，人类目前了解到的是，经过某种过程后，人类通过神经冲动的方式，或是控制声带，或是控制手，使用目标器官进行控制。因此，似乎最好、最快的方法，便是使人类可以用原生的神经冲动进行控制。&lt;/p&gt;
&lt;p&gt;对于直接用神经冲动的交互，从几十年前到现在一直都是在不断继续的，从脑袋上贴电极到脑袋里插电线，类似的尝试一直在不断的继续。比较有侵入性的，是让盲人复明的摄像头以及电子耳蜗 —— 这些装置的侵入性主要因为对进行信息的输入的需求上。无侵入性的解决方案主要是脑袋上贴电极的方法，探测人脑的活动而进行反应。&lt;/p&gt;
&lt;p&gt;对于大部分人来说，提高计算机和人类的交互效率，并不需要真的进行双向的加速 —— 从计算机到人的下行带宽已经足够，需要解决的问题只是更快的人到计算机的上行带宽。目前，已经有一些头盔式脑机输入装置走上了商业销售的道路。虽然他们仍然需要进行训练方可使用，而且输入的速率也非常低，精度也有问题，但是在未来，这类技术在解决了精度和速率问题后，很有可能变成下一代的主要输入方式之一。也许，现在被大家调侃的脑上插管用于通讯、嘴里插管用于喝 Soylent 的情景，在 20 年后已经变成常态。&lt;/p&gt;</content><category term="misc"/><category term="human io bci"/></entry><entry><title>法拍屋拍卖（失败）杂记</title><link href="https://tifan.net/blog/2016/05/01/court-auction-house/" rel="alternate"/><published>2016-05-01T17:00:00-07:00</published><updated>2016-05-01T17:00:00-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2016-05-01:/blog/2016/05/01/court-auction-house/</id><summary type="html">&lt;p class="first last"&gt;随便写一点拍法拍屋的经历&lt;/p&gt;
</summary><content type="html">&lt;p&gt;经纪人最近开始给我推荐做法拍屋了。这次是拍卖一个 mid city (zip code 90019) 的房产。 MLS 上看，这房子和鬼屋相差不多，实地考察，也和鬼屋相差不多。听说，这屋子是一个 old lady 死后留下的，需要执行遗嘱，因此让政府拍卖掉。负责拍卖的机构是 The Public Administrator for the County of Los Angeles，专管死人财产的，属于 Treasurer and Tax Collector 管理。&lt;/p&gt;
&lt;p&gt;拍卖的这个财产是个 6250 尺地皮、 1242 尺居住面积的 house。从外面看，杂草丛生、车库里有一辆大约 1960 年代的老爷车，车库已经整体损坏。进入查看，扑面而来的是一股强烈的刺激性气味。屋顶有部分损坏，内部所有内饰均有严重的霉变以及损坏，需要全部移除并更换。内部只有一个洗手间。看到这种房子时，我总不能让我自己不去思考房子的主人到底有了什么样的经历，是什么让这个屋主在晚年竟无力体面的生活（从屋子的状况可以轻易的看出这一点）。但是，资本主义毕竟是资本主义，我也非圣人，只是另一个投机倒把的投机分子而已。&lt;/p&gt;
&lt;p&gt;查看房子时，带了合同工。合同工讲，大约需要 6 万美元修复房子主体，另需要大约 1 万美元修复车库。若扩建搭建另一个卫生间，也许需要额外的近 1 万美元。当然，也可以选择多出一些钱，扩建一个卧室，使其更吸引人——毕竟是木质结构的房子，扩建是较容易的。&lt;/p&gt;
&lt;p&gt;房屋所在的学区并不理想，大约都是 3 分的学校。但是，此房所处在的城市规划允许其转换为一套 2 户的 townhouse，每一户大约在 1400 尺。通过附近的房价比较，我对其修复扩建后的潜在价值估计在了 65 到 70 万美元的级别。因此，考虑我的成本、税务以及贷款成本（私人贷款，利率较高），大约我愿意出的价格在 45 万美元的级别。&lt;/p&gt;
&lt;div class="figure align-center"&gt;
&lt;img alt="Court ordered onsite auction" src="https://tifan.net/images/20160501_auction.jpg" /&gt;
&lt;p class="caption"&gt;法拍屋拍卖当天悬挂的牌子。&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;拍卖当日，房子首先开放内部查看。主人大概已经去世了一阵子，而且内部看起来在主人去世前已经需要不少需维护的地方。有意思的是，洗手间内有一个假肢，由于窗户全部用木板封闭（玻璃早已全部破损），又无电灯，仅可以靠手机闪光灯的微弱亮光，因此有一女买家进去后，看到了这个假肢，立即发出了尖叫。发出尖叫也不是完全没有道理的——卫生间内也确实布有血迹，包括入门的台阶之上。&lt;/p&gt;
&lt;p&gt;经纪人的助手告诉我，这里有不少买家他都看起来很眼熟 (looks familiar)，但是不需要担心他们，他们是来捡漏的，肯定他们捡不到。另几个人是私人投资者，是需要警惕的，但是他们的底线也比较保守。不过，这次我的分析也是比较保守的，因此碰到了激进投资者，我肯定不会出高价的。&lt;/p&gt;
&lt;p&gt;让我的经纪人代我提交表格后，领到了一张黄牌子。拍卖人会喊价，如果你接受则举着牌子，否则放下牌子。价格从 29 万美元开始， delta 为 1 万美元。几秒钟后，价格便涨到了 51 万美元，接下来，两个亚洲人（一个是韩国人，因为他戴了一顶帽子；另一个目测是东南亚人）开始了叫板。又过了几秒钟，喊到了 58 万，韩国人不出了。整个拍卖过程大约 10 秒钟的样子。&lt;/p&gt;
&lt;p&gt;回去的路上，和经纪人讨论分析。最后的结论是，要不然把这套房子包装成 high end lux house，要不然拆掉做成 upscale townhouse，并且中标者用了自己的钱或者比我的成本地很多的融资手段，否则他也没得赚。如果这样做，大概卖出价格 house 要 90 ~ 95 万， townhouse 价格要 70~80 万，才是个合适的投资。&lt;/p&gt;
&lt;p&gt;不管怎么样，一个区域不怎么样的地区，居然可以卖出这种价格，看来洛杉矶的房市又要看涨咯。再者说来，也算是丰富了人生经历吧。&lt;/p&gt;
</content><category term="misc"/><category term="life investment house"/></entry><entry><title>三线城市和电子商务</title><link href="https://tifan.net/blog/2016/03/06/varnishing-retailers-in-smaller-cities-of-china/" rel="alternate"/><published>2016-03-06T02:57:20-08:00</published><updated>2016-03-06T02:57:20-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2016-03-06:/blog/2016/03/06/varnishing-retailers-in-smaller-cities-of-china/</id><summary type="html">&lt;p class="first last"&gt;电商一定会摧毁中国三线以及更小城市的零售业。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;电商一定会摧毁中国三线以及更小城市的零售业。&lt;/p&gt;
&lt;p&gt;回国探亲和回国商务旅行总是不一样的——只有在回国探亲的时候，我才更有机会了解到中国四线城市的实际情况。与期望中一样，或者更应该说和期望中不一样，四线城市的零售业与十年前我还在那里生活时一样，零售业仍有选择少、质次价高的情况。&lt;/p&gt;
&lt;p&gt;由于中国实际的发展不均衡、特别是教育资源的不均衡导致的平均文化程度低以及其引发的对品质的更低需求，又附加已知的受教育程度与收入水平之正相关特性 (&lt;a class="reference external" href="http://www.bls.gov/emp/ep_chart_001.htm"&gt;http://www.bls.gov/emp/ep_chart_001.htm&lt;/a&gt;)，导致的结果是相对更发达的城市有受教育程度更高的人群，以及更多的高薪工作机会留在了发达城市。实际情况符合这一推论之结果——聊城市并无可提供高薪就业机会的主要公司，更多的就业机会提供者是公共部门以及制造业。&lt;/p&gt;
&lt;p&gt;传统的，中国的小城市被认为有生活成本低的特性。然而，事实上这 stereotype 早已被打破。由于小城市的收入水平更低，零售业中，高品质商品之额外成本实际更高——这是由于更高的价格带来的更低的销量造成的平均附加成本，包括运输成本、平摊之店面成本、行政成本以及利润更高。这实际上并不代表此商品对商家的利润更高——相反的，由于其高的价格促使的更高的附加价格在一个小的市场上共同影响下，此商品的销售可能性更低，最后导致的便是或是此成本被最终的购买者承担，或是零售店在计算后认为销售此类产品并不 feasible 而放弃其销售。因此，与大城市相比，除了房价的绝对值更低之外，从工作机会到个人发展机会上并无优势。&lt;/p&gt;
&lt;p&gt;在电商普及之前，对于不发达地区的居民来说，唯一的购买高质量商品或最新商品的方法便是旅行去附近的主要城市并进行购买。实际上，这也是不发达地区居民以前不得不去做的——市政府所在市买不到则去省会，省会买不到则去附近的更大型城市，甚至是一线超级城市。&lt;/p&gt;
&lt;p&gt;然而，随着支付手段以及物流的不断发展，对于有更高品质的商品有需求的消费者，已经有了更加便捷的渠道完成购买。物流的价格很低，速度又相对来说较快，且利用 B2C 平台购买大件商品无需担心传统上的零售业骗局（例如 2000 年代中关村流行的转型等宰人技巧），商品价格透明且容易对比，这一切已经挤压了零售品商家的生存空间。除非低价的快销品、新鲜食品之外，其他的商品在电商完成规模经营后，都会比实体零售店有更低的附加成本。从实际情况来看，二三线以及更小的城市在电商平台的消费能力更强也印证了这一猜测——更小的城市的客户满足其高品质商品的主要渠道已是电商。&lt;/p&gt;
&lt;p&gt;另一个例子，亦可以证明这一观点。 MUJI 最近在中国重庆以及成都新开了几家零售店。这一日本品牌在中国的西南开店的唯一目的其实并不是仅服务这几家店——很容易看出， MUJI 希望以这几家店为核心以及主要仓库，覆盖中国的西南省份。&lt;/p&gt;
&lt;p&gt;未来，在中国可能会看到的情况是小型城市除了生活必需品以及食品 (换句话说， grocery store 里销售的那些东西)，其余商品基本只能使用电商进行购买。各大交通核心节点附近会有大量仓库为大型 B2C 电商服务。即使在更大的城市，实体店也会不可避免的变成以展示、试用为主，下单以及结算则会大部分以互联网方式进行以减轻城市内的仓储压力。&lt;/p&gt;
</content><category term="misc"/><category term="thoughts"/></entry><entry><title>买房的一点心得</title><link href="https://tifan.net/blog/2016/01/10/advice-on-home-purchase/" rel="alternate"/><published>2016-01-10T02:58:00-08:00</published><updated>2016-01-10T02:58:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2016-01-10:/blog/2016/01/10/advice-on-home-purchase/</id><summary type="html">&lt;p class="first last"&gt;买了在美国的第一套自住房一年多以后悟出的一些道理，以警示后人。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;目前这套房子已经买了一年多了。随着美国房价的进一步增长，似乎回报也算不错，但是作为目前的 primary residence，其实住起来还是有很多不爽的地方的。这里的很多“不爽”，只有在买过自己的房子才能知道。既然是经验性的内容，贴出来让后人以为鉴。&lt;/p&gt;
&lt;p&gt;能住新房，自然是皆大欢喜，问题可以慢慢发现，毕竟还有建筑商的保修。但是，对于大部分人来说，由于 zoning 的限制以及冗长的 permitting 流程导致的开发商无动机在大城市^H^H^H三番和洛杉矶盖房的现状，二手房大概是唯一的选择。二手房交付时为 as-is （“依样”），因此不少问题，购房时无法发现，那么就只好自认倒霉了。&lt;/p&gt;
&lt;p&gt;也许你会说，我有房屋保险。恭喜你，房屋保险是一个美丽的谎言。它与健保一样，有高的抵扣额，提出赔偿请求后下年保费会升高，而且保险公司可能在你做出多次赔偿请求后拒绝续约。拒绝续约的后果是严重的，商业银行的贷款常常要求屋主必须拥有房屋保险。被拒绝续约后，可能唯一选择便是使用昂贵的 FAIR (Fair Access to Insurance Requirements) 计划。相信我，你 &lt;strong&gt;不想&lt;/strong&gt; 这样做。&lt;/p&gt;
&lt;div class="section" id="homeowner-s-association-hoa"&gt;
&lt;h2&gt;屋主协会 (Homeowner's Association, HOA)&lt;/h2&gt;
&lt;p&gt;在中国，他的类似物是业主委员会。屋主协会是购买 condo/townhome 的屋主最经常抱怨的东西。一定要注意，在比较大的社区里，选举新的屋主协会代表甚至是修改屋主协会的制度都是非常难的事情——你几乎不可能召集到一半以上的住户前来投票。因此，详细阅读 HOA 的规定，你也许会发现其中的某些规定完全违反常理，例如屋主委员会禁止你在家中安装洗衣机，或者禁止将房屋出租等。&lt;/p&gt;
&lt;p&gt;另外，也需要注意屋主委员会的费用是否过高。降低费用是有理论操作性的，但实际上的操作可能性见上文。&lt;/p&gt;
&lt;p&gt;最后，不管怎么样，你都有权利查阅屋主协会的帐目以及账户余款，要求他们使用账户余款支付不合理费用。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;房屋的结构&lt;/h2&gt;
&lt;p&gt;这里的结构，当然不是指的风格上的结构或者户型上的结构——此类选择自然各人均有偏好——我比较喜欢的是当代建筑。此处的结构，指的是建筑物本身的结构稳定性和损耗程度。&lt;/p&gt;
&lt;p&gt;你可能希望询问房屋的屋顶上次翻修时间。不同于中国的建筑，拜谢于不负责任的工人以及非常不科学的建筑设计，美国的建筑水平、质量、寿命都是极差的，房顶的平均使用年限大概在 30 年左右。需要换屋顶在中国几乎是闻所未闻的事情，但在美国这却是稀松平常的事情。如果美国的房子都像中国的房子那样基本没有维护，二十年都不用，就会全都塌掉。&lt;/p&gt;
&lt;p&gt;换屋顶的价格不菲，如果房子本身历史已经较久（例如湾区常见的 1950 年代的房子），可能已经在需要换屋顶的边缘。&lt;/p&gt;
&lt;p&gt;在加利福尼亚州，八十年代之前的房子可能使用的是爆米花屋顶。鉴别的方法很简单，看屋顶是不是凹凸不平的、像爆米花的即可，你也可以使用搜索引擎检索 &lt;em&gt;popcorn ceiling&lt;/em&gt; 查看它到底是什么样子的。&lt;/p&gt;
&lt;p&gt;爆米花屋顶是石棉 (abestos) 做的。石棉有致癌性——石棉纤维被吸入肺中后，可能会引发肺癌。铲除石棉屋顶也会花费不少时间，而且也会花不少钱。鉴于在美国多年来见到的比中国工人更不负责任的美国工人的干活方式，我谨慎的认为除石棉公司其实搞不定这件事情。因此，为了避免枉死，还是应远离此类房子。&lt;/p&gt;
&lt;p&gt;白蚁以及霉变也是很糟糕的事情。他们都不容易根治，而且对房屋的结构会有很大的影响。不要忘了，本质上，美国的房子只是木头垒起来外面贴上纸做的墙皮而已，不要对他们的结构强度太放心。况且，对于加利福尼亚州的居民，这些房子已经经过了多年地震的洗礼，房屋本身的结构真的要打一个问好。当然，搬离加州其实也是个不错的选择，这个话题暂且不表，以后有机会再谈。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;保险以及其他&lt;/h2&gt;
&lt;p&gt;加州是个地震的多发区。尽管房屋地震保险是有争议的 （地震发生的时间未知但保费却非常昂贵，而且地震保险的实际覆盖非常差），但地震保险以及房屋保险是你应该认真考虑的问题。可以阅读 &lt;a class="reference external" href="http://www.latimes.com/la-homeauto-story1-story.html"&gt;LATimes 上的这篇文章&lt;/a&gt; 了解更多。&lt;/p&gt;
&lt;p&gt;房屋本身的保险，则可以提前和保险经纪进行联系。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h2&gt;检修房屋&lt;/h2&gt;
&lt;p&gt;如果你在追踪我的 Twitter，可能你已经发现了我的房子出了不少问题——从漏水到空调罢工再到冰箱损坏。这一切对于老房子来说，都是非常正常且一定会发生的。&lt;/p&gt;
&lt;p&gt;某些小问题，很容易请 inspector 来进行检查，例如我买房子的时候就已经修了一些问题了。但是，谁都不能保证接下来会发生什么，例如漏水就是个不容易发现的问题。&lt;/p&gt;
&lt;p&gt;对于这些，可以选择购买额外的保险，当然，这保险也不一定要买——毕竟电器之类的，不要指望它可以工作太久。至于大件 appliance，保险自然是有办法让你很不爽的不想用或者是有很差的 coverage，个人意见是没有也罢；坏了就权当自己掏钱升级罢了。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;网络&lt;/h2&gt;
&lt;p&gt;网络是很重要的。很可能，你只有一个 ISP 的选择 —— 对于我来说，这是 Time Warner Cable。对于你，可能是 Comcast，可能是 AT&amp;amp;T，可能是 Cox ... 但是，不管怎么样，应该尽可能选择有 FiOS 或者是其他 FTTH 接入的社区，若没有，也应确保自己搬入后有高速网络——很遗憾， AT&amp;amp;T 以及 Verizon 仍然在宣传 DSL 为高速网络，甚至有时候 Verizon 会建议你使用 &amp;quot;high speed dial-up&amp;quot;. 没错儿，就是拨号上网。&lt;/p&gt;
&lt;p&gt;不管如何，应确保你有 FTTH 或 Cable —— 这可以通过去 ISP 网站输入地址查询后得知。&lt;/p&gt;
&lt;p&gt;如果发现你的房子有 Google Fiber 提供或者 （北加） 2Gbps Comcast —— 不要犹豫，就选他吧。顺畅的网络才能带来舒适的生活。&lt;/p&gt;
&lt;div class="section" id="id6"&gt;
&lt;h3&gt;网络布线&lt;/h3&gt;
&lt;p&gt;这是一个不得不说的问题。如果你希望让家中的网络更稳定，请确认新房内每个房间都有以太网接口，以及墙内使用了超五类或者更好的线材，并有位置放置你的交换机以及无线接入点。&lt;/p&gt;
&lt;p&gt;尽管不是每个人都会选择在家中设置多个无线接入点，但是你可能会在以后发现家中某些房间覆盖不好，这时，你至少有机会安装一个新的接入点，或者使用以太网办公。考虑到百兆以上的速度在某些地方已经普及，而且很多人都有家中存储服务器的需求，因此千兆已经是最低要求。&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
</content><category term="misc"/><category term="life"/></entry><entry><title>CLLI Code - 美国电信运营商的地址编码</title><link href="https://tifan.net/blog/2015/12/21/clli-code/" rel="alternate"/><published>2015-12-21T02:20:00-08:00</published><updated>2015-12-21T02:20:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2015-12-21:/blog/2015/12/21/clli-code/</id><summary type="html">&lt;p class="first last"&gt;美国运营商用于定位交换中心的编码方式。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;在北美， traceroute 里经常会看到一些看起来像地理位置的编码，比如说 &lt;tt class="docutils literal"&gt;agg28.tustcaft01r.socal.rr.com&lt;/tt&gt; 里面的 &lt;tt class="docutils literal"&gt;tustcaft01r&lt;/tt&gt; 。其实，这是由贝尔公司提出的地理位置以及交换中心的命名方式，叫做 CLLI code (读作 silly code)。&lt;/p&gt;
&lt;p&gt;CLLI 代码是电话时代的发明。不熟悉北美拨号计划的，可以自己补习一下 NANP 是如何形成的。总之，这是一种带有地理位置以及交换中心和设备名的系统命名法。&lt;/p&gt;
&lt;p&gt;上面的 &lt;tt class="docutils literal"&gt;tustcaft01r&lt;/tt&gt; 的解如下：&lt;/p&gt;
&lt;pre class="literal-block"&gt;
tust    Tustin
ca      California
ft      FT 为交换中心代码
01r     Router 01
&lt;/pre&gt;
&lt;p&gt;一部分 CLLI 代码可以在网上查到。交换中心是公开信息，如果这个交换中心恰好也是其他电话公司的交换中心，那么你甚至可以找到他的具体位置。&lt;/p&gt;
&lt;p&gt;下面留一些课后题，请大家自行寻找答案。&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;310-399 对应的 CLLI Code, rate center, 原始电话公司以及交换中心是什么？&lt;/li&gt;
&lt;li&gt;&lt;tt class="docutils literal"&gt;FRMTCAED&lt;/tt&gt; 在哪儿？&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;不得不说，美国的通信行业还是领先了中国不少。中国到现在为止都没有一套行业遵守的公开命名标准，更不用说没有人设置反向解析了。当然，这是有历史原因的。贝尔是当时的事实垄断公司，其他的电话公司自然会遵循贝尔的命名法则。不过，似乎中国电信到现在都没有做到全国公司统一呢。&lt;/p&gt;
</content><category term="misc"/><category term="network"/></entry><entry><title>手机</title><link href="https://tifan.net/blog/2015/03/22/cell-phone/" rel="alternate"/><published>2015-03-22T01:47:00-08:00</published><updated>2015-03-22T01:47:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2015-03-22:/blog/2015/03/22/cell-phone/</id><summary type="html">&lt;p class="first last"&gt;手机。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;做一个项目，需要把一个小的中文字体放到嵌入式设备里。一下子想到了上大学的时候用的 Motorola ROKR E6 手机。遂连接上 USB 线，用终端连接到手机上，最后用 zmodem 下载回来了手机里的字体。&lt;/p&gt;
&lt;p&gt;这是一部 Linux 手机，和我的 iPaq 一起让我打发掉了无数无聊^H^H应该听的课，比如大学物理或者高等数学，或者是马哲毛邓三之类。特别是 iPaq，在大部分人都不知道手机 GPRS 可以上网的时候让我能随时上网，顺便被围观。我还记得汶川地震的时候，我用 iPaq 加上 PCMCIA 卡后背配上一张 PCMCIA GSM Modem 卡上网，发现网上说地震了。&lt;/p&gt;
&lt;p&gt;以上是废话。上高中或者上大学的时候，感觉换个手机，或者买点什么电子设备还是很能让我兴奋的事情。现在，基本上想买什么也不用怕买不到，上网 order 一下过两天就收到了，那种兴奋的感觉再也没有了。&lt;/p&gt;
&lt;p&gt;曾经还用过手机上课的时候写程序，甚至交叉编译编译器以及一大票工具链到手机上就为了可以随时写程序或者玩儿点什么东西，或者是 ssh 到服务器上。现在？给我钱我都懒得折腾，车子 trunk 里总是有一两台笔记本，需要路上做点什么东西，随时可以找个地方停下来车 tether 网络用。&lt;/p&gt;
&lt;p&gt;后来，换了 HTC Dream，这手机有键盘——直到今天我还是想要个有键盘和轨迹球的手机——而且居然还这么容易编译二进制，这让我一下子变成了成天拿着手机敲命令干些无聊事情的呆子。&lt;/p&gt;
&lt;p&gt;科技发展，让我的这种“没事儿给自己找麻烦”的成果越来越没有实用价值了。或者，是因为越来越懒了？&lt;/p&gt;
&lt;p&gt;# 突然发现，全文都是废话。&lt;/p&gt;
</content><category term="misc"/><category term="life"/></entry><entry><title>服务器的系统命名法</title><link href="https://tifan.net/blog/2015/02/24/systematic-hostname/" rel="alternate"/><published>2015-02-24T23:03:00-08:00</published><updated>2015-02-24T23:03:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2015-02-24:/blog/2015/02/24/systematic-hostname/</id><summary type="html">&lt;p class="first last"&gt;计算机科学的两个难题是缓存的失效 (cache invalidation)、命名事物以及偏差一错误 (off-by-one error)。本文旨在为第二个（或者是第一个，取决于你如何数数）难题提供一个较为通用的解决方案。这篇文章提出了一种类比系统命名法的服务器命名法，使每台服务器都有一个确定的名字。这是服务器 fleet 管理系列文章中的一篇。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;计算机科学的两个难题是缓存的失效 (cache invalidation)、命名事物以及偏差一错误 (off-by-one error)。本文旨在为第二个（或者是第一个，取决于你如何数数）难题提供一个较为通用的解决方案。&lt;/p&gt;
&lt;p&gt;一般的，服务器命名有两种模式——或是直接对服务器取名，或是对服务器进行系统的编号。显然，前者在服务器数量较小时可以使用，但它并不能 scale。因此，必须有一种方式可以将服务器的命名可以 scale。&lt;/p&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;命名的要点&lt;/h2&gt;
&lt;p&gt;让我们思考服务器的命名有哪些要点。为了支持日渐流行的 IaaS 服务同时兼容传统的数据中心，同时可以快速解读出服务器主机名所包含的信息，命名中应该至少包含有以下信息:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;服务器的地理位置&lt;/li&gt;
&lt;li&gt;服务器的 colocation 提供商&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;这对于网络追踪时解读 traceroute 信息是格外有帮助的。&lt;/p&gt;
&lt;p&gt;Bare metal machine 除了以上的信息之外，还有下列信息是固定不变的:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;机柜的位置&lt;/li&gt;
&lt;li&gt;服务器在机柜中的位置&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;以上信息在需要 colocation 服务人员协助操作硬件时格外有用。&lt;/p&gt;
&lt;p&gt;除此之外，有一些常被认为是不变信息的信息其实可能是有所变化的:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;服务器的硬件配置&lt;/li&gt;
&lt;li&gt;服务器的角色&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;对于这些有可能会变化的信息，不应编码到主机名中。为了满足以上的需求，提出的主机命名法如下。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h2&gt;主机名的组件&lt;/h2&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Colocation 服务商，或 IaaS 服务提供商&lt;/li&gt;
&lt;li&gt;服务器的地理位置代码&lt;/li&gt;
&lt;li&gt;机柜编码&lt;/li&gt;
&lt;li&gt;服务器在机柜中的位置&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;假设在为 Contoso 公司（域名为 contoso.com）命名生产服务器，并决定使用 pro.contoso.com 域名。则 Contoso 在 Seattle 的所有服务器域名均为 &lt;tt class="docutils literal"&gt;sea.prod.contoso.com&lt;/tt&gt; ；其中 SEA 为 Seattle 机场之机场代码。&lt;/p&gt;
&lt;p&gt;假设 Contoso 在 Seattle 使用了两家 colocation 服务商， The Phone Company 以及 A Datum Corp。将这两家公司分别简化为 pc (as in Phone Company) 以及 dc (as in Datum Corp)。将 colocation 的机柜从 AA 开始序列编号 (aa, ab, ... , az, ba, ... ) 进行扩展。注意机柜编号时不使用数字。&lt;/p&gt;
&lt;p&gt;假设使用标准 42U 机柜，则服务器的命名将采用机器最顶处的机位进行编码。例如一台 4U 服务器占用了 20, 21, 22, 23 机位，此服务器的上沿处编码为 20, 则服务器编码即为 20。&lt;/p&gt;
&lt;p&gt;到此处，一台 20 号机位、机柜编码为 AB、数据中心为 PC、地理位置为 SEA 的服务器，主机名将是 &lt;tt class="docutils literal"&gt;abpc20.sea.pro.contoso.com&lt;/tt&gt; 。&lt;/p&gt;
&lt;pre class="literal-block"&gt;
dcname      = 2*LOALPHA
rackname    = 2*LOALPHA
rackpos     = 2*DIGIT
geoloc      = 3*LOALPHA

hostname = dcname rackname rackpos &amp;quot;.&amp;quot; geoloc &amp;quot;.&amp;quot; pro.contoso.com
&lt;/pre&gt;
&lt;p&gt;对于使用 IaaS 的公司，同一数据中心多可用区的部署可视为同地理位置不同 colocation 服务商的情况。 rackname 以及 rackpos 可以使用顺序编号。&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"/></entry><entry><title>一个中国国际网络的监视器</title><link href="https://tifan.net/blog/2015/01/10/nwmon-network-performance-monitor/" rel="alternate"/><published>2015-01-10T19:13:00-08:00</published><updated>2015-01-10T19:13:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2015-01-10:/blog/2015/01/10/nwmon-network-performance-monitor/</id><summary type="html">&lt;p class="first last"&gt;（又）一个中国国际网络的监视器&lt;/p&gt;
</summary><content type="html">&lt;p&gt;nwmon 是我最新的一个网络监视项目。使用 rrdtool 构建，这个监视器是轻量级的，可运行于几乎所有的平台上。&lt;/p&gt;
&lt;p&gt;这个项目的目的是通过全球各主要数据中心对中国各重点网络交换中心的监视获取在极度拥塞前提下的连接中国以外与中国的网络的最佳链路以及时间—性能曲线并根据其对在海外或中国的对其相反的方向的连接性有使用需求的人群对网络路径的选择进行有针对性的优化。&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://nwmon.tifan.net"&gt;http://nwmon.tifan.net&lt;/a&gt; 是目前的 nwmon 公共访问地址。&lt;/p&gt;
</content><category term="misc"/></entry><entry><title>Yet another read-modify-write race</title><link href="https://tifan.net/blog/2014/11/17/race-condition-of-wordpress/" rel="alternate"/><published>2014-11-17T02:03:00-08:00</published><updated>2014-11-17T02:03:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2014-11-17:/blog/2014/11/17/race-condition-of-wordpress/</id><summary type="html">&lt;p class="first last"&gt;Race condition is likely to happen, especially when programmers don't know what scale means.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Race condition is likely to happen, especially when programmers don't know what scale means.&lt;/p&gt;
&lt;p&gt;We're talking about a Wordpress website with a moderately high load. The
scheduled post functionality is not functioning properly. All scheduled posts
ends up in &lt;em&gt;missed schedule&lt;/em&gt; state.&lt;/p&gt;
&lt;p&gt;Initial investigation revealed &lt;tt class="docutils literal"&gt;cron&lt;/tt&gt; was not set in the database.
Typically, when a user schedules a post in Wordpress, the system will create a
new cron entry in the Wordpress self-managed cron facility. Later, Wordpress
will access &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wp-cron.php&lt;/span&gt;&lt;/tt&gt; and run the cron task. The &lt;tt class="docutils literal"&gt;cron&lt;/tt&gt; was serialized
first and stored in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;wp-options&lt;/span&gt;&lt;/tt&gt; as an option entry. However, in this setup,
the entry never shows up in MySQL console, even if I observed other typical
data you would expect in &lt;tt class="docutils literal"&gt;cron&lt;/tt&gt;, such as, update checks.&lt;/p&gt;
&lt;p&gt;Confusing as hell, I made multiple attempts in monitoring database transaction
and PHP trace. Surprisingly, database query log indicates Wordpress is updating
&lt;tt class="docutils literal"&gt;option_name=cron&lt;/tt&gt; in &lt;tt class="docutils literal"&gt;wp_posts&lt;/tt&gt; at 20 qps. Backtrace revealed
that Wordpress will &lt;strong&gt;schedule cron task on every request&lt;/strong&gt; &lt;a class="reference external" href="https://github.com/WordPress/WordPress/blob/4.0-branch/wp-includes/update.php#L674"&gt;ref&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Wordpress developers assumed most sites are not heavy loaded. Thus, &lt;a class="reference external" href="https://github.com/WordPress/WordPress/blob/4.0-branch/wp-includes/cron.php#L70"&gt;the design&lt;/a&gt; decision (if they have any design) is to store all cron task entries in one database row and use a &lt;a class="reference external" href="https://github.com/WordPress/WordPress/blob/4.0-branch/wp-includes/cron.php#L71"&gt;read&lt;/a&gt;-&lt;a class="reference external" href="https://github.com/WordPress/WordPress/blob/4.0-branch/wp-includes/cron.php#L416"&gt;unserialize&lt;/a&gt;-&lt;a class="reference external" href="https://github.com/WordPress/WordPress/blob/4.0-branch/wp-includes/cron.php#L456"&gt;append&lt;/a&gt;-&lt;a class="reference external" href="https://github.com/WordPress/WordPress/blob/4.0-branch/wp-includes/cron.php#L466"&gt;serialize&lt;/a&gt;-write workflow. Such workflow works perfectly fine when we're taking about small to medium scaled websites with no more than 10 page visits per minute. Well, probably a few more. However, it implied &lt;strong&gt;no data would be changed between read and write&lt;/strong&gt;. Unfortunately, when we're talking about heavy loaded site, you would expect tens or even hundreds of queries hitting your box, rendering the assumption unsafe. Yet another read-modify-write-race.&lt;/p&gt;
&lt;p&gt;The fix? Disable the damn update check! The caveat? You will be managing updates
manually. You &lt;a class="reference external" href="https://www.google.com/search?q=wordpress+backwards+compatibility"&gt;want&lt;/a&gt; to do
that anyway...&lt;/p&gt;
&lt;pre class="code php literal-block"&gt;
&lt;span class="x"&gt;remove_action( 'init', 'wp_schedule_update_checks' );
remove_action( 'load-plugins.php', 'wp_update_plugins' );
remove_action( 'load-themes.php', 'wp_update_themes' );
remove_action( 'load-update-core.php', 'wp_update_plugins' );
remove_action( 'load-update-core.php', 'wp_update_themes' );
remove_action( 'load-update.php', 'wp_update_plugins' );
remove_action( 'load-update.php', 'wp_update_themes' );
remove_action( 'upgrader_process_complete', 'wp_update_plugins' );
remove_action( 'upgrader_process_complete', 'wp_update_themes' );
remove_action( 'upgrader_process_complete', 'wp_version_check' );
remove_action( 'wp_maybe_auto_update', 'wp_maybe_auto_update' );
remove_action( 'wp_update_plugins', 'wp_update_plugins' );
remove_action( 'wp_update_themes', 'wp_update_themes' );
remove_action( 'wp_version_check', 'wp_version_check' );&lt;/span&gt;
&lt;/pre&gt;
</content><category term="misc"/></entry><entry><title>反社交网络</title><link href="https://tifan.net/blog/2014/11/01/goodbye-social-network/" rel="alternate"/><published>2014-11-01T02:09:00-07:00</published><updated>2014-11-01T02:09:00-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2014-11-01:/blog/2014/11/01/goodbye-social-network/</id><summary type="html">&lt;p class="first last"&gt;在重度使用社交网络若干年后的一些思考&lt;/p&gt;
</summary><content type="html">&lt;p&gt;从上大学起，我就开始使用了 Twitter。这篇文章算是对这段时间的一个交代，也是我自己对社交网路的一些思考。&lt;/p&gt;
&lt;p&gt;社交网络没有错。不能否认的是，社交网络对我的影响是巨大的，从这里认识了很多有趣的人，也收获了不少知识。在中国时，这是我了解更广阔的世界的途径，而移居美国之后，这是我了解中国正在发生什么的一个主要渠道。作为信息源，Twitter 的未经审核的自由发言特性以及墙所带来的天然门槛导致其其实是一个很好的信息窗口。社交网络的价值也体现在这里。&lt;/p&gt;
&lt;p&gt;然而，从另一方面来看，社交网络实际上是一个反生产力的工具。公认的，社交是个强需求；在分析社交网络的内容之后，却可以发现，社交网络中最多的内容便是碎片化的、低信息熵的内容。在资本市场看重用户数以及活跃度的背景下，各个社交服务的发展方向便是使用户发布信息更加实时；对于惯用社交网络的用户来说，这种由资本市场驱动的产品发展方向引导其在社交网站中有更多的 engagement；而这被认为是造成上述的后果的根源。从实际的数据中，也可以容易的看出上述趋势。&lt;/p&gt;
&lt;img alt="自 2008 年至 2014 年的 Twitter 活跃度。这张图片揭示了 2011 年中旬起使用度有明显的提高。" src="http://tifan.la/QHWV/5LnAnNHC.jpg" /&gt;
&lt;p&gt;这是我从 2008 年到 2014 年的 Twitter 活跃程度直方图。明显的可以看出，随着移动互联网在中国的爆发以及我的移居美国，社交网络活跃度有明显的上升，而有效信息产出的速率并无较大的变化。&lt;/p&gt;
&lt;p&gt;10 月份，我进行了一段时间的停止使用 Twitter 以及微信朋友圈的实验。实验结果表明，在短期来看，有效信息的获取并未有明显的减少，最有价值的信息来源依然是邮件列表以及报刊，一个已经运行了很长时间的稳定系统。反而，我有了更多的时间 pursude 以前杯认为是无法完成的事情。例如，最近已经可以每天抽出时间临摹一百字的中文字帖，也有了更多的时间思考互联网作为一个整体。&lt;/p&gt;
&lt;p&gt;我并不能确定是否未来会完全抛弃社交网络的使用，且因部分账户的关联 ，以及历史性的存档， Twitter 的主要账户也不会彻底关闭。接下来的一段时间，我会尝试更多的将时间花在更有价值的事情上。&lt;/p&gt;
</content><category term="life"/><category term="personal"/><category term="life"/></entry><entry><title>无题杂记</title><link href="https://tifan.net/blog/2014/10/27/wu-ti-za-ji/" rel="alternate"/><published>2014-10-27T18:30:06-07:00</published><updated>2014-10-27T18:30:06-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2014-10-27:/blog/2014/10/27/wu-ti-za-ji/</id><summary type="html">&lt;p&gt;出于一些奇奇怪怪的原因，十月份回了一次东部。飞机是飞去 IAD 的，这也是我第一次来美国时到达的机场。下飞机以后，从 IAD 出来，看到了一家 Dunkin' Donuts，居然有一点小激动。搬到加州来两年多了，居然还没去过一次。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;出于一些奇奇怪怪的原因，十月份回了一次东部。飞机是飞去 IAD 的，这也是我第一次来美国时到达的机场。下飞机以后，从 IAD 出来，看到了一家 Dunkin' Donuts，居然有一点小激动。搬到加州来两年多了，居然还没去过一次。&lt;/p&gt;


&lt;p&gt;从东部搬来加州后，似乎很多事情一下子就变了。很明显的一点是喜欢去的几家连锁餐厅附近根本没有， Cheesecake Factory, Applebee's 居然最近的都在 downtown LA。有一家店倒是就在我家附近，这家店叫做 P.F. Chang's，我自然不会这么无聊的去吃这种东西。虽然五个人还在附近，但既然进和出更近，也就慢慢习惯了这种加州特色的汉堡。&lt;/p&gt;
&lt;p&gt;和美国人聊天的时候，如果有人问起来你从哪里来，一般我都说我从马里兰来。本以为从马里兰搬来洛杉矶的总是少数人，没想到我州搬来 LA 的还是很多的，虽然部分不理解马里兰挺好的为啥要搬来 LA，但是——我自己不就搬来了么。&lt;/p&gt;
&lt;p&gt;最近在抽一点点时间练字，虽然中文写的难看已是持续了十几年的事实，但总还有救药的。至于为什么在美国还练写汉字？我也不知道。&lt;/p&gt;
&lt;p&gt;独自莫凭栏，无限江山，别时容易见时难。&lt;/p&gt;
&lt;p&gt;流水落花春去也，天上人间。&lt;/p&gt;</content><category term="misc"/><category term="personal"/></entry><entry><title>关于拖延症</title><link href="https://tifan.net/blog/2014/08/24/guan-yu-tuo-yan-zheng/" rel="alternate"/><published>2014-08-24T21:49:30-07:00</published><updated>2014-08-24T21:49:30-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2014-08-24:/blog/2014/08/24/guan-yu-tuo-yan-zheng/</id><summary type="html">&lt;p&gt;如果不是刚才手贱点了 rake generate &amp;amp;&amp;amp; rake deploy；鬼知道那篇在磁盘里放了好几个月的文章什么时候才会被发布出来。&lt;/p&gt;
&lt;p&gt;其实应该是基本 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;如果不是刚才手贱点了 rake generate &amp;amp;&amp;amp; rake deploy；鬼知道那篇在磁盘里放了好几个月的文章什么时候才会被发布出来。&lt;/p&gt;
&lt;p&gt;其实应该是基本不可能吧。&lt;/p&gt;</content><category term="misc"/><category term="生活"/></entry><entry><title>搬家</title><link href="https://tifan.net/blog/2014/07/27/ban-jia/" rel="alternate"/><published>2014-07-27T00:00:27-07:00</published><updated>2014-07-27T00:00:27-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2014-07-27:/blog/2014/07/27/ban-jia/</id><summary type="html">&lt;p&gt;搬家可以是一件小事，也从来不是一件小事。此时，我正在盯着屋子里的箱子，思考如何开始搬家。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;搬家可以是一件小事，也从来不是一件小事。此时，我正在盯着屋子里的箱子，思考如何开始搬家。&lt;/p&gt;


&lt;p&gt;似乎从来美国开始，搬家就是每年都会发生的事情。从约克路搬到榛子圈，再从来到洛杉矶时的颠破流离、一个月搬家三四次，再到灰地街，最后又要搬到威尔榭大道，有主动搬家，也有被动搬家；有相对痛苦的搬家，也有提包走人的时候。总之，没有任何一次搬家，是真正的 settle down -- 当然，谁也不会期望研究生有任何可能 settle down 的。&lt;/p&gt;
&lt;p&gt;于是，这大概是第一次能够短期内定下来的新家了。&lt;/p&gt;
&lt;p&gt;搬家时，搬到新地方的标志，不是开始在那里睡觉了，而是把电脑设备全都运输完毕。&lt;/p&gt;
&lt;hr/&gt;

&lt;p&gt;8 月 24 日补记：搬来一个月以后，还是没有把所有的箱子都拆开。&lt;/p&gt;</content><category term="misc"/><category term="生活"/></entry><entry><title>扯掉太极助手的马甲</title><link href="https://tifan.net/blog/2013/12/23/the-company-behind-taig/" rel="alternate"/><published>2013-12-23T01:39:00-08:00</published><updated>2013-12-23T01:39:00-08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2013-12-23:/blog/2013/12/23/the-company-behind-taig/</id><summary type="html">&lt;p&gt;Abstract: 本文通过常用的社会工程学手段试图还原太极助手这一 iOS 7 Jailbreak bundle 的中国产 AppStore 后面的支持者。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Abstract: 本文通过常用的社会工程学手段试图还原太极助手这一 iOS 7 Jailbreak bundle 的中国产 AppStore 后面的支持者。&lt;/p&gt;


&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ whois taig.com
Domain Name: TAIG.COM
Registry Domain ID: 5070333_DOMAIN_COM-VRSN
Registrar WHOIS Server: whois.godaddy.com
Registrar URL: http://www.godaddy.com
Update Date: &lt;span class="m"&gt;2013&lt;/span&gt;-11-05 &lt;span class="m"&gt;18&lt;/span&gt;:27:16
Creation Date: &lt;span class="m"&gt;1999&lt;/span&gt;-04-06 &lt;span class="m"&gt;23&lt;/span&gt;:00:00
Registrar Registration Expiration Date: &lt;span class="m"&gt;2015&lt;/span&gt;-04-06 &lt;span class="m"&gt;23&lt;/span&gt;:00:00
Registrar: GoDaddy.com, LLC
Registrar IANA ID: &lt;span class="m"&gt;146&lt;/span&gt;
Registrar Abuse Contact Email: abuse@godaddy.com
Registrar Abuse Contact Phone: +1.480-624-2505
Domain Status: clientTransferProhibited
Domain Status: clientUpdateProhibited
Domain Status: clientRenewProhibited
Domain Status: clientDeleteProhibited
Registry Registrant ID:
Registrant Name: zhou shengjin
Registrant Organization:
Registrant Street: Beijing changping district changping road
Registrant City: Beijing
Registrant State/Province: beijing
Registrant Postal Code: &lt;span class="m"&gt;100096&lt;/span&gt;
Registrant Country: China
Registrant Phone: +1.8811225068
Registrant Phone Ext:
Registrant Fax:
Registrant Fax Ext:
Registrant Email: nomas.chow@gmail.com
Registry Admin ID:
Admin Name: zhou shengjin
Admin Organization:
Admin Street: Beijing changping district changping road
Admin City: Beijing
Admin State/Province: beijing
Admin Postal Code: &lt;span class="m"&gt;100096&lt;/span&gt;
Admin Country: China
Admin Phone: +1.8811225068
Admin Phone Ext:
Admin Fax:
Admin Fax Ext:
Admin Email: nomas.chow@gmail.com
Registry Tech ID:
Tech Name: zhou shengjin
Tech Organization:
Tech Street: Beijing changping district changping road
Tech City: Beijing
Tech State/Province: beijing
Tech Postal Code: &lt;span class="m"&gt;100096&lt;/span&gt;
Tech Country: China
Tech Phone: +1.8811225068
Tech Phone Ext:
Tech Fax:
Tech Fax Ext:
Tech Email: nomas.chow@gmail.com
Name Server: NS3.DNSV4.COM
Name Server: NS4.DNSV4.COM
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;显然的， &lt;code&gt;taig.com&lt;/code&gt; 是一个足够老的域名。这个域名里的联系电话， +1.8811225068 应为 +86-18811225068. 这是我们的线索之一。地址『北京市昌平区昌平路』与手机号码归属地北京相匹配。 Email 地址则是另一个有效的线索。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ host www.taig.com
www.taig.com has address &lt;span class="m"&gt;211&lt;/span&gt;.155.82.248
www.taig.com has address &lt;span class="m"&gt;203&lt;/span&gt;.191.148.133
www.taig.com has address &lt;span class="m"&gt;42&lt;/span&gt;.62.21.140
www.taig.com has address &lt;span class="m"&gt;42&lt;/span&gt;.62.21.141
www.taig.com has address &lt;span class="m"&gt;42&lt;/span&gt;.62.21.142
www.taig.com has address &lt;span class="m"&gt;42&lt;/span&gt;.62.21.143
www.taig.com has address &lt;span class="m"&gt;42&lt;/span&gt;.62.21.144
www.taig.com has address &lt;span class="m"&gt;211&lt;/span&gt;.155.82.233
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;看这势头，不像是什么小公司的基础设施。 &lt;code&gt;whois&lt;/code&gt; 得到的结果令人失望，因其均指向了各个数据中心，而 bgp.he.net 并没有给出更多的信息。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ curl -s www.taig.com&lt;span class="p"&gt;|&lt;/span&gt;grep -Eo &lt;span class="s2"&gt;&amp;quot;http://[^\&amp;quot;&amp;#39;]+&amp;quot;&lt;/span&gt;
http://bbdown.iphonespirit.com/site/image/logo.ico
http://js.pingguoyingyong.com/taiji-home/css/style.css
http://bbs.taig.com
http://www.taig.com/archives/category/news
http://static.youku.com/v1.0.0334/v/swf/player_yk.swf
http://static.youku.com/v1.0.0334/v/swf/player_yk.swf
http://www.adobe.com/go/getflash
http://bbdown.iphonespirit.com/ios/7/TaiG_JailBreak_iOS7_ForWin_v1.0.zip
http://bbdown.iphonespirit.com/ios/7/TaiG_JailBreak_iOS7_ForMac_v1.0.dmg
http://www.taig.com/archives/category/news
http://www.taig.com/archives/548
http://bbdown.iphonespirit.com/site/docpic/2348.jpg
http://www.taig.com/archives/548
http://www.taig.com/archives/548
http://www.taig.com/archives/253
http://www.taig.com/archives/251
http://www.taig.com/archives/249
http://www.taig.com/archives/247
http://www.taig.com/archives/241
http://www.taig.com/archives/239
http://www.taig.com/archives/237
http://www.taig.com/archives/233
http://js.pingguoyingyong.com/taiji-home/js/build.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;这一次的结果则看起来很有意思。下面是一些域名的 &lt;code&gt;whois&lt;/code&gt; 信息备份。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ whois pingguoyingyong.com
Domain Name: PINGGUOYINGYONG.COM
Registry Domain ID: 1701302087_DOMAIN_COM-VRSN
Registrar WHOIS Server: whois.godaddy.com
Registrar URL: http://www.godaddy.com
Update Date: &lt;span class="m"&gt;2013&lt;/span&gt;-02-04 &lt;span class="m"&gt;05&lt;/span&gt;:56:33
Creation Date: &lt;span class="m"&gt;2012&lt;/span&gt;-02-09 &lt;span class="m"&gt;09&lt;/span&gt;:52:46
Registrar Registration Expiration Date: &lt;span class="m"&gt;2015&lt;/span&gt;-02-09 &lt;span class="m"&gt;09&lt;/span&gt;:52:46
Registrar: GoDaddy.com, LLC
Registrar IANA ID: &lt;span class="m"&gt;146&lt;/span&gt;
Registrar Abuse Contact Email: abuse@godaddy.com
Registrar Abuse Contact Phone: +1.480-624-2505
Domain Status: clientTransferProhibited
Domain Status: clientUpdateProhibited
Domain Status: clientRenewProhibited
Domain Status: clientDeleteProhibited
Registry Registrant ID:
Registrant Name: John Lennon
Registrant Organization: Apple Application INC.
Registrant Street: China
Registrant City: guangdong
Registrant State/Province: baiyun
Registrant Postal Code: &lt;span class="m"&gt;000000&lt;/span&gt;
Registrant Country: China
Registrant Phone: +86.138000138000
Registrant Phone Ext:
Registrant Fax:
Registrant Fax Ext:
Registrant Email: fidate@gmail.com
Registry Admin ID:
Admin Name: John Lennon
Admin Organization: Apple Application INC.
Admin Street: China
Admin City: guangdong
Admin State/Province: baiyun
Admin Postal Code: &lt;span class="m"&gt;000000&lt;/span&gt;
Admin Country: China
Admin Phone: +86.138000138000
Admin Phone Ext:
Admin Fax:
Admin Fax Ext:
Admin Email: fidate@gmail.com
Registry Tech ID:
Tech Name: John Lennon
Tech Organization: Apple Application INC.
Tech Street: China
Tech City: guangdong
Tech State/Province: baiyun
Tech Postal Code: &lt;span class="m"&gt;000000&lt;/span&gt;
Tech Country: China
Tech Phone: +86.138000138000
Tech Phone Ext:
Tech Fax:
Tech Fax Ext:
Tech Email: fidate@gmail.com
Name Server: F1G1NS1.DNSPOD.NET
Name Server: F1G1NS2.DNSPOD.NET
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;经查，此域名的邮箱拥有另一个域名，&lt;code&gt;idestop.com&lt;/code&gt;。邮箱的主人早在 2006 年便并聚在了在北京市一个名为『新龙城』的社区内。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ whois iphonespirit.com

Domain Name ..................... iphonespirit.com
Sponsoring Registrar ............ HICHINA ZHICHENG TECHNOLOGY LTD.
Name Server ..................... ns3.dnsv4.com
                                  ns4.dnsv4.com
Registrant ID ................... whois-protect
Registrant Name ................. WHOIS AGENT
Registrant Organization ......... DOMAIN WHOIS PROTECTION SERVICE
Registrant Address .............. &lt;span class="m"&gt;3&lt;/span&gt;/F.,HiChina Mansion,No.27 Gulouwai Avenue
                                  Dongcheng District,Beijing &lt;span class="m"&gt;100120&lt;/span&gt;,China
Registrant City ................. Beijing
Registrant Province/State ....... Beijing
Registrant Postal Code .......... &lt;span class="m"&gt;100120&lt;/span&gt;
Registrant Country Code ......... CN
Registrant Phone Number ......... +8610.64242266
Registrant Fax .................. +8610.84138796
Registrant Email ................ domainadm@hichina.com
Administrative ID ............... whois-protect
Administrative Name ............. WHOIS AGENT
Administrative Organization ..... DOMAIN WHOIS PROTECTION SERVICE
Administrative Address .......... &lt;span class="m"&gt;3&lt;/span&gt;/F.,HiChina Mansion,No.27 Gulouwai Avenue
                                  Dongcheng District,Beijing &lt;span class="m"&gt;100120&lt;/span&gt;,China
Administrative City ............. Beijing
Administrative Province/State ... Beijing
Administrative Postal Code ...... &lt;span class="m"&gt;100120&lt;/span&gt;
Administrative Country Code ..... CN
Administrative Phone Number ..... +8610.64242266
Administrative Fax .............. +8610.84138796
Administrative Email ............ domainadm@hichina.com
Billing ID ...................... whois-protect
Billing Name .................... WHOIS AGENT
Billing Organization ............ DOMAIN WHOIS PROTECTION SERVICE
Billing Address ................. &lt;span class="m"&gt;3&lt;/span&gt;/F.,HiChina Mansion,No.27 Gulouwai Avenue
                                  Dongcheng District,Beijing &lt;span class="m"&gt;100120&lt;/span&gt;,China
Billing City .................... Beijing
Billing Province/State .......... Beijing
Billing Postal Code ............. &lt;span class="m"&gt;100120&lt;/span&gt;
Billing Country Code ............ CN
Billing Phone Number ............ +8610.64242266
Billing Fax ..................... +8610.84138796
Billing Email ................... domainadm@hichina.com
Technical ID .................... whois-protect
Technical Name .................. WHOIS AGENT
Technical Organization .......... DOMAIN WHOIS PROTECTION SERVICE
Technical Address ............... &lt;span class="m"&gt;3&lt;/span&gt;/F.,HiChina Mansion,No.27 Gulouwai Avenue
                                  Dongcheng District,Beijing &lt;span class="m"&gt;100120&lt;/span&gt;,China
Technical City .................. Beijing
Technical Province/State ........ Beijing
Technical Postal Code ........... &lt;span class="m"&gt;100120&lt;/span&gt;
Technical Country Code .......... CN
Technical Phone Number .......... +8610.64242266
Technical Fax ................... +8610.84138796
Technical Email ................. domainadm@hichina.com
Domain Create Date .............. &lt;span class="m"&gt;2013&lt;/span&gt;-03-29 &lt;span class="m"&gt;19&lt;/span&gt;:54:24
Expiration Date ................. &lt;span class="m"&gt;2014&lt;/span&gt;-03-29 &lt;span class="m"&gt;19&lt;/span&gt;:54:24
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;虽然这个域名有 whois protect，但依然可以进一步的进行 DNS 分析。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ host bbdown.iphonespirit.com
bbdown.iphonespirit.com is an &lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; bbdown.iphonespirit.com.51ccdn.com.
bbdown.iphonespirit.com.51ccdn.com is an &lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; c01.i08.sisyun.com.
c01.i08.sisyun.com is an &lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; c01.i08.cncsd.hadns.net.
c01.i08.cncsd.hadns.net has address &lt;span class="m"&gt;61&lt;/span&gt;.156.242.76
c01.i08.cncsd.hadns.net has address &lt;span class="m"&gt;60&lt;/span&gt;.210.10.77
c01.i08.cncsd.hadns.net has address &lt;span class="m"&gt;61&lt;/span&gt;.156.157.183
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;随手一搜索，我们&lt;a href="https://www.google.com/search?q=&amp;quot;iphonespirit.com&amp;quot;"&gt;可以发现&lt;/a&gt;『苹果核』使用的分发域名便是这个域名。而苹果核使用了 360 的核心，不得不让人有某些联想。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ host js.pingguoyingyong.com
js.pingguoyingyong.com has address &lt;span class="m"&gt;117&lt;/span&gt;.121.11.32
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;接下来，我们搜索这个 IP 地址则得到了一个&lt;a href="http://www.dnspoo.com/a/www.kuaiyong.com"&gt;惊奇的发现&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ host www.kuaiyong.com
www.kuaiyong.com has address &lt;span class="m"&gt;117&lt;/span&gt;.121.11.16
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;经查，海外解析地址为 .16，国内解析地址为 .32&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;$ curl -s --head -H&amp;quot;Host: www.kuaiyong.com&amp;quot; 117.121.11.32
HTTP/1.1 200 OK
Server: nginx/1.0.15
Date: Sun, 22 Dec 2013 22:40:11 GMT
Content-Type: text/html
Content-Length: 9268
Last-Modified: Thu, 19 Dec 2013 05:47:21 GMT
Connection: keep-alive
Accept-Ranges: bytes

$ curl -s -H&amp;quot;Host: nosuchhost.com&amp;quot; 117.121.11.32 | grep &amp;#39;&lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;&amp;#39;
&lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Test Page for the Nginx HTTP Server on EPEL&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;

$ curl -s -H&amp;quot;Host: www.kuaiyong.com&amp;quot; 117.121.11.32 | grep &amp;#39;&lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;&amp;#39;
&lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt; 快用苹果助手 &lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;a href="http://s.weibo.com/user/%25E5%25A4%25AA%25E6%259E%2581%25E5%258A%25A9%25E6%2589%258B&amp;amp;Refer=weibo_user"&gt;惊奇的发现之2&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;结论&lt;/h1&gt;
&lt;p&gt;由于 TaiG 的下载链接托管在了 &lt;code&gt;iphonespirit.com&lt;/code&gt; 上，我们有理由相信 TaiG 和 360 或 360 投资的某些公司有某种联系。由于 TaiG 的 JS 资源托管到了 &lt;code&gt;pingguoyingyong.com&lt;/code&gt; 上，我们有理由相信 TaiG 和快用助手有某种深层次的合作，或曰 TaiG 只是快用的另一个马甲。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;你们将为你们的无知和狂妄而流下悔恨的眼泪，而这些，我都将作为我科学事业道路上的绊脚石。
—— 大锑赵明毅&lt;/p&gt;
&lt;/blockquote&gt;</content><category term="misc"/><category term="internet"/></entry><entry><title>in-addr.arpa Sux</title><link href="https://tifan.net/blog/2013/08/16/in-addr-dot-arpa-sux/" rel="alternate"/><published>2013-08-16T18:02:00-07:00</published><updated>2013-08-16T18:02:00-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2013-08-16:/blog/2013/08/16/in-addr-dot-arpa-sux/</id><summary type="html">&lt;p&gt;是的，作为一贯的 Abuser，我把域名换成了 http://1.175.64.211.in-addr.arpa/ 。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;是的，作为一贯的 Abuser，我把域名换成了 http://1.175.64.211.in-addr.arpa/ 。&lt;/p&gt;


&lt;p&gt;互联网真神奇，为什么几乎没有人想到给 in-addr.arpa 加上正向解析呢？&lt;/p&gt;
&lt;p&gt;当然，我们不必说 &lt;code&gt;hn.kd.ny.adsl&lt;/code&gt; ，更不必说大部分中国 IP 地址没有做 PTR 了。&lt;/p&gt;</content><category term="misc"/><category term="internet"/></entry><entry><title>谈智能路由器以及网络安全</title><link href="https://tifan.net/blog/2013/08/03/smart-routers-and-internet-security/" rel="alternate"/><published>2013-08-03T23:35:00-07:00</published><updated>2013-08-03T23:35:00-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2013-08-03:/blog/2013/08/03/smart-routers-and-internet-security/</id><summary type="html">&lt;p&gt;TL; DR 请自行分辨智能路由器的功能与可能引入的安全威胁，并慎重考虑这种设备的应用。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;声明: 本文作者与提到的品牌间没有利益关系。作者仅以网络安全研究人员的身份从技术角度对所提到的产品进行分析。&lt;/em&gt;&lt;/p&gt;
</summary><content type="html">&lt;p&gt;TL; DR 请自行分辨智能路由器的功能与可能引入的安全威胁，并慎重考虑这种设备的应用。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;声明: 本文作者与提到的品牌间没有利益关系。作者仅以网络安全研究人员的身份从技术角度对所提到的产品进行分析。&lt;/em&gt;&lt;/p&gt;


&lt;p&gt;最近，市面上出现了一种『智能路由器』。然而，这种智能路由器其实提高了终端用户的安全威胁，并引入了部分新的安全挑战。这类安全威胁主要是引入了第三方信任关系而导致的。&lt;/p&gt;
&lt;p&gt;首先，来纠正一处官方网站上的错误。根据其官方网站，这种路由器是『全球首款带操作系统的路由器』。这种说法，充分的暴露了此路由器生产厂商对于网络协议以及基础架构的不了解。稍微懂一些计算机的工程师便知道，操作系统本身的这个概念便是非常模糊的。但凡可以管理计算机硬件资源、协调用户态应用程序的计算机软件便可以称之为操作系统。从中国大陆市面上最常见的 TP-Link 路由器（至少是作者在国内的时候）到运行互联网核心业务的 Cisco 12000 系列路由器，操作系统也从 VxWorks 到 IOS，没有任何一个是没有操作系统的。&lt;/p&gt;
&lt;p&gt;根据其官方主页的指示，这种路由器有自动升级的功能。对于此类功能，常见的攻击有 DNS 投毒/劫持 DNS 后跳转其升级服务器到攻击者的服务器上，通过伪造升级包的方式发起的攻击。对于没有签名，或者使用弱签名手段的方式进行的签名，很容易造成极高的安全风险。&lt;/p&gt;
&lt;p&gt;其官方主页亦提示了此种路由器拥有带内远程管理的功能。通过来自新浪微博的讨论得知，这种带内管理的实现依赖于厂商设置的服务器进行的中转功能。因此，我们可以容易的得出这样一个结论，即是此种路由器可以接受任意厂商下达的指令，即使这种指令是终端用户以及获得授权的管理员的意愿之外的。这将用户暴露在以下的几种风险中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过直接攻击厂商，获得控制机的权限，并下发指令&lt;/li&gt;
&lt;li&gt;通过记录下发指令的报文，对其进行逆向工程，合适时配合中间人攻击，成功破解其通讯协议后直接下发指令&lt;/li&gt;
&lt;li&gt;通过暴力破解或者社会工程学攻击终端用户，获取其权限并直接下发指令&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于开发者来说，最容易掉以轻心也最容易犯的错误便是 security by obscurity。&lt;/p&gt;
&lt;p&gt;这种路由器值得注意的另一个功能，便是去除视频网站中附加的广告。在不讨论这种行为本身是否合乎视频网站的用户协议的前提下，我们很容易知道这种路由器抑或直接进行了深度包检测并替换了含有某些规则的报文，亦或直接将视频网站跳转至第三方服务器。通过网友讨论，得知此路由器使用了第二种策略。而这种策略必然是通过本地或者远程的数据查询对照而得出的。这又引入了一个新的问题——在用户的授权下，这种路由器将处理某些流量，使其重定向到第三方用户非信任的服务器中。一方面，第三方服务器的安全性并没有得到验证；而另一方面，这种黑盒很有可能在用户不知情的情况下，有意或者无意的将某些用户未指定转发的流量转发到第三方服务器上。&lt;/p&gt;
&lt;p&gt;通过介绍，我们很容易得出这种网络设备的一部分能力：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;接受远程控制，即使这种控制来自于未经授权的攻击者&lt;/li&gt;
&lt;li&gt;自动的下载来自非信任区域的可执行文件，并在本地执行且替换本地的可执行文件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这个世界上，有另一种东西也符合上面的能力，这便是我们常说的 BotNet。远程控制服务器便是 C&amp;amp;C，而终端，则是用户购买来的智能路由器。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;以下是阴谋论&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;推特网友 @chainkhoo 提到，在使用了这种路由器的加速功能后，发现 Google Account 在异地登陆。这种登陆是发生在关闭了加速功能后的，而报警显示，登陆的尝试来自于&lt;em&gt;加速服务器&lt;/em&gt;上的。&lt;/p&gt;
&lt;p&gt;It's entirely possible to hijack &lt;code&gt;accounts.google.com&lt;/code&gt; by redirecting SSL encrypted traffic to non-SSL traffic, i.e. proxying http://account.google.com to https://accounts.google.com and record what the f**k they want.&lt;/p&gt;
&lt;p&gt;这个团队最近的宣传攻势相当强，而且已经公开宣传了翻墙。我想，这大概已经触犯了共产党的底线——而看到了上面的事实，又想一想现实——这大概就是 &lt;code&gt;state sponsored hackers&lt;/code&gt; 吧！即赚钱，又获得了这么大的僵尸网络，还跪舔了有关部门。呵呵，贵国人挺厉害的。&lt;/p&gt;</content><category term="misc"/><category term="security"/></entry><entry><title>No Hello Please</title><link href="https://tifan.net/blog/2013/07/17/no-hello-please/" rel="alternate"/><published>2013-07-17T10:36:00-07:00</published><updated>2013-07-17T10:36:00-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2013-07-17:/blog/2013/07/17/no-hello-please/</id><summary type="html">&lt;p&gt;TL; DR 请不要在即时消息中说你好。请直接说出你的问题。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;TL; DR 请不要在即时消息中说你好。请直接说出你的问题。&lt;/p&gt;


&lt;p&gt;这是一个很常见的即时消息转录：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;RandomGuy (12:34:07 PM): yt?&lt;/p&gt;
&lt;p&gt;me (12:34:24 PM): Yep&lt;/p&gt;
&lt;p&gt;## RandomGuy is typing...&lt;/p&gt;
&lt;p&gt;## RandomGuy stopped typing&lt;/p&gt;
&lt;p&gt;## RandomGuy is typing...&lt;/p&gt;
&lt;p&gt;-*- tifan is curious -*-&lt;/p&gt;
&lt;p&gt;RandomGuy (12:47:33 PM): I've got a foo installation with bar configuration, however \&amp;lt;SNIP&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在这个转录的消息中， &lt;code&gt;RandomGuy&lt;/code&gt; 在发送第一条消息到发送真正有价值的消息时，花费了 13 分钟的时间用于思考自己的问题如何描述。这 13 分钟，对话的另一方则只能看到即时消息的提示，对方在打字或者停止了打字（也许是在思考）。&lt;/p&gt;
&lt;p&gt;而实际情况则是，如果 &lt;code&gt;RandomGuy&lt;/code&gt; 不说 yt, hi, ping 一类的寒暄语，这 13 分钟本可以不通知消息的另一方的。即时消息不同于电话的一点在于，打字的速度比讲话的速度慢很多，因此，yt, hi 一类的问候语并不显得『更礼貌』。&lt;/p&gt;
&lt;p&gt;鼓励使用下面的聊天方式：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;RandomGuy (12:32:11 PM): yt? I've fetched foo from //depot/bar/... and the buildout \&amp;lt;SNIP&gt;&lt;/p&gt;
&lt;p&gt;tifan (12:33:41 PM): That's probably because you're using the wrong Python version. Try Python 2.7.5. \&amp;lt;Insert meme here&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;如果你支持使用此种聊天方式提高效率，请将本文链接附加在即时聊天工具的状态中。&lt;/em&gt;&lt;/p&gt;</content><category term="misc"/><category term="social"/></entry><entry><title>BOFH</title><link href="https://tifan.net/blog/2013/07/13/bofh/" rel="alternate"/><published>2013-07-13T18:38:00-07:00</published><updated>2013-07-13T18:38:00-07:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2013-07-13:/blog/2013/07/13/bofh/</id><summary type="html">&lt;p&gt;一个 BOFH 劫持了 google analytics 的 DNS，在 ga.js 里插入了下面一句话：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;document.write(&amp;#39;&amp;lt;iframe src=&amp;quot;http://www.renren.com/Logout.do&amp;quot; width=&amp;quot;0px&amp;quot; height=&amp;quot;0px&amp;quot; border=0 style=&amp;quot;display:none;&amp;quot;&amp;gt;&amp;#39;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;于是，整个网络里的校 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;一个 BOFH 劫持了 google analytics 的 DNS，在 ga.js 里插入了下面一句话：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;document.write(&amp;#39;&amp;lt;iframe src=&amp;quot;http://www.renren.com/Logout.do&amp;quot; width=&amp;quot;0px&amp;quot; height=&amp;quot;0px&amp;quot; border=0 style=&amp;quot;display:none;&amp;quot;&amp;gt;&amp;#39;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;于是，整个网络里的校内就经常被登出了。&lt;/p&gt;</content><category term="misc"/><category term="security"/></entry><entry><title>做产品与中国国情（上）：创新以及产品为何失败</title><link href="https://tifan.net/blog/2012/11/01/product-and-china/" rel="alternate"/><published>2012-11-01T14:00:00+00:00</published><updated>2012-11-01T14:00:00+00:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2012-11-01:/blog/2012/11/01/product-and-china/</id><summary type="html">&lt;p&gt;这几天，我在思考一个问题，什么产品，可能有一个潜在的商业模型，同时又符合中国国情？这个产品，必然是和计算机（倒不仅限于互联网）相关，因为这是我最熟悉的领域。因此，有了下面的一些想法，还在慢慢完善，今天先总结一下最近几个月的一些经验和教训以及思考，过几天大概还会继续写一写产品怎么做以及中国国情到底是什么。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;这几天，我在思考一个问题，什么产品，可能有一个潜在的商业模型，同时又符合中国国情？这个产品，必然是和计算机（倒不仅限于互联网）相关，因为这是我最熟悉的领域。因此，有了下面的一些想法，还在慢慢完善，今天先总结一下最近几个月的一些经验和教训以及思考，过几天大概还会继续写一写产品怎么做以及中国国情到底是什么。&lt;/p&gt;


&lt;h2&gt;什么是创新&lt;/h2&gt;
&lt;p&gt;当然，要看怎么定义创新。我们可以说“微创新”是创新——没错儿，确实有一定的创新。在投资人的角度看，投资给会赚钱的流氓显然比投资给不会赚钱的君子得到的回报更多。微博算创新么？对于整天泡在 Twitter 上的我来说，这玩意儿很中国，确实是 Twitter 的 localization。&lt;/p&gt;
&lt;p&gt;看另外两个我的朋友的创业产品，姚欣宇的 GitCafe 以及高阳的 SegFault。为什么要提这两个产品呢？首先，这两个人我认识，也认为是很靠谱的创业者（后者欠我一顿包子暂且不说），其次，二位都是互联网行业的创业者，搞的东西也是我相当感兴趣的也是会用得到的，再次，二位的东西都有国外的先驱。&lt;/p&gt;
&lt;p&gt;有国外的先驱这一点，可能是大家会攻击的最多的。这里，我想为这二位辩护一下。GitHub 也好，StackOverflow 也好，界面都是英文的！即使是在美国工作的我，Gmail 页面也是选用中文，汉语总是我的母语，虽然技术英文同样是一眼就知道，但是汉语更亲切是毫无疑问的。国内的程序员，大家都知道，英文水平不过如此，想表达清楚意思问题不大，但是看起来还是稍微有一些吃力的。其次，这些产品都是完全的本地化的，不扯虚的。这点，很多装B人士都爱搞些看不懂的东西出来。单凭这两点，做这些东西也是值得的。&lt;/p&gt;
&lt;p&gt;或者，用 GRE 类比的方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;新浪：MSNBC&lt;/li&gt;
&lt;li&gt;Solidot：Slashdot&lt;/li&gt;
&lt;li&gt;GitCafe：GitHub&lt;/li&gt;
&lt;li&gt;SegFault：StackOverflow&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不可否认的是，国人有跟风的习惯，也有不认真的习惯。君不见华人偷渡来美国以后都在开餐馆。饭做的好吃也就罢了，我宁愿吃肯德基也不愿意吃这些中餐，即便中餐的价格更低。做产品也是如此。&lt;/p&gt;
&lt;h2&gt;怎样做差的产品&lt;/h2&gt;
&lt;p&gt;国内的好产品，真的不多。如果让我说国内做的最好的，一时还真想不出来。思想太西化也许是一点，但是我们自己的产品做不好是不能否认的。最近在爱范儿担负起来了产品经理的职责以及另一个创业团队的技术总监之后，更是感觉到了几点困难：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不认真&lt;/li&gt;
&lt;li&gt;人不好管&lt;/li&gt;
&lt;li&gt;不遵守规则&lt;/li&gt;
&lt;li&gt;不认账&lt;/li&gt;
&lt;li&gt;刷存在感&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;上面五点是近乎废话的话。但这五句废话确实是这几个月甚至最近几年来总结出来的教训。特别是最后一点，我的感触太深了。&lt;/p&gt;
&lt;p&gt;不认真，体现在哪里呢？体现在细节上。举个例子，大家就知道了。&lt;/p&gt;
&lt;p&gt;新浪微博，都用过吧。校内，都在用吧。他们的页脚都有一些链接你们知道么？傻逼的是，新浪微博也好，校内也好，到了页脚以后总会自动加载一些 feed，导致想点下面的链接的我总是没办法点击！&lt;/p&gt;
&lt;p&gt;这个设计，大概是抄袭的 Twitter。但是 Twitter 怎么做的呢？下面的链接移动到了左边呀。左边的链接是浮动的。新浪浪费掉了这个空间，校内呢，一堆广告，大片空白，空间也浪费掉了。&lt;/p&gt;
&lt;p&gt;类似的例子少吗？在国内的你们留意下身边就会发现，一点也不少！为什么呢？不认真呗。认真设计，认真测试，怎么会有这种问题？就只是不认真这一点，很多时候，看到国内的产品，我就已经先入为主的认为这东西是很烂的。多数情况下，不会错。&lt;/p&gt;
&lt;p&gt;人不好管？更好解释了。大家心里都静不下来呀，老想着怎么闷声发大财，可惜啊，图森跑，乃乙武。耍点小聪明，以为经理不知道么？乃乙武呀！耍小聪明，害的还是自己。短的来说，项目可能要翻工，代码要重写，长期来说，自己的知识水平、技术能力没有得到提高，害的不是自己么？&lt;/p&gt;
&lt;p&gt;不遵守规则，司空见惯！哪次回国前几天敢一个人过马路？从宏观来说，整个社会都不在把规则看成规则的时候，在所有人都走捷径，走后门，找关系的时候，你指望社会的组成分子——个人遵守规则？这点呢，和第五点，刷存在感，也有很大的联系。而在计算机行业中，不遵守规则的行为，我见的太多了，简单举一点例子，所有人都会觉得自己中枪了的。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不会缩进. 我们的教育必须要检讨了，我相信很多老师都不知道什么是缩进。&lt;/li&gt;
&lt;li&gt;不会给变量命名. ijk, abc, tmp, var123 横行。同样，还是教育的问题。我觉得，真的有必要开一门课叫做 CS001，专门教大家如何学计算机。&lt;/li&gt;
&lt;li&gt;不会抽象. 其实还是个人修养的问题，但是为什么不想学习，不想规范化，不想学习国外的先进经验呢？&lt;/li&gt;
&lt;li&gt;不懂软件工程. 适当利用软件工程的方法真的可以大幅度提高效率的。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不认账。或者，稍微说的宽泛一些，抵赖以及找借口、推卸责任。“我当时心情不好”之类的扯淡理由，拜托，不要用！工作永远是工作，你心情不好，请回家休息，调整好心情再来上班。（澄清一下，上面那句话是认真的，心情不好真的不要来上班，不要去工作。）做错了，真的没有关系的，请承认错误，请记住这个错误，下次修正。中国人爱面子，但是承认错误不丢脸的。况且，你也不是圣人，犯错又如何呢？承认又如何呢？&lt;/p&gt;
&lt;p&gt;刷存在感。这点，简直能概括所有的陋习！相比大家办过毕业离校手续，或者办过护照，或者办过户口迁移或者任何其他和政府打交道的工作吧。为什么办事难呢？因为所有人都在疯狂的刷存在感！不卡你一下，怎么能显示的出来我的厉害呢？一人卡一下，什么事情都办不成。共产党官方将此称为“官僚作风”，但是显然现在……不是当官的也在刷存在感呀。比如，邮电局？嘿嘿嘿。不听指挥有时候也能归为刷存在感的一类。&lt;/p&gt;
&lt;p&gt;最危险的，就是在公司内部刷存在感。朝三暮四很可怕，不合理的规范国内也多的是，使用行政力量而不是通过说理服人来进行管理也算。它的危险之处在于作为管理人员，官僚作风会让被管理的职员产生抵触情绪，消极工作，或者因为刷存在感，在不了解情况的情况下，制定了一些不合理的规定或者错误的方向性决定。这种情况，管理人员要为错误的决策付出代价的。但是国内的信用、行业声誉体系也并未形成，开掉他，他可以立即去另一家公司祸害别人。&lt;/p&gt;
&lt;p&gt;以上几点综合起来，我们看到的就是一个典型的（失败的、有中国特色的）产品。举个例子？12306呗。再来一个？12306ng, 哈哈哈。能用么？前者还算能用，别扭。后者就是一笑话。&lt;/p&gt;
&lt;p&gt;再来一个？青大的 OA 系统。再来？现在的青大主页也算。还想要？随便翻个政府网站。想要个非计算机的？随便什么国产劣质产品都是。&lt;/p&gt;
&lt;p&gt;因为对国内的体系的不了解，一部分内容并不是取自于计算机行业的，而是来自于学习我家工厂的一些管理经验总结出来的。但是我相信对于任何行业，这基本都是适用的。&lt;/p&gt;</content><category term="misc"/><category term="thoughts"/></entry><entry><title>抓恶意爬虫以及防止恶意爬虫的一点想法</title><link href="https://tifan.net/blog/2012/06/23/thoughts-about-spambots/" rel="alternate"/><published>2012-06-23T06:21:33+00:00</published><updated>2012-06-23T06:21:33+00:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2012-06-23:/blog/2012/06/23/thoughts-about-spambots/</id><summary type="html">&lt;p&gt;爬虫很讨厌。&lt;/p&gt;
&lt;p&gt;为什么这么讨厌爬虫呢？是因为这玩意儿会让我的缓存全部失效。你想，平时大部分用户都是访问网站首页的几篇文章而已，突然来个爬虫，爬掉你的整个网站。例如爱范儿，几千篇文章，一篇一篇的爬下去，吃 CPU 不说，爬到后面，缓存干脆命中不到，只好去查询 MySQL，查询是要费时间的，也是要跑 TCP 的， overhead 很大的亲。如果爬的频率一高，机器说不定就会宕机了。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;爬虫很讨厌。&lt;/p&gt;
&lt;p&gt;为什么这么讨厌爬虫呢？是因为这玩意儿会让我的缓存全部失效。你想，平时大部分用户都是访问网站首页的几篇文章而已，突然来个爬虫，爬掉你的整个网站。例如爱范儿，几千篇文章，一篇一篇的爬下去，吃 CPU 不说，爬到后面，缓存干脆命中不到，只好去查询 MySQL，查询是要费时间的，也是要跑 TCP 的， overhead 很大的亲。如果爬的频率一高，机器说不定就会宕机了。&lt;/p&gt;


&lt;p&gt;为了防止爬虫，我的工作也没少做。一是按时分析 HTTP access log，看什么不正常的东西，二是看 collectd，看图形有什么不正常的，三就是封不对的 User-agent。下面是 nginx 封掉的一些讨厌的 bot:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;if ($http_user_agent ~ &amp;quot;Rome&amp;quot;) {&lt;/span&gt;
&lt;span class="err"&gt;    return 403;&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;if ($http_user_agent = &amp;quot;Mozilla/4.0&amp;quot;) {&lt;/span&gt;
&lt;span class="err"&gt;    return 444;&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;if ($http_user_agent ~ &amp;quot;linkcrawler&amp;quot;) {&lt;/span&gt;
&lt;span class="err"&gt;    return 403;&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;span class="err"&gt;if ($http_user_agent ~ &amp;quot;larbin&amp;quot;) {&lt;/span&gt;
&lt;span class="err"&gt;    return 403;&lt;/span&gt;
&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;另外，今天曝光网宿科技的一个 bot 121.9.213.36, user-agent: "Mozilla/8.0 (compatible; MSIE 8.0; Windows 7" (自己还少了一个括号)。&lt;/p&gt;
&lt;p&gt;但是上面的都是事后诸葛亮，出事儿的时候根本没用。于是，想到了一点也许可以解决 spambot 的方法。&lt;/p&gt;
&lt;p&gt;首先，我们应该分析 spambot 的行为。让我们思考，bot 的作者会怎么写一个 bot 呢？&lt;/p&gt;
&lt;p&gt;Approach I: 输入一个页面，分析页面内所有链接，加入链接到一个 hashmap 中，爬过的写一个标志位，没爬过的分发到爬虫线程。&lt;/p&gt;
&lt;p&gt;Approach II: 输入一个 URL pattern，直接生成待爬取页面列表，爬。&lt;/p&gt;
&lt;p&gt;不管是哪种 approach，他们的目的都是获取网页内容。而这和普通用户有什么区别呢？&lt;/p&gt;
&lt;p&gt;区别 I: Bot 不大可能请求 CSS 等资源，而正常用户会。&lt;/p&gt;
&lt;p&gt;区别 II: Bot 不大可能去执行 JavaScript (当然现在也有 headless 的基于浏览器的 bot)&lt;/p&gt;
&lt;p&gt;区别 III: Bot 的访问趋势是不同于人的，人的正常浏览习惯是在首页浏览，看到感兴趣的，打开一篇，看完继续翻页，而机器则不是这样子的&lt;/p&gt;
&lt;p&gt;有这三个 major 区别，我们很容易在 Web 服务器上阻断 spambot。当然，如果使用 headless 的浏览器，区别 I 就会被干掉。区别 II 仍然有效，待会儿说。&lt;/p&gt;
&lt;p&gt;对于简单的 bot，我们可以使用 nginx 外加模块，连接 memcached 计数的办法干掉。比如某个 ip 访问页面，计数器加 5，访问 CSS/JS 则减 1，半小时以后 invalidate。一般的用户很快就会被清到 0，因为一般页面上 CSS/JS 数量会远远超过 5 per page，何况我们还有图片。&lt;/p&gt;
&lt;p&gt;对于简单的 bot，我们还可以使用 JavaScript 请求 AJAX，访问某个页面，这个页面将 memcached 中该 ip 的记录标志为可信。&lt;/p&gt;
&lt;p&gt;对于 headless browser 类型的 bot，我们还是有办法解决的。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;创建浏览器对象需要内存。他们会希望页面加载结束后，立即抓取内容，加载下一个页面，我们可以使用 JavaScript 延时 AJAX 的方法标志&lt;/li&gt;
&lt;li&gt;headless browser 也希望尽快的抓取内容，不妨对人的访问速度进行 profile，如果某用户的 pattern 不符合 profile，则标记为可疑&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;当然，bot 也是有好有坏的，比如我们希望 Google bot 等尽快抓取，因此可能还需要实现某个白名单（注意 User-agent 不代表一切，还有 source IP）。这样一来，大部分 spambot 就会没办法抓取。至于某些花了很大精力抓取内容的 bot，我只想说，最近的文章可以用 RSS 获取全文，您何苦呢？！&lt;/p&gt;
&lt;p&gt;最后，对抗 bot 的战争是永远不会结束的，上面仅仅是对可能的反 bot 方法进行初步讨论。遇到疑似 bot 的访问时，不妨提供 recaptcha 让它填写。另外，一些人工智能算法也有可能可以 apply 进来。但是，我们要牢记，我们的目的只是挡住大部分 bot，不影响站点的正常工作而已。After all，如果 launch 了一大把分布的 bot，不断的抓取，这种行为应该视为 DDoS 的一种。对于 DDoS，目前没有什么太好的办法，因此我也就不继续讨论了。&lt;/p&gt;
&lt;p&gt;本文的部分研究可以在我之前的一篇论文中找到。&lt;a href="http://ieeexplore.ieee.org/xpl/articleDetails.jsp?arnumber=6127462"&gt;IEEE Xplore&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://difan.org.cn/BlogIMG/Botdetection.pdf"&gt;A Distributed Network-Sensor Based Intrusion Detection Framework in Enterprise Networks&lt;/a&gt;, Difan Zhang, Wei Yu, and Rommie Hardy. Free Local Copy.&lt;/p&gt;</content><category term="misc"/><category term="security"/></entry><entry><title>令人失望的阿里云</title><link href="https://tifan.net/blog/2012/06/08/disappointing-aliyun/" rel="alternate"/><published>2012-06-08T12:09:32+00:00</published><updated>2012-06-08T12:09:32+00:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2012-06-08:/blog/2012/06/08/disappointing-aliyun/</id><summary type="html">&lt;p&gt;阿里云果然烂透了，从用的第一天起，就充满了杯具。&lt;/p&gt;
&lt;p&gt;爱范儿迁移到阿里云的第一天早晨9点，阿里云就开始挂了，无奈之下，切 …&lt;/p&gt;</summary><content type="html">&lt;p&gt;阿里云果然烂透了，从用的第一天起，就充满了杯具。&lt;/p&gt;
&lt;p&gt;爱范儿迁移到阿里云的第一天早晨9点，阿里云就开始挂了，无奈之下，切换回了日本Linode，看了看，似乎是我自己的配置问题，后来因为人太多，10M小水管不够用就没人可以访问了，于是我们上了又拍云cdn，感觉效果不错，访问速度快了很多，也启用了 cdn.ifanr.cn 的 cdn 域名。&lt;/p&gt;
&lt;p&gt;于是，修 PHP，改 Wordpress，又搬了回去，似乎问题没有了，团队成员也都很开心，速度变快了----除了我，成天被 GFW RESET…&lt;/p&gt;
&lt;!-- more --&gt;

&lt;p&gt;某天，发现 PHP 确实有问题，于是让 PHP 吐个核看看，写到磁盘里。我们用了 APC 做 Object Cache，512M 的 shm，当然这些也会被 dump 出来，8个 PHP-FPM 进程一共是 4GB。写 core 不要紧，一看才知道 IO 居然这么差，写 core 的时候 IOWAIT 居然到了 100%，愣是挂了半个小时，这半个小时 ssh 都登陆不进去，后来从 collectd 里看到，居然 loadavg 到了三十几，所有的 CPU 全都在等 IO，所有的 read() 全都被 block 住了--你妹！&lt;/p&gt;
&lt;p&gt;我有个坏毛病，没事儿爱输入 sync 玩，某次输入一看，怎么这么久！&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;[L:root@ifanr-cn] ~# time sync&lt;/span&gt;
&lt;span class="err"&gt;sync  0.00s user 0.02s system 0% cpu 4.927 total&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;于是，跑个 hdparm 看了看，一看不要紧----&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;[L:root@ifanr-cn] ~# hdparm -t /dev/xvdb1&lt;/span&gt;
&lt;span class="err"&gt;/dev/xvdb1:&lt;/span&gt;
&lt;span class="err"&gt;Timing buffered disk reads:  74 MB in  3.11 seconds =  23.76 MB/sec&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;那么，我们来 iostat -x 看看吧&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt;   &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;nice&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="k"&gt;system&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;iowait&lt;/span&gt;  &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;steal&lt;/span&gt;   &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;idle&lt;/span&gt;
           &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;84&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;63&lt;/span&gt;   &lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;53&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;   &lt;span class="mi"&gt;62&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;
    &lt;span class="n"&gt;Device&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;rrqm&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;   &lt;span class="n"&gt;wrqm&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;     &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;     &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;   &lt;span class="n"&gt;rsec&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;   &lt;span class="n"&gt;wsec&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;avgrq&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sz&lt;/span&gt; &lt;span class="n"&gt;avgqu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sz&lt;/span&gt;   &lt;span class="n"&gt;await&lt;/span&gt;  &lt;span class="n"&gt;svctm&lt;/span&gt;  &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;util&lt;/span&gt;
    &lt;span class="n"&gt;xvda&lt;/span&gt;              &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;   &lt;span class="mi"&gt;810&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;  &lt;span class="mi"&gt;202&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt; &lt;span class="mi"&gt;16136&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;68&lt;/span&gt;   &lt;span class="mi"&gt;143&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;  &lt;span class="mi"&gt;719&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;75&lt;/span&gt;   &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;94&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;
    &lt;span class="n"&gt;xvdb&lt;/span&gt;              &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;   &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;   &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;
    &lt;span class="n"&gt;scd0&lt;/span&gt;              &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;   &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;   &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;

    &lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="k"&gt;user&lt;/span&gt;   &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;nice&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="k"&gt;system&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;iowait&lt;/span&gt;  &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;steal&lt;/span&gt;   &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;idle&lt;/span&gt;
               &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;   &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;06&lt;/span&gt;   &lt;span class="mi"&gt;69&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;

    &lt;span class="n"&gt;Device&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;rrqm&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;   &lt;span class="n"&gt;wrqm&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;     &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;     &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;   &lt;span class="n"&gt;rsec&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;   &lt;span class="n"&gt;wsec&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="n"&gt;avgrq&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sz&lt;/span&gt; &lt;span class="n"&gt;avgqu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sz&lt;/span&gt;   &lt;span class="n"&gt;await&lt;/span&gt;  &lt;span class="n"&gt;svctm&lt;/span&gt;  &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;util&lt;/span&gt;
    &lt;span class="n"&gt;xvda&lt;/span&gt;              &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;   &lt;span class="mi"&gt;748&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;  &lt;span class="mi"&gt;198&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt; &lt;span class="mi"&gt;15776&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;79&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;68&lt;/span&gt;   &lt;span class="mi"&gt;143&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;96&lt;/span&gt;  &lt;span class="mi"&gt;746&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;70&lt;/span&gt;   &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;05&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;
    &lt;span class="n"&gt;xvdb&lt;/span&gt;              &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;   &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;   &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;
    &lt;span class="n"&gt;scd0&lt;/span&gt;              &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;     &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;   &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;   &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;上面的数据是正常数据哦亲！&lt;/p&gt;
&lt;p&gt;无奈之下，&lt;a href="http://www.weibo.com/1039929634/ynqS5zk99"&gt;发了条微博&lt;/a&gt;，一看回复，原来大家都有这毛病！&lt;/p&gt;
&lt;p&gt;本来觉得阿里还算是个正儿八经的公司，现在一看，连个盘柜都不舍得买么？！就连我校的好多年前买来的日立光纤存储都能到 400MB/s 好不好（虽然有 RAID）！&lt;/p&gt;
&lt;p&gt;吐槽完毕，反正阿里云和IBM一样，以忽悠为主。&lt;/p&gt;</content><category term="misc"/><category term="tech"/></entry><entry><title>A Tale of Screen RAM</title><link href="https://tifan.net/blog/2011/12/15/a-tale-of-screen-ram/" rel="alternate"/><published>2011-12-15T00:16:14-05:00</published><updated>2011-12-15T00:16:14-05:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2011-12-15:/blog/2011/12/15/a-tale-of-screen-ram/</id><summary type="html">&lt;p&gt;考完托福，从 @kDolphin 那里看到了小米提出的新概念----屏幕 RAM。作为一个对嵌入式一知半解，也玩过单片机的&lt;del&gt;化学&lt;/del&gt;&lt;del&gt;计算机&lt;/del&gt;好人修电脑系学生，我对此概念十分不解----莫非来美国大农村呆了一年就这么赶不上科技的潮流了？  &lt;/p&gt;
&lt;p&gt;于是，根据小米等各种论坛的线索，我大致归纳了"屏幕RAM"的几个特点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这是一种缓存机制，使得 GPU 只需要运算 delta 值。在没有此机制的屏幕上绘图要求 GPU 不断的工作，产生图形&lt;/li&gt;
&lt;li&gt;这种机制可以得到性能的提升，原因是 GPU 不需要 concentrace on 不断的渲染相同的图像&lt;/li&gt;
&lt;li&gt;这种机制可以节约电量&lt;/li&gt;
&lt;li&gt;这种机制主要应用在高端手机上&lt;/li&gt;
&lt;li&gt;这种机制的成本是 80 元人民币，约合 $15&lt;/li&gt;
&lt;/ul&gt;
</summary><content type="html">&lt;p&gt;考完托福，从 @kDolphin 那里看到了小米提出的新概念----屏幕 RAM。作为一个对嵌入式一知半解，也玩过单片机的&lt;del&gt;化学&lt;/del&gt;&lt;del&gt;计算机&lt;/del&gt;好人修电脑系学生，我对此概念十分不解----莫非来美国大农村呆了一年就这么赶不上科技的潮流了？  &lt;/p&gt;
&lt;p&gt;于是，根据小米等各种论坛的线索，我大致归纳了"屏幕RAM"的几个特点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这是一种缓存机制，使得 GPU 只需要运算 delta 值。在没有此机制的屏幕上绘图要求 GPU 不断的工作，产生图形&lt;/li&gt;
&lt;li&gt;这种机制可以得到性能的提升，原因是 GPU 不需要 concentrace on 不断的渲染相同的图像&lt;/li&gt;
&lt;li&gt;这种机制可以节约电量&lt;/li&gt;
&lt;li&gt;这种机制主要应用在高端手机上&lt;/li&gt;
&lt;li&gt;这种机制的成本是 80 元人民币，约合 $15&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;暂且抛开这种机制在一边，我们看一看 LCD 的显示原理。  &lt;/p&gt;
&lt;p&gt;不管是 TTF 还是较早的 STN 屏幕，其都是由一个一个的亚像素（彩色显示器）/栅格（灰度显示器）组成的。给液晶分子加上一个电压，液晶分子将改变其光透射特性，这可能 result in 颜色的改变（对于彩色显示器），或者透光度的改变（对于黑白显示器）。外加电场消失时，透光度恢复。不管是什么技术，他们的基本原理都是这样的。  &lt;/p&gt;
&lt;p&gt;如果有一些嵌入式开发经验，可能将 LCD 看作一组寄存器的概念更能接受----实际上，我们无法对 LCD 直接进行操作，我们操作的对象是 LCD 控制器，控制器控制各个像素/栅格的状态，这也是为了节省宝贵的 IO 端口。可以将 LCD 作为一组寄存器操作的 LCD 面板叫做 "智能 LCD 面板"。这种面板自带的控制器中含有 FrameBuffer (帧缓存) 甚至渲染引擎。我们在嵌入式开发中常用的 1602 屏幕就自带了显示引擎、字库以及帧缓存。而有一些其他的屏幕，例如 S3C2440 开发板中常用的某些 LCD 屏幕就没有自己的帧缓存、显示引擎，这种屏幕叫做"哑面板"。哑面板需要外置的 LCD 控制器，而智能面板不需要额外的 LCD 控制器。  &lt;/p&gt;
&lt;p&gt;帧缓存这个名词听起来有一点点绕口，其实它更通俗的名字叫做"显存"。显示引擎预先渲染一些内容，并将其保存在显存中，如果显存太小，则显示控制器将不断的运行，有可能导致的是因为运算能力较差产生 lag。另一方面，带有 LCD 控制器的面板可以接受高级指令，也不需要外界（这里的系统定义为 LCD）的额外控制（除了时钟源）就可以保持显示内容。显然，这种 LCD 因为内置了显示引擎而造成成本的增加。然而，哑面板虽然成本低，但需要外界不断的刷新内容而保持屏幕的正常显示，这无疑将增加外界的计算压力。  &lt;/p&gt;
&lt;p&gt;这里，作为读者，你可能会自然的想到，这与上面小米的 assertion match exactly. 那么，小米说的是实话么？其实，不尽然。  &lt;/p&gt;
&lt;p&gt;我们都知道，现代计算机中的一个重要组成部分叫做显示适配器，俗称显卡。手机等嵌入式设备也不例外，但是由于其空间受限制，一般更倾向于 SoC 设计。内置 LCD 控制器的智能 LCD 似乎很适合，但是我们知道，2008年之后的手机市场已经开始很荒谬的开始了性能竞争----例如我现在使用的 iPhone 4S 的主频是 800MHz----作为对比，2009 年我购买的 HTC Dream 的主频是 384MHz，2007 年我购买的 Motorola Rokr E6 则是 313MHz 的主频。当然，上面是废话。LCD 控制器的内置"显卡"毫无疑问相比于英伟达等专业厂商的显卡是很弱的。尽管哑面板没有帧缓存，要求不断的刷新屏幕，并使用了更多的总线带宽（显示器的带宽总是很大的），但是----无论如何，这些工作都是需要做的，不管是液晶面板做了，还是面板外进行的。  &lt;/p&gt;
&lt;p&gt;小米手机的 GPU，根据公共可得的数据，是 Adreno 220。我查阅了此款 GPU 的资料，得知这是一款 SoC 的 GPU 方案，内置 SRAM FrameBuffer。当然了，苹果的 A5 处理器一样是 SoC 设计，也内置了 FrameBuffer。另一方面，根据魅族公布的资料，魅族 MX 的 GPU 也是 SoC 实现的 Cortex-A9 + Mali-400MP GPU。这款片上同样集成了一定数量的内存作为帧缓存使用，根据 ARM 的介绍，其最大支持的帧缓存大小为单帧4096x4096。  &lt;/p&gt;
&lt;p&gt;即使说到这里，可能还是有一些读者不理解他们有什么太大的差距，在这里，我直接给出结论以及推导结论的 reasoning 过程：  &lt;/p&gt;
&lt;p&gt;Whereas the goal of displaying graphics should be archived, there must be some particular chipset computing for the LCD display panel&lt;/p&gt;
&lt;p&gt;The computation can either be on the LCD controller with a smart panel or on the host with a dump panel;  &lt;/p&gt;
&lt;p&gt;Both phones have a built-in Graphical Processing Unit which fills the framebuffer, embedded in the chipset;  &lt;/p&gt;
&lt;p&gt;Thus, both phone functions in the exactly the same way with different chipsets.  &lt;/p&gt;
&lt;p&gt;由于填充 framebuffer 的过程必须由内置的 GPU 完成，则显示器将显示来自 framebuffer 的内容。既然两种手机都显示来自 framebuffer 的内容，那么不管他们选择什么样的液晶面板，都会达到一样的效果，那就是帧缓存在液晶屏幕之外。  &lt;/p&gt;
&lt;p&gt;作为初步结论，我认为，屏幕RAM应该是指的液晶面板内的 FrameBuffer。在当前的运行模式下，其存在的意义不大，在硬件抽象层之上，操作系统是不需要关心这种实现的。尽管哑面板有着种种缺点，但因为目前的设计，实际上智能面板的"智能部分"已经由更加智能的智能手机片内的 GPU 所完成。因此，小米抛出的"帧缓存"是比较没有讨论意义的。  &lt;/p&gt;
&lt;p&gt;从另一个方面上看，小米的争论其实也是无效的争论。倘若小米的论据和结论都是有效的，这对于一个正常的企业是不可忍受的----种种迹象表明魅族并不是一个打一枪就走人的公司，其更多的是很负责任的公司，从各种细节就可以看出。我只购买过一个魅族 M6 音乐播放器，非常耐用，而且服务不错。80 元的成本，相比 3000 元的定价来说，如果不是利润已经被压缩到不可以接受的地步，正常的企业是不会为此而生产有严重缺陷的产品的。&lt;/p&gt;</content><category term="misc"/><category term="tech"/></entry><entry><title>刷 SPL 失败的 HTC Dream 怎样恢复加速传感器的功能</title><link href="https://tifan.net/blog/2009/11/07/htc-dream-bricked-spl/" rel="alternate"/><published>2009-11-07T23:42:04+08:00</published><updated>2009-11-07T23:42:04+08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2009-11-07:/blog/2009/11/07/htc-dream-bricked-spl/</id><summary type="html">&lt;p&gt;手贱不可怕，怕的是手贱以后没有办法挽救。&lt;/p&gt;
</summary><content type="html">&lt;p&gt;手贱不可怕，怕的是手贱以后没有办法挽救。&lt;/p&gt;


&lt;p&gt;我在9月底尝试在 HTC Dream 上刷了 Danger SPL。由于没有预先更新 radio 成功，手机启动失败。&lt;/p&gt;
&lt;p&gt;修复的方式是换了一个 NAND 芯片（业内人士成为_字库_），说的是加速度感应器没问题。拿到后确实没问题，但是固件是 HiAPK.com 的固件，我不是太喜欢这个固件，主要是慢，选用了cynaogen mod 4.0.2，感觉比较稳定，以及他的 recovery 1.4。刷了以后，加速度感应失效。打电话给 JS，JS 说是不是刷机了，告诉我需要重新发去"做数据"。说一句题外话，换了 NAND 芯片后，本机的序列号会被 reset。&lt;/p&gt;
&lt;p&gt;我觉得不对头，反正硬件没坏，于是开始去xda-developer翻帖子。&lt;/p&gt;
&lt;p&gt;查资料的结果就是 &lt;code&gt;/system/bin/akmd&lt;/code&gt; 是传感器的守护进程(还有个用户，compass)；&lt;code&gt;/data/misc/akmd\_set.txt&lt;/code&gt; 是 &lt;code&gt;akmd&lt;/code&gt; 的配置文件，可以删除这个文件重新校准。但是我这里并没有这个文件，也许是因为换 NAND 的原因吧。说句题外话，我觉得根本不用换 NAND 的，重新插到编程器上烧录就好了吧。JS确实可以"做数据"。我不甘心花来回40的顺丰快递换一个文件，于是决定自己折腾折腾。&lt;/p&gt;
&lt;p&gt;反正硬件都是一样的，软件问题啦。最先怀疑的就是akmd，看见有些帖子说可以换老版本的akmd，抓紧去找了个 HTC 的 1.1 ROM，提取出来了 &lt;code&gt;akmd&lt;/code&gt;，在console里替换掉，运行 GPS Status 果然可以正确的指南针了。考虑到众多 G1 用户有这样的问题，我把它发来了自己的服务器上。点&lt;a href="http://www.huaxueba.com/alex/akmd.gz"&gt;这个链接&lt;/a&gt;就可以下载了。&lt;/p&gt;
&lt;p&gt;文件信息：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;akmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gz&lt;/span&gt; &lt;span class="mi"&gt;48358&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;

&lt;span class="n"&gt;akmd&lt;/span&gt;
&lt;span class="n"&gt;md5sum&lt;/span&gt;  &lt;span class="mi"&gt;517&lt;/span&gt;&lt;span class="n"&gt;a87a4e6caa5e66f0520c68dcb7c0e&lt;/span&gt;   
&lt;span class="n"&gt;sha1sum&lt;/span&gt; &lt;span class="n"&gt;b741df4aa075115192fe2659c472cd1f6bcd84a0&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;下载回来文件解压，拷贝到sd卡第一个分区。在 &lt;code&gt;console&lt;/code&gt; 下（ Home + Power 开机，用 cm-1.4 的 recovery 选择 Console ）执行下面的命令：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;mount -a&lt;/span&gt;
&lt;span class="err"&gt;cd /system/bin&lt;/span&gt;
&lt;span class="err"&gt;cp ./akmd /sdcard/akmd_old  &lt;/span&gt;
&lt;span class="err"&gt;cp /sdcard/akmd /system/bin  &lt;/span&gt;
&lt;span class="err"&gt;tar cf /sdcard/misc.tar /data/misc/  &lt;/span&gt;
&lt;span class="err"&gt;rm /data/misc/akmd_set.txt  &lt;/span&gt;
&lt;span class="err"&gt;rm /data/misc/rild*  &lt;/span&gt;
&lt;span class="err"&gt;reboot&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;试试你的加速度感应器吧！&lt;/p&gt;
&lt;p&gt;关键字：&lt;/p&gt;
&lt;p&gt;Android Google G1 换字库 重力感应 SPL刷死 akmd&lt;/p&gt;</content><category term="misc"/><category term="tech"/></entry><entry><title>915resolution 的 Solaris 10 port</title><link href="https://tifan.net/blog/2009/03/06/solaris-10-915resolution-port/" rel="alternate"/><published>2009-03-06T21:51:50+08:00</published><updated>2009-03-06T21:51:50+08:00</updated><author><name>Difan Zhang</name></author><id>tag:tifan.net,2009-03-06:/blog/2009/03/06/solaris-10-915resolution-port/</id><summary type="html">&lt;p class="first last"&gt;Hacked 915resolution for Solaris&lt;/p&gt;
</summary><content type="html">&lt;p&gt;(本文为 2009-03-06 21:51:50 +0800 的文章在 2025-08-06 17:00 -0700 的重新发布)&lt;/p&gt;
&lt;p&gt;915resolution 是一个 Linux 下的小工具，用于直接修改 VBIOS 中的模式表，在系统启动后但图形界面加载前动态注入新的分辨率信息。&lt;/p&gt;
&lt;p&gt;使用本 port 可以在 Solaris 上在笔记本电脑上设置正确的分辨率。&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="/images/915resolution_sol_x86.tar.bz2"&gt;点击下载 915resolution Solaris 版本&lt;/a&gt;&lt;/p&gt;
</content><category term="misc"/></entry></feed>