mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-10-09 23:53:28 -04:00
hwstub: add stub gdb backend
Change-Id: I1e3fe2bec3f5d926a02027c13b5056d461da2f9d
This commit is contained in:
parent
eb39277d4a
commit
cea6fca67b
5 changed files with 561 additions and 89 deletions
112
utils/hwstub/include/hwstub_gdb.hpp
Normal file
112
utils/hwstub/include/hwstub_gdb.hpp
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2016 by Amaury Pouly
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#ifndef __HWSTUB_GDB_HPP__
|
||||
#define __HWSTUB_GDB_HPP__
|
||||
|
||||
#include "hwstub.hpp"
|
||||
#include "hwstub_net.hpp"
|
||||
|
||||
namespace hwstub {
|
||||
namespace gdb {
|
||||
|
||||
|
||||
/** GDB context
|
||||
*
|
||||
* A GDB context connects to a GDB server and has only one device. */
|
||||
class context : public hwstub::context
|
||||
{
|
||||
friend class device;
|
||||
friend class handle;
|
||||
protected:
|
||||
context(int socke_fd);
|
||||
public:
|
||||
virtual ~context();
|
||||
/** Create a context from an existing socket */
|
||||
static std::shared_ptr<context> create(int socket_fd);
|
||||
/** Create a TCP socket context with a domain name and a port. If port is empty,
|
||||
* a default port is used. */
|
||||
static std::shared_ptr<context> create_tcp(const std::string& domain,
|
||||
const std::string& port, std::string *error = nullptr);
|
||||
|
||||
protected:
|
||||
/** Send a message to the server. Context will always serialize calls to send()
|
||||
* so there is no need to worry about concurrency issues. */
|
||||
error send(void *buffer, size_t& sz);
|
||||
/** Receive a message from the server, sz is updated with the received size.
|
||||
* Context will always serialize calls to recv() so there is no need to
|
||||
* worry about concurrency issues. */
|
||||
error recv(void *buffer, size_t& sz);
|
||||
/* NOTE ctx_dev_t is always 0 */
|
||||
virtual error fetch_device_list(std::vector<ctx_dev_t>& list, void*& ptr);
|
||||
virtual void destroy_device_list(void *ptr);
|
||||
virtual error create_device(ctx_dev_t dev, std::shared_ptr<device>& hwdev);
|
||||
virtual bool match_device(ctx_dev_t dev, std::shared_ptr<device> hwdev);
|
||||
|
||||
hwstub::net::socket_connection m_conn;
|
||||
};
|
||||
|
||||
/** GDB device
|
||||
*
|
||||
* Device based on gdb context. */
|
||||
class device : public hwstub::device
|
||||
{
|
||||
friend class context; /* for ctor */
|
||||
protected:
|
||||
device(std::shared_ptr<hwstub::context> ctx);
|
||||
public:
|
||||
virtual ~device();
|
||||
|
||||
protected:
|
||||
virtual error open_dev(std::shared_ptr<handle>& handle);
|
||||
virtual bool has_multiple_open() const;
|
||||
};
|
||||
|
||||
/** GDB handle
|
||||
*
|
||||
* Handle based on gdb device. */
|
||||
class handle : public hwstub::handle
|
||||
{
|
||||
friend class device; /* for ctor */
|
||||
protected:
|
||||
handle(std::shared_ptr<hwstub::device> dev);
|
||||
public:
|
||||
virtual ~handle();
|
||||
|
||||
protected:
|
||||
virtual error read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic);
|
||||
virtual error write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic);
|
||||
virtual error get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz);
|
||||
virtual error get_dev_log(void *buf, size_t& buf_sz);
|
||||
virtual error exec_dev(uint32_t addr, uint16_t flags);
|
||||
virtual error status() const;
|
||||
virtual size_t get_buffer_size();
|
||||
|
||||
protected:
|
||||
struct hwstub_version_desc_t m_desc_version;
|
||||
struct hwstub_layout_desc_t m_desc_layout;
|
||||
struct hwstub_target_desc_t m_desc_target;
|
||||
};
|
||||
|
||||
} // namespace virt
|
||||
} // namespace hwstub
|
||||
|
||||
#endif /* __HWSTUB_GDB_HPP__ */
|
||||
|
||||
|
|
@ -29,6 +29,58 @@
|
|||
namespace hwstub {
|
||||
namespace net {
|
||||
|
||||
/** Net helper
|
||||
*
|
||||
* This class provides helpers to implement a socket based context. It abstracts
|
||||
* send/recv to return sensible error codes, print debug messages, and has helpers
|
||||
* to create unix and tcp sockets.
|
||||
*
|
||||
* NOTE: this class is NOT thread-safe */
|
||||
class socket_connection
|
||||
{
|
||||
public:
|
||||
socket_connection();
|
||||
~socket_connection();
|
||||
/** Return socket file descriptor, or -1 if none */
|
||||
int get_socket_fd();
|
||||
/** Init a socket connection with an existing file descriptor. Note that the
|
||||
* file descriptor will be closed when the class will be destroyed or init
|
||||
* again. If a previous socket was associated with this class before, it will
|
||||
* be closed. */
|
||||
void init(int socket_fd);
|
||||
/** Close the socket connection now */
|
||||
void close();
|
||||
/** Create a TCP connection with a domain name and a port.
|
||||
* In case of error, return -1 */
|
||||
static int create_tcp(const std::string& domain,
|
||||
const std::string& port, std::string *error = nullptr);
|
||||
/** Init a UNIX socket connection with a file system path (see man for details)
|
||||
* In case of error, the previous connection is NOT closed and
|
||||
* the function returns false, otherwise it returns true. */
|
||||
static int create_unix(const std::string& path,
|
||||
std::string *error = nullptr);
|
||||
/** Init a UNIX socket connection with an abstract name (see man for details)
|
||||
* In case of error, the previous connection is NOT closed and
|
||||
* the function returns false, otherwise it returns true. */
|
||||
static int create_unix_abstract(const std::string& path,
|
||||
std::string *error = nullptr);
|
||||
|
||||
/** Useful functions for network byte order conversion */
|
||||
static uint32_t to_net_order(uint32_t u);
|
||||
static uint32_t from_net_order(uint32_t u);
|
||||
|
||||
/** set operation timeout */
|
||||
void set_timeout(std::chrono::milliseconds ms);
|
||||
|
||||
/** Send a message to the server. */
|
||||
error send(void *buffer, size_t& sz, std::ostream& debug = cnull);
|
||||
/** Receive a message from the server, sz is updated with the received size. */
|
||||
error recv(void *buffer, size_t& sz, std::ostream& debug = cnull);
|
||||
|
||||
protected:
|
||||
int m_socketfd; /* socket file descriptor */
|
||||
};
|
||||
|
||||
/** Net context
|
||||
*
|
||||
* A socket context provides access to another context through a network. This
|
||||
|
|
@ -56,6 +108,7 @@ public:
|
|||
/** Create a UNIX socket context with an abstract name (see man for details) */
|
||||
static std::shared_ptr<context> create_unix_abstract(const std::string& path,
|
||||
std::string *error = nullptr);
|
||||
|
||||
/** Useful functions for network byte order conversion */
|
||||
uint32_t to_net_order(uint32_t u);
|
||||
uint32_t from_net_order(uint32_t u);
|
||||
|
|
@ -144,7 +197,7 @@ protected:
|
|||
virtual error send(void *buffer, size_t& sz);
|
||||
virtual error recv(void *buffer, size_t& sz);
|
||||
|
||||
int m_socketfd; /* socket file descriptor */
|
||||
socket_connection m_conn;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
224
utils/hwstub/lib/hwstub_gdb.cpp
Normal file
224
utils/hwstub/lib/hwstub_gdb.cpp
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
/***************************************************************************
|
||||
* __________ __ ___.
|
||||
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||
* \/ \/ \/ \/ \/
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (C) 2016 by Amaury Pouly
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
****************************************************************************/
|
||||
#include "hwstub_gdb.hpp"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace hwstub {
|
||||
namespace gdb {
|
||||
|
||||
/**
|
||||
* Context
|
||||
*/
|
||||
context::context(int socket_fd)
|
||||
{
|
||||
m_conn.init(socket_fd);
|
||||
}
|
||||
|
||||
context::~context()
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<context> context::create(int socket_fd)
|
||||
{
|
||||
// NOTE: can't use make_shared() because of the protected ctor */
|
||||
return std::shared_ptr<context>(new context(socket_fd));
|
||||
}
|
||||
|
||||
std::shared_ptr<context> context::create_tcp(const std::string& domain,
|
||||
const std::string& port, std::string *error)
|
||||
{
|
||||
int fd = hwstub::net::socket_connection::create_tcp(domain, port, error);
|
||||
if(fd >= 0)
|
||||
return context::create(fd);
|
||||
else
|
||||
return std::shared_ptr<context>();
|
||||
}
|
||||
|
||||
error context::fetch_device_list(std::vector<ctx_dev_t>& list, void*& ptr)
|
||||
{
|
||||
(void) ptr;
|
||||
list.resize(1);
|
||||
list[0] = 0;
|
||||
return error::SUCCESS;
|
||||
}
|
||||
|
||||
void context::destroy_device_list(void *ptr)
|
||||
{
|
||||
(void) ptr;
|
||||
}
|
||||
|
||||
error context::create_device(ctx_dev_t dev, std::shared_ptr<hwstub::device>& hwdev)
|
||||
{
|
||||
/* there is only one device, so we don't use dev */
|
||||
(void) dev;
|
||||
// NOTE: can't use make_shared() because of the protected ctor */
|
||||
hwdev.reset(new device(shared_from_this()));
|
||||
return error::SUCCESS;
|
||||
}
|
||||
|
||||
bool context::match_device(ctx_dev_t dev, std::shared_ptr<hwstub::device> hwdev)
|
||||
{
|
||||
(void) dev;
|
||||
(void) hwdev;
|
||||
/* there is only one device, so they all match */
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Device
|
||||
*/
|
||||
device::device(std::shared_ptr<hwstub::context> ctx)
|
||||
:hwstub::device(ctx)
|
||||
{
|
||||
}
|
||||
|
||||
device::~device()
|
||||
{
|
||||
}
|
||||
|
||||
error device::open_dev(std::shared_ptr<hwstub::handle>& h)
|
||||
{
|
||||
// NOTE: can't use make_shared() because of the protected ctor */
|
||||
h.reset(new handle(shared_from_this()));
|
||||
return error::SUCCESS;
|
||||
}
|
||||
|
||||
bool device::has_multiple_open() const
|
||||
{
|
||||
/* we want to serialize accesses, otherwise multiple concurrent messages to
|
||||
* the server over the same connection will confuse the server */
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle
|
||||
*/
|
||||
handle::handle(std::shared_ptr<hwstub::device> dev)
|
||||
:hwstub::handle(dev)
|
||||
{
|
||||
m_desc_version.bLength = sizeof(m_desc_version);
|
||||
m_desc_version.bDescriptorType = HWSTUB_DT_VERSION;
|
||||
m_desc_version.bMajor = HWSTUB_VERSION_MAJOR;
|
||||
m_desc_version.bMinor = HWSTUB_VERSION_MINOR;
|
||||
m_desc_version.bRevision = 0;
|
||||
|
||||
m_desc_layout.bLength = sizeof(m_desc_layout);
|
||||
m_desc_layout.bDescriptorType = HWSTUB_DT_LAYOUT;
|
||||
m_desc_layout.dCodeStart = 0;
|
||||
m_desc_layout.dCodeSize = 0;
|
||||
m_desc_layout.dStackStart = 0;
|
||||
m_desc_layout.dStackSize = 0;
|
||||
m_desc_layout.dBufferStart = 0;
|
||||
m_desc_layout.dBufferSize = 1;
|
||||
|
||||
m_desc_target.bLength = sizeof(m_desc_target);
|
||||
m_desc_target.bDescriptorType = HWSTUB_DT_TARGET;
|
||||
m_desc_target.dID = HWSTUB_TARGET_UNK;
|
||||
strncpy(m_desc_target.bName, "GDB server", sizeof(m_desc_target.bName));
|
||||
m_desc_target.bName[sizeof(m_desc_target.bName) - 1] = 0;
|
||||
}
|
||||
|
||||
handle::~handle()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
error handle::read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic)
|
||||
{
|
||||
std::shared_ptr<hwstub::context> hctx = get_device()->get_context();
|
||||
if(!hctx)
|
||||
return error::NO_CONTEXT;
|
||||
context *ctx = dynamic_cast<context*>(hctx.get());
|
||||
ctx->debug() << "[gdb::handle] --> READ(0x" << std::hex
|
||||
<< addr << "," << sz << "," << atomic << ")\n";
|
||||
ctx->debug() << "[gdb::handle] <-- READ unimplemented\n";
|
||||
/* implement read here using ctx->send() and ctx->recv() */
|
||||
(void) addr;
|
||||
(void) buf;
|
||||
(void) sz;
|
||||
(void) atomic;
|
||||
return error::DUMMY;
|
||||
}
|
||||
|
||||
error handle::write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic)
|
||||
{
|
||||
std::shared_ptr<hwstub::context> hctx = get_device()->get_context();
|
||||
if(!hctx)
|
||||
return error::NO_CONTEXT;
|
||||
context *ctx = dynamic_cast<context*>(hctx.get());
|
||||
ctx->debug() << "[gdb::handle] --> WRITE(0x" << std::hex
|
||||
<< addr << "," << sz << "," << atomic << ")\n";
|
||||
ctx->debug() << "[gdb::handle] <-- WRITE unimplemented\n";
|
||||
/* implement read here using ctx.send() and ctx.recv() */
|
||||
(void) addr;
|
||||
(void) buf;
|
||||
(void) sz;
|
||||
(void) atomic;
|
||||
return error::DUMMY;
|
||||
}
|
||||
|
||||
error handle::get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz)
|
||||
{
|
||||
void *p = nullptr;
|
||||
switch(desc)
|
||||
{
|
||||
case HWSTUB_DT_VERSION: p = &m_desc_version; break;
|
||||
case HWSTUB_DT_LAYOUT: p = &m_desc_layout; break;
|
||||
case HWSTUB_DT_TARGET: p = &m_desc_target; break;
|
||||
default: break;
|
||||
}
|
||||
if(p == nullptr)
|
||||
return error::ERROR;
|
||||
/* size is in the bLength field of the descriptor */
|
||||
size_t desc_sz = *(uint8_t *)p;
|
||||
buf_sz = std::min(buf_sz, desc_sz);
|
||||
memcpy(buf, p, buf_sz);
|
||||
return error::SUCCESS;
|
||||
}
|
||||
|
||||
error handle::get_dev_log(void *buf, size_t& buf_sz)
|
||||
{
|
||||
(void) buf;
|
||||
(void) buf_sz;
|
||||
return error::DUMMY;
|
||||
}
|
||||
|
||||
error handle::exec_dev(uint32_t addr, uint16_t flags)
|
||||
{
|
||||
(void) addr;
|
||||
(void) flags;
|
||||
return error::DUMMY;
|
||||
}
|
||||
|
||||
error handle::status() const
|
||||
{
|
||||
return hwstub::handle::status();
|
||||
}
|
||||
|
||||
size_t handle::get_buffer_size()
|
||||
{
|
||||
return 2048;
|
||||
}
|
||||
|
||||
} // namespace gdb
|
||||
} // namespace net
|
||||
|
||||
|
|
@ -29,39 +29,6 @@
|
|||
namespace hwstub {
|
||||
namespace net {
|
||||
|
||||
/**
|
||||
* Context
|
||||
*/
|
||||
context::context()
|
||||
:m_state(state::HELLO), m_error(error::SUCCESS)
|
||||
{
|
||||
}
|
||||
|
||||
context::~context()
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<context> context::create_socket(int socket_fd)
|
||||
{
|
||||
// NOTE: can't use make_shared() because of the protected ctor */
|
||||
return std::shared_ptr<socket_context>(new socket_context(socket_fd));
|
||||
}
|
||||
|
||||
std::string context::default_unix_path()
|
||||
{
|
||||
return "hwstub";
|
||||
}
|
||||
|
||||
std::string context::default_tcp_domain()
|
||||
{
|
||||
return "localhost";
|
||||
}
|
||||
|
||||
std::string context::default_tcp_port()
|
||||
{
|
||||
return "6666";
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
/* len is the total length, including a 0 character if any */
|
||||
|
|
@ -171,10 +138,163 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Socket connection
|
||||
*/
|
||||
socket_connection::socket_connection()
|
||||
:m_socketfd(-1)
|
||||
{
|
||||
}
|
||||
|
||||
socket_connection::~socket_connection()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
int socket_connection::get_socket_fd()
|
||||
{
|
||||
return m_socketfd;
|
||||
}
|
||||
|
||||
void socket_connection::init(int socket_fd)
|
||||
{
|
||||
close();
|
||||
m_socketfd = socket_fd;
|
||||
}
|
||||
|
||||
void socket_connection::close()
|
||||
{
|
||||
if(m_socketfd >= 0)
|
||||
::close(m_socketfd);
|
||||
m_socketfd = -1;
|
||||
}
|
||||
|
||||
int socket_connection::create_tcp(const std::string& domain,
|
||||
const std::string& port, std::string *error)
|
||||
{
|
||||
return create_tcp_low(domain, port, false, error);
|
||||
}
|
||||
|
||||
int socket_connection::create_unix(const std::string& path,
|
||||
std::string *error)
|
||||
{
|
||||
return create_unix_low(false, path.c_str(), path.size() + 1, true, error);
|
||||
}
|
||||
|
||||
int socket_connection::create_unix_abstract(const std::string& path,
|
||||
std::string *error)
|
||||
{
|
||||
std::string fake_path = "#" + path; /* the # will be overriden by 0 */
|
||||
return create_unix_low(true, fake_path.c_str(), fake_path.size(), true, error);
|
||||
}
|
||||
|
||||
uint32_t socket_connection::to_net_order(uint32_t u)
|
||||
{
|
||||
return htonl(u);
|
||||
}
|
||||
|
||||
uint32_t socket_connection::from_net_order(uint32_t u)
|
||||
{
|
||||
return ntohl(u);
|
||||
}
|
||||
|
||||
void socket_connection::set_timeout(std::chrono::milliseconds ms)
|
||||
{
|
||||
struct timeval tv;
|
||||
tv.tv_usec = 1000 * (ms.count() % 1000);
|
||||
tv.tv_sec = ms.count() / 1000;
|
||||
/* set timeout for the client operations */
|
||||
setsockopt(m_socketfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
|
||||
setsockopt(m_socketfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv));
|
||||
}
|
||||
|
||||
error socket_connection::send(void *buffer, size_t& sz, std::ostream& debug)
|
||||
{
|
||||
debug << "[net::sock] send(" << sz << "): ";
|
||||
int ret = ::send(m_socketfd, buffer, sz, MSG_NOSIGNAL);
|
||||
if(ret >= 0)
|
||||
{
|
||||
debug << "good(" << ret << ")\n";
|
||||
sz = (size_t)ret;
|
||||
return error::SUCCESS;
|
||||
}
|
||||
/* convert some errors */
|
||||
debug << "fail(" << errno << "," << strerror(errno) << ")\n";
|
||||
switch(errno)
|
||||
{
|
||||
#if EAGAIN != EWOULDBLOCK
|
||||
case EAGAIN:
|
||||
#endif
|
||||
case EWOULDBLOCK: return error::TIMEOUT;
|
||||
case ECONNRESET: case EPIPE: return error::SERVER_DISCONNECTED;
|
||||
default: return error::NET_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
error socket_connection::recv(void *buffer, size_t& sz, std::ostream& debug)
|
||||
{
|
||||
debug << "[net::sock] recv(" << sz << "): ";
|
||||
int ret = ::recv(m_socketfd, buffer, sz, MSG_WAITALL);
|
||||
if(ret > 0)
|
||||
{
|
||||
debug << "good(" << ret << ")\n";
|
||||
sz = (size_t)ret;
|
||||
return error::SUCCESS;
|
||||
}
|
||||
if(ret == 0)
|
||||
{
|
||||
debug << "disconnected\n";
|
||||
return error::SERVER_DISCONNECTED;
|
||||
}
|
||||
debug << "fail(" << errno << "," << strerror(errno) << ")\n";
|
||||
switch(errno)
|
||||
{
|
||||
#if EAGAIN != EWOULDBLOCK
|
||||
case EAGAIN:
|
||||
#endif
|
||||
case EWOULDBLOCK: return error::TIMEOUT;
|
||||
default: return error::NET_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Context
|
||||
*/
|
||||
context::context()
|
||||
:m_state(state::HELLO), m_error(error::SUCCESS)
|
||||
{
|
||||
}
|
||||
|
||||
context::~context()
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<context> context::create_socket(int socket_fd)
|
||||
{
|
||||
// NOTE: can't use make_shared() because of the protected ctor */
|
||||
return std::shared_ptr<socket_context>(new socket_context(socket_fd));
|
||||
}
|
||||
|
||||
std::string context::default_unix_path()
|
||||
{
|
||||
return "hwstub";
|
||||
}
|
||||
|
||||
std::string context::default_tcp_domain()
|
||||
{
|
||||
return "localhost";
|
||||
}
|
||||
|
||||
std::string context::default_tcp_port()
|
||||
{
|
||||
return "6666";
|
||||
}
|
||||
|
||||
std::shared_ptr<context> context::create_tcp(const std::string& domain,
|
||||
const std::string& port, std::string *error)
|
||||
{
|
||||
int fd = create_tcp_low(domain, port, false, error);
|
||||
int fd = socket_connection::create_tcp(domain, port, error);
|
||||
if(fd >= 0)
|
||||
return context::create_socket(fd);
|
||||
else
|
||||
|
|
@ -183,7 +303,7 @@ std::shared_ptr<context> context::create_tcp(const std::string& domain,
|
|||
|
||||
std::shared_ptr<context> context::create_unix(const std::string& path, std::string *error)
|
||||
{
|
||||
int fd = create_unix_low(false, path.c_str(), path.size() + 1, true, error);
|
||||
int fd = socket_connection::create_unix(path, error);
|
||||
if(fd >= 0)
|
||||
return context::create_socket(fd);
|
||||
else
|
||||
|
|
@ -192,8 +312,7 @@ std::shared_ptr<context> context::create_unix(const std::string& path, std::stri
|
|||
|
||||
std::shared_ptr<context> context::create_unix_abstract(const std::string& path, std::string *error)
|
||||
{
|
||||
std::string fake_path = "#" + path; /* the # will be overriden by 0 */
|
||||
int fd = create_unix_low(true, fake_path.c_str(), fake_path.size(), true, error);
|
||||
int fd = socket_connection::create_unix_abstract(path, error);
|
||||
if(fd >= 0)
|
||||
return context::create_socket(fd);
|
||||
else
|
||||
|
|
@ -264,12 +383,12 @@ bool context::match_device(ctx_dev_t dev, std::shared_ptr<hwstub::device> hwdev)
|
|||
|
||||
uint32_t context::to_net_order(uint32_t u)
|
||||
{
|
||||
return htonl(u);
|
||||
return socket_connection::to_net_order(u);
|
||||
}
|
||||
|
||||
uint32_t context::from_net_order(uint32_t u)
|
||||
{
|
||||
return ntohl(u);
|
||||
return socket_connection::from_net_order(u);
|
||||
}
|
||||
|
||||
error context::send_cmd(uint32_t cmd, uint32_t args[HWSTUB_NET_ARGS], uint8_t *send_data,
|
||||
|
|
@ -492,74 +611,29 @@ void context::stop_context()
|
|||
* Socket context
|
||||
*/
|
||||
socket_context::socket_context(int socket_fd)
|
||||
:m_socketfd(socket_fd)
|
||||
{
|
||||
m_conn.init(socket_fd);
|
||||
set_timeout(std::chrono::milliseconds(1000));
|
||||
}
|
||||
|
||||
socket_context::~socket_context()
|
||||
{
|
||||
stop_context();
|
||||
close(m_socketfd);
|
||||
}
|
||||
|
||||
void socket_context::set_timeout(std::chrono::milliseconds ms)
|
||||
{
|
||||
struct timeval tv;
|
||||
tv.tv_usec = 1000 * (ms.count() % 1000);
|
||||
tv.tv_sec = ms.count() / 1000;
|
||||
/* set timeout for the client operations */
|
||||
setsockopt(m_socketfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
|
||||
setsockopt(m_socketfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv));
|
||||
m_conn.set_timeout(ms);
|
||||
}
|
||||
|
||||
error socket_context::send(void *buffer, size_t& sz)
|
||||
{
|
||||
debug() << "[net::ctx::sock] send(" << sz << "): ";
|
||||
int ret = ::send(m_socketfd, buffer, sz, MSG_NOSIGNAL);
|
||||
if(ret >= 0)
|
||||
{
|
||||
debug() << "good(" << ret << ")\n";
|
||||
sz = (size_t)ret;
|
||||
return error::SUCCESS;
|
||||
}
|
||||
/* convert some errors */
|
||||
debug() << "fail(" << errno << "," << strerror(errno) << ")\n";
|
||||
switch(errno)
|
||||
{
|
||||
#if EAGAIN != EWOULDBLOCK
|
||||
case EAGAIN:
|
||||
#endif
|
||||
case EWOULDBLOCK: return error::TIMEOUT;
|
||||
case ECONNRESET: case EPIPE: return error::SERVER_DISCONNECTED;
|
||||
default: return error::NET_ERROR;
|
||||
}
|
||||
return m_conn.send(buffer, sz, debug());
|
||||
}
|
||||
|
||||
error socket_context::recv(void *buffer, size_t& sz)
|
||||
{
|
||||
debug() << "[net::ctx::sock] recv(" << sz << "): ";
|
||||
int ret = ::recv(m_socketfd, buffer, sz, MSG_WAITALL);
|
||||
if(ret > 0)
|
||||
{
|
||||
debug() << "good(" << ret << ")\n";
|
||||
sz = (size_t)ret;
|
||||
return error::SUCCESS;
|
||||
}
|
||||
if(ret == 0)
|
||||
{
|
||||
debug() << "disconnected\n";
|
||||
return error::SERVER_DISCONNECTED;
|
||||
}
|
||||
debug() << "fail(" << errno << "," << strerror(errno) << ")\n";
|
||||
switch(errno)
|
||||
{
|
||||
#if EAGAIN != EWOULDBLOCK
|
||||
case EAGAIN:
|
||||
#endif
|
||||
case EWOULDBLOCK: return error::TIMEOUT;
|
||||
default: return error::NET_ERROR;
|
||||
}
|
||||
return m_conn.recv(buffer, sz, debug());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "hwstub_usb.hpp"
|
||||
#include "hwstub_virtual.hpp"
|
||||
#include "hwstub_net.hpp"
|
||||
#include "hwstub_gdb.hpp"
|
||||
#include <cctype>
|
||||
|
||||
namespace hwstub {
|
||||
|
|
@ -41,6 +42,8 @@ void print_usage(FILE *f, bool client, bool server)
|
|||
if(client)
|
||||
fprintf(f, " usb USB context (using libusb)\n");
|
||||
fprintf(f, " tcp TCP context\n");
|
||||
if(client)
|
||||
fprintf(f, " gdb A GDB context over tcp\n");
|
||||
fprintf(f, " unix Local unix domain context\n");
|
||||
if(client)
|
||||
fprintf(f, " virt A virtual context (testing and debugging mostly)\n");
|
||||
|
|
@ -63,6 +66,8 @@ void print_usage(FILE *f, bool client, bool server)
|
|||
fprintf(f, "in which case the domain must start with a '#':\n");
|
||||
fprintf(f, " unix:///path/to/socket\n");
|
||||
fprintf(f, " unix://#hwstub\n");
|
||||
fprintf(f, "When creating a GDB context, the domain and port are the usual TCP parameters:\n");
|
||||
fprintf(f, " gdb://localhost:4141\n");
|
||||
if(client)
|
||||
{
|
||||
fprintf(f, "When creating a virtual context, the domain must contain a specification of the devices.\n");
|
||||
|
|
@ -288,6 +293,10 @@ std::shared_ptr<context> create_context(const uri& uri, std::string *error)
|
|||
{
|
||||
return hwstub::net::context::create_tcp(uri.domain(), uri.port(), error);
|
||||
}
|
||||
else if(uri.scheme() == "gdb")
|
||||
{
|
||||
return hwstub::gdb::context::create_tcp(uri.domain(), uri.port(), error);
|
||||
}
|
||||
else if(uri.scheme() == "unix")
|
||||
{
|
||||
/* port must be empty */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue