paul@272 | 1 | /* |
paul@272 | 2 | * A dataspace server exposing regions of a MMC/SD card. |
paul@272 | 3 | * |
paul@272 | 4 | * Copyright (C) 2020, 2021, 2023, 2024 Paul Boddie <paul@boddie.org.uk> |
paul@272 | 5 | * |
paul@272 | 6 | * This program is free software; you can redistribute it and/or |
paul@272 | 7 | * modify it under the terms of the GNU General Public License as |
paul@272 | 8 | * published by the Free Software Foundation; either version 2 of |
paul@272 | 9 | * the License, or (at your option) any later version. |
paul@272 | 10 | * |
paul@272 | 11 | * This program is distributed in the hope that it will be useful, |
paul@272 | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
paul@272 | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
paul@272 | 14 | * GNU General Public License for more details. |
paul@272 | 15 | * |
paul@272 | 16 | * You should have received a copy of the GNU General Public License |
paul@272 | 17 | * along with this program; if not, write to the Free Software |
paul@272 | 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, |
paul@272 | 19 | * Boston, MA 02110-1301, USA |
paul@272 | 20 | */ |
paul@272 | 21 | |
paul@272 | 22 | #include <l4/devices/cpm-x1600.h> |
paul@272 | 23 | #include <l4/devices/dma.h> |
paul@272 | 24 | #include <l4/devices/dma-x1600.h> |
paul@272 | 25 | #include <l4/devices/memory.h> |
paul@272 | 26 | #include <l4/devices/msc-x1600.h> |
paul@272 | 27 | |
paul@272 | 28 | #include <l4/re/c/util/cap_alloc.h> |
paul@272 | 29 | #include <l4/sys/debugger.h> |
paul@272 | 30 | #include <l4/sys/err.h> |
paul@272 | 31 | #include <l4/sys/factory.h> |
paul@272 | 32 | #include <l4/sys/icu.h> |
paul@272 | 33 | #include <l4/sys/ipc.h> |
paul@272 | 34 | #include <l4/sys/irq.h> |
paul@272 | 35 | |
paul@272 | 36 | #include <ipc/thread.h> |
paul@272 | 37 | |
paul@272 | 38 | #include <stdio.h> |
paul@272 | 39 | #include <stdlib.h> |
paul@272 | 40 | |
paul@272 | 41 | #include <mem/memory_incremental.h> |
paul@272 | 42 | #include <fsserver/page_queue_shared.h> |
paul@272 | 43 | #include <fsserver/pages.h> |
paul@272 | 44 | #include <resource/resource_server.h> |
paul@272 | 45 | #include <systypes/env.h> |
paul@272 | 46 | |
paul@272 | 47 | #include "msc_region_opener.h" |
paul@272 | 48 | #include "msc_region_operations.h" |
paul@272 | 49 | |
paul@272 | 50 | |
paul@272 | 51 | |
paul@272 | 52 | /* Common configuration. */ |
paul@272 | 53 | |
paul@272 | 54 | static l4_cap_idx_t icucap; |
paul@272 | 55 | |
paul@272 | 56 | |
paul@272 | 57 | |
paul@272 | 58 | /* Device and resource discovery. */ |
paul@272 | 59 | |
paul@272 | 60 | static long item_in_range(long start, long end, long index) |
paul@272 | 61 | { |
paul@272 | 62 | if (start < end) |
paul@272 | 63 | return start + index; |
paul@272 | 64 | else if (start > end) |
paul@272 | 65 | return start - index; |
paul@272 | 66 | else |
paul@272 | 67 | return start; |
paul@272 | 68 | } |
paul@272 | 69 | |
paul@272 | 70 | |
paul@272 | 71 | |
paul@272 | 72 | /* Common functions. */ |
paul@272 | 73 | |
paul@272 | 74 | static int init_irq(int num, l4_cap_idx_t irq, l4_uint32_t start, l4_uint32_t end) |
paul@272 | 75 | { |
paul@272 | 76 | /* Create interrupt object. */ |
paul@272 | 77 | |
paul@272 | 78 | long err = l4_error(l4_factory_create_irq(l4re_global_env->factory, irq)); |
paul@272 | 79 | |
paul@272 | 80 | if (err) |
paul@272 | 81 | { |
paul@272 | 82 | printf("Could not create IRQ object: %ld\n", err); |
paul@272 | 83 | return 1; |
paul@272 | 84 | } |
paul@272 | 85 | |
paul@272 | 86 | /* Bind interrupt objects to IRQ numbers. */ |
paul@272 | 87 | |
paul@272 | 88 | err = l4_error(l4_icu_bind(icucap, |
paul@272 | 89 | item_in_range(start, end, num), |
paul@272 | 90 | irq)); |
paul@272 | 91 | |
paul@272 | 92 | if (err) |
paul@272 | 93 | { |
paul@272 | 94 | printf("Could not bind IRQ to the ICU: %ld\n", err); |
paul@272 | 95 | return 1; |
paul@272 | 96 | } |
paul@272 | 97 | |
paul@272 | 98 | return 0; |
paul@272 | 99 | } |
paul@272 | 100 | |
paul@272 | 101 | |
paul@272 | 102 | |
paul@272 | 103 | /* Peripheral resources. */ |
paul@272 | 104 | |
paul@272 | 105 | static Cpm_x1600_chip *cpm; |
paul@272 | 106 | static Dma_x1600_chip *dma; |
paul@272 | 107 | static Msc_x1600_chip *msc; |
paul@272 | 108 | |
paul@272 | 109 | /* Obtain an abstraction for the memory card. */ |
paul@272 | 110 | |
paul@272 | 111 | static MscRegionOperations *get_msc_region_operations(int msc_channel_num, |
paul@272 | 112 | int dma_channel_num, |
paul@272 | 113 | int card) |
paul@272 | 114 | { |
paul@272 | 115 | l4_addr_t cpm_base = 0, cpm_base_end = 0; |
paul@272 | 116 | l4_addr_t dma_base = 0, dma_base_end = 0; |
paul@272 | 117 | l4_addr_t msc_base = 0, msc_base_end = 0; |
paul@272 | 118 | l4_addr_t msc_phys_base = 0, msc_phys_base_end = 0; |
paul@272 | 119 | l4_uint32_t dma_irq_start = 0, dma_irq_end = 0; |
paul@272 | 120 | l4_uint32_t msc_irq_start = 0, msc_irq_end = 0; |
paul@272 | 121 | |
paul@272 | 122 | icucap = l4re_env_get_cap("icu"); |
paul@272 | 123 | |
paul@272 | 124 | /* Obtain resource details describing I/O memory. */ |
paul@272 | 125 | |
paul@272 | 126 | if (get_memory("x1600-cpm", &cpm_base, &cpm_base_end) < 0) |
paul@272 | 127 | return NULL; |
paul@272 | 128 | |
paul@272 | 129 | cpm = new Cpm_x1600_chip(cpm_base); |
paul@272 | 130 | |
paul@272 | 131 | if (get_memory("x1600-dma", &dma_base, &dma_base_end) < 0) |
paul@272 | 132 | return NULL; |
paul@272 | 133 | |
paul@272 | 134 | dma = new Dma_x1600_chip(dma_base, dma_base_end, cpm); |
paul@272 | 135 | |
paul@272 | 136 | if (get_irq("x1600-dma", &dma_irq_start, &dma_irq_end) < 0) |
paul@272 | 137 | return NULL; |
paul@272 | 138 | |
paul@272 | 139 | l4_cap_idx_t dma_irq = l4re_util_cap_alloc(); |
paul@272 | 140 | |
paul@272 | 141 | if (init_irq(0, dma_irq, dma_irq_start, dma_irq_end)) |
paul@272 | 142 | return NULL; |
paul@272 | 143 | |
paul@272 | 144 | dma->enable(); |
paul@272 | 145 | |
paul@272 | 146 | if (get_memory_complete("x1600-msc", &msc_base, &msc_base_end, |
paul@272 | 147 | &msc_phys_base, &msc_phys_base_end) < 0) |
paul@272 | 148 | return NULL; |
paul@272 | 149 | |
paul@272 | 150 | msc = new Msc_x1600_chip(msc_phys_base, msc_base, msc_base_end, cpm); |
paul@272 | 151 | |
paul@272 | 152 | if (get_irq("x1600-msc", &msc_irq_start, &msc_irq_end) < 0) |
paul@272 | 153 | return NULL; |
paul@272 | 154 | |
paul@272 | 155 | l4_cap_idx_t msc_irq = l4re_util_cap_alloc(); |
paul@272 | 156 | |
paul@272 | 157 | if (init_irq(msc_channel_num, msc_irq, msc_irq_start, msc_irq_end)) |
paul@272 | 158 | return NULL; |
paul@272 | 159 | |
paul@272 | 160 | Dma_x1600_channel *dma_channel = dma->get_channel(dma_channel_num, dma_irq); |
paul@272 | 161 | Msc_channel *msc_channel = msc->get_channel(msc_channel_num, msc_irq, dma_channel); |
paul@272 | 162 | |
paul@272 | 163 | msc_channel->enable(); |
paul@272 | 164 | |
paul@272 | 165 | if (card >= msc_channel->num_cards()) |
paul@272 | 166 | return NULL; |
paul@272 | 167 | |
paul@272 | 168 | struct dma_region region; |
paul@272 | 169 | |
paul@272 | 170 | if (get_dma_region(512, 12, ®ion)) |
paul@272 | 171 | return NULL; |
paul@272 | 172 | |
paul@272 | 173 | return new MscRegionOperations(msc_channel, card, region); |
paul@272 | 174 | } |
paul@272 | 175 | |
paul@272 | 176 | |
paul@272 | 177 | |
paul@272 | 178 | /* Default number of pages for files. */ |
paul@272 | 179 | |
paul@272 | 180 | const unsigned int MEMORY_PAGES = 20; |
paul@272 | 181 | |
paul@272 | 182 | |
paul@272 | 183 | |
paul@272 | 184 | /* Server program. */ |
paul@272 | 185 | |
paul@272 | 186 | int main(int argc, char *argv[]) |
paul@272 | 187 | { |
paul@272 | 188 | l4_debugger_set_object_name(l4re_env()->main_thread, "block_server"); |
paul@272 | 189 | long err; |
paul@272 | 190 | |
paul@272 | 191 | /* Introduce concurrency control. */ |
paul@272 | 192 | |
paul@272 | 193 | err = ipc_thread_init(); |
paul@272 | 194 | |
paul@272 | 195 | if (err) |
paul@272 | 196 | { |
paul@272 | 197 | printf("Initialisation error: %s\n", l4sys_errtostr(err)); |
paul@272 | 198 | return 1; |
paul@272 | 199 | } |
paul@272 | 200 | |
paul@272 | 201 | if (argc < 4) |
paul@272 | 202 | { |
paul@272 | 203 | printf("Need a MSC channel/peripheral number, a DMA channel number, " \ |
paul@272 | 204 | "and a card number.\n\n" \ |
paul@272 | 205 | "A number of memory pages can be indicated for the use of the " \ |
paul@272 | 206 | "server.\n\n" \ |
paul@272 | 207 | "A named capability from the environment can be specified.\n"); |
paul@272 | 208 | return 1; |
paul@272 | 209 | } |
paul@272 | 210 | |
paul@272 | 211 | int msc_channel_num = atoi(argv[1]); |
paul@272 | 212 | int dma_channel_num = atoi(argv[2]); |
paul@272 | 213 | int card = atoi(argv[3]); |
paul@272 | 214 | |
paul@272 | 215 | unsigned int memory_pages = MEMORY_PAGES; |
paul@272 | 216 | |
paul@272 | 217 | if (argc > 4) |
paul@272 | 218 | memory_pages = atoi(argv[4]); |
paul@272 | 219 | |
paul@272 | 220 | const char *server_name = (argc > 5) ? argv[5] : ENV_FILESYSTEM_SERVER_NAME; |
paul@272 | 221 | |
paul@272 | 222 | /* Obtain a DMA space for associating allocated memory with physical |
paul@272 | 223 | addresses. */ |
paul@272 | 224 | |
paul@272 | 225 | l4_cap_idx_t dma_space; |
paul@272 | 226 | |
paul@272 | 227 | err = get_dma_space(&dma_space); |
paul@272 | 228 | |
paul@272 | 229 | if (err) |
paul@272 | 230 | { |
paul@272 | 231 | printf("Could not obtain DMA space: %s\n", l4sys_errtostr(err)); |
paul@272 | 232 | return 1; |
paul@272 | 233 | } |
paul@272 | 234 | |
paul@272 | 235 | MscRegionOperations *ops = get_msc_region_operations(msc_channel_num, |
paul@272 | 236 | dma_channel_num, |
paul@272 | 237 | card); |
paul@272 | 238 | |
paul@272 | 239 | if (ops == NULL) |
paul@272 | 240 | { |
paul@272 | 241 | printf("Could not access memory card peripheral.\n"); |
paul@272 | 242 | return 1; |
paul@272 | 243 | } |
paul@272 | 244 | |
paul@272 | 245 | /* Some memory plus infrastructure. */ |
paul@272 | 246 | |
paul@272 | 247 | MemoryIncremental mem(memory_pages, PAGE_SIZE, dma_space); |
paul@272 | 248 | PageQueueShared queue; |
paul@272 | 249 | Pages pages(&mem, &queue); |
paul@272 | 250 | ResourceRegistry registry(&pages); |
paul@272 | 251 | MscRegionOpener opener(®istry, ops); |
paul@272 | 252 | |
paul@272 | 253 | /* Register a server associating it with the given object. */ |
paul@272 | 254 | |
paul@272 | 255 | ResourceServer server(&opener); |
paul@272 | 256 | server.bind(server_name); |
paul@272 | 257 | |
paul@272 | 258 | printf("Starting server using %d pages...\n", memory_pages); |
paul@272 | 259 | server.start(); |
paul@272 | 260 | |
paul@272 | 261 | return 0; |
paul@272 | 262 | } |
paul@272 | 263 | |
paul@272 | 264 | // vim: tabstop=2 expandtab shiftwidth=2 |