2016-12-08 42 views
7

Mam zaimplementowaną funkcję wykrywania urządzenia USB. Działa i teraz muszę wysłać/odczytać dane.Dane wysyłania/odbierania urządzenia USB

zacząłem patrzeć na wiele obj-c źródeł i znalazłem tylko jedną dobrą article w dokumentacji jabłkowego, który opisuje w jaki sposób możemy wysłać paczkę do naszego urządzenia 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); 
} 

Ale nie znaleźć sposób tworzenia i wysyłania danych za pomocą Swift. Struct na Swift wygląda następująco:

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) 
} 

nie mogę dowiedzieć się, jakie parametry ma pData, zwLenDone.

Są to dane, które trzeba wysłać:

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

Kolejne pytanie brzmi: Jak mogę odbierać dane. Wiem, że odpowiedź jest w tym artykule, ale nie mogę go przekonwertować na Swift.

Oto, co mogłem przekonwertować na Swift 3. Moja klasa wykrywa USB urządzenie, uzyskać jego konfigurację:

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() 
    } 
} 

nazywam go tak:

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

ORSSerialPort, napisany przez Andrew Madsen, to doskonały zarówno biblioteki napisane w Objective-C i Swift (która wersja nie jestem pewien). Nawet jeśli nie jest to tym, czego szukasz, kod źródłowy może pomóc ci ustawić właściwą drogę. https://github.com/armadsen/ORSSerialPort –

+0

Utworzyłem bibliotekę [USBDeviceSwift] (https://github.com/Arti3DPlayer/USBDeviceSwift) dla wygodnej pracy z 'IOKit.usb' i' IOKit.hid' – Arti

Odpowiedz

2

Po wielu pytań na stackoverflow i źródeł uczenia się i zrozumieć to:

Najpierw zdefiniuj nie zaimplementowane funkcje

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) 
} 

Następnie tworzymy naszą klasę:

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() 
} 
} 

Ty może spojrzeć na metodę getStatus gdzie mogę utworzyć USBRequest i wysłać ją do urządzenia . Następnie w requestPtr:[UInt8] otrzymałem odpowiedź od urządzenia. Dziękuję za pomoc dla facetów.

Możemy wykorzystać rudy wskaźnik urządzenia w dowolnym miejscu w projekcie, na przykład:

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

Wow! Wygląda naprawdę dobrze. –

+0

Utworzyłem bibliotekę [USBDeviceSwift] (https://github.com/Arti3DPlayer/USBDeviceSwift) dla wygodnej pracy z 'IOKit.usb' i' IOKit.hid' – Arti

5

W artykule można odwołać posiada funkcję o nazwie WriteToDevice. Jednym z jej parametrów jest

UInt8 writeBuffer[] 

Ten writeBuffer, dane, które chcesz wysłać, to macierz C bajtów:

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

Co bajtów musisz wysłać? To naprawdę zależy od urządzenia na drugim końcu - dane techniczne producenta powinny Ci to powiedzieć. Aby zdać C-tablicę jako NSData, który jest prawdopodobnie co pData jest, można użyć:

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

(Z) wLenDone jest chyba to, co nazywa się msgLength, 3. C-array mają żadnej wiedzy własnej długości, więc większość funkcji wymaga długości jako oddzielnego parametru.

Jeśli chodzi o odbieranie danych, domyślam się, że dzieje się to w matchCallback: używasz iteratora do odbierania bajtów, a następnie analizowania ich.

ODPOWIEDŹ NA KOMENTARZ:

nie jestem obeznany z C#, a ja nie jestem ekspertem w tej rzeczy, ale może to pomoże:

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 

Kilka obserwacji: nic nie wiedzą C#, ale czy pakiet nie powinien być wpisany w struct? RequestType ma wartość 0, więc nie otrzymasz odpowiedzi - czy tego chcesz? A może chcesz wysłać UsbCtrlFlags.RequestType_Standard jako czwarty parametr? Po co wysyłać długość jako wartość?

Tak czy inaczej, teraz wyślij pakiet na urządzenie USB i zobacz, co się stanie.

+0

Dziękuję dla odpowiedzi, ale nadal nie mogę dowiedzieć się :(Mam kod aC#, który to zrobić. Wysyłają pakiet o tej strukturze: http://libusbdotnet.sourceforge.net/V2/html/59b69ac6-c868-22e2-5c92 -f4163192bd52.htm '' 'pakiet var = new UsbSetupPacket ((bajt) (UsbCtrlFlags.Direction_In | UsbCtrlFlags.Recipient_Device | UsbCtrlFlags.RequestType_Standard), 6, 0x200, 0, (krótka) długość);' '' mógłbyś proszę pokazać swój przykład z tymi danymi – Arti

+0

Dziękuję za odpowiedź, i wymyślę problem.Pokażę odpowiedź trochę później – Arti

+0

Stworzyłem [USBDeviceSwift] (https://github.com/Arti3DPlayer/USBDevic eSwift) do wygodnej pracy z 'IOKit.usb' i' IOKit.hid' – Arti