pax_global_header00006660000000000000000000000064136041010150014501gustar00rootroot0000000000000052 comment=5bc2e0efa3ad19ac03b5dfd6ea98787d73e2795d go-mtpfs-1.0.0/000077500000000000000000000000001360410101500132335ustar00rootroot00000000000000go-mtpfs-1.0.0/.gitignore000066400000000000000000000000231360410101500152160ustar00rootroot00000000000000*~ /go-mtpfs *.swp go-mtpfs-1.0.0/CONTRIBUTING000066400000000000000000000003551360410101500150700ustar00rootroot00000000000000CONTRIBUTING You can send patches by github pull requests. Before your contibution can be accepted, Google must have a Contributor License Agreement (CLA) on file. You can register your CLA at https://cla.developers.google.com/clas go-mtpfs-1.0.0/LICENSE000066400000000000000000000030521360410101500142400ustar00rootroot00000000000000// New BSD License // // Copyright (c) 2012 Google Inc. All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Ivan Krasin nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // go-mtpfs-1.0.0/README.md000066400000000000000000000034661360410101500145230ustar00rootroot00000000000000### INTRODUCTION Go-mtpfs is a simple FUSE filesystem for mounting Android devices as a MTP device. It will expose all storage areas of a device in the mount, and only reads file metadata as needed, making it mount quickly. It uses Android extensions to read/write partial data, so manipulating large files requires no extra space in /tmp. It has been tested on various flagship devices (Galaxy Nexus, Xoom, Nexus 7). As of Jan. 2013, it uses a pure Go implementation of MTP, which is based on libusb. ### COMPILATION * Install the Go compiler suite; e.g. on Ubuntu: ``` sudo apt-get install golang-go ``` * Install libmtp header files ``` sudo apt-get install libusb1-devel ``` * Then check out go-mtpfs, and run ``` go build ./ ``` This will leave a binary `go-mtpfs` * You may need some tweaking to get libusb to compile. See the comment near the top of usb/usb.go, ie. ``` # edit to suit libusb installation: vi /tmp/go/src/github.com/hanwen/go-mtpfs/usb/usb.go go install github.com/hanwen/go-mtpfs ``` * 32-bit and 64-bit linux x86 binaries are at https://hanwen.home.xs4all.nl/public/software/go-mtpfs/ ### USAGE ``` mkdir xoom go-mtpfs xoom & cp -a ~/Music/Some-Album xoom/Music/ fusermount -u xoom ``` After a file is closed (eg. if "cp" completes), it is safe to unplug the device; the filesystem then will continue to function, but generates I/O errors when it reads from or writes to the device. ### CAVEATS * It does not implement rename between directories, because the Android stack does not implement it. * It does not implement Event handling, ie. it will not notice changes that the phone makes to the media database while connected. ### FEEDBACK You can send your feedback through the issue tracker at https://github.com/hanwen/go-mtpfs ### DISCLAIMER This is not an official Google product. go-mtpfs-1.0.0/all.bash000066400000000000000000000003651360410101500146460ustar00rootroot00000000000000#!/bin/sh # Script to exercise everything. set -eux for x in fs mtp do go build github.com/hanwen/go-mtpfs/$x go test -i github.com/hanwen/go-mtpfs/$x go test github.com/hanwen/go-mtpfs/$x done go build github.com/hanwen/go-mtpfs go-mtpfs-1.0.0/fs/000077500000000000000000000000001360410101500136435ustar00rootroot00000000000000go-mtpfs-1.0.0/fs/android.go000066400000000000000000000066101360410101500156150ustar00rootroot00000000000000// Copyright 2012 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package fs import ( "bytes" "context" "fmt" "log" "syscall" "time" "github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fuse" ) type androidNode struct { mtpNodeImpl // If set, the backing file was changed. write bool start time.Time byteCount int64 } func (n *androidNode) startEdit() bool { if n.write { return true } n.start = time.Now() n.byteCount = 0 if err := n.fs.dev.AndroidBeginEditObject(n.Handle()); err != nil { log.Println("AndroidBeginEditObject failed:", err) return false } n.write = true return true } func (n *androidNode) endEdit() bool { if !n.write { return true } dt := time.Now().Sub(n.start) log.Printf("%d bytes in %v: %d mb/s", n.byteCount, dt, (1e3*n.byteCount)/(dt.Nanoseconds())) if err := n.fs.dev.AndroidEndEditObject(n.Handle()); err != nil { log.Println("AndroidEndEditObject failed:", err) return false } n.write = false return true } var _ = (fs.NodeOpener)((*androidNode)(nil)) func (n *androidNode) Open(ctx context.Context, flags uint32) (file fs.FileHandle, fuseFlags uint32, code syscall.Errno) { return &androidFile{ node: n, }, 0, 0 } var _ = (fs.NodeSetattrer)((*androidNode)(nil)) func (n *androidNode) Setattr(ctx context.Context, file fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) (code syscall.Errno) { if size, ok := in.GetSize(); ok { w := n.write if !n.startEdit() { return syscall.EIO } if err := n.fs.dev.AndroidTruncate(n.Handle(), int64(size)); err != nil { log.Println("AndroidTruncate failed:", err) return syscall.EIO } n.Size = int64(size) if !w { if !n.endEdit() { return syscall.EIO } } } return n.Getattr(ctx, file, out) } var _ = mtpNode((*androidNode)(nil)) type androidFile struct { fs.FileHandle node *androidNode } var _ = (fs.FileReader)((*androidFile)(nil)) func (f *androidFile) Read(ctx context.Context, dest []byte, off int64) (fuse.ReadResult, syscall.Errno) { if off > f.node.Size { // ENXIO = no such address. return nil, syscall.Errno(int(syscall.ENXIO)) } if off+int64(len(dest)) > f.node.Size { dest = dest[:f.node.Size-off] } b := bytes.NewBuffer(dest[:0]) err := f.node.fs.dev.AndroidGetPartialObject64(f.node.Handle(), b, off, uint32(len(dest))) if err != nil { log.Println("AndroidGetPartialObject64 failed:", err) return nil, syscall.EIO } return fuse.ReadResultData(dest[:b.Len()]), 0 } func (f *androidFile) String() string { return fmt.Sprintf("androidFile h=0x%x", f.node.Handle()) } var _ = (fs.FileWriter)((*androidFile)(nil)) func (f *androidFile) Write(ctx context.Context, dest []byte, off int64) (written uint32, status syscall.Errno) { if !f.node.startEdit() { return 0, syscall.EIO } f.node.byteCount += int64(len(dest)) b := bytes.NewBuffer(dest) err := f.node.fs.dev.AndroidSendPartialObject(f.node.Handle(), off, uint32(len(dest)), b) if err != nil { log.Println("AndroidSendPartialObject failed:", err) return 0, syscall.EIO } written = uint32(len(dest) - b.Len()) if off+int64(written) > f.node.Size { f.node.Size = off + int64(written) } return written, 0 } var _ = (fs.FileFlusher)((*androidFile)(nil)) func (f *androidFile) Flush(ctx context.Context) syscall.Errno { if !f.node.endEdit() { return syscall.EIO } return 0 } go-mtpfs-1.0.0/fs/classic.go000066400000000000000000000177701360410101500156270ustar00rootroot00000000000000package fs import ( "context" "fmt" "io/ioutil" "log" "os" "syscall" "time" "github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fuse" "github.com/hanwen/go-mtpfs/mtp" ) type classicNode struct { mtpNodeImpl // local file containing the contents. backing string // If set, the backing file was changed. dirty bool // If set, there was some error writing to the backing store; // don't flush file to device. error syscall.Errno } func (n *classicNode) send() error { if !n.dirty { return nil } if n.backing == "" { log.Panicf("sending file without backing store: %q", n.obj.Filename) } f := n.obj if n.error != 0 { n.dirty = false os.Remove(n.backing) n.backing = "" n.error = 0 n.obj.CompressedSize = 0 n.Size = 0 log.Printf("not sending file %q due to write errors", f.Filename) return syscall.EIO // TODO - send back n.error } fi, err := os.Stat(n.backing) if err != nil { log.Printf("could not do stat for send: %v", err) return err } if fi.Size() == 0 { log.Printf("cannot send 0 byte file %q", f.Filename) return syscall.EINVAL } if n.obj.Filename == "" { return nil } if n.fs.mungeVfat[n.StorageID()] { f.Filename = SanitizeDosName(f.Filename) } backing, err := os.Open(n.backing) if err != nil { return err } defer backing.Close() log.Printf("sending file %q to device: %d bytes.", f.Filename, fi.Size()) if n.Handle() != 0 { // Apparently, you can't overwrite things in MTP. if err := n.fs.dev.DeleteObject(n.Handle()); err != nil { return err } n.handle = 0 } if fi.Size() > 0xFFFFFFFF { f.CompressedSize = 0xFFFFFFFF } else { f.CompressedSize = uint32(fi.Size()) } n.Size = fi.Size() start := time.Now() _, _, handle, err := n.fs.dev.SendObjectInfo(n.StorageID(), f.ParentObject, f) if err != nil { log.Printf("SendObjectInfo failed %v", err) return syscall.EINVAL } if err = n.fs.dev.SendObject(backing, fi.Size()); err != nil { log.Printf("SendObject failed %v", err) return syscall.EINVAL } dt := time.Now().Sub(start) log.Printf("sent %d bytes in %d ms. %.1f MB/s", fi.Size(), dt.Nanoseconds()/1e6, 1e3*float64(fi.Size())/float64(dt.Nanoseconds())) n.dirty = false n.handle = handle // XXX We could leave the file for future reading, but the // management of free space is a hassle when doing large // copies. return err } // Drop backing data if unused. Returns freed up space. func (n *classicNode) trim() int64 { if n.dirty || n.backing == "" { // XXX || n.Inode().AnyFile() != nil { return 0 } fi, err := os.Stat(n.backing) if err != nil { return 0 } log.Printf("removing local cache for %q, %d bytes", n.obj.Filename, fi.Size()) if err := os.Remove(n.backing); err != nil { return 0 } n.backing = "" return fi.Size() } // PTP supports partial fetch (not exposed in libmtp), but we might as // well get the whole thing. func (n *classicNode) fetch() error { if n.backing != "" { return nil } sz := n.Size if err := n.fs.ensureFreeSpace(sz); err != nil { return err } f, err := ioutil.TempFile(n.fs.options.Dir, "") if err != nil { return err } defer f.Close() start := time.Now() err = n.fs.dev.GetObject(n.Handle(), f) dt := time.Now().Sub(start) if err == nil { n.backing = f.Name() n.dirty = false log.Printf("fetched %q, %d bytes in %d ms. %.1f MB/s", n.obj.Filename, sz, dt.Nanoseconds()/1e6, 1e3*float64(sz)/float64(dt.Nanoseconds())) } else { log.Printf("error fetching: %v", err) err = syscall.EIO } return err } var _ = (fs.NodeOpener)((*classicNode)(nil)) func (n *classicNode) Open(ctx context.Context, flags uint32) (file fs.FileHandle, fuseFlags uint32, code syscall.Errno) { return &pendingFile{ node: n, }, 0, 0 } var _ = (fs.NodeSetattrer)((*classicNode)(nil)) func (n *classicNode) Setattr(ctx context.Context, file fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) (code syscall.Errno) { if file != nil { return file.(fs.FileSetattrer).Setattr(ctx, in, out) } return n.mtpNodeImpl.Setattr(ctx, file, in, out) } //////////////// // writing files. type pendingFile struct { loopback fs.FileHandle flags uint32 node *classicNode } func (p *pendingFile) rwLoopback() (fs.FileHandle, syscall.Errno) { if p.loopback == nil { if err := p.node.fetch(); err != nil { return nil, fs.ToErrno(err) } f, err := os.OpenFile(p.node.backing, os.O_RDWR|os.O_CREATE, 0644) if err != nil { return nil, fs.ToErrno(err) } p.loopback = fs.NewLoopbackFile(int(f.Fd())) } return p.loopback, 0 } var _ = (fs.FileReader)((*pendingFile)(nil)) func (p *pendingFile) Read(ctx context.Context, data []byte, off int64) (fuse.ReadResult, syscall.Errno) { if p.loopback == nil { if err := p.node.fetch(); err != nil { log.Printf("fetch failed: %v", err) return nil, syscall.EIO } f, err := os.OpenFile(p.node.backing, os.O_RDWR|os.O_CREATE, 0644) if err != nil { return nil, fs.ToErrno(err) } p.loopback = fs.NewLoopbackFile(int(f.Fd())) } return p.loopback.(fs.FileReader).Read(ctx, data, off) } var _ = (fs.FileWriter)((*pendingFile)(nil)) func (p *pendingFile) Write(ctx context.Context, data []byte, off int64) (uint32, syscall.Errno) { p.node.dirty = true f, code := p.rwLoopback() if code != 0 { return 0, code } n, code := f.(fs.FileWriter).Write(ctx, data, off) if code != 0 { p.node.error = code } return n, code } var _ = (fs.FileSetattrer)((*pendingFile)(nil)) func (p *pendingFile) Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) syscall.Errno { if size, ok := in.GetSize(); ok { f, code := p.rwLoopback() if code != 0 { return code } code = f.(fs.FileSetattrer).Setattr(ctx, in, out) if code != 0 { return code } p.node.dirty = true if code == 0 && size == 0 { p.node.error = 0 } return code } return 0 } var _ = (fs.FileFlusher)((*pendingFile)(nil)) func (p *pendingFile) Flush(ctx context.Context) syscall.Errno { if p.loopback == nil { return 0 } code := p.loopback.(fs.FileFlusher).Flush(ctx) if code != 0 { return code } s := fs.ToErrno(p.node.send()) if s == syscall.ENOSYS { return syscall.EIO } return s } var _ = (fs.FileReleaser)((*pendingFile)(nil)) func (p *pendingFile) Release(ctx context.Context) syscall.Errno { if p.loopback != nil { return p.loopback.(fs.FileReleaser).Release(ctx) } return 0 } //////////////////////////////////////////////////////////////// func (fs *deviceFS) trimUnused(todo int64, node *fs.Inode) (done int64) { for _, ch := range node.Children() { if done > todo { break } if fn, ok := ch.Operations().(*classicNode); ok { done += fn.trim() } else if ch.IsDir() { done += fs.trimUnused(todo-done, ch) } } return } func (fs *deviceFS) freeBacking() (int64, error) { t := syscall.Statfs_t{} if err := syscall.Statfs(fs.options.Dir, &t); err != nil { return 0, err } return int64(t.Bfree * uint64(t.Bsize)), nil } func (fs *deviceFS) ensureFreeSpace(want int64) error { free, err := fs.freeBacking() if err != nil { return err } if free > want { return nil } todo := want - free + 10*1024 fs.trimUnused(todo, &fs.root.Inode) free, err = fs.freeBacking() if err != nil { return err } if free > want { return nil } return fmt.Errorf("not enough space in %s. Have %d, want %d", fs.options.Dir, free, want) } func (fs *deviceFS) setupClassic() error { if fs.options.Dir == "" { var err error fs.options.Dir, err = ioutil.TempDir(os.TempDir(), "go-mtpfs") if err != nil { return err } fs.delBackingDir = true } if fi, err := os.Lstat(fs.options.Dir); err != nil || !fi.IsDir() { return fmt.Errorf("%s is not a directory", fs.options.Dir) } return nil } func (dfs *deviceFS) createClassicFile(obj mtp.ObjectInfo) (file fs.FileHandle, node fs.InodeEmbedder, err error) { backingFile, err := ioutil.TempFile(dfs.options.Dir, "") cl := &classicNode{ mtpNodeImpl: mtpNodeImpl{ obj: &obj, fs: dfs, }, dirty: true, backing: backingFile.Name(), } file = &pendingFile{ loopback: fs.NewLoopbackFile(int(backingFile.Fd())), node: cl, } node = cl return } go-mtpfs-1.0.0/fs/device_test.go000066400000000000000000000121721360410101500164730ustar00rootroot00000000000000package fs // This test requires an unlocked android MTP device plugged in. import ( "bytes" "flag" "fmt" "io" "io/ioutil" "math/rand" "os" "path/filepath" "testing" "time" "github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fuse" "github.com/hanwen/go-mtpfs/mtp" ) // VerboseTest returns true if the testing framework is run with -v. func VerboseTest() bool { flag := flag.Lookup("test.v") return flag != nil && flag.Value.String() == "true" } func init() { rand.Seed(time.Now().UnixNano()) } func startFs(t *testing.T, useAndroid bool) (storageRoot string, cleanup func()) { dev, err := mtp.SelectDevice("") if err != nil { t.Fatalf("SelectDevice failed: %v", err) } defer func() { if dev != nil { dev.Close() } }() if err = dev.Configure(); err != nil { t.Fatalf("Configure failed: %v", err) } sids, err := SelectStorages(dev, "") if err != nil { t.Fatalf("selectStorages failed: %v", err) } if len(sids) == 0 { t.Fatal("no storages found. Unlock device?") } tempdir, err := ioutil.TempDir("", "mtpfs") if err != nil { t.Fatal(err) } opts := DeviceFsOptions{ Android: useAndroid, } root, err := NewDeviceFSRoot(dev, sids, opts) if err != nil { t.Fatal("NewDeviceFs failed:", err) } server, err := fs.Mount(tempdir, root, &fs.Options{ MountOptions: fuse.MountOptions{ SingleThreaded: true, // Debug: VerboseTest(), }, }) if err != nil { t.Fatalf("mount failed: %v", err) } if false { dev.MTPDebug = VerboseTest() dev.USBDebug = VerboseTest() dev.DataDebug = VerboseTest() } go server.Serve() server.WaitMount() for i := 0; i < 10; i++ { fis, err := ioutil.ReadDir(tempdir) if err == nil && len(fis) > 0 { storageRoot = filepath.Join(tempdir, fis[0].Name()) break } time.Sleep(1) } if storageRoot == "" { server.Unmount() t.Fatal("could not find entries in mount point.") } d := dev dev = nil return storageRoot, func() { server.Unmount() d.Close() } } // Use this function to simulate improper connection handling of a // predecessor. func xTestBoom(t *testing.T) { root, clean := startFs(t, true) _ = root _ = clean go func() { panic("boom") }() } func testDevice(t *testing.T, useAndroid bool) { root, cleanup := startFs(t, useAndroid) defer cleanup() _, err := os.Lstat(root + "/Music") if err != nil { t.Logf("Music not found: %v", err) } dirName := filepath.Join(root, fmt.Sprintf("mtpfs-dir-test.x:y-%x", rand.Int31())) if err := os.Mkdir(dirName, 0755); err != nil { t.Fatalf("Mkdir: %v", err) } if _, err := ioutil.ReadDir(dirName); err != nil { t.Fatalf("ReadDir: %v", err) } if err := os.Remove(dirName); err != nil { t.Fatalf("Rmdir: %v", err) } name := filepath.Join(root, fmt.Sprintf("mtpfs-test-%x", rand.Int31())) golden := "abcpxq134" if err := ioutil.WriteFile(name, []byte("abcpxq134"), 0644); err != nil { t.Fatal("WriteFile failed", err) } defer os.Remove(name) got, err := ioutil.ReadFile(name) if err != nil { t.Fatal("ReadFile failed", err) } if string(got) != golden { t.Fatalf("got %q, want %q", got, golden) } f, err := os.OpenFile(name, os.O_RDWR|os.O_APPEND, 0644) if err != nil { t.Fatal("OpenFile failed:", err) } defer f.Close() n, _ := f.ReadAt(make([]byte, 4096), 4096) if n > 0 { t.Fatalf("beyond EOF read should fail: got %d bytes", n) } golden += "hello" _, err = f.Write([]byte("hello")) if err != nil { t.Fatal("file.Write failed", err) } err = f.Close() if err != nil { t.Fatal("Close failed", err) } got, err = ioutil.ReadFile(name) if err != nil { t.Fatal("ReadFile failed", err) } if string(got) != golden { t.Fatalf("got %q, want %q", got, golden) } newName := filepath.Join(root, fmt.Sprintf("mtpfs-test-%x", rand.Int31())) err = os.Rename(name, newName) if err != nil { t.Fatal("Rename failed", err) } defer os.Remove(newName) if fi, err := os.Lstat(name); err == nil { t.Fatal("should have disappeared after rename", fi) } if _, err := os.Lstat(newName); err != nil { t.Fatal("should be able to stat after rename", err) } err = os.Remove(newName) if err != nil { t.Fatalf("Remove failed: %v", err) } if fi, err := os.Lstat(newName); err == nil { t.Fatal("should have disappeared after Remove", fi) } } func testReadBlockBoundary(t *testing.T, android bool) { root, cleanup := startFs(t, android) defer cleanup() name := filepath.Join(root, fmt.Sprintf("mtpfs-test-%x", rand.Int31())) page := 4096 buf := bytes.Repeat([]byte("a"), 32*page) if err := ioutil.WriteFile(name, buf, 0644); err != nil { t.Fatalf("WriteFile: %v", err) } f, err := os.Open(name) if err != nil { t.Fatalf("Open(%q): %v", name, err) } total := 0 for { b := make([]byte, page) n, err := f.Read(b) total += n if n == 0 && err == io.EOF { break } if n != 4096 || err != nil { t.Fatalf("Read: %v (%d bytes)", err, n) } } f.Close() } func TestReadBlockBoundaryAndroid(t *testing.T) { testReadBlockBoundary(t, true) } func TestReadBlockBoundaryNormal(t *testing.T) { testReadBlockBoundary(t, false) } func TestAndroid(t *testing.T) { testDevice(t, true) } func TestNormal(t *testing.T) { testDevice(t, false) } go-mtpfs-1.0.0/fs/fs.go000066400000000000000000000326161360410101500146120ustar00rootroot00000000000000// Copyright 2012 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package fs import ( "bytes" "context" "fmt" "log" "os" "strings" "syscall" "time" "github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fuse" "github.com/hanwen/go-mtpfs/mtp" ) const blockSize = 512 type DeviceFsOptions struct { // Assume removable volumes are VFAT and munge filenames // accordingly. RemovableVFat bool // Backing directory. Dir string // Use android extensions if available. Android bool } // DeviceFS implements a fuse.NodeFileSystem that mounts multiple // storages. type deviceFS struct { backingDir string delBackingDir bool root *rootNode dev *mtp.Device devInfo mtp.DeviceInfo storages []uint32 mungeVfat map[uint32]bool options *DeviceFsOptions } // DeviceFs is a simple filesystem interface to an MTP device. It must // be mounted as SingleThread to make sure it is threadsafe. The file // system assumes the device does not touch the storage. Arguments // are the opened MTP device and a directory for the backing store. func NewDeviceFSRoot(d *mtp.Device, storages []uint32, options DeviceFsOptions) (*rootNode, error) { fs := &deviceFS{ root: &rootNode{}, dev: d, options: &options, } fs.root.fs = fs fs.storages = storages if err := d.GetDeviceInfo(&fs.devInfo); err != nil { return nil, err } if !strings.Contains(fs.devInfo.MTPExtension, "android.com") { fs.options.Android = false } if !options.Android { if err := fs.setupClassic(); err != nil { return nil, err } } fs.mungeVfat = make(map[uint32]bool) for _, sid := range fs.storages { var info mtp.StorageInfo if err := fs.dev.GetStorageInfo(sid, &info); err != nil { return nil, err } fs.mungeVfat[sid] = info.IsRemovable() && fs.options.RemovableVFat } return fs.Root(), nil } func (fs *deviceFS) Root() *rootNode { return fs.root } func (fs *deviceFS) String() string { return fmt.Sprintf("deviceFS(%s)", fs.devInfo.Model) } func (dfs *deviceFS) OnAdd(ctx context.Context) { for _, sid := range dfs.storages { var info mtp.StorageInfo if err := dfs.dev.GetStorageInfo(sid, &info); err != nil { log.Printf("GetStorageInfo %x: %v", sid, err) continue } obj := mtp.ObjectInfo{ ParentObject: NOPARENT_ID, StorageID: sid, Filename: info.StorageDescription, } folder := dfs.newFolder(obj, NOPARENT_ID) name := info.StorageDescription stable := fs.StableAttr{ Mode: syscall.S_IFDIR, Ino: uint64(sid) << 33, } dfs.root.Inode.AddChild(name, dfs.root.Inode.NewPersistentInode( ctx, folder, stable), false) } } // TODO - this should be per storage and return just the free space in // the storage. func (fs *deviceFS) newFile(obj mtp.ObjectInfo, size int64, id uint32) (node fs.InodeEmbedder) { if obj.CompressedSize != 0xFFFFFFFF { size = int64(obj.CompressedSize) } var m *mtpNodeImpl if fs.options.Android { n := &androidNode{} m = &n.mtpNodeImpl node = n } else { n := &classicNode{} m = &n.mtpNodeImpl node = n } m.obj = &obj m.handle = id m.fs = fs m.Size = size return node } type rootNode struct { fs.Inode fs *deviceFS } var _ = (fs.NodeOnAdder)((*rootNode)(nil)) func (r *rootNode) OnAdd(ctx context.Context) { r.fs.OnAdd(ctx) } const NOPARENT_ID = 0xFFFFFFFF // XXX func (n *rootNode) OnUnmount() { if n.fs.delBackingDir { os.RemoveAll(n.fs.options.Dir) n.fs.delBackingDir = false } } func (n *rootNode) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno { total := uint64(0) free := uint64(0) for _, ch := range n.Children() { var s fuse.StatfsOut if errno := ch.Operations().(fs.NodeStatfser).Statfs(ctx, &s); errno == 0 { total += s.Blocks free += s.Bfree } } *out = fuse.StatfsOut{ Bsize: blockSize, Blocks: total, Bavail: free, Bfree: free, } return 0 } const forbidden = ":*?\"<>|" func SanitizeDosName(name string) string { if strings.IndexAny(name, forbidden) == -1 { return name } dest := make([]byte, len(name)) for i := 0; i < len(name); i++ { if strings.Contains(forbidden, string(name[i])) { dest[i] = '_' } else { dest[i] = name[i] } } return string(dest) } //////////////// // mtpNode type mtpNode interface { Handle() uint32 StorageID() uint32 SetName(string) } type mtpNodeImpl struct { fs.Inode // MTP handle. handle uint32 obj *mtp.ObjectInfo fs *deviceFS // This is needed because obj.CompressedSize only goes to // 0xFFFFFFFF Size int64 } var _ = (fs.NodeStatfser)((*mtpNodeImpl)(nil)) func (n *mtpNodeImpl) Statfs(ctx context.Context, out *fuse.StatfsOut) syscall.Errno { total := uint64(0) free := uint64(0) var info mtp.StorageInfo if err := n.fs.dev.GetStorageInfo(n.StorageID(), &info); err != nil { log.Printf("GetStorageInfo %x: %v", n.StorageID(), err) return 0 } total += uint64(info.MaxCapability) free += uint64(info.FreeSpaceInBytes) *out = fuse.StatfsOut{ Bsize: blockSize, Blocks: total / blockSize, Bavail: free / blockSize, Bfree: free / blockSize, } return 0 } var _ = (fs.NodeGetxattrer)((*mtpNodeImpl)(nil)) func (n *mtpNodeImpl) Getxattr(ctx context.Context, attr string, dest []byte) (uint32, syscall.Errno) { return 0, syscall.ENOSYS } var _ = (fs.NodeSetxattrer)((*mtpNodeImpl)(nil)) func (n *mtpNodeImpl) Setxattr(ctx context.Context, attr string, dest []byte, flags uint32) syscall.Errno { return syscall.ENOSYS } var _ = (fs.NodeGetattrer)((*mtpNodeImpl)(nil)) func (n *mtpNodeImpl) Getattr(ctx context.Context, file fs.FileHandle, out *fuse.AttrOut) (code syscall.Errno) { if n.IsDir() { out.Mode = 0755 } else { out.Mode = 0644 } f := n.obj if f != nil { out.Size = uint64(n.Size) t := f.ModificationDate out.SetTimes(&t, &t, &t) out.Blocks = (out.Size + blockSize - 1) / blockSize } return 0 } func (n *mtpNodeImpl) setTime(mTime *time.Time) { // Unfortunately, we can't set the modtime; it's READONLY in // the Android MTP implementation. We just change the time in // the mount, but this is not persisted. if mTime != nil { n.obj.ModificationDate = *mTime } } func (n *mtpNodeImpl) Handle() uint32 { return n.handle } func (n *mtpNodeImpl) SetName(nm string) { n.obj.Filename = nm } func (n *mtpNodeImpl) StorageID() uint32 { return n.obj.StorageID } func (n *mtpNodeImpl) Setattr(ctx context.Context, file fs.FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) (code syscall.Errno) { if mt, ok := in.GetMTime(); ok { n.setTime(&mt) var atime = mt if a, ok := in.GetATime(); ok { atime = a } out.SetTimes(&atime, &mt, nil) } return 0 } var _ = mtpNode((*folderNode)(nil)) //////////////// // files ////////////////// // folders type folderNode struct { mtpNodeImpl fetched bool } func (fs *deviceFS) newFolder(obj mtp.ObjectInfo, h uint32) *folderNode { obj.AssociationType = mtp.OFC_Association return &folderNode{ mtpNodeImpl: mtpNodeImpl{ handle: h, obj: &obj, fs: fs, }, } } // Keep the root nodes for all device storages alive. func (n *folderNode) Deletable() bool { return n.Handle() != NOPARENT_ID } // Fetches data from device returns false on failure. func (n *folderNode) fetch(ctx context.Context) bool { if n.fetched { return true } handles := mtp.Uint32Array{} if err := n.fs.dev.GetObjectHandles(n.StorageID(), 0x0, n.Handle(), &handles); err != nil { log.Printf("GetObjectHandles failed: %v", err) return false } infos := map[uint32]*mtp.ObjectInfo{} sizes := map[uint32]int64{} for _, handle := range handles.Values { obj := mtp.ObjectInfo{} if err := n.fs.dev.GetObjectInfo(handle, &obj); err != nil { log.Printf("GetObjectInfo for handle %d failed: %v", handle, err) continue } if obj.Filename == "" { log.Printf("ignoring handle 0x%x with empty name in dir 0x%x", handle, n.Handle()) continue } if obj.CompressedSize == 0xFFFFFFFF { var val mtp.Uint64Value if err := n.fs.dev.GetObjectPropValue(handle, mtp.OPC_ObjectSize, &val); err != nil { log.Printf("GetObjectPropValue handle %d failed: %v", handle, err) return false } sizes[handle] = int64(val.Value) } infos[handle] = &obj } for handle, info := range infos { var node fs.InodeEmbedder info.ParentObject = n.Handle() isdir := info.ObjectFormat == mtp.OFC_Association stable := fs.StableAttr{ // Avoid ID 1. Ino: uint64(handle) << 1, } if isdir { fNode := n.fs.newFolder(*info, handle) node = fNode stable.Mode = syscall.S_IFDIR } else { sz := sizes[handle] node = n.fs.newFile(*info, sz, handle) stable.Mode = syscall.S_IFREG } n.AddChild(info.Filename, n.NewPersistentInode(ctx, node, stable), true) } n.fetched = true return true } var _ = (fs.NodeReaddirer)((*folderNode)(nil)) func (n *folderNode) Readdir(ctx context.Context) (stream fs.DirStream, status syscall.Errno) { if !n.fetch(ctx) { return nil, syscall.EIO } r := []fuse.DirEntry{} for k, ch := range n.Children() { r = append(r, fuse.DirEntry{Mode: ch.Mode(), Name: k, Ino: ch.StableAttr().Ino}) } return fs.NewListDirStream(r), 0 } func (n *folderNode) basenameRename(oldName string, newName string) error { ch := n.GetChild(oldName) mFile := ch.Operations().(mtpNode) if mFile.Handle() != 0 { // Only rename on device if it was sent already. v := mtp.StringValue{Value: newName} if err := n.fs.dev.SetObjectPropValue(mFile.Handle(), mtp.OPC_ObjectFileName, &v); err != nil { return err } } return nil } var _ = (fs.NodeRenamer)((*folderNode)(nil)) func (n *folderNode) Rename(ctx context.Context, oldName string, newParent fs.InodeEmbedder, newName string, flags uint32) (code syscall.Errno) { fn, ok := newParent.(*folderNode) if !ok { return syscall.ENOSYS } fn.fetch(ctx) n.fetch(ctx) if f := n.GetChild(newName); f != nil { if fn != n { // TODO - delete destination? log.Printf("old folder already has child %q", newName) return syscall.ENOSYS } // does mtp overwrite the destination? } if fn != n { return syscall.ENOSYS } if newName != oldName { if err := n.basenameRename(oldName, newName); err != nil { log.Printf("basenameRename failed: %v", err) return syscall.EIO } } return 0 } var _ = (fs.NodeLookuper)((*folderNode)(nil)) func (n *folderNode) Lookup(ctx context.Context, name string, out *fuse.EntryOut) (node *fs.Inode, code syscall.Errno) { if !n.fetch(ctx) { return nil, syscall.EIO } ch := n.GetChild(name) if ch == nil { return nil, syscall.ENOENT } if ga, ok := ch.Operations().(fs.NodeGetattrer); ok { var attr fuse.AttrOut code = ga.Getattr(ctx, nil, &attr) out.Attr = attr.Attr } return ch, code } var _ = (fs.NodeMkdirer)((*folderNode)(nil)) func (n *folderNode) Mkdir(ctx context.Context, name string, mode uint32, out *fuse.EntryOut) (*fs.Inode, syscall.Errno) { if !n.fetch(ctx) { return nil, syscall.EIO } obj := mtp.ObjectInfo{ Filename: name, ObjectFormat: mtp.OFC_Association, ModificationDate: time.Now(), ParentObject: n.Handle(), StorageID: n.StorageID(), } if n.fs.mungeVfat[n.StorageID()] { obj.Filename = SanitizeDosName(obj.Filename) } _, _, newId, err := n.fs.dev.SendObjectInfo(n.StorageID(), n.Handle(), &obj) if err != nil { log.Printf("CreateFolder failed: %v", err) return nil, syscall.EIO } f := n.fs.newFolder(obj, newId) stable := fs.StableAttr{Mode: syscall.S_IFDIR} ch := n.NewPersistentInode(ctx, f, stable) out.Mode = 0755 return ch, 0 } var _ = (fs.NodeUnlinker)((*folderNode)(nil)) func (n *folderNode) Unlink(ctx context.Context, name string) syscall.Errno { if !n.fetch(ctx) { return syscall.EIO } ch := n.GetChild(name) if ch == nil { return syscall.ENOENT } f := ch.Operations().(mtpNode) if f.Handle() != 0 { if err := n.fs.dev.DeleteObject(f.Handle()); err != nil { log.Printf("DeleteObject failed: %v", err) return syscall.EIO } } else { f.SetName("") } n.RmChild(name) return 0 } var _ = (fs.NodeRmdirer)((*folderNode)(nil)) func (n *folderNode) Rmdir(ctx context.Context, name string) syscall.Errno { return n.Unlink(ctx, name) } var _ = (fs.NodeCreater)((*folderNode)(nil)) func (n *folderNode) Create(ctx context.Context, name string, flags uint32, mode uint32, out *fuse.EntryOut) (ch *fs.Inode, file fs.FileHandle, fuseFlags uint32, errno syscall.Errno) { if !n.fetch(ctx) { errno = syscall.EIO return } obj := mtp.ObjectInfo{ StorageID: n.StorageID(), Filename: name, ObjectFormat: mtp.OFC_Undefined, ModificationDate: time.Now(), ParentObject: n.Handle(), CompressedSize: 0, } var fsNode fs.InodeEmbedder if n.fs.options.Android { _, _, handle, err := n.fs.dev.SendObjectInfo(n.StorageID(), n.Handle(), &obj) if err != nil { log.Println("SendObjectInfo failed", err) errno = syscall.EIO return } if err = n.fs.dev.SendObject(&bytes.Buffer{}, 0); err != nil { log.Println("SendObject failed:", err) errno = syscall.EIO return } aNode := &androidNode{ mtpNodeImpl: mtpNodeImpl{ obj: &obj, fs: n.fs, handle: handle, }, } if !aNode.startEdit() { errno = syscall.EIO return } file = &androidFile{ node: aNode, } fsNode = aNode } else { var err error file, fsNode, err = n.fs.createClassicFile(obj) if err != nil { errno = fs.ToErrno(err) return } } ch = n.NewPersistentInode(ctx, fsNode, fs.StableAttr{}) var a fuse.AttrOut out.Attr = a.Attr return ch, file, 0, 0 } go-mtpfs-1.0.0/fs/select.go000066400000000000000000000014541360410101500154550ustar00rootroot00000000000000package fs import ( "log" "regexp" "github.com/hanwen/go-mtpfs/mtp" ) func SelectStorages(dev *mtp.Device, pat string) ([]uint32, error) { sids := mtp.Uint32Array{} if err := dev.GetStorageIDs(&sids); err != nil { return nil, err } re, err := regexp.Compile(pat) if err != nil { return nil, err } filtered := []uint32{} for _, id := range sids.Values { var s mtp.StorageInfo if err := dev.GetStorageInfo(id, &s); err != nil { return nil, err } if !s.IsHierarchical() && !s.IsDCF() { log.Printf("skipping non hierarchical or DCF storage %q", s.StorageDescription) continue } if re.FindStringIndex(s.StorageDescription) == nil { log.Printf("filtering out storage %q", s.StorageDescription) continue } filtered = append(filtered, id) } return filtered, nil } go-mtpfs-1.0.0/go.mod000066400000000000000000000004541360410101500143440ustar00rootroot00000000000000module github.com/hanwen/go-mtpfs go 1.12 require ( github.com/hanwen/go-fuse v1.0.0 github.com/hanwen/go-fuse/v2 v2.0.2 github.com/hanwen/usb v0.0.0-20141217151552-69aee4530ac7 github.com/kylelemons/godebug v1.1.0 // indirect golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 // indirect ) go-mtpfs-1.0.0/go.sum000066400000000000000000000030631360410101500143700ustar00rootroot00000000000000github.com/hanwen/go-fuse v0.0.0-20190726130028-2f298055551b h1:oUwn+w6XmXlah84XM7iSqKcr6ojbKUED0o4JVpGW7n4= github.com/hanwen/go-fuse v0.0.0-20190726130028-2f298055551b/go.mod h1:PHVWttMW0DYH6ESFXdZ8S+STmGjwEuGX6gsCPi605mg= github.com/hanwen/go-fuse v1.0.0 h1:GxS9Zrn6c35/BnfiVsZVWmsG803xwE7eVRDvcf/BEVc= github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok= github.com/hanwen/go-fuse/v2 v2.0.1 h1:ZqWzfYbVQe/xSDDFNqW9Ivl+AwWt+t0/5lWLhbroJTM= github.com/hanwen/go-fuse/v2 v2.0.1/go.mod h1:OT5e1HOVxxQyq/qgsYpObejwriAgou9DURqOpiGxWfI= github.com/hanwen/go-fuse/v2 v2.0.2 h1:BtsqKI5RXOqDMnTgpCb0IWgvRgGLJdqYVZ/Hm6KgKto= github.com/hanwen/go-fuse/v2 v2.0.2/go.mod h1:HH3ygZOoyRbP9y2q7y3+JM6hPL+Epe29IbWaS0UA81o= github.com/hanwen/usb v0.0.0-20141217151552-69aee4530ac7 h1:fJ8PRDCj5Fa3oEwweff/QaCGUYjLjGUmCgeOUT+JkJM= github.com/hanwen/usb v0.0.0-20141217151552-69aee4530ac7/go.mod h1:yF/X+HyjXB5nFLDk2wr03cx0BRaFJ7iaAPFGRaKnwEk= github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522 h1:Ve1ORMCxvRmSXBwJK+t3Oy+V2vRW2OetUQBq4rJIkZE= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= go-mtpfs-1.0.0/main.go000066400000000000000000000046651360410101500145210ustar00rootroot00000000000000// Copyright 2012 Google Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "flag" "log" "os" "strings" "syscall" "time" fusefs "github.com/hanwen/go-fuse/v2/fs" "github.com/hanwen/go-fuse/v2/fuse" "github.com/hanwen/go-mtpfs/fs" "github.com/hanwen/go-mtpfs/mtp" ) func main() { debug := flag.String("debug", "", "comma-separated list of debugging options: usb, data, mtp, fuse") usbTimeout := flag.Int("usb-timeout", 5000, "timeout in milliseconds") vfat := flag.Bool("vfat", true, "assume removable RAM media uses VFAT, and rewrite names.") other := flag.Bool("allow-other", false, "allow other users to access mounted fuse. Default: false.") deviceFilter := flag.String("dev", "", "regular expression to filter device IDs, "+ "which are composed of manufacturer/product/serial.") storageFilter := flag.String("storage", "", "regular expression to filter storage areas.") android := flag.Bool("android", true, "use android extensions if available") flag.Parse() if len(flag.Args()) != 1 { log.Fatalf("Usage: %s [options] MOUNT-POINT\n", os.Args[0]) } mountpoint := flag.Arg(0) dev, err := mtp.SelectDevice(*deviceFilter) if err != nil { log.Fatalf("detect failed: %v", err) } defer dev.Close() debugs := map[string]bool{} for _, s := range strings.Split(*debug, ",") { debugs[s] = true } dev.MTPDebug = debugs["mtp"] dev.DataDebug = debugs["data"] dev.USBDebug = debugs["usb"] dev.Timeout = *usbTimeout if err = dev.Configure(); err != nil { log.Fatalf("Configure failed: %v", err) } sids, err := fs.SelectStorages(dev, *storageFilter) if err != nil { log.Fatalf("selectStorages failed: %v", err) } opts := fs.DeviceFsOptions{ RemovableVFat: *vfat, Android: *android, } root, err := fs.NewDeviceFSRoot(dev, sids, opts) if err != nil { log.Fatalf("NewDeviceFs failed: %v", err) } sec := time.Second mountOpts := &fusefs.Options{ MountOptions: fuse.MountOptions{ SingleThreaded: true, AllowOther: *other, Debug: debugs["fuse"] || debugs["fs"], }, UID: uint32(syscall.Getuid()), GID: uint32(syscall.Getgid()), AttrTimeout: &sec, EntryTimeout: &sec, } server, err := fusefs.Mount(mountpoint, root, mountOpts) if err != nil { log.Fatalf("mount failed: %v", err) } server.WaitMount() log.Printf("FUSE mounted") server.Wait() root.OnUnmount() } go-mtpfs-1.0.0/mtp/000077500000000000000000000000001360410101500140335ustar00rootroot00000000000000go-mtpfs-1.0.0/mtp/android.go000066400000000000000000000051101360410101500157770ustar00rootroot00000000000000package mtp import ( "io" ) // Android MTP extensions // Same as GetPartialObject, but with 64 bit offset const OC_ANDROID_GET_PARTIAL_OBJECT64 = 0x95C1 // Same as GetPartialObject64, but copying host to device const OC_ANDROID_SEND_PARTIAL_OBJECT = 0x95C2 // Truncates file to 64 bit length const OC_ANDROID_TRUNCATE_OBJECT = 0x95C3 // Must be called before using SendPartialObject and TruncateObject const OC_ANDROID_BEGIN_EDIT_OBJECT = 0x95C4 // Called to commit changes made by SendPartialObject and TruncateObject const OC_ANDROID_END_EDIT_OBJECT = 0x95C5 func init() { OC_names[0x95C1] = "ANDROID_GET_PARTIAL_OBJECT64" OC_names[0x95C2] = "ANDROID_SEND_PARTIAL_OBJECT" OC_names[0x95C3] = "ANDROID_TRUNCATE_OBJECT" OC_names[0x95C4] = "ANDROID_BEGIN_EDIT_OBJECT" OC_names[0x95C5] = "ANDROID_END_EDIT_OBJECT" } // AndroidGetPartialObject64 reads a section of a file. func (d *Device) AndroidGetPartialObject64(handle uint32, w io.Writer, offset int64, size uint32) error { var req, rep Container req.Code = OC_ANDROID_GET_PARTIAL_OBJECT64 req.Param = []uint32{handle, uint32(offset & 0xFFFFFFFF), uint32(offset >> 32), size} return d.RunTransaction(&req, &rep, w, nil, 0) } // AndroidBeginEditObject opens a file for writing. func (d *Device) AndroidBeginEditObject(handle uint32) error { var req, rep Container req.Code = OC_ANDROID_BEGIN_EDIT_OBJECT req.Param = []uint32{handle} return d.RunTransaction(&req, &rep, nil, nil, 0) } // AndroidTruncate truncates at a file at a given length. func (d *Device) AndroidTruncate(handle uint32, offset int64) error { var req, rep Container req.Code = OC_ANDROID_TRUNCATE_OBJECT req.Param = []uint32{handle, uint32(offset & 0xFFFFFFFF), uint32(offset >> 32)} return d.RunTransaction(&req, &rep, nil, nil, 0) } // AndroidSendPartialObject writes a section of a file. func (d *Device) AndroidSendPartialObject(handle uint32, offset int64, size uint32, r io.Reader) error { var req, rep Container req.Code = OC_ANDROID_SEND_PARTIAL_OBJECT req.Param = []uint32{handle, uint32(offset & 0xFFFFFFFF), uint32(offset >> 32), size} // MtpServer.cpp is buggy: it uses write() without offset // rather than pwrite to send the data for data coming with // the header packet d.SeparateHeader = true err := d.RunTransaction(&req, &rep, nil, r, int64(size)) d.SeparateHeader = false return err } // AndroidEndEditObject closes a file opened for write. func (d *Device) AndroidEndEditObject(handle uint32) error { var req, rep Container req.Code = OC_ANDROID_END_EDIT_OBJECT req.Param = []uint32{handle} return d.RunTransaction(&req, &rep, nil, nil, 0) } go-mtpfs-1.0.0/mtp/const.go000066400000000000000000002051661360410101500155220ustar00rootroot00000000000000package mtp // DO NOT EDIT : generated automatically by munge.py const AC_ReadWrite = 0x0000 const AC_ReadOnly = 0x0001 const AC_ReadOnly_with_Object_Deletion = 0x0002 var AC_names = map[int]string{0x0000: "ReadWrite", 0x0001: "ReadOnly", 0x0002: "ReadOnly_with_Object_Deletion", } const AT_Undefined = 0x0000 const AT_GenericFolder = 0x0001 const AT_Album = 0x0002 const AT_TimeSequence = 0x0003 const AT_HorizontalPanoramic = 0x0004 const AT_VerticalPanoramic = 0x0005 const AT_2DPanoramic = 0x0006 const AT_AncillaryData = 0x0007 var AT_names = map[int]string{0x0000: "Undefined", 0x0001: "GenericFolder", 0x0002: "Album", 0x0003: "TimeSequence", 0x0004: "HorizontalPanoramic", 0x0005: "VerticalPanoramic", 0x0006: "2DPanoramic", 0x0007: "AncillaryData", } const DL_LE = 0x0F const DL_BE = 0xF0 var DL_names = map[int]string{0x0F: "LE", 0xF0: "BE", } // device property code const DPC_Undefined = 0x5000 const DPC_BatteryLevel = 0x5001 const DPC_FunctionalMode = 0x5002 const DPC_ImageSize = 0x5003 const DPC_CompressionSetting = 0x5004 const DPC_WhiteBalance = 0x5005 const DPC_RGBGain = 0x5006 const DPC_FNumber = 0x5007 const DPC_FocalLength = 0x5008 const DPC_FocusDistance = 0x5009 const DPC_FocusMode = 0x500A const DPC_ExposureMeteringMode = 0x500B const DPC_FlashMode = 0x500C const DPC_ExposureTime = 0x500D const DPC_ExposureProgramMode = 0x500E const DPC_ExposureIndex = 0x500F const DPC_ExposureBiasCompensation = 0x5010 const DPC_DateTime = 0x5011 const DPC_CaptureDelay = 0x5012 const DPC_StillCaptureMode = 0x5013 const DPC_Contrast = 0x5014 const DPC_Sharpness = 0x5015 const DPC_DigitalZoom = 0x5016 const DPC_EffectMode = 0x5017 const DPC_BurstNumber = 0x5018 const DPC_BurstInterval = 0x5019 const DPC_TimelapseNumber = 0x501A const DPC_TimelapseInterval = 0x501B const DPC_FocusMeteringMode = 0x501C const DPC_UploadURL = 0x501D const DPC_Artist = 0x501E const DPC_CopyrightInfo = 0x501F const DPC_SupportedStreams = 0x5020 const DPC_EnabledStreams = 0x5021 const DPC_VideoFormat = 0x5022 const DPC_VideoResolution = 0x5023 const DPC_VideoQuality = 0x5024 const DPC_VideoFrameRate = 0x5025 const DPC_VideoContrast = 0x5026 const DPC_VideoBrightness = 0x5027 const DPC_AudioFormat = 0x5028 const DPC_AudioBitrate = 0x5029 const DPC_AudioSamplingRate = 0x502A const DPC_AudioBitPerSample = 0x502B const DPC_AudioVolume = 0x502C const DPC_EXTENSION = 0xD000 const DPC_CANON_BeepMode = 0xD001 const DPC_EK_ColorTemperature = 0xD001 const DPC_CANON_BatteryKind = 0xD002 const DPC_EK_DateTimeStampFormat = 0xD002 const DPC_CANON_BatteryStatus = 0xD003 const DPC_EK_BeepMode = 0xD003 const DPC_CANON_UILockType = 0xD004 const DPC_CASIO_UNKNOWN_1 = 0xD004 const DPC_EK_VideoOut = 0xD004 const DPC_CANON_CameraMode = 0xD005 const DPC_CASIO_UNKNOWN_2 = 0xD005 const DPC_EK_PowerSaving = 0xD005 const DPC_CANON_ImageQuality = 0xD006 const DPC_EK_UI_Language = 0xD006 const DPC_CANON_FullViewFileFormat = 0xD007 const DPC_CASIO_UNKNOWN_3 = 0xD007 const DPC_CANON_ImageSize = 0xD008 const DPC_CASIO_RECORD_LIGHT = 0xD008 const DPC_CANON_SelfTime = 0xD009 const DPC_CASIO_UNKNOWN_4 = 0xD009 const DPC_CANON_FlashMode = 0xD00A const DPC_CASIO_UNKNOWN_5 = 0xD00A const DPC_CANON_Beep = 0xD00B const DPC_CASIO_MOVIE_MODE = 0xD00B const DPC_CANON_ShootingMode = 0xD00C const DPC_CASIO_HD_SETTING = 0xD00C const DPC_CANON_ImageMode = 0xD00D const DPC_CASIO_HS_SETTING = 0xD00D const DPC_CANON_DriveMode = 0xD00E const DPC_CANON_EZoom = 0xD00F const DPC_CASIO_CS_HIGH_SPEED = 0xD00F const DPC_CANON_MeteringMode = 0xD010 const DPC_CASIO_CS_UPPER_LIMIT = 0xD010 const DPC_NIKON_ShootingBank = 0xD010 const DPC_CANON_AFDistance = 0xD011 const DPC_CASIO_CS_SHOT = 0xD011 const DPC_NIKON_ShootingBankNameA = 0xD011 const DPC_CANON_FocusingPoint = 0xD012 const DPC_CASIO_UNKNOWN_6 = 0xD012 const DPC_NIKON_ShootingBankNameB = 0xD012 const DPC_CANON_WhiteBalance = 0xD013 const DPC_CASIO_UNKNOWN_7 = 0xD013 const DPC_NIKON_ShootingBankNameC = 0xD013 const DPC_CANON_SlowShutterSetting = 0xD014 const DPC_NIKON_ShootingBankNameD = 0xD014 const DPC_CANON_AFMode = 0xD015 const DPC_CASIO_UNKNOWN_8 = 0xD015 const DPC_NIKON_ResetBank0 = 0xD015 const DPC_CANON_ImageStabilization = 0xD016 const DPC_NIKON_RawCompression = 0xD016 const DPC_CANON_Contrast = 0xD017 const DPC_CASIO_UNKNOWN_9 = 0xD017 const DPC_FUJI_ColorTemperature = 0xD017 const DPC_NIKON_WhiteBalanceAutoBias = 0xD017 const DPC_CANON_ColorGain = 0xD018 const DPC_CASIO_UNKNOWN_10 = 0xD018 const DPC_FUJI_Quality = 0xD018 const DPC_NIKON_WhiteBalanceTungstenBias = 0xD018 const DPC_CANON_Sharpness = 0xD019 const DPC_CASIO_UNKNOWN_11 = 0xD019 const DPC_NIKON_WhiteBalanceFluorescentBias = 0xD019 const DPC_CANON_Sensitivity = 0xD01A const DPC_CASIO_UNKNOWN_12 = 0xD01A const DPC_NIKON_WhiteBalanceDaylightBias = 0xD01A const DPC_CANON_ParameterSet = 0xD01B const DPC_CASIO_UNKNOWN_13 = 0xD01B const DPC_NIKON_WhiteBalanceFlashBias = 0xD01B const DPC_CANON_ISOSpeed = 0xD01C const DPC_CASIO_UNKNOWN_14 = 0xD01C const DPC_NIKON_WhiteBalanceCloudyBias = 0xD01C const DPC_CANON_Aperture = 0xD01D const DPC_CASIO_UNKNOWN_15 = 0xD01D const DPC_NIKON_WhiteBalanceShadeBias = 0xD01D const DPC_CANON_ShutterSpeed = 0xD01E const DPC_NIKON_WhiteBalanceColorTemperature = 0xD01E const DPC_CANON_ExpCompensation = 0xD01F const DPC_NIKON_WhiteBalancePresetNo = 0xD01F const DPC_CANON_FlashCompensation = 0xD020 const DPC_CASIO_UNKNOWN_16 = 0xD020 const DPC_NIKON_WhiteBalancePresetName0 = 0xD020 const DPC_CANON_AEBExposureCompensation = 0xD021 const DPC_NIKON_WhiteBalancePresetName1 = 0xD021 const DPC_NIKON_WhiteBalancePresetName2 = 0xD022 const DPC_CANON_AvOpen = 0xD023 const DPC_NIKON_WhiteBalancePresetName3 = 0xD023 const DPC_CANON_AvMax = 0xD024 const DPC_NIKON_WhiteBalancePresetName4 = 0xD024 const DPC_CANON_FocalLength = 0xD025 const DPC_NIKON_WhiteBalancePresetVal0 = 0xD025 const DPC_CANON_FocalLengthTele = 0xD026 const DPC_NIKON_WhiteBalancePresetVal1 = 0xD026 const DPC_CANON_FocalLengthWide = 0xD027 const DPC_NIKON_WhiteBalancePresetVal2 = 0xD027 const DPC_CANON_FocalLengthDenominator = 0xD028 const DPC_NIKON_WhiteBalancePresetVal3 = 0xD028 const DPC_CANON_CaptureTransferMode = 0xD029 const DPC_NIKON_WhiteBalancePresetVal4 = 0xD029 const DPC_CANON_Zoom = 0xD02A const DPC_NIKON_ImageSharpening = 0xD02A const DPC_CANON_NamePrefix = 0xD02B const DPC_NIKON_ToneCompensation = 0xD02B const DPC_CANON_SizeQualityMode = 0xD02C const DPC_NIKON_ColorModel = 0xD02C const DPC_CANON_SupportedThumbSize = 0xD02D const DPC_NIKON_HueAdjustment = 0xD02D const DPC_CANON_SizeOfOutputDataFromCamera = 0xD02E const DPC_CANON_SizeOfInputDataToCamera = 0xD02F const DPC_CANON_RemoteAPIVersion = 0xD030 const DPC_CASIO_UNKNOWN_17 = 0xD030 const DPC_NIKON_ShootingMode = 0xD030 const DPC_CANON_FirmwareVersion = 0xD031 const DPC_NIKON_JPEG_Compression_Policy = 0xD031 const DPC_CANON_CameraModel = 0xD032 const DPC_NIKON_ColorSpace = 0xD032 const DPC_CANON_CameraOwner = 0xD033 const DPC_NIKON_AutoDXCrop = 0xD033 const DPC_CANON_UnixTime = 0xD034 const DPC_CANON_CameraBodyID = 0xD035 const DPC_CANON_CameraOutput = 0xD036 const DPC_NIKON_VideoMode = 0xD036 const DPC_CANON_DispAv = 0xD037 const DPC_NIKON_EffectMode = 0xD037 const DPC_CANON_AvOpenApex = 0xD038 const DPC_CANON_DZoomMagnification = 0xD039 const DPC_CANON_MlSpotPos = 0xD03A const DPC_CANON_DispAvMax = 0xD03B const DPC_CANON_AvMaxApex = 0xD03C const DPC_CANON_EZoomStartPosition = 0xD03D const DPC_CANON_FocalLengthOfTele = 0xD03E const DPC_CANON_EZoomSizeOfTele = 0xD03F const DPC_CANON_PhotoEffect = 0xD040 const DPC_NIKON_CSMMenuBankSelect = 0xD040 const DPC_CANON_AssistLight = 0xD041 const DPC_NIKON_MenuBankNameA = 0xD041 const DPC_CANON_FlashQuantityCount = 0xD042 const DPC_NIKON_MenuBankNameB = 0xD042 const DPC_CANON_RotationAngle = 0xD043 const DPC_NIKON_MenuBankNameC = 0xD043 const DPC_CANON_RotationScene = 0xD044 const DPC_NIKON_MenuBankNameD = 0xD044 const DPC_CANON_EventEmulateMode = 0xD045 const DPC_NIKON_ResetBank = 0xD045 const DPC_CANON_DPOFVersion = 0xD046 const DPC_CANON_TypeOfSupportedSlideShow = 0xD047 const DPC_CANON_AverageFilesizes = 0xD048 const DPC_NIKON_A1AFCModePriority = 0xD048 const DPC_CANON_ModelID = 0xD049 const DPC_NIKON_A2AFSModePriority = 0xD049 const DPC_NIKON_A3GroupDynamicAF = 0xD04A const DPC_NIKON_A4AFActivation = 0xD04B const DPC_NIKON_FocusAreaIllumManualFocus = 0xD04C const DPC_NIKON_FocusAreaIllumContinuous = 0xD04D const DPC_NIKON_FocusAreaIllumWhenSelected = 0xD04E const DPC_NIKON_VerticalAFON = 0xD050 const DPC_NIKON_AFLockOn = 0xD051 const DPC_NIKON_FocusAreaZone = 0xD052 const DPC_NIKON_EnableCopyright = 0xD053 const DPC_NIKON_ISOAuto = 0xD054 const DPC_NIKON_EVISOStep = 0xD055 const DPC_NIKON_EVStepExposureComp = 0xD057 const DPC_NIKON_ExposureCompensation = 0xD058 const DPC_NIKON_CenterWeightArea = 0xD059 const DPC_NIKON_ExposureBaseMatrix = 0xD05A const DPC_NIKON_ExposureBaseCenter = 0xD05B const DPC_NIKON_ExposureBaseSpot = 0xD05C const DPC_NIKON_LiveViewAFArea = 0xD05D const DPC_NIKON_AELockMode = 0xD05E const DPC_NIKON_AELAFLMode = 0xD05F const DPC_NIKON_LiveViewAFFocus = 0xD061 const DPC_NIKON_MeterOff = 0xD062 const DPC_NIKON_SelfTimer = 0xD063 const DPC_NIKON_MonitorOff = 0xD064 const DPC_NIKON_ImgConfTime = 0xD065 const DPC_NIKON_AutoOffTimers = 0xD066 const DPC_NIKON_AngleLevel = 0xD067 const DPC_NIKON_D2MaximumShots = 0xD069 const DPC_NIKON_ExposureDelayMode = 0xD06A const DPC_NIKON_LongExposureNoiseReduction = 0xD06B const DPC_NIKON_FileNumberSequence = 0xD06C const DPC_NIKON_ControlPanelFinderRearControl = 0xD06D const DPC_NIKON_ControlPanelFinderViewfinder = 0xD06E const DPC_NIKON_D7Illumination = 0xD06F const DPC_NIKON_NrHighISO = 0xD070 const DPC_NIKON_SHSET_CH_GUID_DISP = 0xD071 const DPC_NIKON_ArtistName = 0xD072 const DPC_NIKON_CopyrightInfo = 0xD073 const DPC_NIKON_FlashSyncSpeed = 0xD074 const DPC_NIKON_E3AAFlashMode = 0xD076 const DPC_NIKON_E4ModelingFlash = 0xD077 const DPC_NIKON_BracketOrder = 0xD07A const DPC_NIKON_BracketingSet = 0xD07C const DPC_CASIO_UNKNOWN_18 = 0xD080 const DPC_NIKON_F1CenterButtonShootingMode = 0xD080 const DPC_NIKON_CenterButtonPlaybackMode = 0xD081 const DPC_NIKON_F2Multiselector = 0xD082 const DPC_NIKON_CenterButtonZoomRatio = 0xD08B const DPC_NIKON_FunctionButton2 = 0xD08C const DPC_NIKON_AFAreaPoint = 0xD08D const DPC_NIKON_NormalAFOn = 0xD08E const DPC_NIKON_CleanImageSensor = 0xD08F const DPC_NIKON_ImageCommentString = 0xD090 const DPC_NIKON_ImageCommentEnable = 0xD091 const DPC_NIKON_ImageRotation = 0xD092 const DPC_NIKON_ManualSetLensNo = 0xD093 const DPC_NIKON_MovScreenSize = 0xD0A0 const DPC_NIKON_MovVoice = 0xD0A1 const DPC_NIKON_MovMicrophone = 0xD0A2 const DPC_NIKON_Bracketing = 0xD0C0 const DPC_NIKON_AutoExposureBracketStep = 0xD0C1 const DPC_NIKON_AutoExposureBracketProgram = 0xD0C2 const DPC_NIKON_AutoExposureBracketCount = 0xD0C3 const DPC_NIKON_WhiteBalanceBracketStep = 0xD0C4 const DPC_NIKON_WhiteBalanceBracketProgram = 0xD0C5 const DPC_NIKON_LensID = 0xD0E0 const DPC_NIKON_LensSort = 0xD0E1 const DPC_NIKON_LensType = 0xD0E2 const DPC_NIKON_FocalLengthMin = 0xD0E3 const DPC_NIKON_FocalLengthMax = 0xD0E4 const DPC_NIKON_MaxApAtMinFocalLength = 0xD0E5 const DPC_NIKON_MaxApAtMaxFocalLength = 0xD0E6 const DPC_NIKON_FinderISODisp = 0xD0F0 const DPC_NIKON_AutoOffPhoto = 0xD0F2 const DPC_NIKON_AutoOffMenu = 0xD0F3 const DPC_NIKON_AutoOffInfo = 0xD0F4 const DPC_NIKON_SelfTimerShootNum = 0xD0F5 const DPC_NIKON_VignetteCtrl = 0xD0F7 const DPC_NIKON_AutoDistortionControl = 0xD0F8 const DPC_NIKON_SceneMode = 0xD0F9 const DPC_CANON_EOS_Aperture = 0xD101 const DPC_MTP_SecureTime = 0xD101 const DPC_NIKON_ACPower = 0xD101 const DPC_CANON_EOS_ShutterSpeed = 0xD102 const DPC_MTP_DeviceCertificate = 0xD102 const DPC_NIKON_WarningStatus = 0xD102 const DPC_OLYMPUS_ResolutionMode = 0xD102 const DPC_CANON_EOS_ISOSpeed = 0xD103 const DPC_MTP_RevocationInfo = 0xD103 const DPC_OLYMPUS_FocusPriority = 0xD103 const DPC_CANON_EOS_ExpCompensation = 0xD104 const DPC_NIKON_AFLockStatus = 0xD104 const DPC_OLYMPUS_DriveMode = 0xD104 const DPC_CANON_EOS_AutoExposureMode = 0xD105 const DPC_NIKON_AELockStatus = 0xD105 const DPC_OLYMPUS_DateTimeFormat = 0xD105 const DPC_CANON_EOS_DriveMode = 0xD106 const DPC_NIKON_FVLockStatus = 0xD106 const DPC_OLYMPUS_ExposureBiasStep = 0xD106 const DPC_NIKON_AutofocusLCDTopMode2 = 0xD107 const DPC_OLYMPUS_WBMode = 0xD107 const DPC_CANON_EOS_FocusMode = 0xD108 const DPC_NIKON_AutofocusArea = 0xD108 const DPC_OLYMPUS_OneTouchWB = 0xD108 const DPC_CANON_EOS_WhiteBalance = 0xD109 const DPC_NIKON_FlexibleProgram = 0xD109 const DPC_OLYMPUS_ManualWB = 0xD109 const DPC_CANON_EOS_ColorTemperature = 0xD10A const DPC_OLYMPUS_ManualWBRBBias = 0xD10A const DPC_CANON_EOS_WhiteBalanceAdjustA = 0xD10B const DPC_OLYMPUS_CustomWB = 0xD10B const DPC_CANON_EOS_WhiteBalanceAdjustB = 0xD10C const DPC_NIKON_USBSpeed = 0xD10C const DPC_OLYMPUS_CustomWBValue = 0xD10C const DPC_CANON_EOS_WhiteBalanceXA = 0xD10D const DPC_NIKON_CCDNumber = 0xD10D const DPC_OLYMPUS_ExposureTimeEx = 0xD10D const DPC_CANON_EOS_WhiteBalanceXB = 0xD10E const DPC_NIKON_CameraOrientation = 0xD10E const DPC_OLYMPUS_BulbMode = 0xD10E const DPC_CANON_EOS_ColorSpace = 0xD10F const DPC_NIKON_GroupPtnType = 0xD10F const DPC_OLYMPUS_AntiMirrorMode = 0xD10F const DPC_CANON_EOS_PictureStyle = 0xD110 const DPC_NIKON_FNumberLock = 0xD110 const DPC_OLYMPUS_AEBracketingFrame = 0xD110 const DPC_CANON_EOS_BatteryPower = 0xD111 const DPC_OLYMPUS_AEBracketingStep = 0xD111 const DPC_CANON_EOS_BatterySelect = 0xD112 const DPC_NIKON_TVLockSetting = 0xD112 const DPC_OLYMPUS_WBBracketingFrame = 0xD112 const DPC_CANON_EOS_CameraTime = 0xD113 const DPC_NIKON_AVLockSetting = 0xD113 const DPC_OLYMPUS_WBBracketingRBRange = 0xD113 const DPC_NIKON_IllumSetting = 0xD114 const DPC_OLYMPUS_WBBracketingGMFrame = 0xD114 const DPC_CANON_EOS_Owner = 0xD115 const DPC_NIKON_FocusPointBright = 0xD115 const DPC_OLYMPUS_WBBracketingGMRange = 0xD115 const DPC_CANON_EOS_ModelID = 0xD116 const DPC_OLYMPUS_FLBracketingFrame = 0xD118 const DPC_CANON_EOS_PTPExtensionVersion = 0xD119 const DPC_OLYMPUS_FLBracketingStep = 0xD119 const DPC_CANON_EOS_DPOFVersion = 0xD11A const DPC_OLYMPUS_FlashBiasCompensation = 0xD11A const DPC_CANON_EOS_AvailableShots = 0xD11B const DPC_OLYMPUS_ManualFocusMode = 0xD11B const DPC_CANON_EOS_CaptureDestination = 0xD11C const DPC_CANON_EOS_BracketMode = 0xD11D const DPC_OLYMPUS_RawSaveMode = 0xD11D const DPC_CANON_EOS_CurrentStorage = 0xD11E const DPC_OLYMPUS_AUXLightMode = 0xD11E const DPC_CANON_EOS_CurrentFolder = 0xD11F const DPC_OLYMPUS_LensSinkMode = 0xD11F const DPC_NIKON_ExternalFlashAttached = 0xD120 const DPC_OLYMPUS_BeepStatus = 0xD120 const DPC_NIKON_ExternalFlashStatus = 0xD121 const DPC_NIKON_ExternalFlashSort = 0xD122 const DPC_OLYMPUS_ColorSpace = 0xD122 const DPC_NIKON_ExternalFlashMode = 0xD123 const DPC_OLYMPUS_ColorMatching = 0xD123 const DPC_NIKON_ExternalFlashCompensation = 0xD124 const DPC_OLYMPUS_Saturation = 0xD124 const DPC_NIKON_NewExternalFlashMode = 0xD125 const DPC_NIKON_FlashExposureCompensation = 0xD126 const DPC_OLYMPUS_NoiseReductionPattern = 0xD126 const DPC_OLYMPUS_NoiseReductionRandom = 0xD127 const DPC_OLYMPUS_ShadingMode = 0xD129 const DPC_OLYMPUS_ISOBoostMode = 0xD12A const DPC_OLYMPUS_ExposureIndexBiasStep = 0xD12B const DPC_OLYMPUS_FilterEffect = 0xD12C const DPC_OLYMPUS_ColorTune = 0xD12D const DPC_OLYMPUS_Language = 0xD12E const DPC_OLYMPUS_LanguageCode = 0xD12F const DPC_CANON_EOS_CompressionS = 0xD130 const DPC_NIKON_HDRMode = 0xD130 const DPC_OLYMPUS_RecviewMode = 0xD130 const DPC_CANON_EOS_CompressionM1 = 0xD131 const DPC_MTP_PlaysForSureID = 0xD131 const DPC_NIKON_HDRHighDynamic = 0xD131 const DPC_OLYMPUS_SleepTime = 0xD131 const DPC_CANON_EOS_CompressionM2 = 0xD132 const DPC_MTP_ZUNE_UNKNOWN2 = 0xD132 const DPC_NIKON_HDRSmoothing = 0xD132 const DPC_OLYMPUS_ManualWBGMBias = 0xD132 const DPC_CANON_EOS_CompressionL = 0xD133 const DPC_OLYMPUS_AELAFLMode = 0xD135 const DPC_OLYMPUS_AELButtonStatus = 0xD136 const DPC_OLYMPUS_CompressionSettingEx = 0xD137 const DPC_OLYMPUS_ToneMode = 0xD139 const DPC_OLYMPUS_GradationMode = 0xD13A const DPC_OLYMPUS_DevelopMode = 0xD13B const DPC_OLYMPUS_ExtendInnerFlashMode = 0xD13C const DPC_OLYMPUS_OutputDeviceMode = 0xD13D const DPC_OLYMPUS_LiveViewMode = 0xD13E const DPC_CANON_EOS_PCWhiteBalance1 = 0xD140 const DPC_NIKON_OptimizeImage = 0xD140 const DPC_OLYMPUS_LCDBacklight = 0xD140 const DPC_CANON_EOS_PCWhiteBalance2 = 0xD141 const DPC_OLYMPUS_CustomDevelop = 0xD141 const DPC_CANON_EOS_PCWhiteBalance3 = 0xD142 const DPC_NIKON_Saturation = 0xD142 const DPC_OLYMPUS_GradationAutoBias = 0xD142 const DPC_CANON_EOS_PCWhiteBalance4 = 0xD143 const DPC_NIKON_BW_FillerEffect = 0xD143 const DPC_OLYMPUS_FlashRCMode = 0xD143 const DPC_CANON_EOS_PCWhiteBalance5 = 0xD144 const DPC_NIKON_BW_Sharpness = 0xD144 const DPC_OLYMPUS_FlashRCGroupValue = 0xD144 const DPC_CANON_EOS_MWhiteBalance = 0xD145 const DPC_NIKON_BW_Contrast = 0xD145 const DPC_OLYMPUS_FlashRCChannelValue = 0xD145 const DPC_NIKON_BW_Setting_Type = 0xD146 const DPC_OLYMPUS_FlashRCFPMode = 0xD146 const DPC_OLYMPUS_FlashRCPhotoChromicMode = 0xD147 const DPC_NIKON_Slot2SaveMode = 0xD148 const DPC_OLYMPUS_FlashRCPhotoChromicBias = 0xD148 const DPC_NIKON_RawBitMode = 0xD149 const DPC_OLYMPUS_FlashRCPhotoChromicManualBias = 0xD149 const DPC_OLYMPUS_FlashRCQuantityLightLevel = 0xD14A const DPC_OLYMPUS_FocusMeteringValue = 0xD14B const DPC_OLYMPUS_ISOBracketingFrame = 0xD14C const DPC_OLYMPUS_ISOBracketingStep = 0xD14D const DPC_NIKON_ISOAutoTime = 0xD14E const DPC_OLYMPUS_BulbMFMode = 0xD14E const DPC_NIKON_FlourescentType = 0xD14F const DPC_OLYMPUS_BurstFPSValue = 0xD14F const DPC_CANON_EOS_PictureStyleStandard = 0xD150 const DPC_NIKON_TuneColourTemperature = 0xD150 const DPC_OLYMPUS_ISOAutoBaseValue = 0xD150 const DPC_CANON_EOS_PictureStylePortrait = 0xD151 const DPC_NIKON_TunePreset0 = 0xD151 const DPC_OLYMPUS_ISOAutoMaxValue = 0xD151 const DPC_CANON_EOS_PictureStyleLandscape = 0xD152 const DPC_NIKON_TunePreset1 = 0xD152 const DPC_OLYMPUS_BulbLimiterValue = 0xD152 const DPC_CANON_EOS_PictureStyleNeutral = 0xD153 const DPC_NIKON_TunePreset2 = 0xD153 const DPC_OLYMPUS_DPIMode = 0xD153 const DPC_CANON_EOS_PictureStyleFaithful = 0xD154 const DPC_NIKON_TunePreset3 = 0xD154 const DPC_OLYMPUS_DPICustomValue = 0xD154 const DPC_CANON_EOS_PictureStyleBlackWhite = 0xD155 const DPC_NIKON_TunePreset4 = 0xD155 const DPC_OLYMPUS_ResolutionValueSetting = 0xD155 const DPC_OLYMPUS_AFTargetSize = 0xD157 const DPC_OLYMPUS_LightSensorMode = 0xD158 const DPC_OLYMPUS_AEBracket = 0xD159 const DPC_OLYMPUS_WBRBBracket = 0xD15A const DPC_OLYMPUS_WBGMBracket = 0xD15B const DPC_OLYMPUS_FlashBracket = 0xD15C const DPC_OLYMPUS_ISOBracket = 0xD15D const DPC_OLYMPUS_MyModeStatus = 0xD15E const DPC_CANON_EOS_PictureStyleUserSet1 = 0xD160 const DPC_NIKON_BeepOff = 0xD160 const DPC_CANON_EOS_PictureStyleUserSet2 = 0xD161 const DPC_NIKON_AutofocusMode = 0xD161 const DPC_CANON_EOS_PictureStyleUserSet3 = 0xD162 const DPC_NIKON_AFAssist = 0xD163 const DPC_NIKON_ImageReview = 0xD165 const DPC_NIKON_AFAreaIllumination = 0xD166 const DPC_NIKON_FlashMode = 0xD167 const DPC_NIKON_FlashCommanderMode = 0xD168 const DPC_NIKON_FlashSign = 0xD169 const DPC_NIKON_ISO_Auto = 0xD16A const DPC_NIKON_RemoteTimeout = 0xD16B const DPC_NIKON_GridDisplay = 0xD16C const DPC_NIKON_FlashModeManualPower = 0xD16D const DPC_NIKON_FlashModeCommanderPower = 0xD16E const DPC_NIKON_AutoFP = 0xD16F const DPC_CANON_EOS_PictureStyleParam1 = 0xD170 const DPC_CANON_EOS_PictureStyleParam2 = 0xD171 const DPC_CANON_EOS_PictureStyleParam3 = 0xD172 const DPC_CANON_EOS_FlavorLUTParams = 0xD17f const DPC_CANON_EOS_CustomFunc1 = 0xD180 const DPC_NIKON_CSMMenu = 0xD180 const DPC_CANON_EOS_CustomFunc2 = 0xD181 const DPC_MTP_ZUNE_UNKNOWN1 = 0xD181 const DPC_MTP_Zune_UnknownVersion = 0xD181 const DPC_NIKON_WarningDisplay = 0xD181 const DPC_CANON_EOS_CustomFunc3 = 0xD182 const DPC_NIKON_BatteryCellKind = 0xD182 const DPC_CANON_EOS_CustomFunc4 = 0xD183 const DPC_NIKON_ISOAutoHiLimit = 0xD183 const DPC_CANON_EOS_CustomFunc5 = 0xD184 const DPC_NIKON_DynamicAFArea = 0xD184 const DPC_CANON_EOS_CustomFunc6 = 0xD185 const DPC_CANON_EOS_CustomFunc7 = 0xD186 const DPC_NIKON_ContinuousSpeedHigh = 0xD186 const DPC_CANON_EOS_CustomFunc8 = 0xD187 const DPC_NIKON_InfoDispSetting = 0xD187 const DPC_CANON_EOS_CustomFunc9 = 0xD188 const DPC_CANON_EOS_CustomFunc10 = 0xD189 const DPC_NIKON_PreviewButton = 0xD189 const DPC_NIKON_PreviewButton2 = 0xD18A const DPC_NIKON_AEAFLockButton2 = 0xD18B const DPC_NIKON_IndicatorDisp = 0xD18D const DPC_NIKON_CellKindPriority = 0xD18E const DPC_CANON_EOS_CustomFunc11 = 0xD18a const DPC_CANON_EOS_CustomFunc12 = 0xD18b const DPC_CANON_EOS_CustomFunc13 = 0xD18c const DPC_CANON_EOS_CustomFunc14 = 0xD18d const DPC_CANON_EOS_CustomFunc15 = 0xD18e const DPC_CANON_EOS_CustomFunc16 = 0xD18f const DPC_CANON_EOS_CustomFunc17 = 0xD190 const DPC_NIKON_BracketingFramesAndSteps = 0xD190 const DPC_CANON_EOS_CustomFunc18 = 0xD191 const DPC_CANON_EOS_CustomFunc19 = 0xD192 const DPC_NIKON_LiveViewMode = 0xD1A0 const DPC_NIKON_LiveViewDriveMode = 0xD1A1 const DPC_NIKON_LiveViewStatus = 0xD1A2 const DPC_NIKON_LiveViewImageZoomRatio = 0xD1A3 const DPC_NIKON_LiveViewProhibitCondition = 0xD1A4 const DPC_NIKON_ExposureDisplayStatus = 0xD1B0 const DPC_NIKON_ExposureIndicateStatus = 0xD1B1 const DPC_NIKON_InfoDispErrStatus = 0xD1B2 const DPC_NIKON_ExposureIndicateLightup = 0xD1B3 const DPC_NIKON_FlashOpen = 0xD1C0 const DPC_NIKON_FlashCharged = 0xD1C1 const DPC_NIKON_FlashMRepeatValue = 0xD1D0 const DPC_NIKON_FlashMRepeatCount = 0xD1D1 const DPC_NIKON_FlashMRepeatInterval = 0xD1D2 const DPC_NIKON_FlashCommandChannel = 0xD1D3 const DPC_NIKON_FlashCommandSelfMode = 0xD1D4 const DPC_NIKON_FlashCommandSelfCompensation = 0xD1D5 const DPC_NIKON_FlashCommandSelfValue = 0xD1D6 const DPC_NIKON_FlashCommandAMode = 0xD1D7 const DPC_NIKON_FlashCommandACompensation = 0xD1D8 const DPC_NIKON_FlashCommandAValue = 0xD1D9 const DPC_NIKON_FlashCommandBMode = 0xD1DA const DPC_NIKON_FlashCommandBCompensation = 0xD1DB const DPC_NIKON_FlashCommandBValue = 0xD1DC const DPC_CANON_EOS_CustomFuncEx = 0xD1a0 const DPC_CANON_EOS_MyMenu = 0xD1a1 const DPC_CANON_EOS_MyMenuList = 0xD1a2 const DPC_CANON_EOS_WftStatus = 0xD1a3 const DPC_CANON_EOS_WftInputTransmission = 0xD1a4 const DPC_CANON_EOS_HDDirectoryStructure = 0xD1a5 const DPC_CANON_EOS_BatteryInfo = 0xD1a6 const DPC_CANON_EOS_AdapterInfo = 0xD1a7 const DPC_CANON_EOS_LensStatus = 0xD1a8 const DPC_CANON_EOS_QuickReviewTime = 0xD1a9 const DPC_CANON_EOS_CardExtension = 0xD1aa const DPC_CANON_EOS_TempStatus = 0xD1ab const DPC_CANON_EOS_ShutterCounter = 0xD1ac const DPC_CANON_EOS_SpecialOption = 0xD1ad const DPC_CANON_EOS_PhotoStudioMode = 0xD1ae const DPC_CANON_EOS_SerialNumber = 0xD1af const DPC_CANON_EOS_EVFOutputDevice = 0xD1b0 const DPC_CANON_EOS_EVFMode = 0xD1b1 const DPC_CANON_EOS_DepthOfFieldPreview = 0xD1b2 const DPC_CANON_EOS_EVFSharpness = 0xD1b3 const DPC_CANON_EOS_EVFWBMode = 0xD1b4 const DPC_CANON_EOS_EVFClickWBCoeffs = 0xD1b5 const DPC_CANON_EOS_EVFColorTemp = 0xD1b6 const DPC_CANON_EOS_ExposureSimMode = 0xD1b7 const DPC_CANON_EOS_EVFRecordStatus = 0xD1b8 const DPC_CANON_EOS_LvAfSystem = 0xD1ba const DPC_CANON_EOS_MovSize = 0xD1bb const DPC_CANON_EOS_LvViewTypeSelect = 0xD1bc const DPC_CANON_EOS_Artist = 0xD1d0 const DPC_CANON_EOS_Copyright = 0xD1d1 const DPC_CANON_EOS_BracketValue = 0xD1d2 const DPC_CANON_EOS_FocusInfoEx = 0xD1d3 const DPC_CANON_EOS_DepthOfField = 0xD1d4 const DPC_CANON_EOS_Brightness = 0xD1d5 const DPC_CANON_EOS_LensAdjustParams = 0xD1d6 const DPC_CANON_EOS_EFComp = 0xD1d7 const DPC_CANON_EOS_LensName = 0xD1d8 const DPC_CANON_EOS_AEB = 0xD1d9 const DPC_CANON_EOS_StroboSetting = 0xD1da const DPC_CANON_EOS_StroboWirelessSetting = 0xD1db const DPC_CANON_EOS_StroboFiring = 0xD1dc const DPC_CANON_EOS_LensID = 0xD1dd const DPC_NIKON_ActivePicCtrlItem = 0xD200 const DPC_FUJI_ReleaseMode = 0xD201 const DPC_NIKON_ChangePicCtrlItem = 0xD201 const DPC_FUJI_FocusAreas = 0xD206 const DPC_FUJI_AELock = 0xD213 const DPC_MTP_ZUNE_UNKNOWN3 = 0xD215 const DPC_MTP_ZUNE_UNKNOWN4 = 0xD216 const DPC_FUJI_Aperture = 0xD218 const DPC_FUJI_ShutterSpeed = 0xD219 const DPC_MTP_SynchronizationPartner = 0xD401 const DPC_MTP_DeviceFriendlyName = 0xD402 const DPC_MTP_VolumeLevel = 0xD403 const DPC_MTP_DeviceIcon = 0xD405 const DPC_MTP_SessionInitiatorInfo = 0xD406 const DPC_MTP_PerceivedDeviceType = 0xD407 const DPC_MTP_PlaybackRate = 0xD410 const DPC_MTP_PlaybackObject = 0xD411 const DPC_MTP_PlaybackContainerIndex = 0xD412 const DPC_MTP_PlaybackPosition = 0xD413 const DPC_EXTENSION_MASK = 0xF000 var DPC_names = map[int]string{0x5000: "Undefined", 0x5001: "BatteryLevel", 0x5002: "FunctionalMode", 0x5003: "ImageSize", 0x5004: "CompressionSetting", 0x5005: "WhiteBalance", 0x5006: "RGBGain", 0x5007: "FNumber", 0x5008: "FocalLength", 0x5009: "FocusDistance", 0x500A: "FocusMode", 0x500B: "ExposureMeteringMode", 0x500C: "FlashMode", 0x500D: "ExposureTime", 0x500E: "ExposureProgramMode", 0x500F: "ExposureIndex", 0x5010: "ExposureBiasCompensation", 0x5011: "DateTime", 0x5012: "CaptureDelay", 0x5013: "StillCaptureMode", 0x5014: "Contrast", 0x5015: "Sharpness", 0x5016: "DigitalZoom", 0x5017: "EffectMode", 0x5018: "BurstNumber", 0x5019: "BurstInterval", 0x501A: "TimelapseNumber", 0x501B: "TimelapseInterval", 0x501C: "FocusMeteringMode", 0x501D: "UploadURL", 0x501E: "Artist", 0x501F: "CopyrightInfo", 0x5020: "SupportedStreams", 0x5021: "EnabledStreams", 0x5022: "VideoFormat", 0x5023: "VideoResolution", 0x5024: "VideoQuality", 0x5025: "VideoFrameRate", 0x5026: "VideoContrast", 0x5027: "VideoBrightness", 0x5028: "AudioFormat", 0x5029: "AudioBitrate", 0x502A: "AudioSamplingRate", 0x502B: "AudioBitPerSample", 0x502C: "AudioVolume", 0xD000: "EXTENSION", 0xD080: "CASIO_UNKNOWN_18", 0xD118: "OLYMPUS_FLBracketingFrame", 0xD127: "OLYMPUS_NoiseReductionRandom", 0xD129: "OLYMPUS_ShadingMode", 0xD12A: "OLYMPUS_ISOBoostMode", 0xD12B: "OLYMPUS_ExposureIndexBiasStep", 0xD12C: "OLYMPUS_FilterEffect", 0xD12D: "OLYMPUS_ColorTune", 0xD12E: "OLYMPUS_Language", 0xD12F: "OLYMPUS_LanguageCode", 0xD135: "OLYMPUS_AELAFLMode", 0xD136: "OLYMPUS_AELButtonStatus", 0xD137: "OLYMPUS_CompressionSettingEx", 0xD139: "OLYMPUS_ToneMode", 0xD13A: "OLYMPUS_GradationMode", 0xD13B: "OLYMPUS_DevelopMode", 0xD13C: "OLYMPUS_ExtendInnerFlashMode", 0xD13D: "OLYMPUS_OutputDeviceMode", 0xD13E: "OLYMPUS_LiveViewMode", 0xD147: "OLYMPUS_FlashRCPhotoChromicMode", 0xD14A: "OLYMPUS_FlashRCQuantityLightLevel", 0xD14B: "OLYMPUS_FocusMeteringValue", 0xD14C: "OLYMPUS_ISOBracketingFrame", 0xD14D: "OLYMPUS_ISOBracketingStep", 0xD157: "OLYMPUS_AFTargetSize", 0xD158: "OLYMPUS_LightSensorMode", 0xD159: "OLYMPUS_AEBracket", 0xD15A: "OLYMPUS_WBRBBracket", 0xD15B: "OLYMPUS_WBGMBracket", 0xD15C: "OLYMPUS_FlashBracket", 0xD15D: "OLYMPUS_ISOBracket", 0xD15E: "OLYMPUS_MyModeStatus", 0xD215: "MTP_ZUNE_UNKNOWN3", 0xD216: "MTP_ZUNE_UNKNOWN4", 0xD401: "MTP_SynchronizationPartner", 0xD402: "MTP_DeviceFriendlyName", 0xD403: "MTP_VolumeLevel", 0xD405: "MTP_DeviceIcon", 0xD406: "MTP_SessionInitiatorInfo", 0xD407: "MTP_PerceivedDeviceType", 0xD410: "MTP_PlaybackRate", 0xD411: "MTP_PlaybackObject", 0xD412: "MTP_PlaybackContainerIndex", 0xD413: "MTP_PlaybackPosition", 0xF000: "EXTENSION_MASK", } // device property form field const DPFF_None = 0x00 const DPFF_Range = 0x01 const DPFF_Enumeration = 0x02 var DPFF_names = map[int]string{0x00: "None", 0x01: "Range", 0x02: "Enumeration", } // device property get/set const DPGS_Get = 0x00 const DPGS_GetSet = 0x01 var DPGS_names = map[int]string{0x00: "Get", 0x01: "GetSet", } // data type code const DTC_UNDEF = 0x0000 const DTC_INT8 = 0x0001 const DTC_UINT8 = 0x0002 const DTC_INT16 = 0x0003 const DTC_UINT16 = 0x0004 const DTC_INT32 = 0x0005 const DTC_UINT32 = 0x0006 const DTC_INT64 = 0x0007 const DTC_UINT64 = 0x0008 const DTC_INT128 = 0x0009 const DTC_UINT128 = 0x000A const DTC_ARRAY_MASK = 0x4000 const DTC_STR = 0xFFFF var DTC_names = map[int]string{0x0000: "UNDEF", 0x0001: "INT8", 0x0002: "UINT8", 0x0003: "INT16", 0x0004: "UINT16", 0x0005: "INT32", 0x0006: "UINT32", 0x0007: "INT64", 0x0008: "UINT64", 0x0009: "INT128", 0x000A: "UINT128", 0x4000: "ARRAY_MASK", 0xFFFF: "STR", } // event code const EC_Undefined = 0x4000 const EC_CancelTransaction = 0x4001 const EC_ObjectAdded = 0x4002 const EC_ObjectRemoved = 0x4003 const EC_StoreAdded = 0x4004 const EC_StoreRemoved = 0x4005 const EC_DevicePropChanged = 0x4006 const EC_ObjectInfoChanged = 0x4007 const EC_DeviceInfoChanged = 0x4008 const EC_RequestObjectTransfer = 0x4009 const EC_StoreFull = 0x400A const EC_DeviceReset = 0x400B const EC_StorageInfoChanged = 0x400C const EC_CaptureComplete = 0x400D const EC_UnreportedStatus = 0x400E const EC_CANON_ObjectInfoChanged = 0xC008 const EC_CANON_RequestObjectTransfer = 0xC009 const EC_CANON_CameraModeChanged = 0xC00C const EC_CANON_ShutterButtonPressed = 0xC00E const EC_CANON_StartDirectTransfer = 0xC011 const EC_CANON_StopDirectTransfer = 0xC013 const EC_Nikon_ObjectAddedInSDRAM = 0xC101 const EC_Nikon_CaptureCompleteRecInSdram = 0xC102 const EC_Nikon_AdvancedTransfer = 0xC103 const EC_Nikon_PreviewImageAdded = 0xC104 const EC_CANON_EOS_RequestObjectTransferTS = 0xC1a2 const EC_MTP_ObjectPropChanged = 0xC801 const EC_MTP_ObjectPropDescChanged = 0xC802 const EC_MTP_ObjectReferencesChanged = 0xC803 const EC_CANON_EOS_RequestGetEvent = 0xc101 const EC_CANON_EOS_ObjectAddedEx = 0xc181 const EC_CANON_EOS_ObjectRemoved = 0xc182 const EC_CANON_EOS_RequestGetObjectInfoEx = 0xc183 const EC_CANON_EOS_StorageStatusChanged = 0xc184 const EC_CANON_EOS_StorageInfoChanged = 0xc185 const EC_CANON_EOS_RequestObjectTransfer = 0xc186 const EC_CANON_EOS_ObjectInfoChangedEx = 0xc187 const EC_CANON_EOS_ObjectContentChanged = 0xc188 const EC_CANON_EOS_PropValueChanged = 0xc189 const EC_CANON_EOS_AvailListChanged = 0xc18a const EC_CANON_EOS_CameraStatusChanged = 0xc18b const EC_CANON_EOS_WillSoonShutdown = 0xc18d const EC_CANON_EOS_ShutdownTimerUpdated = 0xc18e const EC_CANON_EOS_RequestCancelTransfer = 0xc18f const EC_CANON_EOS_RequestObjectTransferDT = 0xc190 const EC_CANON_EOS_RequestCancelTransferDT = 0xc191 const EC_CANON_EOS_StoreAdded = 0xc192 const EC_CANON_EOS_StoreRemoved = 0xc193 const EC_CANON_EOS_BulbExposureTime = 0xc194 const EC_CANON_EOS_RecordingTime = 0xc195 const EC_CANON_EOS_AfResult = 0xc1a3 var EC_names = map[int]string{0x4000: "Undefined", 0x4001: "CancelTransaction", 0x4002: "ObjectAdded", 0x4003: "ObjectRemoved", 0x4004: "StoreAdded", 0x4005: "StoreRemoved", 0x4006: "DevicePropChanged", 0x4007: "ObjectInfoChanged", 0x4008: "DeviceInfoChanged", 0x4009: "RequestObjectTransfer", 0x400A: "StoreFull", 0x400B: "DeviceReset", 0x400C: "StorageInfoChanged", 0x400D: "CaptureComplete", 0x400E: "UnreportedStatus", 0xC101: "Nikon_ObjectAddedInSDRAM", 0xC102: "Nikon_CaptureCompleteRecInSdram", 0xC103: "Nikon_AdvancedTransfer", 0xC104: "Nikon_PreviewImageAdded", 0xC801: "MTP_ObjectPropChanged", 0xC802: "MTP_ObjectPropDescChanged", 0xC803: "MTP_ObjectReferencesChanged", } const ERROR_TIMEOUT = 0x02FA const ERROR_CANCEL = 0x02FB const ERROR_BADPARAM = 0x02FC const ERROR_RESP_EXPECTED = 0x02FD const ERROR_DATA_EXPECTED = 0x02FE const ERROR_IO = 0x02FF var ERROR_names = map[int]string{0x02FA: "TIMEOUT", 0x02FB: "CANCEL", 0x02FC: "BADPARAM", 0x02FD: "RESP_EXPECTED", 0x02FE: "DATA_EXPECTED", 0x02FF: "IO", } const FST_Undefined = 0x0000 const FST_GenericFlat = 0x0001 const FST_GenericHierarchical = 0x0002 const FST_DCF = 0x0003 var FST_names = map[int]string{0x0000: "Undefined", 0x0001: "GenericFlat", 0x0002: "GenericHierarchical", 0x0003: "DCF", } // get object handles const GOH_ALL_ASSOCS = 0x00000000 const GOH_ALL_FORMATS = 0x00000000 const GOH_ALL_STORAGE = 0xffffffff const GOH_ROOT_PARENT = 0xffffffff var GOH_names = map[int64]string{0x00000000: "ALL_ASSOCS", 0xffffffff: "ALL_STORAGE", } const HANDLER_ROOT = 0x00000000 const HANDLER_SPECIAL = 0xffffffff var HANDLER_names = map[int64]string{0x00000000: "ROOT", 0xffffffff: "SPECIAL", } const NIKON_MaxCurvePoints = 19 var NIKON_names = map[int]string{19: "MaxCurvePoints"} // operation code const OC_Undefined = 0x1000 const OC_GetDeviceInfo = 0x1001 const OC_OpenSession = 0x1002 const OC_CloseSession = 0x1003 const OC_GetStorageIDs = 0x1004 const OC_GetStorageInfo = 0x1005 const OC_GetNumObjects = 0x1006 const OC_GetObjectHandles = 0x1007 const OC_GetObjectInfo = 0x1008 const OC_GetObject = 0x1009 const OC_GetThumb = 0x100A const OC_DeleteObject = 0x100B const OC_SendObjectInfo = 0x100C const OC_SendObject = 0x100D const OC_InitiateCapture = 0x100E const OC_FormatStore = 0x100F const OC_ResetDevice = 0x1010 const OC_SelfTest = 0x1011 const OC_SetObjectProtection = 0x1012 const OC_PowerDown = 0x1013 const OC_GetDevicePropDesc = 0x1014 const OC_GetDevicePropValue = 0x1015 const OC_SetDevicePropValue = 0x1016 const OC_ResetDevicePropValue = 0x1017 const OC_TerminateOpenCapture = 0x1018 const OC_MoveObject = 0x1019 const OC_CopyObject = 0x101A const OC_GetPartialObject = 0x101B const OC_InitiateOpenCapture = 0x101C const OC_StartEnumHandles = 0x101D const OC_EnumHandles = 0x101E const OC_StopEnumHandles = 0x101F const OC_GetVendorExtensionMaps = 0x1020 const OC_GetVendorDeviceInfo = 0x1021 const OC_GetResizedImageObject = 0x1022 const OC_GetFilesystemManifest = 0x1023 const OC_GetStreamInfo = 0x1024 const OC_GetStream = 0x1025 const OC_EXTENSION = 0x9000 const OC_CANON_GetPartialObjectInfo = 0x9001 const OC_CASIO_STILL_START = 0x9001 const OC_CANON_SetObjectArchive = 0x9002 const OC_CASIO_STILL_STOP = 0x9002 const OC_CANON_KeepDeviceOn = 0x9003 const OC_EK_GetSerial = 0x9003 const OC_CANON_LockDeviceUI = 0x9004 const OC_EK_SetSerial = 0x9004 const OC_CANON_UnlockDeviceUI = 0x9005 const OC_EK_SendFileObjectInfo = 0x9005 const OC_CANON_GetObjectHandleByName = 0x9006 const OC_EK_SendFileObject = 0x9006 const OC_NIKON_GetProfileAllData = 0x9006 const OC_CASIO_FOCUS = 0x9007 const OC_NIKON_SendProfileData = 0x9007 const OC_CANON_InitiateReleaseControl = 0x9008 const OC_EK_SetText = 0x9008 const OC_NIKON_DeleteProfile = 0x9008 const OC_CANON_TerminateReleaseControl = 0x9009 const OC_CASIO_CF_PRESS = 0x9009 const OC_NIKON_SetProfileData = 0x9009 const OC_CANON_TerminatePlaybackMode = 0x900A const OC_CASIO_CF_RELEASE = 0x900A const OC_CANON_ViewfinderOn = 0x900B const OC_CANON_ViewfinderOff = 0x900C const OC_CASIO_GET_OBJECT_INFO = 0x900C const OC_CANON_DoAeAfAwb = 0x900D const OC_CANON_GetCustomizeSpec = 0x900E const OC_CANON_GetCustomizeItemInfo = 0x900F const OC_CANON_GetCustomizeData = 0x9010 const OC_NIKON_AdvancedTransfer = 0x9010 const OC_CANON_SetCustomizeData = 0x9011 const OC_NIKON_GetFileInfoInBlock = 0x9011 const OC_CANON_GetCaptureStatus = 0x9012 const OC_CANON_CheckEvent = 0x9013 const OC_CANON_FocusLock = 0x9014 const OC_CANON_FocusUnlock = 0x9015 const OC_CANON_GetLocalReleaseParam = 0x9016 const OC_CANON_SetLocalReleaseParam = 0x9017 const OC_CANON_AskAboutPcEvf = 0x9018 const OC_CANON_SendPartialObject = 0x9019 const OC_CANON_InitiateCaptureInMemory = 0x901A const OC_CANON_GetPartialObjectEx = 0x901B const OC_CANON_SetObjectTime = 0x901C const OC_CANON_GetViewfinderImage = 0x901D const OC_CANON_GetObjectAttributes = 0x901E const OC_CANON_ChangeUSBProtocol = 0x901F const OC_CANON_GetChanges = 0x9020 const OC_CANON_GetObjectInfoEx = 0x9021 const OC_CANON_InitiateDirectTransfer = 0x9022 const OC_CANON_TerminateDirectTransfer = 0x9023 const OC_CANON_SendObjectInfoByPath = 0x9024 const OC_CASIO_SHUTTER = 0x9024 const OC_CANON_SendObjectByPath = 0x9025 const OC_CASIO_GET_OBJECT = 0x9025 const OC_CANON_InitiateDirectTansferEx = 0x9026 const OC_CASIO_GET_THUMBNAIL = 0x9026 const OC_CANON_GetAncillaryObjectHandles = 0x9027 const OC_CASIO_GET_STILL_HANDLES = 0x9027 const OC_CANON_GetTreeInfo = 0x9028 const OC_CASIO_STILL_RESET = 0x9028 const OC_CANON_GetTreeSize = 0x9029 const OC_CASIO_HALF_PRESS = 0x9029 const OC_CANON_NotifyProgress = 0x902A const OC_CASIO_HALF_RELEASE = 0x902A const OC_CANON_NotifyCancelAccepted = 0x902B const OC_CASIO_CS_PRESS = 0x902B const OC_CANON_902C = 0x902C const OC_CASIO_CS_RELEASE = 0x902C const OC_CANON_GetDirectory = 0x902D const OC_CASIO_ZOOM = 0x902D const OC_CASIO_CZ_PRESS = 0x902E const OC_CASIO_CZ_RELEASE = 0x902F const OC_CANON_SetPairingInfo = 0x9030 const OC_CANON_GetPairingInfo = 0x9031 const OC_CANON_DeletePairingInfo = 0x9032 const OC_CANON_GetMACAddress = 0x9033 const OC_CANON_SetDisplayMonitor = 0x9034 const OC_CANON_PairingComplete = 0x9035 const OC_CANON_GetWirelessMAXChannel = 0x9036 const OC_CASIO_MOVIE_START = 0x9041 const OC_CASIO_MOVIE_STOP = 0x9042 const OC_CASIO_MOVIE_PRESS = 0x9043 const OC_CASIO_MOVIE_RELEASE = 0x9044 const OC_CASIO_GET_MOVIE_HANDLES = 0x9045 const OC_CASIO_MOVIE_RESET = 0x9046 const OC_NIKON_GetLargeThumb = 0x90C4 const OC_NIKON_GetPictCtrlData = 0x90CC const OC_NIKON_SetPictCtrlData = 0x90CD const OC_NIKON_DelCstPicCtrl = 0x90CE const OC_NIKON_GetPicCtrlCapability = 0x90CF const OC_NIKON_GetDevicePTPIPInfo = 0x90E0 const OC_CANON_EOS_GetStorageIDs = 0x9101 const OC_MTP_WMDRMPD_GetSecureTimeChallenge = 0x9101 const OC_OLYMPUS_Capture = 0x9101 const OC_CANON_EOS_GetStorageInfo = 0x9102 const OC_MTP_WMDRMPD_GetSecureTimeResponse = 0x9102 const OC_CANON_EOS_GetObjectInfo = 0x9103 const OC_MTP_WMDRMPD_SetLicenseResponse = 0x9103 const OC_OLYMPUS_SelfCleaning = 0x9103 const OC_CANON_EOS_GetObject = 0x9104 const OC_MTP_WMDRMPD_GetSyncList = 0x9104 const OC_CANON_EOS_DeleteObject = 0x9105 const OC_MTP_WMDRMPD_SendMeterChallengeQuery = 0x9105 const OC_CANON_EOS_FormatStore = 0x9106 const OC_MTP_WMDRMPD_GetMeterChallenge = 0x9106 const OC_OLYMPUS_SetRGBGain = 0x9106 const OC_CANON_EOS_GetPartialObject = 0x9107 const OC_MTP_WMDRMPD_SetMeterResponse = 0x9107 const OC_OLYMPUS_SetPresetMode = 0x9107 const OC_CANON_EOS_GetDeviceInfoEx = 0x9108 const OC_MTP_WMDRMPD_CleanDataStore = 0x9108 const OC_OLYMPUS_SetWBBiasAll = 0x9108 const OC_CANON_EOS_GetObjectInfoEx = 0x9109 const OC_MTP_WMDRMPD_GetLicenseState = 0x9109 const OC_CANON_EOS_GetThumbEx = 0x910A const OC_MTP_WMDRMPD_SendWMDRMPDCommand = 0x910A const OC_CANON_EOS_SendPartialObject = 0x910B const OC_MTP_WMDRMPD_SendWMDRMPDRequest = 0x910B const OC_CANON_EOS_SetObjectAttributes = 0x910C const OC_CANON_EOS_GetObjectTime = 0x910D const OC_CANON_EOS_SetObjectTime = 0x910E const OC_CANON_EOS_RemoteRelease = 0x910F const OC_OLYMPUS_GetCameraControlMode = 0x910a const OC_OLYMPUS_SetCameraControlMode = 0x910b const OC_OLYMPUS_SetWBRGBGain = 0x910c const OC_CANON_EOS_SetDevicePropValueEx = 0x9110 const OC_CANON_EOS_GetRemoteMode = 0x9113 const OC_CANON_EOS_SetRemoteMode = 0x9114 const OC_CANON_EOS_SetEventMode = 0x9115 const OC_CANON_EOS_GetEvent = 0x9116 const OC_CANON_EOS_TransferComplete = 0x9117 const OC_CANON_EOS_CancelTransfer = 0x9118 const OC_CANON_EOS_ResetTransfer = 0x9119 const OC_CANON_EOS_PCHDDCapacity = 0x911A const OC_CANON_EOS_SetUILock = 0x911B const OC_CANON_EOS_ResetUILock = 0x911C const OC_CANON_EOS_KeepDeviceOn = 0x911D const OC_CANON_EOS_SetNullPacketMode = 0x911E const OC_CANON_EOS_UpdateFirmware = 0x911F const OC_CANON_EOS_TransferCompleteDT = 0x9120 const OC_CANON_EOS_CancelTransferDT = 0x9121 const OC_CANON_EOS_GetWftProfile = 0x9122 const OC_CANON_EOS_SetWftProfile = 0x9122 const OC_MTP_WPDWCN_ProcessWFCObject = 0x9122 const OC_CANON_EOS_SetProfileToWft = 0x9124 const OC_CANON_EOS_BulbStart = 0x9125 const OC_CANON_EOS_BulbEnd = 0x9126 const OC_CANON_EOS_RequestDevicePropValue = 0x9127 const OC_CANON_EOS_RemoteReleaseOn = 0x9128 const OC_CANON_EOS_RemoteReleaseOff = 0x9129 const OC_CANON_EOS_InitiateViewfinder = 0x9151 const OC_CANON_EOS_TerminateViewfinder = 0x9152 const OC_CANON_EOS_GetViewFinderData = 0x9153 const OC_CANON_EOS_DoAf = 0x9154 const OC_CANON_EOS_DriveLens = 0x9155 const OC_CANON_EOS_DepthOfFieldPreview = 0x9156 const OC_CANON_EOS_ClickWB = 0x9157 const OC_CANON_EOS_Zoom = 0x9158 const OC_CANON_EOS_ZoomPosition = 0x9159 const OC_CANON_EOS_SetLiveAfFrame = 0x915a const OC_CANON_EOS_AfCancel = 0x9160 const OC_MTP_AAVT_OpenMediaSession = 0x9170 const OC_MTP_AAVT_CloseMediaSession = 0x9171 const OC_MTP_AAVT_GetNextDataBlock = 0x9172 const OC_MTP_AAVT_SetCurrentTimePosition = 0x9173 const OC_MTP_WMDRMND_SendRegistrationRequest = 0x9180 const OC_MTP_WMDRMND_GetRegistrationResponse = 0x9181 const OC_MTP_WMDRMND_GetProximityChallenge = 0x9182 const OC_MTP_WMDRMND_SendProximityResponse = 0x9183 const OC_MTP_WMDRMND_SendWMDRMNDLicenseRequest = 0x9184 const OC_MTP_WMDRMND_GetWMDRMNDLicenseResponse = 0x9185 const OC_CANON_EOS_FAPIMessageTX = 0x91FE const OC_CANON_EOS_FAPIMessageRX = 0x91FF const OC_NIKON_GetPreviewImg = 0x9200 const OC_MTP_WMPPD_ReportAddedDeletedItems = 0x9201 const OC_NIKON_StartLiveView = 0x9201 const OC_MTP_WMPPD_ReportAcquiredItems = 0x9202 const OC_NIKON_EndLiveView = 0x9202 const OC_MTP_WMPPD_PlaylistObjectPref = 0x9203 const OC_NIKON_GetLiveViewImg = 0x9203 const OC_MTP_ZUNE_GETUNDEFINED001 = 0x9204 const OC_NIKON_MfDrive = 0x9204 const OC_NIKON_ChangeAfArea = 0x9205 const OC_NIKON_AfDriveCancel = 0x9206 const OC_MTP_WMDRMPD_SendWMDRMPDAppRequest = 0x9212 const OC_MTP_WMDRMPD_GetWMDRMPDAppResponse = 0x9213 const OC_MTP_WMDRMPD_EnableTrustedFilesOperations = 0x9214 const OC_MTP_WMDRMPD_DisableTrustedFilesOperations = 0x9215 const OC_MTP_WMDRMPD_EndTrustedAppSession = 0x9216 const OC_OLYMPUS_GetDeviceInfo = 0x9301 const OC_OLYMPUS_Init1 = 0x9302 const OC_OLYMPUS_SetDateTime = 0x9402 const OC_OLYMPUS_GetDateTime = 0x9482 const OC_OLYMPUS_SetCameraID = 0x9501 const OC_OLYMPUS_GetCameraID = 0x9581 const OC_MTP_GetObjectPropsSupported = 0x9801 const OC_MTP_GetObjectPropDesc = 0x9802 const OC_MTP_GetObjectPropValue = 0x9803 const OC_MTP_SetObjectPropValue = 0x9804 const OC_MTP_GetObjPropList = 0x9805 const OC_MTP_SetObjPropList = 0x9806 const OC_MTP_GetInterdependendPropdesc = 0x9807 const OC_MTP_SendObjectPropList = 0x9808 const OC_MTP_GetObjectReferences = 0x9810 const OC_MTP_SetObjectReferences = 0x9811 const OC_MTP_UpdateDeviceFirmware = 0x9812 const OC_MTP_Skip = 0x9820 const OC_CHDK = 0x9999 const OC_EXTENSION_MASK = 0xF000 var OC_names = map[int]string{0x1000: "Undefined", 0x1001: "GetDeviceInfo", 0x1002: "OpenSession", 0x1003: "CloseSession", 0x1004: "GetStorageIDs", 0x1005: "GetStorageInfo", 0x1006: "GetNumObjects", 0x1007: "GetObjectHandles", 0x1008: "GetObjectInfo", 0x1009: "GetObject", 0x100A: "GetThumb", 0x100B: "DeleteObject", 0x100C: "SendObjectInfo", 0x100D: "SendObject", 0x100E: "InitiateCapture", 0x100F: "FormatStore", 0x1010: "ResetDevice", 0x1011: "SelfTest", 0x1012: "SetObjectProtection", 0x1013: "PowerDown", 0x1014: "GetDevicePropDesc", 0x1015: "GetDevicePropValue", 0x1016: "SetDevicePropValue", 0x1017: "ResetDevicePropValue", 0x1018: "TerminateOpenCapture", 0x1019: "MoveObject", 0x101A: "CopyObject", 0x101B: "GetPartialObject", 0x101C: "InitiateOpenCapture", 0x101D: "StartEnumHandles", 0x101E: "EnumHandles", 0x101F: "StopEnumHandles", 0x1020: "GetVendorExtensionMaps", 0x1021: "GetVendorDeviceInfo", 0x1022: "GetResizedImageObject", 0x1023: "GetFilesystemManifest", 0x1024: "GetStreamInfo", 0x1025: "GetStream", 0x9000: "EXTENSION", 0x9007: "CASIO_FOCUS", 0x902E: "CASIO_CZ_PRESS", 0x902F: "CASIO_CZ_RELEASE", 0x9041: "CASIO_MOVIE_START", 0x9042: "CASIO_MOVIE_STOP", 0x9043: "CASIO_MOVIE_PRESS", 0x9044: "CASIO_MOVIE_RELEASE", 0x9045: "CASIO_GET_MOVIE_HANDLES", 0x9046: "CASIO_MOVIE_RESET", 0x9170: "MTP_AAVT_OpenMediaSession", 0x9171: "MTP_AAVT_CloseMediaSession", 0x9172: "MTP_AAVT_GetNextDataBlock", 0x9173: "MTP_AAVT_SetCurrentTimePosition", 0x9180: "MTP_WMDRMND_SendRegistrationRequest", 0x9181: "MTP_WMDRMND_GetRegistrationResponse", 0x9182: "MTP_WMDRMND_GetProximityChallenge", 0x9183: "MTP_WMDRMND_SendProximityResponse", 0x9184: "MTP_WMDRMND_SendWMDRMNDLicenseRequest", 0x9185: "MTP_WMDRMND_GetWMDRMNDLicenseResponse", 0x9201: "MTP_WMPPD_ReportAddedDeletedItems", 0x9202: "MTP_WMPPD_ReportAcquiredItems", 0x9203: "MTP_WMPPD_PlaylistObjectPref", 0x9204: "MTP_ZUNE_GETUNDEFINED001", 0x9212: "MTP_WMDRMPD_SendWMDRMPDAppRequest", 0x9213: "MTP_WMDRMPD_GetWMDRMPDAppResponse", 0x9214: "MTP_WMDRMPD_EnableTrustedFilesOperations", 0x9215: "MTP_WMDRMPD_DisableTrustedFilesOperations", 0x9216: "MTP_WMDRMPD_EndTrustedAppSession", 0x9301: "OLYMPUS_GetDeviceInfo", 0x9302: "OLYMPUS_Init1", 0x9402: "OLYMPUS_SetDateTime", 0x9482: "OLYMPUS_GetDateTime", 0x9501: "OLYMPUS_SetCameraID", 0x9581: "OLYMPUS_GetCameraID", 0x9801: "MTP_GetObjectPropsSupported", 0x9802: "MTP_GetObjectPropDesc", 0x9803: "MTP_GetObjectPropValue", 0x9804: "MTP_SetObjectPropValue", 0x9805: "MTP_GetObjPropList", 0x9806: "MTP_SetObjPropList", 0x9807: "MTP_GetInterdependendPropdesc", 0x9808: "MTP_SendObjectPropList", 0x9810: "MTP_GetObjectReferences", 0x9811: "MTP_SetObjectReferences", 0x9812: "MTP_UpdateDeviceFirmware", 0x9820: "MTP_Skip", 0x9999: "CHDK", 0xF000: "EXTENSION_MASK", } // object format code const OFC_Undefined = 0x3000 const OFC_Association = 0x3001 const OFC_Script = 0x3002 const OFC_Executable = 0x3003 const OFC_Text = 0x3004 const OFC_HTML = 0x3005 const OFC_DPOF = 0x3006 const OFC_AIFF = 0x3007 const OFC_WAV = 0x3008 const OFC_MP3 = 0x3009 const OFC_AVI = 0x300A const OFC_MPEG = 0x300B const OFC_ASF = 0x300C const OFC_Defined = 0x3800 const OFC_EXIF_JPEG = 0x3801 const OFC_TIFF_EP = 0x3802 const OFC_FlashPix = 0x3803 const OFC_BMP = 0x3804 const OFC_CIFF = 0x3805 const OFC_Undefined_0x3806 = 0x3806 const OFC_GIF = 0x3807 const OFC_JFIF = 0x3808 const OFC_PCD = 0x3809 const OFC_PICT = 0x380A const OFC_PNG = 0x380B const OFC_Undefined_0x380C = 0x380C const OFC_TIFF = 0x380D const OFC_TIFF_IT = 0x380E const OFC_JP2 = 0x380F const OFC_JPX = 0x3810 const OFC_DNG = 0x3811 const OFC_EK_M3U = 0xb002 const OFC_CANON_CRW = 0xb101 const OFC_CANON_CRW3 = 0xb103 const OFC_CANON_MOV = 0xb104 const OFC_CANON_CHDK_CRW = 0xb1ff const OFC_MTP_MediaCard = 0xb211 const OFC_MTP_MediaCardGroup = 0xb212 const OFC_MTP_Encounter = 0xb213 const OFC_MTP_EncounterBox = 0xb214 const OFC_MTP_M4A = 0xb215 const OFC_MTP_Firmware = 0xb802 const OFC_MTP_WindowsImageFormat = 0xb881 const OFC_MTP_UndefinedAudio = 0xb900 const OFC_MTP_WMA = 0xb901 const OFC_MTP_OGG = 0xb902 const OFC_MTP_AAC = 0xb903 const OFC_MTP_AudibleCodec = 0xb904 const OFC_MTP_FLAC = 0xb906 const OFC_MTP_SamsungPlaylist = 0xb909 const OFC_MTP_UndefinedVideo = 0xb980 const OFC_MTP_WMV = 0xb981 const OFC_MTP_MP4 = 0xb982 const OFC_MTP_MP2 = 0xb983 const OFC_MTP_3GP = 0xb984 const OFC_MTP_UndefinedCollection = 0xba00 const OFC_MTP_AbstractMultimediaAlbum = 0xba01 const OFC_MTP_AbstractImageAlbum = 0xba02 const OFC_MTP_AbstractAudioAlbum = 0xba03 const OFC_MTP_AbstractVideoAlbum = 0xba04 const OFC_MTP_AbstractAudioVideoPlaylist = 0xba05 const OFC_MTP_AbstractContactGroup = 0xba06 const OFC_MTP_AbstractMessageFolder = 0xba07 const OFC_MTP_AbstractChapteredProduction = 0xba08 const OFC_MTP_AbstractAudioPlaylist = 0xba09 const OFC_MTP_AbstractVideoPlaylist = 0xba0a const OFC_MTP_AbstractMediacast = 0xba0b const OFC_MTP_WPLPlaylist = 0xba10 const OFC_MTP_M3UPlaylist = 0xba11 const OFC_MTP_MPLPlaylist = 0xba12 const OFC_MTP_ASXPlaylist = 0xba13 const OFC_MTP_PLSPlaylist = 0xba14 const OFC_MTP_UndefinedDocument = 0xba80 const OFC_MTP_AbstractDocument = 0xba81 const OFC_MTP_XMLDocument = 0xba82 const OFC_MTP_MSWordDocument = 0xba83 const OFC_MTP_MHTCompiledHTMLDocument = 0xba84 const OFC_MTP_MSExcelSpreadsheetXLS = 0xba85 const OFC_MTP_MSPowerpointPresentationPPT = 0xba86 const OFC_MTP_UndefinedMessage = 0xbb00 const OFC_MTP_AbstractMessage = 0xbb01 const OFC_MTP_UndefinedContact = 0xbb80 const OFC_MTP_AbstractContact = 0xbb81 const OFC_MTP_vCard2 = 0xbb82 const OFC_MTP_vCard3 = 0xbb83 const OFC_MTP_UndefinedCalendarItem = 0xbe00 const OFC_MTP_AbstractCalendarItem = 0xbe01 const OFC_MTP_vCalendar1 = 0xbe02 const OFC_MTP_vCalendar2 = 0xbe03 const OFC_MTP_UndefinedWindowsExecutable = 0xbe80 const OFC_MTP_MediaCast = 0xbe81 const OFC_MTP_Section = 0xbe82 var OFC_names = map[int]string{0x3000: "Undefined", 0x3001: "Association", 0x3002: "Script", 0x3003: "Executable", 0x3004: "Text", 0x3005: "HTML", 0x3006: "DPOF", 0x3007: "AIFF", 0x3008: "WAV", 0x3009: "MP3", 0x300A: "AVI", 0x300B: "MPEG", 0x300C: "ASF", 0x3800: "Defined", 0x3801: "EXIF_JPEG", 0x3802: "TIFF_EP", 0x3803: "FlashPix", 0x3804: "BMP", 0x3805: "CIFF", 0x3806: "Undefined_0x3806", 0x3807: "GIF", 0x3808: "JFIF", 0x3809: "PCD", 0x380A: "PICT", 0x380B: "PNG", 0x380C: "Undefined_0x380C", 0x380D: "TIFF", 0x380E: "TIFF_IT", 0x380F: "JP2", 0x3810: "JPX", 0x3811: "DNG", 0xb211: "MTP_MediaCard", 0xb212: "MTP_MediaCardGroup", 0xb213: "MTP_Encounter", 0xb214: "MTP_EncounterBox", 0xb215: "MTP_M4A", 0xb802: "MTP_Firmware", 0xb881: "MTP_WindowsImageFormat", 0xb900: "MTP_UndefinedAudio", 0xb901: "MTP_WMA", 0xb902: "MTP_OGG", 0xb903: "MTP_AAC", 0xb904: "MTP_AudibleCodec", 0xb906: "MTP_FLAC", 0xb909: "MTP_SamsungPlaylist", 0xb980: "MTP_UndefinedVideo", 0xb981: "MTP_WMV", 0xb982: "MTP_MP4", 0xb983: "MTP_MP2", 0xb984: "MTP_3GP", 0xba00: "MTP_UndefinedCollection", 0xba01: "MTP_AbstractMultimediaAlbum", 0xba02: "MTP_AbstractImageAlbum", 0xba03: "MTP_AbstractAudioAlbum", 0xba04: "MTP_AbstractVideoAlbum", 0xba05: "MTP_AbstractAudioVideoPlaylist", 0xba06: "MTP_AbstractContactGroup", 0xba07: "MTP_AbstractMessageFolder", 0xba08: "MTP_AbstractChapteredProduction", 0xba09: "MTP_AbstractAudioPlaylist", 0xba0a: "MTP_AbstractVideoPlaylist", 0xba0b: "MTP_AbstractMediacast", 0xba10: "MTP_WPLPlaylist", 0xba11: "MTP_M3UPlaylist", 0xba12: "MTP_MPLPlaylist", 0xba13: "MTP_ASXPlaylist", 0xba14: "MTP_PLSPlaylist", 0xba80: "MTP_UndefinedDocument", 0xba81: "MTP_AbstractDocument", 0xba82: "MTP_XMLDocument", 0xba83: "MTP_MSWordDocument", 0xba84: "MTP_MHTCompiledHTMLDocument", 0xba85: "MTP_MSExcelSpreadsheetXLS", 0xba86: "MTP_MSPowerpointPresentationPPT", 0xbb00: "MTP_UndefinedMessage", 0xbb01: "MTP_AbstractMessage", 0xbb80: "MTP_UndefinedContact", 0xbb81: "MTP_AbstractContact", 0xbb82: "MTP_vCard2", 0xbb83: "MTP_vCard3", 0xbe00: "MTP_UndefinedCalendarItem", 0xbe01: "MTP_AbstractCalendarItem", 0xbe02: "MTP_vCalendar1", 0xbe03: "MTP_vCalendar2", 0xbe80: "MTP_UndefinedWindowsExecutable", 0xbe81: "MTP_MediaCast", 0xbe82: "MTP_Section", } // object property code const OPC_WirelessConfigurationFile = 0xB104 const OPC_BuyFlag = 0xD901 const OPC_StorageID = 0xDC01 const OPC_ObjectFormat = 0xDC02 const OPC_ProtectionStatus = 0xDC03 const OPC_ObjectSize = 0xDC04 const OPC_AssociationType = 0xDC05 const OPC_AssociationDesc = 0xDC06 const OPC_ObjectFileName = 0xDC07 const OPC_DateCreated = 0xDC08 const OPC_DateModified = 0xDC09 const OPC_Keywords = 0xDC0A const OPC_ParentObject = 0xDC0B const OPC_AllowedFolderContents = 0xDC0C const OPC_Hidden = 0xDC0D const OPC_SystemObject = 0xDC0E const OPC_PersistantUniqueObjectIdentifier = 0xDC41 const OPC_SyncID = 0xDC42 const OPC_PropertyBag = 0xDC43 const OPC_Name = 0xDC44 const OPC_CreatedBy = 0xDC45 const OPC_Artist = 0xDC46 const OPC_DateAuthored = 0xDC47 const OPC_Description = 0xDC48 const OPC_URLReference = 0xDC49 const OPC_LanguageLocale = 0xDC4A const OPC_CopyrightInformation = 0xDC4B const OPC_Source = 0xDC4C const OPC_OriginLocation = 0xDC4D const OPC_DateAdded = 0xDC4E const OPC_NonConsumable = 0xDC4F const OPC_CorruptOrUnplayable = 0xDC50 const OPC_ProducerSerialNumber = 0xDC51 const OPC_RepresentativeSampleFormat = 0xDC81 const OPC_RepresentativeSampleSize = 0xDC82 const OPC_RepresentativeSampleHeight = 0xDC83 const OPC_RepresentativeSampleWidth = 0xDC84 const OPC_RepresentativeSampleDuration = 0xDC85 const OPC_RepresentativeSampleData = 0xDC86 const OPC_Width = 0xDC87 const OPC_Height = 0xDC88 const OPC_Duration = 0xDC89 const OPC_Rating = 0xDC8A const OPC_Track = 0xDC8B const OPC_Genre = 0xDC8C const OPC_Credits = 0xDC8D const OPC_Lyrics = 0xDC8E const OPC_SubscriptionContentID = 0xDC8F const OPC_ProducedBy = 0xDC90 const OPC_UseCount = 0xDC91 const OPC_SkipCount = 0xDC92 const OPC_LastAccessed = 0xDC93 const OPC_ParentalRating = 0xDC94 const OPC_MetaGenre = 0xDC95 const OPC_Composer = 0xDC96 const OPC_EffectiveRating = 0xDC97 const OPC_Subtitle = 0xDC98 const OPC_OriginalReleaseDate = 0xDC99 const OPC_AlbumName = 0xDC9A const OPC_AlbumArtist = 0xDC9B const OPC_Mood = 0xDC9C const OPC_DRMStatus = 0xDC9D const OPC_SubDescription = 0xDC9E const OPC_IsCropped = 0xDCD1 const OPC_IsColorCorrected = 0xDCD2 const OPC_ImageBitDepth = 0xDCD3 const OPC_Fnumber = 0xDCD4 const OPC_ExposureTime = 0xDCD5 const OPC_ExposureIndex = 0xDCD6 const OPC_DisplayName = 0xDCE0 const OPC_BodyText = 0xDCE1 const OPC_Subject = 0xDCE2 const OPC_Priority = 0xDCE3 const OPC_GivenName = 0xDD00 const OPC_MiddleNames = 0xDD01 const OPC_FamilyName = 0xDD02 const OPC_Prefix = 0xDD03 const OPC_Suffix = 0xDD04 const OPC_PhoneticGivenName = 0xDD05 const OPC_PhoneticFamilyName = 0xDD06 const OPC_EmailPrimary = 0xDD07 const OPC_EmailPersonal1 = 0xDD08 const OPC_EmailPersonal2 = 0xDD09 const OPC_EmailBusiness1 = 0xDD0A const OPC_EmailBusiness2 = 0xDD0B const OPC_EmailOthers = 0xDD0C const OPC_PhoneNumberPrimary = 0xDD0D const OPC_PhoneNumberPersonal = 0xDD0E const OPC_PhoneNumberPersonal2 = 0xDD0F const OPC_PhoneNumberBusiness = 0xDD10 const OPC_PhoneNumberBusiness2 = 0xDD11 const OPC_PhoneNumberMobile = 0xDD12 const OPC_PhoneNumberMobile2 = 0xDD13 const OPC_FaxNumberPrimary = 0xDD14 const OPC_FaxNumberPersonal = 0xDD15 const OPC_FaxNumberBusiness = 0xDD16 const OPC_PagerNumber = 0xDD17 const OPC_PhoneNumberOthers = 0xDD18 const OPC_PrimaryWebAddress = 0xDD19 const OPC_PersonalWebAddress = 0xDD1A const OPC_BusinessWebAddress = 0xDD1B const OPC_InstantMessengerAddress = 0xDD1C const OPC_InstantMessengerAddress2 = 0xDD1D const OPC_InstantMessengerAddress3 = 0xDD1E const OPC_PostalAddressPersonalFull = 0xDD1F const OPC_PostalAddressPersonalFullLine1 = 0xDD20 const OPC_PostalAddressPersonalFullLine2 = 0xDD21 const OPC_PostalAddressPersonalFullCity = 0xDD22 const OPC_PostalAddressPersonalFullRegion = 0xDD23 const OPC_PostalAddressPersonalFullPostalCode = 0xDD24 const OPC_PostalAddressPersonalFullCountry = 0xDD25 const OPC_PostalAddressBusinessFull = 0xDD26 const OPC_PostalAddressBusinessLine1 = 0xDD27 const OPC_PostalAddressBusinessLine2 = 0xDD28 const OPC_PostalAddressBusinessCity = 0xDD29 const OPC_PostalAddressBusinessRegion = 0xDD2A const OPC_PostalAddressBusinessPostalCode = 0xDD2B const OPC_PostalAddressBusinessCountry = 0xDD2C const OPC_PostalAddressOtherFull = 0xDD2D const OPC_PostalAddressOtherLine1 = 0xDD2E const OPC_PostalAddressOtherLine2 = 0xDD2F const OPC_PostalAddressOtherCity = 0xDD30 const OPC_PostalAddressOtherRegion = 0xDD31 const OPC_PostalAddressOtherPostalCode = 0xDD32 const OPC_PostalAddressOtherCountry = 0xDD33 const OPC_OrganizationName = 0xDD34 const OPC_PhoneticOrganizationName = 0xDD35 const OPC_Role = 0xDD36 const OPC_Birthdate = 0xDD37 const OPC_MessageTo = 0xDD40 const OPC_MessageCC = 0xDD41 const OPC_MessageBCC = 0xDD42 const OPC_MessageRead = 0xDD43 const OPC_MessageReceivedTime = 0xDD44 const OPC_MessageSender = 0xDD45 const OPC_ActivityBeginTime = 0xDD50 const OPC_ActivityEndTime = 0xDD51 const OPC_ActivityLocation = 0xDD52 const OPC_ActivityRequiredAttendees = 0xDD54 const OPC_ActivityOptionalAttendees = 0xDD55 const OPC_ActivityResources = 0xDD56 const OPC_ActivityAccepted = 0xDD57 const OPC_Owner = 0xDD5D const OPC_Editor = 0xDD5E const OPC_Webmaster = 0xDD5F const OPC_URLSource = 0xDD60 const OPC_URLDestination = 0xDD61 const OPC_TimeBookmark = 0xDD62 const OPC_ObjectBookmark = 0xDD63 const OPC_ByteBookmark = 0xDD64 const OPC_LastBuildDate = 0xDD70 const OPC_TimetoLive = 0xDD71 const OPC_MediaGUID = 0xDD72 const OPC_TotalBitRate = 0xDE91 const OPC_BitRateType = 0xDE92 const OPC_SampleRate = 0xDE93 const OPC_NumberOfChannels = 0xDE94 const OPC_AudioBitDepth = 0xDE95 const OPC_ScanDepth = 0xDE97 const OPC_AudioWAVECodec = 0xDE99 const OPC_AudioBitRate = 0xDE9A const OPC_VideoFourCCCodec = 0xDE9B const OPC_VideoBitRate = 0xDE9C const OPC_FramesPerThousandSeconds = 0xDE9D const OPC_KeyFrameDistance = 0xDE9E const OPC_BufferSize = 0xDE9F const OPC_EncodingQuality = 0xDEA0 const OPC_EncodingProfile = 0xDEA1 var OPC_names = map[int]string{0xB104: "WirelessConfigurationFile", 0xD901: "BuyFlag", 0xDC01: "StorageID", 0xDC02: "ObjectFormat", 0xDC03: "ProtectionStatus", 0xDC04: "ObjectSize", 0xDC05: "AssociationType", 0xDC06: "AssociationDesc", 0xDC07: "ObjectFileName", 0xDC08: "DateCreated", 0xDC09: "DateModified", 0xDC0A: "Keywords", 0xDC0B: "ParentObject", 0xDC0C: "AllowedFolderContents", 0xDC0D: "Hidden", 0xDC0E: "SystemObject", 0xDC41: "PersistantUniqueObjectIdentifier", 0xDC42: "SyncID", 0xDC43: "PropertyBag", 0xDC44: "Name", 0xDC45: "CreatedBy", 0xDC46: "Artist", 0xDC47: "DateAuthored", 0xDC48: "Description", 0xDC49: "URLReference", 0xDC4A: "LanguageLocale", 0xDC4B: "CopyrightInformation", 0xDC4C: "Source", 0xDC4D: "OriginLocation", 0xDC4E: "DateAdded", 0xDC4F: "NonConsumable", 0xDC50: "CorruptOrUnplayable", 0xDC51: "ProducerSerialNumber", 0xDC81: "RepresentativeSampleFormat", 0xDC82: "RepresentativeSampleSize", 0xDC83: "RepresentativeSampleHeight", 0xDC84: "RepresentativeSampleWidth", 0xDC85: "RepresentativeSampleDuration", 0xDC86: "RepresentativeSampleData", 0xDC87: "Width", 0xDC88: "Height", 0xDC89: "Duration", 0xDC8A: "Rating", 0xDC8B: "Track", 0xDC8C: "Genre", 0xDC8D: "Credits", 0xDC8E: "Lyrics", 0xDC8F: "SubscriptionContentID", 0xDC90: "ProducedBy", 0xDC91: "UseCount", 0xDC92: "SkipCount", 0xDC93: "LastAccessed", 0xDC94: "ParentalRating", 0xDC95: "MetaGenre", 0xDC96: "Composer", 0xDC97: "EffectiveRating", 0xDC98: "Subtitle", 0xDC99: "OriginalReleaseDate", 0xDC9A: "AlbumName", 0xDC9B: "AlbumArtist", 0xDC9C: "Mood", 0xDC9D: "DRMStatus", 0xDC9E: "SubDescription", 0xDCD1: "IsCropped", 0xDCD2: "IsColorCorrected", 0xDCD3: "ImageBitDepth", 0xDCD4: "Fnumber", 0xDCD5: "ExposureTime", 0xDCD6: "ExposureIndex", 0xDCE0: "DisplayName", 0xDCE1: "BodyText", 0xDCE2: "Subject", 0xDCE3: "Priority", 0xDD00: "GivenName", 0xDD01: "MiddleNames", 0xDD02: "FamilyName", 0xDD03: "Prefix", 0xDD04: "Suffix", 0xDD05: "PhoneticGivenName", 0xDD06: "PhoneticFamilyName", 0xDD07: "EmailPrimary", 0xDD08: "EmailPersonal1", 0xDD09: "EmailPersonal2", 0xDD0A: "EmailBusiness1", 0xDD0B: "EmailBusiness2", 0xDD0C: "EmailOthers", 0xDD0D: "PhoneNumberPrimary", 0xDD0E: "PhoneNumberPersonal", 0xDD0F: "PhoneNumberPersonal2", 0xDD10: "PhoneNumberBusiness", 0xDD11: "PhoneNumberBusiness2", 0xDD12: "PhoneNumberMobile", 0xDD13: "PhoneNumberMobile2", 0xDD14: "FaxNumberPrimary", 0xDD15: "FaxNumberPersonal", 0xDD16: "FaxNumberBusiness", 0xDD17: "PagerNumber", 0xDD18: "PhoneNumberOthers", 0xDD19: "PrimaryWebAddress", 0xDD1A: "PersonalWebAddress", 0xDD1B: "BusinessWebAddress", 0xDD1C: "InstantMessengerAddress", 0xDD1D: "InstantMessengerAddress2", 0xDD1E: "InstantMessengerAddress3", 0xDD1F: "PostalAddressPersonalFull", 0xDD20: "PostalAddressPersonalFullLine1", 0xDD21: "PostalAddressPersonalFullLine2", 0xDD22: "PostalAddressPersonalFullCity", 0xDD23: "PostalAddressPersonalFullRegion", 0xDD24: "PostalAddressPersonalFullPostalCode", 0xDD25: "PostalAddressPersonalFullCountry", 0xDD26: "PostalAddressBusinessFull", 0xDD27: "PostalAddressBusinessLine1", 0xDD28: "PostalAddressBusinessLine2", 0xDD29: "PostalAddressBusinessCity", 0xDD2A: "PostalAddressBusinessRegion", 0xDD2B: "PostalAddressBusinessPostalCode", 0xDD2C: "PostalAddressBusinessCountry", 0xDD2D: "PostalAddressOtherFull", 0xDD2E: "PostalAddressOtherLine1", 0xDD2F: "PostalAddressOtherLine2", 0xDD30: "PostalAddressOtherCity", 0xDD31: "PostalAddressOtherRegion", 0xDD32: "PostalAddressOtherPostalCode", 0xDD33: "PostalAddressOtherCountry", 0xDD34: "OrganizationName", 0xDD35: "PhoneticOrganizationName", 0xDD36: "Role", 0xDD37: "Birthdate", 0xDD40: "MessageTo", 0xDD41: "MessageCC", 0xDD42: "MessageBCC", 0xDD43: "MessageRead", 0xDD44: "MessageReceivedTime", 0xDD45: "MessageSender", 0xDD50: "ActivityBeginTime", 0xDD51: "ActivityEndTime", 0xDD52: "ActivityLocation", 0xDD54: "ActivityRequiredAttendees", 0xDD55: "ActivityOptionalAttendees", 0xDD56: "ActivityResources", 0xDD57: "ActivityAccepted", 0xDD5D: "Owner", 0xDD5E: "Editor", 0xDD5F: "Webmaster", 0xDD60: "URLSource", 0xDD61: "URLDestination", 0xDD62: "TimeBookmark", 0xDD63: "ObjectBookmark", 0xDD64: "ByteBookmark", 0xDD70: "LastBuildDate", 0xDD71: "TimetoLive", 0xDD72: "MediaGUID", 0xDE91: "TotalBitRate", 0xDE92: "BitRateType", 0xDE93: "SampleRate", 0xDE94: "NumberOfChannels", 0xDE95: "AudioBitDepth", 0xDE97: "ScanDepth", 0xDE99: "AudioWAVECodec", 0xDE9A: "AudioBitRate", 0xDE9B: "VideoFourCCCodec", 0xDE9C: "VideoBitRate", 0xDE9D: "FramesPerThousandSeconds", 0xDE9E: "KeyFrameDistance", 0xDE9F: "BufferSize", 0xDEA0: "EncodingQuality", 0xDEA1: "EncodingProfile", } const OPFF_None = 0x00 const OPFF_Range = 0x01 const OPFF_Enumeration = 0x02 const OPFF_DateTime = 0x03 const OPFF_FixedLengthArray = 0x04 const OPFF_RegularExpression = 0x05 const OPFF_ByteArray = 0x06 const OPFF_LongString = 0xFF var OPFF_names = map[int]string{0x00: "None", 0x01: "Range", 0x02: "Enumeration", 0x03: "DateTime", 0x04: "FixedLengthArray", 0x05: "RegularExpression", 0x06: "ByteArray", 0xFF: "LongString", } const PS_NoProtection = 0x0000 const PS_ReadOnly = 0x0001 const PS_MTP_ReadOnlyData = 0x8002 const PS_MTP_NonTransferableData = 0x8003 var PS_names = map[int]string{0x0000: "NoProtection", 0x0001: "ReadOnly", 0x8002: "MTP_ReadOnlyData", 0x8003: "MTP_NonTransferableData", } // return code const RC_Undefined = 0x2000 const RC_OK = 0x2001 const RC_GeneralError = 0x2002 const RC_SessionNotOpen = 0x2003 const RC_InvalidTransactionID = 0x2004 const RC_OperationNotSupported = 0x2005 const RC_ParameterNotSupported = 0x2006 const RC_IncompleteTransfer = 0x2007 const RC_InvalidStorageId = 0x2008 const RC_InvalidObjectHandle = 0x2009 const RC_DevicePropNotSupported = 0x200A const RC_InvalidObjectFormatCode = 0x200B const RC_StoreFull = 0x200C const RC_ObjectWriteProtected = 0x200D const RC_StoreReadOnly = 0x200E const RC_AccessDenied = 0x200F const RC_NoThumbnailPresent = 0x2010 const RC_SelfTestFailed = 0x2011 const RC_PartialDeletion = 0x2012 const RC_StoreNotAvailable = 0x2013 const RC_SpecificationByFormatUnsupported = 0x2014 const RC_NoValidObjectInfo = 0x2015 const RC_InvalidCodeFormat = 0x2016 const RC_UnknownVendorCode = 0x2017 const RC_CaptureAlreadyTerminated = 0x2018 const RC_DeviceBusy = 0x2019 const RC_InvalidParentObject = 0x201A const RC_InvalidDevicePropFormat = 0x201B const RC_InvalidDevicePropValue = 0x201C const RC_InvalidParameter = 0x201D const RC_SessionAlreadyOpened = 0x201E const RC_TransactionCanceled = 0x201F const RC_SpecificationOfDestinationUnsupported = 0x2020 const RC_InvalidEnumHandle = 0x2021 const RC_NoStreamEnabled = 0x2022 const RC_InvalidDataSet = 0x2023 const RC_CANON_UNKNOWN_COMMAND = 0xA001 const RC_EK_FilenameRequired = 0xA001 const RC_NIKON_HardwareError = 0xA001 const RC_EK_FilenameConflicts = 0xA002 const RC_NIKON_OutOfFocus = 0xA002 const RC_EK_FilenameInvalid = 0xA003 const RC_NIKON_ChangeCameraModeFailed = 0xA003 const RC_NIKON_InvalidStatus = 0xA004 const RC_CANON_OPERATION_REFUSED = 0xA005 const RC_NIKON_SetPropertyNotSupported = 0xA005 const RC_CANON_LENS_COVER = 0xA006 const RC_NIKON_WbResetError = 0xA006 const RC_NIKON_DustReferenceError = 0xA007 const RC_NIKON_ShutterSpeedBulb = 0xA008 const RC_CANON_A009 = 0xA009 const RC_NIKON_MirrorUpSequence = 0xA009 const RC_NIKON_CameraModeNotAdjustFNumber = 0xA00A const RC_NIKON_NotLiveView = 0xA00B const RC_NIKON_MfDriveStepEnd = 0xA00C const RC_NIKON_MfDriveStepInsufficiency = 0xA00E const RC_NIKON_AdvancedTransferCancel = 0xA022 const RC_CANON_BATTERY_LOW = 0xA101 const RC_CANON_NOT_READY = 0xA102 const RC_MTP_Invalid_WFC_Syntax = 0xA121 const RC_MTP_WFC_Version_Not_Supported = 0xA122 const RC_MTP_Media_Session_Limit_Reached = 0xA171 const RC_MTP_No_More_Data = 0xA172 const RC_MTP_Undefined = 0xA800 const RC_MTP_Invalid_ObjectPropCode = 0xA801 const RC_MTP_Invalid_ObjectProp_Format = 0xA802 const RC_MTP_Invalid_ObjectProp_Value = 0xA803 const RC_MTP_Invalid_ObjectReference = 0xA804 const RC_MTP_Invalid_Dataset = 0xA806 const RC_MTP_Specification_By_Group_Unsupported = 0xA807 const RC_MTP_Specification_By_Depth_Unsupported = 0xA808 const RC_MTP_Object_Too_Large = 0xA809 const RC_MTP_ObjectProp_Not_Supported = 0xA80A var RC_names = map[int]string{0x2000: "Undefined", 0x2001: "OK", 0x2002: "GeneralError", 0x2003: "SessionNotOpen", 0x2004: "InvalidTransactionID", 0x2005: "OperationNotSupported", 0x2006: "ParameterNotSupported", 0x2007: "IncompleteTransfer", 0x2008: "InvalidStorageId", 0x2009: "InvalidObjectHandle", 0x200A: "DevicePropNotSupported", 0x200B: "InvalidObjectFormatCode", 0x200C: "StoreFull", 0x200D: "ObjectWriteProtected", 0x200E: "StoreReadOnly", 0x200F: "AccessDenied", 0x2010: "NoThumbnailPresent", 0x2011: "SelfTestFailed", 0x2012: "PartialDeletion", 0x2013: "StoreNotAvailable", 0x2014: "SpecificationByFormatUnsupported", 0x2015: "NoValidObjectInfo", 0x2016: "InvalidCodeFormat", 0x2017: "UnknownVendorCode", 0x2018: "CaptureAlreadyTerminated", 0x2019: "DeviceBusy", 0x201A: "InvalidParentObject", 0x201B: "InvalidDevicePropFormat", 0x201C: "InvalidDevicePropValue", 0x201D: "InvalidParameter", 0x201E: "SessionAlreadyOpened", 0x201F: "TransactionCanceled", 0x2020: "SpecificationOfDestinationUnsupported", 0x2021: "InvalidEnumHandle", 0x2022: "NoStreamEnabled", 0x2023: "InvalidDataSet", 0xA121: "MTP_Invalid_WFC_Syntax", 0xA122: "MTP_WFC_Version_Not_Supported", 0xA171: "MTP_Media_Session_Limit_Reached", 0xA172: "MTP_No_More_Data", 0xA800: "MTP_Undefined", 0xA801: "MTP_Invalid_ObjectPropCode", 0xA802: "MTP_Invalid_ObjectProp_Format", 0xA803: "MTP_Invalid_ObjectProp_Value", 0xA804: "MTP_Invalid_ObjectReference", 0xA806: "MTP_Invalid_Dataset", 0xA807: "MTP_Specification_By_Group_Unsupported", 0xA808: "MTP_Specification_By_Depth_Unsupported", 0xA809: "MTP_Object_Too_Large", 0xA80A: "MTP_ObjectProp_Not_Supported", } // storage const ST_Undefined = 0x0000 const ST_FixedROM = 0x0001 const ST_RemovableROM = 0x0002 const ST_FixedRAM = 0x0003 const ST_RemovableRAM = 0x0004 var ST_names = map[int]string{0x0000: "Undefined", 0x0001: "FixedROM", 0x0002: "RemovableROM", 0x0003: "FixedRAM", 0x0004: "RemovableRAM", } const USB_CONTAINER_UNDEFINED = 0x0000 const USB_CONTAINER_COMMAND = 0x0001 const USB_CONTAINER_DATA = 0x0002 const USB_CONTAINER_RESPONSE = 0x0003 const USB_CONTAINER_EVENT = 0x0004 const USB_BULK_HS_MAX_PACKET_LEN_READ = 512 const USB_BULK_HS_MAX_PACKET_LEN_WRITE = 512 var USB_names = map[int]string{0x0000: "CONTAINER_UNDEFINED", 0x0001: "CONTAINER_COMMAND", 0x0002: "CONTAINER_DATA", 0x0003: "CONTAINER_RESPONSE", 0x0004: "CONTAINER_EVENT", 512: "BULK_HS_MAX_PACKET_LEN_READ", } const VENDOR_EASTMAN_KODAK = 0x00000001 const VENDOR_SEIKO_EPSON = 0x00000002 const VENDOR_AGILENT = 0x00000003 const VENDOR_POLAROID = 0x00000004 const VENDOR_AGFA_GEVAERT = 0x00000005 const VENDOR_MICROSOFT = 0x00000006 const VENDOR_EQUINOX = 0x00000007 const VENDOR_VIEWQUEST = 0x00000008 const VENDOR_STMICROELECTRONICS = 0x00000009 const VENDOR_NIKON = 0x0000000A const VENDOR_CANON = 0x0000000B const VENDOR_FOTONATION = 0x0000000C const VENDOR_PENTAX = 0x0000000D const VENDOR_FUJI = 0x0000000E var VENDOR_names = map[int]string{0x00000001: "EASTMAN_KODAK", 0x00000002: "SEIKO_EPSON", 0x00000003: "AGILENT", 0x00000004: "POLAROID", 0x00000005: "AGFA_GEVAERT", 0x00000006: "MICROSOFT", 0x00000007: "EQUINOX", 0x00000008: "VIEWQUEST", 0x00000009: "STMICROELECTRONICS", 0x0000000A: "NIKON", 0x0000000B: "CANON", 0x0000000C: "FOTONATION", 0x0000000D: "PENTAX", 0x0000000E: "FUJI", } go-mtpfs-1.0.0/mtp/device_test.go000066400000000000000000000233431360410101500166650ustar00rootroot00000000000000package mtp // These tests require a single Android MTP device that is connected // and unlocked. import ( "bytes" "flag" "fmt" "math/rand" "strings" "testing" "time" ) // VerboseTest returns true if the testing framework is run with -v. func VerboseTest() bool { flag := flag.Lookup("test.v") return flag != nil && flag.Value.String() == "true" } func setDebug(dev *Device) { dev.DataDebug = VerboseTest() dev.MTPDebug = VerboseTest() dev.USBDebug = VerboseTest() } func TestAndroid(t *testing.T) { dev, err := SelectDevice("") if err != nil { t.Fatal(err) } defer dev.Close() setDebug(dev) info := DeviceInfo{} err = dev.GetDeviceInfo(&info) if err != nil { t.Fatal("GetDeviceInfo failed:", err) } if !strings.Contains(info.MTPExtension, "android.com:") { t.Log("no android extensions", info.MTPExtension) return } if err = dev.Configure(); err != nil { t.Fatal("Configure failed:", err) } sids := Uint32Array{} err = dev.GetStorageIDs(&sids) if err != nil { t.Fatalf("GetStorageIDs failed: %v", err) } if len(sids.Values) == 0 { t.Fatalf("No storages") } id := sids.Values[0] // 500 + 512 triggers the null read case on both sides. const testSize = 500 + 512 name := fmt.Sprintf("mtp-doodle-test%x", rand.Int31()) send := ObjectInfo{ StorageID: id, ObjectFormat: OFC_Undefined, ParentObject: 0xFFFFFFFF, Filename: name, CompressedSize: uint32(testSize), ModificationDate: time.Now(), Keywords: "bla", } data := make([]byte, testSize) for i := range data { data[i] = byte('0' + i%10) } _, _, handle, err := dev.SendObjectInfo(id, 0xFFFFFFFF, &send) if err != nil { t.Fatal("SendObjectInfo failed:", err) } else { buf := bytes.NewBuffer(data) t.Logf("Sent objectinfo handle: 0x%x\n", handle) err = dev.SendObject(buf, int64(len(data))) if err != nil { t.Log("SendObject failed:", err) } } magicStr := "life universe" magicOff := 21 magicSize := 42 err = dev.AndroidBeginEditObject(handle) if err != nil { t.Errorf("AndroidBeginEditObject: %v", err) return } err = dev.AndroidTruncate(handle, int64(magicSize)) if err != nil { t.Errorf("AndroidTruncate: %v", err) } buf := bytes.NewBufferString(magicStr) err = dev.AndroidSendPartialObject(handle, int64(magicOff), uint32(buf.Len()), buf) if err != nil { t.Errorf("AndroidSendPartialObject: %v", err) } if buf.Len() > 0 { t.Errorf("buffer not consumed") } err = dev.AndroidEndEditObject(handle) if err != nil { t.Errorf("AndroidEndEditObject: %v", err) } buf = &bytes.Buffer{} err = dev.GetObject(handle, buf) if err != nil { t.Errorf("GetObject: %v", err) } if buf.Len() != magicSize { t.Errorf("truncate failed:: %v", err) } for i := 0; i < len(magicStr); i++ { data[i+magicOff] = magicStr[i] } want := string(data[:magicSize]) if buf.String() != want { t.Errorf("read result was %q, want %q", buf.String(), want) } buf = &bytes.Buffer{} err = dev.AndroidGetPartialObject64(handle, buf, int64(magicOff), 5) if err != nil { t.Errorf("AndroidGetPartialObject64: %v", err) } want = magicStr[:5] got := buf.String() if got != want { t.Errorf("AndroidGetPartialObject64: got %q want %q", got, want) } // Try write at end of file. err = dev.AndroidBeginEditObject(handle) if err != nil { t.Errorf("AndroidBeginEditObject: %v", err) return } buf = bytes.NewBufferString(magicStr) err = dev.AndroidSendPartialObject(handle, int64(magicSize), uint32(buf.Len()), buf) if err != nil { t.Errorf("AndroidSendPartialObject: %v", err) } if buf.Len() > 0 { t.Errorf("buffer not consumed") } err = dev.AndroidEndEditObject(handle) if err != nil { t.Errorf("AndroidEndEditObject: %v", err) } buf = &bytes.Buffer{} err = dev.GetObject(handle, buf) if err != nil { t.Errorf("GetObject: %v", err) } want = string(data[:magicSize]) + magicStr got = buf.String() if got != want { t.Errorf("GetObject: got %q want %q", got, want) } err = dev.DeleteObject(handle) if err != nil { t.Fatalf("DeleteObject failed: %v", err) } } func TestDeviceProperties(t *testing.T) { dev, err := SelectDevice("") if err != nil { t.Fatal(err) } defer dev.Close() setDebug(dev) err = dev.Configure() if err != nil { t.Fatal("Configure failed:", err) } // Test non-supported device property first. var battery DevicePropDesc err = dev.GetDevicePropDesc(DPC_BatteryLevel, &battery) if err != nil { // Not an error; not supported on Android. t.Log("battery failed:", err) } else { t.Logf("%#v\n", battery) } var friendly DevicePropDesc err = dev.GetDevicePropDesc(DPC_MTP_DeviceFriendlyName, &friendly) if err != nil { t.Fatal("GetDevicePropDesc FriendlyName failed:", err) } else { t.Logf("%s: %#v\n", DPC_names[DPC_MTP_DeviceFriendlyName], friendly) } before := friendly.CurrentValue.(string) newVal := fmt.Sprintf("gomtp device_test %x", rand.Int31()) str := StringValue{newVal} err = dev.SetDevicePropValue(DPC_MTP_DeviceFriendlyName, &str) if err != nil { t.Error("SetDevicePropValue failed:", err) } str.Value = "" err = dev.GetDevicePropValue(DPC_MTP_DeviceFriendlyName, &str) if err != nil { t.Error("GetDevicePropValue failed:", err) } if str.Value != newVal { t.Logf("got %q for property value, want %q\n", str.Value, newVal) } err = dev.ResetDevicePropValue(DPC_MTP_DeviceFriendlyName) if err != nil { // For some reason, this is not supported? Returns // unknown error code 0xffff t.Log("ResetDevicePropValue failed:", err) } str = StringValue{before} err = dev.SetDevicePropValue(DPC_MTP_DeviceFriendlyName, &str) if err != nil { t.Error("SetDevicePropValue failed:", err) } // Test object properties. props := Uint16Array{} err = dev.GetObjectPropsSupported(OFC_Undefined, &props) if err != nil { t.Errorf("GetObjectPropsSupported failed: %v\n", err) } else { t.Logf("GetObjectPropsSupported (OFC_Undefined) value: %s\n", getNames(OPC_names, props.Values)) } for _, p := range props.Values { var objPropDesc ObjectPropDesc if p == OPC_PersistantUniqueObjectIdentifier { // can't deal with int128. continue } err = dev.GetObjectPropDesc(p, OFC_Undefined, &objPropDesc) name := OPC_names[int(p)] if err != nil { t.Errorf("GetObjectPropDesc(%s) failed: %v\n", name, err) } else { t.Logf("GetObjectPropDesc(%s) value: %#v %T\n", name, objPropDesc, InstantiateType(objPropDesc.DataType).Interface()) } } } func TestDeviceInfo(t *testing.T) { dev, err := SelectDevice("") if err != nil { t.Fatal(err) } defer dev.Close() setDebug(dev) i, _ := dev.ID() t.Log("device:", i) info := DeviceInfo{} err = dev.GetDeviceInfo(&info) if err != nil { t.Error("GetDeviceInfo failed:", err) } else { t.Logf("%v\n", &info) } } func TestDeviceStorage(t *testing.T) { dev, err := SelectDevice("") if err != nil { t.Fatal(err) } defer dev.Close() setDebug(dev) i, _ := dev.ID() t.Log("device:", i) info := DeviceInfo{} err = dev.GetDeviceInfo(&info) if err != nil { t.Log("GetDeviceInfo failed:", err) } else { t.Logf("device info %v\n", &info) } err = dev.Configure() if err != nil { t.Fatal("Configure failed:", err) } sids := Uint32Array{} err = dev.GetStorageIDs(&sids) if err != nil { t.Fatalf("GetStorageIDs failed: %v", err) } else { t.Logf("%#v\n", sids) } if len(sids.Values) == 0 { t.Fatalf("No storages") } id := sids.Values[0] var storageInfo StorageInfo dev.GetStorageInfo(id, &storageInfo) if err != nil { t.Fatalf("GetStorageInfo failed: %s", err) } else { t.Logf("%#v\n", storageInfo) } resp, err := dev.GetNumObjects(id, 0x0, 0x0) if err != nil { t.Fatalf("GenericRPC failed: %s", err) } else { t.Logf("num objects %#v\n", resp) } // 500 + 512 triggers the null read case on both sides. const testSize = 500 + 512 data := make([]byte, testSize) for i := range data { data[i] = byte(i % 256) } name := fmt.Sprintf("go-mtp-test%x", rand.Int31()) buf := bytes.NewBuffer(data) send := ObjectInfo{ StorageID: id, ObjectFormat: OFC_Undefined, ParentObject: 0xFFFFFFFF, Filename: name, CompressedSize: uint32(len(data)), ModificationDate: time.Now(), Keywords: "bla", } _, _, handle, err := dev.SendObjectInfo(id, 0xFFFFFFFF, &send) if err != nil { t.Fatalf("SendObjectInfo failed: %s", err) } else { t.Logf("Sent objectinfo handle: 0x%x\n", handle) err = dev.SendObject(buf, int64(len(data))) if err != nil { t.Log("SendObject failed:", err) } } hs := Uint32Array{} err = dev.GetObjectHandles(id, OFC_Undefined, //OFC_Association, 0xFFFFFFFF, &hs) if err != nil { t.Fatalf("GetObjectHandles failed: %v", err) } else { found := false for _, h := range hs.Values { if h == handle { found = true break } } if !found { t.Fatalf("uploaded file is not there") } } var backInfo ObjectInfo err = dev.GetObjectInfo(handle, &backInfo) if err != nil { t.Fatalf("GetObjectInfo failed: %v", err) } else { t.Logf("info %#v\n", backInfo) } var objSize Uint64Value err = dev.GetObjectPropValue(handle, OPC_ObjectSize, &objSize) if err != nil { t.Fatalf("GetObjectPropValue failed: %v", err) } else { t.Logf("info %#v\n", objSize) } if objSize.Value != testSize { t.Errorf("object size error: got %v, want %v", objSize.Value, testSize) } backBuf := &bytes.Buffer{} err = dev.GetObject(handle, backBuf) if err != nil { t.Fatalf("GetObject failed: %v", err) } else { if bytes.Compare(backBuf.Bytes(), data) != 0 { t.Fatalf("back comparison failed.") } } newName := fmt.Sprintf("mtp-doodle-test%x", rand.Int31()) err = dev.SetObjectPropValue(handle, OPC_ObjectFileName, &StringValue{newName}) if err != nil { t.Errorf("error renaming object: %v", err) } err = dev.DeleteObject(handle) if err != nil { t.Fatalf("DeleteObject failed: %v", err) } } go-mtpfs-1.0.0/mtp/encoding.go000066400000000000000000000221231360410101500161500ustar00rootroot00000000000000package mtp import ( "encoding/binary" "fmt" "io" "reflect" "strings" "time" "unicode/utf8" ) var byteOrder = binary.LittleEndian func decodeStr(r io.Reader) (string, error) { var szSlice [1]byte _, err := r.Read(szSlice[:]) if err != nil { return "", err } sz := int(szSlice[0]) if sz == 0 { return "", nil } utfStr := make([]byte, 4*sz) data := make([]byte, 2*sz) n, err := r.Read(data) if err != nil { return "", err } if n < len(data) { return "", fmt.Errorf("underflow") } w := 0 for i := 0; i < int(2*sz); i += 2 { cp := byteOrder.Uint16(data[i:]) w += utf8.EncodeRune(utfStr[w:], rune(cp)) } if utfStr[w-1] == 0 { w-- } s := string(utfStr[:w]) return s, nil } func encodeStr(buf []byte, s string) ([]byte, error) { if s == "" { buf[0] = 0 return buf[:1], nil } codepoints := 0 buf = append(buf[:0], 0) var rune [2]byte for _, r := range s { byteOrder.PutUint16(rune[:], uint16(r)) buf = append(buf, rune[0], rune[1]) codepoints++ } buf = append(buf, 0, 0) codepoints++ if codepoints > 254 { return nil, fmt.Errorf("string too long") } buf[0] = byte(codepoints) return buf, nil } func encodeStrField(w io.Writer, f reflect.Value) error { out := make([]byte, 2*f.Len()+4) enc, err := encodeStr(out, f.Interface().(string)) if err != nil { return err } _, err = w.Write(enc) return err } func kindSize(k reflect.Kind) int { switch k { case reflect.Int8: return 1 case reflect.Int16: return 2 case reflect.Int32: return 4 case reflect.Int64: return 8 case reflect.Uint8: return 1 case reflect.Uint16: return 2 case reflect.Uint32: return 4 default: panic(fmt.Sprintf("unknown kind %v", k)) } } var nullValue reflect.Value func decodeArray(r io.Reader, t reflect.Type) (reflect.Value, error) { var sz uint32 if err := binary.Read(r, byteOrder, &sz); err != nil { return nullValue, err } ksz := int(kindSize(t.Elem().Kind())) data := make([]byte, int(sz)*ksz) _, err := r.Read(data) if err != nil { return nullValue, err } slice := reflect.MakeSlice(t, int(sz), int(sz)) for i := 0; i < int(sz); i++ { from := data[i*ksz:] var val uint64 switch ksz { case 1: val = uint64(from[0]) case 2: val = uint64(byteOrder.Uint16(from[0:])) case 4: val = uint64(byteOrder.Uint32(from[0:])) default: panic("unimp") } slice.Index(i).SetUint(val) } return slice, nil } func encodeArray(w io.Writer, val reflect.Value) error { sz := uint32(val.Len()) if err := binary.Write(w, byteOrder, &sz); err != nil { return err } kind := val.Type().Elem().Kind() ksz := 0 if kind == reflect.Interface { ksz = int(kindSize(val.Index(0).Elem().Kind())) } else { ksz = int(kindSize(kind)) } data := make([]byte, int(sz)*ksz) for i := 0; i < int(sz); i++ { elt := val.Index(i) to := data[i*ksz:] switch kind { case reflect.Uint8: to[0] = byte(elt.Uint()) case reflect.Uint16: byteOrder.PutUint16(to, uint16(elt.Uint())) case reflect.Uint32: byteOrder.PutUint32(to, uint32(elt.Uint())) case reflect.Uint64: byteOrder.PutUint64(to, uint64(elt.Uint())) case reflect.Int8: to[0] = byte(elt.Int()) case reflect.Int16: byteOrder.PutUint16(to, uint16(elt.Int())) case reflect.Int32: byteOrder.PutUint32(to, uint32(elt.Int())) case reflect.Int64: byteOrder.PutUint64(to, uint64(elt.Int())) default: panic(fmt.Sprintf("unimplemented: encode for kind %v", kind)) } } _, err := w.Write(data) return err } var timeType = reflect.ValueOf(time.Now()).Type() const timeFormat = "20060102T150405" const timeFormatNumTZ = "20060102T150405-0700" var zeroTime = time.Time{} func encodeTime(w io.Writer, f reflect.Value) error { tptr := f.Addr().Interface().(*time.Time) s := "" if !tptr.Equal(zeroTime) { s = tptr.Format(timeFormat) } out := make([]byte, 2*len(s)+3) enc, err := encodeStr(out, s) if err != nil { return err } _, err = w.Write(enc) return err } func decodeTime(r io.Reader, f reflect.Value) error { s, err := decodeStr(r) if err != nil { return err } var t time.Time if s != "" { // Samsung has trailing dots. s = strings.TrimRight(s, ".") // Jolla Sailfish has trailing "Z". s = strings.TrimRight(s, "Z") t, err = time.Parse(timeFormat, s) if err != nil { // Nokia lumia has numTZ t, err = time.Parse(timeFormatNumTZ, s) if err != nil { return err } } } f.Set(reflect.ValueOf(t)) return nil } func decodeField(r io.Reader, f reflect.Value, typeSelector DataTypeSelector) error { if !f.CanAddr() { return fmt.Errorf("canaddr false") } if f.Type() == timeType { return decodeTime(r, f) } switch f.Kind() { case reflect.Uint8: fallthrough case reflect.Uint16: fallthrough case reflect.Uint32: fallthrough case reflect.Uint64: fallthrough case reflect.Int64: fallthrough case reflect.Int32: fallthrough case reflect.Int16: fallthrough case reflect.Int8: return binary.Read(r, byteOrder, f.Addr().Interface()) case reflect.String: s, err := decodeStr(r) if err != nil { return err } f.SetString(s) case reflect.Slice: sl, err := decodeArray(r, f.Type()) if err != nil { return err } f.Set(sl) case reflect.Interface: val := InstantiateType(typeSelector) decodeField(r, val, typeSelector) f.Set(val) default: panic(fmt.Sprintf("unimplemented kind %v", f)) } return nil } func encodeField(w io.Writer, f reflect.Value) error { if f.Type() == timeType { return encodeTime(w, f) } switch f.Kind() { case reflect.Uint8: fallthrough case reflect.Uint16: fallthrough case reflect.Uint32: fallthrough case reflect.Uint64: fallthrough case reflect.Int64: fallthrough case reflect.Int32: fallthrough case reflect.Int16: fallthrough case reflect.Int8: return binary.Write(w, byteOrder, f.Interface()) case reflect.String: return encodeStrField(w, f) case reflect.Slice: return encodeArray(w, f) case reflect.Interface: return encodeField(w, f.Elem()) default: panic(fmt.Sprintf("unimplemented kind %v", f)) } } // Decode MTP data stream into data structure. func Decode(r io.Reader, iface interface{}) error { decoder, ok := iface.(Decoder) if ok { return decoder.Decode(r) } typeSel := DataTypeSelector(0xfe) return decodeWithSelector(r, iface, typeSel) } func decodeWithSelector(r io.Reader, iface interface{}, typeSel DataTypeSelector) error { val := reflect.ValueOf(iface) if val.Kind() != reflect.Ptr { return fmt.Errorf("need ptr argument: %T", iface) } val = val.Elem() t := val.Type() for i := 0; i < t.NumField(); i++ { if err := decodeField(r, val.Field(i), typeSel); err != nil { return err } if val.Field(i).Type().Name() == "DataTypeSelector" { typeSel = val.Field(i).Interface().(DataTypeSelector) } } return nil } // Encode MTP data stream into data structure. func Encode(w io.Writer, iface interface{}) error { encoder, ok := iface.(Encoder) if ok { return encoder.Encode(w) } val := reflect.ValueOf(iface) if val.Kind() != reflect.Ptr { msg := fmt.Sprintf("need ptr argument: %T", iface) return fmt.Errorf(msg) //panic("need ptr argument: %T") } val = val.Elem() t := val.Type() for i := 0; i < t.NumField(); i++ { if err := encodeField(w, val.Field(i)); err != nil { return err } } return nil } // Instantiates an object of wanted type as addressable value. func InstantiateType(t DataTypeSelector) reflect.Value { var val interface{} switch t { case DTC_INT8: v := int8(0) val = &v case DTC_UINT8: v := int8(0) val = &v case DTC_INT16: v := int16(0) val = &v case DTC_UINT16: v := uint16(0) val = &v case DTC_INT32: v := int32(0) val = &v case DTC_UINT32: v := uint32(0) val = &v case DTC_INT64: v := int64(0) val = &v case DTC_UINT64: v := uint64(0) val = &v case DTC_INT128: v := [16]byte{} val = &v case DTC_UINT128: v := [16]byte{} val = &v case DTC_STR: s := "" val = &s default: panic(fmt.Sprintf("type not known 0x%x", t)) } return reflect.ValueOf(val).Elem() } func decodePropDescForm(r io.Reader, selector DataTypeSelector, formFlag uint8) (DataDependentType, error) { if formFlag == DPFF_Range { f := PropDescRangeForm{} err := decodeWithSelector(r, reflect.ValueOf(&f).Interface(), selector) return &f, err } else if formFlag == DPFF_Enumeration { f := PropDescEnumForm{} err := decodeWithSelector(r, reflect.ValueOf(&f).Interface(), selector) return &f, err } return nil, nil } func (pd *ObjectPropDesc) Decode(r io.Reader) error { if err := Decode(r, &pd.ObjectPropDescFixed); err != nil { return err } form, err := decodePropDescForm(r, pd.DataType, pd.FormFlag) pd.Form = form return err } func (pd *DevicePropDesc) Decode(r io.Reader) error { if err := Decode(r, &pd.DevicePropDescFixed); err != nil { return err } form, err := decodePropDescForm(r, pd.DataType, pd.FormFlag) pd.Form = form return err } func (pd *DevicePropDesc) Encode(w io.Writer) error { if err := Encode(w, &pd.DevicePropDescFixed); err != nil { return err } return Encode(w, pd.Form) } func (pd *ObjectPropDesc) Encode(w io.Writer) error { if err := Encode(w, &pd.ObjectPropDescFixed); err != nil { return err } return Encode(w, pd.Form) } go-mtpfs-1.0.0/mtp/encoding_test.go000066400000000000000000000133511360410101500172120ustar00rootroot00000000000000package mtp import ( "bytes" "fmt" "reflect" "strings" "testing" "time" ) const deviceInfoStr = `6400 0600 0000 6400 266d 0069 0063 0072 006f 0073 006f 0066 0074 002e 0063 006f 006d 003a 0020 0031 002e 0030 003b 0020 0061 006e 0064 0072 006f 0069 0064 002e 0063 006f 006d 003a 0020 0031 002e 0030 003b 0000 0000 001e 0000 0001 1002 1003 1004 1005 1006 1007 1008 1009 100a 100b 100c 100d 1014 1015 1016 1017 101b 1001 9802 9803 9804 9805 9810 9811 98c1 95c2 95c3 95c4 95c5 9504 0000 0002 4003 4004 4005 4003 0000 0001 d402 d403 5000 0000 001a 0000 0000 3001 3004 3005 3008 3009 300b 3001 3802 3804 3807 3808 380b 380d 3801 b902 b903 b982 b983 b984 b905 ba10 ba11 ba14 ba82 ba06 b905 6100 7300 7500 7300 0000 084e 0065 0078 0075 0073 0020 0037 0000 0004 3100 2e00 3000 0000 1130 0031 0035 0064 0032 0035 0036 0038 0035 0038 0034 0038 0030 0032 0031 0062 0000 00` const objInfoStr = `0100 0100 0130 0000 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 064d 0075 0073 0069 0063 0000 0000 1032 0030 0030 0030 0030 0031 0030 0031 0054 0031 0039 0031 0031 0033 0030 0000 0000` func parseHex(s string) []byte { hex := strings.Replace(s, " ", "", -1) hex = strings.Replace(hex, "\n", "", -1) buf := bytes.NewBufferString(hex) bin := make([]byte, len(hex)/2) _, err := fmt.Fscanf(buf, "%x", &bin) if err != nil { panic(err) } if buf.Len() > 0 { panic("consume") } return bin } func diffIndex(a, b []byte) error { l := len(b) if len(a) < len(b) { l = len(a) } for i := 0; i < l; i++ { if a[i] != b[i] { return fmt.Errorf("data idx 0x%x got %x want %x", i, a[i], b[i]) } } if len(a) != len(b) { return fmt.Errorf("length mismatch got %d want %d", len(a), len(b)) } return nil } func TestDecode(t *testing.T) { bin := parseHex(deviceInfoStr) var info DeviceInfo buf := bytes.NewBuffer(bin) err := Decode(buf, &info) if err != nil { t.Fatalf("unexpected decode error %v", err) } buf = &bytes.Buffer{} err = Encode(buf, &info) if err != nil { t.Fatalf("unexpected encode error %v", err) } err = diffIndex(buf.Bytes(), bin) if err != nil { t.Error(err) fmt.Println("got") hexDump(buf.Bytes()) fmt.Println("want") hexDump(bin) } } func TestDecodeObjInfo(t *testing.T) { bin := parseHex(objInfoStr) var info ObjectInfo buf := bytes.NewBuffer(bin) err := Decode(buf, &info) if err != nil { t.Fatalf("unexpected decode error %v", err) } buf = &bytes.Buffer{} err = Encode(buf, &info) if err != nil { t.Fatalf("unexpected encode error %v", err) } err = diffIndex(buf.Bytes(), bin) if err != nil { t.Error(err) fmt.Println("got") hexDump(buf.Bytes()) fmt.Println("want") hexDump(bin) } } type TestStr struct { S string } func TestEncodeStrEmpty(t *testing.T) { b := &bytes.Buffer{} err := Encode(b, &TestStr{}) if err != nil { t.Fatalf("unexpected encode error %v", err) } if string(b.Bytes()) != "\000" { t.Fatalf("string encode mismatch %q ", b.Bytes()) } } type TimeValue struct { Value time.Time } func TestDecodeTime(t *testing.T) { ts := &TestStr{"20120101T010022."} samsung := &bytes.Buffer{} if err := Encode(samsung, ts); err != nil { t.Fatalf("str encode failed: %v", err) } tv := &TimeValue{} if err := Decode(samsung, tv); err != nil { t.Fatalf("decode failed: %v", err) } buf := bytes.Buffer{} if err := Encode(&buf, tv); err != nil { t.Fatalf("encode failed: %v", err) } if err := Decode(&buf, ts); err != nil { t.Fatalf("decode failed: %v", err) } want := "20120101T010022" got := ts.S if got != want { t.Errorf("time encode/decode: got %q want %q", got, want) } } func TestVariantDPD(t *testing.T) { uint16range := PropDescRangeForm{ MinimumValue: uint16(1), MaximumValue: uint16(11), StepSize: uint16(2), } fixed := DevicePropDescFixed{ DevicePropertyCode: DPC_BatteryLevel, DataType: DTC_UINT16, GetSet: DPGS_GetSet, FactoryDefaultValue: uint16(3), CurrentValue: uint16(5), FormFlag: DPFF_Range, } dp := DevicePropDesc{fixed, &uint16range} buf := &bytes.Buffer{} err := Encode(buf, &dp) if err != nil { t.Fatalf("encode error: %v", err) } back := DevicePropDesc{} if err := Decode(buf, &back); err != nil { t.Fatalf("encode error: %v", err) } if !reflect.DeepEqual(back, dp) { t.Fatalf("reflect.DeepEqual failed: got %#v, want %#v", dp, back) } } func DisabledTestVariantOPD(t *testing.T) { uint16enum := PropDescEnumForm{ Values: []DataDependentType{uint16(1), uint16(11), uint16(2)}, } fixed := ObjectPropDescFixed{ ObjectPropertyCode: OPC_WirelessConfigurationFile, DataType: DTC_UINT16, GetSet: DPGS_GetSet, FactoryDefaultValue: uint16(3), GroupCode: 0x21, FormFlag: DPFF_Enumeration, } dp := ObjectPropDesc{fixed, &uint16enum} buf := &bytes.Buffer{} err := Encode(buf, &dp) if err != nil { t.Fatalf("encode error: %v", err) } back := ObjectPropDesc{} if err := Decode(buf, &back); err != nil { t.Fatalf("encode error: %v", err) } if !reflect.DeepEqual(back, dp) { t.Fatalf("reflect.DeepEqual failed: got %#v, want %#v", dp, back) } } func TestDecodeStr(t *testing.T) { enc := make([]byte, 100) test := "ö" out, err := encodeStr(enc, test) if err != nil { t.Fatalf("encodeStr: %v", err) } buf := bytes.NewBuffer(out) roundtrip, err := decodeStr(buf) if err != nil { t.Fatalf("encodeStr: %v", err) } if roundtrip != test { t.Fatalf("got %q, want %q", roundtrip, test) } } func TestEncodeStr(t *testing.T) { mtpStr := []byte("\x02\xe4\x00\x00\x00") str := "ä" if out, err := encodeStr(nil, str); err != nil { t.Fatalf("encodeStr: %v", err) } else if bytes.Compare(out, mtpStr) != 0 { t.Fatalf("got %q, want %q", out, mtpStr) } } go-mtpfs-1.0.0/mtp/mtp.go000066400000000000000000000312771360410101500151740ustar00rootroot00000000000000package mtp import ( "bytes" "encoding/binary" "fmt" "io" "log" "os" "strings" "time" "github.com/hanwen/usb" ) // An MTP device. type Device struct { h *usb.DeviceHandle dev *usb.Device claimed bool // split off descriptor? devDescr usb.DeviceDescriptor ifaceDescr usb.InterfaceDescriptor sendEP byte fetchEP byte eventEP byte configValue byte // In milliseconds. Defaults to 2 seconds. Timeout int // Print request/response codes. MTPDebug bool // Print USB calls. USBDebug bool // Print data as it passes over the USB connection. DataDebug bool // If set, send header in separate write. SeparateHeader bool session *sessionData } type sessionData struct { tid uint32 sid uint32 } // RCError are return codes from the Container.Code field. type RCError uint16 func (e RCError) Error() string { n, ok := RC_names[int(e)] if ok { return n } return fmt.Sprintf("RetCode %x", uint16(e)) } func (d *Device) fetchMaxPacketSize() int { return d.dev.GetMaxPacketSize(d.fetchEP) } func (d *Device) sendMaxPacketSize() int { return d.dev.GetMaxPacketSize(d.sendEP) } // Close releases the interface, and closes the device. func (d *Device) Close() error { if d.h == nil { return nil // or error? } if d.session != nil { var req, rep Container req.Code = OC_CloseSession // RunTransaction runs close, so can't use CloseSession(). if err := d.runTransaction(&req, &rep, nil, nil, 0); err != nil { err := d.h.Reset() if d.USBDebug { log.Printf("USB: Reset, err: %v", err) } } } if d.claimed { err := d.h.ReleaseInterface(d.ifaceDescr.InterfaceNumber) if d.USBDebug { log.Printf("USB: ReleaseInterface 0x%x, err: %v", d.ifaceDescr.InterfaceNumber, err) } } err := d.h.Close() d.h = nil if d.USBDebug { log.Printf("USB: Close, err: %v", err) } return err } // Done releases the libusb device reference. func (d *Device) Done() { d.dev.Unref() d.dev = nil } // Claims the USB interface of the device. func (d *Device) claim() error { if d.h == nil { return fmt.Errorf("mtp: claim: device not open") } err := d.h.ClaimInterface(d.ifaceDescr.InterfaceNumber) if d.USBDebug { log.Printf("USB: ClaimInterface 0x%x, err: %v", d.ifaceDescr.InterfaceNumber, err) } if err == nil { d.claimed = true } return err } // Open opens an MTP device. func (d *Device) Open() error { if d.Timeout == 0 { d.Timeout = 2000 } if d.h != nil { return fmt.Errorf("already open") } var err error d.h, err = d.dev.Open() if d.USBDebug { log.Printf("USB: Open, err: %v", err) } if err != nil { return err } if d.ifaceDescr.InterfaceStringIndex == 0 { // Some of the win8phones have no interface field. info := DeviceInfo{} d.GetDeviceInfo(&info) if !strings.Contains(info.MTPExtension, "microsoft/WindowsPhone") { d.Close() return fmt.Errorf("mtp: no MTP extensions in %s", info.MTPExtension) } } else { iface, err := d.h.GetStringDescriptorASCII(d.ifaceDescr.InterfaceStringIndex) if err != nil { d.Close() return err } if !strings.Contains(iface, "MTP") { d.Close() return fmt.Errorf("has no MTP in interface string") } } d.claim() return nil } // ID is the manufacturer + product + serial func (d *Device) ID() (string, error) { if d.h == nil { return "", fmt.Errorf("mtp: ID: device not open") } var ids []string for _, b := range []byte{ d.devDescr.Manufacturer, d.devDescr.Product, d.devDescr.SerialNumber} { s, err := d.h.GetStringDescriptorASCII(b) if err != nil { if d.USBDebug { log.Printf("USB: GetStringDescriptorASCII, err: %v", err) } return "", err } ids = append(ids, s) } return strings.Join(ids, " "), nil } func (d *Device) sendReq(req *Container) error { c := usbBulkContainer{ usbBulkHeader: usbBulkHeader{ Length: uint32(usbHdrLen + 4*len(req.Param)), Type: USB_CONTAINER_COMMAND, Code: req.Code, TransactionID: req.TransactionID, }, } for i := range req.Param { c.Param[i] = req.Param[i] } var wData [usbBulkLen]byte buf := bytes.NewBuffer(wData[:0]) binary.Write(buf, binary.LittleEndian, c.usbBulkHeader) if err := binary.Write(buf, binary.LittleEndian, c.Param[:len(req.Param)]); err != nil { panic(err) } d.dataPrint(d.sendEP, buf.Bytes()) _, err := d.h.BulkTransfer(d.sendEP, buf.Bytes(), d.Timeout) if err != nil { return err } return nil } // Fetches one USB packet. The header is split off, and the remainder is returned. // dest should be at least 512bytes. func (d *Device) fetchPacket(dest []byte, header *usbBulkHeader) (rest []byte, err error) { n, err := d.h.BulkTransfer(d.fetchEP, dest[:d.fetchMaxPacketSize()], d.Timeout) if n > 0 { d.dataPrint(d.fetchEP, dest[:n]) } if err != nil { return nil, err } buf := bytes.NewBuffer(dest[:n]) if err = binary.Read(buf, binary.LittleEndian, header); err != nil { return nil, err } return buf.Bytes(), nil } func (d *Device) decodeRep(h *usbBulkHeader, rest []byte, rep *Container) error { if h.Type != USB_CONTAINER_RESPONSE { return SyncError(fmt.Sprintf("got type %d (%s) in response, want CONTAINER_RESPONSE.", h.Type, USB_names[int(h.Type)])) } rep.Code = h.Code rep.TransactionID = h.TransactionID restLen := int(h.Length) - usbHdrLen if restLen > len(rest) { return fmt.Errorf("header specified 0x%x bytes, but have 0x%x", restLen, len(rest)) } nParam := restLen / 4 for i := 0; i < nParam; i++ { rep.Param = append(rep.Param, byteOrder.Uint32(rest[4*i:])) } if rep.Code != RC_OK { return RCError(rep.Code) } return nil } // SyncError is an error type that indicates lost transaction // synchronization in the protocol. type SyncError string func (s SyncError) Error() string { return string(s) } // Runs a single MTP transaction. dest and src cannot be specified at // the same time. The request should fill out Code and Param as // necessary. The response is provided here, but usually only the // return code is of interest. If the return code is an error, this // function will return an RCError instance. // // Errors that are likely to affect future transactions lead to // closing the connection. Such errors include: invalid transaction // IDs, USB errors (BUSY, IO, ACCESS etc.), and receiving data for // operations that expect no data. func (d *Device) RunTransaction(req *Container, rep *Container, dest io.Writer, src io.Reader, writeSize int64) error { if d.h == nil { return fmt.Errorf("mtp: cannot run operation %v, device is not open", OC_names[int(req.Code)]) } if err := d.runTransaction(req, rep, dest, src, writeSize); err != nil { _, ok2 := err.(SyncError) _, ok1 := err.(usb.Error) if ok1 || ok2 { log.Printf("fatal error %v; closing connection.", err) d.Close() } return err } return nil } // runTransaction is like RunTransaction, but without sanity checking // before and after the call. func (d *Device) runTransaction(req *Container, rep *Container, dest io.Writer, src io.Reader, writeSize int64) error { var finalPacket []byte if d.session != nil { req.SessionID = d.session.sid req.TransactionID = d.session.tid d.session.tid++ } if d.MTPDebug { log.Printf("MTP request %s %v\n", OC_names[int(req.Code)], req.Param) } if err := d.sendReq(req); err != nil { if d.MTPDebug { log.Printf("MTP sendreq failed: %v\n", err) } return err } if src != nil { hdr := usbBulkHeader{ Type: USB_CONTAINER_DATA, Code: req.Code, Length: uint32(writeSize), TransactionID: req.TransactionID, } _, err := d.bulkWrite(&hdr, src, writeSize) if err != nil { return err } } fetchPacketSize := d.fetchMaxPacketSize() data := make([]byte, fetchPacketSize) h := &usbBulkHeader{} rest, err := d.fetchPacket(data[:], h) if err != nil { return err } var unexpectedData bool if h.Type == USB_CONTAINER_DATA { if dest == nil { dest = &NullWriter{} unexpectedData = true if d.MTPDebug { log.Printf("MTP discarding unexpected data 0x%x bytes", h.Length) } } if d.MTPDebug { log.Printf("MTP data 0x%x bytes", h.Length) } dest.Write(rest) if len(rest)+usbHdrLen == fetchPacketSize { // If this was a full packet, read until we // have a short read. _, finalPacket, err = d.bulkRead(dest) if err != nil { return err } } h = &usbBulkHeader{} if len(finalPacket) > 0 { if d.MTPDebug { log.Printf("Reusing final packet") } rest = finalPacket finalBuf := bytes.NewBuffer(finalPacket[:len(finalPacket)]) err = binary.Read(finalBuf, binary.LittleEndian, h) } else { rest, err = d.fetchPacket(data[:], h) } } err = d.decodeRep(h, rest, rep) if d.MTPDebug { log.Printf("MTP response %s %v", getName(RC_names, int(rep.Code)), rep.Param) } if unexpectedData { return SyncError(fmt.Sprintf("unexpected data for code %s", getName(RC_names, int(req.Code)))) } if err != nil { return err } if d.session != nil && rep.TransactionID != req.TransactionID { return SyncError(fmt.Sprintf("transaction ID mismatch got %x want %x", rep.TransactionID, req.TransactionID)) } rep.SessionID = req.SessionID return nil } // Prints data going over the USB connection. func (d *Device) dataPrint(ep byte, data []byte) { if !d.DataDebug { return } dir := "send" if 0 != ep&usb.ENDPOINT_IN { dir = "recv" } fmt.Fprintf(os.Stderr, "%s: 0x%x bytes with ep 0x%x:\n", dir, len(data), ep) hexDump(data) } // The linux usb stack can send 16kb per call, according to libusb. const rwBufSize = 0x4000 // bulkWrite returns the number of non-header bytes written. func (d *Device) bulkWrite(hdr *usbBulkHeader, r io.Reader, size int64) (n int64, err error) { packetSize := d.sendMaxPacketSize() if hdr != nil { if size+usbHdrLen > 0xFFFFFFFF { hdr.Length = 0xFFFFFFFF } else { hdr.Length = uint32(size + usbHdrLen) } packetArr := make([]byte, packetSize) var packet []byte if d.SeparateHeader { packet = packetArr[:usbHdrLen] } else { packet = packetArr[:] } buf := bytes.NewBuffer(packet[:0]) binary.Write(buf, byteOrder, hdr) cpSize := int64(len(packet) - usbHdrLen) if cpSize > size { cpSize = size } _, err = io.CopyN(buf, r, cpSize) d.dataPrint(d.sendEP, buf.Bytes()) _, err = d.h.BulkTransfer(d.sendEP, buf.Bytes(), d.Timeout) if err != nil { return cpSize, err } size -= cpSize n += cpSize } var buf [rwBufSize]byte var lastTransfer int for size > 0 { var m int toread := buf[:] if int64(len(toread)) > size { toread = buf[:int(size)] } m, err = r.Read(toread) if err != nil { break } size -= int64(m) d.dataPrint(d.sendEP, buf[:m]) lastTransfer, err = d.h.BulkTransfer(d.sendEP, buf[:m], d.Timeout) n += int64(lastTransfer) if err != nil || lastTransfer == 0 { break } } if lastTransfer%packetSize == 0 { // write a short packet just to be sure. d.h.BulkTransfer(d.sendEP, buf[:0], d.Timeout) } return n, err } func (d *Device) bulkRead(w io.Writer) (n int64, lastPacket []byte, err error) { var buf [rwBufSize]byte var lastRead int for { toread := buf[:] lastRead, err = d.h.BulkTransfer(d.fetchEP, toread, d.Timeout) if err != nil { break } if lastRead > 0 { d.dataPrint(d.fetchEP, buf[:lastRead]) w, err := w.Write(buf[:lastRead]) n += int64(w) if err != nil { break } } if d.MTPDebug { log.Printf("MTP bulk read 0x%x bytes.", lastRead) } if lastRead < len(toread) { // short read. break } } packetSize := d.fetchMaxPacketSize() if lastRead%packetSize == 0 { // This should be a null packet, but on Linux + XHCI it's actually // CONTAINER_OK instead. To be liberal with the XHCI behavior, return // the final packet and inspect it in the calling function. var nullReadSize int nullReadSize, err = d.h.BulkTransfer(d.fetchEP, buf[:], d.Timeout) if d.MTPDebug { log.Printf("Expected null packet, read %d bytes", nullReadSize) } return n, buf[:nullReadSize], err } return n, buf[:0], err } // Configure is a robust version of OpenSession. On failure, it resets // the device and reopens the device and the session. func (d *Device) Configure() error { if d.h == nil { if err := d.Open(); err != nil { return err } } err := d.OpenSession() if err == RCError(RC_SessionAlreadyOpened) { // It's open, so close the session. Fortunately, this // even works without a transaction ID, at least on Android. d.CloseSession() err = d.OpenSession() } if err != nil { log.Printf("OpenSession failed: %v; attempting reset", err) if d.h != nil { d.h.Reset() } d.Close() // Give the device some rest. time.Sleep(1000 * time.Millisecond) if err := d.Open(); err != nil { return fmt.Errorf("opening after reset: %v", err) } if err := d.OpenSession(); err != nil { return fmt.Errorf("OpenSession after reset: %v", err) } } return nil } go-mtpfs-1.0.0/mtp/munge.py000066400000000000000000000037061360410101500155260ustar00rootroot00000000000000# transform ptp.h into Go. # $ python munge.py ~/vc/libmtp/src/ptp.h > mtp/const.go && gofmt -w mtp/const.go import re import sys def toInt(v): v = v.lower() if v.startswith('0x'): return int(v[2:], 16) assert not v.startswith('0') return int(v) expanation = { 'DPC': 'device property code', 'DPFF': 'device property form field', 'DPGS': 'device property get/set', 'DTC': 'data type code', 'EC': 'event code', 'EC': 'event code', 'GOH': 'get object handles', 'OC': 'operation code', 'OFC': 'object format code', 'OPC': 'object property code', 'RC': 'return code', 'ST': 'storage', } # prefix -> (name => val) data = {} for l in open(sys.argv[1]): m = re.match('^#define[ \t]+([A-Z0-9_a-z]+)[ \t]+((0x)?[0-9a-fA-F]+)$', l) if not m: continue name = m.group(1) val = m.group(2) if not name.startswith('PTP_'): continue name = name[4:] if '_' not in name : continue prefix, suffix = name.split('_', 1) if prefix not in data: data[prefix] = {} data[prefix][suffix] = val del data['CANON'] vendors = set(data['VENDOR'].keys()) # sys.stderr.write('vendors %s' % vendors) # brilliant. vendors.add('EK') print 'package mtp' print '// DO NOT EDIT : generated automatically by munge.py' for p, nv in sorted(data.items()): if p in expanation: print '\n// %s' % expanation[p] names = [] seen = set() for v, n in sorted((v, n) for n, v in nv.items()): print 'const %s_%s = %s' % (p, n, v) iv = toInt(v) if iv in seen: continue seen.add(iv) if '_' in n: prefix, _ = n.split('_', 1) if prefix in vendors: #sys.stderr.write('vendor ext %s\n' % n) continue names.append('%s: "%s",\n' % (v, n)) print '''var %s_names = map[int]string{%s}''' %( p, ''.join(names)) go-mtpfs-1.0.0/mtp/nullreader.go000066400000000000000000000005221360410101500165160ustar00rootroot00000000000000package mtp import ( "io" ) type NullReader struct{} func (nr *NullReader) Read(dest []byte) (n int, err error) { return len(dest), nil } type NullWriter struct{} func (nw *NullWriter) Write(dest []byte) (n int, err error) { return len(dest), nil } var _ = (io.Reader)((*NullReader)(nil)) var _ = (io.Writer)((*NullWriter)(nil)) go-mtpfs-1.0.0/mtp/ops.go000066400000000000000000000136561360410101500151760ustar00rootroot00000000000000package mtp import ( "bytes" "fmt" "io" "log" "math/rand" "time" ) func init() { rand.Seed(time.Now().UnixNano()) } // OpenSession opens a session, which is necesary for any command that // queries or modifies storage. It is an error to open a session // twice. If OpenSession() fails, it will not attempt to close the device. func (d *Device) OpenSession() error { if d.session != nil { return fmt.Errorf("session already open") } var req, rep Container req.Code = OC_OpenSession // avoid 0xFFFFFFFF and 0x00000000 for session IDs. sid := uint32(rand.Int31()) | 1 req.Param = []uint32{sid} // session // If opening the session fails, we want to be able to reset // the device, so don't do sanity checks afterwards. if err := d.runTransaction(&req, &rep, nil, nil, 0); err != nil { return err } d.session = &sessionData{ tid: 1, sid: sid, } return nil } // Closes a sessions. This is done automatically if the device is closed. func (d *Device) CloseSession() error { var req, rep Container req.Code = OC_CloseSession err := d.RunTransaction(&req, &rep, nil, nil, 0) d.session = nil return err } func (d *Device) GetData(req *Container, info interface{}) error { var buf bytes.Buffer var rep Container if err := d.RunTransaction(req, &rep, &buf, nil, 0); err != nil { return err } err := Decode(&buf, info) if d.MTPDebug && err == nil { log.Printf("MTP decoded %#v", info) } return err } func (d *Device) GetDeviceInfo(info *DeviceInfo) error { var req Container req.Code = OC_GetDeviceInfo return d.GetData(&req, info) } func (d *Device) GetStorageIDs(info *Uint32Array) error { var req Container req.Code = OC_GetStorageIDs return d.GetData(&req, info) } func (d *Device) GetObjectPropDesc(objPropCode, objFormatCode uint16, info *ObjectPropDesc) error { var req Container req.Code = OC_MTP_GetObjectPropDesc req.Param = []uint32{uint32(objPropCode), uint32(objFormatCode)} return d.GetData(&req, info) } func (d *Device) GetObjectPropValue(objHandle uint32, objPropCode uint16, value interface{}) error { var req Container req.Code = OC_MTP_GetObjectPropValue req.Param = []uint32{objHandle, uint32(objPropCode)} return d.GetData(&req, value) } func (d *Device) SetObjectPropValue(objHandle uint32, objPropCode uint16, value interface{}) error { var req, rep Container req.Code = OC_MTP_SetObjectPropValue req.Param = []uint32{objHandle, uint32(objPropCode)} return d.SendData(&req, &rep, value) } func (d *Device) SendData(req *Container, rep *Container, value interface{}) error { var buf bytes.Buffer if err := Encode(&buf, value); err != nil { return err } if d.MTPDebug { log.Printf("MTP encoded %#v", value) } return d.RunTransaction(req, rep, nil, &buf, int64(buf.Len())) } func (d *Device) GetObjectPropsSupported(objFormatCode uint16, props *Uint16Array) error { var req Container req.Code = OC_MTP_GetObjectPropsSupported req.Param = []uint32{uint32(objFormatCode)} return d.GetData(&req, props) } func (d *Device) GetDevicePropDesc(propCode uint16, info *DevicePropDesc) error { var req Container req.Code = OC_GetDevicePropDesc req.Param = append(req.Param, uint32(propCode)) return d.GetData(&req, info) } func (d *Device) SetDevicePropValue(propCode uint32, src interface{}) error { var req, rep Container req.Code = OC_SetDevicePropValue req.Param = []uint32{propCode} return d.SendData(&req, &rep, src) } func (d *Device) GetDevicePropValue(propCode uint32, dest interface{}) error { var req Container req.Code = OC_GetDevicePropValue req.Param = []uint32{propCode} return d.GetData(&req, dest) } func (d *Device) ResetDevicePropValue(propCode uint32) error { var req, rep Container req.Code = OC_ResetDevicePropValue req.Param = []uint32{propCode} return d.RunTransaction(&req, &rep, nil, nil, 0) } func (d *Device) GetStorageInfo(ID uint32, info *StorageInfo) error { var req Container req.Code = OC_GetStorageInfo req.Param = []uint32{ID} return d.GetData(&req, info) } func (d *Device) GetObjectHandles(storageID, objFormatCode, parent uint32, info *Uint32Array) error { var req Container req.Code = OC_GetObjectHandles req.Param = []uint32{storageID, objFormatCode, parent} return d.GetData(&req, info) } func (d *Device) GetObjectInfo(handle uint32, info *ObjectInfo) error { var req Container req.Code = OC_GetObjectInfo req.Param = []uint32{handle} return d.GetData(&req, info) } func (d *Device) GetNumObjects(storageId uint32, formatCode uint16, parent uint32) (uint32, error) { var req, rep Container req.Code = OC_GetNumObjects req.Param = []uint32{storageId, uint32(formatCode), parent} if err := d.RunTransaction(&req, &rep, nil, nil, 0); err != nil { return 0, err } return rep.Param[0], nil } func (d *Device) DeleteObject(handle uint32) error { var req, rep Container req.Code = OC_DeleteObject req.Param = []uint32{handle, 0x0} return d.RunTransaction(&req, &rep, nil, nil, 0) } func (d *Device) SendObjectInfo(wantStorageID, wantParent uint32, info *ObjectInfo) (storageID, parent, handle uint32, err error) { var req, rep Container req.Code = OC_SendObjectInfo req.Param = []uint32{wantStorageID, wantParent} if err = d.SendData(&req, &rep, info); err != nil { return } if len(rep.Param) < 3 { err = fmt.Errorf("SendObjectInfo: got %v, need 3 response parameters", rep.Param) return } return rep.Param[0], rep.Param[1], rep.Param[2], nil } func (d *Device) SendObject(r io.Reader, size int64) error { var req, rep Container req.Code = OC_SendObject return d.RunTransaction(&req, &rep, nil, r, size) } func (d *Device) GetObject(handle uint32, w io.Writer) error { var req, rep Container req.Code = OC_GetObject req.Param = []uint32{handle} return d.RunTransaction(&req, &rep, w, nil, 0) } func (d *Device) GetPartialObject(handle uint32, w io.Writer, offset uint32, size uint32) error { var req, rep Container req.Code = OC_ANDROID_GET_PARTIAL_OBJECT64 req.Param = []uint32{handle, offset, size} return d.RunTransaction(&req, &rep, w, nil, 0) } go-mtpfs-1.0.0/mtp/print.go000066400000000000000000000031251360410101500155170ustar00rootroot00000000000000package mtp import ( "fmt" "os" "strings" ) // hexDump prints hex on stderr. func hexDump(data []byte) { i := 0 for i < len(data) { next := i + 16 if next > len(data) { next = len(data) } ss := []string{} s := fmt.Sprintf("%x", data[i:next]) for j := 0; j < len(s); j += 4 { e := j + 4 if len(s) < e { e = len(s) } ss = append(ss, s[j:e]) } chars := make([]byte, next-i) for j, c := range data[i:next] { if c < 32 || c > 127 { c = '.' } chars[j] = c } fmt.Fprintf(os.Stderr, "%04x: %-40s %s\n", i, strings.Join(ss, " "), chars) i = next } } // getName extracts a single name. func getName(m map[int]string, val int) string { n, ok := m[val] if !ok { n = fmt.Sprintf("0x%x", val) } return n } // getNames extracts names from name map. func getNames(m map[int]string, vals []uint16) string { r := []string{} for _, v := range vals { n, ok := m[int(v)] if !ok { n = fmt.Sprintf("0x%x", v) } r = append(r, n) } return strings.Join(r, ", ") } func (i *DeviceInfo) String() string { return fmt.Sprintf("stdv: %x, ext: %x, mtp: v%x, mtp ext: %q fmod: %x ops: %s evs: %s "+ "dprops: %s fmts: %s capfmts: %s manu: %q model: %q devv: %q serno: %q", i.StandardVersion, i.MTPVendorExtensionID, i.MTPVersion, i.MTPExtension, i.FunctionalMode, getNames(OC_names, i.OperationsSupported), getNames(EC_names, i.EventsSupported), getNames(DPC_names, i.DevicePropertiesSupported), getNames(OFC_names, i.CaptureFormats), getNames(OFC_names, i.PlaybackFormats), i.Manufacturer, i.Model, i.DeviceVersion, i.SerialNumber) } go-mtpfs-1.0.0/mtp/select.go000066400000000000000000000061401360410101500156420ustar00rootroot00000000000000package mtp import ( "fmt" "regexp" "strings" "github.com/hanwen/usb" ) func candidateFromDeviceDescriptor(d *usb.Device) *Device { dd, err := d.GetDeviceDescriptor() if err != nil { return nil } for i := byte(0); i < dd.NumConfigurations; i++ { cdecs, err := d.GetConfigDescriptor(i) if err != nil { return nil } for _, iface := range cdecs.Interfaces { for _, a := range iface.AltSetting { if len(a.EndPoints) != 3 { continue } m := Device{} for _, s := range a.EndPoints { switch { case s.Direction() == usb.ENDPOINT_IN && s.TransferType() == usb.TRANSFER_TYPE_INTERRUPT: m.eventEP = s.EndpointAddress case s.Direction() == usb.ENDPOINT_IN && s.TransferType() == usb.TRANSFER_TYPE_BULK: m.fetchEP = s.EndpointAddress case s.Direction() == usb.ENDPOINT_OUT && s.TransferType() == usb.TRANSFER_TYPE_BULK: m.sendEP = s.EndpointAddress } } if m.sendEP > 0 && m.fetchEP > 0 && m.eventEP > 0 { m.devDescr = *dd m.ifaceDescr = a m.dev = d.Ref() m.configValue = cdecs.ConfigurationValue return &m } } } } return nil } // FindDevices finds likely MTP devices without opening them. func FindDevices(c *usb.Context) ([]*Device, error) { l, err := c.GetDeviceList() if err != nil { return nil, err } var cands []*Device for _, d := range l { cand := candidateFromDeviceDescriptor(d) if cand != nil { cands = append(cands, cand) } } l.Done() return cands, nil } // selectDevice finds a device that matches given pattern func selectDevice(cands []*Device, pattern string) (*Device, error) { re, err := regexp.Compile(pattern) if err != nil { return nil, err } var found []*Device for _, cand := range cands { if err := cand.Open(); err != nil { continue } found = append(found, cand) } if len(found) == 0 { return nil, fmt.Errorf("no MTP devices found") } cands = found found = nil var ids []string for i, cand := range cands { id, err := cand.ID() if err != nil { // TODO - close cands return nil, fmt.Errorf("Id dev %d: %v", i, err) } if pattern == "" || re.FindString(id) != "" { found = append(found, cand) ids = append(ids, id) } else { cand.Close() cand.Done() } } if len(found) == 0 { return nil, fmt.Errorf("no device matched") } if len(found) > 1 { return nil, fmt.Errorf("mtp: more than 1 device: %s", strings.Join(ids, ",")) } cand := found[0] config, err := cand.h.GetConfiguration() if err != nil { return nil, fmt.Errorf("could not get configuration of %v: %v", ids[0], err) } if config != cand.configValue { if err := cand.h.SetConfiguration(cand.configValue); err != nil { return nil, fmt.Errorf("could not set configuration of %v: %v", ids[0], err) } } return found[0], nil } // SelectDevice returns opened MTP device that matches the given pattern. func SelectDevice(pattern string) (*Device, error) { c := usb.NewContext() devs, err := FindDevices(c) if err != nil { return nil, err } if len(devs) == 0 { return nil, fmt.Errorf("no MTP devices found") } return selectDevice(devs, pattern) } go-mtpfs-1.0.0/mtp/types.go000066400000000000000000000072651360410101500155400ustar00rootroot00000000000000// The MTP packages defines data types and procedures for // communicating with an MTP device. Beyond the communication // primitive, it implements many useful operations in the file ops.go. // These may serve as an example how to implement further operations. package mtp import ( "io" "time" ) // Container is the data type for sending/receiving MTP requests and // responses. type Container struct { Code uint16 SessionID uint32 TransactionID uint32 Param []uint32 } type DeviceInfo struct { StandardVersion uint16 MTPVendorExtensionID uint32 MTPVersion uint16 MTPExtension string FunctionalMode uint16 OperationsSupported []uint16 EventsSupported []uint16 DevicePropertiesSupported []uint16 CaptureFormats []uint16 PlaybackFormats []uint16 Manufacturer string Model string DeviceVersion string SerialNumber string } // DataTypeSelector is the special type to indicate the actual type of // fields of DataDependentType. type DataTypeSelector uint16 type DataDependentType interface{} // The Decoder interface is for types that need special decoding // support, eg. the ones using DataDependentType. type Decoder interface { Decode(r io.Reader) error } type Encoder interface { Encode(w io.Writer) error } type PropDescRangeForm struct { MinimumValue DataDependentType MaximumValue DataDependentType StepSize DataDependentType } type PropDescEnumForm struct { Values []DataDependentType } type DevicePropDescFixed struct { DevicePropertyCode uint16 DataType DataTypeSelector GetSet uint8 FactoryDefaultValue DataDependentType CurrentValue DataDependentType FormFlag uint8 } type DevicePropDesc struct { DevicePropDescFixed Form interface{} } type ObjectPropDescFixed struct { ObjectPropertyCode uint16 DataType DataTypeSelector GetSet uint8 FactoryDefaultValue DataDependentType GroupCode uint32 FormFlag uint8 } type ObjectPropDesc struct { ObjectPropDescFixed Form interface{} } type Uint32Array struct { Values []uint32 } type Uint16Array struct { Values []uint16 } type Uint64Value struct { Value uint64 } type StringValue struct { Value string } type StorageInfo struct { StorageType uint16 FilesystemType uint16 AccessCapability uint16 MaxCapability uint64 FreeSpaceInBytes uint64 FreeSpaceInImages uint32 StorageDescription string VolumeLabel string } func (d *StorageInfo) IsHierarchical() bool { return d.FilesystemType == FST_GenericHierarchical } func (d *StorageInfo) IsDCF() bool { return d.FilesystemType == FST_DCF } func (d *StorageInfo) IsRemovable() bool { return (d.StorageType == ST_RemovableROM || d.StorageType == ST_RemovableRAM) } type ObjectInfo struct { StorageID uint32 ObjectFormat uint16 ProtectionStatus uint16 CompressedSize uint32 ThumbFormat uint16 ThumbCompressedSize uint32 ThumbPixWidth uint32 ThumbPixHeight uint32 ImagePixWidth uint32 ImagePixHeight uint32 ImageBitDepth uint32 ParentObject uint32 AssociationType uint16 AssociationDesc uint32 SequenceNumber uint32 Filename string CaptureDate time.Time ModificationDate time.Time Keywords string } // USB stuff. type usbBulkHeader struct { Length uint32 Type uint16 Code uint16 TransactionID uint32 } type usbBulkContainer struct { usbBulkHeader Param [5]uint32 } const usbHdrLen = 2*2 + 2*4 const usbBulkLen = 5*4 + usbHdrLen