24 #include <sys/select.h>
31 #define LOG(level, text, ...) do {\
32 printf("%s[%d] - %s: " text, __FUNCTION__, __LINE__, level, ##__VA_ARGS__);}while(0);
34 #define ERROR(text, ...) do {\
35 printf("%s[%d] - %s: " text, __FUNCTION__, __LINE__, "ERROR", ##__VA_ARGS__);}while(0);
36 #define INFO(text, ...) do {\
37 printf("%s[%d] - %s: " text, __FUNCTION__, __LINE__, "INFO", ##__VA_ARGS__);}while(0);
40 #define DEBUG(text, ...) do {\
41 printf("%s[%d] - %s: " text, __FUNCTION__, __LINE__, "DEBUG", ##__VA_ARGS__);}while(0);
43 #define DEBUG(text, ...)
46 #define REPORT_IF_UNEQUAL(lhs, rhs) do {\
47 if((lhs) != (rhs)) ERROR("Unexpected error!\n");}while(0);
49 #define UDEV_ADD_EVENT "add"
50 #define UDEV_REMOVE_EVENT "remove"
51 static const suseconds_t MONITOR_TIMEOUT_USECS = 250000;
52 static const int PIPE_READ_FD = 0;
53 static const int PIPE_WRITE_FD = 1;
54 static const int CONTROL_MESSAGE_SIZE = 4;
56 const char * supported_property_list[] =
74 struct udev_device *m_device;
75 const char* m_devnode;
78 device_record(
int identifier,
struct udev_device * device,
const char* devnode) :
79 m_identifier(identifier), m_device(device), m_devnode(devnode)
81 DEBUG(
"adding device 0x%x, %s\n", (
unsigned int)m_device, m_devnode);
86 DEBUG(
"Unreffing device 0x%x, %s\n", (
unsigned int)m_device, m_devnode);
87 udev_device_unref(m_device);
89 inline struct udev_device* get_device() {
return m_device;}
90 inline int get_identifier() {
return m_identifier;}
91 inline const char * get_devnode() {
return m_devnode;}
96 std::list<device_record *> m_device_records;
97 pthread_mutex_t m_mutex;
99 void * m_callback_data;
100 struct udev *m_udev_context;
101 int m_last_used_identifier;
102 bool m_enable_monitoring;
103 pthread_t m_monitor_thread;
104 struct udev_monitor * m_monitor;
105 int m_control_pipe[2];
108 device_manager() : m_enable_monitoring(false), m_callback(NULL), m_last_used_identifier(0), m_monitor_thread(0)
110 pthread_mutexattr_t mutex_attribute;
111 REPORT_IF_UNEQUAL(0, pthread_mutexattr_init(&mutex_attribute));
112 REPORT_IF_UNEQUAL(0, pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE));
113 REPORT_IF_UNEQUAL(0, pthread_mutex_init(&m_mutex, &mutex_attribute));
115 INFO(
"Creating new device manager object.\n");
116 m_udev_context = udev_new();
117 if(NULL == m_udev_context)
119 ERROR(
"Critical error! udev_new() failed!\n");
123 INFO(
"Successfully created device manager object 0x%x with udev context 0x%x.\n",
124 (
unsigned int)
this, (
unsigned int)m_udev_context);
128 m_enable_monitoring =
true;
129 m_monitor = udev_monitor_new_from_netlink(m_udev_context,
"udev");
130 if(NULL == m_monitor)
132 ERROR(
"Critical error! Could not create monitor!\n");
138 if(0 != udev_monitor_filter_add_match_subsystem_devtype(m_monitor,
"usb",
"usb_device"))
140 ERROR(
"Critical error! Could not add filters to udev monitor.\n");
144 if(0 != udev_monitor_enable_receiving(m_monitor))
146 ERROR(
"Critical error! Could not enable monitoring!\n");
149 if(0 != pipe(m_control_pipe))
151 ERROR(
"Critical error! Could not create pipe.\n");
152 m_control_pipe[PIPE_READ_FD] = 0;
153 m_control_pipe[PIPE_WRITE_FD] = 0;
156 if(0 != pthread_create(&m_monitor_thread, NULL, device_manager::monitor_thread_wrapper, (
void *)
this))
158 ERROR(
"Critical error! Could not launch monitor thread!\n");
159 m_monitor_thread = 0;
167 INFO(
"Stopping monitor thread.\n");
168 m_enable_monitoring =
false;
170 if(0 != m_control_pipe[PIPE_WRITE_FD])
172 close(m_control_pipe[PIPE_WRITE_FD]);
175 if(0 != m_monitor_thread)
177 if(0 != pthread_join(m_monitor_thread, NULL))
179 ERROR(
"Error. Monitor thread did not join.\n");
183 if(0 != m_control_pipe[PIPE_READ_FD])
185 close(m_control_pipe[PIPE_READ_FD]);
188 reset_device_records();
190 if(NULL != m_monitor)
192 udev_monitor_unref(m_monitor);
195 INFO(
"Destroying device manager object.\n");
196 udev_unref(m_udev_context);
197 pthread_mutex_destroy(&m_mutex);
200 static void * monitor_thread_wrapper(
void* data)
203 obj->monitor_for_changes();
205 rusbCtrl_result_t init()
208 enumerate_connected_devices();
210 return RUSBCTRL_SUCCESS;
213 rusbCtrl_result_t term()
216 INFO(
"Clearing device records.\n");
217 REPORT_IF_UNEQUAL(0, pthread_mutex_lock(&m_mutex));
218 reset_device_records();
220 m_callback_data = NULL;
221 REPORT_IF_UNEQUAL(0, pthread_mutex_unlock(&m_mutex));
223 return RUSBCTRL_SUCCESS;
227 char * get_property(
int identifier,
const char *key)
230 std::list<device_record *>::iterator iter;
231 struct udev_device *device;
232 for(iter = m_device_records.begin(); iter != m_device_records.end(); iter++)
234 if(identifier == (*iter)->get_identifier())
236 device = (*iter)->get_device();
240 if(iter != m_device_records.end())
242 DEBUG(
"Found record with identifer 0x%x. Querying...\n", identifier);
243 const char * value = udev_device_get_sysattr_value(device, key);
246 ERROR(
"Could not find property %s.\n", key);
251 char * user_buffer = (
char*)malloc(strlen(value));
252 strncpy(user_buffer, value, strlen(value));
256 ERROR(
"Found no record for device with id 0x%x\n", identifier);
260 int register_callback(
rusbCtrl_devCallback_t callback,
void* callback_data,
int ** device_list,
int * device_list_size)
263 REPORT_IF_UNEQUAL(0, pthread_mutex_lock(&m_mutex));
264 m_callback = callback;
265 m_callback_data = callback_data;
266 if((NULL != device_list) && (NULL != device_list_size))
268 get_connected_devices_list(device_list, device_list_size);
272 ERROR(
"Empty pointers provided. Won't supply connected devices.\n");
274 REPORT_IF_UNEQUAL(0, pthread_mutex_unlock(&m_mutex));
276 return RUSBCTRL_SUCCESS;
279 void process_control_event()
283 if(0 == read(m_control_pipe[PIPE_READ_FD], (
void *)&message, CONTROL_MESSAGE_SIZE))
287 INFO(
"Detected EOF. Calling for shutdown.\n");
288 m_enable_monitoring =
false;
292 void monitor_for_changes()
294 INFO(
"monitor thread launched.\n");
295 int control_fd = m_control_pipe[PIPE_READ_FD];
296 int monitor_fd = udev_monitor_get_fd(m_monitor);
299 ERROR(
"Critical error! Could not get udev monitor fd.\n");
302 int max_fd = (monitor_fd > control_fd ? monitor_fd : control_fd);
304 while(
true == m_enable_monitoring)
306 fd_set monitor_fd_set;
307 FD_ZERO(&monitor_fd_set);
308 FD_SET(monitor_fd, &monitor_fd_set);
309 FD_SET(control_fd, &monitor_fd_set);
311 int ret = select((max_fd + 1), &monitor_fd_set, NULL, NULL, NULL);
312 DEBUG(
"Unblocking now. ret is 0x%x\n", ret);
315 ERROR(
"select() returned 0.\n");
316 m_enable_monitoring =
false;
322 if(0 != FD_ISSET(control_fd, &monitor_fd_set))
324 process_control_event();
326 if(0 != FD_ISSET(monitor_fd, &monitor_fd_set))
328 process_udev_monitor_event();
333 m_enable_monitoring =
false;
334 ERROR(
"Error polling monitor FD!\n");
339 INFO(
"Monitor thread shutting down.\n");
344 void reset_device_records()
346 if(0 != m_device_records.size())
348 std::list<device_record *>::iterator iter;
349 for(iter = m_device_records.begin(); iter != m_device_records.end(); iter++)
353 m_device_records.clear();
358 void get_connected_devices_list(
int ** device_list,
int * device_list_size)
361 *device_list_size = m_device_records.size();
362 if(0 != *device_list_size)
365 int * buffer = (
int *)malloc(*device_list_size *
sizeof(
int));
366 std::list<device_record *>::iterator iter;
368 for(iter = m_device_records.begin(); iter != m_device_records.end(); iter++)
370 buffer[i++] = (*iter)->get_identifier();
372 *device_list = buffer;
377 rusbCtrl_result_t enumerate_connected_devices()
379 rusbCtrl_result_t result = RUSBCTRL_SUCCESS;
380 REPORT_IF_UNEQUAL(0, pthread_mutex_lock(&m_mutex));
381 reset_device_records();
382 struct udev_enumerate *enumerator = udev_enumerate_new(m_udev_context);
383 if(NULL == enumerator)
385 REPORT_IF_UNEQUAL(0, pthread_mutex_unlock(&m_mutex));
386 ERROR(
"Could not create udev enumerator!\n");
387 return RUSBCTRL_FAILURE;
392 if(0 != udev_enumerate_add_match_property(enumerator,
"DEVTYPE",
"usb_device"))
394 ERROR(
"Couldn't add property to match.\n");
395 result = RUSBCTRL_FAILURE;
398 if(0 != udev_enumerate_scan_devices(enumerator))
400 ERROR(
"Couldn't scan devices.\n");
401 result = RUSBCTRL_FAILURE;
405 struct udev_list_entry *device_list_head = udev_enumerate_get_list_entry(enumerator);
406 struct udev_list_entry *device_list_iterator = NULL;
407 udev_list_entry_foreach(device_list_iterator, device_list_head)
409 const char * sys_path = udev_list_entry_get_name(device_list_iterator);
410 struct udev_device *device = udev_device_new_from_syspath(m_udev_context, sys_path);
411 INFO(
"Detected device [syspath: %s, udev_device prt: 0x%x]\n", sys_path, (
unsigned int)device);
415 add_device_to_records(device, identifier);
419 REPORT_IF_UNEQUAL(0, pthread_mutex_unlock(&m_mutex));
420 udev_enumerate_unref(enumerator);
424 bool add_device_to_records(
struct udev_device *device,
int &identifier)
427 identifier = get_new_identifier();
428 INFO(
"Adding device 0x%x to records. Identifier is 0x%x\n", (
unsigned int)device, identifier);
429 device_record *temp(
new device_record(identifier, device, udev_device_get_devnode(device)));
430 m_device_records.push_back(temp);
431 print_device_properties(device);
435 bool remove_device_from_records(
struct udev_device *device,
int &identifier)
438 DEBUG(
"Removing device 0%x from records.\n", (
unsigned int)device);
439 const char * devnode = udev_device_get_devnode(device);
442 ERROR(
"Invalid devnode for incoming data.\n");
446 std::list<device_record *>::iterator iter;
447 for(iter = m_device_records.begin(); iter != m_device_records.end(); iter++)
449 if(0 == strncmp(devnode, (*iter)->get_devnode(), strlen(devnode)))
451 identifier = (*iter)->get_identifier();
455 if(iter != m_device_records.end())
457 INFO(
"Found record with identifer 0x%x. Removing it.\n", identifier);
459 m_device_records.erase(iter);
462 ERROR(
"Found no record for device\n");
465 void print_device_properties(
struct udev_device * device)
467 INFO(
"USB device Node Path: %s\n", udev_device_get_devnode(device));
469 udev_list_entry *device_attr_list = udev_device_get_sysattr_list_entry(device);
470 udev_list_entry *current_attr = NULL;
471 const char *key = NULL;
472 udev_list_entry_foreach(current_attr, device_attr_list)
474 key = udev_list_entry_get_name(current_attr);
475 DEBUG(
"key: %s, value: %s\n", key, udev_device_get_sysattr_value(device, key));
477 #endif // Disabled because udev_device_get_sysattr_list_entry() is not supported on certain platforms.
480 void process_udev_monitor_event()
482 struct udev_device *device = udev_monitor_receive_device(m_monitor);
485 ERROR(
"udev_monitor_receive_device failed!\n");
489 const char* action = udev_device_get_action(device);
491 if(0 == strncmp(action, UDEV_ADD_EVENT, strlen(UDEV_ADD_EVENT)))
497 REPORT_IF_UNEQUAL(0, pthread_mutex_lock(&m_mutex));
498 result = add_device_to_records(device, identifier);
501 REPORT_IF_UNEQUAL(0, pthread_mutex_unlock(&m_mutex));
503 if((result) && (m_callback))
505 m_callback(identifier, 1, m_callback_data);
509 else if(0 == strncmp(action, UDEV_REMOVE_EVENT, strlen(UDEV_REMOVE_EVENT)))
515 REPORT_IF_UNEQUAL(0, pthread_mutex_lock(&m_mutex));
516 result = remove_device_from_records(device, identifier);
517 REPORT_IF_UNEQUAL(0, pthread_mutex_unlock(&m_mutex));
519 udev_device_unref(device);
520 if((result) && (m_callback))
522 m_callback(identifier, 0, m_callback_data);
527 udev_device_unref(device);
530 int get_new_identifier()
533 m_last_used_identifier++;
534 return m_last_used_identifier;
542 rusbCtrl_result_t result = manager.init();
548 rusbCtrl_result_t result = manager.term();
553 return manager.register_callback(cb, cbData, devList, devListNumEntries);
557 return manager.get_property(devId, propertyName);