2016-12-08 134 views
7

我已经实现了检测USB设备的功能。它的工作原理,现在我需要发送/读取数据。USB设备发送/接收数据

我开始翻阅大量的obj-c来源和苹果文档中只找到一个好article,描述我们怎么可以把包我们的USB设备:

IOReturn WriteToDevice(IOUSBDeviceInterface **dev, UInt16 deviceAddress, 
         UInt16 length, UInt8 writeBuffer[]) 
{ 

    IOUSBDevRequest  request; 
    request.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBVendor, 
               kUSBDevice); 
    request.bRequest = 0xa0; 
    request.wValue = deviceAddress; 
    request.wIndex = 0; 
    request.wLength = length; 
    request.pData = writeBuffer; 

    return (*dev)->DeviceRequest(dev, &request); 
} 

但我没有找到一个如何使用Swift创建和发送数据。在Swift的结构是这样的:

public struct IOUSBDevRequest { 
    public var bmRequestType: UInt8 
    public var bRequest: UInt8 
    public var wValue: UInt16 
    public var wIndex: UInt16 
    public var wLength: UInt16 
    public var pData: UnsafeMutableRawPointer! 
    public var wLenDone: UInt32 
    public init() 

    public init(bmRequestType: UInt8, bRequest: UInt8, wValue: UInt16, wIndex: UInt16, wLength: UInt16, pData: UnsafeMutableRawPointer!, wLenDone: UInt32) 
} 

我想不出什么参数是pDatazwLenDone

这是我需要发送数据:

{   
'direction':'in',   
'recipient':'device', 
'requestType': 'standard', 
'request':  6,   
'value':  0x300,   
'index':  0,   
'length':  255 
} 

下一个问题是:我如何可以接收数据。我知道答案在本文中,但我无法将其转换为Swift。

这是我可以在Swift 3转换。我的班级检测USB设备,让他的配置:

class DFUDevice: NSObject { 
let vendorId = 0x0483 
let productId = 0xdf11 

static let sharedInstance = DFUDevice() 

var deviceName:String = "" 

private func deviceAdded(iterator: io_iterator_t) { 
    var plugInInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOCFPlugInInterface>?>? 
    var deviceInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOUSBDeviceInterface>?>? 
    var configPtr:IOUSBConfigurationDescriptorPtr? 

    var score: Int32 = 0 

    while case let usbDevice = IOIteratorNext(iterator), usbDevice != 0 { 
     // io_name_t imports to swift as a tuple (Int8, ..., Int8) 128 ints 
     // although in device_types.h it's defined: 
     // typedef char io_name_t[128]; 
     var deviceNameCString: [CChar] = [CChar](repeating: 0, count: 128) 
     let deviceNameResult = IORegistryEntryGetName(usbDevice, &deviceNameCString) 

     if(deviceNameResult != kIOReturnSuccess) { 
      print("Error getting device name") 
     } 

     self.deviceName = String.init(cString: &deviceNameCString) 
     print("usb Device Name: \(deviceName)") 

     // Get plugInInterface for current USB device 

     let plugInInterfaceResult = IOCreatePlugInInterfaceForService(
      usbDevice, 
      kIOUSBDeviceUserClientTypeID, 
      kIOCFPlugInInterfaceID, 
      &plugInInterfacePtrPtr, 
      &score) 

     // dereference pointer for the plug in interface 
     guard plugInInterfaceResult == kIOReturnSuccess, 
      let plugInInterface = plugInInterfacePtrPtr?.pointee?.pointee else { 
       print("Unable to get Plug-In Interface") 
       continue 
     } 

     // use plug in interface to get a device interface 
     let deviceInterfaceResult = withUnsafeMutablePointer(to: &deviceInterfacePtrPtr) { 
      $0.withMemoryRebound(to: Optional<LPVOID>.self, capacity: 1) { 
       plugInInterface.QueryInterface(
        plugInInterfacePtrPtr, 
        CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), 
        $0) 
      } 
     } 

     // dereference pointer for the device interface 
     guard deviceInterfaceResult == kIOReturnSuccess, 
      let deviceInterface = deviceInterfacePtrPtr?.pointee?.pointee else { 
       print("Unable to get Device Interface") 
       continue 
     } 

     var ret = deviceInterface.USBDeviceOpen(deviceInterfacePtrPtr) 
     if (ret == kIOReturnSuccess) 
     { 
      // set first configuration as active 
      ret = deviceInterface.GetConfigurationDescriptorPtr(deviceInterfacePtrPtr, 0, &configPtr) 
      if (ret != kIOReturnSuccess) 
      { 
       print("Could not set active configuration (error: %x)\n", ret); 
       continue 
      } 
      guard let config = configPtr?.pointee else { 
       continue 
      } 

      if config.bLength > 0 { 
       //HERE I NEED SEND DATA 

      } else { 
       print("ConfigurationDescriptor not valid") 
      } 
      print(config.bLength) 
     } 
     else if (ret == kIOReturnExclusiveAccess) 
     { 
      // this is not a problem as we can still do some things 
     } 
     else 
     { 
      print("Could not open device (error: %x)\n", ret) 
      continue 
     } 

     IOObjectRelease(usbDevice) 
    } 
} 


func initUsb() { 
    var matchedIterator:io_iterator_t = 0 
    var removalIterator:io_iterator_t = 0 
    let notifyPort:IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault) 
    IONotificationPortSetDispatchQueue(notifyPort, DispatchQueue(label: "IODetector")) 

    let matchingDict = IOServiceMatching(kIOUSBDeviceClassName) 
     as NSMutableDictionary 
    matchingDict[kUSBVendorID] = NSNumber(value: self.vendorId) 
    matchingDict[kUSBProductID] = NSNumber(value: self.productId) 

    let matchingCallback:IOServiceMatchingCallback = { (userData, iterator) in 
     let this = Unmanaged<DFUDevice> 
      .fromOpaque(userData!).takeUnretainedValue() 
     this.deviceAdded(iterator: iterator) 
     this.connected(iterator: iterator) 
    } 

    let removalCallback: IOServiceMatchingCallback = { 
     (userData, iterator) in 
     let this = Unmanaged<DFUDevice> 
      .fromOpaque(userData!).takeUnretainedValue() 
     this.disconnected(iterator: iterator) 
    } 

    let selfPtr = Unmanaged.passUnretained(self).toOpaque() 

    IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, matchingDict, matchingCallback, selfPtr, &matchedIterator) 
    IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, matchingDict, removalCallback, selfPtr, &removalIterator) 

    self.deviceAdded(iterator: matchedIterator) 
    self.deviceAdded(iterator: removalIterator) 

    RunLoop.current.run() 
    } 
} 

我这样称呼它:

