2016-12-08 165 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