radio_tool 0.2.1
Loading...
Searching...
No Matches
usb_radio_factory.cpp
1
19#include <radio_tool/radio/usb_radio_factory.hpp>
20#include <radio_tool/radio/tyt_radio.hpp>
21#include <radio_tool/radio/tyt_sgl_radio.hpp>
22#include <radio_tool/radio/yaesu_radio.hpp>
23
24#include <libusb-1.0/libusb.h>
25
26#include <exception>
27#include <functional>
28#include <codecvt>
29#include <cstring>
30#include <iostream>
31#include <thread>
32
33using namespace radio_tool::radio;
34
35struct DeviceMapper
36{
37 std::function<bool(const libusb_device_descriptor&)> SupportsDevice;
38 std::function<RadioOperations* (libusb_device_handle*)> CreateOperations;
39};
40
44const std::vector<DeviceMapper> RadioSupports = {
45 {TYTRadio::SupportsDevice, TYTRadio::Create},
46 {TYTSGLRadio::SupportsDevice, TYTSGLRadio::Create},
47 {YaesuRadio::SupportsDevice, YaesuRadio::Create}
48};
49
50USBRadioFactory::USBRadioFactory() : usb_ctx(nullptr)
51{
52 usb_ctx = CreateContext();
53 events = std::thread(&USBRadioFactory::HandleEvents, this);
54}
55
56USBRadioFactory::~USBRadioFactory()
57{
58 auto ctx = usb_ctx;
59 usb_ctx = nullptr;
60 events.join();
61
62 libusb_exit(ctx);
63}
64
65auto USBRadioFactory::ListDevices(const uint16_t& idx_offset) const -> const std::vector<RadioInfo*>
66{
67 std::vector<RadioInfo*> ret;
68
69 libusb_device** devs;
70 auto ndev = libusb_get_device_list(usb_ctx, &devs);
71 int err = LIBUSB_SUCCESS;
72 auto n_idx = 0;
73
74 if (ndev >= 0)
75 {
76 for (auto x = 0; x < ndev; x++)
77 {
78 libusb_device_descriptor desc;
79 if (LIBUSB_SUCCESS == (err = libusb_get_device_descriptor(devs[x], &desc)))
80 {
81 for (const auto& fnSupport : RadioSupports)
82 {
83 if (fnSupport.SupportsDevice(desc))
84 {
85 int err = LIBUSB_SUCCESS;
86 libusb_device_handle* h;
87 auto cdev = const_cast<libusb_device*>(devs[x]);
88 if (LIBUSB_SUCCESS == (err = libusb_open(cdev, &h)))
89 {
90 std::wstring mfg, prd;
91 // Yaesu FT-70D doesn't support string descriptors
92 if (desc.idVendor == YaesuRadio::VID && desc.idProduct == YaesuRadio::PID)
93 {
94 mfg = L"Yaesu";
95 prd = L"FT-70D";
96 }
97 else
98 {
99 mfg = GetDeviceString(desc.iManufacturer, h);
100 prd = GetDeviceString(desc.iProduct, h);
101 }
102
103 auto bus = libusb_get_bus_number(cdev);
104 auto port = libusb_get_port_number(cdev);
105
106 auto fnOpen = [bus, port, &fnSupport]()
107 {
108 auto openDev = OpenDevice(bus, port);
109 return fnSupport.CreateOperations(openDev);
110 };
111
112 auto nInf = new USBRadioInfo(fnOpen, mfg, prd, desc.idVendor, desc.idProduct, idx_offset + n_idx);
113 ret.push_back(nInf);
114 n_idx++;
115 libusb_close(h);
116 }
117 else
118 {
119 std::cerr << "Failed to open device VID=0x"
120 << std::hex << std::setw(4) << std::setfill('0') << desc.idVendor
121 << ", PID=0x"
122 << std::hex << std::setw(4) << std::setfill('0') << desc.idProduct
123 << " (" << libusb_error_name(err) << ")"
124 << std::endl;
125 }
126 }
127 }
128 }
129 }
130
131 libusb_free_device_list(devs, 1);
132 }
133 else
134 {
135 throw std::runtime_error(libusb_error_name(ndev));
136 }
137 return ret;
138}
139
140auto USBRadioFactory::GetDeviceString(const uint8_t& desc, libusb_device_handle* h) const -> std::wstring
141{
142 auto err = 0;
143 int prd_len = 0;
144 unsigned char lang[42], prd[255];
145 memset(prd, 0, 255);
146
147 err = libusb_get_string_descriptor(h, 0, 0, lang, 42);
148 if (LIBUSB_SUCCESS > err)
149 {
150 throw std::runtime_error(libusb_error_name(err));
151 }
152 if (LIBUSB_SUCCESS > (prd_len = libusb_get_string_descriptor(h, desc, lang[2] << 8 | lang[3], prd, 255)))
153 {
154 throw std::runtime_error(libusb_error_name(prd_len));
155 }
156
157 // Encoded as UTF-16 (LE), Prefixed with length and some other byte.
158 typedef std::codecvt_utf16<char16_t, 1114111UL, std::little_endian> cvt;
159 auto u16 = std::wstring_convert<cvt, char16_t>().from_bytes((const char*)prd + 2, (const char*)prd + prd_len);
160 return std::wstring(u16.begin(), u16.end());
161}
162
163auto USBRadioFactory::OpenDevice(const uint8_t& bus, const uint8_t& port) -> libusb_device_handle*
164{
165 auto usb_ctx = CreateContext();
166
167 libusb_device** devs;
168 auto ndev = libusb_get_device_list(usb_ctx, &devs);
169 int err = LIBUSB_SUCCESS;
170 auto n_idx = 0;
171
172 if (ndev >= 0)
173 {
174 for (auto x = 0; x < ndev; x++)
175 {
176 auto b = libusb_get_bus_number(devs[x]);
177 auto p = libusb_get_port_number(devs[x]);
178 if (b == bus && p == port)
179 {
180 libusb_device_handle* handle;
181
182 if (libusb_open(devs[x], &handle) == LIBUSB_SUCCESS)
183 {
184 return handle;
185 }
186 break;
187 }
188 }
189
190 libusb_free_device_list(devs, 1);
191 }
192 else
193 {
194 throw std::runtime_error(libusb_error_name(ndev));
195 }
196
197 return nullptr;
198}
199
200auto USBRadioFactory::CreateContext() -> libusb_context*
201{
202 libusb_context* usb_ctx;
203
204 auto err = libusb_init(&usb_ctx);
205 if (err != LIBUSB_SUCCESS)
206 {
207 throw std::runtime_error(libusb_error_name(err));
208 }
209#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000107)
210 libusb_set_log_cb(
211 usb_ctx, [](libusb_context*, enum libusb_log_level, const char* str)
212 { std::wcout << str << std::endl; },
213 LIBUSB_LOG_CB_CONTEXT);
214#endif
215
216 return usb_ctx;
217}
218
219auto USBRadioFactory::HandleEvents() -> void
220{
221 std::cerr << "USB events tread started: #" << std::this_thread::get_id() << std::endl;
222 while (usb_ctx != nullptr)
223 {
224 timeval timeout = { 0, 100 };
225 auto err = libusb_handle_events_timeout(usb_ctx, &timeout);
226 if (err != LIBUSB_SUCCESS)
227 {
228 if (err != LIBUSB_ERROR_BUSY &&
229 err != LIBUSB_ERROR_TIMEOUT &&
230 err != LIBUSB_ERROR_OVERFLOW &&
231 err != LIBUSB_ERROR_INTERRUPTED)
232 {
233 break;
234 }
235 }
236 }
237 std::cerr << "USB events tread exited: #" << std::this_thread::get_id() << std::endl;
238}