2020/03/07

[golang][gousb]デバイスの認識

では、gousbを使ってみよう。

 

exampleを使う。

https://github.com/google/gousb/blob/master/example_test.go

まあ、まずはデバイスを認識できるかどうかからだ。
libusb-1.0がいるから、事前にaptなりなんなりでインストールしておく。

01: // sudo apt install libusb-1.0
02: 
03: package main
04: 
05: import (
06:     "fmt"
07:     "log"
08: 
09:     "github.com/google/gousb"
10: )
11: 
12: const (
13:     VID = 0x054c
14:     PID = 0x02e1
15: )
16: 
17: // https://github.com/google/gousb/blob/master/example_test.go
18: func exampleSimple() {
19:     // Initialize a new Context.
20:     ctx := gousb.NewContext()
21:     defer ctx.Close()
22: 
23:     // Open any device with a given VID/PID using a convenience function.
24:     dev, err := ctx.OpenDeviceWithVIDPID(VID, PID)
25:     if err != nil {
26:         log.Fatalf("Could not open a device: %v", err)
27:     }
28:     defer dev.Close()
29: 
30:     // Claim the default interface using a convenience function.
31:     // The default interface is always #0 alt #0 in the currently active
32:     // config.
33:     intf, done, err := dev.DefaultInterface()
34:     if err != nil {
35:         log.Fatalf("%s.DefaultInterface(): %v", dev, err)
36:     }
37:     defer done()
38: 
39:     // Open an OUT endpoint.
40:     ep, err := intf.OutEndpoint(7)
41:     if err != nil {
42:         log.Fatalf("%s.OutEndpoint(7): %v", intf, err)
43:     }
44: 
45:     // Generate some data to write.
46:     data := make([]byte, 5)
47:     for i := range data {
48:         data[i] = byte(i)
49:     }
50: 
51:     // Write data to the USB device.
52:     numBytes, err := ep.Write(data)
53:     if numBytes != 5 {
54:         log.Fatalf("%s.Write([5]): only %d bytes written, returned error is %v", ep, numBytes, err)
55:     }
56:     fmt.Println("5 bytes successfully sent to the endpoint")
57: }
58: 
59: func main() {
60:     exampleSimple()
61: }
  

$ go run .
2020/03/07 11:04:01 Could not open a device: libusb: bad access [code -3]
exit status 1

まあ、いきなりは動かんか。

 

デバイスを使う場合、sudoとかしないといけなさそうな気がするのだが、lsusbなんかはsudoしなくても使える。
wiresharkなんかはグループを作っておけばsudoせずにいけたのだが、libusbはどうだったか。

L.24のctx.OpenDeviceWithVIDPID(VID, PID) で起きている。
C言語で書くとこんな感じだった。

https://github.com/hirokuma/libhknfcrw_c/blob/master/examples/cyg_devaccess_pasori.c#L109

sudoしてみる。

$ sudo ./go_pasori1
2020/03/07 11:11:44 vid=054c,pid=02e1,bus=2,addr=5.DefaultInterface(): failed to select interface #0 alternate setting 0 of config 1 of device vid=054c,pid=02e1,bus=2,addr=5: failed to claim interface 0 on vid=054c,pid=02e1,bus=2,addr=5,config=1: libusb: device or resource busy [code -6]

これはL.35のメッセージだから、権限の問題ということか。

udevで設定を書くことで回避できるようだが、しばらくはsudoでよかろう。


では、次はL.35のエラーについてだ。
Busyっぽいのだが、私はRC-S370をVirtualBoxのデバイス指定で使えるようにしただけだ。

dmesgを見てみる。

[15145.225082] usb 2-2: new full-speed USB device number 6 using ohci-pci
[15146.112505] usb 2-2: New USB device found, idVendor=054c, idProduct=02e1
[15146.112507] usb 2-2: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[15146.112509] usb 2-2: Product: RC-S370/P
[15146.112511] usb 2-2: Manufacturer: Sony
[15146.124738] usb 2-2: NFC: NXP PN533 firmware ver 1.48 now attached

OSがRC-S370をデバイスとして認めてしまっている・・・?
そういえば、いつからかLinuxはNFCデバイス(たぶんReader/Writerだろう)を識別するようになったという記事を見かけた気がする。

RC-S380(Pasori)とLinux NFC Subsystem | 何かできる気がする
https://dekirukigasuru.com/blog/2019/01/05/linux-nfc-subsystem/

Linux NFC Subsystem、ね。
できれば、今回は新しいことは試さず、USBとして動かしたいのだ。

 

そういえばC言語で書いていたときも、default interfaceみたいなものは使えなかった気がする。

https://github.com/hirokuma/libhknfcrw_c/blob/master/examples/cyg_devaccess_pasori.c#L133-L156

おそらく、この辺りはend pointを探しているところだろう。

なので、便利関数を使うサンプルではなく、その下にある方のサンプルを使おう。

