4 Commits

Author SHA1 Message Date
Gurjant Kalsi
e1f04d68a6 [mocom] Lock access to the workers list with a mutex. 2016-02-23 15:49:10 -08:00
Gurjant Kalsi
11d65682e4 [mocom] Setup Mocom app on the Dartuino Target. 2016-02-23 15:26:11 -08:00
Travis Geiselbrecht
d16ad10f17 [app][mocom] start of the mocom app 2016-02-21 14:15:58 -08:00
Travis Geiselbrecht
89e52a426e [lib][cpputils] start of a quick library of some common cpp stuff 2016-02-21 14:15:58 -08:00
30 changed files with 2355 additions and 115 deletions

33
app/mocom/app.c Normal file
View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <app.h>
extern void mocom_init(const struct app_descriptor *app);
extern void mocom_entry(const struct app_descriptor *app, void *args);
APP_START(mocom)
.init = mocom_init,
.entry = mocom_entry,
APP_END

313
app/mocom/channel.cpp Normal file
View File

@@ -0,0 +1,313 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "channel.hpp"
#include <err.h>
#include <debug.h>
#include <stdio.h>
#include <assert.h>
#include <trace.h>
#include <string.h>
#include <rand.h>
#include <target.h>
#include <compiler.h>
#include <ctype.h>
#include <platform.h>
#include "mux.hpp"
#include "prot/mux.h"
#include "cmd_handler/console.hpp"
#define LOCAL_TRACE 0
namespace mocom {
// base channel class
status_t channel::queue_tx(const void *buf, size_t len)
{
LTRACEF("buf %p, len %zu\n", buf, len);
if (m_tx_buf)
return ERR_BUSY;
m_tx_buf = (const uint8_t *)buf;
m_tx_len = len;
return NO_ERROR;
}
status_t channel::close()
{
return NO_ERROR;
}
// command channel
void control_channel::process_rx_packet(const uint8_t *buf, size_t len)
{
LTRACEF("buf %p, len %zu\n", buf, len);
const struct pmux_control_header *header = (const struct pmux_control_header *)buf;
if (len < sizeof(struct pmux_control_header))
return;
switch (header->op) {
default:
case PMUX_CONTROL_NONE:
break;
case PMUX_CONTROL_CHANNEL_CLOSED:
// they're telling us they got something on a closed channel
// XXX ignore for now
break;
case PMUX_CONTROL_OPEN:
// they're asking us to open a new channel
PANIC_UNIMPLEMENTED;
break;
case PMUX_CONTROL_OPEN_COMMAND: {
// they're asking us to open a command interpreter channel
channel *c = new command_channel(m_mux, header->channel);
if (!m_mux.add_channel(c)) {
// already exists
delete c;
}
break;
}
case PMUX_CONTROL_CLOSE: {
// they're asking us to close a channel
channel *c = m_mux.find_channel(header->channel);
if (c) {
c->close();
m_mux.remove_channel(c);
delete c;
}
break;
}
}
}
// command channel
static int tokenize_command(const char *inbuffer, size_t inbuflen, char *buffer, size_t buflen, char **args, size_t arg_count)
{
size_t inpos;
size_t outpos;
size_t arg;
enum {
INITIAL = 0,
NEXT_FIELD,
SPACE,
IN_SPACE,
TOKEN,
IN_TOKEN,
QUOTED_TOKEN,
IN_QUOTED_TOKEN,
} state;
inpos = 0;
outpos = 0;
arg = 0;
state = INITIAL;
for (;;) {
char c = inbuffer[inpos];
DEBUG_ASSERT(inpos < inbuflen);
LTRACEF_LEVEL(2, "c 0x%hhx state %d arg %d inpos %d pos %d\n", c, state, arg, inpos, outpos);
switch (state) {
case INITIAL:
case NEXT_FIELD:
if (c == '\0')
goto done;
if (isspace(c))
state = SPACE;
else
state = TOKEN;
break;
case SPACE:
state = IN_SPACE;
break;
case IN_SPACE:
if (c == '\0')
goto done;
if (!isspace(c)) {
state = TOKEN;
} else {
inpos++; // consume the space
if (inpos == inbuflen)
goto done;
}
break;
case TOKEN:
// start of a token
DEBUG_ASSERT(c != '\0');
if (c == '"') {
// start of a quoted token
state = QUOTED_TOKEN;
} else {
// regular, unquoted token
state = IN_TOKEN;
args[arg] = &buffer[outpos];
}
break;
case IN_TOKEN:
if (c == '\0') {
arg++;
goto done;
}
if (isspace(c)) {
arg++;
buffer[outpos] = 0;
outpos++;
/* are we out of tokens? */
if (arg == arg_count)
goto done;
state = NEXT_FIELD;
} else {
buffer[outpos] = c;
outpos++;
inpos++;
if (inpos == inbuflen) {
arg++;
goto done;
}
}
break;
case QUOTED_TOKEN:
// start of a quoted token
DEBUG_ASSERT(c == '"');
state = IN_QUOTED_TOKEN;
args[arg] = &buffer[outpos];
inpos++; // consume the quote
if (inpos == inbuflen)
goto done;
break;
case IN_QUOTED_TOKEN:
if (c == '\0') {
arg++;
goto done;
}
if (c == '"') {
arg++;
buffer[outpos] = 0;
outpos++;
/* are we out of tokens? */
if (arg == arg_count)
goto done;
state = NEXT_FIELD;
}
buffer[outpos] = c;
outpos++;
inpos++;
if (inpos == inbuflen) {
arg++;
goto done;
}
break;
}
/* are we at the end of the output buffer? */
if (outpos == buflen - 1)
break;
}
done:
buffer[outpos] = 0;
return arg;
}
void command_channel::process_rx_packet(const uint8_t *buf, size_t len)
{
LTRACEF("buf %p, len %zu\n", buf, len);
//hexdump8_ex(buf, len, 0);
if (m_state == STATE_INITIAL) {
// try to parse the incoming command
char outbuf[128];
char *tokens[16];
int count = tokenize_command((const char *)buf, len, outbuf, sizeof(outbuf), tokens, countof(tokens));
LTRACEF("command word count %d\n", count);
if (count >= 2 && !strcmp(tokens[0], "open") && !strcmp(tokens[1], "console")) {
// the only command we understand right now
DEBUG_ASSERT(!m_handler);
cmd_handler::console *c = new cmd_handler::console(*this);
if (c->Init() < NO_ERROR) {
delete c;
goto error;
}
m_handler = c;
queue_tx(strdup("ok 0\n"), strlen("ok 0\n"));
m_state = STATE_ESTABLISHED;
} else {
error:
// everything else is unrecognized
snprintf(outbuf, sizeof(outbuf), "err %d %s\n", -1, "unrecognized command");
LTRACEF("returning '%s\n", outbuf);
queue_tx(strdup(outbuf), strlen(outbuf));
}
} else if (m_state == STATE_ESTABLISHED) {
DEBUG_ASSERT(m_handler);
m_handler->process_rx_packet(buf, len);
}
}
void command_channel::tx_complete()
{
LTRACE_ENTRY;
channel::tx_complete();
if (m_handler)
m_handler->tx_complete();
}
status_t command_channel::close()
{
LTRACE_ENTRY;
if (m_handler)
m_handler->close();
return channel::close();
}
command_channel::~command_channel()
{
if (m_handler)
delete m_handler;
}
};