let DFUDeviceDaemon = Thread(target: DFUDevice.sharedInstance, selector:#selector(DFUDevice.initUsb), object: nil) 
DFUDeviceDaemon.start() 
+0

ORSSerialPort,由安德鲁·马德森写的,写的是在这两个Objective-C和斯威夫特一个优秀的库(不知道哪个版本)。即使这不是你想要的,源代码可能会帮助你设置正确的方向。 https://github.com/armadsen/ORSSerialPort –

+0

我已经创建了[USBDeviceSwift](https://github.com/Arti3DPlayer/USBDeviceSwift)库,以便于使用'IOKit.usb'和'IOKit.hid'工作。 – Arti

回答

2

计算器和学习资源很多问题后我搞清楚:

首先定义未实现的功能

import Foundation 

import IOKit 
import IOKit.usb 
import IOKit.usb.IOUSBLib 

//from IOUSBLib.h 
let kIOUSBDeviceUserClientTypeID = CFUUIDGetConstantUUIDWithBytes(nil, 
                    0x9d, 0xc7, 0xb7, 0x80, 0x9e, 0xc0, 0x11, 0xD4, 
                    0xa5, 0x4f, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61) 
let kIOUSBDeviceInterfaceID = CFUUIDGetConstantUUIDWithBytes(nil, 
                  0x5c, 0x81, 0x87, 0xd0, 0x9e, 0xf3, 0x11, 0xD4, 
                  0x8b, 0x45, 0x00, 0x0a, 0x27, 0x05, 0x28, 0x61) 

//from IOCFPlugin.h 
let kIOCFPlugInInterfaceID = CFUUIDGetConstantUUIDWithBytes(nil, 
                  0xC2, 0x44, 0xE8, 0x58, 0x10, 0x9C, 0x11, 0xD4, 
                  0x91, 0xD4, 0x00, 0x50, 0xE4, 0xC6, 0x42, 0x6F) 


/*! 
@defined USBmakebmRequestType 
@discussion Macro to encode the bRequest field of a Device Request. It is used when constructing an IOUSBDevRequest. 
*/ 

func USBmakebmRequestType(direction:Int, type:Int, recipient:Int) -> UInt8 { 
    return UInt8((direction & kUSBRqDirnMask) << kUSBRqDirnShift)|UInt8((type & kUSBRqTypeMask) << kUSBRqTypeShift)|UInt8(recipient & kUSBRqRecipientMask) 
} 

然后创建我们的类:

extension Notification.Name { 
    static let dfuDeviceConnected = Notification.Name("DFUDeviceConnected") 
    static let dfuDeviceDisconnected = Notification.Name("DFUDeviceDisconnected") 
} 

class DFUDevice: NSObject { 
let vendorId = 0x0483 
let productId = 0xdf11 

static let sharedInstance = DFUDevice() 

var deviceInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOUSBDeviceInterface>?>? 
var plugInInterfacePtrPtr: UnsafeMutablePointer<UnsafeMutablePointer<IOCFPlugInInterface>?>? 
var interfacePtrPtr:UnsafeMutablePointer<UnsafeMutablePointer<IOUSBInterfaceInterface>?>? 

private func rawDeviceAdded(iterator: io_iterator_t) { 
    var score:Int32 = 0 
    var kr:Int32 = 0 

    while case let usbDevice = IOIteratorNext(iterator), usbDevice != 0 { 
     // io_name_t imports to swift as a tuple (Int8, ..., Int8) 128 ints 
     // although in device_types.h it's defined: 
     // typedef char io_name_t[128]; 
     var deviceNameCString: [CChar] = [CChar](repeating: 0, count: 128) 
     let deviceNameResult = IORegistryEntryGetName(usbDevice, &deviceNameCString) 

     if(deviceNameResult != kIOReturnSuccess) { 
      print("Error getting device name") 
     } 

     let deviceName = String.init(cString: &deviceNameCString) 
     print("usb Device Name: \(deviceName)") 

     // Get plugInInterface for current USB device 
     let plugInInterfaceResult = IOCreatePlugInInterfaceForService(
      usbDevice, 
      kIOUSBDeviceUserClientTypeID, 
      kIOCFPlugInInterfaceID, 
      &plugInInterfacePtrPtr, 
      &score) 

     // USB device object is no longer needed. 
     IOObjectRelease(usbDevice) 

     // Dereference pointer for the plug-in interface 
     guard plugInInterfaceResult == kIOReturnSuccess, 
      let plugInInterface = plugInInterfacePtrPtr?.pointee?.pointee else { 
       print("Unable to get Plug-In Interface") 
       continue 
     } 

     // use plug in interface to get a device interface 
     let deviceInterfaceResult = withUnsafeMutablePointer(to: &deviceInterfacePtrPtr) { 
      $0.withMemoryRebound(to: Optional<LPVOID>.self, capacity: 1) { 
       plugInInterface.QueryInterface(
        plugInInterfacePtrPtr, 
        CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), 
        $0) 
      } 
     } 

     // dereference pointer for the device interface 
     guard deviceInterfaceResult == kIOReturnSuccess, 
      let deviceInterface = deviceInterfacePtrPtr?.pointee?.pointee else { 
       print("Unable to get Device Interface") 
       continue 
     } 

     kr = deviceInterface.USBDeviceOpen(deviceInterfacePtrPtr) 

     if (kr != kIOReturnSuccess) 
     { 
      print("Could not open device (error: \(kr))") 
      continue 
     } 
     else if (kr == kIOReturnExclusiveAccess) 
     { 
      // this is not a problem as we can still do some things 
      continue 
     } 

     self.connected() 
    } 
} 

private func rawDeviceRemoved(iterator: io_iterator_t) { 
    var kr:Int32 = 0 

    while case let usbDevice = IOIteratorNext(iterator), usbDevice != 0 { 
     // USB device object is no longer needed. 
     kr = IOObjectRelease(usbDevice) 

     if (kr != kIOReturnSuccess) 
     { 
      print("Couldn’t release raw device object (error: \(kr))") 
      continue 
     } 

     self.disconnected() 

    } 
} 

func getStatus() throws -> [UInt8] { 
    guard let deviceInterface = self.deviceInterfacePtrPtr?.pointee?.pointee else { 
     throw DFUDeviceError.DeviceInterfaceNotFound 
    } 

    var kr:Int32 = 0 
    let length:Int = 6 
    var requestPtr:[UInt8] = [UInt8](repeating: 0, count: length) 
    var request = IOUSBDevRequest(bmRequestType: USBmakebmRequestType(direction: kUSBIn, type: kUSBDevice, recipient: kUSBStandard), 
            bRequest: DFUREQUEST.GETSTATUS.rawValue, 
            wValue: 0, 
            wIndex: 0, 
            wLength: UInt16(length), 
            pData: &requestPtr, 
            wLenDone: 255) 

    kr = deviceInterface.DeviceRequest(self.deviceInterfacePtrPtr, &request) 

    if (kr != kIOReturnSuccess) { 
     throw DFUDeviceError.RequestError(desc: "Get device status request error: \(kr)") 
    } 

    return requestPtr 
} 



private func configureDevice() -> Int32 { 
    var kr:Int32 = 0 

    guard let deviceInterface = deviceInterfacePtrPtr?.pointee?.pointee else { 
     print("Unable to get Device Interface") 
     return -1 
    } 

    var numConfig:UInt8 = 0 

    kr = deviceInterface.GetNumberOfConfigurations(deviceInterfacePtrPtr, &numConfig) 
    if numConfig == 0 { 
     print("Device Number Of Configurations: 0") 
     return -1 
    } 

    var configPtr:IOUSBConfigurationDescriptorPtr? 

    // set first configuration as active 
    kr = deviceInterface.GetConfigurationDescriptorPtr(deviceInterfacePtrPtr, 0, &configPtr) 
    if (kr != kIOReturnSuccess) 
    { 
     print("Couldn’t get configuration descriptor for index (error: %x)\n", kr); 
     return -1 
    } 

    guard let config = configPtr?.pointee else { 
     return -1 
    } 

    //Set the device’s configuration. The configuration value is found in 
    //the bConfigurationValue field of the configuration descriptor 

    kr = deviceInterface.SetConfiguration(deviceInterfacePtrPtr, config.bConfigurationValue) 
    if (kr != kIOReturnSuccess) 
    { 
     print("Couldn’t set configuration to value (error: %x)\n", kr); 
     return -1 
    } 

    return kIOReturnSuccess 
} 


func connected() { 
    NotificationCenter.default.post(name: .dfuDeviceConnected, object: nil) 
    globalLogPost("DFU device has been device connected") 
} 

func disconnected() { 
    NotificationCenter.default.post(name: .dfuDeviceDisconnected, object: nil) 
    globalLogPost("DFU device has been disconnected") 
} 

func initUsb() { 
    var matchedIterator:io_iterator_t = 0 
    var removalIterator:io_iterator_t = 0 
    let notifyPort:IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault) 
    IONotificationPortSetDispatchQueue(notifyPort, DispatchQueue(label: "IODetector")) 

    let matchingDict = IOServiceMatching(kIOUSBDeviceClassName) 
     as NSMutableDictionary 
    matchingDict[kUSBVendorID] = NSNumber(value: self.vendorId) 
    matchingDict[kUSBProductID] = NSNumber(value: self.productId) 

    let matchingCallback:IOServiceMatchingCallback = { (userData, iterator) in 
     let this = Unmanaged<DFUDevice> 
      .fromOpaque(userData!).takeUnretainedValue() 
     this.rawDeviceAdded(iterator: iterator) 
    } 

    let removalCallback: IOServiceMatchingCallback = { 
     (userData, iterator) in 
     let this = Unmanaged<DFUDevice> 
      .fromOpaque(userData!).takeUnretainedValue() 
     this.rawDeviceRemoved(iterator: iterator) 
    } 

    let selfPtr = Unmanaged.passUnretained(self).toOpaque() 

    IOServiceAddMatchingNotification(notifyPort, kIOFirstMatchNotification, matchingDict, matchingCallback, selfPtr, &matchedIterator) 
    IOServiceAddMatchingNotification(notifyPort, kIOTerminatedNotification, matchingDict, removalCallback, selfPtr, &removalIterator) 

    self.rawDeviceAdded(iterator: matchedIterator) 
    self.rawDeviceRemoved(iterator: removalIterator) 

    RunLoop.current.run() 
} 
} 

