blob: 2877ad7bab23c300e9cadf5bf960a6008422b559 [file] [log] [blame]
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "usbdev_serial.h"
#include <cstdio>
#include <iostream>
#include <unistd.h>
#include "stream_test.h"
#include "usbdev_utils.h"
USBDevSerial::~USBDevSerial() { Stop(); }
// Iniitialise a stream between the specified input and output ports.
bool USBDevSerial::Open(const char *in_name, const char *out_name) {
// Take a copy of the port names.
inPort_ = in_name;
outPort_ = out_name;
return OpenPorts();
}
// Open the input and output ports to the board/device for this stream.
bool USBDevSerial::OpenPorts() {
in_ = port_open(inPort_.c_str(), false);
if (in_ < 0) {
return false;
}
out_ = port_open(outPort_.c_str(), true);
if (out_ < 0) {
close(in_);
return false;
}
if (cfg.verbose) {
printf("S%u: input '%s' (%d) output '%s' (%d)\n", id_, inPort_.c_str(), in_,
outPort_.c_str(), out_);
}
return true;
}
void USBDevSerial::Stop() {
// Close any open port handles.
if (in_ >= 0) {
close(in_);
in_ = -1;
}
if (out_ >= 0) {
close(out_);
out_ = -1;
}
}
void USBDevSerial::Pause() {
// This is the same behavior as stopping.
Stop();
}
bool USBDevSerial::Resume() {
// I don't think we can expect this to suspend/resume at the byte level
// anyway; we seem to resume at a different point in the LFSR, presumably
// because some internal buffer within the kernel/driver level dumps some
// bytes when we close the handle.
return OpenPorts();
}
// Return a summary report of the stream settings of status.
std::string USBDevSerial::Report(bool status, bool verbose) const { return ""; }
// Sending of OUT traffic to device.
bool USBDevSerial::ServiceOUT() {
uint8_t *data;
size_t to_send = DataAvailable(&data);
if (to_send > 0U) {
ssize_t nsent;
if (send_) {
if (cfg.verbose) {
std::cout << PrefixID() << "Trying to send " << to_send << "byte(s)"
<< std::endl;
}
// Propagate the modified bytes to the output port.
nsent = send_bytes(out_, &buf_.data[buf_.rd_idx], to_send);
if (nsent < 0) {
return false;
}
} else {
nsent = to_send;
}
if (cfg.verbose) {
std::cout << PrefixID() << (send_ ? "Sent" : "Dropped") << nsent
<< " byte(s)" << std::endl;
}
ConsumeData(nsent);
}
return true;
}
// Retrieving of IN traffic from device.
bool USBDevSerial::ServiceIN() {
// Decide how many bytes to try to read into our buffer.
uint8_t *dp;
uint32_t space_bytes = SpaceAvailable(&dp);
uint32_t to_fetch = space_bytes;
if (to_fetch > transfer_bytes_ - bytes_recvd_) {
to_fetch = transfer_bytes_ - bytes_recvd_;
}
ssize_t nrecvd;
if (!SigReceived() || retrieve_) {
// Read as many bytes as we can from the input port.
nrecvd = recv_bytes(in_, dp, to_fetch);
if (nrecvd < 0) {
return false;
}
// Update the circular buffer with the amount of data that we've written.
CommitData(nrecvd);
if (nrecvd > 0 && !SigReceived()) {
uint32_t dropped = SigDetect(&sig_, dp, (uint32_t)nrecvd);
// Consume stream signature, rather than propagating it to the output
// side.
if (SigReceived()) {
SigProcess(sig_);
dropped += sizeof(usbdev_stream_sig_t);
}
// Skip past any dropped bytes, including the signature, so that if there
// are additional bytes we may process them.
nrecvd = ((uint32_t)nrecvd > dropped) ? ((uint32_t)nrecvd - dropped) : 0;
dp += dropped;
if (dropped) {
DiscardData(dropped);
}
}
} else {
// Generate a stream of bytes _as if_ we'd received them correctly from
// the device.
GenerateData(dp, to_fetch);
nrecvd = to_fetch;
// Update the circular buffer with the amount of data that we've written.
CommitData(nrecvd);
}
bool ok = true;
if (nrecvd > 0) {
// Check the received LFSR-generated byte(s) and combine them with the
// output of our host-side LFSR.
ok = ProcessData(dp, nrecvd);
}
return ok;
}
// Service this stream.
bool USBDevSerial::Service() {
// The base class may perform some diagnostic reporting common to all streams.
bool ok = USBDevStream::Service();
if (ok) {
// Handle OUT traffic first to try to create more buffer space.
ok = ServiceOUT();
if (ok) {
ok = ServiceIN();
}
}
return ok;
}