1/*
2 * Copyright (c) 2014, ETH Zurich. All rights reserved.
3 *
4 * This file is distributed under the terms in the attached LICENSE file.
5 * If you do not find this file, copies can be found by writing to:
6 * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
7 */
8/*
9 *
10 *
11 */
12#include <stdio.h>
13#include <string.h>
14
15#include <barrelfish/barrelfish.h>
16
17#include <dma/dma_manager_client.h>
18
19#include "dma_mgr.h"
20#include "debug.h"
21
22struct dma_service
23{
24    struct dma_mgr_driver_info info;
25    struct dma_service *prev;
26    struct dma_service *next;
27};
28
29static struct dma_service *dma_services = NULL;
30
31static void service_insert(struct dma_service *svc)
32{
33    if (dma_services == NULL) {
34        svc->next = NULL;
35        svc->prev = NULL;
36        dma_services = svc;
37        return;
38    }
39
40    struct dma_service *current = dma_services;
41    while (current) {
42        if (current->info.mem_low > svc->info.mem_low) {
43            if (current->prev) {
44                svc->prev = current->prev;
45                current->prev->next = svc;
46            } else {
47                svc->prev = NULL;
48                dma_services = svc;
49            }
50            svc->next = current;
51            current->prev = svc;
52            break;
53        }
54        if (current->next == NULL) {
55            current->next = svc;
56            svc->prev = current;
57            break;
58        }
59        current = current->next;
60    }
61}
62
63static inline uint8_t get_diff(uint8_t a,
64                               uint8_t b)
65{
66    if (a > b) {
67        return a - b;
68    } else {
69        return b - a;
70    }
71}
72
73static struct dma_service *service_lookup(lpaddr_t mem_low,
74                                          size_t size,
75                                          uint8_t numa_node)
76{
77
78    struct dma_service *current = dma_services;
79    struct dma_service *best = NULL;
80    while (current) {
81        if (current->info.mem_low <= mem_low) {
82            if (current->info.mem_high >= (mem_low + size)) {
83                /* we have a match */
84                if (best == NULL) {
85                    best = current;
86                } else {
87                    uint8_t best_numa_diff = get_diff(numa_node,
88                                                      best->info.numa_node);
89                    uint8_t curr_numa_diff = get_diff(numa_node,
90                                                      current->info.numa_node);
91                    if (curr_numa_diff < best_numa_diff) {
92                        best = current;
93                    }
94                }
95            }
96        }
97        current = current->next;
98    }
99    return best;
100}
101
102static struct dma_service *service_lookup_iref(iref_t iref)
103{
104    struct dma_service *current = dma_services;
105    while(current) {
106        if (current->info.iref == iref) {
107            return current;
108        }
109        current = current->next;
110    }
111    return NULL;
112}
113
114/*
115 * ============================================================================
116 *
117 * ============================================================================
118 */
119
120errval_t driver_store_init(void)
121{
122    return SYS_ERR_OK;
123}
124
125errval_t driver_store_insert(lpaddr_t mem_low,
126                             lpaddr_t mem_high,
127                             uint8_t numa_node,
128                             uint8_t type,
129                             iref_t iref)
130{
131    struct dma_service *driver = calloc(1, sizeof(*driver));
132    if (driver == NULL) {
133        return LIB_ERR_MALLOC_FAIL;
134    }
135
136    DS_DEBUG("insert: {%016lx, %016lx, %u, %u} @ %x\n", mem_low, mem_high, numa_node,
137             type, iref);
138
139    driver->info.mem_low = mem_low;
140    driver->info.mem_high = mem_high;
141    driver->info.numa_node = numa_node;
142    driver->info.type = type;
143    driver->info.iref = iref;
144
145    service_insert(driver);
146
147    return SYS_ERR_OK;
148}
149
150errval_t driver_store_lookup_by_iref(iref_t iref,
151                                     struct dma_mgr_driver_info **info)
152{
153    DS_DEBUG("lookup: iref:%"PRIxIREF"\n", iref);
154
155    struct dma_service *svc = service_lookup_iref(iref);
156    if (svc == NULL) {
157        DS_DEBUG("no such service: iref:%"PRIxIREF"\n", iref);
158        return DMA_ERR_SVC_VOID;
159    }
160
161    *info = &svc->info;
162
163    return SYS_ERR_OK;
164}
165
166errval_t driver_store_lookup(lpaddr_t mem_low,
167                             size_t size,
168                             uint8_t numa_node,
169                             struct dma_mgr_driver_info **info)
170{
171
172    DS_DEBUG("lookup: {%016lx, %016lx, %u}\n", mem_low, size, numa_node);
173
174    struct dma_service *svc = service_lookup(mem_low, size, numa_node);
175    if (svc == NULL) {
176        return DMA_ERR_SVC_VOID;
177    }
178
179    DS_DEBUG("lookup: {%016lx, %016lx, %u} @ %x\n", mem_low, size, numa_node,
180             svc->info.iref);
181
182    *info = &svc->info;
183
184    return SYS_ERR_OK;
185}
186