io/m_io_hid_mac.c
/* DO NOT use IOHIDDeviceSetReportWithCallback. As of macOS 10.12 it returns
 * a not implemented error. As of 10.13 it will cause a kernel panic.
 * To make writes non-blocking we have a write thread that uses the
 * blocking write function. */
DO NOT use IOHIDDeviceSetReportWithCallback. As of macOS 10.12 it returns
a not implemented error. As of 10.13 it will cause a kernel panic.
To make writes non-blocking we have a write thread that uses the
blocking write function.
DO NOT use IOHIDDeviceSetReportWithCallback . As of macOS 10 . 12 it returns a not implemented error . As of 10 . 13 it will cause a kernel panic . To make writes non - blocking we have a write thread that uses the blocking write function .
static void *M_io_hid_write_loop(void *arg)
{
	M_io_handle_t *handle = arg;
	M_io_layer_t  *layer;
	IOReturn       ioret;
	M_io_error_t   ioerr;
	const uint8_t *data;
	uint8_t        reportid;
	CFIndex        len;
	while (handle->run) {
		M_thread_mutex_lock(handle->write_lock);
		if (M_buf_len(handle->writebuf) == 0) {
			M_thread_cond_wait(handle->write_cond, handle->write_lock);
		}
		if (!handle->run || handle->device == NULL) {
			M_thread_mutex_unlock(handle->write_lock);
			break;
		}
		if (M_buf_len(handle->writebuf) == 0) {
			M_thread_mutex_unlock(handle->write_lock);
			continue;
		}
		handle->in_write = M_TRUE;
		M_thread_mutex_unlock(handle->write_lock);
		data     = (const uint8_t *)M_buf_peek(handle->writebuf);
		len      = (CFIndex)M_buf_len(handle->writebuf);
		reportid = data[0];
		if (!handle->uses_reportid) {
			data++;
			len--;
		}
		ioret = IOHIDDeviceSetReport(handle->device, kIOHIDReportTypeOutput, reportid, data, len);
		ioerr = M_io_mac_ioreturn_to_err(ioret);
		if (M_io_error_is_critical(ioerr)) {
			M_thread_mutex_lock(handle->write_lock);
			handle->in_write = M_FALSE;
			M_thread_mutex_unlock(handle->write_lock);
			layer = M_io_layer_acquire(handle->io, 0, NULL);
			M_snprintf(handle->error, sizeof(handle->error), "%s", M_io_mac_ioreturn_errormsg(ioret));
			M_io_hid_close_device(handle);
			M_io_layer_softevent_add(layer, M_TRUE, M_EVENT_TYPE_ERROR, ioerr);
			M_io_layer_release(layer);
			break;
		}
		if (ioerr == M_IO_ERROR_SUCCESS) {
			M_buf_truncate(handle->writebuf, 0);
		}
		M_thread_mutex_lock(handle->write_lock);
		handle->in_write = M_FALSE;
		M_thread_mutex_unlock(handle->write_lock);
		if (ioerr == M_IO_ERROR_SUCCESS) {
			layer = M_io_layer_acquire(handle->io, 0, NULL);
			M_io_layer_softevent_add(layer, M_TRUE, M_EVENT_TYPE_WRITE, M_IO_ERROR_SUCCESS);
			M_io_layer_release(layer);
		}
	}
	return NULL;
}
static void * M_io_hid_write_loop ( void * arg ) { M_io_handle_t * handle = arg ; M_io_layer_t * layer ; IOReturn ioret ; M_io_error_t ioerr ; const uint8_t * data ; uint8_t reportid ; CFIndex len ; while ( handle -> run ) { M_thread_mutex_lock ( handle -> write_lock ) ; if ( M_buf_len ( handle -> writebuf ) == 0 ) { M_thread_cond_wait ( handle -> write_cond , handle -> write_lock ) ; } if ( ! handle -> run || handle -> device == NULL ) { M_thread_mutex_unlock ( handle -> write_lock ) ; break ; } if ( M_buf_len ( handle -> writebuf ) == 0 ) { M_thread_mutex_unlock ( handle -> write_lock ) ; continue ; } handle -> in_write = M_TRUE ; M_thread_mutex_unlock ( handle -> write_lock ) ; data = ( const uint8_t * ) M_buf_peek ( handle -> writebuf ) ; len = ( CFIndex ) M_buf_len ( handle -> writebuf ) ; reportid = data [ 0 ] ; if ( ! handle -> uses_reportid ) { data ++ ; len -- ; } ioret = IOHIDDeviceSetReport ( handle -> device , kIOHIDReportTypeOutput , reportid , data , len ) ; ioerr = M_io_mac_ioreturn_to_err ( ioret ) ; if ( M_io_error_is_critical ( ioerr ) ) { M_thread_mutex_lock ( handle -> write_lock ) ; handle -> in_write = M_FALSE ; M_thread_mutex_unlock ( handle -> write_lock ) ; layer = M_io_layer_acquire ( handle -> io , 0 , NULL ) ; M_snprintf ( handle -> error , sizeof ( handle -> error ) , " " , M_io_mac_ioreturn_errormsg ( ioret ) ) ; M_io_hid_close_device ( handle ) ; M_io_layer_softevent_add ( layer , M_TRUE , M_EVENT_TYPE_ERROR , ioerr ) ; M_io_layer_release ( layer ) ; break ; } if ( ioerr == M_IO_ERROR_SUCCESS ) { M_buf_truncate ( handle -> writebuf , 0 ) ; } M_thread_mutex_lock ( handle -> write_lock ) ; handle -> in_write = M_FALSE ; M_thread_mutex_unlock ( handle -> write_lock ) ; if ( ioerr == M_IO_ERROR_SUCCESS ) { layer = M_io_layer_acquire ( handle -> io , 0 , NULL ) ; M_io_layer_softevent_add ( layer , M_TRUE , M_EVENT_TYPE_WRITE , M_IO_ERROR_SUCCESS ) ; M_io_layer_release ( layer ) ; } } return NULL ; }
DO NOT use IOHIDDeviceSetReportWithCallback.
DO NOT use IOHIDDeviceSetReportWithCallback .
/* Within this locked block in_write is false
		 * but writebuf is only modified in the write
		 * callback when it's holding the lock. We don't
		 * have to worry about checking the length here
		 * while data is being added. */ /* If there isn't anything to write we have nothing to
 		 * do right now. */ /* Don't send the report id in the data if we're not using report ids. */ /* Must do this AFTER write flag is set back to false. Otherwise, WRITE event
		 * handlers on main thread might be executed before the write flag is updated,
		 * causing them to get M_IO_WOULDBLOCK and then sit forever waiting for another
		 * WRITE event that will never be sent.
		 */