1/**
2 * \brief Implements the handler function for a driver domain
3 * to act upon requests from a device manager (i.e., Kaluga).
4 *
5 * The stubs will create, destroy driver instances using functions
6 * mainly found in `modules.c` of this library.
7 */
8
9/*
10 * Copyright (c) 2016, ETH Zurich.
11 * All rights reserved.
12 *
13 * This file is distributed under the terms in the attached LICENSE file.
14 * If you do not find this file, copies can be found by writing to:
15 * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
16 */
17#include <stdlib.h>
18#include <stdio.h>
19#include <string.h>
20#include <assert.h>
21
22#include <barrelfish/barrelfish.h>
23#include <barrelfish/nameservice_client.h>
24
25#include <driverkit/driverkit.h>
26
27#include <if/ddomain_defs.h>
28#include "debug.h"
29
30/**
31 * State for connection.
32 */
33static struct bind_state {
34    struct ddomain_binding* binding;
35    bool is_done;
36    errval_t err;
37} rpc_bind;
38
39/**
40 * Check if the argument is non-null and of non zero length
41 */
42static bool arg_valid(const char * a){
43    return a != NULL && strlen(a) > 0;
44}
45
46/**
47 * Act upon request to create a driver instance.
48 *
49 * \param binding Controller binding
50 * \param cls     What class to instantiate?
51 * \param cls_len Ignored.
52 * \param name    What name the driver instance should have.
53 * \param nlen    Ignored.
54 * \param cap     Capabilities for the driver instance.
55 * \param flags   Flags for the driver instance.
56 */
57static void create_handler(struct ddomain_binding* binding, const char* cls, size_t cls_len,
58                           const char* name, size_t nlen,
59                           const char* a1, size_t a1len, const char* a2, size_t a2len,
60                           const char* a3, size_t a3len, const char* a4, size_t a4len,
61                           struct capref cap1, struct capref cap2, struct capref cap3,
62                           struct capref cap4, struct capref cap5,  struct capref cap6,
63                           uint64_t flags) {
64    errval_t err;
65    DRIVERKIT_DEBUG("Driver domain got create message from kaluga for cls=%s,"
66            "name=%s\n", cls, name);
67
68    iref_t dev = 0, ctrl = 0;
69
70    static size_t NR_CAPS  = 6;
71    static size_t NR_ARGS = 4;
72
73    // This array is owned by the driver after create:
74    struct capref* cap_array = calloc(sizeof(struct capref), NR_CAPS);
75    cap_array[0] = cap1;
76    cap_array[1] = cap2;
77    cap_array[2] = cap3;
78    cap_array[3] = cap4;
79    cap_array[4] = cap5;
80    cap_array[5] = cap6;
81
82    struct capref cnodecap;
83    err = slot_alloc_root(&cnodecap);
84    assert(err_is_ok(err));
85    err = cap_copy(cnodecap, cap_array[0]);
86    struct capref cap0_0 = {
87            .slot = 0,
88            .cnode = build_cnoderef(cnodecap, CNODE_TYPE_OTHER)
89    };
90    char debug_msg[100];
91    debug_print_cap_at_capref(debug_msg, sizeof(debug_msg), cap0_0);
92    DRIVERKIT_DEBUG("Received cap0_0=%s\n", debug_msg);
93
94    char** args_array = calloc(sizeof(char*), 4);
95    args_array[0] = arg_valid(a1) ? strdup(a1) : NULL;
96    args_array[1] = arg_valid(a2) ? strdup(a2) : NULL;
97    args_array[2] = arg_valid(a3) ? strdup(a3) : NULL;
98    args_array[3] = arg_valid(a4) ? strdup(a4) : NULL;
99
100    int args_len;
101    for(args_len=0; args_len<NR_ARGS; args_len++) {
102        if(args_array[args_len] == NULL) break;
103    }
104
105    DRIVERKIT_DEBUG("Instantiate driver\n");
106    err = driverkit_create_driver(cls, name, cap_array, NR_CAPS, args_array, args_len, flags, &dev, &ctrl);
107    if (err_is_fail(err)) {
108        DEBUG_ERR(err, "Instantiating driver failed, report this back to Kaluga."
109                "name=%s, cls=%s\n", name, cls);
110    }
111
112    DRIVERKIT_DEBUG("sending create response to kaluga\n");
113    err = ddomain_create_response__tx(binding, NOP_CONT, dev, ctrl, err);
114    if (err_is_fail(err)) {
115        USER_PANIC_ERR(err, "Sending reply failed.\n");
116    }
117}
118
119/**
120 * Destroy an existing driver instance.
121 *
122 * \param binding Controller binding.
123 * \param name    Name of the driver instance.
124 * \param len     Ignored
125 */
126static void destroy_handler(struct ddomain_binding* binding, const char* name, size_t len) {
127    DRIVERKIT_DEBUG("Driver domain got destroy message for instance %s\n", name);
128    errval_t err = driverkit_destroy(name);
129    if (err_is_fail(err)) {
130        DEBUG_ERR(err, "Destroying driver failed, report this back to Kaluga.");
131    }
132
133    err = binding->tx_vtbl.destroy_response(binding, NOP_CONT, err);
134    if (err_is_fail(err)) {
135        USER_PANIC_ERR(err, "Sending reply failed.");
136    }
137}
138
139/**
140 * Stubs table for functions to call on driver instance.
141 */
142static const struct ddomain_rx_vtbl rpc_rx_vtbl = {
143    .create_call = create_handler,
144    .destroy_call = destroy_handler,
145};
146
147/**
148 * Called if connection to the manager has completed.
149 *
150 * \param st  NULL
151 * \param err Connection initiated successfully?
152 * \param b   Created binding, this is stored in rpc_bind.
153 */
154static void rpc_bind_cb(void *st, errval_t err, struct ddomain_binding *b)
155{
156    b->st = NULL;
157    if (err_is_fail(err)) {
158        DEBUG_ERR(err, "oct_event bind failed");
159        goto out;
160    }
161
162    DRIVERKIT_DEBUG("Driver domain has connected to ddomain controller service.\n");
163    rpc_bind.binding = b;
164    rpc_bind.binding->rx_vtbl = rpc_rx_vtbl;
165
166out:
167    assert(!rpc_bind.is_done);
168    rpc_bind.is_done = true;
169    rpc_bind.err = err;
170}
171
172/**
173 * Connects to the driver domain manager.
174 *
175 * \param  connect_to iref where to connect.
176 * \retval SYS_ERR_OK Connected to the driver manager.
177 */
178errval_t ddomain_communication_init(iref_t connect_to, uint64_t ident)
179{
180    rpc_bind.err = SYS_ERR_OK;
181    rpc_bind.is_done = false;
182
183    errval_t err = ddomain_bind(connect_to, rpc_bind_cb, NULL, get_default_waitset(), IDC_BIND_FLAGS_DEFAULT);
184    if (err_is_fail(err)) {
185        return err;
186    }
187    DRIVERKIT_DEBUG("%s:%s:%d: Trying to connect to kaluga...\n", __FILE__, __FUNCTION__, __LINE__);
188    // XXX: broken
189    while (!rpc_bind.is_done) {
190        messages_wait_and_handle_next();
191    }
192
193    DRIVERKIT_DEBUG("%s:%s:%d: Send identify %"PRIu64"\n", __FILE__, __FUNCTION__, __LINE__, ident);
194    errval_t send_err = rpc_bind.binding->tx_vtbl.identify(rpc_bind.binding, NOP_CONT, ident);
195    assert(err_is_ok(send_err));
196
197    return rpc_bind.err;
198}
199