戻る
snom105でsideshowを動かしてみようという試み。
※新しいものは下に追加されます。
Vista SideShowの概要調査 (07-2-21)
- XMLを解析して表示…メニュー、内容、ダイアログ、イベント通知。
- Vista側にはUMDFなドライバ、snom側に通信する。
- ドライバインストールはPnP-X、PnpXはUPnP(SSDP)よりWSDP(WS-D)が推奨。
- sideshowの実現、WPD/MTP(TCPIP)か、独自/SOAPか
WPD/MTPIPの場合:
- snom105←MTPIP←wpdmtpip.dll(wpdmtp.inf)←AuxiliaryDisplayEnhancedDriver.dll(WindowsSidwShowEnhancedDriver.inf)という流れ
- ドライバ作成不要、標準ではUSBに対するinf記述しかないのでinfファイルは必要。
- AuxilaryDisplayEnhancedDriver.dllがひょっとしたらMTPIPに対応してないかも。
- snom側が大変、ただ電話帳転送とか考えるとMTP必要なのかも。
独自/SOAPの場合:
- snom105←soap/http←独自ドライバ(umdf)という流れ。
- WSDPでどうせSOAP必要なんでsnom側はラクかな。
- UMDFで遊んでみたいのでまずは独自/SOAPで実装することにする。
snom105にkernel-2.6移植 (07-3-30)
- serial/cpm…kernel-2.6.21-rc1には実装済み。
- とりあえずカーネル起動
- watchdog:m8xx_wdt.cはまだ移植されてない→timer_interrupt中でadhocにクリア。
- 16MB指定でも動作するようにする→ __initial_memory_limit= 0x00800000;
- ttyCPM0のスピード→dts中のcurrent-speed=<e100> //57600bps
- boot-wrapperを付け加える(dtsもここでセット)…あとまわし。
- root=はmtdでcramfs
- sash,bashの動作確認…どーでもいいがbashの初回ロードに2秒ぐらいかかる、mtdは16bitバスなflashだからねぇ。
- bash以外のダイナミックリンクが刺さる…根が深くて面白そうなので下に項目作成→AT_DCACHEBSIZEをセットしない。
- fs_enetの確認
- MIIバスつながってない、半2重固定→CONFIG_FIXED_PHYはバインド方法不明につきダミーread/write。
- 送信はできるんだが受信ができてない…100%CRCエラー→無視。
- 50%受信不可→dev_alloc_skb()ラップしてRxBDのバッファを16byte界に。
- ttyCPM1が刺さる…cpm_set_brgに-1が渡っている→fsl_soc.cでrx-clockの値を代入。
- RTC確認(内蔵のはクロック周波数の関係で使えない)…CONFIG_GEN_RTCもCONFIG_RTC_CLASSも/dev/rtcも不要でdateコマンドは使えるし進み具合もOK。
hwclockはどうせ使えないしー。
- boot-wrapperの作成
- mtd, erase-size=0x10000。FIS directory はサーチが遅いので採用しない。
- ひとまずカーネル終了。
- 105disp,105keyデバイスドライバ…はSideShow実験の後で
- 105soundデバイスドライバ…はかなりあとまわし。
2.6.21-rc1でpowerpc/8xxなカーネルでダイナミックリンクが動作しない
hello, world!なダイナミックリンクelfバイナリを作ると実行後だんまりになる。
しかしダイナミックリンクでも動作するバイナリもあるし、/lib/tls/ld.so.1を使うと動作したりする。
Stand-alone shell (version 3.7)
> strace.s -ELD_DEBUG=all ./hello
execve("./hello", ["./hello"], [/* 3 vars */]) = 0
...
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
= 0x3001a0000
<<ここで死亡>>
12: relocation processing: /lib/tls/libc.so.6 (lazy)
12: symbol=_res; lookup in file=./hello
12: symbol=_res; lookup in file=/lib/tls/libc.so.6
で、これたぶんTLSメモリの確保の部分、つまりこのあたりで落ちている。
/* Allocate. */
GL(dl_tls_dtv_slotinfo_list) = (struct dtv_slotinfo_list *)
malloc (sizeof (struct dtv_slotinfo_list)
+ nelem * sizeof (struct dtv_slotinfo)); //これがmmapコール
/* No need to check the return value. If memory allocation failed
the program would have been terminated. */
slotinfo = memset (GL(dl_tls_dtv_slotinfo_list)->slotinfo, '\0',
nelem * sizeof (struct dtv_slotinfo));
powerpcはキャッシュラインサイズdcache_bsize
(32bit版ではdtsではなくarch/powerpc/kernel/cputable.c中の固定値)を、
elf起動時のmain(argc,argv,env,aux-vector)の
aux-vectorの中でAT_DCACHEBSIZEとして渡している。
これは正常にdcache_bsize=16になっている。
でglibc4のsysdeps/powerpc/elf/libc-start.cで__cache_line_size大域変数に代入、sysdeps/powerpc/powerpc32/memset.Sでこれを見て高速に動作するようになっている。
で、ld.so自体のmemsetにはこれがうまく効いていないのかと思い、ld.soを変更して調べてみる。
ps3$ apt-get source libc6
ps3$ cd glibc-2.3.6.ds1
ps3$ dpkg-buildpackage -rfakeroot -uc -b …とりあえず再コンパイル。4時間程度かかる。
ps3$ vi build-tree/glibc-2.3.6/elf/rtld.c
ps3$ rm stamp-dir/build_libc
ps3$ debian/rules build_libc …5分ぐらい
ps3$ strip build-tree/powerpc-libc/elf/ld.so
やっぱりmemsetで刺さっているのだが、memset直前に表示させた__cache_line_sizeは16で問題ない。memsetを自前のforループで置き換えても問題ない。
それぞれのアドレスを表示してみる。
malloc(?)…{
mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
= 0x30019000 …最初のmallocでページ確保。
}
…
malloc(8+512)…{
mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
= 0x3001a000 …領域が足りなくなったので次のページ確保。
} = 0x30019f58
memset(0x30019f58+8, 0, 512) …ここで刺さる
これでピンときた。このmemsetはページをまたいでクリアしている。
しかも確保した領域0x3001a000はmmap直後であり、TLBに存在していない。
つまり、memset中で使われている高速化のための特殊な命令がページフォルトした場合、TLBセット後にこの命令を正しく再開できないのではないか?
これで動くバイナリがあったりほかのld.soを使うと動作する理由も説明できる。
ためしにmemset直前に0x3001a000ページに通常アクセスしてTLBをロードしておくとmemsetでこけなくなる。
というわけでmemset.Sを眺めたら、dcbtstとdcbz命令が使われている。
で、2.6.21-rc1のhead_8xx.Sのページフォルトのコメントを意訳すると、
/* 補足: フォルトアドレスはDARレジスタに入っている。でもdcbi命令じゃだめ。
一応カーネル内部ではPAGE_HWWRITEを設定しておけば回避できるけどね。
そんなわけでカーネル空間以外ではdcbi命令は使えないから、残念。 */
ああああやっぱり。
で、memset.Sは__cache_line_sizeが0ならdcbz命令を使わないので、elf起動時にaux-vectorにAT_DCACHEBSIZEをセットしなければいいはず。カーネルのinclude/asm-powerpc/elf.hのARCH_DLINFOを書き換えて対応完了。
__cache_line_sizeが0でもdcbtst命令は使われているなあ、と dcbz dcbi darでググると、
dcbst,dcbz,dcbi,dcbf,dcbt命令が問題らしい。freescaleの公開errataには情報ないぞ。
CONFIG_8xx_DCBxFIXEDというパッチがあるみたいなんで試してみるのはあとまわし。
PnP-Xでネットワークに表示 (07-4-8)
- wsd、ProbeMatch返しても何も来ない…AppSequenceのInstanceIdがInstanceIDだった…orz。
- AppSequenceのSequenceIdは不要。
- PnP-X sampleがダウンロードできるようになった…これデバイス側もvistaじゃないと動かないじゃん。(wsdapi.dll)
- XAddrsはhttpのみ、soap.udpはだめ。5357/tcpでbindすべき?
このあたりのポート番号の使い方はどこにドキュメントあるのじゃ?
- wsdp:ModelNumberやModelUrlは順番に依存。
UMDFでインストール (07-5-6)
《内項目はvs2008に適用。》
- WDK (Windows Driver Kit)をインストール。サブスクライバの6001.18002は腐ってるのでConnectから。
- COMインプロセスだけどDllRegisterServerは呼ばれず、ServiceBinary項目のDllファイルのDllGetClassObject関数がrclsid=DriverCLSID項目で呼ばれる。riidはIClassFactory→IDriverEntry。
- ATLで作成できる。VisualC++2005→新しいプロジェクト→ATLプロジェクト、属性なしDLL《該当なし》で追加のオプションなしで「完了」。ATLライブラリとCランタイムはスタティックリンクに。
- PSプロジェクト(プロキシスタブ)は不要なので削除。
- .defファイルのDllRegisterServer/DllUnregisterServer《/DllInstall》はコメントアウトしておく。
- プロジェクトプロパティ→リンカ→出力の登録はいいえ。
- プロジェクトプロパティ→C/C++→全般→追加のインクルードディレクトリに C:\WinDDK\6000\inc\ddk;C:\WinDDK\6000\inc\wdf\umdf\10を指定。
- プロジェクトプロパティ→MIDL→全般、も同様に。
- stdafx.hのWINVERと_WIN32_WINNT は 0x0600にする《不要》。
- メインの.cpp《dllmain.h》のDECLARE_REGISTRY_APPID_RESOURCEIDをコメントアウト。
- .rgsや.rc中の該当行は削除してもよい。
- .infファイルはプロジェクト直下に作成し、そのプロパティ→カスタムビルドステップで以下を指定する。
- コマンドライン copy /Y $(InputPath) $(IntDir) >NUL
- 説明 infファイルのコピー中です。
- 出力ファイル $(IntDir)\$(InputFileName)
- driverオブジェクトのコードを書く。
- 《重要:Resource.hや.rcを閉じる》
- 追加→クラス…、ATLシンプルオブジェクト、短い名前はdriver
- フリースレッド、アグリゲーションいいえ、カスタムインタフェースで完了
- idlファイルを開いて、
import "wudfddi.idl"; を追加
Idriverインタフェースを削除
coclass driverのIdriver→IDriverEntry
- driver.hのclass定義の、
置換2箇所 Idriver→IDriverEntry
下のpublic:に、以下のメソッド定義を追加
STDMETHOD(OnInitialize)(IWDFDriver* );
STDMETHOD(OnDeviceAdd)(IWDFDriver*, IWDFDeviceInitialize*);
STDMETHOD_(void, OnDeinitialize)(IWDFDriver*);
- あとはdriver.cppで実装
- device、queueオブジェクト等のコードを書く。
これらのオブジェクトはクラスファクトリ経由では作成されない。
- 《重要:Resource.hや.rcを閉じる》
- 追加→クラス…、ATLシンプルオブジェクト、短い名前はdevice等
- フリースレッド、アグリゲーションいいえ、カスタムインタフェースで完了
- idlファイルを開いて、
Ideviceインタフェースおよびcoclass deviceを削除
- device.hのclass定義の、
CComCoClass、Ideviceの継承を削除
BEGIN_COM_MAPからIdeviceを削除
OBJECT_ENTRY_AUTO(__uuidof(device),Cdevice)を削除
- インタフェースの追加…手動で行う。
- device.hのclass定義に、
IPnpCallbackHardwareの継承を追加
BEGIN_COM_MAPにIPnpCallbackHardwareを追加
下のpublic:にメソッド定義を手動で追加
STDMETHOD(OnPrepareHardware)(IWDFDevice*); …
- あとはdevice.cppで実装
デバッグ出力をファイルに書き出していた(mailslotもnamedpipeもumdfからはセキュリティの関係上か動かない)のだけど、
socket-udpで他のマシンに投げるようにした。
wudfhostにデバッガをアタッチすればDebugOutputStringで出せるけど面倒すぎるしね。
SideShowへのコマンドはOpen→Ioctl→Close、のセットで連続同時にやってくる。IQueueCallbackCreateやIFileCallbackCleanupは実装不要。
Windows 2008で動作させる場合は、PnP-X IP bus enumeratorサービスを自動起動にすることと、ワイヤレスLAN機能をサーバマネージャから追加すること。
WSDデバイスへの通信 (07-5-18)
クライアントからは以下のように通信する。これはUMDF中のdllに限らず、一般アプリケーションからでも同様になる。
- WSDCreateDeviceProxy("urn:uuid:DEVICE-GUID", "urn:uuid:GENERATED-MY-GUID", コンテキスト, &host);
この関数実行時点でsoap.udp3702/Resolveが投げられ、
urn:uuid:DEVICE-GUIDが
<wsa:EndpointReference><wsa:Address>
と一致するホストが取得される。大文字小文字は区別される。
さらにResolveMatchしたアドレス(通常はsoap.http5357)にGetが投げられ
(この時のwsa:fromはurn:uuid:GENERATED-MY-GUID)、
デバイスに関するメタデータが取得される。
なんでResolveしているのかっつーと、ネットワークデバイスはDHCPでアドレス変わりうるから。ちなみに物理アドレスURLを渡すこともできる。
- host->GetServiceProxyById("uri:SERVICE-NAME", &service);
uri:SERVICE-NAMEがメタデータの
<wsdp:Hosted><wsdp:ServiceId>
と一致するサービスが選択される。大文字小文字は区別される。
- service->GetEndpointProxy(&endp);
サービスのエンドポイントが
<wsdp:Hosted><wsa:EndpointReference><wsa:Address>
から取得される。これはsoap.http5357でもかまわない。
- endp->SendTwoWayRequest(....);
この関数で実際にsoap.httpが実行される。Body構造をOpFormatInt32等で指定するのが手で書くと面倒だけど。
で、UMDF dll中から使うためには、インストールされたデバイスに対応したDEVICE-GUIDを取得する必要がある。この方法が不明。
一応、正しそうな方法は以下のようだと思われる。
- device->RetrieveDeviceInstanceId(...)でDEVICE-INSTANCE-ID取得
これは実際には
URN:UUID:DEVICE-GUID\UMB\3&320DABCD&6&URN:UUID:DEVICE-GUID/URI:SERVICE-NAME
のような、DEVICE-GUIDとSERVICE-NAMEを組み合わせたものを大文字にしたものになっている。
- CoCreateInstance(__uuidof(FunctionDiscovery), 0, CLSCTX_INPROC_SERVER, __uuidof(IFunctionDiscovery), &disc);
- disc->CreateInstanceCollectionQuery(FCTN_CATEGORY_PNP, 0, 0, 0, 0, &query);
- query->AddQueryConstraint(FD_QUERYCONSTRAINT_PROVIDERINSTANCEID,DEVICE-INSTANCE-ID);
- query->Execute(&collect);
- collect->Item(0, &inst); まででIFunctionInstance取得。
- inst->OpenPropertyStore(0, &prop);
- prop->GetValue(PKEY_PNPX_GlobalIdentity, &DEVICE-GUID); でDEVICE-GUID取得。
しかし、この方法をOnDeviceAddやOnPrepareHardwareから実行するとOpenPropertyStoreがブロックされエラーになる。
デバイス中ですでにPropertyStoreを開いているためだと思われる。
device->RetrieveDevicePropertyStoreで代替できそうだけど
第一引数に渡す名前が不明。
UMBでもdevice->GetDefaultIoTargetとかでIWDFUsbTargetDeviceインタフェースみたいなのが取得できればラクなんだけど。
device->RetrieveDeviceInstanceIdで取得したDEVICE-INSTANCE-IDからDEVICE-GUID部分を取り出して小文字に変えてWSDCreateDeviceProxyに渡すか、
HKLM\SYSEM\CurrentControlSet\Enum\DEVICE-INSTANCE-ID開いてLocationInformationキーの値に直接通信するしかないのか。
とサポートフォーラムで尋ねたら以下の方法で解決。ちなみにSetupDiGetDevicePropertiesを用いるとやっぱりブロックされる。
- dev= SetupDiGetClassDevs(&GUID_DEVCLASS_SIDESHOW, 0, 0, DIGCF_PRESENT);
- SetupDiOpenDeviceInfo(dev, DEVICE-INSTANCE-ID, 0, 0, &info);
- SetupDiGetDeviceProperty(dev, &info, &PKEY_PNPX_GlobalIdentity, &prop, &DEVICE-GUID, ...);
.NETリモーティングとWCF (07-6-3)
しかしSOAPでのイベント受信の方法が不明。IWSDServiceProxy::SubscribeToOperationはさっぱり使い方がわからん。
wsdcodegenがLonghorn preview SDKでやっと公開された…wsdlファイルが必要…ASP.NET webサービスで適当にこさえる…ASP.NET webサービスってイベント定義できない…WCFでできるかなー…WCFで吐くwsdlって使えねー…こうしてインプリメントが進まないのであった。
でもちょっとWCFさわったのでせっかくだから.NET Remotingとの対比メモを残すぜ。
私の好きな.NET remotingではクライアント側で定義ファイル(app.config等)で指定されたオブジェクトが自動的にサーバ側で作られる。
作った後は通常のオブジェクトとまったく同じ取り扱いが可能。
サーバ側からクライアント側の関数を呼び返したい場合は、
サーバオブジェクトにイベント定義してもいいし、
public sub server_myfunc(byval cli as object)
cli.callclientcallback("I'm callbacking")
end sub
程度で自由に呼び返せたりする。VisualBasic万歳。
しかし.NET remotingでは、クライアント側で呼び出すサーバオブジェクトの実装が必要となる。定義ではない。
つまり上記のようにコールバックする場合には、サーバ側のほうでもcliオブジェクトの実装が必要。
イベント定義にした場合でもAddressofに渡すオブジェクトの実装がサーバ側に必要。
soapsudsなどを使ってカラの実装を作ることもできるが面倒。で、インタフェース経由で呼び出すことにより実装の共有を避けることができる。
public sub server_myfunc(byval cli as Iclientcallback)
implements Iserver.server_myfunc
cli.callclientcallback("I'm callbacking")
end sub
インタフェースの共有(公開)は必要ですが大したことじゃありません。
で、WCF。インタフェースをほぼ必ず使う.NET remoting、だったりする。
違う点は以下の通り…WCF、面倒なだけで利点ないじゃん。
.NET remotingを使い続けよっと。
- 直接Newでリモートオブジェクトが生成されず、クライアント側でClientBase(Of Interface)を継承した自前のラッパクラス(svcutil.exeで生成)を使うのが前提になった。正直面倒くさい。
- 引数や戻り値に相手方のオブジェクトやインタフェースは渡せず、基本型かData Contractでマークしてある構造体のみになった。
- のでサーバ側でDataset.xsdで作った派生DataTableをそのままクライアントに返す、みたいな芸当が.NET remotingではDataset.xsdを共有するだけでできたのだがWCFではxsdの生成コードにData Contractをつけて回らないとならなくなった=事実上できなくなった。
- .NET remotingではhttpサーバポートは指定されたポートを.NET自身が直接listenしていたが、WCFでは別の機構がlistenして、アクセスurlによって振り分けるようになった。ASP.NET webサービスとの統合目的ですな。
- この別の機構、が、netsh http show urlaclで表示される。サーバ側でadd urlaclで事前登録必要。
- コールバックはインタフェースが渡せなくなったので、Duplex contractという形の定義が必要。
コールバックなのでクライアント側にurlacl登録が必要だがhttp://+:80/Temporary_Listen_Address/GUID、という形で登録済み。
- メタ情報は/?wsdlも吐くけど機械判読形式では/mex配下でサポートされ、svcutil.exeを用いてインタフェース定義およびラッパクラス.vbを生成する。
- 標準ではWindows認証なのでm_WSHttpBinding.Security.Mode = SecurityMode.None とか両側で実施。
…自前でwse:EventSource="true"なwsdl書く…あっさりwsdcodegenがコード吐いてくれた。
コールバックはhttp://*:5357/GUID、がendpoint。
http202は返すけど、ReferenceParametersの項目(wse:Identifier)とsoap:Bodyの順番があってなかったり省略したりするとRequestStubFunctionを呼び返してくれない。
IUnknown*はAddRef/Releaseだけ呼ばれる。
で、コールバック後腐るので強引に__stdcallにしたらやっと動いた。
net.tcpフォーマット (09-12-9)
さらに余談。WCFにnettcpBindingがあって、バイナリフォーマットで効率がいいらしい。
実態はsoap(wshttp)のxml(文字列ベース)をバイナリ化したもので、
XmlDictionaryWriter.CreateBinaryWriterの拡張。
まずTOKENの表があり、上から順に01、03、05、07…と番号が振られる。
これはtcpコネクション内で再利用され、
同じ関数を呼び出した場合TOKEN表の長さは基本的には0になる。
06 ws-soapであることを示す
cc 01 全体の長さ
84 01 TOKEN表の長さ
1c "http://tempuri.org/ICalc/Add"
48 "net.tcp://192.168.0.1:999/ServiceModelSamples/Services/CalculatorService"
03 "Add"
13 "http://tempuri.org/"
02 "n1"
02 "n2"
長さは128以上は 80+下位7bit 上位8bit で表す。
表にない一般的なTOKENには偶数の値が既定で振られている。
それを利用して以下のようにwsなsoapをエンコードしている。
56 02 <s:Envelope
0b 01 73 04 xmlns:s="http://www.w3.org/2003/05/soap-envelope"
0b 01 61 06 xmlns:a="http://www.w3.org/2005/08/addressing">
56 08 <s:Header>
44 0a <a:Action
1e 00 82 s:mustUnderstand="1">
ab 01 http://tempuri.org/ICalc/Add </>
44 1a <a:MessageID>
ad 12 34...xx urn:uuid:1234-xxxxx </>
44 2c <a:ReplyTo>
44 2a <a:Address>
ab 14 http://www.w3.org/2005/08/addressing/anonymous </>
01 </a:ReplyTo>
44 0c <a:To
1e 00 82 s:mustUnderstand="1">
ab 03 net.tcp://192.168.0.1:999/ServiceModelSamples... </>
01 </s:Header>
56 0e <s:Body>
42 05 <Add
0a 07 xmlns="http://tempuri.org/">
42 09 <n1>
89 0a 10</>
42 0b <n2>
89 14 20</>
01 </Add>
01 </s:Body>
01 </s:Envelope>
ノードは44でa:、45でb:、56でs:、42で接頭辞なし、のようになっていてTOKENが続く。
アトリビュートは0b 数 "?"でxmlns:?=、0aでxmlns=、0c TOKENでa:XX=、0d TOKENでb:XX=、1e TOKENでs:XX=、のようになる。
データは81で0、83で1、85でFalse、87でTrue、
89 XXで-128〜127、8b LL HHでShort、8d X4でInteger、8f X8でLong、
91 X4でSingle、93 X8でDouble、
99 数 "xxx"で255文字まで、9b LL HH "xxx"で65535文字まで、9d X4 "xxx"で65536文字以上、
ab TOKENでトークン文字列のようになっている。
最下位ビットはそのノードが閉じるときは1、閉じないときは0であり、空文字列や空配列はデータをおかずにそのまま01(閉じる)を使用する。
設計と実装(07-7-7)
さて、と。実験はだいたい終了。設計と実装にうつるべ。
- 画面ライブラリ、XもQt/embeddedも大きすぎるので110gcをそのまま移植中…
x86版は終了…powerpc版も終了、ほしい人…いないよね。
- PS3にsnomのふりさせて実装してるんだが、ipv6使うとPS3のeth0が飛ぶ。
パッチがあるみたいだけどカーネル再コンパイル面倒いのでUSB etherつなげて誤魔化し。
add-onも07-5-16に更新されているし、入れなおさないとなー。
- umdfドライバからのWSDCreateDeviceProxyが刺さるようになる。通常のアプリからだと問題なし。
以前動いていたんだけど権限がらみを実験しているうちにどっかおかしくなったか、とあちこちこねくりまわすもわからず。
アンチウィルスを無効にしてもだめ、アンインストールしたら…動いた。
NOD32は比較的悪さをしないんだけど、こういう脂っこいところではインストールしちゃやっぱ駄目だ駄目だ駄目だ。
でNOD32 V3が出たので試したら大丈夫だった。
デバイスドライバをWS-Dから導入したらSideshowコンパネに出てこない。
「ハードウェアの追加」からなら大丈夫なんだけど。
権限がらみなのか、下位(UMBus)が悪さしてるのか…CoInstallerで別インストールすればいいのはわかるがそんなの書きたくないしのう。
…とフォーラムで尋ねてほっといたのだがVista SP1のWindowsUpdate(08-5-16)で1年越しに解決。SP1プレビューでもSP1手動当てでも解決しなかったのに謎。
残る問題点。
PnPXでインストールして、機器の電源切って再起動やスリープ解除するとドライバがアンインストールされるのはいいんだが、その後機器の電源入れても復旧しないことかな。
その状態で再起動や再スリープ解除すると自動的に再インストールしてくれるんだが。
Helloを機器側から投げても駄目。定期的にProbe投げて見つかったら再インストールしてくれ。