dRonin  adbada4
dRonin GCS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
rawhid.cpp
Go to the documentation of this file.
1 
14 /*
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 3 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful, but
21  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23  * for more details.
24  *
25  * You should have received a copy of the GNU General Public License along
26  * with this program; if not, see <http://www.gnu.org/licenses/>
27  */
28 
29 #include "rawhid.h"
30 
31 #include "rawhid_const.h"
34 #include <QtGlobal>
35 #include <QList>
36 #include <QMutexLocker>
37 
38 class IConnection;
39 
40 // timeout value used when we want to return directly without waiting
41 static const int READ_TIMEOUT = 200;
42 static const int READ_SIZE = 64;
43 
44 static const int WRITE_SIZE = 64;
45 
46 static const int WRITE_RETRIES = 10;
47 
49  : m_handle(handle)
50  , m_running(true)
51 {
52 }
53 
55 {
56  // This should already be done / is bogus
57 
58  m_running = false;
59  // wait for the thread to terminate
60  if (wait(10000) == false)
61  qWarning() << "Cannot terminate RawHIDReadThread";
62 }
63 
65 {
66  while (m_running) {
67  // here we use a temporary buffer so we don't need to lock
68  // the mutex while we are reading from the device
69 
70  // Want to read in regular chunks that match the packet size the device
71  // is using. In this case it is 64 bytes (the interrupt packet limit)
72  // although it would be nice if the device had a different report to
73  // configure this
74  unsigned char buffer[READ_SIZE] = { 0 };
75 
76  int ret = hid_read_timeout(m_handle, buffer, READ_SIZE, READ_TIMEOUT);
77 
78  if (ret > 0) // read some data
79  {
80  QMutexLocker lock(&m_readBufMtx);
81 
82  bool needSignal = m_readBuffer.size() == 0;
83 
84  // Note: Preprocess the USB packets in this OS independent code
85  // First byte is report ID, second byte is the number of valid bytes
86  m_readBuffer.append(reinterpret_cast<char *>(&buffer[2]), buffer[1]);
87 
88  if (needSignal) {
89  emit readyToRead();
90  }
91  } else if (ret == 0) // nothing read
92  {
93  } else // < 0 => error
94  {
95  // This thread exiting on error is OK/sane.
96  m_running = false;
97  }
98  }
99 }
100 
102 {
103  QMutexLocker lock(&m_readBufMtx);
104 
105  size = qMin(size, m_readBuffer.size());
106 
107  memcpy(data, m_readBuffer.constData(), size);
108  m_readBuffer.remove(0, size);
109 
110  return size;
111 }
112 
114 {
115  QMutexLocker lock(&m_readBufMtx);
116  return m_readBuffer.size();
117 }
118 
119 // *********************************************************************************
120 
122  : m_handle(handle)
123  , m_running(true)
124 {
125 }
126 
128 
130 {
131  connect(this, &QThread::finished, this, &QObject::deleteLater);
132 
133  int retry = 0;
134  while (m_running) {
135  unsigned char buffer[WRITE_SIZE] = { 0 };
136  int size;
137 
138  {
139  QMutexLocker lock(&m_writeBufMtx);
140  size = qMin(WRITE_SIZE - 2, m_writeBuffer.size());
141 
142  while (m_running && (size <= 0)) {
144 
145  size = m_writeBuffer.size();
146  }
147 
148  // NOTE: data size is limited to 2 bytes less than the
149  // usb packet size (64 bytes for interrupt) to make room
150  // for the reportID and valid data length
151  size = qMin(WRITE_SIZE - 2, m_writeBuffer.size());
152  memcpy(&buffer[2], m_writeBuffer.constData(), size);
153 
154  buffer[0] = 2; // reportID
155  buffer[1] = size; // valid data length
156  }
157 
158  // must hold lock through the send to know how much was sent
159  int ret = hid_write(m_handle, buffer, WRITE_SIZE);
160 
161  if (ret > 0) {
162  // only remove the size actually written to the device
163  QMutexLocker lock(&m_writeBufMtx);
164  m_writeBuffer.remove(0, size);
165  } else if (ret == -110) // timeout
166  {
167  // timeout occured
168  RAW_HID_QXTLOG_DEBUG("Send Timeout: No data written to device.");
169  } else if (ret < 0) // < 0 => error
170  {
171  ++retry;
172  if (retry > WRITE_RETRIES) {
173  retry = 0;
174  qWarning() << "[RawHID] Error writing to device";
175  break; // Exit the loop but keep running.
176  } else {
177  this->msleep(40);
178  }
179  } else {
180  RAW_HID_QXTLOG_DEBUG("No data written to device ??");
181  }
182  }
183 
184  while (m_running) {
185  this->msleep(100); // Wait until we've been asked to exit.
186  }
187 
188  // If we're at this point, RawHID has asked us to exit, so the read thread is
189  // down and no one will touch m_handle again.
190  hid_close(m_handle);
191 
192  m_handle = NULL;
193 }
194 
197 {
198  m_running = false;
199 
200  QMutexLocker lock(&m_writeBufMtx);
201 
202  m_newDataToWrite.wakeOne();
203 }
204 
205 int RawHIDWriteThread::pushDataToWrite(const char *data, int size)
206 {
207  QMutexLocker lock(&m_writeBufMtx);
208 
209  m_writeBuffer.append(data, size);
210  m_newDataToWrite.wakeOne(); // signal that new data arrived
211 
212  return size;
213 }
214 
216 {
217  return m_writeBuffer.size();
218 }
219 
220 // *********************************************************************************
221 
222 RawHID::RawHID(USBDevice *deviceStructure)
223  : QIODevice()
224  , m_deviceInfo(deviceStructure)
225  , m_readThread(NULL)
226  , m_writeThread(NULL)
227 {
228 }
229 
231 {
232  hid_exit();
233 }
234 
235 bool RawHID::open(OpenMode mode)
236 {
237  hid_device *handle;
238 
239  // Initialize the hidapi library (safe to call multiple times)
240  hid_init();
241 
242  // Open the device using the VID, PID
243  handle = hid_open_path(m_deviceInfo->getPath().toLatin1());
244 
245  if (handle) {
246  m_handle = handle;
247 
250 
251  // Plumb through read thread's ready read signal to our clients
252  connect(m_readThread, &RawHIDReadThread::readyToRead, this, &RawHID::sendReadyRead);
253 
254  m_readThread->start();
255  m_writeThread->start();
256  } else {
257  qWarning() << "[RawHID] Failed to open USB device";
258  return false;
259  }
260 
261  return QIODevice::open(mode);
262 }
263 
264 void RawHID::sendReadyRead()
265 {
266  emit readyRead();
267 }
268 
270 {
271  if (!isOpen())
272  return;
273 
274  RAW_HID_QXTLOG_DEBUG("RawHID: close()");
275 
276  m_readThread->stop();
277  m_readThread->wait(); /* Block indefinitely for read thread exit */
278  m_readThread->deleteLater();
279  m_readThread = NULL;
280 
281  m_writeThread->stop();
282  m_handle = NULL;
283 
284  m_writeThread = NULL; /* Write thread is in charge of tearing itself and
285  * m_handle down */
286 
287  QIODevice::close();
288 }
289 
291 {
292  return true;
293 }
294 
296 {
297  if (!m_readThread)
298  return -1;
299 
300  return m_readThread->getBytesAvailable() + QIODevice::bytesAvailable();
301 }
302 
303 qint64 RawHID::bytesToWrite() const
304 {
305  if (!m_writeThread)
306  return -1;
307 
308  return m_writeThread->getBytesToWrite() + QIODevice::bytesToWrite();
309 }
310 
311 qint64 RawHID::readData(char *data, qint64 maxSize)
312 {
313  if (!m_readThread)
314  return -1;
315 
316  return m_readThread->getReadData(data, maxSize);
317 }
318 
319 qint64 RawHID::writeData(const char *data, qint64 maxSize)
320 {
321  qint64 ret = m_writeThread->pushDataToWrite(data, maxSize);
322 
323  if (ret > 0) {
324  emit bytesWritten(ret);
325  }
326 
327  return ret;
328 }
329 
virtual qint64 bytesToWrite() const
Definition: rawhid.cpp:303
void stop()
Definition: rawhid.h:69
RawHIDReadThread * m_readThread
Definition: rawhid.h:159
RawHIDReadThread(hid_device *device)
Definition: rawhid.cpp:48
QByteArray m_writeBuffer
Definition: rawhid.h:115
virtual ~RawHIDReadThread()
Definition: rawhid.cpp:54
end buffer
void stop()
Tell the thread to stop and make sure it wakes up immediately.
Definition: rawhid.cpp:196
virtual void close()
Definition: rawhid.cpp:269
virtual bool open(OpenMode mode)
Definition: rawhid.cpp:235
QMutex m_writeBufMtx
Definition: rawhid.h:118
virtual bool isSequential() const
Definition: rawhid.cpp:290
hid_device * m_handle
Definition: rawhid.h:84
DataFields data
RawHIDWriteThread(hid_device *device)
Definition: rawhid.cpp:121
virtual qint64 bytesAvailable() const
Definition: rawhid.cpp:295
QByteArray m_readBuffer
Definition: rawhid.h:79
virtual ~RawHID()
Definition: rawhid.cpp:230
QMutex m_readBufMtx
Definition: rawhid.h:82
int pushDataToWrite(const char *data, int size)
Definition: rawhid.cpp:205
qint64 getBytesAvailable()
Definition: rawhid.cpp:113
hid_device * m_handle
Definition: rawhid.h:123
bool m_running
Definition: rawhid.h:86
virtual ~RawHIDWriteThread()
Definition: rawhid.cpp:127
RawHIDWriteThread * m_writeThread
Definition: rawhid.h:160
QString getPath() const
Definition: usbdevice.h:37
virtual qint64 writeData(const char *data, qint64 maxSize)
Definition: rawhid.cpp:319
hid_device * m_handle
Definition: rawhid.h:157
int getReadData(char *data, int size)
Definition: rawhid.cpp:101
qint64 getBytesToWrite()
Definition: rawhid.cpp:215
USBDevice * m_deviceInfo
Definition: rawhid.h:156
virtual qint64 readData(char *data, qint64 maxSize)
Definition: rawhid.cpp:311
QWaitCondition m_newDataToWrite
Definition: rawhid.h:121