SDRAngel  4.11.5
Developer docs for <a href="https://github.com/f4exb/sdrangel">SDRangel<\a>, an Open Source Qt5 / OpenGL 3.0+ SDR and signal analyzer frontend to various hardware.
ambeengine.cpp
Go to the documentation of this file.
1 // Copyright (C) 2019 F4EXB //
3 // written by Edouard Griffiths //
4 // //
5 // This program is free software; you can redistribute it and/or modify //
6 // it under the terms of the GNU General Public License as published by //
7 // the Free Software Foundation as version 3 of the License, or //
8 // (at your option) any later version. //
9 // //
10 // This program is distributed in the hope that it will be useful, //
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
13 // GNU General Public License V3 for more details. //
14 // //
15 // You should have received a copy of the GNU General Public License //
16 // along with this program. If not, see <http://www.gnu.org/licenses/>. //
18 
19 #include <fcntl.h>
20 #include <sys/stat.h>
21 
22 #ifndef _MSC_VER
23 #include <dirent.h>
24 #include <unistd.h>
25 #include <libgen.h>
26 #endif
27 
28 #ifndef __WINDOWS__
29 #include <termios.h>
30 #include <sys/ioctl.h>
31 #ifndef __APPLE__
32 #include <linux/serial.h>
33 #endif
34 #endif
35 
36 #include <chrono>
37 #include <thread>
38 
39 #include <QThread>
40 #include <QBuffer>
41 #include <QDataStream>
42 
43 #include "ambeworker.h"
44 #include "ambeengine.h"
45 
47 {}
48 
50 {
51  qDebug("AMBEEngine::~AMBEEngine: %lu controllers", m_controllers.size());
52 }
53 
54 #ifdef __WINDOWS__
56 {
57  m_comList.clear();
58  m_comList8250.clear();
59  char comCStr[16];
60 
61  // Arbitrarily set the list to the 20 first COM ports
62  for (int i = 1; i <= 20; i++)
63  {
64  sprintf(comCStr, "COM%d", i);
65  m_comList.push_back(std::string(comCStr));
66  }
67 }
68 
69 // Do not activate serial support at all for windows
70 void AMBEEngine::scan(std::vector<QString>& ambeDevices)
71 {
72 }
73 #else
75 {
76  int n;
77  struct dirent **namelist;
78  m_comList.clear();
79  m_comList8250.clear();
80  const char* sysdir = "/sys/class/tty/";
81 
82  // Scan through /sys/class/tty - it contains all tty-devices in the system
83  n = scandir(sysdir, &namelist, NULL, alphasort);
84  if (n < 0)
85  {
86  perror("scandir");
87  }
88  else
89  {
90  while (n--)
91  {
92  if (strcmp(namelist[n]->d_name, "..") && strcmp(namelist[n]->d_name, "."))
93  {
94  // Construct full absolute file path
95  std::string devicedir = sysdir;
96  devicedir += namelist[n]->d_name;
97  // Register the device
99  }
100 
101  free(namelist[n]);
102  }
103 
104  free(namelist);
105  }
106 
107  // Only non-serial8250 has been added to comList without any further testing
108  // serial8250-devices must be probe to check for validity
110 }
111 #endif // not Windows
112 
113 #ifndef __WINDOWS__
115  std::vector<std::string>& comList,
116  std::vector<std::string>& comList8250,
117  const std::string& dir)
118 {
119  // Get the driver the device is using
120  std::string driver = get_driver(dir);
121 
122  // Skip devices without a driver
123  if (driver.size() > 0)
124  {
125  //std::cerr << "register_comport: dir: "<< dir << " driver: " << driver << std::endl;
126  std::string devfile = std::string("/dev/") + basename((char *) dir.c_str());
127 
128  // Put serial8250-devices in a seperate list
129  if (driver == "serial8250") {
130  comList8250.push_back(devfile);
131  } else {
132  comList.push_back(devfile);
133  }
134  }
135 }
136 
138  std::vector<std::string>& comList,
139  std::vector<std::string> comList8250)
140 {
141  struct serial_struct serinfo;
142  std::vector<std::string>::iterator it = comList8250.begin();
143 
144  // Iterate over all serial8250-devices
145  while (it != comList8250.end())
146  {
147 
148  // Try to open the device
149  int fd = open((*it).c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY);
150 
151  if (fd >= 0)
152  {
153  // Get serial_info
154  if (ioctl(fd, TIOCGSERIAL, &serinfo) == 0)
155  {
156  // If device type is no PORT_UNKNOWN we accept the port
157  if (serinfo.type != PORT_UNKNOWN) {
158  comList.push_back(*it);
159  }
160  }
161 
162  close(fd);
163  }
164  it++;
165  }
166 }
167 
168 std::string AMBEEngine::get_driver(const std::string& tty)
169 {
170  struct stat st;
171  std::string devicedir = tty;
172  // Append '/device' to the tty-path
173  devicedir += "/device";
174 
175  // Stat the devicedir and handle it if it is a symlink
176  if (lstat(devicedir.c_str(), &st) == 0 && S_ISLNK(st.st_mode))
177  {
178  char buffer[1024];
179  memset(buffer, 0, sizeof(buffer));
180  // Append '/driver' and return basename of the target
181  devicedir += "/driver";
182 
183  if (readlink(devicedir.c_str(), buffer, sizeof(buffer)) > 0) {
184  return basename(buffer);
185  }
186  }
187 
188  return "";
189 }
190 
191 void AMBEEngine::scan(std::vector<QString>& ambeDevices)
192 {
193  getComList();
194  std::vector<std::string>::const_iterator it = m_comList.begin();
195  ambeDevices.clear();
196 
197  while (it != m_comList.end())
198  {
199  AMBEWorker *worker = new AMBEWorker();
200  qDebug("AMBEEngine::scan: com: %s", it->c_str());
201 
202  if (worker->open(*it))
203  {
204  ambeDevices.push_back(QString(it->c_str()));
205  worker->close();
206  }
207 
208  delete worker;
209  ++it;
210  }
211 }
212 #endif // not Windows
213 
214 bool AMBEEngine::registerController(const std::string& deviceRef)
215 {
216  AMBEWorker *worker = new AMBEWorker();
217 
218  if (worker->open(deviceRef))
219  {
220  m_controllers.push_back(AMBEController());
221  m_controllers.back().worker = worker;
222  m_controllers.back().thread = new QThread();
223  m_controllers.back().device = deviceRef;
224 
225  m_controllers.back().worker->moveToThread(m_controllers.back().thread);
226  connect(m_controllers.back().worker, SIGNAL(finished()), m_controllers.back().thread, SLOT(quit()));
227  connect(m_controllers.back().worker, SIGNAL(finished()), m_controllers.back().worker, SLOT(deleteLater()));
228  connect(m_controllers.back().thread, SIGNAL(finished()), m_controllers.back().thread, SLOT(deleteLater()));
229  connect(&m_controllers.back().worker->m_inputMessageQueue, SIGNAL(messageEnqueued()), m_controllers.back().worker, SLOT(handleInputMessages()));
230  std::this_thread::sleep_for(std::chrono::seconds(1));
231  m_controllers.back().thread->start();
232 
233  return true;
234  }
235 
236  return false;
237 }
238 
239 void AMBEEngine::releaseController(const std::string& deviceRef)
240 {
241  std::vector<AMBEController>::iterator it = m_controllers.begin();
242 
243  while (it != m_controllers.end())
244  {
245  if (it->device == deviceRef)
246  {
247  disconnect(&it->worker->m_inputMessageQueue, SIGNAL(messageEnqueued()), it->worker, SLOT(handleInputMessages()));
248  it->worker->stop();
249  it->thread->wait(100);
250  it->worker->m_inputMessageQueue.clear();
251  it->worker->close();
252  qDebug() << "AMBEEngine::releaseController: closed device at: " << it->device.c_str();
253  m_controllers.erase(it);
254  break;
255  }
256 
257  ++it;
258  }
259 }
260 
262 {
263  std::vector<AMBEController>::iterator it = m_controllers.begin();
264 
265  while (it != m_controllers.end())
266  {
267  disconnect(&it->worker->m_inputMessageQueue, SIGNAL(messageEnqueued()), it->worker, SLOT(handleInputMessages()));
268  it->worker->stop();
269  it->thread->wait(100);
270  it->worker->m_inputMessageQueue.clear();
271  it->worker->close();
272  qDebug() << "AMBEEngine::release: closed device at: " << it->device.c_str();
273  ++it;
274  }
275 
276  m_controllers.clear();
277 }
278 
279 void AMBEEngine::getDeviceRefs(std::vector<QString>& deviceNames)
280 {
281  std::vector<AMBEController>::const_iterator it = m_controllers.begin();
282 
283  while (it != m_controllers.end())
284  {
285  deviceNames.push_back(QString(it->device.c_str()));
286  ++it;
287  }
288 }
289 
291  const unsigned char *mbeFrame,
292  int mbeRateIndex,
293  int mbeVolumeIndex,
294  unsigned char channels,
295  bool useLP,
296  int upsampling,
297  AudioFifo *audioFifo)
298 {
299  std::vector<AMBEController>::iterator it = m_controllers.begin();
300  std::vector<AMBEController>::iterator itAvail = m_controllers.end();
301  bool done = false;
302  QMutexLocker locker(&m_mutex);
303 
304  while (it != m_controllers.end())
305  {
306  if (it->worker->hasFifo(audioFifo))
307  {
308  it->worker->pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useLP, upsampling, audioFifo);
309  done = true;
310  }
311  else if (it->worker->isAvailable())
312  {
313  itAvail = it;
314  }
315 
316  ++it;
317  }
318 
319  if (!done)
320  {
321  if (itAvail != m_controllers.end())
322  {
323  int wNum = itAvail - m_controllers.begin();
324 
325  qDebug("AMBEEngine::pushMbeFrame: push %p on empty queue %d", audioFifo, wNum);
326  itAvail->worker->pushMbeFrame(mbeFrame, mbeRateIndex, mbeVolumeIndex, channels, useLP, upsampling, audioFifo);
327  }
328  else
329  {
330  qDebug("AMBEEngine::pushMbeFrame: no DV device available. MBE frame dropped");
331  }
332  }
333 }
334 
335 QByteArray AMBEEngine::serialize() const
336 {
337  QStringList qDeviceList;
338  std::vector<AMBEController>::const_iterator it = m_controllers.begin();
339 
340  while (it != m_controllers.end())
341  {
342  qDebug("AMBEEngine::serialize: %s", it->device.c_str());
343  qDeviceList << QString(it->device.c_str());
344  ++it;
345  }
346 
347  QByteArray data;
348  QDataStream *stream = new QDataStream(&data, QIODevice::WriteOnly);
349  (*stream) << qDeviceList;
350  delete stream;
351 
352  return data;
353 }
354 
355 bool AMBEEngine::deserialize(const QByteArray& data)
356 {
357  if (data.size() <= 0)
358  {
359  qDebug("AMBEEngine::deserialize: invalid or no data");
360  return false;
361  }
362 
363  QStringList qDeviceList;
364  QDataStream *stream = new QDataStream(data);
365  (*stream) >> qDeviceList;
366  delete stream;
367 
368  releaseAll();
369 
370  for (int i = 0; i < qDeviceList.size(); ++i)
371  {
372  qDebug(" AMBEEngine::deserialize: %s", qDeviceList.at(i).toStdString().c_str());
373  registerController(qDeviceList.at(i).toStdString());
374  }
375 
376  return true;
377 }
static void probe_serial8250_comports(std::vector< std::string > &comList, std::vector< std::string > comList8250)
Definition: ambeengine.cpp:137
void getDeviceRefs(std::vector< QString > &devicesRefs)
reference of the devices used (device path or url)
Definition: ambeengine.cpp:279
static void register_comport(std::vector< std::string > &comList, std::vector< std::string > &comList8250, const std::string &dir)
Definition: ambeengine.cpp:114
std::vector< std::string > m_comList8250
Definition: ambeengine.h:85
void scan(std::vector< QString > &ambeDevices)
Definition: ambeengine.cpp:191
bool registerController(const std::string &deviceRef)
create a new controller for the device in reference
Definition: ambeengine.cpp:214
std::vector< std::string > m_comList
Definition: ambeengine.h:84
void releaseAll()
Definition: ambeengine.cpp:261
int32_t i
Definition: decimators.h:244
QByteArray serialize() const
Definition: ambeengine.cpp:335
void getComList()
Definition: ambeengine.cpp:74
bool deserialize(const QByteArray &data)
Definition: ambeengine.cpp:355
void pushMbeFrame(const unsigned char *mbeFrame, int mbeRateIndex, int mbeVolumeIndex, unsigned char channels, bool useHP, int upsampling, AudioFifo *audioFifo)
Definition: ambeengine.cpp:290
QMutex m_mutex
Definition: ambeengine.h:86
std::vector< AMBEController > m_controllers
Definition: ambeengine.h:83
bool open(const std::string &deviceRef)
Either serial device or ip:port.
Definition: ambeworker.cpp:48
static std::string get_driver(const std::string &tty)
Definition: ambeengine.cpp:168
void releaseController(const std::string &deviceRef)
release controller resources for the device in reference
Definition: ambeengine.cpp:239
void close()
Definition: ambeworker.cpp:53