mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	
		
			
				
	
	
		
			611 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			611 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <libusbp_internal.h>
 | |
| #include <mach/mach_time.h>
 | |
| 
 | |
| struct libusbp_generic_handle
 | |
| {
 | |
|     // ioh is short for "IOKit Handle"
 | |
|     IOUSBInterfaceInterface182 ** ioh;
 | |
| 
 | |
|     IOCFPlugInInterface ** plug_in;
 | |
| 
 | |
|     mach_port_t async_port;
 | |
| 
 | |
|     // Timeouts are stored in milliseconds.  0 is forever.
 | |
|     uint32_t out_timeout[MAX_ENDPOINT_NUMBER + 1];
 | |
|     uint32_t in_timeout[MAX_ENDPOINT_NUMBER + 1];
 | |
| 
 | |
|     // These arrays allow us to convert from a normal USB endpoint address like
 | |
|     // 0x82 (Endpoint 2 IN) to the pipe index needed by IOUSBInterface functions.
 | |
|     uint8_t out_pipe_index[MAX_ENDPOINT_NUMBER + 1];
 | |
|     uint8_t in_pipe_index[MAX_ENDPOINT_NUMBER + 1];
 | |
| };
 | |
| 
 | |
| #ifdef LIBUSBP_LOG
 | |
| void log_mach_msg(mach_msg_return_t mr, mach_msg_header_t * header)
 | |
