uboot/drivers/serial/serial_semihosting.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
   4 */
   5
   6#include <common.h>
   7#include <dm.h>
   8#include <malloc.h>
   9#include <serial.h>
  10#include <semihosting.h>
  11
  12/**
  13 * struct smh_serial_priv - Semihosting serial private data
  14 * @infd: stdin file descriptor (or error)
  15 * @outfd: stdout file descriptor (or error)
  16 * @counter: Counter used to fake pending every other call
  17 */
  18struct smh_serial_priv {
  19        int infd;
  20        int outfd;
  21        unsigned counter;
  22};
  23
  24#if CONFIG_IS_ENABLED(DM_SERIAL)
  25static int smh_serial_getc(struct udevice *dev)
  26{
  27        char ch = 0;
  28        struct smh_serial_priv *priv = dev_get_priv(dev);
  29
  30        if (priv->infd < 0)
  31                return smh_getc();
  32
  33        smh_read(priv->infd, &ch, sizeof(ch));
  34        return ch;
  35}
  36
  37static int smh_serial_putc(struct udevice *dev, const char ch)
  38{
  39        smh_putc(ch);
  40        return 0;
  41}
  42
  43static ssize_t smh_serial_puts(struct udevice *dev, const char *s, size_t len)
  44{
  45        int ret;
  46        struct smh_serial_priv *priv = dev_get_priv(dev);
  47        unsigned long written;
  48
  49        if (priv->outfd < 0) {
  50                char *buf;
  51
  52                /* Try and avoid a copy if we can */
  53                if (!s[len + 1]) {
  54                        smh_puts(s);
  55                        return len;
  56                }
  57
  58                buf = strndup(s, len);
  59                if (!buf)
  60                        return -ENOMEM;
  61
  62                smh_puts(buf);
  63                free(buf);
  64                return len;
  65        }
  66
  67        ret = smh_write(priv->outfd, s, len, &written);
  68        if (written)
  69                return written;
  70        return ret;
  71}
  72
  73static int smh_serial_pending(struct udevice *dev, bool input)
  74{
  75        struct smh_serial_priv *priv = dev_get_priv(dev);
  76
  77        if (input)
  78                return priv->counter++ & 1;
  79        return false;
  80}
  81
  82static const struct dm_serial_ops smh_serial_ops = {
  83        .putc = smh_serial_putc,
  84        .puts = smh_serial_puts,
  85        .getc = smh_serial_getc,
  86        .pending = smh_serial_pending,
  87};
  88
  89static int smh_serial_bind(struct udevice *dev)
  90{
  91        if (semihosting_enabled())
  92                return 0;
  93        return -ENOENT;
  94}
  95
  96static int smh_serial_probe(struct udevice *dev)
  97{
  98        struct smh_serial_priv *priv = dev_get_priv(dev);
  99
 100        priv->infd = smh_open(":tt", MODE_READ);
 101        priv->outfd = smh_open(":tt", MODE_WRITE);
 102        return 0;
 103}
 104
 105U_BOOT_DRIVER(smh_serial) = {
 106        .name   = "serial_semihosting",
 107        .id     = UCLASS_SERIAL,
 108        .bind   = smh_serial_bind,
 109        .probe  = smh_serial_probe,
 110        .priv_auto = sizeof(struct smh_serial_priv),
 111        .ops    = &smh_serial_ops,
 112        .flags  = DM_FLAG_PRE_RELOC,
 113};
 114
 115U_BOOT_DRVINFO(smh_serial) = {
 116        .name = "serial_semihosting",
 117};
 118#else /* DM_SERIAL */
 119static int infd = -ENODEV;
 120static int outfd = -ENODEV;
 121static unsigned counter = 1;
 122
 123static int smh_serial_start(void)
 124{
 125        infd = smh_open(":tt", MODE_READ);
 126        outfd = smh_open(":tt", MODE_WRITE);
 127        return 0;
 128}
 129
 130static int smh_serial_stop(void)
 131{
 132        if (outfd >= 0)
 133                smh_close(outfd);
 134        return 0;
 135}
 136
 137static void smh_serial_setbrg(void)
 138{
 139}
 140
 141static int smh_serial_getc(void)
 142{
 143        char ch = 0;
 144
 145        if (infd < 0)
 146                return smh_getc();
 147
 148        smh_read(infd, &ch, sizeof(ch));
 149        return ch;
 150}
 151
 152static int smh_serial_tstc(void)
 153{
 154        return counter++ & 1;
 155}
 156
 157static void smh_serial_puts(const char *s)
 158{
 159        ulong unused;
 160
 161        if (outfd < 0)
 162                smh_puts(s);
 163        else
 164                smh_write(outfd, s, strlen(s), &unused);
 165}
 166
 167struct serial_device serial_smh_device = {
 168        .name   = "serial_smh",
 169        .start  = smh_serial_start,
 170        .stop   = smh_serial_stop,
 171        .setbrg = smh_serial_setbrg,
 172        .getc   = smh_serial_getc,
 173        .tstc   = smh_serial_tstc,
 174        .putc   = smh_putc,
 175        .puts   = smh_serial_puts,
 176};
 177
 178void smh_serial_initialize(void)
 179{
 180        if (semihosting_enabled())
 181                serial_register(&serial_smh_device);
 182}
 183
 184__weak struct serial_device *default_serial_console(void)
 185{
 186        return &serial_smh_device;
 187}
 188#endif
 189
 190#ifdef CONFIG_DEBUG_UART_SEMIHOSTING
 191#include <debug_uart.h>
 192
 193static inline void _debug_uart_init(void)
 194{
 195}
 196
 197static inline void _debug_uart_putc(int c)
 198{
 199        smh_putc(c);
 200}
 201
 202DEBUG_UART_FUNCS
 203#endif
 204