• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_sys_io/sys_io.h"
16 
17 #include <stdio.h>
18 
19 #include <cinttypes>
20 
21 #include "pico/stdlib.h"
22 #include "pw_status/status.h"
23 #include "pw_sync/thread_notification.h"
24 
25 namespace {
26 
27 pw::sync::ThreadNotification chars_available_signal;
28 
chars_available_callback(void * arg)29 void chars_available_callback([[maybe_unused]] void* arg) {
30   chars_available_signal.release();
31 }
32 
LazyInitSysIo()33 void LazyInitSysIo() {
34   static bool initialized = false;
35   if (!initialized) {
36     stdio_init_all();
37     stdio_set_chars_available_callback(chars_available_callback, nullptr);
38     initialized = true;
39   }
40 }
41 
42 // Spin until host connects.
WaitForConnect()43 void WaitForConnect() {
44   // In order to stop this sleep polling, we could register a shared IRQ handler
45   // for the USB interrupt and block on a signal from that.
46   while (!stdio_usb_connected()) {
47     sleep_ms(50);
48   }
49 }
50 
51 }  // namespace
52 
53 // This whole implementation is very inefficient because it only reads / writes
54 // 1 byte at a time. It also does lazy initialization checks with every byte.
55 namespace pw::sys_io {
56 
ReadByte(std::byte * dest)57 Status ReadByte(std::byte* dest) {
58   LazyInitSysIo();
59   WaitForConnect();
60 
61   while (true) {
62     int c = getchar_timeout_us(0);
63     if (c != PICO_ERROR_TIMEOUT) {
64       *dest = static_cast<std::byte>(c);
65       return OkStatus();
66     }
67 
68     // Wait for signal from the chars_available_callback().
69     chars_available_signal.acquire();
70   }
71 }
72 
TryReadByte(std::byte * dest)73 Status TryReadByte(std::byte* dest) {
74   LazyInitSysIo();
75   int c = getchar_timeout_us(0);
76   if (c == PICO_ERROR_TIMEOUT) {
77     return Status::DeadlineExceeded();
78   }
79   *dest = static_cast<std::byte>(c);
80   return OkStatus();
81 }
82 
WriteByte(std::byte b)83 Status WriteByte(std::byte b) {
84   // The return value of this is just the character sent.
85   LazyInitSysIo();
86   putchar_raw(static_cast<int>(b));
87   return OkStatus();
88 }
89 
90 // Writes a string using pw::sys_io, and add newline characters at the end.
WriteLine(std::string_view s)91 StatusWithSize WriteLine(std::string_view s) {
92   size_t chars_written = 0;
93   StatusWithSize result = WriteBytes(as_bytes(span(s)));
94   if (!result.ok()) {
95     return result;
96   }
97   chars_written += result.size();
98 
99   // Write trailing newline.
100   result = WriteBytes(as_bytes(span("\r\n", 2)));
101   chars_written += result.size();
102 
103   return StatusWithSize(OkStatus(), chars_written);
104 }
105 
106 }  // namespace pw::sys_io
107