source: mainline/uspace/drv/block/ddisk/ddisk.c@ 07c913b

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 07c913b was f2e78b0, checked in by Jakub Jermar <jakub@…>, 11 years ago

Very basic and incomplete ddisk driver.

  • Property mode set to 100644
File size: 10.6 KB
Line 
1/*
2 * Copyright (c) 2015 Jakub Jermar
3 * Copyright (c) 2013 Jiri Svoboda
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @file
31 * MSIM ddisk block device driver
32 */
33
34#include <assert.h>
35#include <stdio.h>
36#include <errno.h>
37#include <str_error.h>
38#include <ddf/driver.h>
39#include <ddf/log.h>
40#include <device/hw_res_parsed.h>
41#include <ddi.h>
42#include <bd_srv.h>
43#include <fibril_synch.h>
44#include <as.h>
45
46#define NAME "ddisk"
47
48#define DDISK_FUN_NAME "a"
49
50#define DDISK_BLOCK_SIZE 512
51
52static int ddisk_dev_add(ddf_dev_t *dev);
53static int ddisk_dev_remove(ddf_dev_t *dev);
54static int ddisk_dev_gone(ddf_dev_t *dev);
55static int ddisk_fun_online(ddf_fun_t *fun);
56static int ddisk_fun_offline(ddf_fun_t *fun);
57
58static void ddisk_bd_connection(ipc_callid_t, ipc_call_t *, void *);
59
60static driver_ops_t driver_ops = {
61 .dev_add = ddisk_dev_add,
62 .dev_remove = ddisk_dev_remove,
63 .dev_gone = ddisk_dev_gone,
64 .fun_online = ddisk_fun_online,
65 .fun_offline = ddisk_fun_offline
66};
67
68static driver_t ddisk_driver = {
69 .name = NAME,
70 .driver_ops = &driver_ops
71};
72
73typedef struct {
74 int irq;
75 uintptr_t base;
76} ddisk_res_t;
77
78typedef struct {
79 ioport32_t dma_buffer;
80 ioport32_t block;
81 union {
82 ioport32_t status;
83 ioport32_t command;
84 };
85 ioport32_t size;
86} ddisk_regs_t;
87
88struct ddisk_dev;
89
90typedef struct ddisk_fun {
91 ddf_fun_t *fun;
92 struct ddisk_dev *ddisk_dev;
93
94 size_t blocks;
95
96 bd_srvs_t bds;
97} ddisk_fun_t;
98
99typedef struct ddisk_dev {
100 fibril_mutex_t lock;
101 ddf_dev_t *dev;
102 ddisk_res_t ddisk_res;
103 ddisk_fun_t *ddisk_fun;
104 ddisk_regs_t *ddisk_regs;
105 uintptr_t dma_buffer_phys;
106 void *dma_buffer;
107} ddisk_dev_t;
108
109static int ddisk_bd_open(bd_srvs_t *, bd_srv_t *);
110static int ddisk_bd_close(bd_srv_t *);
111static int ddisk_bd_read_blocks(bd_srv_t *, uint64_t, size_t, void *, size_t);
112static int ddisk_bd_write_blocks(bd_srv_t *, uint64_t, size_t, const void *,
113 size_t);
114static int ddisk_bd_get_block_size(bd_srv_t *, size_t *);
115static int ddisk_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
116
117bd_ops_t ddisk_bd_ops = {
118 .open = ddisk_bd_open,
119 .close = ddisk_bd_close,
120 .read_blocks = ddisk_bd_read_blocks,
121 .write_blocks = ddisk_bd_write_blocks,
122 .get_block_size = ddisk_bd_get_block_size,
123 .get_num_blocks = ddisk_bd_get_num_blocks,
124};
125
126int ddisk_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
127{
128 return EOK;
129}
130
131int ddisk_bd_close(bd_srv_t *bd)
132{
133 return EOK;
134}
135
136int
137ddisk_bd_read_blocks(bd_srv_t *bd, uint64_t ba, size_t cnt, void *buf,
138 size_t size)
139{
140 if (size < cnt * DDISK_BLOCK_SIZE)
141 return EINVAL;
142
143 return ENOTSUP;
144}
145
146int ddisk_bd_write_blocks(bd_srv_t *bd, uint64_t ba, size_t cnt,
147 const void *buf, size_t size)
148{
149 if (size < cnt * DDISK_BLOCK_SIZE)
150 return EINVAL;
151
152 return ENOTSUP;
153}
154
155int ddisk_bd_get_block_size(bd_srv_t *bd, size_t *rsize)
156{
157 *rsize = DDISK_BLOCK_SIZE;
158 return EOK;
159}
160
161int ddisk_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
162{
163 ddisk_fun_t *ddisk_fun = (ddisk_fun_t *) bd->srvs->sarg;
164
165 *rnb = ddisk_fun->blocks;
166 return EOK;
167}
168
169static int ddisk_get_res(ddf_dev_t *dev, ddisk_res_t *ddisk_res)
170{
171 async_sess_t *parent_sess;
172 hw_res_list_parsed_t hw_res;
173 int rc;
174
175 parent_sess = ddf_dev_parent_sess_create(dev, EXCHANGE_SERIALIZE);
176 if (parent_sess == NULL)
177 return ENOMEM;
178
179 hw_res_list_parsed_init(&hw_res);
180 rc = hw_res_get_list_parsed(parent_sess, &hw_res, 0);
181 if (rc != EOK)
182 return rc;
183
184 if ((hw_res.mem_ranges.count != 1) || (hw_res.irqs.count != 1)) {
185 rc = EINVAL;
186 goto error;
187 }
188
189 addr_range_t *regs = &hw_res.mem_ranges.ranges[0];
190 ddisk_res->base = RNGABS(*regs);
191 ddisk_res->irq = hw_res.irqs.irqs[0];
192
193 if (RNGSZ(*regs) < sizeof(ddisk_regs_t)) {
194 rc = EINVAL;
195 goto error;
196 }
197
198 rc = EOK;
199error:
200 hw_res_list_parsed_clean(&hw_res);
201 return rc;
202}
203
204static int ddisk_fun_create(ddisk_dev_t *ddisk_dev)
205{
206 int rc;
207 ddf_fun_t *fun = NULL;
208 ddisk_fun_t *ddisk_fun = NULL;
209
210 fun = ddf_fun_create(ddisk_dev->dev, fun_exposed, DDISK_FUN_NAME);
211 if (fun == NULL) {
212 ddf_msg(LVL_ERROR, "Failed creating DDF function.");
213 rc = ENOMEM;
214 goto error;
215 }
216
217 /* Allocate soft state */
218 ddisk_fun = ddf_fun_data_alloc(fun, sizeof(ddisk_fun_t));
219 if (!ddisk_fun) {
220 ddf_msg(LVL_ERROR, "Failed allocating softstate.");
221 rc = ENOMEM;
222 goto error;
223 }
224
225 ddisk_fun->fun = fun;
226 ddisk_fun->ddisk_dev = ddisk_dev;
227
228 bd_srvs_init(&ddisk_fun->bds);
229 ddisk_fun->bds.ops = &ddisk_bd_ops;
230 ddisk_fun->bds.sarg = ddisk_fun;
231
232 /* Set up a connection handler. */
233 ddf_fun_set_conn_handler(fun, ddisk_bd_connection);
234
235 ddisk_fun->blocks = pio_read_32(&ddisk_dev->ddisk_regs->size) /
236 DDISK_BLOCK_SIZE;
237
238 rc = ddf_fun_bind(fun);
239 if (rc != EOK) {
240 ddf_msg(LVL_ERROR, "Failed binding DDF function %s: %s",
241 DDISK_FUN_NAME, str_error(rc));
242 goto error;
243 }
244
245 ddf_fun_add_to_category(fun, "bd");
246
247 ddisk_dev->ddisk_fun = ddisk_fun;
248 return EOK;
249error:
250 if (fun != NULL)
251 ddf_fun_destroy(fun);
252
253 return rc;
254}
255
256static int ddisk_fun_remove(ddisk_fun_t *ddisk_fun)
257{
258 int rc;
259
260 if (ddisk_fun->fun == NULL)
261 return EOK;
262
263 ddf_msg(LVL_DEBUG, "ddisk_fun_remove(%p, '%s')", ddisk_fun,
264 DDISK_FUN_NAME);
265 rc = ddf_fun_offline(ddisk_fun->fun);
266 if (rc != EOK) {
267 ddf_msg(LVL_ERROR, "Error offlining function '%s'.",
268 DDISK_FUN_NAME);
269 goto error;
270 }
271
272 rc = ddf_fun_unbind(ddisk_fun->fun);
273 if (rc != EOK) {
274 ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.",
275 DDISK_FUN_NAME);
276 goto error;
277 }
278
279 ddf_fun_destroy(ddisk_fun->fun);
280 ddisk_fun->fun = NULL;
281 rc = EOK;
282error:
283 return rc;
284}
285
286static int ddisk_fun_unbind(ddisk_fun_t *ddisk_fun)
287{
288 int rc;
289
290 if (ddisk_fun->fun == NULL)
291 return EOK;
292
293 ddf_msg(LVL_DEBUG, "ddisk_fun_unbind(%p, '%s')", ddisk_fun,
294 DDISK_FUN_NAME);
295 rc = ddf_fun_unbind(ddisk_fun->fun);
296 if (rc != EOK) {
297 ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.",
298 DDISK_FUN_NAME);
299 goto error;
300 }
301
302 ddf_fun_destroy(ddisk_fun->fun);
303 ddisk_fun->fun = NULL;
304 rc = EOK;
305error:
306 return rc;
307}
308
309/** Add new device
310 *
311 * @param dev New device
312 * @return EOK on success or negative error code.
313 */
314static int ddisk_dev_add(ddf_dev_t *dev)
315{
316 ddisk_dev_t *ddisk_dev;
317 ddisk_res_t res;
318 int rc;
319
320 rc = ddisk_get_res(dev, &res);
321 if (rc != EOK) {
322 ddf_msg(LVL_ERROR, "Invalid HW resource configuration.");
323 return EINVAL;
324 }
325
326 ddisk_dev = ddf_dev_data_alloc(dev, sizeof(ddisk_dev_t));
327 if (!ddisk_dev) {
328 ddf_msg(LVL_ERROR, "Failed allocating soft state.");
329 rc = ENOMEM;
330 goto error;
331 }
332
333 fibril_mutex_initialize(&ddisk_dev->lock);
334 ddisk_dev->dev = dev;
335 ddisk_dev->ddisk_res = res;
336
337 /*
338 * Enable access to ddisk's PIO registers.
339 */
340 void *vaddr;
341 rc = pio_enable((void *) res.base, sizeof(ddisk_regs_t), &vaddr);
342 if (rc != EOK) {
343 ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
344 goto error;
345 }
346 ddisk_dev->ddisk_regs = vaddr;
347
348 if ((int32_t) pio_read_32(&ddisk_dev->ddisk_regs->size) <= 0) {
349 ddf_msg(LVL_WARN, "No disk detected.");
350 rc = EIO;
351 goto error;
352 }
353
354 /*
355 * Allocate DMA buffer.
356 */
357 ddisk_dev->dma_buffer = AS_AREA_ANY;
358 rc = dmamem_map_anonymous(DDISK_BLOCK_SIZE, DMAMEM_4GiB,
359 AS_AREA_READ | AS_AREA_WRITE, 0, &ddisk_dev->dma_buffer_phys,
360 &ddisk_dev->dma_buffer);
361 if (rc != EOK) {
362 ddf_msg(LVL_ERROR, "Cannot allocate DMA memory.");
363 goto error;
364 }
365
366 ddf_msg(LVL_NOTE, "Allocated DMA buffer at %p virtual and %p physical.",
367 ddisk_dev->dma_buffer, (void *) ddisk_dev->dma_buffer_phys);
368
369 rc = ddisk_fun_create(ddisk_dev);
370 if (rc != EOK) {
371 ddf_msg(LVL_ERROR, "Failed initializing ddisk controller.");
372 rc = EIO;
373 goto error;
374 }
375
376 ddf_msg(LVL_NOTE, "Device at %p with %d blocks (%dB) using interrupt %d",
377 (void *) ddisk_dev->ddisk_res.base, ddisk_dev->ddisk_fun->blocks,
378 ddisk_dev->ddisk_fun->blocks * DDISK_BLOCK_SIZE, ddisk_dev->ddisk_res.irq);
379
380 return EOK;
381error:
382 if (ddisk_dev->ddisk_regs)
383 pio_disable(ddisk_dev->ddisk_regs, sizeof(ddisk_regs_t));
384 if (ddisk_dev->dma_buffer)
385 dmamem_unmap_anonymous(ddisk_dev->dma_buffer);
386 return rc;
387}
388
389
390static int ddisk_dev_remove_common(ddisk_dev_t *ddisk_dev, bool surprise)
391{
392 int rc;
393
394 if (!surprise)
395 rc = ddisk_fun_remove(ddisk_dev->ddisk_fun);
396 else
397 rc = ddisk_fun_unbind(ddisk_dev->ddisk_fun);
398 if (rc != EOK) {
399 ddf_msg(LVL_ERROR, "Unable to cleanup function '%s'.",
400 DDISK_FUN_NAME);
401 return rc;
402 }
403
404 rc = pio_disable(ddisk_dev->ddisk_regs, sizeof(ddisk_regs_t));
405 if (rc != EOK) {
406 ddf_msg(LVL_ERROR, "Unable to disable PIO.");
407 return rc;
408 }
409
410 dmamem_unmap_anonymous(ddisk_dev->dma_buffer);
411
412 return EOK;
413}
414
415static int ddisk_dev_remove(ddf_dev_t *dev)
416{
417 ddisk_dev_t *ddisk_dev = (ddisk_dev_t *) ddf_dev_data_get(dev);
418
419 ddf_msg(LVL_DEBUG, "ddisk_dev_remove(%p)", dev);
420
421 return ddisk_dev_remove_common(ddisk_dev, false);
422}
423
424static int ddisk_dev_gone(ddf_dev_t *dev)
425{
426 ddisk_dev_t *ddisk_dev = (ddisk_dev_t *) ddf_dev_data_get(dev);
427
428 ddf_msg(LVL_DEBUG, "ddisk_dev_gone(%p)", dev);
429
430 return ddisk_dev_remove_common(ddisk_dev, true);
431}
432
433static int ddisk_fun_online(ddf_fun_t *fun)
434{
435 ddf_msg(LVL_DEBUG, "ddisk_fun_online()");
436 return ddf_fun_online(fun);
437}
438
439static int ddisk_fun_offline(ddf_fun_t *fun)
440{
441 ddf_msg(LVL_DEBUG, "ddisk_fun_offline()");
442 return ddf_fun_offline(fun);
443}
444
445/** Block device connection handler */
446static void ddisk_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
447{
448 ddisk_fun_t *ddfun;
449
450 ddfun = (ddisk_fun_t *) ddf_fun_data_get((ddf_fun_t *)arg);
451 bd_conn(iid, icall, &ddfun->bds);
452}
453
454int main(int argc, char *argv[])
455{
456 printf(NAME ": HelenOS MSIM ddisk device driver\n");
457 ddf_log_init(NAME);
458 return ddf_driver_main(&ddisk_driver);
459}
Note: See TracBrowser for help on using the repository browser.