mirror of
https://github.com/RPCS3/hidapi.git
synced 2026-07-21 00:45:21 -04:00
Made the Mac implementation robust to disconnects and re-connects while a device has an open handle.
This commit is contained in:
@@ -40,12 +40,19 @@ struct hid_device_ {
|
||||
IOHIDDeviceRef device_handle;
|
||||
int blocking;
|
||||
int uses_numbered_reports;
|
||||
int disconnected;
|
||||
CFStringRef run_loop_mode;
|
||||
uint8_t *input_report_buf;
|
||||
struct input_report *input_reports;
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
hid_device *next;
|
||||
};
|
||||
|
||||
/* Static list of all the devices open. This way when a device gets
|
||||
disconnected, its hid_device structure can be marked as disconnected
|
||||
from hid_device_removal_callback(). */
|
||||
static hid_device *device_list = NULL;
|
||||
|
||||
static hid_device *new_hid_device(void)
|
||||
{
|
||||
@@ -53,11 +60,75 @@ static hid_device *new_hid_device(void)
|
||||
dev->device_handle = NULL;
|
||||
dev->blocking = 1;
|
||||
dev->uses_numbered_reports = 0;
|
||||
dev->disconnected = 0;
|
||||
dev->run_loop_mode = NULL;
|
||||
dev->input_report_buf = NULL;
|
||||
dev->input_reports = NULL;
|
||||
dev->next = NULL;
|
||||
|
||||
pthread_mutex_init(&dev->mutex, NULL);
|
||||
|
||||
/* Add the new record to the device_list. */
|
||||
if (!device_list)
|
||||
device_list = dev;
|
||||
else {
|
||||
hid_device *d = device_list;
|
||||
while (d) {
|
||||
if (!d->next) {
|
||||
d->next = dev;
|
||||
break;
|
||||
}
|
||||
d = d->next;
|
||||
}
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void free_hid_device(hid_device *dev)
|
||||
{
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
/* Delete any input reports still left over. */
|
||||
struct input_report *rpt = dev->input_reports;
|
||||
while (rpt) {
|
||||
struct input_report *next = rpt->next;
|
||||
free(rpt->data);
|
||||
free(rpt);
|
||||
rpt = next;
|
||||
}
|
||||
|
||||
/* Free the string and the report buffer. The check for NULL
|
||||
is necessary here as CFRelease() doesn't handle NULL like
|
||||
free() and others do. */
|
||||
if (dev->run_loop_mode)
|
||||
CFRelease(dev->run_loop_mode);
|
||||
free(dev->input_report_buf);
|
||||
|
||||
pthread_mutex_destroy(&dev->mutex);
|
||||
|
||||
/* Remove it from the device list. */
|
||||
hid_device *d = device_list;
|
||||
if (d == dev) {
|
||||
device_list = d->next;
|
||||
}
|
||||
else {
|
||||
while (d) {
|
||||
if (d->next == dev) {
|
||||
d->next = d->next->next;
|
||||
break;
|
||||
}
|
||||
|
||||
d = d->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free the structure itself. */
|
||||
free(dev);
|
||||
|
||||
}
|
||||
|
||||
static IOHIDManagerRef hid_mgr = 0x0;
|
||||
|
||||
|
||||
@@ -346,6 +417,19 @@ hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short pr
|
||||
return handle;
|
||||
}
|
||||
|
||||
static void hid_device_removal_callback(void *context, IOReturn result,
|
||||
void *sender, IOHIDDeviceRef dev_ref)
|
||||
{
|
||||
hid_device *d = device_list;
|
||||
while (d) {
|
||||
if (d->device_handle == dev_ref) {
|
||||
d->disconnected = 1;
|
||||
}
|
||||
|
||||
d = d->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* The Run Loop calls this function for each input report received.
|
||||
This function puts the data into a linked list to be picked up by
|
||||
hid_read(). */
|
||||
@@ -430,8 +514,8 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
|
||||
IOHIDDeviceRegisterInputReportCallback(
|
||||
os_dev, dev->input_report_buf, max_input_report_len,
|
||||
&hid_report_callback, dev);
|
||||
IOHIDManagerRegisterDeviceRemovalCallback(hid_mgr, hid_device_removal_callback, NULL);
|
||||
|
||||
pthread_mutex_init(&dev->mutex, NULL);
|
||||
|
||||
return dev;
|
||||
}
|
||||
@@ -444,7 +528,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
|
||||
return_error:
|
||||
free(device_array);
|
||||
CFRelease(device_set);
|
||||
free(dev);
|
||||
free_hid_device(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -454,6 +538,10 @@ static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char
|
||||
size_t length_to_send;
|
||||
IOReturn res;
|
||||
|
||||
/* Return if the device has been disconnected. */
|
||||
if (dev->disconnected)
|
||||
return -1;
|
||||
|
||||
if (data[0] == 0x0) {
|
||||
/* Not using numbered Reports.
|
||||
Don't send the report number. */
|
||||
@@ -467,17 +555,20 @@ static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char
|
||||
length_to_send = length;
|
||||
}
|
||||
|
||||
res = IOHIDDeviceSetReport(dev->device_handle,
|
||||
type,
|
||||
data[0], /* Report ID*/
|
||||
data_to_send, length_to_send);
|
||||
if (!dev->disconnected) {
|
||||
res = IOHIDDeviceSetReport(dev->device_handle,
|
||||
type,
|
||||
data[0], /* Report ID*/
|
||||
data_to_send, length_to_send);
|
||||
|
||||
if (res == kIOReturnSuccess) {
|
||||
return length;
|
||||
if (res == kIOReturnSuccess) {
|
||||
return length;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
|
||||
@@ -512,6 +603,12 @@ int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
|
||||
ret_val = return_data(dev, data, length);
|
||||
goto ret;
|
||||
}
|
||||
|
||||
/* Return if the device has been disconnected. */
|
||||
if (dev->disconnected) {
|
||||
ret_val = -1;
|
||||
goto ret;
|
||||
}
|
||||
|
||||
/* There are no input reports queued up.
|
||||
Need to get some from the OS. */
|
||||
@@ -527,6 +624,14 @@ int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
|
||||
while (1) {
|
||||
code = CFRunLoopRunInMode(dev->run_loop_mode, 1000, TRUE);
|
||||
|
||||
/* Return if the device has been disconnected */
|
||||
if (code == kCFRunLoopRunFinished) {
|
||||
dev->disconnected = 1;
|
||||
ret_val = -1;
|
||||
goto ret;
|
||||
}
|
||||
|
||||
|
||||
/* Return if some data showed up. */
|
||||
if (dev->input_reports)
|
||||
break;
|
||||
@@ -543,6 +648,7 @@ int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
|
||||
goto ret;
|
||||
}
|
||||
else {
|
||||
dev->disconnected = 1;
|
||||
ret_val = -1; /* An error occured (maybe CTRL-C?). */
|
||||
goto ret;
|
||||
}
|
||||
@@ -551,6 +657,13 @@ int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
|
||||
/* Non-blocking. See if the OS has any reports to give. */
|
||||
SInt32 code;
|
||||
code = CFRunLoopRunInMode(dev->run_loop_mode, 0, TRUE);
|
||||
if (code == kCFRunLoopRunFinished) {
|
||||
/* The run loop is finished, indicating an error
|
||||
or the device had been disconnected. */
|
||||
dev->disconnected = 1;
|
||||
ret_val = -1;
|
||||
goto ret;
|
||||
}
|
||||
if (dev->input_reports) {
|
||||
/* Return the first one */
|
||||
ret_val = return_data(dev, data, length);
|
||||
@@ -586,6 +699,10 @@ int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data,
|
||||
CFIndex len = length;
|
||||
IOReturn res;
|
||||
|
||||
/* Return if the device has been unplugged. */
|
||||
if (dev->disconnected)
|
||||
return -1;
|
||||
|
||||
res = IOHIDDeviceGetReport(dev->device_handle,
|
||||
kIOHIDReportTypeFeature,
|
||||
data[0], /* Report ID */
|
||||
@@ -602,24 +719,14 @@ void HID_API_EXPORT hid_close(hid_device *dev)
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
/* Close the OS handle to the device. */
|
||||
IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeNone);
|
||||
|
||||
/* Delete any input reports still left over. */
|
||||
struct input_report *rpt = dev->input_reports;
|
||||
while (rpt) {
|
||||
struct input_report *next = rpt->next;
|
||||
free(rpt->data);
|
||||
free(rpt);
|
||||
rpt = next;
|
||||
/* Close the OS handle to the device, but only if it's not
|
||||
been unplugged. If it's been unplugged, then calling
|
||||
IOHIDDeviceClose() will crash. */
|
||||
if (!dev->disconnected) {
|
||||
IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeNone);
|
||||
}
|
||||
|
||||
/* Free the string and the report buffer. */
|
||||
CFRelease(dev->run_loop_mode);
|
||||
free(dev->input_report_buf);
|
||||
pthread_mutex_destroy(&dev->mutex);
|
||||
|
||||
free(dev);
|
||||
free_hid_device(dev);
|
||||
}
|
||||
|
||||
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
|
||||
|
||||
Reference in New Issue
Block a user