| {
 | |
|     uint64_t t = mach_absolute_time();
 | |
|     size_t main_payload_size = header->msgh_size - sizeof(mach_msg_header_t);
 | |
|     assert(main_payload_size < 1024);  // avoid buffer overruns
 | |
|     uint8_t * payload = (uint8_t *)(header + 1);
 | |
|     mach_msg_trailer_t * trailer = (mach_msg_trailer_t *)(payload + main_payload_size);
 | |
|     size_t payload_size = main_payload_size + trailer->msgh_trailer_size;
 | |
|     assert(payload_size < 1024);  // avoid buffer overruns
 | |
| 
 | |
|     printf("mach_msg at %lld: %#x, %s, %d\n", t, mr, mach_error_string(mr), header->msgh_size);
 | |
|     printf("  bits: %#x\n", header->msgh_bits);
 | |
|     printf("  ports: %d, %d, %d\n", header->msgh_remote_port,
 | |
|         header->msgh_local_port, header->msgh_voucher_port);
 | |
|     printf("  id: %d\n", header->msgh_id);
 | |
|     for (size_t i = 0; i < payload_size; i++)
 | |
|     {
 | |
|         if ((i % 8) == 0) { printf("    "); }
 | |
|         printf("%02x ", payload[i]);
 | |
|         if ((i % 8) == 7 || i == payload_size - 1) { printf("\n"); }
 | |
|     }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| // Gets the properties of all the pipes and uses that to populate the
 | |
| // out_pipe_index and in_pipe_index arrays.
 | |
| static libusbp_error * process_pipe_properties(libusbp_generic_handle * handle)
 | |
| {
 | |
|     uint8_t endpoint_count;
 | |
|     kern_return_t kr = (*handle->ioh)->GetNumEndpoints(handle->ioh, &endpoint_count);
 | |
|     if (kr != KERN_SUCCESS)
 | |
|     {
 | |
|         return error_create_mach(kr, "Failed to get number of endpoints.");
 | |
|     }
 | |
| 
 | |
|     for(uint32_t i = 1; i <= endpoint_count; i++)
 | |
|     {
 | |
|         uint8_t direction;
 | |
|         uint8_t endpoint_number;
 | |
|         uint8_t transfer_type;
 | |
|         uint16_t max_packet_size;
 | |
|         uint8_t interval;
 | |
|         kern_return_t kr = (*handle->ioh)->GetPipeProperties(handle->ioh, i,
 | |
|             &direction, &endpoint_number, &transfer_type, &max_packet_size, &interval);
 | |
|         if (kr != KERN_SUCCESS)
 | |
|         {
 | |
|             return error_create_mach(kr, "Failed to get pipe properties for pipe %d.", i);
 | |
|         }
 | |
| 
 | |
|         if (endpoint_number <= MAX_ENDPOINT_NUMBER)
 | |
|         {
 | |
|             if (direction)
 | |
|             {
 | |
|                 handle->in_pipe_index[endpoint_number] = i;
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 handle->out_pipe_index[endpoint_number] = i;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| // Sets the configruation of a device to 1 if it is not configured.
 | |
| static libusbp_error * set_configuration(io_service_t service)
 | |
| {
 | |
|     assert(service != MACH_PORT_NULL);
 | |
| 
 | |
|     libusbp_error * error = NULL;
 | |
| 
 | |
|     // Turn io_service_t into something we can actually use.
 | |
|     IOUSBDeviceInterface ** dev_handle = NULL;
 | |
|     IOCFPlugInInterface ** plug_in = NULL;
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         error = service_to_interface(service,
 | |
|             kIOUSBDeviceUserClientTypeID,
 | |
|             CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID197),
 | |
|             (void **)&dev_handle,
 | |
|             &plug_in);
 | |
|     }
 | |
| 
 | |
|     uint8_t config_num = 0;
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         kern_return_t kr = (*dev_handle)->GetConfiguration(dev_handle, &config_num);
 | |
|         if (kr != KERN_SUCCESS)
 | |
|         {
 | |
|             // We failed to get the current configuration.  The documentation of
 | |
|             // GetConfiguration doesn't state whether it actually does I/O on
 | |
|             // the device or not, but if it does do I/O then one possible reason
 | |
|             // for GetConfiguration to fail is that the device simply doesn't
 | |
|             // support the request.  Let's just assume the device is not
 | |
|             // configured and set it to configuration 1.
 | |
|             config_num = 0;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Open the device for exclusive access.
 | |
|     if (error == NULL && config_num == 0)
 | |
|     {
 | |
|         kern_return_t kr = (*dev_handle)->USBDeviceOpen(dev_handle);
 | |
|         if (kr != KERN_SUCCESS)
 | |
|         {
 | |
|             error = error_create_mach(kr, "Failed to open handle to device.");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Set the configuration.
 | |
|     if (error == NULL && config_num == 0)
 | |
|     {
 | |
|         uint8_t new_config_num = 1;
 | |
|         kern_return_t kr = (*dev_handle)->SetConfiguration(dev_handle, new_config_num);
 | |
|         if (kr != KERN_SUCCESS)
 | |
|         {
 | |
|             error = error_create_mach(kr, "Failed to set configuration to 1.");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Clean up.
 | |
|     if (dev_handle != NULL)
 | |
|     {
 | |
|         (*dev_handle)->USBDeviceClose(dev_handle);
 | |
|         (*dev_handle)->Release(dev_handle);
 | |
|         dev_handle = NULL;
 | |
|         (*plug_in)->Release(plug_in);
 | |
|         plug_in = NULL;
 | |
|     }
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| // Sets the configruation of the device to 1 if it is not set, and then
 | |
| // retrieves the io_service_t representing the specific interface we want
 | |
| // to talk to.
 | |
| static libusbp_error * set_configuration_and_get_service(
 | |
|     const libusbp_generic_interface * gi,
 | |
|     io_service_t * service)
 | |
| {
 | |
|     assert(gi != NULL);
 | |
|     assert(service != NULL);
 | |
|     *service = MACH_PORT_NULL;
 | |
| 
 | |
|     uint64_t device_id = generic_interface_get_device_id(gi);
 | |
|     uint8_t interface_number = generic_interface_get_interface_number(gi);
 | |
| 
 | |
|     libusbp_error * error = NULL;
 | |
| 
 | |
|     // Get an io_service_t for the physical device.
 | |
|     io_service_t device_service = MACH_PORT_NULL;
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         error = service_get_from_id(device_id, &device_service);
 | |
|     }
 | |
| 
 | |
|     // Set the configruation to 1 if it is not set.
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         error = set_configuration(device_service);
 | |
|     }
 | |
| 
 | |
|     // Get the io_service_t for the interface.
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         error = service_get_usb_interface(device_service, interface_number, service);
 | |
|     }
 | |
| 
 | |
|     if (device_service != MACH_PORT_NULL) { IOObjectRelease(device_service); }
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| libusbp_error * libusbp_generic_handle_open(
 | |
|     const libusbp_generic_interface * gi,
 | |
|     libusbp_generic_handle ** handle)
 | |
| {
 | |
|     if (handle == NULL)
 | |
|     {
 | |
|         return error_create("Generic handle output pointer is null.");
 | |
|     }
 | |
| 
 | |
|     *handle = NULL;
 | |
| 
 | |
|     if (gi == NULL)
 | |
|     {
 | |
|         return error_create("Generic interface is null.");
 | |
|     }
 | |
| 
 | |
|     libusbp_error * error = NULL;
 | |
| 
 | |
|     // Allocate memory for the handle.
 | |
|     libusbp_generic_handle * new_handle = NULL;
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         new_handle = calloc(1, sizeof(libusbp_generic_handle));
 | |
|         if (new_handle == NULL)
 | |
|         {
 | |
|             error = &error_no_memory;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Get the io_service_t representing the IOUSBInterface.
 | |
|     io_service_t service = MACH_PORT_NULL;
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         uint64_t interface_id;
 | |
|         bool has_interface_id = generic_interface_get_interface_id(gi, &interface_id);
 | |
|         if (has_interface_id)
 | |
|         {
 | |
|             // This generic interface has an I/O Registry ID for a specific USB interface,
 | |
|             // so lets just get the corresponding io_service_t.
 | |
|             error = service_get_from_id(interface_id, &service);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             // This generic interface does not have an ID for the specific USB interface yet,
 | |
|             // probably because it is a non-composite device and we need to put it into the
 | |
|             // right configuration.
 | |
| 
 | |
|             error = set_configuration_and_get_service(gi, &service);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Get the IOInterfaceInterface
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         error = service_to_interface(service,
 | |
|             kIOUSBInterfaceUserClientTypeID,
 | |
|             CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID182),
 | |
|             (void **)&new_handle->ioh,
 | |
|             &new_handle->plug_in);
 | |
|     }
 | |
| 
 | |
|     // Open the interface for exclusive access.
 | |
|     // (Otherwise, we can't read from non-zero pipes.)
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         kern_return_t kr = (*new_handle->ioh)->USBInterfaceOpen(new_handle->ioh);
 | |
|         if (kr != KERN_SUCCESS)
 | |
|         {
 | |
|             error = error_create_mach(kr, "Failed to open generic handle.");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Retrieve and store important information from the pipe properties.
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         error = process_pipe_properties(new_handle);
 | |
|     }
 | |
| 
 | |
|     // Create the async port.
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         kern_return_t kr = (*new_handle->ioh)->CreateInterfaceAsyncPort(new_handle->ioh,
 | |
|             &new_handle->async_port);
 | |
|         if (kr != KERN_SUCCESS)
 | |
|         {
 | |
|             error = error_create_mach(kr, "Failed to create asynchronous port.");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Pass the handle to the caller.
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         *handle = new_handle;
 | |
|         new_handle = NULL;
 | |
|     }
 | |
| 
 | |
|     // Clean up.
 | |
|     libusbp_generic_handle_close(new_handle);
 | |
|     if (service != MACH_PORT_NULL) { IOObjectRelease(service); }
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| void libusbp_generic_handle_close(libusbp_generic_handle * handle)
 | |
| {
 | |
|     if (handle != NULL)
 | |
|     {
 | |
|         if (handle->ioh != NULL)
 | |
|         {
 | |
|             (*handle->ioh)->USBInterfaceClose(handle->ioh);
 | |
|             (*handle->ioh)->Release(handle->ioh);
 | |
|             (*handle->plug_in)->Release(handle->plug_in);
 | |
|         }
 | |
|         free(handle);
 | |
|     }
 | |
| }
 | |
| 
 | |
| libusbp_error * libusbp_generic_handle_open_async_in_pipe(
 | |
|     libusbp_generic_handle * handle,
 | |
|     uint8_t pipe_id,
 | |
|     libusbp_async_in_pipe ** pipe)
 | |
| {
 | |
|     return async_in_pipe_create(handle, pipe_id, pipe);
 | |
| }
 | |
| 
 | |
| libusbp_error * libusbp_generic_handle_set_timeout(
 | |
|     libusbp_generic_handle * handle,
 | |
|     uint8_t pipe_id,
 | |
|     uint32_t timeout)
 | |
| {
 | |
|     if (handle == NULL)
 | |
|     {
 | |
|         return error_create("Generic handle is null.");
 | |
|     }
 | |
| 
 | |
|     libusbp_error * error = NULL;
 | |
| 
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         error = check_pipe_id(pipe_id);
 | |
|     }
 | |
| 
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         uint8_t endpoint_number = pipe_id & MAX_ENDPOINT_NUMBER;
 | |
| 
 | |
|         if (pipe_id & 0x80)
 | |
|         {
 | |
|             handle->in_timeout[endpoint_number] = timeout;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             handle->out_timeout[endpoint_number] = timeout;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| libusbp_error * libusbp_control_transfer(
 | |
|     libusbp_generic_handle * handle,
 | |
|     uint8_t bmRequestType,
 | |
|     uint8_t bRequest,
 | |
|     uint16_t wValue,
 | |
|     uint16_t wIndex,
 | |
|     void * buffer,
 | |
|     uint16_t wLength,
 | |
|     size_t * transferred)
 | |
| {
 | |
|     if (transferred != NULL)
 | |
|     {
 | |
|         *transferred = 0;
 | |
|     }
 | |
| 
 | |
|     if (handle == NULL)
 | |
|     {
 | |
|         return error_create("Generic handle is null.");
 | |
|     }
 | |
| 
 | |
|     IOUSBDevRequestTO request;
 | |
|     request.bmRequestType = bmRequestType;
 | |
|     request.bRequest = bRequest;
 | |
|     request.wValue = wValue;
 | |
|     request.wIndex = wIndex;
 | |
|     request.wLength = wLength;
 | |
|     request.pData = buffer;
 | |
|     request.completionTimeout = handle->out_timeout[0];
 | |
|     request.wLenDone = 0;
 | |
| 
 | |
|     kern_return_t kr = (*handle->ioh)->ControlRequestTO(handle->ioh, 0, &request);
 | |
|     if (transferred != NULL) { *transferred = request.wLenDone; }
 | |
|     if (kr != KERN_SUCCESS)
 | |
|     {
 | |
|         return error_create_mach(kr, "Control transfer failed.");
 | |
|     }
 | |
| 
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| libusbp_error * libusbp_read_pipe(
 | |
|     libusbp_generic_handle * handle,
 | |
|     uint8_t pipe_id,
 | |
|     void * buffer,
 | |
|     size_t size,
 | |
|     size_t * transferred)
 | |
| {
 | |
|     if (transferred != NULL)
 | |
|     {
 | |
|         *transferred = 0;
 | |
|     }
 | |
| 
 | |
|     if (handle == NULL)
 | |
|     {
 | |
|         return error_create("Generic handle is null.");
 | |
|     }
 | |
| 
 | |
|     libusbp_error * error = NULL;
 | |
| 
 | |
|     if (error == NULL && size == 0)
 | |
|     {
 | |
|         error = error_create("Transfer size 0 is not allowed.");
 | |
|     }
 | |
| 
 | |
|     if (error == NULL && size > UINT32_MAX)
 | |
|     {
 | |
|         error = error_create("Transfer size is too large.");
 | |
|     }
 | |
| 
 | |
|     if (error == NULL && buffer == NULL)
 | |
|     {
 | |
|         error = error_create("Buffer is null.");
 | |
|     }
 | |
| 
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         error = check_pipe_id_in(pipe_id);
 | |
|     }
 | |
| 
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         uint8_t endpoint_number = pipe_id & MAX_ENDPOINT_NUMBER;
 | |
|         uint32_t no_data_timeout = 0;
 | |
|         uint32_t completion_timeout = handle->in_timeout[endpoint_number];
 | |
|         uint32_t iokit_size = size;
 | |
|         uint32_t pipe_index = handle->in_pipe_index[endpoint_number];
 | |
|         kern_return_t kr = (*handle->ioh)->ReadPipeTO(handle->ioh, pipe_index,
 | |
|           buffer, &iokit_size, no_data_timeout, completion_timeout);
 | |
|         if (transferred != NULL) { *transferred = iokit_size; }
 | |
|         if (kr != KERN_SUCCESS)
 | |
|         {
 | |
|             error = error_create_mach(kr, "");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (error != NULL)
 | |
|     {
 | |
|         error = error_add(error, "Failed to read from pipe.");
 | |
|     }
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| libusbp_error * libusbp_write_pipe(
 | |
|     libusbp_generic_handle * handle,
 | |
|     uint8_t pipe_id,
 | |
|     const void * buffer,
 | |
|     size_t size,
 | |
|     size_t * transferred)
 | |
| {
 | |
|     if (transferred != NULL)
 | |
|     {
 | |
|         *transferred = 0;
 | |
|     }
 | |
| 
 | |
|     if (handle == NULL)
 | |
|     {
 | |
|         return error_create("Generic handle is null.");
 | |
|     }
 | |
| 
 | |
|     libusbp_error * error = NULL;
 | |
| 
 | |
|     if (error == NULL && size > UINT32_MAX)
 | |
|     {
 | |
|         error = error_create("Transfer size is too large.");
 | |
|     }
 | |
| 
 | |
|     if (error == NULL && buffer == NULL && size)
 | |
|     {
 | |
|         error = error_create("Buffer is null.");
 | |
|     }
 | |
| 
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         error = check_pipe_id_out(pipe_id);
 | |
|     }
 | |
| 
 | |
|     if (error == NULL)
 | |
|     {
 | |
|         uint8_t endpoint_number = pipe_id & MAX_ENDPOINT_NUMBER;
 | |
|         uint32_t no_data_timeout = 0;
 | |
|         uint32_t completion_timeout = handle->out_timeout[endpoint_number];
 | |
|         uint32_t pipe_index = handle->out_pipe_index[endpoint_number];
 | |
|         kern_return_t kr = (*handle->ioh)->WritePipeTO(handle->ioh, pipe_index,
 | |
|           (void *)buffer, size, no_data_timeout, completion_timeout);
 | |
|         if (kr != KERN_SUCCESS)
 | |
|         {
 | |
|             error = error_create_mach(kr, "");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (error == NULL && transferred != NULL)
 | |
|     {
 | |
|         // There was no error, so just assume the entire amount was transferred.
 | |
|         // WritePipeTO does not give us a number.
 | |
|         *transferred = size;
 | |
|     }
 | |
| 
 | |
|     if (error != NULL)
 | |
|     {
 | |
|         error = error_add(error, "Failed to write to pipe.");
 | |
|     }
 | |
| 
 | |
|     return error;
 | |
| }
 | |
| 
 | |
| #pragma pack(4)
 | |
| typedef struct
 | |
| {
 | |
|     mach_msg_header_t header;
 | |
|     uint8_t buffer[1024];
 | |
| } our_msg;
 | |
| 
 | |
| libusbp_error * generic_handle_events(libusbp_generic_handle * handle)
 | |
| {
 | |
|     assert(handle != NULL);
 | |
| 
 | |
|     while(1)
 | |
|     {
 | |
|         // Check for messages on this handle's async port using mach_msg().
 | |
|         our_msg msg;
 | |
|         mach_msg_option_t option = MACH_RCV_MSG | MACH_RCV_TIMEOUT;
 | |
|         option |= MACH_RCV_LARGE;  // tmphax to receive large messages
 | |
|         mach_msg_size_t send_size = 0;
 | |
|         mach_msg_size_t rcv_size = sizeof(msg); // might need to make this bigger
 | |
|         mach_msg_timeout_t timeout = 0;  // non-blocking
 | |
|         mach_port_t notify = MACH_PORT_NULL;
 | |
|         mach_msg_return_t mr = mach_msg(&msg.header, option, send_size, rcv_size,
 | |
|             handle->async_port, timeout, notify);
 | |
| 
 | |
|         if (mr != MACH_MSG_SUCCESS)
 | |
|         {
 | |
|             if (mr == MACH_RCV_TIMED_OUT)
 | |
|             {
 | |
|                 // There was no message available on the port.
 | |
|                 return NULL;
 | |
|             }
 | |
|             else if (mr == MACH_RCV_TOO_LARGE)
 | |
|             {
 | |
|                 return error_create("Mach message received was too large: %d > %d\n",
 | |
|                     msg.header.msgh_size, (unsigned int)sizeof(msg));
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 return error_create_mach(mr, "Failed to receive mach message.");
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         #ifdef LIBUSBP_LOG
 | |
|         log_mach_msg(mr, &msg.header);
 | |
|         #endif
 | |
| 
 | |
|         // We have received a message from the mach port that contains a blob of
 | |
|         // binary data.  The blob seems to include a pointer to the
 | |
|         // async_in_transfer, a kern_return_t code for the transfer, and a pointer
 | |
|         // to the callback we specified when we submitted the transfer.  We aren't
 | |
|         // supposed to process that data ourselves; we are supposed to call
 | |
|         // IODispatchCalloutFromMessage.
 | |
|         //
 | |
|         // The third argument to IODispatchCalloutFromMessage is supposed to be a
 | |
|         // IONotificationPortRef, but we don't have access to the
 | |
|         // IONotificationPortRef, which is a protected member of IOUSBInterface
 | |
|         // class with no accessors.  Passing NULL (or any arbitary pointer) seems to
 | |
|         // work.  IODispatchCalloutFromMessage has no return value.
 | |
|         IODispatchCalloutFromMessage(0, &msg.header, NULL);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void ** libusbp_generic_handle_get_cf_plug_in(libusbp_generic_handle * handle)
 | |
| {
 | |
|     if (handle == NULL)
 | |
|     {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     return (void **)handle->plug_in;
 | |
| }
 | |
| 
 | |
| IOUSBInterfaceInterface182 ** generic_handle_get_ioh(const libusbp_generic_handle * handle)
 | |
| {
 | |
|     assert(handle != NULL);
 | |
|     return handle->ioh;
 | |
| }
 | |
| 
 | |
| uint8_t generic_handle_get_pipe_index(const libusbp_generic_handle * handle, uint8_t pipe_id)
 | |
| {
 | |
|     uint8_t endpoint_number = pipe_id & MAX_ENDPOINT_NUMBER;
 | |
|     if (pipe_id & 0x80)
 | |
|     {
 | |
|         return handle->in_pipe_index[endpoint_number];
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         return handle->out_pipe_index[endpoint_number];
 | |
|     }
 | |
| }
 |