https://github.com/google/gousb/blob/master/example_test.go#L76

ここから下を貼り付けただけなので、コードは省略だ。

2020/03/07 11:47:56 vid=054c,pid=02e1,bus=2,addr=6.Config(2): device vid=054c,pid=02e1,bus=2,addr=6: configuration id 2 not found in the descriptor of the device. Available config ids: [1]

"Config(2)"なので、ここのエラーか。

https://github.com/google/gousb/blob/master/example_test.go#L105

そういえば、なんとなくコードを貼り付けて動かしているのだが・・・この2とか3とかってなんなのだ??
エラーログの一番最後に"[1]"となっているから、2じゃなくて1ならconfigがあるということか。

・・・1にすると進んだ。。。
まあいい、最後まで通してから考えよう。

 

次のエラーはこれ。

2020/03/07 11:53:06 vid=054c,pid=02e1,bus=2,addr=6,config=1.Interface(3, 0): descriptor of interface (3, 0) in vid=054c,pid=02e1,bus=2,addr=6,config=1: interface 3 not found, available interfaces 0..0

3じゃなくて0ってことか。
が、0にしたらしたで別のエラーになった。

2020/03/07 11:54:51 vid=054c,pid=02e1,bus=2,addr=6,config=1.Interface(3, 0): failed to claim interface 0 on vid=054c,pid=02e1,bus=2,addr=6,config=1: libusb: device or resource busy [code -6]

ダメだ、このやり方じゃダメだ。。。


C言語で書いていたときのやり方がいいだろう。
調べられるリストを作って、先頭から順に調べて使えるものを探す、と。

改めて、openする流れをおさらいしよう。

https://github.com/hirokuma/libhknfcrw_c/blob/master/examples/cyg_devaccess_pasori.c#L47

  1. libusb_open_device_with_vid_pid()でハンドル取得
  2. libusb_get_device()でデバイス取得
  3. libusb_get_config_descriptor()でコンフィグ取得
  4. コンフィグからinterfaceの数だけEnd Pointを探す?
  5. libusb_claim_interface()でclaim

自分で書いておきながら理解していないが、当時もlibusbのサンプルから持ってきたやり方を使っただけだろう。
コンフィグ中をぐるぐる探すところも、本当はbreakとかして抜けるべき何じゃなかろうかね。。。

01: // sudo apt install libusb-1.0
02: 
03: package main
04: 
05: import (
06:     "fmt"
07:     "log"
08: 
09:     "github.com/google/gousb"
10: )
11: 
12: const (
13:     VID = 0x054c
14:     PID = 0x02e1
15: )
16: 
17: // https://github.com/google/gousb/blob/master/example_test.go
18: func openUsb() {
19:     // Initialize a new Context.
20:     ctx := gousb.NewContext()
21:     defer ctx.Close()
22: 
23:     // Open any device with a given VID/PID using a convenience function.
24:     dev, err := ctx.OpenDeviceWithVIDPID(VID, PID)
25:     if err != nil {
26:         log.Fatalf("Could not open a device: %v", err)
27:     }
28:     defer dev.Close()
29: 
30:     for _, cfg := range dev.Desc.Configs {
31:         for _, inf := range cfg.Interfaces {
32:             for _, alt := range inf.AltSettings {
33:                 for _, endPnt := range alt.Endpoints {
34:                     fmt.Printf("endpoint=%v\n", endPnt)
35:                 }
36:             }
37:         }
38:     }
39: }
40: 
41: func main() {
42:     openUsb()
43: }
  

$ sudo ./go_pasori1
endpoint=ep #4 OUT (address 0x04) bulk [64 bytes]
endpoint=ep #4 IN (address 0x84) bulk [64 bytes]

 

最初、for文でinterface数なんかをがんばって取得してぐるぐる回そうとしていたのだが、lsusbサンプルを見てrangeというものがあったことを思い出す。


おそらくこれでUSBデバイスはオープンできたと思う。

だが、C言語でlibusbを使ったときは最後にlibusb_claim_interface()を呼んでいた。
これは何をする関数なのだろうか?

http://libusb.sourceforge.net/api-1.0/group__dev.html#ga32fabedf5f13fdecf1cb33acdb19b57a

制御するデバイスを主張する、なのか?

C言語版では固定で0を使っていたのだが、果たしてうまくいっていたのだろうか・・・。

 

それはともかく、gousbだとConfig()がそれなのか。
しかし、libusb_bulk_transfer()は無く、代わりにReadStream, WriteStreamを取得してアクセスするようだ。
その辺はサンプルを見た方が早いだろう。

https://github.com/google/gousb/blob/master/example_test.go#L52

 

Closeするのはdevだけでよさそうな気配がする。
これはまだ確信を得られないので、後で考えよう。

0 件のコメント:

コメントを投稿

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。

注: コメントを投稿できるのは、このブログのメンバーだけです。