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
bdr_alarm(clock_t stamp)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
hw_intr(unsigned int irqs)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
apply_env()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 minix-service command in
100 * the following way:
101 * minix-service up /service/mmc -args "log_level=2 instance=1
102 * driver=dummy" -dev /dev/c2d0 */
103 char driver[16];
104 memset(driver, '\0', 16);
105 (void) env_get_param("driver", driver, 16);
106 if (strlen(driver) == 0
107 || strncmp(driver, "mmchs", strlen("mmchs") + 1) == 0) {
108 /* early init of host mmc host controller. This code should
109 * depend on knowing the hardware that is running bellow. */
110 #ifdef __arm__
111 host_initialize_host_structure_mmchs(&host);
112 #endif
113 } else if (strncmp(driver, "dummy", strlen("dummy") + 1) == 0) {
114 host_initialize_host_structure_dummy(&host);
115 } else {
116 log_warn(&log, "Unknown driver %s\n", driver);
117 }
118 /* Initialize the verbosity level. */
119 v = 0;
120 if (env_parse("log_level", "d", 0, &v, LEVEL_NONE,
121 LEVEL_TRACE) == EP_SET) {
122 set_log_level(v);
123 }
124
125 /* Find out which driver instance we are. */
126 v = 0;
127 env_parse("instance", "d", 0, &v, 0, 3);
128 if (host.host_set_instance(&host, v)) {
129 log_warn(&log, "Failed to set mmc instance to %d\n", v);
130 return -1; /* NOT OK */
131 }
132 return OK;
133 }
134
135 ;
136
137 /*===========================================================================*
138 * block_open *
139 *===========================================================================*/
140 static int
block_open(devminor_t minor,int access)141 block_open(devminor_t minor, int access)
142 {
143 struct sd_slot *slot;
144 slot = get_slot(minor);
145 int i, j;
146 int part_count, sub_part_count;
147
148 i = j = part_count = sub_part_count = 0;
149
150 if (!slot) {
151 log_debug(&log, "Not handling open on non existing slot\n");
152 return EIO;
153 }
154
155 assert(slot->host != NULL);
156
157 if (!slot->host->card_detect(slot)) {
158 log_debug(&log, "No card inserted in the SD slot\n");
159 return EIO;
160 }
161
162 /* If we are already open just increase the open count and return */
163 if (slot->card.state == SD_MODE_DATA_TRANSFER_MODE) {
164 assert(slot->card.open_ct >= 0);
165 slot->card.open_ct++;
166 log_trace(&log, "increased open count to %d\n",
167 slot->card.open_ct);
168 return OK;
169 }
170
171 /* We did not have an sd-card inserted so we are going to probe for it
172 */
173 log_debug(&log, "First open on (%d)\n", minor);
174 if (!host.card_initialize(slot)) {
175 // * TODO: set card state to INVALID until removed? */
176 return EIO;
177 }
178
179 partition(&mmc_driver, 0 /* first card on bus */ , P_PRIMARY,
180 0 /* atapi device?? */ );
181
182 log_trace(&log, "descr \toffset(bytes) size(bytes)\n", minor);
183
184 log_trace(&log, "disk %d\t0x%016llx 0x%016llx\n", i,
185 slot->card.part[0].dv_base, slot->card.part[0].dv_size);
186 for (i = 1; i < 5; i++) {
187 if (slot->card.part[i].dv_size == 0)
188 continue;
189 part_count++;
190 log_trace(&log, "part %d\t0x%016llx 0x%016llx\n", i,
191 slot->card.part[i].dv_base, slot->card.part[i].dv_size);
192 for (j = 0; j < 4; j++) {
193 if (slot->card.subpart[(i - 1) * 4 + j].dv_size == 0)
194 continue;
195 sub_part_count++;
196 log_trace(&log,
197 " sub %d/%d\t0x%016llx 0x%016llx\n", i, j,
198 slot->card.subpart[(i - 1) * 4 + j].dv_base,
199 slot->card.subpart[(i - 1) * 4 + j].dv_size);
200 }
201 }
202 log_debug(&log, "Found %d partitions and %d sub partitions\n",
203 part_count, sub_part_count);
204 slot->card.open_ct++;
205 assert(slot->card.open_ct == 1);
206 return OK;
207 }
208
209 /*===========================================================================*
210 * block_close *
211 *===========================================================================*/
212 static int
block_close(devminor_t minor)213 block_close(devminor_t minor)
214 {
215 struct sd_slot *slot;
216
217 slot = get_slot(minor);
218 if (!slot) {
219 log_debug(&log, "Not handling open on non existing slot\n");
220 return EIO;
221 }
222
223 /* if we arrived here we expect a card to be present, we will need do
224 * deal with removal later */
225 assert(slot->host != NULL);
226 assert(slot->card.open_ct >= 1);
227
228 /* If this is not the last open count simply decrease the counter and
229 * return */
230 if (slot->card.open_ct > 1) {
231 slot->card.open_ct--;
232 log_trace(&log, "decreased open count to %d\n",
233 slot->card.open_ct);
234 return OK;
235 }
236
237 assert(slot->card.open_ct == 1);
238 log_debug(&log, "freeing the block device as it is no longer used\n");
239
240 /* release the card as check the open_ct should be 0 */
241 slot->host->card_release(&slot->card);
242 assert(slot->card.open_ct == 0);
243 return OK;
244 }
245
246 static int
copyto(endpoint_t dst_e,cp_grant_id_t gr_id,vir_bytes offset,vir_bytes address,size_t bytes)247 copyto(endpoint_t dst_e,
248 cp_grant_id_t gr_id, vir_bytes offset, vir_bytes address, size_t bytes)
249 {
250 /* Helper function that used memcpy to copy data when the endpoint ==
251 * SELF */
252 if (dst_e == SELF) {
253 memcpy((char *) gr_id + offset, (char *) address, bytes);
254 return OK;
255 } else {
256 /* Read io_size bytes from our data at the correct * offset
257 * and write it to the output buffer at 0 */
258 return sys_safecopyto(dst_e, gr_id, offset, address, bytes);
259 }
260 }
261
262 static int
copyfrom(endpoint_t src_e,cp_grant_id_t gr_id,vir_bytes offset,vir_bytes address,size_t bytes)263 copyfrom(endpoint_t src_e,
264 cp_grant_id_t gr_id, vir_bytes offset, vir_bytes address, size_t bytes)
265 {
266 /* Helper function that used memcpy to copy data when the endpoint ==
267 * SELF */
268 if (src_e == SELF) {
269 memcpy((char *) address, (char *) gr_id + offset, bytes);
270 return OK;
271 } else {
272 return sys_safecopyfrom(src_e, gr_id, offset, address, bytes);
273 }
274 }
275
276 /*===========================================================================*
277 * block_transfer *
278 *===========================================================================*/
279 static int
block_transfer(devminor_t minor,int do_write,u64_t position,endpoint_t endpt,iovec_t * iov,unsigned int nr_req,int flags)280 block_transfer(
281 devminor_t minor, /* minor device number */
282 int do_write, /* read or write? */
283 u64_t position, /* offset on device to read or write */
284 endpoint_t endpt, /* process doing the request */
285 iovec_t * iov, /* pointer to read or write request vector */
286 unsigned int nr_req, /* length of request vector */
287 int flags /* transfer flags */
288 )
289 {
290 unsigned long counter;
291 iovec_t *ciov; /* Current IO Vector */
292 struct device *dev; /* The device used */
293 struct sd_slot *slot; /* The sd slot the requests is pointed to */
294 vir_bytes io_size; /* Size to read/write to/from the iov */
295 vir_bytes io_offset; /* Size to read/write to/from the iov */
296 vir_bytes bytes_written;
297
298 int r, blk_size, i;
299
300 /* Get the current "device" geometry */
301 dev = block_part(minor);
302 if (dev == NULL) {
303 log_warn(&log,
304 "Transfer requested on unknown device minor(%d)\n", minor);
305 /* Unknown device */
306 return ENXIO;
307 }
308 log_trace(&log, "I/O on minor(%d) %s at 0x%016llx\n", minor,
309 (do_write) ? "Write" : "Read", position);
310
311 slot = get_slot(minor);
312 assert(slot);
313
314 if (slot->card.blk_size == 0) {
315 log_warn(&log, "Request on a card with block size of 0\n");
316 return EINVAL;
317 }
318 if (slot->card.blk_size > COPYBUFF_SIZE) {
319 log_warn(&log,
320 "Card block size (%d) exceeds internal buffer size %d\n",
321 slot->card.blk_size, COPYBUFF_SIZE);
322 return EINVAL;
323 }
324
325 /* It is fully up to the driver to decide on restrictions for the
326 * parameters of transfers, in those cases we return EINVAL */
327 if (position % slot->card.blk_size != 0) {
328 /* Starting at a block boundary */
329 log_warn(&log,
330 "Requests must start at a block boundary"
331 "(start,block size)=(%016llx,%08x)\n", position,
332 slot->card.blk_size);
333 return EINVAL;
334 }
335
336 blk_size = slot->card.blk_size;
337
338 bytes_written = 0;
339
340 /* Are we trying to start reading past the end */
341 if (position >= dev->dv_size) {
342 log_warn(&log, "start reading past drive size\n");
343 return 0;
344 };
345
346 ciov = iov;
347 /* do some more validation */
348 for (counter = 0; counter < nr_req; counter++) {
349 assert(ciov != NULL);
350 if (ciov->iov_size % blk_size != 0) {
351 /* transfer a multiple of blk_size */
352 log_warn(&log,
353 "Requests must start at a block boundary "
354 "(start,block size)=(%016llx,%08x)\n", position,
355 slot->card.blk_size);
356 return EINVAL;
357 }
358
359 if (ciov->iov_size <= 0) {
360 log_warn(&log,
361 "Invalid iov size for iov %d of %d size\n",
362 counter, nr_req, ciov->iov_size);
363 return EINVAL;
364 }
365 ciov++;
366 }
367
368 ciov = iov;
369 for (counter = 0; counter < nr_req; counter++) {
370 /* Assume we are to transfer the amount of data given in the
371 * input/output vector but ensure we are not doing i/o past
372 * our own boundaries */
373 io_size = ciov->iov_size;
374 io_offset = position + bytes_written;
375
376 /* Check we are not reading/writing past the end */
377 if (position + bytes_written + io_size > dev->dv_size) {
378 io_size = dev->dv_size - (position + bytes_written);
379 };
380
381 log_trace(&log,
382 "I/O %s request(%d/%d) iov(grant,size,iosize,"
383 "offset)=(%d,%d,%d,%d)\n",
384 (do_write) ? "write" : "read", counter + 1, nr_req,
385 ciov->iov_addr, ciov->iov_size, io_size, io_offset);
386 /* transfer max one block at the time */
387 for (i = 0; i < io_size / blk_size; i++) {
388 if (do_write) {
389 /* Read io_size bytes from i/o vector starting
390 * at 0 and write it to out buffer at the
391 * correct offset */
392 r = copyfrom(endpt, ciov->iov_addr,
393 i * blk_size, (vir_bytes) copybuff,
394 blk_size);
395 if (r != OK) {
396 log_warn(&log,
397 "I/O write error: %s iov(base,size)=(%d,%d)"
398 " at offset=%d\n",
399 strerror(_SIGN r), ciov->iov_addr,
400 ciov->iov_size, io_offset);
401 return EINVAL;
402 }
403
404 /* write a single block */
405 slot->host->write(&slot->card,
406 (dev->dv_base / blk_size) +
407 (io_offset / blk_size) + i, 1, copybuff);
408 bytes_written += blk_size;
409 } else {
410 /* read a single block info copybuff */
411 slot->host->read(&slot->card,
412 (dev->dv_base / blk_size) +
413 (io_offset / blk_size) + i, 1, copybuff);
414 /* Read io_size bytes from our data at the
415 * correct offset and write it to the output
416 * buffer at 0 */
417 r = copyto(endpt, ciov->iov_addr, i * blk_size,
418 (vir_bytes) copybuff, blk_size);
419 if (r != OK) {
420 log_warn(&log,
421 "I/O read error: %s iov(base,size)=(%d,%d)"
422 " at offset=%d\n",
423 strerror(_SIGN r), ciov->iov_addr,
424 ciov->iov_size, io_offset);
425 return EINVAL;
426 }
427 bytes_written += blk_size;
428 }
429 }
430 ciov++;
431 }
432 return bytes_written;
433 }
434
435 /*===========================================================================*
436 * block_ioctl *
437 *===========================================================================*/
438 static int
block_ioctl(devminor_t minor,unsigned long request,endpoint_t endpt,cp_grant_id_t grant,endpoint_t UNUSED (user_endpt))439 block_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
440 cp_grant_id_t grant, endpoint_t UNUSED(user_endpt))
441 {
442 /* IOCTL handling */
443 struct sd_slot *slot;
444 log_trace(&log,
445 "enter (minor,request,endpoint,grant)=(%d,%lu,%d)\n", minor,
446 request, endpt, grant);
447
448 slot = get_slot(minor);
449 if (!slot) {
450 log_warn(&log,
451 "Doing ioctl on non existing block device(%d)\n", minor);
452 return EINVAL;
453 }
454
455 switch (request) {
456 case DIOCOPENCT:
457 // TODO: add a check for card validity */
458 log_trace(&log, "returning open count %d\n",
459 slot->card.open_ct);
460 /* return the current open count */
461 return sys_safecopyto(endpt, grant, 0,
462 (vir_bytes) & slot->card.open_ct,
463 sizeof(slot->card.open_ct));
464 case DIOCFLUSH:
465 /* No need to flush but some devices like movinands require
466 * 500 ms inactivity */
467 return OK;
468 }
469
470 return ENOTTY;
471 }
472
473 /*===========================================================================*
474 * block_part *
475 *===========================================================================*/
476 static struct device *
block_part(devminor_t minor)477 block_part(devminor_t minor)
478 {
479 /*
480 * Reuse the existing MINIX major/minor partitioning scheme.
481 * - 8 drives
482 * - 5 devices per drive allowing direct access to the disk and up to 4
483 * partitions (IBM style partitioning without extended partitions)
484 * - 4 Minix style sub partitions per partitions
485 */
486 struct device *dev;
487 struct sd_slot *slot;
488
489 dev = NULL;
490 slot = get_slot(minor);
491 if (!slot) {
492 log_warn(&log,
493 "Device information requested for non existing partition "
494 "minor(%d)\n", minor);
495 return NULL;
496 }
497
498 if (!slot->host->card_detect(slot)) {
499 log_warn(&log,
500 "Device information requested from empty slot(%d)\n",
501 minor);
502 return NULL;
503 }
504
505 if (minor < 5) {
506 /* we are talking about the first disk */
507 dev = &slot->card.part[minor];
508 log_trace(&log,
509 "returning partition(%d) (base,size)=(0x%016llx,0x%016llx)\n",
510 minor, dev->dv_base, dev->dv_size);
511 } else if (minor >= 128 && minor < 128 + 16) {
512 /* sub partitions of the first disk we don't care about the
513 * rest */
514 dev = &slot->card.subpart[minor - 128];
515 log_trace(&log,
516 "returning sub partition(%d) (base,size)=(0x%016llx,0x%016llx)\n",
517 minor - 128, dev->dv_base, dev->dv_size);
518
519 } else {
520 log_warn(&log,
521 "Device information requested for non existing "
522 "partition minor(%d)\n", minor);
523 }
524 return dev;
525 }
526
527 /*===========================================================================*
528 * sef_local_startup *
529 *===========================================================================*/
530 static void
sef_local_startup()531 sef_local_startup()
532 {
533 log_info(&log, "Initializing the MMC block device\n");
534 if (apply_env()) {
535 log_warn(&log, "Failed while applying environment settings\n");
536 exit(EXIT_FAILURE);
537 }
538
539 if (host.host_init(&host)) {
540 log_warn(&log, "Failed to initialize the host controller\n");
541 exit(EXIT_FAILURE);
542 }
543 /*
544 * Register callbacks for fresh start, live update and restart.
545 * Use the same function for all event types
546 */
547 sef_setcb_init_fresh(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
block_system_event_cb(int type,sef_init_info_t * info)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
block_signal_handler_cb(int signo)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 *
get_slot(devminor_t minor)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
set_log_level(int level)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
main(int argc,char ** argv)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