source: mainline/uspace/drv/block/ddisk/ddisk.c@ eadaeae8

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

Make capability handles type-safe

Define distinct pointer types for the handles of the supported
capability types and use them instead of integer handles. This makes it
virtually impossible to pass a non-handle or a handle of different type
instead of the proper handle. Also turn cap_handle_t into an "untyped"
capability handle that can be assigned to and from the "typed" handles.

This commit also fixes a bug in msim-con driver, which wrongly used the
IRQ number instead of the IRQ capability handle to unregister the IRQ.

This commit also fixes the wrong use of the capability handle instead
of error code in libusbhost.

  • Property mode set to 100644
File size: 13.3 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/interrupt.h>
40#include <ddf/log.h>
41#include <device/hw_res_parsed.h>
42#include <ddi.h>
43#include <bd_srv.h>
44#include <fibril_synch.h>
45#include <as.h>
46
47#define NAME "ddisk"
48
49#define DDISK_FUN_NAME "a"
50
51#define DDISK_BLOCK_SIZE 512
52
53#define DDISK_STAT_IRQ_PENDING 0x4
54#define DDISK_CMD_READ 0x1
55#define DDISK_CMD_WRITE 0x2
56#define DDISK_CMD_IRQ_DEASSERT 0x4
57
58static errno_t ddisk_dev_add(ddf_dev_t *);
59static errno_t ddisk_dev_remove(ddf_dev_t *);
60static errno_t ddisk_dev_gone(ddf_dev_t *);
61static errno_t ddisk_fun_online(ddf_fun_t *);
62static errno_t ddisk_fun_offline(ddf_fun_t *);
63
64static void ddisk_bd_connection(ipc_callid_t, ipc_call_t *, void *);
65
66static void ddisk_irq_handler(ipc_call_t *, ddf_dev_t *);
67
68static driver_ops_t driver_ops = {
69 .dev_add = ddisk_dev_add,
70 .dev_remove = ddisk_dev_remove,
71 .dev_gone = ddisk_dev_gone,
72 .fun_online = ddisk_fun_online,
73 .fun_offline = ddisk_fun_offline
74};
75
76static driver_t ddisk_driver = {
77 .name = NAME,
78 .driver_ops = &driver_ops
79};
80
81typedef struct {
82 int irq;
83 uintptr_t base;
84} ddisk_res_t;
85
86typedef struct {
87 ioport32_t dma_buffer;
88 ioport32_t block;
89 union {
90 ioport32_t status;
91 ioport32_t command;
92 };
93 ioport32_t size;
94} ddisk_regs_t;
95
96typedef struct {
97 fibril_mutex_t lock;
98
99 fibril_condvar_t io_cv;
100 bool io_busy;
101
102 ssize_t size;
103 size_t blocks;
104
105 uintptr_t dma_buffer_phys;
106 void *dma_buffer;
107
108 ddf_dev_t *dev;
109 ddf_fun_t *fun;
110
111 ddisk_res_t ddisk_res;
112 ddisk_regs_t *ddisk_regs;
113
114 cap_irq_handle_t irq_cap;
115
116 bd_srvs_t bds;
117} ddisk_t;
118
119static errno_t ddisk_bd_open(bd_srvs_t *, bd_srv_t *);
120static errno_t ddisk_bd_close(bd_srv_t *);
121static errno_t ddisk_bd_read_blocks(bd_srv_t *, aoff64_t, size_t, void *, size_t);
122static errno_t ddisk_bd_write_blocks(bd_srv_t *, aoff64_t, size_t, const void *,
123 size_t);
124static errno_t ddisk_bd_get_block_size(bd_srv_t *, size_t *);
125static errno_t ddisk_bd_get_num_blocks(bd_srv_t *, aoff64_t *);
126
127bd_ops_t ddisk_bd_ops = {
128 .open = ddisk_bd_open,
129 .close = ddisk_bd_close,
130 .read_blocks = ddisk_bd_read_blocks,
131 .write_blocks = ddisk_bd_write_blocks,
132 .get_block_size = ddisk_bd_get_block_size,
133 .get_num_blocks = ddisk_bd_get_num_blocks,
134};
135
136irq_pio_range_t ddisk_irq_pio_ranges[] = {
137 {
138 .base = 0,
139 .size = sizeof(ddisk_regs_t)
140 }
141};
142
143irq_cmd_t ddisk_irq_commands[] = {
144 {
145 .cmd = CMD_PIO_READ_32,
146 .addr = NULL,
147 .dstarg = 1
148 },
149 {
150 .cmd = CMD_AND,
151 .srcarg = 1,
152 .value = DDISK_STAT_IRQ_PENDING,
153 .dstarg = 2
154 },
155 {
156 .cmd = CMD_PREDICATE,
157 .srcarg = 2,
158 .value = 2
159 },
160 {
161 /* Deassert the DMA interrupt. */
162 .cmd = CMD_PIO_WRITE_32,
163 .value = DDISK_CMD_IRQ_DEASSERT,
164 .addr = NULL
165 },
166 {
167 .cmd = CMD_ACCEPT
168 }
169};
170
171irq_code_t ddisk_irq_code = {
172 .rangecount = 1,
173 .ranges = ddisk_irq_pio_ranges,
174 .cmdcount = sizeof(ddisk_irq_commands) / sizeof(irq_cmd_t),
175 .cmds = ddisk_irq_commands,
176};
177
178void ddisk_irq_handler(ipc_call_t *icall, ddf_dev_t *dev)
179{
180 ddf_msg(LVL_DEBUG, "ddisk_irq_handler(), status=%" PRIx32,
181 (uint32_t) IPC_GET_ARG1(*icall));
182
183 ddisk_t *ddisk = (ddisk_t *) ddf_dev_data_get(dev);
184
185 fibril_mutex_lock(&ddisk->lock);
186 fibril_condvar_broadcast(&ddisk->io_cv);
187 fibril_mutex_unlock(&ddisk->lock);
188}
189
190errno_t ddisk_bd_open(bd_srvs_t *bds, bd_srv_t *bd)
191{
192 return EOK;
193}
194
195errno_t ddisk_bd_close(bd_srv_t *bd)
196{
197 return EOK;
198}
199
200static
201errno_t ddisk_rw_block(ddisk_t *ddisk, bool read, aoff64_t ba, void *buf)
202{
203 fibril_mutex_lock(&ddisk->lock);
204
205 ddf_msg(LVL_DEBUG, "ddisk_rw_block(): read=%d, ba=%" PRId64 ", buf=%p",
206 read, ba, buf);
207
208 if (ba >= ddisk->blocks)
209 return ELIMIT;
210
211 while (ddisk->io_busy)
212 fibril_condvar_wait(&ddisk->io_cv, &ddisk->lock);
213
214 ddisk->io_busy = true;
215
216 if (!read)
217 memcpy(ddisk->dma_buffer, buf, DDISK_BLOCK_SIZE);
218
219 pio_write_32(&ddisk->ddisk_regs->dma_buffer,
220 ddisk->dma_buffer_phys);
221 pio_write_32(&ddisk->ddisk_regs->block, (uint32_t) ba);
222 pio_write_32(&ddisk->ddisk_regs->command,
223 read ? DDISK_CMD_READ : DDISK_CMD_WRITE);
224
225 fibril_condvar_wait(&ddisk->io_cv, &ddisk->lock);
226
227 if (read)
228 memcpy(buf, ddisk->dma_buffer, DDISK_BLOCK_SIZE);
229
230 ddisk->io_busy = false;
231 fibril_condvar_signal(&ddisk->io_cv);
232 fibril_mutex_unlock(&ddisk->lock);
233
234 return EOK;
235}
236
237static
238errno_t ddisk_bd_rw_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt, void *buf,
239 size_t size, bool is_read)
240{
241 ddisk_t *ddisk = (ddisk_t *) bd->srvs->sarg;
242 aoff64_t i;
243 errno_t rc;
244
245 if (size < cnt * DDISK_BLOCK_SIZE)
246 return EINVAL;
247
248 for (i = 0; i < cnt; i++) {
249 rc = ddisk_rw_block(ddisk, is_read, ba + i,
250 buf + i * DDISK_BLOCK_SIZE);
251 if (rc != EOK)
252 return rc;
253 }
254
255 return EOK;
256}
257
258errno_t ddisk_bd_read_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt, void *buf,
259 size_t size)
260{
261 return ddisk_bd_rw_blocks(bd, ba, cnt, buf, size, true);
262}
263
264errno_t ddisk_bd_write_blocks(bd_srv_t *bd, aoff64_t ba, size_t cnt,
265 const void *buf, size_t size)
266{
267 return ddisk_bd_rw_blocks(bd, ba, cnt, (void *) buf, size, false);
268}
269
270errno_t ddisk_bd_get_block_size(bd_srv_t *bd, size_t *rsize)
271{
272 *rsize = DDISK_BLOCK_SIZE;
273 return EOK;
274}
275
276errno_t ddisk_bd_get_num_blocks(bd_srv_t *bd, aoff64_t *rnb)
277{
278 ddisk_t *ddisk = (ddisk_t *) bd->srvs->sarg;
279
280 *rnb = ddisk->blocks;
281 return EOK;
282}
283
284static errno_t ddisk_get_res(ddf_dev_t *dev, ddisk_res_t *ddisk_res)
285{
286 async_sess_t *parent_sess;
287 hw_res_list_parsed_t hw_res;
288 errno_t rc;
289
290 parent_sess = ddf_dev_parent_sess_get(dev);
291 if (parent_sess == NULL)
292 return ENOMEM;
293
294 hw_res_list_parsed_init(&hw_res);
295 rc = hw_res_get_list_parsed(parent_sess, &hw_res, 0);
296 if (rc != EOK)
297 return rc;
298
299 if ((hw_res.mem_ranges.count != 1) || (hw_res.irqs.count != 1)) {
300 rc = EINVAL;
301 goto error;
302 }
303
304 addr_range_t *regs = &hw_res.mem_ranges.ranges[0];
305 ddisk_res->base = RNGABS(*regs);
306 ddisk_res->irq = hw_res.irqs.irqs[0];
307
308 if (RNGSZ(*regs) < sizeof(ddisk_regs_t)) {
309 rc = EINVAL;
310 goto error;
311 }
312
313 rc = EOK;
314error:
315 hw_res_list_parsed_clean(&hw_res);
316 return rc;
317}
318
319static errno_t ddisk_fun_create(ddisk_t *ddisk)
320{
321 errno_t rc;
322 ddf_fun_t *fun = NULL;
323
324 fun = ddf_fun_create(ddisk->dev, fun_exposed, DDISK_FUN_NAME);
325 if (fun == NULL) {
326 ddf_msg(LVL_ERROR, "Failed creating DDF function.");
327 rc = ENOMEM;
328 goto error;
329 }
330
331 /* Set up a connection handler. */
332 ddf_fun_set_conn_handler(fun, ddisk_bd_connection);
333
334 rc = ddf_fun_bind(fun);
335 if (rc != EOK) {
336 ddf_msg(LVL_ERROR, "Failed binding DDF function %s: %s",
337 DDISK_FUN_NAME, str_error(rc));
338 goto error;
339 }
340
341 ddf_fun_add_to_category(fun, "disk");
342 ddisk->fun = fun;
343
344 return EOK;
345error:
346 if (fun != NULL)
347 ddf_fun_destroy(fun);
348
349 return rc;
350}
351
352static errno_t ddisk_fun_remove(ddisk_t *ddisk)
353{
354 errno_t rc;
355
356 if (ddisk->fun == NULL)
357 return EOK;
358
359 ddf_msg(LVL_DEBUG, "ddisk_fun_remove(%p, '%s')", ddisk,
360 DDISK_FUN_NAME);
361 rc = ddf_fun_offline(ddisk->fun);
362 if (rc != EOK) {
363 ddf_msg(LVL_ERROR, "Error offlining function '%s'.",
364 DDISK_FUN_NAME);
365 goto error;
366 }
367
368 rc = ddf_fun_unbind(ddisk->fun);
369 if (rc != EOK) {
370 ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.",
371 DDISK_FUN_NAME);
372 goto error;
373 }
374
375 ddf_fun_destroy(ddisk->fun);
376 ddisk->fun = NULL;
377 rc = EOK;
378
379error:
380 return rc;
381}
382
383static errno_t ddisk_fun_unbind(ddisk_t *ddisk)
384{
385 errno_t rc;
386
387 if (ddisk->fun == NULL)
388 return EOK;
389
390 ddf_msg(LVL_DEBUG, "ddisk_fun_unbind(%p, '%s')", ddisk,
391 DDISK_FUN_NAME);
392 rc = ddf_fun_unbind(ddisk->fun);
393 if (rc != EOK) {
394 ddf_msg(LVL_ERROR, "Failed unbinding function '%s'.",
395 DDISK_FUN_NAME);
396 goto error;
397 }
398
399 ddf_fun_destroy(ddisk->fun);
400 ddisk->fun = NULL;
401 rc = EOK;
402
403error:
404 return rc;
405}
406
407/** Add new device
408 *
409 * @param dev New device
410 * @return EOK on success or an error code.
411 */
412static errno_t ddisk_dev_add(ddf_dev_t *dev)
413{
414 ddisk_t *ddisk;
415 ddisk_res_t res;
416 errno_t rc;
417
418 /*
419 * Get our resources.
420 */
421 rc = ddisk_get_res(dev, &res);
422 if (rc != EOK) {
423 ddf_msg(LVL_ERROR, "Invalid HW resource configuration.");
424 return EINVAL;
425 }
426
427 /*
428 * Allocate soft state.
429 */
430 ddisk = ddf_dev_data_alloc(dev, sizeof(ddisk_t));
431 if (!ddisk) {
432 ddf_msg(LVL_ERROR, "Failed allocating soft state.");
433 rc = ENOMEM;
434 goto error;
435 }
436
437 /*
438 * Initialize soft state.
439 */
440 fibril_mutex_initialize(&ddisk->lock);
441 ddisk->dev = dev;
442 ddisk->ddisk_res = res;
443
444 fibril_condvar_initialize(&ddisk->io_cv);
445 ddisk->io_busy = false;
446
447 bd_srvs_init(&ddisk->bds);
448 ddisk->bds.ops = &ddisk_bd_ops;
449 ddisk->bds.sarg = ddisk;
450
451 ddisk->irq_cap = CAP_NIL;
452
453 /*
454 * Enable access to ddisk's PIO registers.
455 */
456 void *vaddr;
457 rc = pio_enable((void *) res.base, sizeof(ddisk_regs_t), &vaddr);
458 if (rc != EOK) {
459 ddf_msg(LVL_ERROR, "Cannot initialize device I/O space.");
460 goto error;
461 }
462 ddisk->ddisk_regs = vaddr;
463
464 ddisk->size = (int32_t) pio_read_32(&ddisk->ddisk_regs->size);
465 ddisk->blocks = ddisk->size / DDISK_BLOCK_SIZE;
466
467 if (ddisk->size <= 0) {
468 ddf_msg(LVL_WARN, "No disk detected.");
469 rc = EIO;
470 goto error;
471 }
472
473 /*
474 * Allocate DMA buffer.
475 */
476 ddisk->dma_buffer = AS_AREA_ANY;
477 rc = dmamem_map_anonymous(DDISK_BLOCK_SIZE, DMAMEM_4GiB,
478 AS_AREA_READ | AS_AREA_WRITE, 0, &ddisk->dma_buffer_phys,
479 &ddisk->dma_buffer);
480 if (rc != EOK) {
481 ddf_msg(LVL_ERROR, "Cannot allocate DMA memory.");
482 goto error;
483 }
484
485 ddf_msg(LVL_NOTE, "Allocated DMA buffer at %p virtual and %p physical.",
486 ddisk->dma_buffer, (void *) ddisk->dma_buffer_phys);
487
488 /*
489 * Create an exposed function.
490 */
491 rc = ddisk_fun_create(ddisk);
492 if (rc != EOK) {
493 ddf_msg(LVL_ERROR, "Failed initializing ddisk controller.");
494 rc = EIO;
495 goto error;
496 }
497
498 /*
499 * Register IRQ handler.
500 */
501 ddisk_regs_t *res_phys = (ddisk_regs_t *) res.base;
502 ddisk_irq_pio_ranges[0].base = res.base;
503 ddisk_irq_commands[0].addr = (void *) &res_phys->status;
504 ddisk_irq_commands[3].addr = (void *) &res_phys->command;
505 rc = register_interrupt_handler(dev, ddisk->ddisk_res.irq,
506 ddisk_irq_handler, &ddisk_irq_code, &ddisk->irq_cap);
507 if (rc != EOK) {
508 ddf_msg(LVL_ERROR, "Failed to register interrupt handler.");
509 goto error;
510 }
511
512 /*
513 * Success, report what we have found.
514 */
515 ddf_msg(LVL_NOTE,
516 "Device at %p with %zd blocks (%zuB) using interrupt %d",
517 (void *) ddisk->ddisk_res.base, ddisk->blocks,
518 ddisk->size, ddisk->ddisk_res.irq);
519
520 return EOK;
521
522error:
523 if (ddisk->ddisk_regs)
524 pio_disable(ddisk->ddisk_regs, sizeof(ddisk_regs_t));
525 if (ddisk->dma_buffer)
526 dmamem_unmap_anonymous(ddisk->dma_buffer);
527
528 return rc;
529}
530
531
532static errno_t ddisk_dev_remove_common(ddisk_t *ddisk, bool surprise)
533{
534 errno_t rc;
535
536 if (!surprise)
537 rc = ddisk_fun_remove(ddisk);
538 else
539 rc = ddisk_fun_unbind(ddisk);
540
541 if (rc != EOK) {
542 ddf_msg(LVL_ERROR, "Unable to cleanup function '%s'.",
543 DDISK_FUN_NAME);
544 return rc;
545 }
546
547 unregister_interrupt_handler(ddisk->dev, ddisk->irq_cap);
548
549 rc = pio_disable(ddisk->ddisk_regs, sizeof(ddisk_regs_t));
550 if (rc != EOK) {
551 ddf_msg(LVL_ERROR, "Unable to disable PIO.");
552 return rc;
553 }
554
555 dmamem_unmap_anonymous(ddisk->dma_buffer);
556
557 return EOK;
558}
559
560static errno_t ddisk_dev_remove(ddf_dev_t *dev)
561{
562 ddisk_t *ddisk = (ddisk_t *) ddf_dev_data_get(dev);
563
564 ddf_msg(LVL_DEBUG, "ddisk_dev_remove(%p)", dev);
565 return ddisk_dev_remove_common(ddisk, false);
566}
567
568static errno_t ddisk_dev_gone(ddf_dev_t *dev)
569{
570 ddisk_t *ddisk = (ddisk_t *) ddf_dev_data_get(dev);
571
572 ddf_msg(LVL_DEBUG, "ddisk_dev_gone(%p)", dev);
573 return ddisk_dev_remove_common(ddisk, true);
574}
575
576static errno_t ddisk_fun_online(ddf_fun_t *fun)
577{
578 ddf_msg(LVL_DEBUG, "ddisk_fun_online()");
579 return ddf_fun_online(fun);
580}
581
582static errno_t ddisk_fun_offline(ddf_fun_t *fun)
583{
584 ddf_msg(LVL_DEBUG, "ddisk_fun_offline()");
585 return ddf_fun_offline(fun);
586}
587
588/** Block device connection handler */
589static void ddisk_bd_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
590{
591 ddisk_t *ddisk;
592 ddf_fun_t *fun = (ddf_fun_t *) arg;
593
594 ddisk = (ddisk_t *) ddf_dev_data_get(ddf_fun_get_dev(fun));
595 bd_conn(iid, icall, &ddisk->bds);
596}
597
598int main(int argc, char *argv[])
599{
600 printf(NAME ": HelenOS MSIM ddisk device driver\n");
601 ddf_log_init(NAME);
602 return ddf_driver_main(&ddisk_driver);
603}
Note: See TracBrowser for help on using the repository browser.