1/**
2 * \file
3 * \brief Serial port driver.
4 */
5
6/*
7 * Copyright (c) 2007, 2008, 2012, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, CAB F.78, Universitaetstr. 6, CH-8092 Zurich,
13 * Attn: Systems Group.
14 */
15
16#include <stdlib.h>
17#include <stdio.h>
18#include <string.h>
19#include <barrelfish/barrelfish.h>
20#include <barrelfish/waitset.h>
21#include "serial.h"
22#include "serial_debug.h"
23
24static char *driver_name = "serial0";       // default driver name
25
26static struct serial_buffer buffer;
27
28static serial_input_fn_t *consumer_serial_input = NULL;
29
30void serial_input(char *data, size_t length)
31{
32    if (consumer_serial_input != NULL) {
33        // There is a consumer (client) attached to either the basic service
34        // interface or the terminal service interface. Direct input directly
35        // to service.
36        consumer_serial_input(data, length);
37    } else {
38        // No consumer (client) attached. Buffer input.
39        if (buffer.buf == NULL) {
40            // Allocate a new buffer.
41            buffer.buf = (char *) malloc(length);
42            assert(buffer.buf != NULL);
43            memcpy(buffer.buf, data, length);
44            buffer.len = length;
45        } else {
46            // Append new data to existing buffer.
47            buffer.buf = realloc(buffer.buf, buffer.len + length);
48            assert(buffer.buf != NULL);
49            memcpy(buffer.buf + buffer.len, data, length);
50            buffer.len += length;
51        }
52    }
53}
54
55void set_new_input_consumer(serial_input_fn_t fn)
56{
57    SERIAL_DEBUG("New input consumer set.\n");
58    consumer_serial_input = fn;
59
60    // Send previously buffered input to newly attached consumer.
61    if (buffer.buf != NULL) {
62        SERIAL_DEBUG("Previously buffered input sent to newly attached "
63                     "consumer.\n");
64        consumer_serial_input(buffer.buf, buffer.len);
65        free(buffer.buf);
66        buffer.buf = NULL;
67    }
68}
69
70void start_service(void)
71{
72    SERIAL_DEBUG("Starting services.\n");
73    start_terminal_service(driver_name);
74    start_basic_service(driver_name);
75}
76
77int main(int argc, char *argv[])
78{
79    errval_t err;
80
81    struct serial_params params = {
82        .portbase       = SERIAL_PORTBASE_INVALID,
83        .irq            = SERIAL_IRQ_INVALID,
84        .membase        = SERIAL_MEMBASE_INVALID,
85    };
86
87    // Parse args
88    for (int i = 1; i < argc; i++) {
89        if (strncmp(argv[i], "portbase=", sizeof("portbase=") - 1) == 0) {
90            uint32_t x=
91                strtoul(argv[i] + sizeof("portbase=") - 1, NULL, 0);
92            if (x == 0 || x > 65535) {
93                fprintf(stderr, "Error: invalid portbase 0x%"PRIx32"\n", x);
94                goto usage;
95            }
96            params.portbase = x;
97        } else if (strncmp(argv[i], "irq=", sizeof("irq=") - 1) == 0) {
98             uint32_t x=
99                 strtoul(argv[i] + sizeof("irq=") - 1, NULL, 0);
100             if (x == 0) {
101                fprintf(stderr, "Error: invalid IRQ %"PRIu32"\n", x);
102                goto usage;
103            }
104            params.irq = x;
105        } else if (strncmp(argv[i], "membase=", sizeof("membase=") - 1) == 0) {
106            uint64_t x=
107                strtoull(argv[i] + sizeof("membase=") - 1, NULL, 0);
108            params.membase = x;
109        } else if (strncmp(argv[i], "name=", sizeof("name=") - 1) == 0) {
110             driver_name = argv[i] + sizeof("name=") - 1;
111        } else if (strncmp(argv[i], "auto", 4) == 0) {
112            // do nothing, means we are being started through kaluga
113        } else if (strncmp(argv[i], "int_model=", sizeof("int_model=") - 1) == 0) {
114            // ignore. x86 just assumes that legacy interrupts are used.
115        } else {
116            fprintf(stderr, "Error: unknown option %s\n", argv[i]);
117            goto usage;
118        }
119    }
120
121    // Initialize serial driver
122    err = serial_init(&params);
123    if (err_is_fail(err)) {
124        USER_PANIC_ERR(err, "Error initializing serial driver.");
125    }
126
127    SERIAL_DEBUG("Serial driver initialized.\n"
128                 "Using driver name %s.\n", driver_name);
129
130    // Stick around waiting for input
131    struct waitset *ws = get_default_waitset();
132    while (1) {
133        err = event_dispatch(ws);
134        if (err_is_fail(err)) {
135            USER_PANIC_ERR(err, "Error dispatching events.");
136        }
137    }
138
139    return EXIT_SUCCESS;
140
141usage:
142    fprintf(stderr, "Usage: %s [portbase=PORT] [irq=IRQ] [name=NAME]\n"
143                    "    [membase=ADDR] [kernel]\n", argv[0]);
144    return 1;
145}
146