你可以看看方法getStatus,我创建一个USBRequest并将其发送到设备。然后在requestPtr:[UInt8]我收到来自设备的答案。感谢您帮助球员。

我们可以在项目的任何地方使用矿设备指针,例如:

func upload(value:UInt16, length:UInt16) throws -> [UInt8] { 
     guard let deviceInterface = DFUDevice.sharedInstance.deviceInterfacePtrPtr?.pointee?.pointee else { 
      throw DFUDeviceError.DeviceInterfaceNotFound 
     } 
     var kr:Int32 = 0 
     var requestPtr:[UInt8] = [UInt8](repeating: 0, count: Int(length)) 

     var request = IOUSBDevRequest(bmRequestType: 161, 
             bRequest: DFUREQUEST.UPLOAD.rawValue, 
             wValue: value, 
             wIndex: 0, 
             wLength: length, 
             pData: &requestPtr, 
             wLenDone: 255) 

     kr = deviceInterface.DeviceRequest(DFUDevice.sharedInstance.deviceInterfacePtrPtr, &request) 

     if (kr != kIOReturnSuccess) { 
      throw DFUDeviceError.RequestError(desc: "Upload request error: \(kr), request data: \(request)") 
     } 

     return requestPtr 
    } 
+0

哇!看起来非常好。 –

