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