1/*
2 * libjingle
3 * Copyright 2012 Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *  1. Redistributions of source code must retain the above copyright notice,
9 *     this list of conditions and the following disclaimer.
10 *  2. Redistributions in binary form must reproduce the above copyright notice,
11 *     this list of conditions and the following disclaimer in the documentation
12 *     and/or other materials provided with the distribution.
13 *  3. The name of the author may not be used to endorse or promote products
14 *     derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/media/devices/deviceinfo.h"
29
30#include "talk/media/devices/libudevsymboltable.h"
31#include "webrtc/base/common.h"  // for ASSERT
32
33namespace cricket {
34
35class ScopedLibUdev {
36 public:
37  static ScopedLibUdev* Create() {
38    ScopedLibUdev* ret_val = new ScopedLibUdev();
39    if (!ret_val->Init()) {
40      delete ret_val;
41      return NULL;
42    }
43    return ret_val;
44  }
45  ~ScopedLibUdev() {
46    libudev_.Unload();
47  }
48
49  LibUDevSymbolTable* instance() { return &libudev_; }
50
51 private:
52  ScopedLibUdev() {}
53
54  bool Init() {
55    return libudev_.Load() &&
56           !IsWrongLibUDevAbiVersion(libudev_.GetDllHandle());
57  }
58
59  LibUDevSymbolTable libudev_;
60};
61
62class ScopedUdev {
63 public:
64  explicit ScopedUdev(LibUDevSymbolTable* libudev) : libudev_(libudev) {
65    udev_ = libudev_->udev_new()();
66  }
67  ~ScopedUdev() {
68    if (udev_) libudev_->udev_unref()(udev_);
69  }
70
71  udev* instance() { return udev_; }
72
73 private:
74  LibUDevSymbolTable* libudev_;
75  udev* udev_;
76};
77
78class ScopedUdevEnumerate {
79 public:
80  ScopedUdevEnumerate(LibUDevSymbolTable* libudev, udev* udev)
81      : libudev_(libudev) {
82    enumerate_ = libudev_->udev_enumerate_new()(udev);
83  }
84  ~ScopedUdevEnumerate() {
85    if (enumerate_) libudev_->udev_enumerate_unref()(enumerate_);
86  }
87
88  udev_enumerate* instance() { return enumerate_; }
89
90 private:
91  LibUDevSymbolTable* libudev_;
92  udev_enumerate* enumerate_;
93};
94
95bool GetUsbProperty(const Device& device, const char* property_name,
96                    std::string* property) {
97  rtc::scoped_ptr<ScopedLibUdev> libudev_context(ScopedLibUdev::Create());
98  if (!libudev_context) {
99    return false;
100  }
101  ScopedUdev udev_context(libudev_context->instance());
102  if (!udev_context.instance()) {
103    return false;
104  }
105  ScopedUdevEnumerate enumerate_context(libudev_context->instance(),
106                                        udev_context.instance());
107  if (!enumerate_context.instance()) {
108    return false;
109  }
110  libudev_context->instance()->udev_enumerate_add_match_subsystem()(
111      enumerate_context.instance(), "video4linux");
112  libudev_context->instance()->udev_enumerate_scan_devices()(
113      enumerate_context.instance());
114  udev_list_entry* devices =
115      libudev_context->instance()->udev_enumerate_get_list_entry()(
116          enumerate_context.instance());
117  if (!devices) {
118    return false;
119  }
120  udev_list_entry* dev_list_entry = NULL;
121  const char* property_value = NULL;
122  // Macro that expands to a for-loop over the devices.
123  for (dev_list_entry = devices; dev_list_entry != NULL;
124       dev_list_entry = libudev_context->instance()->
125           udev_list_entry_get_next()(dev_list_entry)) {
126    const char* path = libudev_context->instance()->udev_list_entry_get_name()(
127        dev_list_entry);
128    if (!path) continue;
129    udev_device* dev =
130        libudev_context->instance()->udev_device_new_from_syspath()(
131            udev_context.instance(), path);
132    if (!dev) continue;
133    const char* device_node =
134        libudev_context->instance()->udev_device_get_devnode()(dev);
135    if (!device_node || device.id.compare(device_node) != 0) {
136      continue;
137    }
138    dev = libudev_context->instance()->
139        udev_device_get_parent_with_subsystem_devtype()(
140            dev, "usb", "usb_device");
141    if (!dev) continue;
142    property_value = libudev_context->instance()->
143        udev_device_get_sysattr_value()(
144            dev, property_name);
145    break;
146  }
147  if (!property_value) {
148    return false;
149  }
150  property->assign(property_value);
151  return true;
152}
153
154bool GetUsbId(const Device& device, std::string* usb_id) {
155  std::string id_vendor;
156  std::string id_product;
157  if (!GetUsbProperty(device, "idVendor", &id_vendor)) {
158    return false;
159  }
160  if (!GetUsbProperty(device, "idProduct", &id_product)) {
161    return false;
162  }
163  usb_id->clear();
164  usb_id->append(id_vendor);
165  usb_id->append(":");
166  usb_id->append(id_product);
167  return true;
168}
169
170bool GetUsbVersion(const Device& device, std::string* usb_version) {
171  return GetUsbProperty(device, "version", usb_version);
172}
173
174}  // namespace cricket
175