xref: /minix/minix/drivers/storage/mmc/mmcblk.c (revision 045e0ed3)
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 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
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
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
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
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
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
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 *
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
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
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