100
app/mocom/channel.hpp Normal file
View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <stdint.h>
#include <sys/types.h>
#include <list.h>
#include <lib/cpputils/nocopy.hpp>
namespace mocom {
class mux;
class channel : lk::nocopy {
public:
channel(mux &m, uint32_t num) : m_mux(m), m_num(num) {}
virtual ~channel() {}
virtual void process_rx_packet(const uint8_t *buf, size_t len) = 0;
virtual status_t close();
uint32_t get_num() const { return m_num; }
virtual status_t queue_tx(const void *buf, size_t len);
virtual void tx_complete() { m_tx_buf = nullptr; m_tx_len = 0; }
protected:
mux &m_mux;
uint32_t m_num;
// rx stuff
uint32_t m_rx_sequence = 0;
uint32_t m_pending_ack_sequence = 0;
bool m_pending_ack = false;
// tx stuff
uint32_t m_tx_sequence = 0;
uint32_t m_acked_tx_sequence = 0;
const uint8_t *m_tx_buf = nullptr;
size_t m_tx_len = 0;
// list of us in the mux class
friend class mux;
struct list_node m_list_node = LIST_INITIAL_CLEARED_VALUE;
};
class control_channel : public channel {
public:
control_channel(mux &m, uint32_t num) : channel(m, num) {}
virtual ~control_channel() {}
virtual void process_rx_packet(const uint8_t *buf, size_t len) override;
};
namespace cmd_handler {
class handler;
}
class command_channel : public channel {
public:
command_channel(mux &m, uint32_t num) : channel(m, num) {}
virtual ~command_channel();
virtual void process_rx_packet(const uint8_t *buf, size_t len) override;
virtual void tx_complete() override;
virtual status_t close() override;
private:
enum {
STATE_INITIAL,
STATE_ESTABLISHED
} m_state = STATE_INITIAL;
// specialized handler to handle the command
cmd_handler::handler *m_handler = nullptr;
};
} // namespace mocom

View File

