Software APIs
usbdev_int.cc
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 #include "usbdev_int.h"
5 
6 #include <cassert>
7 #include <cstdio>
8 
9 #include "usbdev_utils.h"
10 
11 // Stub callback function supplied to libusb.
12 void LIBUSB_CALL USBDevInt::CbStubIN(struct libusb_transfer *xfr) {
13  USBDevInt *self = reinterpret_cast<USBDevInt *>(xfr->user_data);
14  self->CallbackIN(xfr);
15 }
16 
17 void LIBUSB_CALL USBDevInt::CbStubOUT(struct libusb_transfer *xfr) {
18  USBDevInt *self = reinterpret_cast<USBDevInt *>(xfr->user_data);
19  self->CallbackOUT(xfr);
20 }
21 
22 bool USBDevInt::Open(unsigned interface) {
23  int rc = dev_->ClaimInterface(interface);
24  if (rc < 0) {
25  return dev_->ErrorUSB("ERROR: Claiming interface", rc);
26  }
27 
28  // Retain the interface number.
29  interface_ = interface;
30 
31  // Remember the (assumed) endpoints which we're using.
32  epOut_ = interface + 1U;
33  epIn_ = 0x80U | epOut_;
34 
35  // No transfers in progress.
36  xfrIn_ = nullptr;
37 
38  xfrOut_ = nullptr;
39 
40  maxPacketSize_ = USBDevice::kDevDataMaxPacketSize;
41 
42  return true;
43 }
44 
46  SetClosing(true);
47 
48  int rc = dev_->ReleaseInterface(interface_);
49  if (rc < 0) {
50  std::cerr << "" << std::endl;
51  }
52 }
53 
55  SetClosing(true);
56 
57  if (verbose_) {
58  std::cout << PrefixID() << "waiting to close" << std::endl;
59  }
60  while (inActive_ || outActive_) {
61  dev_->Service();
62  }
63  if (verbose_) {
64  std::cout << PrefixID() << " closed" << std::endl;
65  }
66 
67  int rc = dev_->ReleaseInterface(interface_);
68  if (rc < 0) {
69  std::cerr << "" << std::endl;
70  }
71 }
72 
74  SetClosing(false);
75 
76  int rc = dev_->ClaimInterface(interface_);
77  if (rc < 0) {
78  return dev_->ErrorUSB("ERROR: Claiming interface", rc);
79  }
80  return true;
81 }
82 
83 // Return a summary report of the stream settings of status.
84 std::string USBDevInt::Report(bool status, bool verbose) const { return ""; }
85 
86 void USBDevInt::DumpIntTransfer(struct libusb_transfer *xfr) const {
87  const void *buf = reinterpret_cast<void *>(xfr->buffer);
88  std::cout << "Buffer " << buf << " length " << xfr->length
89  << " => actual length " << xfr->actual_length << std::endl;
90  buffer_dump(stdout, xfr->buffer, xfr->actual_length);
91 }
92 
93 // Retrieving of IN traffic from device.
94 bool USBDevInt::ServiceIN() {
95  // Ensure that we have enough space available for a full packet; the device
96  // software decides upon the length of each packet.
97  uint8_t *space;
98  bool ok = ProvisionSpace(&space, maxPacketSize_);
99  if (ok) {
100  uint32_t to_fetch = maxPacketSize_;
101  if (to_fetch > transfer_bytes_ - bytes_recvd_) {
102  to_fetch = transfer_bytes_ - bytes_recvd_;
103  }
104 
105  if (!xfrIn_) {
106  xfrIn_ = dev_->AllocTransfer(0U);
107  if (!xfrIn_) {
108  return false;
109  }
110  }
111 
112  if (bulk_) {
113  dev_->FillBulkTransfer(xfrIn_, epIn_, space, to_fetch, CbStubIN, this,
114  kDataTimeout);
115  } else {
116  dev_->FillIntTransfer(xfrIn_, epIn_, space, to_fetch, CbStubIN, this,
117  kDataTimeout);
118  }
119 
120  int rc = dev_->SubmitTransfer(xfrIn_);
121  if (rc < 0) {
122  return dev_->ErrorUSB("ERROR: Submitting IN transfer", rc);
123  }
124  inActive_ = true;
125  } else {
126  inActive_ = false;
127  }
128 
129  return true;
130 }
131 
132 // Sending of OUT traffic to device.
133 bool USBDevInt::ServiceOUT() {
134  // Do we have any data ready to send?
135  uint8_t *data;
136  uint32_t num_bytes = DataAvailable(&data);
137  if (num_bytes > 0U) {
138  // Supply details of the single OUT packet.
139  if (!xfrOut_) {
140  xfrOut_ = dev_->AllocTransfer(0U);
141  if (!xfrOut_) {
142  // Stream is not operational.
143  return false;
144  }
145  }
146 
147  if (bulk_) {
148  dev_->FillBulkTransfer(xfrOut_, epOut_, data, num_bytes, CbStubOUT, this,
149  kDataTimeout);
150  } else {
151  dev_->FillIntTransfer(xfrOut_, epOut_, data, num_bytes, CbStubOUT, this,
152  kDataTimeout);
153  }
154 
155  int rc = dev_->SubmitTransfer(xfrOut_);
156  if (rc < 0) {
157  return dev_->ErrorUSB("ERROR: Submitting OUT transfer", rc);
158  }
159  outActive_ = true;
160  } else {
161  // Nothing to propagate at this time.
162  outActive_ = false;
163  }
164  // Stream remains operational, even if it presently has no work on the OUT
165  // side.
166  return true;
167 }
168 
170  if (failed_) {
171  return false;
172  }
173  // (Re)start IN traffic if not already in progress.
174  if (!inActive_ && !ServiceIN()) {
175  return false;
176  }
177  // (Re)start OUT traffic if not already in progress and there is
178  // data available to be transmitted.
179  if (!outActive_ && !ServiceOUT()) {
180  return false;
181  }
182  return true;
183 }
184 
185 // Callback function supplied to libusb for IN transfers.
186 void USBDevInt::CallbackIN(struct libusb_transfer *xfr) {
187  if (xfr->status != LIBUSB_TRANSFER_COMPLETED) {
188  std::cerr << PrefixID() << " Invalid/unexpected IN transfer status "
189  << xfr->status << std::endl;
190  std::cerr << "length " << xfr->length << " actual " << xfr->actual_length
191  << std::endl;
192  failed_ = true;
193  return;
194  }
195 
196  if (verbose_) {
197  std::cout << PrefixID() << "CallbackIN xfr " << xfr << std::endl;
198  DumpIntTransfer(xfr);
199  }
200 
201  // Collect and parse signature bytes at the start of the IN stream.
202  uint8_t *dp = xfr->buffer;
203  int nrecvd = xfr->actual_length;
204 
205  // Update the circular buffer with the amount of data that we've received
206  CommitData(nrecvd);
207 
208  if (!SigReceived()) {
209  if (nrecvd > 0 && !SigReceived()) {
210  uint32_t dropped = SigDetect(&sig_, dp, (uint32_t)nrecvd);
211 
212  // Consume stream signature, rather than propagating it to the output
213  // side.
214  if (SigReceived()) {
215  SigProcess(sig_);
216  dropped += sizeof(usbdev_stream_sig_t);
217  }
218 
219  // Skip past any dropped bytes, including the signature, so that if there
220  // are additional bytes we may process them.
221  nrecvd = ((uint32_t)nrecvd > dropped) ? ((uint32_t)nrecvd - dropped) : 0;
222  dp += dropped;
223 
224  if (dropped) {
225  DiscardData(dropped);
226  }
227  }
228  }
229 
230  bool ok = true;
231  if (nrecvd > 0) {
232  // Check the received LFSR-generated byte(s) and combine them with the
233  // output of our host-side LFSR.
234  ok = ProcessData(dp, nrecvd);
235  }
236 
237  if (ok) {
238  if (CanSchedule()) {
239  // Attempt to set up another IN transfer.
240  failed_ = !ServiceIN();
241  } else {
242  inActive_ = false;
243  }
244  } else {
245  failed_ = true;
246  }
247 }
248 
249 // Callback function supplied to libusb for OUT transfers.
250 void USBDevInt::CallbackOUT(struct libusb_transfer *xfr) {
251  if (xfr->status != LIBUSB_TRANSFER_COMPLETED) {
252  std::cerr << PrefixID() << " Invalid/unexpected OUT transfer status "
253  << xfr->status << std::endl;
254  std::cerr << "length " << xfr->length << " actual " << xfr->actual_length
255  << std::endl;
256  failed_ = true;
257  return;
258  }
259 
260  if (verbose_) {
261  std::cout << PrefixID() << "CallbackOUT xfr " << xfr << std::endl;
262  DumpIntTransfer(xfr);
263  }
264 
265  // Note: we're not expecting any truncation on OUT transfers.
266  assert(xfr->actual_length == xfr->length);
267  ConsumeData(xfr->actual_length);
268 
269  if (CanSchedule()) {
270  // Attempt to set up another OUT transfer.
271  failed_ = !ServiceOUT();
272  } else {
273  outActive_ = false;
274  }
275 }