2 * Block driver for Multi Media Cards (MMC).
5 #include <minix/syslib.h>
6 #include <minix/driver.h>
7 #include <minix/blockdriver.h>
8 #include <minix/drvlib.h>
10 #include <minix/minlib.h>
13 #include <sys/ioc_disk.h> /* disk IOCTL's */
25 /* used for logging */
26 static struct log log
= {
28 .log_level
= LEVEL_INFO
,
29 .log_func
= default_log
32 /* holding the current host controller */
33 static struct mmc_host host
;
35 #define NR_SUBDEVS (MAX_DRIVES * SUB_PER_DRIVE)
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
];
42 static struct sd_slot
*get_slot(devminor_t minor
);
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
,
50 endpoint_t endpt
, iovec_t
* iov
, unsigned int nr_req
, int flags
);
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
);
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
);
62 bdr_alarm(clock_t stamp
)
64 log_debug(&log
, "alarm %d\n", stamp
);
67 static int apply_env();
68 static void hw_intr(unsigned int irqs
);
70 /* set the global logging level */
71 static void set_log_level(int level
);
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 */
86 hw_intr(unsigned int irqs
)
88 log_debug(&log
, "Hardware inter left over\n");
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 /sbin/mmc -args "log_level=2 instance=1
101 * driver=dummy" -dev /dev/c2d0 */
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. */
110 host_initialize_host_structure_mmchs(&host
);
112 } else if (strncmp(driver
, "dummy", strlen("dummy") + 1) == 0) {
113 host_initialize_host_structure_dummy(&host
);
115 log_warn(&log
, "Unknown driver %s\n", driver
);
117 /* Initialize the verbosity level. */
119 if (env_parse("log_level", "d", 0, &v
, LEVEL_NONE
,
120 LEVEL_TRACE
) == EP_SET
) {
124 /* Find out which driver instance we are. */
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 */
136 /*===========================================================================*
138 *===========================================================================*/
140 block_open(devminor_t minor
, int access
)
142 struct sd_slot
*slot
;
143 slot
= get_slot(minor
);
145 int part_count
, sub_part_count
;
147 i
= j
= part_count
= sub_part_count
= 0;
150 log_debug(&log
, "Not handling open on non existing slot\n");
154 assert(slot
->host
!= NULL
);
156 if (!slot
->host
->card_detect(slot
)) {
157 log_debug(&log
, "No card inserted in the SD slot\n");
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",
170 /* We did not have an sd-card inserted so we are going to probe for it
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? */
178 partition(&mmc_driver
, 0 /* first card on bus */ , P_PRIMARY
,
179 0 /* atapi device?? */ );
181 log_trace(&log
, "descr \toffset(bytes) size(bytes)\n", minor
);
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)
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)
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
);
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);
208 /*===========================================================================*
210 *===========================================================================*/
212 block_close(devminor_t minor
)
214 struct sd_slot
*slot
;
216 slot
= get_slot(minor
);
218 log_debug(&log
, "Not handling open on non existing slot\n");
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);
227 /* If this is not the last open count simply decrease the counter and
229 if (slot
->card
.open_ct
> 1) {
230 slot
->card
.open_ct
--;
231 log_trace(&log
, "decreased open count to %d\n",
236 assert(slot
->card
.open_ct
== 1);
237 log_debug(&log
, "freeing the block device as it is no longer used\n");
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);
246 copyto(endpoint_t dst_e
,
247 cp_grant_id_t gr_id
, vir_bytes offset
, vir_bytes address
, size_t bytes
)
249 /* Helper function that used memcpy to copy data when the endpoint ==
252 memcpy((char *) gr_id
+ offset
, (char *) address
, bytes
);
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
);
262 copyfrom(endpoint_t src_e
,
263 cp_grant_id_t gr_id
, vir_bytes offset
, vir_bytes address
, size_t bytes
)
265 /* Helper function that used memcpy to copy data when the endpoint ==
268 memcpy((char *) address
, (char *) gr_id
+ offset
, bytes
);
271 return sys_safecopyfrom(src_e
, gr_id
, offset
, address
, bytes
);
275 /*===========================================================================*
277 *===========================================================================*/
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 */
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
;
299 /* Get the current "device" geometry */
300 dev
= block_part(minor
);
303 "Transfer requested on unknown device minor(%d)\n", minor
);
307 log_trace(&log
, "I/O on minor(%d) %s at 0x%016llx\n", minor
,
308 (do_write
) ? "Write" : "Read", position
);
310 slot
= get_slot(minor
);
313 if (slot
->card
.blk_size
== 0) {
314 log_warn(&log
, "Request on a card with block size of 0\n");
317 if (slot
->card
.blk_size
> COPYBUFF_SIZE
) {
319 "Card block size (%d) exceeds internal buffer size %d\n",
320 slot
->card
.blk_size
, COPYBUFF_SIZE
);
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 */
329 "Requests must start at a block boundary"
330 "(start,block size)=(%016llx,%08x)\n", position
,
331 slot
->card
.blk_size
);
335 blk_size
= slot
->card
.blk_size
;
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");
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 */
352 "Requests must start at a block boundary "
353 "(start,block size)=(%016llx,%08x)\n", position
,
354 slot
->card
.blk_size
);
358 if (ciov
->iov_size
<= 0) {
360 "Invalid iov size for iov %d of %d size\n",
361 counter
, nr_req
, ciov
->iov_size
);
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
;
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
);
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
++) {
388 /* Read io_size bytes from i/o vector starting
389 * at 0 and write it to out buffer at the
391 r
= copyfrom(endpt
, ciov
->iov_addr
,
392 i
* blk_size
, (vir_bytes
) copybuff
,
396 "I/O write error: %s iov(base,size)=(%d,%d)"
398 strerror(_SIGN r
), ciov
->iov_addr
,
399 ciov
->iov_size
, io_offset
);
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
;
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
416 r
= copyto(endpt
, ciov
->iov_addr
, i
* blk_size
,
417 (vir_bytes
) copybuff
, blk_size
);
420 "I/O read error: %s iov(base,size)=(%d,%d)"
422 strerror(_SIGN r
), ciov
->iov_addr
,
423 ciov
->iov_size
, io_offset
);
426 bytes_written
+= blk_size
;
431 return bytes_written
;
434 /*===========================================================================*
436 *===========================================================================*/
438 block_ioctl(devminor_t minor
, unsigned long request
, endpoint_t endpt
,
439 cp_grant_id_t grant
, endpoint_t
UNUSED(user_endpt
))
442 struct sd_slot
*slot
;
444 "enter (minor,request,endpoint,grant)=(%d,%lu,%d)\n", minor
,
445 request
, endpt
, grant
);
447 slot
= get_slot(minor
);
450 "Doing ioctl on non existing block device(%d)\n", minor
);
456 // TODO: add a check for card validity */
457 log_trace(&log
, "returning open count %d\n",
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
));
464 /* No need to flush but some devices like movinands require
465 * 500 ms inactivity */
472 /*===========================================================================*
474 *===========================================================================*/
475 static struct device
*
476 block_part(devminor_t minor
)
479 * Reuse the existing MINIX major/minor partitioning scheme.
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
486 struct sd_slot
*slot
;
489 slot
= get_slot(minor
);
492 "Device information requested for non existing partition "
493 "minor(%d)\n", minor
);
497 if (!slot
->host
->card_detect(slot
)) {
499 "Device information requested from empty slot(%d)\n",
505 /* we are talking about the first disk */
506 dev
= &slot
->card
.part
[minor
];
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
513 dev
= &slot
->card
.subpart
[minor
- 128];
515 "returning sub partition(%d) (base,size)=(0x%016llx,0x%016llx)\n",
516 minor
- 128, dev
->dv_base
, dev
->dv_size
);
520 "Device information requested for non existing "
521 "partition minor(%d)\n", minor
);
526 /*===========================================================================*
527 * sef_local_startup *
528 *===========================================================================*/
532 log_info(&log
, "Initializing the MMC block device\n");
534 log_warn(&log
, "Failed while applying environment settings\n");
538 if (host
.host_init(&host
)) {
539 log_warn(&log
, "Failed to initialize the host controller\n");
543 * Register callbacks for fresh start, live update and restart.
544 * Use the same function for all event types
546 sef_setcb_init_fresh(block_system_event_cb
);
547 sef_setcb_init_lu(block_system_event_cb
);
549 /* Register a signal handler */
550 sef_setcb_signal_handler(block_signal_handler_cb
);
556 /*===========================================================================*
557 * block_system_event_cb *
558 *===========================================================================*/
560 block_system_event_cb(int type
, sef_init_info_t
* info
)
563 * Callbacks for the System event framework as registered in
564 * sef_local_startup */
567 log_info(&log
, "System event framework fresh start\n");
571 /* Restore the state. post update */
572 log_info(&log
, "System event framework live update\n");
575 case SEF_INIT_RESTART
:
576 log_info(&log
, "System event framework post restart\n");
579 blockdriver_announce(type
);
583 /*===========================================================================*
584 * block_signal_handler_cb *
585 *===========================================================================*/
587 block_signal_handler_cb(int signo
)
589 struct sd_slot
*slot
;
591 log_debug(&log
, "System event framework signal handler sig(%d)\n",
593 /* Only check for termination signal, ignore anything else. */
594 if (signo
!= SIGTERM
)
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 */
601 if (slot
->card
.open_ct
> 0) {
602 log_debug(&log
, "Not responding to SIGTERM (open count=%d)\n",
607 log_info(&log
, "MMC driver exit");
611 #define IS_MINIX_SUB_PARTITION_MINOR(minor) (minor >= MINOR_d0p0s0 )
613 static struct sd_slot
*
614 get_slot(devminor_t minor
)
617 * Get an sd_slot based on the minor number.
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.
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
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];
636 "Device information requested for non existing partition "
637 "minor(%d)\n", minor
);
643 set_log_level(int level
)
645 if (level
< 0 || level
>= 4) {
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
);
656 main(int argc
, char **argv
)
659 /* Set and apply the environment */
660 env_setargs(argc
, argv
);
662 blockdriver_task(&mmc_driver
);