| // 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; |
| } |