aboutsummaryrefslogtreecommitdiff
path: root/smartpen.cc
diff options
context:
space:
mode:
Diffstat (limited to 'smartpen.cc')
-rw-r--r--smartpen.cc269
1 files changed, 204 insertions, 65 deletions
diff --git a/smartpen.cc b/smartpen.cc
index b03f0e5..1282e33 100644
--- a/smartpen.cc
+++ b/smartpen.cc
@@ -18,29 +18,25 @@
#include <QtCore/QDateTime>
#include <QtCore/QDebug>
+#include <QtCore/QDeadlineTimer>
+#include <QtCore/QThread>
#include <QtCore/QStringList>
#include <QtCore/QtEndian>
-#include <usb.h>
+#include <libusb-1.0/libusb.h>
#include "xmlutils.h"
#include "smartpen.h"
-#define PEN_EPOCH (1289335960000LL) // This is probably not correct
-#define PEN_MTU 900
-#define PEN_TIMEOUT_SECONDS 30
+#define PEN_EPOCH (1289335960000LL)
+#define PEN_MTU OBEX_MAXIMUM_MTU
+#define PEN_TIMEOUT_SECONDS 5
#define INVALID_CID 0xFFFFFFFFU
static const char pen_serial_chars[] = "ABCDEFGHJKMNPQRSTUWXYZ23456789";
static const unsigned int pen_serial_num_chars = sizeof(pen_serial_chars) - 1;
-/* Terrible hack comes now: */
-struct obex_usb_intf_transport_t {
- struct obex_usb_intf_transport_t *prev, *next; /* Next and previous interfaces in the list */
- struct usb_device *device; /* USB device that has the interface */
-};
-
Smartpen::Smartpen(QObject *parent) :
- QObject(parent), _obex(0), _connId(INVALID_CID)
+ QObject(parent), _obex(0), _connId(INVALID_CID), _reqComplete(false), _continueReceived(0)
{
}
@@ -58,6 +54,10 @@ bool Smartpen::isConnected() const
QByteArray Smartpen::getObject(const QString &name)
{
+ qDebug() << "Getting object" << name;
+
+ prepareRequest();
+
obex_object_t *obj = OBEX_ObjectNew(_obex, OBEX_CMD_GET);
Q_ASSERT(obj);
@@ -72,23 +72,13 @@ QByteArray Smartpen::getObject(const QString &name)
return QByteArray();
}
- qDebug() << "Getting object" << name;
-
if (OBEX_Request(_obex, obj) < 0) {
qWarning() << "Get object request failed";
+ OBEX_ObjectDelete(_obex, obj);
return QByteArray();
}
- QDateTime start = QDateTime::currentDateTimeUtc();
- QDateTime now;
- do {
- OBEX_HandleInput(_obex, PEN_TIMEOUT_SECONDS);
- now = QDateTime::currentDateTimeUtc();
- } while (_inBuf.isEmpty() && start.secsTo(now) < PEN_TIMEOUT_SECONDS);
-
- if (_inBuf.isEmpty()) {
- qWarning() << "Did not receive any data in" << start.secsTo(now) << "seconds";
- }
+ waitForRequestComplete(PEN_TIMEOUT_SECONDS);
QByteArray result;
qSwap(_inBuf, result);
@@ -117,7 +107,7 @@ QString Smartpen::getPenName()
{
QString name = getParameter(PenName);
if (name.isEmpty()) {
- return name;
+ return name; // Empty string if unknown name
}
QByteArray hex = QByteArray::fromHex(name.mid(2).toLatin1());
@@ -163,7 +153,7 @@ QList<Smartpen::ChangeReport> Smartpen::getChangeList(PenTime from)
QByteArray data = getObject(QString("changelist?start_time=%1").arg(from));
QXmlStreamReader r(data);
- qDebug() << QString::fromAscii(data);
+ qDebug() << "changelist:" << QString::fromLatin1(data);
advanceToFirstChildElement(r, "xml");
advanceToFirstChildElement(r, "changelist");
@@ -271,6 +261,64 @@ quint64 Smartpen::toPenId(const QString &serial)
return id;
}
+bool Smartpen::reset(const Address &addr)
+{
+ libusb_context *ctx = 0;
+ libusb_device **devlist = 0;
+ libusb_device *dev = 0;
+ libusb_device_handle *handle = 0;
+ ssize_t ndevs;
+ int err = 0;
+
+ err = libusb_init(&ctx);
+ if (err != 0) {
+ qWarning() << "libusb_init failed:" << err;
+ goto err0;
+ }
+
+ ndevs = libusb_get_device_list(ctx, &devlist);
+ if (ndevs < 0) {
+ qWarning() << "libusb_get_device_list failed:" << err;
+ goto err1;
+ }
+
+ for (ssize_t i = 0; i < ndevs; ++i) {
+ if (libusb_get_bus_number(devlist[i]) == addr.first &&
+ libusb_get_device_address(devlist[i]) == addr.second) {
+ dev = devlist[i];
+ }
+ }
+
+ if (!dev) {
+ qWarning() << "could not find device in libusb";
+ err = -ENODEV;
+ goto err2;
+ }
+
+ err = libusb_open(dev, &handle);
+ if (err != 0) {
+ qWarning() << "libusb_open failed:" << err;
+ goto err2;
+ }
+
+ err = libusb_reset_device(handle);
+ if (err != 0) {
+ qWarning() << "libusb_reset_device failed: " << err;
+ goto err3;
+ }
+
+ qDebug() << "USB device resetted";
+
+err3:
+ libusb_close(handle);
+err2:
+ libusb_free_device_list(devlist, 1);
+err1:
+ libusb_exit(ctx);
+err0:
+ return err == 0;
+}
+
bool Smartpen::connectToPen(const Address &addr)
{
if (_obex) {
@@ -278,18 +326,21 @@ bool Smartpen::connectToPen(const Address &addr)
return false;
}
- _obex = OBEX_Init(OBEX_TRANS_USB, obexEventCb, 0);
+ _connId = INVALID_CID;
+
+ _obex = OBEX_Init(OBEX_TRANS_USB, obexEventCb, OBEX_FL_CLOEXEC);
Q_ASSERT(_obex);
OBEX_SetUserData(_obex, this);
OBEX_SetTransportMTU(_obex, PEN_MTU, PEN_MTU);
- obex_interface_t *interfaces, *ls_interface = 0;
- int count = OBEX_FindInterfaces(_obex, &interfaces);
+ obex_interface_t *ls_interface = 0;
+ int count = OBEX_EnumerateInterfaces(_obex);
for (int i = 0; i < count; i++) {
- if (interfaces[i].usb.intf->device->bus->location == addr.first &&
- interfaces[i].usb.intf->device->devnum == addr.second) {
- ls_interface = &interfaces[i];
+ obex_interface_t *intf = OBEX_GetInterfaceByIndex(_obex, i);
+ if (intf->usb.bus_number == addr.first &&
+ intf->usb.device_address == addr.second) {
+ ls_interface = intf;
}
}
@@ -298,22 +349,16 @@ bool Smartpen::connectToPen(const Address &addr)
return false;
}
- usb_dev_handle *handle = usb_open(ls_interface->usb.intf->device);
- if (handle) {
- qDebug() << "resetting usb device";
- usb_reset(handle);
- usb_close(handle);
- } else {
- qWarning() << "could not open usb device for resetting";
- }
-
- qDebug() << "connecting to" << ls_interface->usb.product;
+ qDebug() << "Connecting to" << ls_interface->usb.product;
- if (OBEX_InterfaceConnect(_obex, ls_interface) < 0) {
- qWarning() << "Could not connect to Livescribe interface";
+ int err = OBEX_InterfaceConnect(_obex, ls_interface) ;
+ if (err < 0) {
+ qWarning() << "Could not connect to Livescribe interface" << strerror(-err);
return false;
}
+ prepareRequest();
+
static const char * livescribe_service = "LivescribeService";
obex_object_t *object = OBEX_ObjectNew(_obex, OBEX_CMD_CONNECT);
obex_headerdata_t hd;
@@ -337,7 +382,7 @@ bool Smartpen::connectToPen(const Address &addr)
qDebug() << "Connection in progress";
- OBEX_HandleInput(_obex, PEN_TIMEOUT_SECONDS);
+ waitForRequestComplete(PEN_TIMEOUT_SECONDS);
return _connId != INVALID_CID;
}
@@ -346,19 +391,27 @@ void Smartpen::disconnectFromPen()
{
if (_connId != INVALID_CID) {
if (_obex) {
+ OBEX_CancelRequest(_obex, 0);
+
+ prepareRequest();
+
obex_object_t *object = OBEX_ObjectNew(_obex, OBEX_CMD_DISCONNECT);
Q_ASSERT(object);
addConnHeader(object);
- OBEX_Request(_obex, object);
- OBEX_HandleInput(_obex, PEN_TIMEOUT_SECONDS);
+ if (OBEX_Request(_obex, object) == 0) {
+ qDebug() << "Send disconnect";
+ waitForRequestComplete(PEN_TIMEOUT_SECONDS);
+ }
}
_connId = INVALID_CID;
}
if (_obex) {
+ OBEX_TransportDisconnect(_obex);
OBEX_Cleanup(_obex);
_obex = 0;
}
_inBuf.clear();
+ qDebug() << "Disconnected";
}
void Smartpen::obexEventCb(obex_t *handle, obex_object_t *obj,
@@ -372,24 +425,38 @@ void Smartpen::obexEventCb(obex_t *handle, obex_object_t *obj,
void Smartpen::handleObexEvent(obex_object_t *object,
int event, int obex_cmd, int obex_rsp)
{
+ // Special flag used for temporarily ignoring the synthetic events generated during OBEX_CancelRequest
+ static bool aborting_continue = false;
+ if (aborting_continue) return;
switch (event) {
case OBEX_EV_PROGRESS:
- if (obex_cmd == OBEX_CMD_GET) {
- // It seems that the pen wants us to add this header on every continue response
- addConnHeader(object);
- }
break;
case OBEX_EV_REQDONE:
qDebug() << "event reqdone cmd=" << obex_cmd << " rsp=" << OBEX_ResponseToString(obex_rsp);
handleObexRequestDone(object, obex_cmd, obex_rsp);
break;
case OBEX_EV_LINKERR:
- qWarning() << "link error cmd=" << obex_cmd;
- emit error();
+ qWarning() << "link error cmd=" << obex_cmd << " rsp=" << OBEX_ResponseToString(obex_rsp);
+ emit linkError("Link error");
+ break;
+ case OBEX_EV_PARSEERR:
+ qWarning() << "parse error cmd=" << obex_cmd << " rsp=" << OBEX_ResponseToString(obex_rsp);
+ emit linkError("Protocol error");
+ break;
+ case OBEX_EV_CONTINUE:
+ // The standard "Continue" messages sent by OpenObex "forget" to include the ConnectionID, confusing the pen
+ // We're going to therefore implement the "continue" state machine on our own
+ handleObexContinue(object, obex_cmd);
+
+ // Cancel OpenObex's Continue message, avoiding the spurious events that generates
+ Q_ASSERT(!aborting_continue);
+ aborting_continue = true;
+ OBEX_CancelRequest(_obex, 0);
+ aborting_continue = false;
break;
default:
- qDebug() << "event" << event << obex_cmd << obex_rsp;
+ qDebug() << "event unknown=" << event << " cmd=" << obex_cmd << " rsp=" << OBEX_ResponseToString(obex_rsp);
break;
}
}
@@ -400,6 +467,8 @@ void Smartpen::handleObexRequestDone(obex_object_t *object, int obex_cmd, int ob
obex_headerdata_t hdata;
quint32 hlen;
+ _reqComplete = true;
+
switch (obex_cmd & ~OBEX_FINAL) {
case OBEX_CMD_CONNECT:
switch (obex_rsp) {
@@ -414,7 +483,7 @@ void Smartpen::handleObexRequestDone(obex_object_t *object, int obex_cmd, int ob
break;
default:
qWarning() << "Failed connection request:" << OBEX_ResponseToString(obex_rsp);
- emit error();
+ emit linkError("OBEX connection error");
break;
}
break;
@@ -437,7 +506,7 @@ void Smartpen::handleObexRequestDone(obex_object_t *object, int obex_cmd, int ob
qDebug() << "GET request succesful";
while (OBEX_ObjectGetNextHeader(_obex, object, &header_id, &hdata, &hlen)) {
if (header_id == OBEX_HDR_BODY || header_id == OBEX_HDR_BODY_END) {
- _inBuf = QByteArray(reinterpret_cast<const char*>(hdata.bs), hlen);
+ _inBuf.append(reinterpret_cast<const char*>(hdata.bs), hlen);
}
}
break;
@@ -450,6 +519,85 @@ void Smartpen::handleObexRequestDone(obex_object_t *object, int obex_cmd, int ob
}
}
+void Smartpen::handleObexContinue(obex_object_t *object, int obex_cmd)
+{
+ const uint8_t *data;
+ int len;
+
+ _continueReceived++;
+
+ switch (obex_cmd & ~OBEX_FINAL) {
+ case OBEX_CMD_GET:
+ len = OBEX_ObjectReadStream(_obex, object, &data);
+ if (len > 0) {
+ _inBuf.append(reinterpret_cast<const char*>(data), len);
+ }
+ break;
+ }
+}
+
+void Smartpen::prepareRequest()
+{
+ _reqComplete = false;
+ _continueReceived = 0;
+ _inBuf.clear();
+}
+
+bool Smartpen::waitForRequestComplete(int timeout)
+{
+ QDeadlineTimer timer(timeout * 1000UL);
+ timer.setTimerType(Qt::CoarseTimer);
+
+ int cmd = OBEX_ObjectGetCommand(_obex, NULL);
+
+ do {
+ if (OBEX_HandleInput(_obex, timer.remainingTime() / 1000) < 0) {
+ qWarning() << "OBEX_HandleInput failed";
+ break;
+ }
+ if (_continueReceived) {
+ sendContinue(cmd);
+ _continueReceived--;
+
+ // Reset timeout
+ timer.setRemainingTime(timeout * 1000UL);
+ }
+ } while (!_reqComplete && !timer.hasExpired());
+
+ if (!_reqComplete) {
+ qWarning() << "Did not complete request in" << timeout << "seconds";
+ emit linkError("Timeout");
+ return false;
+ }
+
+ return true;
+}
+
+void Smartpen::addConnHeader(obex_object_t *obj) const
+{
+ obex_headerdata_t hd;
+ hd.bq4 = _connId;
+ if (OBEX_ObjectAddHeader(_obex, obj, OBEX_HDR_CONNECTION, hd, sizeof(hd.bq4), OBEX_FL_FIT_ONE_PACKET) < 0) {
+ qCritical() << "Could not add connection header";
+ }
+}
+
+bool Smartpen::sendContinue(int obex_cmd)
+{
+ obex_object_t *obj = OBEX_ObjectNew(_obex, obex_cmd);
+ Q_ASSERT(obj);
+
+ addConnHeader(obj);
+
+ if (OBEX_Request(_obex, obj) < 0) {
+ qWarning() << "Send continue failed";
+ OBEX_ObjectDelete(_obex, obj);
+ return false;
+ }
+
+ return true;
+}
+
QString Smartpen::toPenSerialSegment(quint32 id, int len)
{
@@ -469,7 +617,7 @@ quint32 Smartpen::fromPenSerialSegment(const QString &s)
quint32 id = 0;
for (int i = 0; i < len; i++) {
- uint val = qFind(&pen_serial_chars[0], &pen_serial_chars[pen_serial_num_chars], s[i]) - &pen_serial_chars[0];
+ uint val = std::find(&pen_serial_chars[0], &pen_serial_chars[pen_serial_num_chars], s[i]) - &pen_serial_chars[0];
if (val >= pen_serial_num_chars) return 0;
id = val + id * pen_serial_num_chars;
}
@@ -488,12 +636,3 @@ QByteArray Smartpen::encodeUtf16(const QString &s)
p[size] = 0;
return data;
}
-
-void Smartpen::addConnHeader(obex_object_t *obj) const
-{
- obex_headerdata_t hd;
- hd.bq4 = _connId;
- if (OBEX_ObjectAddHeader(_obex, obj, OBEX_HDR_CONNECTION, hd, sizeof(hd.bq4), 0) < 0) {
- qCritical() << "Could not add connection header";
- }
-}