@@ -0,0 +1,137 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "console.hpp"
#include <err.h>
#include <stdint.h>
#include <trace.h>
#include <assert.h>
#include <debug.h>
#include <../prot/packet_struct.h>
#include <../mocom_app.hpp>
#define LOCAL_TRACE 0
namespace mocom {
namespace cmd_handler {
console::console(command_channel &c)
: handler(c)
{
}
console::~console()
{
the_app->unregister_worker(m_worker);
if (m_registered)
unregister_print_callback(&m_cb);
}
void console::debug_console_callback(print_callback_t *cb, const char *str, size_t len)
{
cbuf_write(&m_outbuf, str, len, false);
the_app->signal_irq();
}
lk_time_t console::work()
{
//LTRACE_ENTRY;
// see if we need to output anything
if (!is_tx_pending() && cbuf_space_used(&m_outbuf) > 0) {
// grab a pointer directly into the cbuf and send it
iovec_t iov[2];
cbuf_peek(&m_outbuf, iov);
DEBUG_ASSERT(iov[0].iov_len > 0);
size_t _len = iov[0].iov_len;
send_stdout_data((const char *)iov[0].iov_base, _len);
// consume the data we sent from the cbuf
cbuf_read(&m_outbuf, NULL, _len, false);
// make sure the worker loops around immediately at least once to queue data
return 0;
} else {
return INFINITE_TIME;
}
}
void console::process_rx_packet(const uint8_t *buf, size_t len)
{
LTRACEF("buf %p, len %zu\n", buf, len);
const struct packet_header *header = (const struct packet_header *)buf;
if (len < sizeof(struct packet_header))
return; // XXX shoudl probably close the channel
if (header->magic != PACKET_HEADER_MAGIC)
return;
if (header->version != PACKET_HEADER_VERSION)
return;
if (header->type == PACKET_HEADER_TYPE_DATA) {
const uint8_t *data = (const uint8_t *)header->data;
hexdump8_ex(data, header->size, 0);
// feed it into the input queue (which doesn't exist right now)
}
}
void console::tx_complete()
{
LTRACE_ENTRY;
handler::tx_complete();
if (cbuf_space_used(&m_outbuf) > 0) {
the_app->signal();
}
}
status_t console::Init()
{
LTRACE_ENTRY;
// initialize the outgoing circular buffer
cbuf_initialize_etc(&m_outbuf, OUTBUF_LEN, &m_outbuf_buf);
// register a worker callback from the main mocom app
m_worker.context = this;
m_worker.work = &_work;
the_app->register_worker(m_worker);
// register for a debug console handler
m_cb.context = this;
m_cb.print = &_debug_console_callback;
register_print_callback(&m_cb);
m_registered = true;
return NO_ERROR;
}
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <stdint.h>
#include <sys/types.h>
#include <debug.h>
#include <lib/cbuf.h>
#include <lib/cpputils/nocopy.hpp>
#include "handler.hpp"
#include "../mocom_app.hpp"
namespace mocom {
namespace cmd_handler {
class console : public handler {
public:
console(command_channel &c);
virtual ~console();
virtual status_t Init() override;
virtual void process_rx_packet(const uint8_t *buf, size_t len) override;
virtual void tx_complete() override;
private:
// main worker callback
lk_time_t work();
static lk_time_t _work(void *context) { return ((console *)context)->work(); }
mocom_app::worker_callback m_worker;
// debug console callback
void debug_console_callback(print_callback_t *cb, const char *str, size_t len);
static void _debug_console_callback(print_callback_t *cb, const char *str, size_t len) {
((console *)cb->context)->debug_console_callback(cb, str, len);
}
bool m_registered = false;
print_callback_t m_cb;
// circular buffer to hold outgoing print data
cbuf_t m_outbuf;
static const size_t OUTBUF_LEN = 1024;
char m_outbuf_buf[OUTBUF_LEN];
// state of tx
};
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "console.hpp"
#include <err.h>
#include <assert.h>
#include <stdint.h>
#include <trace.h>
#include <string.h>
#include <debug.h>
#include <../prot/packet_struct.h>
#define LOCAL_TRACE 0
namespace mocom {
namespace cmd_handler {
handler::handler(command_channel &c)
: m_cc(c)
{
}
handler::~handler()
{
DEBUG_ASSERT(!m_outbuf);
}
status_t handler::send_stdout_data(const char *buf, size_t len)
{
LTRACEF("buf %p, len %zu\n", buf, len);
if (m_outbuf)
return ERR_NOT_READY;
/// XXX remove
m_outbuf = new char[len + sizeof(struct packet_header)];
if (!m_outbuf)
return ERR_NO_MEMORY;
struct packet_header *header = (struct packet_header *)m_outbuf;
header->magic = PACKET_HEADER_MAGIC;
header->version = PACKET_HEADER_VERSION;
header->type = PACKET_HEADER_TYPE_DATA;
header->size = len;
memcpy(header->data, buf, len);
return m_cc.queue_tx(m_outbuf, sizeof(struct packet_header) + len);
}
void handler::tx_complete()
{
LTRACE_ENTRY;
if (m_outbuf) {
delete m_outbuf;
m_outbuf = nullptr;
}
}
void handler::close()
{
LTRACE_ENTRY;
tx_complete();
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <stdint.h>
#include <sys/types.h>
#include <list.h>
#include <lib/cpputils/nocopy.hpp>
#include "../channel.hpp"
namespace mocom {
namespace cmd_handler {
class handler : lk::nocopy {
public:
handler(command_channel &c);
virtual ~handler();
virtual status_t Init() = 0;
virtual void process_rx_packet(const uint8_t *buf, size_t len) = 0;
virtual void close();
virtual void tx_complete();
status_t send_stdout_data(const char *buf, size_t len);
bool is_tx_pending() const { return m_outbuf; }
protected:
command_channel &m_cc;
char *m_outbuf = nullptr;
};
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <compiler.h>
__BEGIN_CDECLS
__END_CDECLS

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#if WITH_DEV_USB
#include <compiler.h>
#include <app/mocom.h>
#include <sys/types.h>
#include <dev/usbc.h>
__BEGIN_CDECLS
status_t mocom_configure_usb_endpoints(uint interface_num, ep_t inep, ep_t outep);
__END_CDECLS
#endif

143
app/mocom/mocom.cpp Normal file
View File

@@ -0,0 +1,143 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "mocom_app.hpp"
#include <app/mocom.h>
#include <err.h>
#include <debug.h>
#include <stdio.h>
#include <trace.h>
#include <target.h>
#include <compiler.h>
#include <app.h>
#include <kernel/thread.h>
#include <kernel/mutex.h>
#include "transport.hpp"
#include "usb.hpp"
#include <lib/cpputils/autolocker.hpp>
#define LOCAL_TRACE 1
namespace mocom {
mocom_app *the_app;
mocom_app::mocom_app(transport &t)
: m_transport(t),
m_mux(m_transport)
{
mutex_init(&m_workers_mutex);
}
mocom_app::~mocom_app()
{
mutex_destroy(&m_workers_mutex);
}
status_t mocom_app::worker()
{
for (;;) {
lk_time_t t = INFINITE_TIME;
// call all of the workers, returning the shortest time returned
// let the transport layer do some work
lk_time_t temp = m_transport.do_work();
if (temp < t)
t = temp;
// call all of the registered workers
mutex_acquire(&m_workers_mutex);
worker_callback *cb;
list_for_every_entry(&m_workers, cb, worker_callback, node) {
temp = cb->work(cb->context);
if (temp < t)
t = temp;
}
mutex_release(&m_workers_mutex);
// wait a cycle
if (t > 0) {
//LTRACEF("waiting %u msec\n", t);
event_wait_timeout(&m_event, t);
}
}
return NO_ERROR;
}
void mocom_app::register_worker(worker_callback &cb)
{
mutex_acquire(&m_workers_mutex);
list_add_head(&m_workers, &cb.node);
mutex_release(&m_workers_mutex);
}
void mocom_app::unregister_worker(worker_callback &cb)
{
mutex_acquire(&m_workers_mutex);
list_delete(&cb.node);
mutex_release(&m_workers_mutex);
}
status_t mocom_app::init()
{
event_init(&m_event, false, EVENT_FLAG_AUTOUNSIGNAL);
m_transport.set_mux(&m_mux);
m_transport.init();
m_mux.init();
return NO_ERROR;
}
extern "C" void mocom_init(const struct app_descriptor *app)
{
}
extern "C" void mocom_entry(const struct app_descriptor *, void *)
{
LTRACE_ENTRY;
// construct a transport for us to talk over
#if WITH_DEV_USB
transport *t = create_usb_transport();
#endif
// construct the main mocom app
mocom_app app(*t);
// set a singleton pointer to it
the_app = &app;
app.init();
// bump ourselves to high priority
thread_set_priority(HIGH_PRIORITY);
app.worker();
}
} // namespace mocom

76
app/mocom/mocom_app.hpp Normal file
View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <list.h>
#include <sys/types.h>
#include <kernel/event.h>
#include <kernel/mutex.h>
#include <lib/cpputils/nocopy.hpp>
#include "transport.hpp"
#include "mux.hpp"
namespace mocom {
class mocom_app : lk::nocopy {
public:
mocom_app(transport &);
~mocom_app();
status_t init();
status_t worker();
// allow registering a worker routine to be run in the main thread context
struct worker_callback {
struct list_node node;
void *context;
lk_time_t (*work)(void *context);
};
void register_worker(worker_callback &cb);
void unregister_worker(worker_callback &cb);
// signal that the workers should cycle through
void signal() { event_signal(&m_event, true); }
void signal_irq() { event_signal(&m_event, false); }
private:
// transport
transport &m_transport;
// mux layer
mux m_mux;
// main app event
event_t m_event;
// mutex to protect list of workers.
mutex_t m_workers_mutex;
struct list_node m_workers = LIST_INITIAL_VALUE(m_workers);
};
// the singleton app
extern mocom_app *the_app;
}

249
app/mocom/mux.cpp Normal file
View File

@@ -0,0 +1,249 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "mux.hpp"
#include "prot/mux.h"
#include <err.h>
#include <debug.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <trace.h>
#include <string.h>
#include <rand.h>
#include <target.h>
#include <compiler.h>
#include <platform.h>
#include <lib/cksum.h>
#include "channel.hpp"
#define LOCAL_TRACE 0
#define TRACE_PACKETS 0
namespace mocom {
static const char *control_op_to_string(uint32_t op)
{
switch (op) {
case PMUX_CONTROL_NONE:
return "none";
case PMUX_CONTROL_CHANNEL_CLOSED:
return "closed";
case PMUX_CONTROL_OPEN:
return "open";
case PMUX_CONTROL_OPEN_COMMAND:
return "openc";
case PMUX_CONTROL_CLOSE:
return "close";
default:
return "unknown";
}
}
#define PMUX_RX 0
#define PMUX_TX 1
static void dump_packet(const void *buf, size_t len, int txrx)
{
#define TXRX ((txrx == PMUX_TX) ? "TX" : "RX")
if (!buf || len == 0) {
// printf("%s NULL\n", TXRX);
return;
}
struct pmux_header *header = (struct pmux_header *)buf;
if ((header->flags & PMUX_FLAG_ACK)) {
printf("%s ACK c %-6d seq %-8d len %-8d tlen %-8d\n",
TXRX, header->channel, header->sequence, header->payload_len, header->total_len);
} else {
if (header->channel == PMUX_CHANNEL_CONTROL) {
struct pmux_control_header *control = (struct pmux_control_header *)header->data;
printf("%s DATA c %-6d seq %-8d len %-8d tlen %-8d CONTROL op %d %6s c %-6d len %-6d\n", TXRX,
header->channel, header->sequence, header->payload_len, header->total_len,
control->op, control_op_to_string(control->op), control->channel, control->len);
} else {
printf("%s DATA c %-6d seq %-8d len %-8d tlen %-8d crc %08x\n", TXRX,
header->channel, header->sequence, header->payload_len, header->total_len, header->crc);
}
}
#undef TXRX
}
void mux::init()
{
// create a default control channel
control_channel *cc = new control_channel(*this, PMUX_CHANNEL_CONTROL);
add_channel(cc);
}
channel *mux::find_channel(uint32_t num)
{
channel *c;
list_for_every_entry(&m_channel_list, c, channel, m_list_node) {
if (c->get_num() == num)
return c;
}
return nullptr;
}
bool mux::add_channel(channel *c)
{
if (find_channel(c->get_num()))
return false;
list_add_head(&m_channel_list, &c->m_list_node);
return true;
}
void mux::remove_channel(channel *c)
{
list_delete(&c->m_list_node);
}
void mux::set_online(bool online)
{
LTRACEF("online %u\n", online);
}
void mux::process_rx_packet(const uint8_t *buf, size_t len)
{
LTRACEF("buf %p, len %zu\n", buf, len);
const struct pmux_header *header = (const struct pmux_header *)buf;
if (len < sizeof(struct pmux_header))
return;
if (header->magic != PMUX_MAGIC)
return;
if (header->version != PMUX_VERSION)
return;
#if TRACE_PACKETS
dump_packet(buf, len, PMUX_RX);
#endif
channel *c = find_channel(header->channel);
if (c) {
if (header->flags & PMUX_FLAG_ACK) {
// this is an ack for us
if (header->sequence > c->m_acked_tx_sequence && header->sequence <= c->m_tx_sequence) {
c->m_acked_tx_sequence = header->sequence;
if (c->m_acked_tx_sequence == c->m_tx_sequence) {
// we are completely acked, so we can kill the tx queue
c->tx_complete();
}
} else {
// ack out of sequence
}
} else {
// we have received data
if (header->sequence == (c->m_rx_sequence + 1)) {
// in sequence from the other side
c->process_rx_packet((uint8_t *)(header + 1), len - sizeof(*header));
c->m_rx_sequence = header->sequence;
c->m_pending_ack_sequence = header->sequence;
c->m_pending_ack = true;
} else if (header->sequence <= c->m_rx_sequence) {
// they are retransmitting for some reason, re-ack it
c->m_pending_ack_sequence = c->m_rx_sequence;
c->m_pending_ack = true;
}
}
} else {
TRACEF("packet for non open channel %u\n", header->channel);
// XXX send CONTROL_CLOSED
PANIC_UNIMPLEMENTED;
}
}
ssize_t mux::prepare_tx_packet(uint8_t *buf, size_t len)
{
//LTRACEF("buf %p, len %zu\n", buf, len);
// see if any channels need an ack
channel *c;
list_for_every_entry(&m_channel_list, c, channel, m_list_node) {
if (c->m_pending_ack) {
c->m_pending_ack = false;
struct pmux_header *header = (struct pmux_header *)buf;
header->magic = PMUX_MAGIC;
header->version = PMUX_VERSION;
header->flags = PMUX_FLAG_ACK;
header->channel = c->get_num();
header->sequence = c->m_pending_ack_sequence;
header->payload_len = 0;
header->total_len = sizeof(struct pmux_header);
#if TRACE_PACKETS
dump_packet(buf, header->total_len, true);
#endif
LTRACEF("acking channel %u\n", c->get_num());
return header->total_len;
}
}
// XXX handle retransmits
// see if any channels have data to send
list_for_every_entry(&m_channel_list, c, channel, m_list_node) {
if (c->m_tx_buf && c->m_acked_tx_sequence == c->m_tx_sequence) {
// we have pending data and the other side has fully acked us
struct pmux_header *header = (struct pmux_header *)buf;
size_t data_len = MIN(c->m_tx_len, len - sizeof(struct pmux_header));
header->magic = PMUX_MAGIC;
header->version = PMUX_VERSION;
header->flags = 0;
header->channel = c->get_num();
header->sequence = ++c->m_tx_sequence;
header->payload_len = data_len;
header->total_len = sizeof(struct pmux_header) + data_len;
memcpy(header->data, c->m_tx_buf, data_len);
header->crc = adler32(1, header->data, header->payload_len);
// XXX for now can only consume the entire queued tx len
DEBUG_ASSERT(data_len == c->m_tx_len);
#if TRACE_PACKETS
dump_packet(buf, header->total_len, true);
#endif
LTRACEF("sending %u bytes on channel %u\n", header->payload_len, c->get_num());
return header->total_len;
}
}
return -1;
}
};

59
app/mocom/mux.hpp Normal file
View File

@@ -0,0 +1,59 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <stdint.h>
#include <sys/types.h>
#include <list.h>
#include <lib/cpputils/nocopy.hpp>
namespace mocom {
class transport;
class channel;
class mux : lk::nocopy {
public:
mux(transport &transport) : m_transport(transport) {}
~mux() {}
void init();
void set_online(bool online);
void process_rx_packet(const uint8_t *buf, size_t len);
ssize_t prepare_tx_packet(uint8_t *buf, size_t len);
private:
// handle to transport
transport &m_transport;
channel *find_channel(uint32_t num);
bool add_channel(channel *c);
void remove_channel(channel *c);
struct list_node m_channel_list = LIST_INITIAL_VALUE(m_channel_list);
friend class control_channel;
};
} // namespace mocom

76
app/mocom/prot/mux.h Normal file
View File

@@ -0,0 +1,76 @@
/* @@@LICENSE
*
* Copyright (c) 2008-2013 LG Electronics, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* LICENSE@@@ */
#ifndef __LIB_PMUX_MUX_H
#define __LIB_PMUX_MUX_H
#include <sys/types.h>
#include <inttypes.h>
/* pmux definitions */
#define PMUX_MAGIC 0x706d7578 /* 'pmux' */
#define PMUX_VERSION 3 /* current version */
#define PMUX_FLAG_ACK 1 /* this packet is an ack */
struct pmux_header {
uint32_t magic;
uint8_t version;
uint8_t _pad;
uint16_t flags;
uint32_t channel;
uint32_t sequence;
uint32_t payload_len;
uint32_t total_len;
uint32_t crc;
uint8_t data[0];
};
/* control messages */
enum pmux_control_message_op {
PMUX_CONTROL_NONE = 0,
PMUX_CONTROL_CHANNEL_CLOSED,
PMUX_CONTROL_OPEN,
PMUX_CONTROL_OPEN_COMMAND, /* open a channel with a command interpreter */
PMUX_CONTROL_CLOSE,
};
struct pmux_control_header {
uint32_t op;
uint32_t channel;
uint32_t len;
uint8_t data[0];
};
enum pmux_channel_num {
PMUX_CHANNEL_NULL, /* non channel, for when you actually dont want to send data */
PMUX_CHANNEL_CONTROL, /* special control commands go over this channel */
PMUX_CHANNEL_CMDSERVICE = 0x50, /* device service commands go over this channel */
PMUX_CHANNEL_MAX_FIXED = 0xfff,
PMUX_CHANNEL_HOST_BASE = 0x1000,
PMUX_CHANNEL_MAX_HOST = 0x7fffffff,
PMUX_CHANNEL_DEVICE_BASE = 0x80000000,
PMUX_CHANNEL_MAX_DEVICE = 0xffffffff
};
#endif

View File

@@ -0,0 +1,64 @@
/* @@@LICENSE
*
* Copyright (c) 2008-2013 LG Electronics, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* LICENSE@@@ */
#ifndef __PACKET_STRUCT_H
#define __PACKET_STRUCT_H
#include <stdint.h>
/* on the wire packet data structure */
#define PACKET_HEADER_MAGIC 0xDECAFBAD
#define PACKET_HEADER_VERSION 1
/* packet types */
#define PACKET_HEADER_TYPE_DATA 0
#define PACKET_HEADER_TYPE_ERR 1
#define PACKET_HEADER_TYPE_OOB 2
struct packet_header {
uint32_t magic;
uint32_t version;
uint32_t size;
uint32_t type;
uint8_t data[0];
};
/* OOB messages */
#define PACKET_OOB_EOF 0
#define PACKET_OOB_SIGNAL 1
#define PACKET_OOB_RETURN 2
#define PACKET_OOB_RESIZE 3
/* data payload for OOB messages */
struct packet_oob_msg {
uint32_t message;
union {
uint32_t signo;
int32_t returncode;
int32_t fileno; /* stdin, stdout, stderr */
struct {
uint32_t rows;
uint32_t cols;
} resize;
uint32_t pad[4]; /* pad it out to 16 bytes, for future expansion*/
} data;
};
#endif

View File

@@ -0,0 +1,72 @@
/* @@@LICENSE
*
* Copyright (c) 2008-2013 LG Electronics, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* LICENSE@@@ */
#ifndef __TRANSPORT_USB_STRUCT_H
#define __TRANSPORT_USB_STRUCT_H
#include <inttypes.h>
#define USBLL_MAGIC 0x7573626c // 'usbl'
#define USBLL_VERSION 1
struct usbll_header {
uint32_t magic;
uint32_t version;
uint32_t txid;
uint32_t rxid;
uint32_t command;
};
/*
* Sync header definition
* It is possible to send accross more data than sizeof(usbll_syn_header):
* data_offset: offset of string data (calculated starting from usbll_syn_header)
* data_length: sizeof string data (including \0 string terminator)
*/
struct usbll_syn_header {
uint8_t nduid[40];
uint32_t mtu;
/* added later, short header means this is default */
uint32_t heartbeat_interval;
uint32_t timeout;
/* adding support of extra data (assumed string) after syn header*/
uint32_t data_offset;
uint32_t data_length;
};
enum usbll_command {
usbll_null = 0, // an ack-like packet to detect spurious syns
usbll_rst,
usbll_syn,
usbll_data,
};
/*
* Recover token structure definition
* USBLL recovery token structure consist of txid, rxid, magic, version (see protocol header)
*/
typedef struct usbll_recovery_token {
uint32_t magic;
uint32_t version;
uint32_t txid;
uint32_t rxid;
} usbll_recovery_token_t;
#endif

23
app/mocom/rules.mk Normal file
View File

@@ -0,0 +1,23 @@
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_DEPS := \
lib/cbuf \
lib/cksum \
lib/cpputils
MODULE_CPPFLAGS := -std=c++11
MODULE_CPPFLAGS += -Wno-invalid-offsetof # XXX this may be a terrible idea
MODULE_SRCS += \
$(LOCAL_DIR)/app.c \
$(LOCAL_DIR)/channel.cpp \
$(LOCAL_DIR)/mocom.cpp \
$(LOCAL_DIR)/mux.cpp \
$(LOCAL_DIR)/usb.cpp \
\
$(LOCAL_DIR)/cmd_handler/console.cpp \
$(LOCAL_DIR)/cmd_handler/handler.cpp \
include make/module.mk

46
app/mocom/transport.hpp Normal file
View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <lib/cpputils/nocopy.hpp>
namespace mocom {
class mux;
class transport : lk::nocopy {
public:
transport() {}
virtual ~transport() {}
// virtual api
virtual status_t init() = 0;
virtual lk_time_t do_work() = 0;
void set_mux(mux *m) { m_mux = m; }
protected:
mux *m_mux = nullptr;
};
}

404
app/mocom/usb.cpp Normal file
View File

@@ -0,0 +1,404 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "usb.hpp"
#include "prot/transport_usb_struct.h"
#include <err.h>
#include <debug.h>
#include <stdio.h>
#include <assert.h>
#include <trace.h>
#include <string.h>
#include <rand.h>
#include <target.h>
#include <compiler.h>
#include <platform.h>
#include <kernel/thread.h>
#include <kernel/spinlock.h>
#include <dev/usb.h>
#include <dev/usbc.h>
#include <app/mocom/usb.h>
#include "mux.hpp"
#include "mocom_app.hpp"
#define LOCAL_TRACE 0
#define TRACE_PACKETS 0
namespace mocom {
#define NDUID_CHRLEN 40
#define NDUID_STRLEN (NDUID_CHRLEN + 1)
/* static configuration */
static ep_t inep;
static ep_t outep;
static uint interface_num;
usb_transport::usb_transport()
: m_inep(inep),
m_outep(outep),
m_interface_num(interface_num)
{
}
usb_transport::~usb_transport()
{
delete[] m_rx_buffer;
delete[] m_tx_buffer;
}
static void usbll_dump_packet(const uint8_t *buf, size_t len, bool tx)
{
const struct usbll_header *header = (const struct usbll_header *)buf;
char traceheader[128];
#if LOCAL_TRACE
//hexdump8_ex(buf, len, 0);
#endif
snprintf(traceheader, sizeof(traceheader), "%s len %5d txid 0x%08x rxid 0x%08x command 0x%x: ",
tx ? "TX" : "RX", (int)len, header->txid, header->rxid, header->command);
switch (header->command) {
case usbll_null:
printf("%s null\n", traceheader);
break;
case usbll_rst:
printf("%s rst\n", traceheader);
break;
case usbll_syn: {
const struct usbll_syn_header *syn_header = (const struct usbll_syn_header *)(header + 1);
char nduid[NDUID_STRLEN];
memcpy(nduid, syn_header->nduid, NDUID_CHRLEN);
nduid[NDUID_CHRLEN] = 0;
if (len >= sizeof(struct usbll_header) + sizeof(struct usbll_syn_header))
printf("%s syn, nduid '%s' mtu %u, heartbeat %u, timeout %u\n",
traceheader, nduid, syn_header->mtu, syn_header->heartbeat_interval, syn_header->timeout);
else
printf("%s syn, nduid '%s' mtu %u\n", traceheader, nduid, syn_header->mtu);
break;
}
case usbll_data:
printf("%s data\n", traceheader);
break;
default:
printf("%s unknown\n", traceheader);
break;
}
}
status_t usb_transport::usb_cb(usb_callback_op_t op, const union usb_callback_args *args)
{
LTRACEF("op %u, args %p\n", op, args);
if (op == USB_CB_ONLINE) {
usbc_setup_endpoint(m_inep, USB_IN, 0x40);
usbc_setup_endpoint(m_outep, USB_OUT, 0x40);
m_online = true;
} else if (op == USB_CB_OFFLINE) {
m_online = false;
}
return NO_ERROR;
}
status_t usb_transport::ep_cb_rx(ep_t endpoint, usbc_transfer_t *t)
{
#if 0 && LOCAL_TRACE
LTRACEF("ep %u transfer %p\n", endpoint, t);
usbc_dump_transfer(t);
if (t->result >= 0) {
hexdump8(t->buf, t->bufpos);
}
#endif
m_rx_transfer_queued = false;
the_app->signal_irq();
return NO_ERROR;
}
status_t usb_transport::ep_cb_tx(ep_t endpoint, usbc_transfer_t *t)
{
m_tx_transfer_queued = false;
the_app->signal_irq();
return NO_ERROR;
}
static void get_nduid(uint8_t *buf)
{
for (int i = 0; i < NDUID_CHRLEN; i++) {
buf[i] = '0' + i;
}
}
bool usb_transport::prepare_tx_packet()
{
DEBUG_ASSERT(m_tx_transfer_queued == false);
struct usbll_header *header = (struct usbll_header *)m_tx_buffer;
header->magic = USBLL_MAGIC;
header->version = USBLL_VERSION;
header->txid = m_txid;
header->rxid = m_rxid;
m_tx_transfer.buflen = 0;
switch (m_state) {
case STATE_LISTEN: {
LTRACEF("STATE_LISTEN, constructing syn packet\n");
// we need to send a syn packet
header->command = usbll_syn;
struct usbll_syn_header *syn = (struct usbll_syn_header *)(header + 1);
get_nduid(syn->nduid);
syn->mtu = 4096;
syn->heartbeat_interval = 250;
syn->timeout = 1000;
syn->data_offset = sizeof(struct usbll_syn_header);
syn->data_length = 0;
m_tx_transfer.buflen = sizeof(*header) + sizeof(*syn);
m_state = STATE_SYNSENT;
return true;
}
case STATE_SYNSENT:
if (current_time() - m_last_tx_time > 1000) {
// retransmit SYN
m_state = STATE_LISTEN;
}
break;
case STATE_ESTABLISHED: {
if (current_time() - m_last_tx_time > 250) { // XXX use negotiated numbers
header->command = usbll_null;
m_tx_transfer.buflen = sizeof(*header);
return true;
}
// ask the mux layer for any data
ssize_t len = m_mux->prepare_tx_packet((uint8_t *)(header + 1), USB_TRANSFER_LEN - sizeof(struct usbll_header));
if (len > 0) {
header->command = usbll_data;
m_tx_transfer.buflen = sizeof(struct usbll_header) + len;
// make sure we're not an even multiple of a usb packet size
if ((m_tx_transfer.buflen % 64) == 0)
m_tx_transfer.buflen++;
DEBUG_ASSERT(m_tx_transfer.buflen <= USB_TRANSFER_LEN);
return true;
}
break;
}
default:
//LTRACEF("other state, doing nothing (for now)\n");
;
}
return false;
}
bool usb_transport::handle_rx_packet(const uint8_t *buf, size_t len)
{
DEBUG_ASSERT(buf);
DEBUG_ASSERT(len <= USB_TRANSFER_LEN);
LTRACEF("buf %p, len %zu\n", buf, len);
const struct usbll_header *header = (const struct usbll_header *)buf;
if (len < sizeof(struct usbll_header))
return false;
if (header->magic != USBLL_MAGIC)
return false;
if (header->version != USBLL_VERSION)
return false;
LTRACEF("state %u, command %u\n", m_state, header->command);
#if TRACE_PACKETS
usbll_dump_packet(buf, len, false);
#endif
switch (m_state) {
case STATE_LISTEN:
case STATE_SYNSENT:
// they're synning us back
if (header->command == usbll_syn) {
m_rxid = header->txid;
if (m_state == STATE_SYNSENT) {
LTRACEF("moving to ESTABLISHED state\n");
m_state = STATE_ESTABLISHED;
m_mux->set_online(true);
}
} else {
LTRACEF("non SYN packet in LISTEN/SYNSENT state\n");
}
break;
case STATE_ESTABLISHED:
if (header->command == usbll_data) {
// pass it up to the mux
m_mux->process_rx_packet((const uint8_t * )(header + 1), len - sizeof(struct usbll_header));
} else if (header->command == usbll_null) {
// we don't care
} else {
LTRACEF("non DATA/NULL packet in ESTABLISHED state\n");
}
}
return true;
}
lk_time_t usb_transport::do_work()
{
LTRACE_ENTRY;
// if we're offline, nothing to do
if (m_online == false) {
// nothing to do
return 1000; // tell the main app to try again in 1000ms
}
// see if we're not receiving for some reason
if (!m_rx_transfer_queued) {
// potentially process data here
if (m_rx_transfer.bufpos > 0)
handle_rx_packet((const uint8_t *)m_rx_transfer.buf, m_rx_transfer.bufpos);
m_rx_transfer.callback = &ep_cb_rx_static;
m_rx_transfer.result = 0;
m_rx_transfer.buf = m_rx_buffer;
m_rx_transfer.buflen = USB_TRANSFER_LEN;
m_rx_transfer.bufpos = 0;
m_rx_transfer.extra = this;
m_rx_transfer_queued = true;
usbc_queue_rx(m_outep, &m_rx_transfer);
}
// check to see if we have some pending tx data
if (!m_tx_transfer_queued) {
if (prepare_tx_packet()) {
// queue it
m_tx_transfer.callback = &ep_cb_tx_static;
m_tx_transfer.result = 0;
m_tx_transfer.buf = m_tx_buffer;
m_tx_transfer.bufpos = 0;
m_tx_transfer.extra = this;
m_tx_transfer_queued = true;
m_last_tx_time = current_time();
#if TRACE_PACKETS
usbll_dump_packet(m_tx_buffer, m_tx_transfer.buflen, true);
#endif
usbc_queue_tx(m_inep, &m_tx_transfer);
}
}
return INFINITE_TIME;
}
status_t usb_transport::init()
{
#define W(w) (w & 0xff), (w >> 8)
#define W3(w) (w & 0xff), ((w >> 8) & 0xff), ((w >> 16) & 0xff)
// build a descriptor for it
uint8_t if_descriptor[] = {
0x09, /* length */
INTERFACE, /* type */
(uint8_t)m_interface_num,/* interface num */
0x00, /* alternates */
0x02, /* endpoint count */
0xff, /* interface class */
0x47, /* interface subclass */
0x11, /* interface protocol */
0x00, /* string index */
/* endpoint IN */
0x07, /* length */
ENDPOINT, /* type */
(uint8_t)(m_inep | 0x80), /* address: 1 IN */
0x02, /* type: bulk */
W(64), /* max packet size: 64 */
00, /* interval */
/* endpoint OUT */
0x07, /* length */
ENDPOINT, /* type */
(uint8_t)m_outep, /* address: 1 OUT */
0x02, /* type: bulk */
W(64), /* max packet size: 64 */
00, /* interval */
};
LTRACEF("epin %u, epout %u\n", m_inep, m_outep);
usb_append_interface_lowspeed(if_descriptor, sizeof(if_descriptor));
usb_append_interface_highspeed(if_descriptor, sizeof(if_descriptor));
usb_register_callback(usb_cb_static, this);
// clear the state
m_state = STATE_LISTEN;
m_online = false;
// allocate some buffers
m_rx_buffer = new uint8_t[USB_TRANSFER_LEN];
m_rx_transfer = { };
m_rx_transfer_queued = false;
m_tx_buffer = new uint8_t[USB_TRANSFER_LEN];
m_tx_transfer = { };
m_tx_transfer_queued = false;
m_rxid = 0;
m_txid = rand();
m_last_tx_time = 0;
return NO_ERROR;
#undef W
#undef W3
}
} // namespace mocom
// C api to configure the endpoints
status_t mocom_configure_usb_endpoints(uint _interface, ep_t _inep, ep_t _outep)
{
LTRACEF("interface %u, inep %u, outep %u\n", _interface, _inep, _outep);
mocom::inep = _inep;
mocom::outep = _outep;
mocom::interface_num = _interface;
return NO_ERROR;
}

116
app/mocom/usb.hpp Normal file
View File

@@ -0,0 +1,116 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <dev/usbc.h>
#include <dev/usb.h>
#include <kernel/spinlock.h>
#include "transport.hpp"
#include <lib/cpputils/nocopy.hpp>
namespace mocom {
class usb_transport : public transport {
public:
usb_transport();
virtual ~usb_transport();
virtual status_t init() override;
virtual lk_time_t do_work() override;
private:
// in/out endpoints
ep_t m_inep;
ep_t m_outep;
uint m_interface_num;
enum {
STATE_LISTEN,
STATE_SYNSENT,
STATE_ESTABLISHED,
} m_state;
volatile bool m_online;
// transfer lock
spin_lock_t lock;
static const size_t USB_TRANSFER_LEN = 4096;
// rx transfers
bool m_rx_transfer_queued;
usbc_transfer_t m_rx_transfer;
uint8_t *m_rx_buffer = nullptr;
// tx transfers
bool m_tx_transfer_queued;
usbc_transfer_t m_tx_transfer;
uint8_t *m_tx_buffer = nullptr;
uint32_t m_txid;
uint32_t m_rxid;
lk_time_t m_last_tx_time;
// usb layer callbacks
status_t usb_cb(usb_callback_op_t op, const union usb_callback_args *args);
status_t ep_cb_rx(ep_t endpoint, usbc_transfer_t *t);
status_t ep_cb_tx(ep_t endpoint, usbc_transfer_t *t);
static status_t usb_cb_static(void *cookie, usb_callback_op_t op, const union usb_callback_args *args);
static status_t ep_cb_rx_static(ep_t endpoint, usbc_transfer_t *t);
static status_t ep_cb_tx_static(ep_t endpoint, usbc_transfer_t *t);
// internal routines
bool prepare_tx_packet();
bool handle_rx_packet(const uint8_t *buf, size_t len);
// no copy
usb_transport(const usb_transport &);
usb_transport& operator=(const usb_transport &);
};
inline status_t usb_transport::usb_cb_static(void *cookie, usb_callback_op_t op, const union usb_callback_args *args) {
usb_transport *t = static_cast<usb_transport *>(cookie);
return t->usb_cb(op, args);
}
inline status_t usb_transport::ep_cb_rx_static(ep_t endpoint, usbc_transfer_t *t)
{
usb_transport *_t = static_cast<usb_transport *>(t->extra);
return _t->ep_cb_rx(endpoint, t);
}
inline status_t usb_transport::ep_cb_tx_static(ep_t endpoint, usbc_transfer_t *t)
{
usb_transport *_t = static_cast<usb_transport *>(t->extra);
return _t->ep_cb_tx(endpoint, t);
}
inline transport *create_usb_transport()
{
usb_transport *u = new usb_transport();
return u;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <kernel/mutex.h>
#include <kernel/spinlock.h>
#include "nocopy.hpp"
// XXX quick lock wrappers. need to move somewhere else
namespace lk {
class automutex : nocopy {
public:
automutex(mutex_t *mutex) : m_lock(mutex) {
mutex_acquire(m_lock);
}
~automutex() {
release();
}
void release() {
if (m_lock) {
mutex_release(m_lock);
m_lock = nullptr;
}
}
private:
mutex_t *m_lock;
};
// XXX this doesn't seem to be terribly efficient on the release case
class autospinlock : nocopy {
public:
autospinlock(spin_lock_t *lock, spin_lock_save_flags_t flags = SPIN_LOCK_FLAG_INTERRUPTS)
: m_lock(lock), m_flags(flags) {
spin_lock_save(m_lock, &m_state, m_flags);
}
~autospinlock() {
release();
}
void release() {
if (m_lock) {
spin_unlock_restore(m_lock, m_state, m_flags);
m_lock = nullptr;
}
}
private:
spin_lock_t *m_lock;
spin_lock_save_flags_t m_flags;
spin_lock_saved_state_t m_state;
};
};

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2015 Travis Geiselbrecht
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
namespace lk {
struct nocopy {
// no copy
nocopy() = default;
nocopy(const nocopy &) = delete;
nocopy& operator=(const nocopy &) = delete;
};
};

0
lib/cpputils/null.cpp Normal file
View File

15
lib/cpputils/rules.mk Normal file
View File

@@ -0,0 +1,15 @@
LOCAL_DIR := $(GET_LOCAL_DIR)
MODULE := $(LOCAL_DIR)
MODULE_DEPS := \
MODULE_SRCS := \
$(LOCAL_DIR)/null.cpp
ifeq ($(WITH_CPP_SUPPORT),true)
MODULE_SRCS += \
endif
include make/module.mk

View File

@@ -5,3 +5,4 @@ include project/virtual/minip.mk
# Console serial port is on pins PA9(TX) and PB7(RX)
include project/virtual/fs.mk
include project/virtual/mocom.mk

View File

@@ -1,13 +1,12 @@
include project/target/stm32f746g-disco.mk
include project/virtual/test.mk
include project/virtual/minip.mk
include project/virtual/mocom.mk
include project/virtual/fs.mk
MODULES += \
lib/fs \
app/loader
include project/virtual/fs.mk
LK_HEAP_IMPLEMENTATION=cmpctmalloc
# Console serial port is on pins PA9(TX) and PB7(RX)

5
project/virtual/mocom.mk Normal file
View File

@@ -0,0 +1,5 @@
# modules related to mocom
MODULES += \
app/mocom

View File

@@ -27,6 +27,7 @@ GLOBAL_DEFINES += \
MODULE_DEPS += \
app/accelerometer \
dev/usb/class/bulktest \
GLOBAL_INCLUDES += $(LOCAL_DIR)/include

View File

@@ -28,8 +28,10 @@
#include <compiler.h>
#include <dev/usb.h>
#include <dev/usbc.h>
#include <dev/usb/class/bulktest.h>
#include <hw/usb.h>
#include <lk/init.h>
#include <app/mocom/usb.h>
#define LOCAL_TRACE 0
@@ -80,34 +82,6 @@ static const uint8_t cfg_descr[] = {
static const uchar langid[] = { 0x04, 0x03, 0x09, 0x04 };
static const uint8_t if_descriptor_lowspeed[] = {
0x09, /* length */
INTERFACE, /* type */
0x01, /* interface num */
0x00, /* alternates */
0x02, /* endpoint count */
0xff, /* interface class */
0xff, /* interface subclass */
0x00, /* interface protocol */
0x00, /* string index */
/* endpoint 1 IN */
0x07, /* length */
ENDPOINT, /* type */
0x81, /* address: 1 IN */
0x02, /* type: bulk */
W(64), /* max packet size: 64 */
00, /* interval */
/* endpoint 1 OUT */
0x07, /* length */
ENDPOINT, /* type */
0x01, /* address: 1 OUT */
0x02, /* type: bulk */
W(64), /* max packet size: 64 */
00, /* interval */
};
usb_config config = {
.lowspeed = {
.device = USB_DESC_STATIC(dev_descr),
@@ -123,97 +97,19 @@ usb_config config = {
.langid = USB_DESC_STATIC(langid),
};
static status_t ep_cb_rx(ep_t endpoint, usbc_transfer_t *t);
static status_t ep_cb_tx(ep_t endpoint, usbc_transfer_t *t);
static void queue_rx(void)
{
static usbc_transfer_t transfer;
static uint8_t buf[512];
transfer.callback = &ep_cb_rx;
transfer.result = 0;
transfer.buf = &buf;
transfer.buflen = sizeof(buf);
transfer.bufpos = 0;
transfer.extra = 0;
usbc_queue_rx(1, &transfer);
}
static void queue_tx(void)
{
static usbc_transfer_t transfer;
static uint8_t buf[512];
for (uint i = 0; i < sizeof(buf); i++) {
buf[i] = ~i;
}
transfer.callback = &ep_cb_tx;
transfer.result = 0;
transfer.buf = &buf;
transfer.buflen = sizeof(buf);
transfer.bufpos = 0;
transfer.extra = 0;
usbc_queue_tx(1, &transfer);
}
static status_t ep_cb_rx(ep_t endpoint, usbc_transfer_t *t)
{
#if LOCAL_TRACE
LTRACEF("ep %u transfer %p\n", endpoint, t);
usbc_dump_transfer(t);
if (t->result >= 0) {
hexdump8(t->buf, t->bufpos);
}
#endif
if (t->result >= 0)
queue_rx();
return NO_ERROR;
}
static status_t ep_cb_tx(ep_t endpoint, usbc_transfer_t *t)
{
#if LOCAL_TRACE
LTRACEF("ep %u transfer %p\n", endpoint, t);
usbc_dump_transfer(t);
#endif
if (t->result >= 0)
queue_tx();
return NO_ERROR;
}
static status_t usb_cb(void *cookie, usb_callback_op_t op, const union usb_callback_args *args)
{
LTRACEF("cookie %p, op %u, args %p\n", cookie, op, args);
if (op == USB_CB_ONLINE) {
usbc_setup_endpoint(1, USB_IN, 0x40);
usbc_setup_endpoint(1, USB_OUT, 0x40);
queue_rx();
queue_tx();
}
return NO_ERROR;
}
void target_usb_setup(void)
{
usb_setup(&config);
printf("appending interfaces\n");
usb_append_interface_lowspeed(if_descriptor_lowspeed, sizeof(if_descriptor_lowspeed));
usb_append_interface_highspeed(if_descriptor_lowspeed, sizeof(if_descriptor_lowspeed));
usb_add_string("LK", 1);
usb_add_string("LK Industries", 2);
usb_register_callback(&usb_cb, NULL);
/* add our bulk endpoint class device */
usb_class_bulktest_init(1, 1, 1);
/* configure the mocom stuff */
mocom_configure_usb_endpoints(2, 2, 2);
usb_start();
}

View File

@@ -31,6 +31,7 @@
#include <dev/usb/class/bulktest.h>
#include <hw/usb.h>
#include <lk/init.h>
#include <app/mocom/usb.h>
#define LOCAL_TRACE 0
@@ -107,5 +108,8 @@ void target_usb_setup(void)
/* add our bulk endpoint class device */
usb_class_bulktest_init(1, 1, 1);
/* configure the mocom stuff */
mocom_configure_usb_endpoints(2, 2, 2);
usb_start();
}