ldivmod, uldivmod: fix qdivrem calls
[minix.git] / drivers / mmc / mmcblk.c
blobd2da50a3fc8ad5d275725c96979b8db2d1214e07
1 /*
2 * Block driver for Multi Media Cards (MMC).
3 */
4 /* kernel headers */
5 #include <minix/syslib.h>
6 #include <minix/driver.h>
7 #include <minix/blockdriver.h>
8 #include <minix/drvlib.h>
9 #include <minix/minlib.h>
12 /* system headers */
13 #include <sys/ioc_disk.h> /* disk IOCTL's */
15 /* usr headers */
16 #include <assert.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <signal.h>
22 /* local headers */
23 #include "mmchost.h"
24 #include "mmclog.h"
26 /* used for logging */
27 static struct mmclog log = {
28 .name = "mmc_block",
29 .log_level = LEVEL_INFO,
30 .log_func = default_log
33 /* holding the current host controller */
34 static struct mmc_host host;
36 #define SUB_PER_DRIVE (NR_PARTITIONS * NR_PARTITIONS)
37 #define NR_SUBDEVS (MAX_DRIVES * SUB_PER_DRIVE)
39 /* When passing data over a grant one needs to pass
40 * a buffer to sys_safecopy copybuff is used for that*/
41 #define COPYBUFF_SIZE 0x1000 /* 4k buff */
42 static unsigned char copybuff[COPYBUFF_SIZE];
44 static struct sd_slot *get_slot(dev_t minor);
46 /* Prototypes for the block device */
47 static int block_open(dev_t minor, int access);
48 static int block_close(dev_t minor);
49 static int block_transfer(dev_t minor,
50 int do_write,
51 u64_t position,
52 endpoint_t endpt, iovec_t * iov, unsigned int nr_req, int flags);
54 static int block_ioctl(dev_t minor,
55 unsigned int request, endpoint_t endpt, cp_grant_id_t grant);
56 static struct device *block_part(dev_t minor);
58 /* System even handling */
59 static void sef_local_startup();
60 static int block_system_event_cb(int type, sef_init_info_t * info);
61 static void block_signal_handler_cb(int signo);
63 void
64 bdr_alarm(clock_t stamp)
66 mmc_log_debug(&log, "alarm %d\n", stamp);
70 static int apply_env();
71 static void hw_intr(unsigned int irqs);
73 /* set the global logging level */
74 static void set_log_level(int level);
76 /* Entry points for the BLOCK driver. */
77 static struct blockdriver mmc_driver = {
78 BLOCKDRIVER_TYPE_DISK, /* handle partition requests */
79 block_open, /* open or mount */
80 block_close, /* on a close */
81 block_transfer, /* does the I/O */
82 block_ioctl, /* ioclt's */
83 NULL, /* no need to clean up (yet) */
84 block_part, /* return partition information */
85 NULL, /* no geometry */
86 hw_intr, /* left over interrupts */
87 bdr_alarm, /* no alarm processing */
88 NULL, /* no processing of other messages */
89 NULL /* no threading support */
92 static void
93 hw_intr(unsigned int irqs)
95 mmc_log_debug(&log, "Hardware inter left over\n");
96 host.hw_intr(irqs);
99 static int
100 apply_env()
102 long v;
103 /* apply the env setting passed to this driver parameters accepted
104 * log_level=[0-4] (NONE,WARN,INFO,DEBUG,TRACE) instance=[0-3]
105 * instance/bus number to use for this driver Passing these arguments
106 * is done when starting the driver using the service command in the
107 * following way service up /sbin/mmc -args "log_level=2 instance=1
108 * driver=dummy" -dev /dev/c2d0 */
109 char driver[16];
110 memset(driver, '\0', 16);
111 (void) env_get_param("driver", driver, 16);
112 if (strlen(driver) == 0
113 || strncmp(driver, "mmchs", strlen("mmchs") + 1) == 0) {
114 /* early init of host mmc host controller. This code should
115 * depend on knowing the hardware that is running bellow. */
116 host_initialize_host_structure_mmchs(&host);
117 } else if (strncmp(driver, "dummy", strlen("dummy") + 1) == 0) {
118 host_initialize_host_structure_dummy(&host);
119 } else {
120 mmc_log_warn(&log, "Unknown driver %s\n", driver);
122 /* Initialize the verbosity level. */
123 v = 0;
124 if (env_parse("log_level", "d", 0, &v, LEVEL_NONE,
125 LEVEL_TRACE) == EP_SET) {
126 set_log_level(v);
129 /* Find out which driver instance we are. */
130 v = 0;
131 env_parse("instance", "d", 0, &v, 0, 3);
132 if (host.host_set_instance(&host, v)) {
133 mmc_log_warn(&log, "Failed to set mmc instance to %d\n", v);
134 return -1; /* NOT OK */
136 return OK;
141 /*===========================================================================*
142 * block_open *
143 *===========================================================================*/
144 static int
145 block_open(dev_t minor, int access)
147 struct sd_slot *slot;
148 slot = get_slot(minor);
149 int i, j;
150 int part_count, sub_part_count;
152 i = j = part_count = sub_part_count = 0;
154 if (!slot) {
155 mmc_log_debug(&log,
156 "Not handling open on non existing slot\n");
157 return EIO;
160 assert(slot->host != NULL);
162 if (!slot->host->card_detect(slot)) {
163 mmc_log_debug(&log, "No card inserted in the SD slot\n");
164 return EIO;
167 /* If we are already open just increase the open count and return */
168 if (slot->card.state == SD_MODE_DATA_TRANSFER_MODE) {
169 assert(slot->card.open_ct >= 0);
170 slot->card.open_ct++;
171 mmc_log_trace(&log, "increased open count to %d\n",
172 slot->card.open_ct);
173 return OK;
176 /* We did not have an sd-card inserted so we are going to probe for it
178 mmc_log_debug(&log, "First open on (%d)\n", minor);
179 if (!host.card_initialize(slot)) {
180 // * TODO: set card state to INVALID until removed? */
181 return EIO;
184 partition(&mmc_driver, 0 /* first card on bus */ , P_PRIMARY,
185 0 /* atapi device?? */ );
187 mmc_log_trace(&log, "descr \toffset(bytes) size(bytes)\n", minor);
189 mmc_log_trace(&log, "disk %d\t0x%016llx 0x%016llx\n", i,
190 slot->card.part[0].dv_base, slot->card.part[0].dv_size);
191 for (i = 1; i < 5; i++) {
192 if (slot->card.part[i].dv_size == 0)
193 continue;
194 part_count++;
195 mmc_log_trace(&log, "part %d\t0x%016llx 0x%016llx\n", i,
196 slot->card.part[i].dv_base, slot->card.part[i].dv_size);
197 for (j = 0; j < 4; j++) {
198 if (slot->card.subpart[(i - 1) * 4 + j].dv_size == 0)
199 continue;
200 sub_part_count++;
201 mmc_log_trace(&log,
202 " sub %d/%d\t0x%016llx 0x%016llx\n", i, j,
203 slot->card.subpart[(i - 1) * 4 + j].dv_base,
204 slot->card.subpart[(i - 1) * 4 + j].dv_size);
207 mmc_log_debug(&log, "Found %d partitions and %d sub partitions\n",
208 part_count, sub_part_count);
209 slot->card.open_ct++;
210 assert(slot->card.open_ct == 1);
211 return OK;
214 /*===========================================================================*
215 * block_close *
216 *===========================================================================*/
217 static int
218 block_close(dev_t minor)
220 struct sd_slot *slot;
222 slot = get_slot(minor);
223 if (!slot) {
224 mmc_log_debug(&log,
225 "Not handling open on non existing slot\n");
226 return EIO;
229 /* if we arrived here we expect a card to be present, we will need do
230 * deal with removal later */
231 assert(slot->host != NULL);
232 assert(slot->card.open_ct >= 1);
234 /* If this is not the last open count simply decrease the counter and
235 * return */
236 if (slot->card.open_ct > 1) {
237 slot->card.open_ct--;
238 mmc_log_trace(&log, "decreased open count to %d\n",
239 slot->card.open_ct);
240 return OK;
243 assert(slot->card.open_ct == 1);
244 mmc_log_debug(&log,
245 "freeing the block device as it is no longer used\n");
247 /* release the card as check the open_ct should be 0 */
248 slot->host->card_release(&slot->card);
249 assert(slot->card.open_ct == 0);
250 return OK;
253 static int
254 copyto(endpoint_t dst_e,
255 cp_grant_id_t gr_id, vir_bytes offset, vir_bytes address, size_t bytes)
257 /* Helper function that used memcpy to copy data when the endpoint ==
258 * SELF */
259 if (dst_e == SELF) {
260 memcpy((char *) gr_id + offset, (char *) address, bytes);
261 return OK;
262 } else {
263 /* Read io_size bytes from our data at the correct * offset
264 * and write it to the output buffer at 0 */
265 return sys_safecopyto(dst_e, gr_id, offset, address, bytes);
269 static int
270 copyfrom(endpoint_t src_e,
271 cp_grant_id_t gr_id, vir_bytes offset, vir_bytes address, size_t bytes)
273 /* Helper function that used memcpy to copy data when the endpoint ==
274 * SELF */
275 if (src_e == SELF) {
276 memcpy((char *) address, (char *) gr_id + offset, bytes);
277 return OK;
278 } else {
279 return sys_safecopyfrom(src_e, gr_id, offset, address, bytes);
283 /*===========================================================================*
284 * block_transfer *
285 *===========================================================================*/
286 static int
287 block_transfer(dev_t minor, /* minor device number */
288 int do_write, /* read or write? */
289 u64_t position, /* offset on device to read or write */
290 endpoint_t endpt, /* process doing the request */
291 iovec_t * iov, /* pointer to read or write request vector */
292 unsigned int nr_req, /* length of request vector */
293 int flags /* transfer flags */
296 unsigned long counter;
297 iovec_t *ciov; /* Current IO Vector */
298 struct device *dev; /* The device used */
299 struct sd_slot *slot; /* The sd slot the requests is pointed to */
300 vir_bytes io_size; /* Size to read/write to/from the iov */
301 vir_bytes io_offset; /* Size to read/write to/from the iov */
302 vir_bytes bytes_written;
304 int r, blk_size, i;
306 /* Get the current "device" geometry */
307 dev = block_part(minor);
308 if (dev == NULL) {
309 mmc_log_warn(&log,
310 "Transfer requested on unknown device minor(%d)\n", minor);
311 /* Unknown device */
312 return ENXIO;
314 mmc_log_trace(&log, "I/O on minor(%d) %s at 0x%016llx\n", minor,
315 (do_write) ? "Write" : "Read", position);
317 slot = get_slot(minor);
318 assert(slot);
320 if (slot->card.blk_size == 0) {
321 mmc_log_warn(&log, "Request on a card with block size of 0\n");
322 return EINVAL;
324 if (slot->card.blk_size > COPYBUFF_SIZE) {
325 mmc_log_warn(&log,
326 "Card block size (%d) exceeds internal buffer size %d\n",
327 slot->card.blk_size, COPYBUFF_SIZE);
328 return EINVAL;
331 /* It is fully up to the driver to decide on restrictions for the
332 * parameters of transfers, in those cases we return EINVAL */
333 if (position % slot->card.blk_size != 0) {
334 /* Starting at a block boundary */
335 mmc_log_warn(&log,
336 "Requests must start at a block boundary"
337 "(start,block size)=(%016llx,%08x)\n", position,
338 slot->card.blk_size);
339 return EINVAL;
342 blk_size = slot->card.blk_size;
344 bytes_written = 0;
346 /* Are we trying to start reading past the end */
347 if (position >= dev->dv_size) {
348 mmc_log_warn(&log, "start reading past drive size\n");
349 return 0;
352 ciov = iov;
353 /* do some more validation */
354 for (counter = 0; counter < nr_req; counter++) {
355 assert(ciov != NULL);
356 if (ciov->iov_size % blk_size != 0) {
357 /* transfer a multiple of blk_size */
358 mmc_log_warn(&log,
359 "Requests must start at a block boundary "
360 "(start,block size)=(%016llx,%08x)\n", position,
361 slot->card.blk_size);
362 return EINVAL;
365 if (ciov->iov_size <= 0) {
366 mmc_log_warn(&log,
367 "Invalid iov size for iov %d of %d size\n",
368 counter, nr_req, ciov->iov_size);
369 return EINVAL;
371 ciov++;
374 ciov = iov;
375 for (counter = 0; counter < nr_req; counter++) {
376 /* Assume we are to transfer the amount of data given in the
377 * input/output vector but ensure we are not doing i/o past
378 * our own boundaries */
379 io_size = ciov->iov_size;
380 io_offset = position + bytes_written;
382 /* Check we are not reading/writing past the end */
383 if (position + bytes_written + io_size > dev->dv_size) {
384 io_size = dev->dv_size - (position + bytes_written);
387 mmc_log_trace(&log,
388 "I/O %s request(%d/%d) iov(grant,size,iosize,"
389 "offset)=(%d,%d,%d,%d)\n",
390 (do_write) ? "write" : "read", counter + 1, nr_req,
391 ciov->iov_addr, ciov->iov_size, io_size, io_offset);
392 /* transfer max one block at the time */
393 for (i = 0; i < io_size / blk_size; i++) {
394 if (do_write) {
395 /* Read io_size bytes from i/o vector starting
396 * at 0 and write it to out buffer at the
397 * correct offset */
398 r = copyfrom(endpt, ciov->iov_addr,
399 i * blk_size, (vir_bytes) copybuff,
400 blk_size);
401 if (r != OK) {
402 mmc_log_warn(&log,
403 "I/O write error: %s iov(base,size)=(%d,%d)"
404 " at offset=%d\n",
405 strerror(_SIGN r), ciov->iov_addr,
406 ciov->iov_size, io_offset);
407 return EINVAL;
410 /* write a single block */
411 slot->host->write(&slot->card,
412 (dev->dv_base / blk_size) +
413 (io_offset / blk_size) + i, 1, copybuff);
414 bytes_written += blk_size;
415 } else {
416 /* read a single block info copybuff */
417 slot->host->read(&slot->card,
418 (dev->dv_base / blk_size) +
419 (io_offset / blk_size) + i, 1, copybuff);
420 /* Read io_size bytes from our data at the
421 * correct offset and write it to the output
422 * buffer at 0 */
423 r = copyto(endpt, ciov->iov_addr, i * blk_size,
424 (vir_bytes) copybuff, blk_size);
425 if (r != OK) {
426 mmc_log_warn(&log,
427 "I/O read error: %s iov(base,size)=(%d,%d)"
428 " at offset=%d\n",
429 strerror(_SIGN r), ciov->iov_addr,
430 ciov->iov_size, io_offset);
431 return EINVAL;
433 bytes_written += blk_size;
436 ciov++;
438 return bytes_written;
441 /*===========================================================================*
442 * block_ioctl *
443 *===========================================================================*/
444 static int
445 block_ioctl(dev_t minor,
446 unsigned int request, endpoint_t endpt, cp_grant_id_t grant)
448 /* IOCTL handling */
449 struct sd_slot *slot;
450 mmc_log_trace(&log,
451 "enter (minor,request,endpoint,grant)=(%d,%lu,%d)\n", minor,
452 request, endpt, grant);
454 slot = get_slot(minor);
455 if (!slot) {
456 mmc_log_warn(&log,
457 "Doing ioctl on non existing block device(%d)\n", minor);
458 return EINVAL;
461 switch (request) {
462 case DIOCOPENCT:
463 // TODO: add a check for card validity */
464 mmc_log_trace(&log, "returning open count %d\n",
465 slot->card.open_ct);
466 /* return the current open count */
467 return sys_safecopyto(endpt, grant, 0,
468 (vir_bytes) & slot->card.open_ct,
469 sizeof(slot->card.open_ct));
470 case DIOCFLUSH:
471 /* No need to flush but some devices like movinands require
472 * 500 ms inactivity */
473 return OK;
476 return EINVAL;
479 /*===========================================================================*
480 * block_part *
481 *===========================================================================*/
482 static struct device *
483 block_part(dev_t minor)
486 * Reuse the existing MINIX major/minor partitioning scheme.
487 * - 8 drives
488 * - 5 devices per drive allowing direct access to the disk and up to 4
489 * partitions (IBM style partitioning without extended partitions)
490 * - 4 Minix style sub partitions per partitions
492 struct device *dev;
493 struct sd_slot *slot;
495 dev = NULL;
496 slot = get_slot(minor);
497 if (!slot) {
498 mmc_log_warn(&log,
499 "Device information requested for non existing partition "
500 "minor(%d)\n", minor);
501 return NULL;
504 if (!slot->host->card_detect(slot)) {
505 mmc_log_warn(&log,
506 "Device information requested from empty slot(%d)\n",
507 minor);
508 return NULL;
511 if (minor < 5) {
512 /* we are talking about the first disk */
513 dev = &slot->card.part[minor];
514 mmc_log_trace(&log,
515 "returning partition(%d) (base,size)=(0x%016llx,0x%016llx)\n",
516 minor, dev->dv_base, dev->dv_size);
517 } else if (minor >= 128 && minor < 128 + 16) {
518 /* sub partitions of the first disk we don't care about the
519 * rest */
520 dev = &slot->card.subpart[minor - 128];
521 mmc_log_trace(&log,
522 "returning sub partition(%d) (base,size)=(0x%016llx,0x%016llx)\n",
523 minor - 128, dev->dv_base, dev->dv_size);
525 } else {
526 mmc_log_warn(&log,
527 "Device information requested for non existing "
528 "partition minor(%d)\n", minor);
530 return dev;
533 /*===========================================================================*
534 * sef_local_startup *
535 *===========================================================================*/
536 static void
537 sef_local_startup()
539 mmc_log_info(&log, "Initializing the MMC block device\n");
540 if (apply_env()) {
541 mmc_log_warn(&log,
542 "Failed while applying environment settings\n");
543 exit(EXIT_FAILURE);
546 if (host.host_init(&host)) {
547 mmc_log_warn(&log,
548 "Failed to initialize the host controller\n");
549 exit(EXIT_FAILURE);
552 * Register callbacks for fresh start, live update and restart.
553 * Use the same function for all event types
555 sef_setcb_init_fresh(block_system_event_cb);
556 sef_setcb_init_lu(block_system_event_cb);
558 /* Register a signal handler */
559 sef_setcb_signal_handler(block_signal_handler_cb);
561 /* SEF startup */
562 sef_startup();
565 /*===========================================================================*
566 * block_system_event_cb *
567 *===========================================================================*/
568 static int
569 block_system_event_cb(int type, sef_init_info_t * info)
572 * Callbacks for the System event framework as registered in
573 * sef_local_startup */
574 switch (type) {
575 case SEF_INIT_FRESH:
576 mmc_log_info(&log, "System event framework fresh start\n");
577 break;
579 case SEF_INIT_LU:
580 /* Restore the state. post update */
581 mmc_log_info(&log, "System event framework live update\n");
582 break;
584 case SEF_INIT_RESTART:
585 mmc_log_info(&log, "System event framework post restart\n");
586 break;
588 blockdriver_announce(type);
589 return OK;
592 /*===========================================================================*
593 * block_signal_handler_cb *
594 *===========================================================================*/
595 static void
596 block_signal_handler_cb(int signo)
598 mmc_log_debug(&log, "System event framework signal handler sig(%d)\n",
599 signo);
600 /* Only check for termination signal, ignore anything else. */
601 if (signo != SIGTERM)
602 return;
603 // FIXME shutdown
604 exit(0);
607 #define IS_MINIX_SUB_PARTITION_MINOR(minor) (minor >= MINOR_d0p0s0 )
609 static struct sd_slot *
610 get_slot(dev_t minor)
613 * Get an sd_slot based on the minor number.
615 * This driver only supports a single card at at time. Also as
616 * we are following the major/minor scheme of other driver we
617 * must return a slot for all minors on disk 0 these are 0-5
618 * for the disk and 4 main partitions and
619 * number 128 till 144 for sub partitions.
621 /* If this is a minor for the first disk (e.g. minor 0 till 5) */
622 if (minor / DEV_PER_DRIVE == 0) {
623 /* we are talking about the first disk and that is all we
624 * support */
625 return &host.slot[0];
626 } else if (IS_MINIX_SUB_PARTITION_MINOR(minor)
627 && (((minor - MINOR_d0p0s0) / SUB_PER_DRIVE) == 0)) {
628 /* a minor from the first disk */
629 return &host.slot[0];
630 } else {
631 mmc_log_trace(&log,
632 "Device information requested for non existing partition "
633 "minor(%d)\n", minor);
634 return NULL;
638 static void
639 set_log_level(int level)
641 if (level < 0 || level >= 4) {
642 return;
644 mmc_log_info(&log, "Setting verbosity level to %d\n", level);
645 log.log_level = level;
646 if (host.set_log_level) {
647 host.set_log_level(level);
652 main(int argc, char **argv)
655 /* Set and apply the environment */
656 env_setargs(argc, argv);
657 sef_local_startup();
658 blockdriver_task(&mmc_driver);
659 return EXIT_SUCCESS;