グレースケール印字フルワイド化・高速化 Hack案(カーネル非改造)¶
0x67(16階調グレースケール)印字モードには「印字幅が240pxに制限される」「階調段数が16固定」という制約があるが、これをカーネル改造・署名鍵の入手・カスタムビルドなしに、ユーザーランドからGPIO/SPIを直接叩いて自前実装することで回避できる可能性がある、という改造案をまとめる。
実機検証済み: この端末では§4.2の前提が成立しません
2026-09-15、実機(spi7.0, カーネル Linux 4.9.193)で検証した結果、driver_overrideファイル自体が
存在せず、§4.2の「spidev強制bind」は不可能と判明しました。加えてunbind/bindを短時間に
繰り返すとGPIOが-EBUSYのまま解放されず/dev/prn-devが消失し、再起動でしか復旧できない
事象も発生しています。詳細と代替案(DTBOオーバーレイでの多重compatible化とそのリスク)は
kernel_analysis.md §7 を参照してください。本ページの§1〜§4は
「当初の設計メモ」として、§4.4のGPIOシーケンス/タイミング定数(実装コードにも反映済み)は
参考価値がありますが、§4.1〜4.3の手順そのものはこのままでは実行できません。
1. 背景 — 何が制約になっているか¶
カーネル内蔵の uyu_printer ドライバ(/dev/prn-dev)の ioctl(0x67) 経路(グレースケール印字)は、以下がバイナリにハードコードされている:
| 制約 | 内容 |
|---|---|
| 印字幅 | 240px固定(ヘッド物理幅384pxのうち中央240pxのみ。左右72pxずつ常時消灯) |
| 階調段数 | 16段(15パス)固定(呼び出し元がarg1=0x10を決め打ちで渡している) |
| ペイロード長 | ioctlハンドラがmemcpyのコピー量を0x78(120バイト)に固定している |
これらは uyu_printer.c 側(カーネル内、r2ghidraで逆コンパイル済み)の複数箇所に散らばった定数で、1関数だけパッチしても直らない。詳細な解析結果は逆コンパイル調査ブランチ(kernel_analysis.md / uyu_printer_decompiled.c、別ブランチで作業中・未マージ)を参照。
素直に直すには「カーネルソースを入手してビルドし直す」か「同等機能を持つカーネルモジュールを自作する」必要があるが、いずれも以下のハードルがある:
- OEM(MeiG)固有のカーネルソースは現時点で未入手(GPL開示請求中)
- カーネルモジュール署名(
CONFIG_MODULE_SIG_FORCE=y)が有効。デフォルト鍵("Build time autogenerated kernel key")のままではあるが、秘密鍵の現物は未入手 CONFIG_KPROBESが無効化されており、実行時の関数フックも不可
2. 発想の転換 — カーネルドライバを経由しない¶
サーマルヘッドの制御に使われているのは、突き詰めると GPIO 8本 + SPI 1本 だけである(詳細はプリンター制御仕様・逆コンパイル解析を参照)。
| 信号 | 役割 |
|---|---|
PRN_STB |
ストローブ(発熱パルス)。SPI転送直後にHigh→ウェイト→Lowで1パス分の加熱 |
PRN_MT_A / PRN_MT_B / PRN_MT_A_0 / PRN_MT_B_0 |
ステッピングモーター4相(紙送り) |
Heat_En / Print_En |
発熱・印字系の元電源スイッチ |
P_Paper |
用紙検知 |
| SPI(384bit/48バイト) | 1ライン分のドットパターン転送 |
これらは全て Linuxの汎用サブシステム(GPIOLIB, SPIコア)で標準的に制御できる種類のI/Oであり、uyu_printerという専用ドライバでなければ制御できないわけではない。既存ドライバがこれらのGPIO/SPIをgpio_request/spi_setupで専有しているのが唯一の障壁であり、そこを解放してやればユーザーランドから直接叩ける。
3. 前提条件(確認済み)¶
この端末で本Hackが成立する根拠となる設定値は以下の通り確認済み。
3.1 カーネルコンフィグ(kernel-conf.txt)¶
CONFIG_SPI=y
CONFIG_SPI_QUP=y
CONFIG_SPI_SPIDEV=y # 汎用SPIユーザーランドドライバがビルドイン済み
CONFIG_GPIOLIB=y
CONFIG_GPIO_SYSFS=y # sysfs経由のユーザーランドGPIO制御が有効
spidev・GPIO sysfsインターフェースは追加のカーネルビルド無しで既に使える状態にある。
3.2 ソフトウェア的な制約の緩さ¶
- SELinux:
Permissive(ポリシー違反はログのみ、ブロックされない) - dm-verity:
Disabled - ブートローダー: アンロック済み(
verifiedbootstate: orange) fastboot getvar all:unlocked:yes/secure:no(Qualcomm Secure Bootも無効)- root:
suバイナリ経由で確保可能(プリンター制御仕様参照)
この組み合わせにより、rootさえ取れれば sysfs 操作は無制限に行える(SELinuxに止められない)。
4. 手順¶
すべて実機上のシェル操作のみで完結し、リブート・カーネル再ビルド・DTB書き換えは不要。
4.1 既存ドライバから GPIO / SPI の専有を解放¶
# uyu_prn デバイスを platform driver から unbind
# (デバイス名・ドライバ名は /sys/bus/platform/drivers/ 配下で要確認)
echo <uyu-prnのデバイス名> > /sys/bus/platform/drivers/<uyu_prnドライバ名>/unbind
これで uyu_prn_remove() が呼ばれ、GPIO 8本・SPIデバイス・/dev/prn-dev (misc device) が全て解放される。この間、既存の1bit印字パス(0x66)も使えなくなる点に注意。
4.2 SPIデバイスを spidev に driver_override で強制バインド¶
# SPIデバイスノードを特定(例: spiX.Y)
ls /sys/bus/spi/devices/
# devicetreeのcompatible文字列("qcom,prn-dev")を無視して
# driver_override で spidev を強制指定
echo spidev > /sys/bus/spi/devices/spiX.Y/driver_override
echo spiX.Y > /sys/bus/spi/drivers/spidev/bind
成功すると /dev/spidevX.Y が生成され、ioctl(SPI_IOC_MESSAGE) で生のSPI転送がユーザーランドから可能になる。driver_override はLinux標準の仕組みで、devicetreeのcompatible一致チェックをバイパスして任意のドライバに強制bindできる。
4.3 GPIO 8本を sysfs 経由で制御¶
echo <N> > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio<N>/direction
echo 1 > /sys/class/gpio/gpio<N>/value
対象GPIO番号は devicetree(uyu-prnノード)から確認済み。
4.4 印字プロトコルをユーザーランドで再実装¶
逆コンパイル解析で判明している LineColorRank_Print のロジック(疑似コード、フル幅・可変段数版に拡張):
// N = 任意の階調段数(元は16固定)、WIDTH = 384(元は240固定)
for (pass = 1; pass < N; pass++) {
// WIDTHドットぶん、「そのドットの階調値 >= pass」でビットを立てる
for (byte_i = 0; byte_i < WIDTH/8; byte_i++) {
packed[byte_i] = pack_8_bits(dot => (pass <= gray_data[dot]));
}
spi_transfer(fd_spidev, packed, WIDTH/8); // ioctl(SPI_IOC_MESSAGE)
gpio_write(PRN_STB, 1);
usleep(strobe_us); // 元はTK/TB(144/6us)相当
gpio_write(PRN_STB, 0);
}
motor_forward_one_step(); // GPIO 4相を自前でシーケンス制御
段数Nとストローブ幅strobe_usを自由なパラメータにできるので、階調数を落として高速化する運用も、そのままここに組み込める。
5. リスク・未検証事項¶
5.1 タイミング精度¶
カーネル内の __udelay() は割込み無効のビジーウェイトで確実に指定時間止まるが、ユーザーランドの usleep() はスケジューラの影響を受け、数十µs単位でブレる可能性がある。ストローブ幅(144〜150µs相当)に対してどの程度のジッタが許容されるかは実機で試すまで不明。
対策候補:
- プロセスを SCHED_FIFO のリアルタイム優先度にする(chrt -f 99 ...)
- mlockall() でページフォルト由来の遅延を排除
- それでも精度が足りなければ、ストローブのHigh/Low自体をSPI転送に相乗りさせる(SPIクロックは正確なので、ダミーバイトの転送時間でウェイトを代用する)等の工夫が要る
5.2 既存機能との排他¶
uyu_prnをunbindしている間は /dev/prn-dev 経由の通常の1bit印字(0x66)・用紙検知(0x77)が使えなくなる。運用上は「グレースケール印字ジョブの間だけunbind→終わったらbindし直して元のドライバに戻す」という排他制御が必要。
5.3 モーター位相のずれ¶
Motor_Cycle(ステッピングモーターの半歩位相トグル)は元ドライバ内のstatic変数で管理されている。unbind/自前実装/再bindを繰り返すと、機械的な位相が元ドライバの想定とずれる可能性がある。実害は「多少のガタつき」程度と思われるが未検証。
5.4 未確認事項 → 実機確認済み(結果は§6参照)¶
- ~~
uyu-prnデバイスの正確なplatform driver名・sysfsパス~~ → 前提が誤り。prn-devは platform driverではなくSPIドライバとしてspi7.0に直接bindされている (readlink /sys/bus/spi/devices/spi7.0/driver→.../bus/spi/drivers/prn-dev)。 - ~~SPIデバイスのバス番号・チップセレクト~~ →
spi7.0(/dev/spidev7.0)と確認済み。 - ~~
spidevのof_match_tableに許可されたcompatible文字列がdriver_override経由でも通るか~~ → そもそもdriver_overrideファイルがこの端末のカーネルに存在しないため検証不能。詳細は§6。
6. 実機検証結果(2026-09-15)と代替案¶
6.1 §4.2は成立しない¶
/sys/bus/spi/devices/spi7.0/driver_override が存在しない(SPIバスのdriver_override対応は
mainlineで4.9より新しいカーネルの機能で、この端末(Linux 4.9.193)にはバックポートされていない)。
driver_override無しでecho spi7.0 > /sys/bus/spi/drivers/spidev/bindを直接試しても、
compatible = "qcom,prn-dev"がspidevのof_device_id/spi_device_idテーブル
(rohm,dh2228fv等の標準プレースホルダーのみ)にマッチせず、probe()が呼ばれないまま失敗する。
6.2 unbind/bindの反復自体が危険¶
unbind→(spidev bind失敗)→再度unbind→prn-devへbind、を短時間に繰り返したところ、
2回目の再bindでgpio_initがHeat_En/Print_Enのgpio_requestで-EBUSY(-16)を返し、
/dev/prn-devが消失。sysfs操作だけでは復旧できず、実機の再起動が必要だった。
PrinterServerAppがステータスポーリング等で/dev/prn-devを掴んだままの状態でunbindすると
GPIO解放処理が正しく完了しない可能性がある(未検証の推測)。テストする際は必ず
am force-stop com.example.printerserverしてから行うこと。
6.3 代替案: DTBOオーバーレイでの多重compatible化(未検証・要実機テスト)¶
boot.imgのベースDTを触らず、dtbo.imgに新しいfragmentを追加して
target-path = "/soc/spi@7af7000/uyu-prn";で直接ターゲット指定し、compatibleに
元の文字列を残したまま追加する:
こうすればunbind後にspidevへのbindが(標準のcompatibleマッチで)成功するはずだが、
この変更は「起動時に毎回評価される」静的なものなので、通常起動時にprn-devとspidevの
どちらが自動bindを勝ち取るかが問題になる。両ドライバともdevice_initcall(レベル6)で
登録されるため、勝敗はdrivers/uyu/とdrivers/spi/spidev.cのカーネルリンク順次第で決まり、
机上では確定できない。もしspidevが勝つと、通常の1bit印字(0x66)が起動するたびに
使えなくなる。実機でdtboを焼いて再起動し、readlink /sys/bus/spi/devices/spi7.0/driverで
どちらが勝つか確認するまでは採用の可否が判断できない。
DTBO抽出・書き換えの一般的な手順(パーティション位置の特定、ddによるフル解凍なしの
部分抽出など)は パーティションテーブル解析 を参照。
7. 関連ページ¶
- プリンター制御仕様 — GPIO/SPIの役割、ioctlオペコード一覧
- カーネル
uyu_printer.c逆コンパイル解析 — GPIOシーケンス・タイミング定数の一次情報源、§7に本Hackの実機検証結果 - 印字ノウハウ・ハマりどころ — ディザリング等、既存の実用ノウハウ
- 端末のセキュリティ機能の調査結果 — 本Hackの前提となるセキュリティ状態
- パーティションテーブル解析 — DTBO抽出・書き換えの参考情報
- PrinterServerApp 開発記録 — 現行の印刷実装