+0

我已经创建了[USBDeviceSwift](https://github.com/Arti3DPlayer/USBDeviceSwift)库,以方便使用'IOKit.usb'和'IOKit.hid' – Arti

5

您引用的文章有一个名为WriteToDevice功能。它的一个参数是

UInt8 writeBuffer[] 

这writeBuffer,你要发送的数据,是字节的C数组:

uint8_t msgLength = 3; 
uint8_t writeBuffer[msgLength]; 
writeBuffer[0] = 0x41; // ASCII 'A' 
writeBuffer[1] = 0x42; // ASCII 'B' 
writeBuffer[2] = 0x43; // ASCII 'C' 

你需要什么字节发送?这真的取决于另一端的设备 - 制造商的技术数据应该告诉你。 要通过C-数组作为NSData的,这可能是pData中是什么,你会使用:

NSData *data = [NSData dataWithBytes:&writeBuffer length:3]; 

的(Z)wLenDone可能就是我所谓的msgLength,3 C-阵列的没有知识他们自己的长度,所以大多数功能需要长度作为一个单独的参数。

至于接收数据,我想这会发生在匹配回调:你使用迭代器接收字节,然后解析它们。

答案评论:

我不熟悉C#,和我在这个东西不是专家,但也许这将帮助:

var package = new UsbSetupPacket( 
(byte)(UsbCtrlFlags.Direction_In | 
     UsbCtrlFlags.Recipient_Device | 
     UsbCtrlFlags.RequestType_Standard), // Index 
6,      // length of data, second phase 
0x200,     // Request 
0,      // RequestType 
(short)length);   // Value 

几个意见:什么都不知道的C#,但不应该包被键入结构? RequestType是0,所以你不会收到任何回应 - 这是你想要的吗?或者你想发送UsbCtrlFlags.RequestType_Standard作为第四个参数吗?为什么要将长度作为价值?

无论如何,你现在要做的是将包发送到USB设备,看看会发生什么。

+0

谢谢回答,但我仍然无法弄清楚:(我有ac代码这样做,他们发送这种结构的包:http://libusbdotnet.sourceforge.net/V2/html/59b69ac6-c868-22e2-5c92 -f4163192bd52.htm var package = new UsbSetupPacket((byte)(UsbCtrlFlags.Direction_In | UsbCtrlFlags.Recipient_Device | UsbCtrlFlags.RequestType_Standard),6,0x200,0,(short)length);''''' 你可不可以 – Arti

+0

感谢您的回答,我弄清楚了问题,稍后发布我的答案。 – Arti

+0

我已经创建了[USBDeviceSwift](https://github.com/Arti3DPlayer/USBDevic eSwift)库,以方便使用'IOKit.usb'和'IOKit.hid' – Arti