Lookin_PTUSBHub.m 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. #import "Lookin_PTUSBHub.h"
  2. #import "Lookin_PTPrivate.h"
  3. #include <netinet/in.h>
  4. #include <sys/socket.h>
  5. #include <sys/ioctl.h>
  6. #include <sys/un.h>
  7. #include <err.h>
  8. NSString * const Lookin_PTUSBHubErrorDomain = @"PTUSBHubError";
  9. typedef uint32_t USBMuxPacketType;
  10. enum {
  11. USBMuxPacketTypeResult = 1,
  12. USBMuxPacketTypeConnect = 2,
  13. USBMuxPacketTypeListen = 3,
  14. USBMuxPacketTypeDeviceAdd = 4,
  15. USBMuxPacketTypeDeviceRemove = 5,
  16. // ? = 6,
  17. // ? = 7,
  18. USBMuxPacketTypePlistPayload = 8,
  19. };
  20. typedef uint32_t USBMuxPacketProtocol;
  21. enum {
  22. USBMuxPacketProtocolBinary = 0,
  23. USBMuxPacketProtocolPlist = 1,
  24. };
  25. typedef uint32_t USBMuxReplyCode;
  26. enum {
  27. USBMuxReplyCodeOK = 0,
  28. USBMuxReplyCodeBadCommand = 1,
  29. USBMuxReplyCodeBadDevice = 2,
  30. USBMuxReplyCodeConnectionRefused = 3,
  31. // ? = 4,
  32. // ? = 5,
  33. USBMuxReplyCodeBadVersion = 6,
  34. };
  35. typedef struct usbmux_packet {
  36. uint32_t size;
  37. USBMuxPacketProtocol protocol;
  38. USBMuxPacketType type;
  39. uint32_t tag;
  40. char data[0];
  41. } __attribute__((__packed__)) usbmux_packet_t;
  42. static const uint32_t kUsbmuxPacketMaxPayloadSize = UINT32_MAX - (uint32_t)sizeof(usbmux_packet_t);
  43. static uint32_t usbmux_packet_payload_size(usbmux_packet_t *upacket) {
  44. return upacket->size - sizeof(usbmux_packet_t);
  45. }
  46. static void *usbmux_packet_payload(usbmux_packet_t *upacket) {
  47. return (void*)upacket->data;
  48. }
  49. static void usbmux_packet_set_payload(usbmux_packet_t *upacket,
  50. const void *payload,
  51. uint32_t payloadLength)
  52. {
  53. memcpy(usbmux_packet_payload(upacket), payload, payloadLength);
  54. }
  55. static usbmux_packet_t *usbmux_packet_alloc(uint32_t payloadSize) {
  56. assert(payloadSize <= kUsbmuxPacketMaxPayloadSize);
  57. uint32_t upacketSize = sizeof(usbmux_packet_t) + payloadSize;
  58. usbmux_packet_t *upacket = CFAllocatorAllocate(kCFAllocatorDefault, upacketSize, 0);
  59. memset(upacket, 0, sizeof(usbmux_packet_t));
  60. upacket->size = upacketSize;
  61. return upacket;
  62. }
  63. static usbmux_packet_t *usbmux_packet_create(USBMuxPacketProtocol protocol,
  64. USBMuxPacketType type,
  65. uint32_t tag,
  66. const void *payload,
  67. uint32_t payloadSize)
  68. {
  69. usbmux_packet_t *upacket = usbmux_packet_alloc(payloadSize);
  70. if (!upacket) {
  71. return NULL;
  72. }
  73. upacket->protocol = protocol;
  74. upacket->type = type;
  75. upacket->tag = tag;
  76. if (payload && payloadSize) {
  77. usbmux_packet_set_payload(upacket, payload, (uint32_t)payloadSize);
  78. }
  79. return upacket;
  80. }
  81. static void usbmux_packet_free(usbmux_packet_t *upacket) {
  82. CFAllocatorDeallocate(kCFAllocatorDefault, upacket);
  83. }
  84. NSString * const Lookin_PTUSBDeviceDidAttachNotification = @"Lookin_PTUSBDeviceDidAttachNotification";
  85. NSString * const Lookin_PTUSBDeviceDidDetachNotification = @"Lookin_PTUSBDeviceDidDetachNotification";
  86. static NSString *kPlistPacketTypeListen = @"Listen";
  87. static NSString *kPlistPacketTypeConnect = @"Connect";
  88. // Represents a channel of communication between the host process and a remote
  89. // (device) system. In practice, a Lookin_PTUSBChannel is connected to a usbmuxd
  90. // endpoint which is configured to either listen for device changes (the
  91. // PTUSBHub's channel is usually configured as a device notification listener) or
  92. // configured as a TCP bridge (e.g. channels returned from PTUSBHub's
  93. // connectToDevice:port:callback:). You should not create channels yourself, but
  94. // let Lookin_PTUSBHub provide you with already configured channels.
  95. @interface Lookin_PTUSBChannel : NSObject {
  96. dispatch_io_t channel_;
  97. dispatch_queue_t queue_;
  98. uint32_t nextPacketTag_;
  99. NSMutableDictionary *responseQueue_;
  100. BOOL autoReadPackets_;
  101. BOOL isReadingPackets_;
  102. }
  103. // The underlying dispatch I/O channel. This is handy if you want to handle your
  104. // own I/O logic without Lookin_PTUSBChannel. Remember to dispatch_retain() the channel
  105. // if you plan on using it as it might be released from the Lookin_PTUSBChannel at any
  106. // point in time.
  107. @property (readonly) dispatch_io_t dispatchChannel;
  108. // The underlying file descriptor.
  109. @property (readonly) dispatch_fd_t fileDescriptor;
  110. // Send data
  111. - (void)sendDispatchData:(dispatch_data_t)data callback:(void(^)(NSError*))callback;
  112. - (void)sendData:(NSData*)data callback:(void(^)(NSError*))callback;
  113. // Read data
  114. - (void)readFromOffset:(off_t)offset length:(size_t)length callback:(void(^)(NSError *error, dispatch_data_t data))callback;
  115. // Close the channel, preventing further reads and writes, but letting currently
  116. // queued reads and writes finish.
  117. - (void)cancel;
  118. // Close the channel, preventing further reads and writes, immediately
  119. // terminating any ongoing reads and writes.
  120. - (void)stop;
  121. @end
  122. @interface Lookin_PTUSBChannel (Private)
  123. + (NSDictionary*)packetDictionaryWithPacketType:(NSString*)messageType payload:(NSDictionary*)payload;
  124. - (BOOL)openOnQueue:(dispatch_queue_t)queue error:(NSError**)error onEnd:(void(^)(NSError *error))onEnd;
  125. - (void)listenWithBroadcastHandler:(void(^)(NSDictionary *packet))broadcastHandler callback:(void(^)(NSError*))callback;
  126. - (BOOL)errorFromPlistResponse:(NSDictionary*)packet error:(NSError**)error;
  127. - (uint32_t)nextPacketTag;
  128. - (void)sendPacketOfType:(USBMuxPacketType)type overProtocol:(USBMuxPacketProtocol)protocol tag:(uint32_t)tag payload:(NSData*)payload callback:(void(^)(NSError*))callback;
  129. - (void)sendPacket:(NSDictionary*)packet tag:(uint32_t)tag callback:(void(^)(NSError *error))callback;
  130. - (void)sendRequest:(NSDictionary*)packet callback:(void(^)(NSError *error, NSDictionary *responsePacket))callback;
  131. - (void)scheduleReadPacketWithCallback:(void(^)(NSError *error, NSDictionary *packet, uint32_t packetTag))callback;
  132. - (void)scheduleReadPacketWithBroadcastHandler:(void(^)(NSDictionary *packet))broadcastHandler;
  133. - (void)setNeedsReadingPacket;
  134. @end
  135. @interface Lookin_PTUSBHub () {
  136. Lookin_PTUSBChannel *channel_;
  137. }
  138. - (void)handleBroadcastPacket:(NSDictionary*)packet;
  139. @end
  140. @implementation Lookin_PTUSBHub
  141. + (Lookin_PTUSBHub*)sharedHub {
  142. static Lookin_PTUSBHub *gSharedHub;
  143. static dispatch_once_t onceToken;
  144. dispatch_once(&onceToken, ^{
  145. gSharedHub = [Lookin_PTUSBHub new];
  146. [gSharedHub listenOnQueue:dispatch_get_main_queue() onStart:^(NSError *error) {
  147. if (error) {
  148. NSLog(@"Lookin_PTUSBHub failed to initialize: %@", error);
  149. }
  150. } onEnd:nil];
  151. });
  152. return gSharedHub;
  153. }
  154. - (id)init {
  155. if (!(self = [super init])) return nil;
  156. return self;
  157. }
  158. - (void)listenOnQueue:(dispatch_queue_t)queue onStart:(void(^)(NSError*))onStart onEnd:(void(^)(NSError*))onEnd {
  159. if (channel_) {
  160. if (onStart) onStart(nil);
  161. return;
  162. }
  163. channel_ = [Lookin_PTUSBChannel new];
  164. NSError *error = nil;
  165. if ([channel_ openOnQueue:queue error:&error onEnd:onEnd]) {
  166. [channel_ listenWithBroadcastHandler:^(NSDictionary *packet) { [self handleBroadcastPacket:packet]; } callback:onStart];
  167. } else if (onStart) {
  168. onStart(error);
  169. }
  170. }
  171. - (void)connectToDevice:(NSNumber*)deviceID port:(int)port onStart:(void(^)(NSError*, dispatch_io_t))onStart onEnd:(void(^)(NSError*))onEnd {
  172. Lookin_PTUSBChannel *channel = [Lookin_PTUSBChannel new];
  173. NSError *error = nil;
  174. if (![channel openOnQueue:dispatch_get_main_queue() error:&error onEnd:onEnd]) {
  175. onStart(error, nil);
  176. return;
  177. }
  178. port = ((port<<8) & 0xFF00) | (port>>8); // limit
  179. NSDictionary *packet = [Lookin_PTUSBChannel packetDictionaryWithPacketType:kPlistPacketTypeConnect
  180. payload:[NSDictionary dictionaryWithObjectsAndKeys:
  181. deviceID, @"DeviceID",
  182. [NSNumber numberWithInt:port], @"PortNumber",
  183. nil]];
  184. [channel sendRequest:packet callback:^(NSError *error_, NSDictionary *responsePacket) {
  185. NSError *error = error_;
  186. [channel errorFromPlistResponse:responsePacket error:&error];
  187. onStart(error, (error ? nil : channel.dispatchChannel) );
  188. }];
  189. }
  190. - (void)handleBroadcastPacket:(NSDictionary*)packet {
  191. NSString *messageType = [packet objectForKey:@"MessageType"];
  192. if ([@"Attached" isEqualToString:messageType]) {
  193. [[NSNotificationCenter defaultCenter] postNotificationName:Lookin_PTUSBDeviceDidAttachNotification object:self userInfo:packet];
  194. } else if ([@"Detached" isEqualToString:messageType]) {
  195. [[NSNotificationCenter defaultCenter] postNotificationName:Lookin_PTUSBDeviceDidDetachNotification object:self userInfo:packet];
  196. } else {
  197. NSLog(@"Warning: Unhandled broadcast message: %@", packet);
  198. }
  199. }
  200. @end
  201. #pragma mark -
  202. @implementation Lookin_PTUSBChannel
  203. + (NSDictionary*)packetDictionaryWithPacketType:(NSString*)messageType payload:(NSDictionary*)payload {
  204. NSDictionary *packet = nil;
  205. static NSString *bundleName = nil;
  206. static NSString *bundleVersion = nil;
  207. static dispatch_once_t onceToken;
  208. dispatch_once(&onceToken, ^{
  209. NSDictionary *infoDict = [NSBundle mainBundle].infoDictionary;
  210. if (infoDict) {
  211. bundleName = [infoDict objectForKey:@"CFBundleName"];
  212. bundleVersion = [[infoDict objectForKey:@"CFBundleVersion"] description];
  213. }
  214. });
  215. if (bundleName) {
  216. packet = [NSDictionary dictionaryWithObjectsAndKeys:
  217. messageType, @"MessageType",
  218. bundleName, @"ProgName",
  219. bundleVersion, @"ClientVersionString",
  220. nil];
  221. } else {
  222. packet = [NSDictionary dictionaryWithObjectsAndKeys:messageType, @"MessageType", nil];
  223. }
  224. if (payload) {
  225. NSMutableDictionary *mpacket = [NSMutableDictionary dictionaryWithDictionary:payload];
  226. [mpacket addEntriesFromDictionary:packet];
  227. packet = mpacket;
  228. }
  229. return packet;
  230. }
  231. - (id)init {
  232. if (!(self = [super init])) return nil;
  233. return self;
  234. }
  235. - (void)dealloc {
  236. //NSLog(@"dealloc %@", self);
  237. if (channel_) {
  238. #if PT_DISPATCH_RETAIN_RELEASE
  239. dispatch_release(channel_);
  240. #endif
  241. channel_ = nil;
  242. }
  243. }
  244. - (BOOL)valid {
  245. return !!channel_;
  246. }
  247. - (dispatch_io_t)dispatchChannel {
  248. return channel_;
  249. }
  250. - (dispatch_fd_t)fileDescriptor {
  251. return dispatch_io_get_descriptor(channel_);
  252. }
  253. - (BOOL)openOnQueue:(dispatch_queue_t)queue error:(NSError**)error onEnd:(void(^)(NSError*))onEnd {
  254. assert(queue != nil);
  255. assert(channel_ == nil);
  256. queue_ = queue;
  257. // Create socket
  258. dispatch_fd_t fd = socket(AF_UNIX, SOCK_STREAM, 0);
  259. if (fd == -1) {
  260. if (error) *error = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
  261. return NO;
  262. }
  263. // prevent SIGPIPE
  264. int on = 1;
  265. setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));
  266. // Connect socket
  267. struct sockaddr_un addr;
  268. addr.sun_family = AF_UNIX;
  269. strcpy(addr.sun_path, "/var/run/usbmuxd");
  270. socklen_t socklen = sizeof(addr);
  271. if (connect(fd, (struct sockaddr*)&addr, socklen) == -1) {
  272. if (error) *error = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:errno userInfo:nil];
  273. return NO;
  274. }
  275. channel_ = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue_, ^(int error) {
  276. close(fd);
  277. if (onEnd) {
  278. onEnd(error == 0 ? nil : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil]);
  279. }
  280. });
  281. return YES;
  282. }
  283. - (void)listenWithBroadcastHandler:(void(^)(NSDictionary *packet))broadcastHandler callback:(void(^)(NSError*))callback {
  284. autoReadPackets_ = YES;
  285. [self scheduleReadPacketWithBroadcastHandler:broadcastHandler];
  286. NSDictionary *packet = [Lookin_PTUSBChannel packetDictionaryWithPacketType:kPlistPacketTypeListen payload:nil];
  287. [self sendRequest:packet callback:^(NSError *error_, NSDictionary *responsePacket) {
  288. if (!callback)
  289. return;
  290. NSError *error = error_;
  291. [self errorFromPlistResponse:responsePacket error:&error];
  292. callback(error);
  293. }];
  294. }
  295. - (BOOL)errorFromPlistResponse:(NSDictionary*)packet error:(NSError**)error {
  296. if (!*error) {
  297. NSNumber *n = [packet objectForKey:@"Number"];
  298. if (!n) {
  299. *error = [NSError errorWithDomain:Lookin_PTUSBHubErrorDomain code:(n ? n.integerValue : 0) userInfo:nil];
  300. return NO;
  301. }
  302. USBMuxReplyCode replyCode = (USBMuxReplyCode)n.integerValue;
  303. if (replyCode != 0) {
  304. NSString *errmessage = @"Unspecified error";
  305. switch (replyCode) {
  306. case USBMuxReplyCodeBadCommand: errmessage = @"illegal command"; break;
  307. case USBMuxReplyCodeBadDevice: errmessage = @"unknown device"; break;
  308. case USBMuxReplyCodeConnectionRefused: errmessage = @"connection refused"; break;
  309. case USBMuxReplyCodeBadVersion: errmessage = @"invalid version"; break;
  310. default: break;
  311. }
  312. *error = [NSError errorWithDomain:Lookin_PTUSBHubErrorDomain code:replyCode userInfo:[NSDictionary dictionaryWithObject:errmessage forKey:NSLocalizedDescriptionKey]];
  313. return NO;
  314. }
  315. }
  316. return YES;
  317. }
  318. - (uint32_t)nextPacketTag {
  319. return ++nextPacketTag_;
  320. }
  321. - (void)sendRequest:(NSDictionary*)packet callback:(void(^)(NSError*, NSDictionary*))callback {
  322. uint32_t tag = [self nextPacketTag];
  323. [self sendPacket:packet tag:tag callback:^(NSError *error) {
  324. if (error) {
  325. callback(error, nil);
  326. return;
  327. }
  328. // TODO: timeout un-triggered callbacks in responseQueue_
  329. if (!self->responseQueue_) self->responseQueue_ = [NSMutableDictionary new];
  330. [self->responseQueue_ setObject:callback forKey:[NSNumber numberWithUnsignedInt:tag]];
  331. }];
  332. // We are awaiting a response
  333. [self setNeedsReadingPacket];
  334. }
  335. - (void)setNeedsReadingPacket {
  336. if (!isReadingPackets_) {
  337. [self scheduleReadPacketWithBroadcastHandler:nil];
  338. }
  339. }
  340. - (void)scheduleReadPacketWithBroadcastHandler:(void(^)(NSDictionary *packet))broadcastHandler {
  341. assert(isReadingPackets_ == NO);
  342. [self scheduleReadPacketWithCallback:^(NSError *error, NSDictionary *packet, uint32_t packetTag) {
  343. // Interpret the package we just received
  344. if (packetTag == 0) {
  345. // Broadcast message
  346. //NSLog(@"Received broadcast: %@", packet);
  347. if (broadcastHandler) broadcastHandler(packet);
  348. } else if (self->responseQueue_) {
  349. // Reply
  350. NSNumber *key = [NSNumber numberWithUnsignedInt:packetTag];
  351. void(^requestCallback)(NSError*,NSDictionary*) = [self->responseQueue_ objectForKey:key];
  352. if (requestCallback) {
  353. [self->responseQueue_ removeObjectForKey:key];
  354. requestCallback(error, packet);
  355. } else {
  356. NSLog(@"Warning: Ignoring reply packet for which there is no registered callback. Packet => %@", packet);
  357. }
  358. }
  359. // Schedule reading another incoming package
  360. if (self->autoReadPackets_) {
  361. [self scheduleReadPacketWithBroadcastHandler:broadcastHandler];
  362. }
  363. }];
  364. }
  365. - (void)scheduleReadPacketWithCallback:(void(^)(NSError*, NSDictionary*, uint32_t))callback {
  366. static usbmux_packet_t ref_upacket;
  367. isReadingPackets_ = YES;
  368. // Read the first `sizeof(ref_upacket.size)` bytes off the channel_
  369. dispatch_io_read(channel_, 0, sizeof(ref_upacket.size), queue_, ^(bool done, dispatch_data_t data, int error) {
  370. //NSLog(@"dispatch_io_read 0,4: done=%d data=%p error=%d", done, data, error);
  371. if (!done)
  372. return;
  373. if (error) {
  374. self->isReadingPackets_ = NO;
  375. callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], nil, 0);
  376. return;
  377. }
  378. // Read size of incoming usbmux_packet_t
  379. uint32_t upacket_len = 0;
  380. char *buffer = NULL;
  381. size_t buffer_size = 0;
  382. PT_PRECISE_LIFETIME_UNUSED dispatch_data_t map_data = dispatch_data_create_map(data, (const void **)&buffer, &buffer_size); // objc_precise_lifetime guarantees 'map_data' isn't released before memcpy has a chance to do its thing
  383. assert(buffer_size == sizeof(ref_upacket.size));
  384. assert(sizeof(upacket_len) == sizeof(ref_upacket.size));
  385. memcpy((void *)&(upacket_len), (const void *)buffer, buffer_size);
  386. #if PT_DISPATCH_RETAIN_RELEASE
  387. dispatch_release(map_data);
  388. #endif
  389. // Allocate a new usbmux_packet_t for the expected size
  390. uint32_t payloadLength = upacket_len - (uint32_t)sizeof(usbmux_packet_t);
  391. usbmux_packet_t *upacket = usbmux_packet_alloc(payloadLength);
  392. // Read rest of the incoming usbmux_packet_t
  393. off_t offset = sizeof(ref_upacket.size);
  394. dispatch_io_read(self->channel_, offset, (size_t)(upacket->size - offset), self->queue_, ^(bool done, dispatch_data_t data, int error) {
  395. //NSLog(@"dispatch_io_read X,Y: done=%d data=%p error=%d", done, data, error);
  396. if (!done) {
  397. return;
  398. }
  399. self->isReadingPackets_ = NO;
  400. if (error) {
  401. callback([[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil], nil, 0);
  402. usbmux_packet_free(upacket);
  403. return;
  404. }
  405. if (upacket_len > kUsbmuxPacketMaxPayloadSize) {
  406. callback(
  407. [[NSError alloc] initWithDomain:Lookin_PTUSBHubErrorDomain code:1 userInfo:@{
  408. NSLocalizedDescriptionKey:@"Received a packet that is too large"}],
  409. nil,
  410. 0
  411. );
  412. usbmux_packet_free(upacket);
  413. return;
  414. }
  415. // Copy read bytes onto our usbmux_packet_t
  416. char *buffer = NULL;
  417. size_t buffer_size = 0;
  418. PT_PRECISE_LIFETIME_UNUSED dispatch_data_t map_data = dispatch_data_create_map(data, (const void **)&buffer, &buffer_size);
  419. assert(buffer_size == upacket->size - offset);
  420. memcpy(((void *)(upacket))+offset, (const void *)buffer, buffer_size);
  421. #if PT_DISPATCH_RETAIN_RELEASE
  422. dispatch_release(map_data);
  423. #endif
  424. // We only support plist protocol
  425. if (upacket->protocol != USBMuxPacketProtocolPlist) {
  426. callback([[NSError alloc] initWithDomain:Lookin_PTUSBHubErrorDomain code:0 userInfo:[NSDictionary dictionaryWithObject:@"Unexpected package protocol" forKey:NSLocalizedDescriptionKey]], nil, upacket->tag);
  427. usbmux_packet_free(upacket);
  428. return;
  429. }
  430. // Only one type of packet in the plist protocol
  431. if (upacket->type != USBMuxPacketTypePlistPayload) {
  432. callback([[NSError alloc] initWithDomain:Lookin_PTUSBHubErrorDomain code:0 userInfo:[NSDictionary dictionaryWithObject:@"Unexpected package type" forKey:NSLocalizedDescriptionKey]], nil, upacket->tag);
  433. usbmux_packet_free(upacket);
  434. return;
  435. }
  436. // Try to decode any payload as plist
  437. NSError *err = nil;
  438. NSDictionary *dict = nil;
  439. if (usbmux_packet_payload_size(upacket)) {
  440. dict = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytesNoCopy:usbmux_packet_payload(upacket) length:usbmux_packet_payload_size(upacket) freeWhenDone:NO] options:NSPropertyListImmutable format:NULL error:&err];
  441. }
  442. // Invoke callback
  443. callback(err, dict, upacket->tag);
  444. usbmux_packet_free(upacket);
  445. });
  446. });
  447. }
  448. - (void)sendPacketOfType:(USBMuxPacketType)type
  449. overProtocol:(USBMuxPacketProtocol)protocol
  450. tag:(uint32_t)tag
  451. payload:(NSData*)payload
  452. callback:(void(^)(NSError*))callback
  453. {
  454. assert(payload.length <= kUsbmuxPacketMaxPayloadSize);
  455. usbmux_packet_t *upacket = usbmux_packet_create(
  456. protocol,
  457. type,
  458. tag,
  459. payload ? payload.bytes : nil,
  460. (uint32_t)(payload ? payload.length : 0)
  461. );
  462. dispatch_data_t data = dispatch_data_create((const void*)upacket, upacket->size, queue_, ^{
  463. // Free packet when data is freed
  464. usbmux_packet_free(upacket);
  465. });
  466. //NSData *data1 = [NSData dataWithBytesNoCopy:(void*)upacket length:upacket->size freeWhenDone:NO];
  467. //[data1 writeToFile:[NSString stringWithFormat:@"/Users/rsms/c-packet-%u.data", tag] atomically:NO];
  468. [self sendDispatchData:data callback:callback];
  469. }
  470. - (void)sendPacket:(NSDictionary*)packet tag:(uint32_t)tag callback:(void(^)(NSError*))callback {
  471. NSError *error = nil;
  472. // NSPropertyListBinaryFormat_v1_0
  473. NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:packet format:NSPropertyListXMLFormat_v1_0 options:0 error:&error];
  474. if (!plistData) {
  475. callback(error);
  476. } else {
  477. [self sendPacketOfType:USBMuxPacketTypePlistPayload overProtocol:USBMuxPacketProtocolPlist tag:tag payload:plistData callback:callback];
  478. }
  479. }
  480. - (void)sendDispatchData:(dispatch_data_t)data callback:(void(^)(NSError*))callback {
  481. off_t offset = 0;
  482. dispatch_io_write(channel_, offset, data, queue_, ^(bool done, dispatch_data_t data, int _errno) {
  483. //NSLog(@"dispatch_io_write: done=%d data=%p error=%d", done, data, error);
  484. if (!done)
  485. return;
  486. if (callback) {
  487. NSError *err = nil;
  488. if (_errno) err = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:_errno userInfo:nil];
  489. callback(err);
  490. }
  491. });
  492. #if PT_DISPATCH_RETAIN_RELEASE
  493. dispatch_release(data); // Release our ref. A ref is still held by dispatch_io_write
  494. #endif
  495. }
  496. #pragma clang diagnostic push
  497. #pragma clang diagnostic ignored "-Wunused-getter-return-value"
  498. - (void)sendData:(NSData*)data callback:(void(^)(NSError*))callback {
  499. dispatch_data_t ddata = dispatch_data_create((const void*)data.bytes, data.length, queue_, ^{
  500. // trick to have the block capture and retain the data
  501. [data length];
  502. });
  503. [self sendDispatchData:ddata callback:callback];
  504. }
  505. #pragma clang diagnostic pop
  506. - (void)readFromOffset:(off_t)offset length:(size_t)length callback:(void(^)(NSError *error, dispatch_data_t data))callback {
  507. dispatch_io_read(channel_, offset, length, queue_, ^(bool done, dispatch_data_t data, int _errno) {
  508. if (!done)
  509. return;
  510. NSError *error = nil;
  511. if (_errno != 0) {
  512. error = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:_errno userInfo:nil];
  513. }
  514. callback(error, data);
  515. });
  516. }
  517. - (void)cancel {
  518. if (channel_) {
  519. dispatch_io_close(channel_, 0);
  520. }
  521. }
  522. - (void)stop {
  523. if (channel_) {
  524. dispatch_io_close(channel_, DISPATCH_IO_STOP);
  525. }
  526. }
  527. @end