RDK Documentation (Open Sourced RDK Components)
usbctrl.cpp
1 /*
2  * If not stated otherwise in this file or this component's Licenses.txt file the
3  * following copyright and licenses apply:
4  *
5  * Copyright 2016 RDK Management
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18 */
19 #include "libudev.h"
20 #include "usbctrl.h"
21 #include <iostream>
22 #include <stdio.h>
23 #include <list>
24 #include <sys/select.h>
25 #include "pthread.h"
26 #include <string.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 
30 //#define ENABLE_DEBUG 1
31 #define LOG(level, text, ...) do {\
32  printf("%s[%d] - %s: " text, __FUNCTION__, __LINE__, level, ##__VA_ARGS__);}while(0);
33 
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);
38 
39 #ifdef ENABLE_DEBUG
40 #define DEBUG(text, ...) do {\
41  printf("%s[%d] - %s: " text, __FUNCTION__, __LINE__, "DEBUG", ##__VA_ARGS__);}while(0);
42 #else
43 #define DEBUG(text, ...)
44 #endif
45 
46 #define REPORT_IF_UNEQUAL(lhs, rhs) do {\
47  if((lhs) != (rhs)) ERROR("Unexpected error!\n");}while(0);
48 
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;
55 
56 const char * supported_property_list[] =
57  {
58  "manufacturer",
59  "product",
60  "idProduct",
61  "idVendor",
62  "serial",
63  "bInterfaceClass",
64  "bInterfaceSubClass"
65  };
66 
68 {
69  public :
71  {
72  private:
73  int m_identifier;
74  struct udev_device *m_device;
75  const char* m_devnode;
76 
77  public:
78  device_record(int identifier, struct udev_device * device, const char* devnode) :
79  m_identifier(identifier), m_device(device), m_devnode(devnode)
80  {
81  DEBUG("adding device 0x%x, %s\n", (unsigned int)m_device, m_devnode);
82  }
83  ~device_record()
84  {
85  /* The udev_device entry needs to be unreffed when the device is removed.*/
86  DEBUG("Unreffing device 0x%x, %s\n", (unsigned int)m_device, m_devnode);
87  udev_device_unref(m_device);
88  }
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;}
92 
93  };
94 
95  private:
96  std::list<device_record *> m_device_records;
97  pthread_mutex_t m_mutex;
98  rusbCtrl_devCallback_t m_callback;
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];
106 
107  public:
108  device_manager() : m_enable_monitoring(false), m_callback(NULL), m_last_used_identifier(0), m_monitor_thread(0)
109  {
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));
114 
115  INFO("Creating new device manager object.\n");
116  m_udev_context = udev_new();
117  if(NULL == m_udev_context)
118  {
119  ERROR("Critical error! udev_new() failed!\n");
120  }
121  else
122  {
123  INFO("Successfully created device manager object 0x%x with udev context 0x%x.\n",
124  (unsigned int)this, (unsigned int)m_udev_context);
125  }
126 
127  /* Set up event monitoring. */
128  m_enable_monitoring = true;
129  m_monitor = udev_monitor_new_from_netlink(m_udev_context, "udev");
130  if(NULL == m_monitor)
131  {
132  ERROR("Critical error! Could not create monitor!\n");
133  return;
134  }
135 
136  do
137  {
138  if(0 != udev_monitor_filter_add_match_subsystem_devtype(m_monitor, "usb", "usb_device"))
139  {
140  ERROR("Critical error! Could not add filters to udev monitor.\n");
141  break;
142  }
143 
144  if(0 != udev_monitor_enable_receiving(m_monitor))
145  {
146  ERROR("Critical error! Could not enable monitoring!\n");
147  break;
148  }
149  if(0 != pipe(m_control_pipe))
150  {
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;
154  break;
155  }
156  if(0 != pthread_create(&m_monitor_thread, NULL, device_manager::monitor_thread_wrapper, (void *)this))
157  {
158  ERROR("Critical error! Could not launch monitor thread!\n");
159  m_monitor_thread = 0;
160  }
161  }while(0);
162  INFO("Done.\n");
163  }
164 
165  ~device_manager()
166  {
167  INFO("Stopping monitor thread.\n");
168  m_enable_monitoring = false;
169  /* Closing the below fd will unblock monitor thread waiting on select().*/
170  if(0 != m_control_pipe[PIPE_WRITE_FD])
171  {
172  close(m_control_pipe[PIPE_WRITE_FD]);
173  }
174 
175  if(0 != m_monitor_thread)
176  {
177  if(0 != pthread_join(m_monitor_thread, NULL))
178  {
179  ERROR("Error. Monitor thread did not join.\n");
180  }
181  }
182 
183  if(0 != m_control_pipe[PIPE_READ_FD])
184  {
185  close(m_control_pipe[PIPE_READ_FD]);
186  }
187 
188  reset_device_records();
189 
190  if(NULL != m_monitor)
191  {
192  udev_monitor_unref(m_monitor);
193  }
194 
195  INFO("Destroying device manager object.\n");
196  udev_unref(m_udev_context);
197  pthread_mutex_destroy(&m_mutex);
198  INFO("Done.\n");
199  }
200  static void * monitor_thread_wrapper(void* data)
201  {
202  device_manager *obj = (device_manager *)data;
203  obj->monitor_for_changes();
204  }
205  rusbCtrl_result_t init()
206  {
207  INFO("Enter.\n");
208  enumerate_connected_devices();
209  INFO("Done.\n");
210  return RUSBCTRL_SUCCESS;
211  }
212 
213  rusbCtrl_result_t term()
214  {
215  INFO("Enter\n");
216  INFO("Clearing device records.\n");
217  REPORT_IF_UNEQUAL(0, pthread_mutex_lock(&m_mutex));
218  reset_device_records();
219  m_callback = NULL;
220  m_callback_data = NULL;
221  REPORT_IF_UNEQUAL(0, pthread_mutex_unlock(&m_mutex));
222  INFO("Done.\n");
223  return RUSBCTRL_SUCCESS;
224  }
225 
226 
227  char * get_property(int identifier, const char *key) //needs lock
228  {
229  /* Find matching entry in the record*/
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++)
233  {
234  if(identifier == (*iter)->get_identifier())
235  {
236  device = (*iter)->get_device();
237  break;
238  }
239  }
240  if(iter != m_device_records.end())
241  {
242  DEBUG("Found record with identifer 0x%x. Querying...\n", identifier);
243  const char * value = udev_device_get_sysattr_value(device, key);
244  if(NULL == value)
245  {
246  ERROR("Could not find property %s.\n", key);
247  return NULL;
248  }
249  else
250  {
251  char * user_buffer = (char*)malloc(strlen(value)); //Will be freed by user
252  strncpy(user_buffer, value, strlen(value));
253  return user_buffer;
254  }
255  }
256  ERROR("Found no record for device with id 0x%x\n", identifier);
257  return NULL;
258  }
259 
260  int register_callback(rusbCtrl_devCallback_t callback, void* callback_data, int ** device_list, int * device_list_size)
261  {
262  //Note: device_list_size stands for the number of entries in the list, not the actual bytes
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))
267  {
268  get_connected_devices_list(device_list, device_list_size);
269  }
270  else
271  {
272  ERROR("Empty pointers provided. Won't supply connected devices.\n");
273  }
274  REPORT_IF_UNEQUAL(0, pthread_mutex_unlock(&m_mutex));
275  INFO("Success!\n");
276  return RUSBCTRL_SUCCESS;
277  }
278 
279  void process_control_event()
280  {
281  int message = 0;
282 
283  if(0 == read(m_control_pipe[PIPE_READ_FD], (void *)&message, CONTROL_MESSAGE_SIZE))
284  {
285  /* Until further messages and use cases are defined, mere EOF is the trigger
286  * for a shutdown.*/
287  INFO("Detected EOF. Calling for shutdown.\n");
288  m_enable_monitoring = false;
289  }
290  }
291 
292  void monitor_for_changes()
293  {
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);
297  if(monitor_fd <= 0)
298  {
299  ERROR("Critical error! Could not get udev monitor fd.\n");
300  return;
301  }
302  int max_fd = (monitor_fd > control_fd ? monitor_fd : control_fd);
303 
304  while(true == m_enable_monitoring)
305  {
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);
310 
311  int ret = select((max_fd + 1), &monitor_fd_set, NULL, NULL, NULL);
312  DEBUG("Unblocking now. ret is 0x%x\n", ret);
313  if(0 == ret)
314  {
315  ERROR("select() returned 0.\n");
316  m_enable_monitoring = false;
317  break;
318  }
319  else if(0 < ret)
320  {
321  //Some activity was detected. Process event further.
322  if(0 != FD_ISSET(control_fd, &monitor_fd_set))
323  {
324  process_control_event();
325  }
326  if(0 != FD_ISSET(monitor_fd, &monitor_fd_set))
327  {
328  process_udev_monitor_event();
329  }
330  }
331  else
332  {
333  m_enable_monitoring = false;
334  ERROR("Error polling monitor FD!\n");
335  }
336 
337  }
338  close(monitor_fd);
339  INFO("Monitor thread shutting down.\n");
340  }
341 
342  private:
343 
344  void reset_device_records() //needs lock
345  {
346  if(0 != m_device_records.size())
347  {
348  std::list<device_record *>::iterator iter;
349  for(iter = m_device_records.begin(); iter != m_device_records.end(); iter++)
350  {
351  delete(*iter);
352  }
353  m_device_records.clear();
354  }
355  INFO("Done.\n");
356  }
357 
358  void get_connected_devices_list(int ** device_list, int * device_list_size) //needs lock
359  {
360  //Note: device_list_size stands for the number of entries in the list, not the actual bytes
361  *device_list_size = m_device_records.size();
362  if(0 != *device_list_size)
363  {
364  /* It's application's responsibility to free this buffer.*/
365  int * buffer = (int *)malloc(*device_list_size * sizeof(int));
366  std::list<device_record *>::iterator iter;
367  int i = 0;
368  for(iter = m_device_records.begin(); iter != m_device_records.end(); iter++)
369  {
370  buffer[i++] = (*iter)->get_identifier();
371  }
372  *device_list = buffer;
373  }
374 
375  }
376 
377  rusbCtrl_result_t enumerate_connected_devices()
378  {
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)
384  {
385  REPORT_IF_UNEQUAL(0, pthread_mutex_unlock(&m_mutex));
386  ERROR("Could not create udev enumerator!\n");
387  return RUSBCTRL_FAILURE;
388  }
389 
390  do
391  {
392  if(0 != udev_enumerate_add_match_property(enumerator, "DEVTYPE", "usb_device"))
393  {
394  ERROR("Couldn't add property to match.\n");
395  result = RUSBCTRL_FAILURE;
396  break;
397  }
398  if(0 != udev_enumerate_scan_devices(enumerator))
399  {
400  ERROR("Couldn't scan devices.\n");
401  result = RUSBCTRL_FAILURE;
402  break;
403  }
404 
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)
408  {
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);
412  if(NULL != device)
413  {
414  int identifier;
415  add_device_to_records(device, identifier);
416  }
417  }
418  }while(0);
419  REPORT_IF_UNEQUAL(0, pthread_mutex_unlock(&m_mutex));
420  udev_enumerate_unref(enumerator);
421  return result;
422  }
423 
424  bool add_device_to_records(struct udev_device *device, int &identifier) //needs lock
425  {
426  /* Create record and push it into the list. */
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);
432  return true;
433  }
434 
435  bool remove_device_from_records(struct udev_device *device, int &identifier) //needs lock
436  {
437  identifier = -1;
438  DEBUG("Removing device 0%x from records.\n", (unsigned int)device);
439  const char * devnode = udev_device_get_devnode(device);
440  if(NULL == devnode)
441  {
442  ERROR("Invalid devnode for incoming data.\n");
443  return false;
444  }
445  /* Find matching entry in the record*/
446  std::list<device_record *>::iterator iter;
447  for(iter = m_device_records.begin(); iter != m_device_records.end(); iter++)
448  {
449  if(0 == strncmp(devnode, (*iter)->get_devnode(), strlen(devnode)))
450  {
451  identifier = (*iter)->get_identifier();
452  break;
453  }
454  }
455  if(iter != m_device_records.end())
456  {
457  INFO("Found record with identifer 0x%x. Removing it.\n", identifier);
458  delete(*iter);
459  m_device_records.erase(iter);
460  return true;
461  }
462  ERROR("Found no record for device\n");
463  return false;
464  }
465  void print_device_properties(struct udev_device * device) //needs lock
466  {
467  INFO("USB device Node Path: %s\n", udev_device_get_devnode(device));
468 #if 0
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)
473  {
474  key = udev_list_entry_get_name(current_attr);
475  DEBUG("key: %s, value: %s\n", key, udev_device_get_sysattr_value(device, key));
476  }
477 #endif // Disabled because udev_device_get_sysattr_list_entry() is not supported on certain platforms.
478  }
479 
480  void process_udev_monitor_event()
481  {
482  struct udev_device *device = udev_monitor_receive_device(m_monitor);
483  if(NULL == device)
484  {
485  ERROR("udev_monitor_receive_device failed!\n");
486  return;
487  }
488 
489  const char* action = udev_device_get_action(device);
490 
491  if(0 == strncmp(action, UDEV_ADD_EVENT, strlen(UDEV_ADD_EVENT)))
492  {
493  //Process 'add' event.
494  int identifier;
495  bool result;
496  {
497  REPORT_IF_UNEQUAL(0, pthread_mutex_lock(&m_mutex));
498  result = add_device_to_records(device, identifier);
499  /*Note: the object "device" is not unreffed here. Instead, the ownership has now been passed to
500  * m_device_records list. "device" will be automatically unreffed when its device_record is destroyed.*/
501  REPORT_IF_UNEQUAL(0, pthread_mutex_unlock(&m_mutex));
502  }
503  if((result) && (m_callback))
504  {
505  m_callback(identifier, 1, m_callback_data);
506  }
507 
508  }
509  else if(0 == strncmp(action, UDEV_REMOVE_EVENT, strlen(UDEV_REMOVE_EVENT)))
510  {
511  //Process 'remove' event.
512  int identifier;
513  bool result;
514  {
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));
518  }
519  udev_device_unref(device);
520  if((result) && (m_callback))
521  {
522  m_callback(identifier, 0, m_callback_data);
523  }
524  }
525  else
526  {
527  udev_device_unref(device);
528  }
529  }
530  int get_new_identifier() //needs lock
531  {
532  //TODO: handle roll-over
533  m_last_used_identifier++;
534  return m_last_used_identifier;
535  }
536 };
537 
538 
539 static device_manager manager;
540 int rusbCtrl_init(void)
541 {
542  rusbCtrl_result_t result = manager.init();
543  return result;
544 
545 }
547 {
548  rusbCtrl_result_t result = manager.term();
549  return result;
550 }
551 int rusbCtrl_registerCallback(rusbCtrl_devCallback_t cb, void *cbData, int **devList, int *devListNumEntries)
552 {
553  return manager.register_callback(cb, cbData, devList, devListNumEntries);
554 }
555 char *rusbCtrl_getProperty(int devId, const char *propertyName)
556 {
557  return manager.get_property(devId, propertyName);
558 }
559 
rusbCtrl_init
int rusbCtrl_init(void)
This API Initiate the library to a state that it is ready to detect device events and invoke callback...
Definition: usbctrl.cpp:540
device_manager::device_record
Definition: usbctrl.cpp:70
device_manager
Definition: usbctrl.cpp:67
rusbCtrl_devCallback_t
void(* rusbCtrl_devCallback_t)(int devId, int inserted, void *cbData)
The callback will be invoked when a device of monitored type is inserted or removed.
Definition: usbctrl.h:67
rusbCtrl_getProperty
char * rusbCtrl_getProperty(int devId, const char *propertyName)
This API compares unique identifier against its internal data structures and validates to provides pr...
Definition: usbctrl.cpp:555
rusbCtrl_term
int rusbCtrl_term()
This API Release all allocated resources.
Definition: usbctrl.cpp:546
rusbCtrl_registerCallback
int rusbCtrl_registerCallback(rusbCtrl_devCallback_t cb, void *cbData, int **devList, int *devListNumEntries)
This callback Allow application to listen for USB insert/remove events. An appliction can only regist...
Definition: usbctrl.cpp:551