diff --git a/usb/config.go b/usb/config.go index b6ab0e1..3205ee4 100644 --- a/usb/config.go +++ b/usb/config.go @@ -15,13 +15,8 @@ package usb -// #include -import "C" - import ( "fmt" - "reflect" - "unsafe" ) type EndpointInfo struct { @@ -87,73 +82,3 @@ type ConfigInfo struct { func (c ConfigInfo) String() string { return fmt.Sprintf("Config %02x", c.Config) } - -func newConfig(dev *C.libusb_device, cfg *C.struct_libusb_config_descriptor) ConfigInfo { - c := ConfigInfo{ - Config: uint8(cfg.bConfigurationValue), - Attributes: uint8(cfg.bmAttributes), - MaxPower: uint8(cfg.MaxPower), - } - - var ifaces []C.struct_libusb_interface - *(*reflect.SliceHeader)(unsafe.Pointer(&ifaces)) = reflect.SliceHeader{ - Data: uintptr(unsafe.Pointer(cfg._interface)), - Len: int(cfg.bNumInterfaces), - Cap: int(cfg.bNumInterfaces), - } - c.Interfaces = make([]InterfaceInfo, 0, len(ifaces)) - for _, iface := range ifaces { - if iface.num_altsetting == 0 { - continue - } - - var alts []C.struct_libusb_interface_descriptor - *(*reflect.SliceHeader)(unsafe.Pointer(&alts)) = reflect.SliceHeader{ - Data: uintptr(unsafe.Pointer(iface.altsetting)), - Len: int(iface.num_altsetting), - Cap: int(iface.num_altsetting), - } - descs := make([]InterfaceSetup, 0, len(alts)) - for _, alt := range alts { - i := InterfaceSetup{ - Number: uint8(alt.bInterfaceNumber), - Alternate: uint8(alt.bAlternateSetting), - IfClass: uint8(alt.bInterfaceClass), - IfSubClass: uint8(alt.bInterfaceSubClass), - IfProtocol: uint8(alt.bInterfaceProtocol), - } - var ends []C.struct_libusb_endpoint_descriptor - *(*reflect.SliceHeader)(unsafe.Pointer(&ends)) = reflect.SliceHeader{ - Data: uintptr(unsafe.Pointer(alt.endpoint)), - Len: int(alt.bNumEndpoints), - Cap: int(alt.bNumEndpoints), - } - i.Endpoints = make([]EndpointInfo, 0, len(ends)) - for _, end := range ends { - ei := EndpointInfo{ - Address: uint8(end.bEndpointAddress), - Attributes: uint8(end.bmAttributes), - MaxPacketSize: uint16(end.wMaxPacketSize), - PollInterval: uint8(end.bInterval), - RefreshRate: uint8(end.bRefresh), - SynchAddress: uint8(end.bSynchAddress), - } - if ei.TransferType() == TRANSFER_TYPE_ISOCHRONOUS { - // bits 0-10 identify the packet size, bits 11-12 are the number of additional transactions per microframe. - // Don't use libusb_get_max_iso_packet_size, as it has a bug where it returns the same value - // regardless of alternative setting used, where different alternative settings might define different - // max packet sizes. - // See http://libusb.org/ticket/77 for more background. - ei.MaxIsoPacket = uint32(end.wMaxPacketSize) & 0x07ff * (uint32(end.wMaxPacketSize)>>11&3 + 1) - } - i.Endpoints = append(i.Endpoints, ei) - } - descs = append(descs, i) - } - c.Interfaces = append(c.Interfaces, InterfaceInfo{ - Number: descs[0].Number, - Setups: descs, - }) - } - return c -} diff --git a/usb/descriptor.go b/usb/descriptor.go index 79e5036..76a9b77 100644 --- a/usb/descriptor.go +++ b/usb/descriptor.go @@ -15,9 +15,6 @@ package usb -// #include -import "C" - type Descriptor struct { // Bus information Bus uint8 // The bus on which the device was detected @@ -39,34 +36,3 @@ type Descriptor struct { // Configuration information Configs []ConfigInfo } - -func newDescriptor(dev *C.libusb_device) (*Descriptor, error) { - var desc C.struct_libusb_device_descriptor - if errno := C.libusb_get_device_descriptor(dev, &desc); errno < 0 { - return nil, usbError(errno) - } - - // Enumerate configurations - var cfgs []ConfigInfo - for i := 0; i < int(desc.bNumConfigurations); i++ { - var cfg *C.struct_libusb_config_descriptor - if errno := C.libusb_get_config_descriptor(dev, C.uint8_t(i), &cfg); errno < 0 { - return nil, usbError(errno) - } - cfgs = append(cfgs, newConfig(dev, cfg)) - C.libusb_free_config_descriptor(cfg) - } - - return &Descriptor{ - Bus: uint8(C.libusb_get_bus_number(dev)), - Address: uint8(C.libusb_get_device_address(dev)), - Spec: BCD(desc.bcdUSB), - Device: BCD(desc.bcdDevice), - Vendor: ID(desc.idVendor), - Product: ID(desc.idProduct), - Class: uint8(desc.bDeviceClass), - SubClass: uint8(desc.bDeviceSubClass), - Protocol: uint8(desc.bDeviceProtocol), - Configs: cfgs, - }, nil -} diff --git a/usb/device.go b/usb/device.go index 86a0a31..72e9699 100644 --- a/usb/device.go +++ b/usb/device.go @@ -15,15 +15,10 @@ package usb -// #include -import "C" - import ( "fmt" - "reflect" "sync" "time" - "unsafe" ) var DefaultReadTimeout = 1 * time.Second @@ -31,7 +26,7 @@ var DefaultWriteTimeout = 1 * time.Second var DefaultControlTimeout = 250 * time.Millisecond //5 * time.Second type Device struct { - handle *C.libusb_device_handle + handle *libusbDevHandle // Embed the device information for easy access *Descriptor @@ -46,7 +41,7 @@ type Device struct { claimed map[uint8]int } -func newDevice(handle *C.libusb_device_handle, desc *Descriptor) *Device { +func newDevice(handle *libusbDevHandle, desc *Descriptor) *Device { ifaces := 0 d := &Device{ handle: handle, @@ -62,48 +57,24 @@ func newDevice(handle *C.libusb_device_handle, desc *Descriptor) *Device { } func (d *Device) Reset() error { - if errno := C.libusb_reset_device(d.handle); errno != 0 { - return usbError(errno) - } - return nil + return libusb.reset(d.handle) } func (d *Device) Control(rType, request uint8, val, idx uint16, data []byte) (int, error) { - //log.Printf("control xfer: %d:%d/%d:%d %x", idx, rType, request, val, string(data)) - dataSlice := (*reflect.SliceHeader)(unsafe.Pointer(&data)) - n := C.libusb_control_transfer( - d.handle, - C.uint8_t(rType), - C.uint8_t(request), - C.uint16_t(val), - C.uint16_t(idx), - (*C.uchar)(unsafe.Pointer(dataSlice.Data)), - C.uint16_t(len(data)), - C.uint(d.ControlTimeout/time.Millisecond)) - if n < 0 { - return int(n), usbError(n) - } - return int(n), nil + return libusb.control(d.handle, d.ControlTimeout, rType, request, val, idx, data) } // ActiveConfig returns the config id (not the index) of the active configuration. // This corresponds to the ConfigInfo.Config field. func (d *Device) ActiveConfig() (uint8, error) { - var cfg C.int - if errno := C.libusb_get_configuration(d.handle, &cfg); errno < 0 { - return 0, usbError(errno) - } - return uint8(cfg), nil + return libusb.getConfig(d.handle) } // SetConfig attempts to change the active configuration. // The cfg provided is the config id (not the index) of the configuration to set, // which corresponds to the ConfigInfo.Config field. func (d *Device) SetConfig(cfg uint8) error { - if errno := C.libusb_set_configuration(d.handle, C.int(cfg)); errno < 0 { - return usbError(errno) - } - return nil + return libusb.setConfig(d.handle, cfg) } // Close the device. @@ -114,9 +85,9 @@ func (d *Device) Close() error { d.lock.Lock() defer d.lock.Unlock() for iface := range d.claimed { - C.libusb_release_interface(d.handle, C.int(iface)) + libusb.release(d.handle, iface) } - C.libusb_close(d.handle) + libusb.close(d.handle) d.handle = nil return nil } @@ -162,19 +133,19 @@ func (d *Device) OpenEndpoint(conf, iface, setup, epoint uint8) (Endpoint, error found: // Set the configuration - var activeConf C.int - if errno := C.libusb_get_configuration(d.handle, &activeConf); errno < 0 { - return nil, fmt.Errorf("usb: getcfg: %s", usbError(errno)) + activeConf, err := libusb.getConfig(d.handle) + if err != nil { + return nil, fmt.Errorf("usb: getcfg: %s", err) } - if int(activeConf) != int(conf) { - if errno := C.libusb_set_configuration(d.handle, C.int(conf)); errno < 0 { - return nil, fmt.Errorf("usb: setcfg: %s", usbError(errno)) + if activeConf != conf { + if err := libusb.setConfig(d.handle, conf); err != nil { + return nil, fmt.Errorf("usb: setcfg: %s", err) } } // Claim the interface - if errno := C.libusb_claim_interface(d.handle, C.int(iface)); errno < 0 { - return nil, fmt.Errorf("usb: claim: %s", usbError(errno)) + if err := libusb.claim(d.handle, iface); err != nil { + return nil, fmt.Errorf("usb: claim: %s", err) } // Increment the claim count @@ -184,9 +155,8 @@ found: // Choose the alternate if setAlternate { - if errno := C.libusb_set_interface_alt_setting(d.handle, C.int(iface), C.int(setup)); errno < 0 { - debug.Printf("altsetting error: %s", usbError(errno)) - return nil, fmt.Errorf("usb: setalt: %s", usbError(errno)) + if err := libusb.setAlt(d.handle, iface, setup); err != nil { + return nil, fmt.Errorf("usb: setalt: %s", err) } } @@ -194,26 +164,7 @@ found: } func (d *Device) GetStringDescriptor(desc_index int) (string, error) { - - // allocate 200-byte array limited the length of string descriptor - goBuffer := make([]byte, 200) - - // get string descriptor from libusb. if errno < 0 then there are any errors. - // if errno >= 0; it is a length of result string descriptor - errno := C.libusb_get_string_descriptor_ascii( - d.handle, - C.uint8_t(desc_index), - (*C.uchar)(unsafe.Pointer(&goBuffer[0])), - 200) - - // if any errors occur - if errno < 0 { - return "", fmt.Errorf("usb: getstr: %s", usbError(errno)) - } - // convert slice of byte to string with limited length from errno - stringDescriptor := string(goBuffer[:errno]) - - return stringDescriptor, nil + return libusb.getStringDesc(d.handle, desc_index) } // SetAutoDetach enables/disables libusb's automatic kernel driver detachment. @@ -221,20 +172,12 @@ func (d *Device) GetStringDescriptor(desc_index int) (string, error) { // on the interface and reattach it when releasing the interface. // Automatic kernel driver detachment is disabled on newly opened device handles by default. func (d *Device) SetAutoDetach(autodetach bool) error { - autodetachInt := 0 - if autodetach { + var autodetachInt int + switch autodetach { + case true: autodetachInt = 1 + case false: + autodetachInt = 0 } - - err := C.libusb_set_auto_detach_kernel_driver( - d.handle, - C.int(autodetachInt), - ) - - // TODO LIBUSB_ERROR_NOT_SUPPORTED (-12) handling - // if any errors occur - if err != C.int(SUCCESS) { - return fmt.Errorf("usb: setautodetach: %s", usbError(err)) - } - return nil + return libusb.setAutoDetach(d.handle, autodetachInt) } diff --git a/usb/endpoint.go b/usb/endpoint.go index a33dd15..f942709 100644 --- a/usb/endpoint.go +++ b/usb/endpoint.go @@ -34,7 +34,7 @@ type transferIntf interface { } type endpoint struct { - h *deviceHandle + h *libusbDevHandle InterfaceSetup EndpointInfo @@ -92,7 +92,7 @@ func (e *endpoint) transfer(buf []byte, timeout time.Duration) (int, error) { func newEndpoint(d *Device) *endpoint { ep := &endpoint{ - h: (*deviceHandle)(d.handle), + h: d.handle, readTimeout: d.ReadTimeout, writeTimeout: d.WriteTimeout, } diff --git a/usb/error.go b/usb/error.go index 73115ee..892c54d 100644 --- a/usb/error.go +++ b/usb/error.go @@ -28,6 +28,14 @@ func (e usbError) Error() string { return fmt.Sprintf("libusb: %s [code %d]", usbErrorString[e], int(e)) } +func fromUSBError(errno C.int) error { + err := usbError(errno) + if err == SUCCESS { + return nil + } + return err +} + const ( SUCCESS usbError = C.LIBUSB_SUCCESS ERROR_IO usbError = C.LIBUSB_ERROR_IO diff --git a/usb/transfer.go b/usb/transfer.go index 9784d20..b82902d 100644 --- a/usb/transfer.go +++ b/usb/transfer.go @@ -14,14 +14,6 @@ package usb -/* -#include - -int compact_iso_data(struct libusb_transfer *xfer, unsigned char *status); -int submit(struct libusb_transfer *xfer); -*/ -import "C" - import ( "errors" "fmt" @@ -31,32 +23,8 @@ import ( "unsafe" ) -// libusb hooks used as injection points for tests. -var ( - cCancel = func(t *libusbTransfer) usbError { - return usbError(C.libusb_cancel_transfer((*C.struct_libusb_transfer)(t))) - } - cSubmit = func(t *libusbTransfer) usbError { - return usbError(C.submit((*C.struct_libusb_transfer)(t))) - } -) - -// because of a limitation of cgo, tests cannot import C. -type deviceHandle C.libusb_device_handle -type libusbTransfer C.struct_libusb_transfer -type libusbIso C.struct_libusb_iso_packet_descriptor - -// also for tests -var ( - libusbIsoSize = C.sizeof_struct_libusb_iso_packet_descriptor - libusbIsoOffset = unsafe.Offsetof(C.struct_libusb_transfer{}.iso_packet_desc) -) - -//export xfer_callback -func xfer_callback(cptr unsafe.Pointer) { - ch := *(*chan struct{})(cptr) - close(ch) -} +// #include +import "C" type usbTransfer struct { // mu protects the transfer state. @@ -84,7 +52,7 @@ func (t *usbTransfer) submit() error { } t.done = make(chan struct{}) t.xfer.user_data = (unsafe.Pointer)(&t.done) - if err := cSubmit(t.xfer); err != SUCCESS { + if err := libusb.submit(t.xfer); err != nil { return err } t.submitted = true @@ -111,10 +79,9 @@ func (t *usbTransfer) wait() (n int, err error) { var status TransferStatus switch TransferType(t.xfer._type) { case TRANSFER_TYPE_ISOCHRONOUS: - n = int(C.compact_iso_data((*C.struct_libusb_transfer)(t.xfer), (*C.uchar)(unsafe.Pointer(&status)))) + n, status = libusb.compactIsoData(t.xfer) default: - n = int(t.xfer.actual_length) - status = TransferStatus(t.xfer.status) + n, status = int(t.xfer.actual_length), TransferStatus(t.xfer.status) } if status != LIBUSB_TRANSFER_COMPLETED { return n, status @@ -130,15 +97,12 @@ func (t *usbTransfer) cancel() error { if !t.submitted { return nil } - err := usbError(cCancel(t.xfer)) + err := libusb.cancel(t.xfer) if err == ERROR_NOT_FOUND { // transfer already completed - err = SUCCESS + return nil } - if err != SUCCESS { - return err - } - return nil + return err } // free releases the memory allocated for the transfer. @@ -150,7 +114,7 @@ func (t *usbTransfer) free() error { if t.submitted { return errors.New("free() cannot be called on a submitted transfer until wait() returns") } - C.libusb_free_transfer((*C.struct_libusb_transfer)(t.xfer)) + libusb.free(t.xfer) t.xfer = nil t.buf = nil t.done = nil @@ -159,7 +123,7 @@ func (t *usbTransfer) free() error { // newUSBTransfer allocates a new transfer structure for communication with a // given device/endpoint, with buf as the underlying transfer buffer. -func newUSBTransfer(dev *deviceHandle, ei EndpointInfo, buf []byte, timeout time.Duration) (*usbTransfer, error) { +func newUSBTransfer(dev *libusbDevHandle, ei EndpointInfo, buf []byte, timeout time.Duration) (*usbTransfer, error) { var isoPackets int tt := ei.TransferType() if tt == TRANSFER_TYPE_ISOCHRONOUS { @@ -169,9 +133,9 @@ func newUSBTransfer(dev *deviceHandle, ei EndpointInfo, buf []byte, timeout time } } - xfer := C.libusb_alloc_transfer(C.int(isoPackets)) - if xfer == nil { - return nil, fmt.Errorf("libusb_alloc_transfer(%d) failed", isoPackets) + xfer, err := libusb.alloc(isoPackets) + if err != nil { + return nil, err } xfer.dev_handle = (*C.struct_libusb_device_handle)(dev) @@ -183,8 +147,7 @@ func newUSBTransfer(dev *deviceHandle, ei EndpointInfo, buf []byte, timeout time xfer.length = C.int(len(buf)) if tt == TRANSFER_TYPE_ISOCHRONOUS { - xfer.num_iso_packets = C.int(isoPackets) - C.libusb_set_iso_packet_lengths(xfer, C.uint(ei.MaxIsoPacket)) + libusb.setIsoPacketLengths(xfer, ei.MaxIsoPacket) } t := &usbTransfer{ diff --git a/usb/transfer_fakelibusb_test.go b/usb/transfer_fakelibusb_test.go index 9b59d1f..319853f 100644 --- a/usb/transfer_fakelibusb_test.go +++ b/usb/transfer_fakelibusb_test.go @@ -19,19 +19,20 @@ import "sync" type fakeLibusb struct { sync.Mutex ts map[*libusbTransfer]chan struct{} + libusbIntf } -func (f *fakeLibusb) submit(t *libusbTransfer) usbError { +func (f *fakeLibusb) submit(t *libusbTransfer) error { f.Lock() defer f.Unlock() if f.ts[t] == nil { f.ts[t] = make(chan struct{}) } close(f.ts[t]) - return SUCCESS + return nil } -func (f *fakeLibusb) cancel(t *libusbTransfer) usbError { return SUCCESS } +func (f *fakeLibusb) cancel(t *libusbTransfer) error { return nil } func (f *fakeLibusb) waitForSubmit(t *usbTransfer) { f.Lock() @@ -52,5 +53,8 @@ func (f *fakeLibusb) runCallback(t *usbTransfer, cb func(*usbTransfer)) { } func newFakeLibusb() *fakeLibusb { - return &fakeLibusb{ts: make(map[*libusbTransfer]chan struct{})} + return &fakeLibusb{ + ts: make(map[*libusbTransfer]chan struct{}), + libusbIntf: libusbImpl{}, + } } diff --git a/usb/transfer_test.go b/usb/transfer_test.go index 21a99b3..1185583 100644 --- a/usb/transfer_test.go +++ b/usb/transfer_test.go @@ -87,12 +87,10 @@ func TestNewTransfer(t *testing.T) { } func TestTransferProtocol(t *testing.T) { - origSubmit, origCancel := cSubmit, cCancel - defer func() { cSubmit, cCancel = origSubmit, origCancel }() + defer func(i libusbIntf) { libusb = i }(libusb) f := newFakeLibusb() - cSubmit = f.submit - cCancel = f.cancel + libusb = f xfers := make([]*usbTransfer, 2) var err error @@ -167,12 +165,10 @@ func TestTransferProtocol(t *testing.T) { } func TestIsoPackets(t *testing.T) { - origSubmit, origCancel := cSubmit, cCancel - defer func() { cSubmit, cCancel = origSubmit, origCancel }() + defer func(i libusbIntf) { libusb = i }(libusb) f := newFakeLibusb() - cSubmit = f.submit - cCancel = f.cancel + libusb = f xfer, err := newUSBTransfer(nil, EndpointInfo{ Address: 0x82, diff --git a/usb/usb.go b/usb/usb.go index 47784fb..d22ad59 100644 --- a/usb/usb.go +++ b/usb/usb.go @@ -20,50 +20,26 @@ package usb // #include import "C" -import ( - "log" - "reflect" - "unsafe" -) - type Context struct { - ctx *C.libusb_context + ctx *libusbContext done chan struct{} } func (c *Context) Debug(level int) { - C.libusb_set_debug(c.ctx, C.int(level)) + libusb.setDebug(c.ctx, level) } func NewContext() *Context { - c := &Context{ + c, err := libusb.init() + if err != nil { + panic(err) + } + ctx := &Context{ + ctx: c, done: make(chan struct{}), } - - if errno := C.libusb_init(&c.ctx); errno != 0 { - panic(usbError(errno)) - } - - go func() { - tv := C.struct_timeval{ - tv_sec: 0, - tv_usec: 100000, - } - for { - select { - case <-c.done: - return - default: - } - if errno := C.libusb_handle_events_timeout_completed(c.ctx, &tv, nil); errno < 0 { - log.Printf("handle_events: error: %s", usbError(errno)) - continue - } - //log.Printf("handle_events returned") - } - }() - - return c + go libusb.handleEvents(ctx.ctx, ctx.done) + return ctx } // ListDevices calls each with each enumerated device. @@ -72,36 +48,30 @@ func NewContext() *Context { // If there are any errors enumerating the devices, // the final one is returned along with any successfully opened devices. func (c *Context) ListDevices(each func(desc *Descriptor) bool) ([]*Device, error) { - var list **C.libusb_device - cnt := C.libusb_get_device_list(c.ctx, &list) - if cnt < 0 { - return nil, usbError(cnt) - } - defer C.libusb_free_device_list(list, 1) - - var slice []*C.libusb_device - *(*reflect.SliceHeader)(unsafe.Pointer(&slice)) = reflect.SliceHeader{ - Data: uintptr(unsafe.Pointer(list)), - Len: int(cnt), - Cap: int(cnt), + list, err := libusb.getDevices(c.ctx) + if err != nil { + return nil, err } var reterr error var ret []*Device - for _, dev := range slice { - desc, err := newDescriptor(dev) + for _, dev := range list { + desc, err := libusb.getDeviceDesc(dev) if err != nil { + libusb.dereference(dev) reterr = err continue } if each(desc) { - var handle *C.libusb_device_handle - if errno := C.libusb_open(dev, &handle); errno != 0 { - reterr = usbError(errno) + handle, err := libusb.open(dev) + if err != nil { + reterr = err continue } ret = append(ret, newDevice(handle, desc)) + } else { + libusb.dereference(dev) } } return ret, reterr @@ -110,21 +80,14 @@ func (c *Context) ListDevices(each func(desc *Descriptor) bool) ([]*Device, erro // OpenDeviceWithVidPid opens Device from specific VendorId and ProductId. // If there are any errors, it'll returns at second value. func (c *Context) OpenDeviceWithVidPid(vid, pid int) (*Device, error) { - - handle := C.libusb_open_device_with_vid_pid(c.ctx, (C.uint16_t)(vid), (C.uint16_t)(pid)) - if handle == nil { - return nil, ERROR_NOT_FOUND + dev, handle, err := libusb.openVIDPID(c.ctx, vid, pid) + if err != nil { + return nil, err } - - dev := C.libusb_get_device(handle) - if dev == nil { - return nil, ERROR_NO_DEVICE - } - - desc, err := newDescriptor(dev) - + desc, err := libusb.getDeviceDesc(dev) // return an error from nil-handle and nil-device if err != nil { + libusb.dereference(dev) return nil, err } @@ -135,7 +98,7 @@ func (c *Context) OpenDeviceWithVidPid(vid, pid int) (*Device, error) { func (c *Context) Close() error { close(c.done) if c.ctx != nil { - C.libusb_exit(c.ctx) + libusb.exit(c.ctx) } c.ctx = nil return nil