1 /* This file contains the device independent character driver interface. 2 * 3 * Character drivers support the following requests. Message format m10 is 4 * used. Field names are prefixed with CDEV_. Separate field names are used for 5 * the "access", "ops", "user", and "request" fields. 6 * 7 * m_type MINOR GRANT COUNT FLAGS ID POS_LO POS_HI 8 * +-------------+-------+--------+-------+-------+------+---------+--------+ 9 * | CDEV_OPEN | minor | access | user | | id | | | 10 * |-------------+-------+--------+-------+-------+------+---------+--------| 11 * | CDEV_CLOSE | minor | | | | id | | | 12 * |-------------+-------+--------+-------+-------+------+---------+--------| 13 * | CDEV_READ | minor | grant | bytes | flags | id | position | 14 * |-------------+-------+--------+-------+-------+------+---------+--------| 15 * | CDEV_WRITE | minor | grant | bytes | flags | id | position | 16 * |-------------+-------+--------+-------+-------+------+---------+--------| 17 * | CDEV_IOCTL | minor | grant | user | flags | id | request | | 18 * |-------------+-------+--------+-------+-------+------+---------+--------| 19 * | CDEV_CANCEL | minor | | | | id | | | 20 * |-------------+-------+--------+-------+-------+------+---------+--------| 21 * | CDEV_SELECT | minor | ops | | | | | | 22 * -------------------------------------------------------------------------- 23 * 24 * The following reply messages are used. 25 * 26 * m_type MINOR STATUS ID 27 * +-----------------+-------+--------+-----+-----+------+---------+--------+ 28 * | CDEV_REPLY | | status | | | id | | | 29 * |-----------------+-------+--------+-----+-----+------+---------+--------| 30 * | CDEV_SEL1_REPLY | minor | status | | | | | | 31 * |-----------------+-------+--------+-----+-----+------+---------+--------| 32 * | CDEV_SEL2_REPLY | minor | status | | | | | | 33 * -------------------------------------------------------------------------- 34 * 35 * Changes: 36 * Sep 01, 2013 complete rewrite of the API (D.C. van Moolenboek) 37 * Aug 20, 2013 retire synchronous protocol (D.C. van Moolenbroek) 38 * Oct 16, 2011 split character and block protocol (D.C. van Moolenbroek) 39 * Aug 27, 2011 move common functions into driver.c (A. Welzel) 40 * Jul 25, 2005 added SYS_SIG type for signals (Jorrit N. Herder) 41 * Sep 15, 2004 added SYN_ALARM type for timeouts (Jorrit N. Herder) 42 * Jul 23, 2004 removed kernel dependencies (Jorrit N. Herder) 43 * Apr 02, 1992 constructed from AT wini and floppy driver (Kees J. Bot) 44 */ 45 46 #include <assert.h> 47 48 #include <minix/drivers.h> 49 #include <minix/chardriver.h> 50 #include <minix/ds.h> 51 52 static int running; 53 54 /* Management data for opened devices. */ 55 static devminor_t open_devs[MAX_NR_OPEN_DEVICES]; 56 static int next_open_devs_slot = 0; 57 58 /*===========================================================================* 59 * clear_open_devs * 60 *===========================================================================*/ 61 static void clear_open_devs(void) 62 { 63 /* Reset the set of previously opened minor devices. */ 64 next_open_devs_slot = 0; 65 } 66 67 /*===========================================================================* 68 * is_open_dev * 69 *===========================================================================*/ 70 static int is_open_dev(devminor_t minor) 71 { 72 /* Check whether the given minor device has previously been opened. */ 73 int i; 74 75 for (i = 0; i < next_open_devs_slot; i++) 76 if (open_devs[i] == minor) 77 return TRUE; 78 79 return FALSE; 80 } 81 82 /*===========================================================================* 83 * set_open_dev * 84 *===========================================================================*/ 85 static void set_open_dev(devminor_t minor) 86 { 87 /* Mark the given minor device as having been opened. */ 88 89 if (next_open_devs_slot >= MAX_NR_OPEN_DEVICES) 90 panic("out of slots for open devices"); 91 92 open_devs[next_open_devs_slot] = minor; 93 next_open_devs_slot++; 94 } 95 96 /*===========================================================================* 97 * chardriver_announce * 98 *===========================================================================*/ 99 void chardriver_announce(void) 100 { 101 /* Announce we are up after a fresh start or restart. */ 102 int r; 103 char key[DS_MAX_KEYLEN]; 104 char label[DS_MAX_KEYLEN]; 105 const char *driver_prefix = "drv.chr."; 106 107 /* Callers are allowed to use ipc_sendrec to communicate with drivers. 108 * For this reason, there may blocked callers when a driver restarts. 109 * Ask the kernel to unblock them (if any). 110 */ 111 if ((r = sys_statectl(SYS_STATE_CLEAR_IPC_REFS, 0, 0)) != OK) 112 panic("chardriver_announce: sys_statectl failed: %d", r); 113 114 /* Publish a driver up event. */ 115 if ((r = ds_retrieve_label_name(label, sef_self())) != OK) 116 panic("chardriver_announce: unable to get own label: %d", r); 117 118 snprintf(key, DS_MAX_KEYLEN, "%s%s", driver_prefix, label); 119 if ((r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE)) != OK) 120 panic("chardriver_announce: unable to publish driver up event: %d", r); 121 122 /* Expect an open for any device before serving regular driver requests. */ 123 clear_open_devs(); 124 } 125 126 /*===========================================================================* 127 * chardriver_reply_task * 128 *===========================================================================*/ 129 void chardriver_reply_task(endpoint_t endpt, cdev_id_t id, int r) 130 { 131 /* Reply to a (read, write, ioctl) task request that was suspended earlier. 132 * Not-so-well-written drivers may use this function to send a reply to a 133 * request that is being processed right now, and then return EDONTREPLY later. 134 */ 135 message m_reply; 136 137 if (r == EDONTREPLY || r == SUSPEND) 138 panic("chardriver: bad task reply: %d", r); 139 140 memset(&m_reply, 0, sizeof(m_reply)); 141 142 m_reply.m_type = CDEV_REPLY; 143 m_reply.m_lchardriver_vfs_reply.status = r; 144 m_reply.m_lchardriver_vfs_reply.id = id; 145 146 if ((r = asynsend3(endpt, &m_reply, AMF_NOREPLY)) != OK) 147 printf("chardriver_reply_task: send to %d failed: %d\n", endpt, r); 148 } 149 150 /*===========================================================================* 151 * chardriver_reply_select * 152 *===========================================================================*/ 153 void chardriver_reply_select(endpoint_t endpt, devminor_t minor, int r) 154 { 155 /* Reply to a select request with a status update. This must not be used to 156 * reply to a select request that is being processed right now. 157 */ 158 message m_reply; 159 160 /* Replying with an error is allowed (if unusual). */ 161 if (r == EDONTREPLY || r == SUSPEND) 162 panic("chardriver: bad select reply: %d", r); 163 164 memset(&m_reply, 0, sizeof(m_reply)); 165 166 m_reply.m_type = CDEV_SEL2_REPLY; 167 m_reply.m_lchardriver_vfs_sel2.minor = minor; 168 m_reply.m_lchardriver_vfs_sel2.status = r; 169 170 if ((r = asynsend3(endpt, &m_reply, AMF_NOREPLY)) != OK) 171 printf("chardriver_reply_select: send to %d failed: %d\n", endpt, r); 172 } 173 174 /*===========================================================================* 175 * send_reply * 176 *===========================================================================*/ 177 static void send_reply(endpoint_t endpt, message *m_ptr, int ipc_status) 178 { 179 /* Send a reply message to a request. */ 180 int r; 181 182 /* If we would block sending the message, send it asynchronously. */ 183 if (IPC_STATUS_CALL(ipc_status) == SENDREC) 184 r = ipc_sendnb(endpt, m_ptr); 185 else 186 r = asynsend3(endpt, m_ptr, AMF_NOREPLY); 187 188 if (r != OK) 189 printf("chardriver: unable to send reply to %d: %d\n", endpt, r); 190 } 191 192 /*===========================================================================* 193 * chardriver_reply * 194 *===========================================================================*/ 195 static void chardriver_reply(message *mess, int ipc_status, int r) 196 { 197 /* Prepare and send a reply message. */ 198 message reply_mess; 199 200 /* If the EDONTREPLY pseudo-reply is given, we do not reply. This is however 201 * allowed only for blocking task calls. Perform a sanity check. 202 */ 203 if (r == EDONTREPLY) { 204 switch (mess->m_type) { 205 case CDEV_READ: 206 case CDEV_WRITE: 207 case CDEV_IOCTL: 208 /* FIXME: we should be able to check FLAGS against 209 * CDEV_NONBLOCK here, but in practice, several drivers do not 210 * send a reply through this path (eg TTY) or simply do not 211 * implement nonblocking calls properly (eg audio, LWIP). 212 */ 213 #if 0 214 if (mess->m_vfs_lchardriver_readwrite.flags & CDEV_NONBLOCK) 215 panic("chardriver: cannot suspend nonblocking I/O"); 216 #endif 217 /*fall-through*/ 218 case CDEV_CANCEL: 219 return; /* alright */ 220 default: 221 panic("chardriver: cannot suspend request %d", mess->m_type); 222 } 223 } 224 225 if (r == SUSPEND) 226 panic("chardriver: SUSPEND should not be used anymore"); 227 228 /* Do not reply with ERESTART. The only possible caller, VFS, will find out 229 * through other means when we have restarted, and is not (fully) ready to 230 * deal with ERESTART errors. 231 */ 232 if (r == ERESTART) 233 return; 234 235 memset(&reply_mess, 0, sizeof(reply_mess)); 236 237 switch (mess->m_type) { 238 case CDEV_OPEN: 239 case CDEV_CLOSE: 240 reply_mess.m_type = CDEV_REPLY; 241 reply_mess.m_lchardriver_vfs_reply.status = r; 242 reply_mess.m_lchardriver_vfs_reply.id = 243 mess->m_vfs_lchardriver_openclose.id; 244 break; 245 246 case CDEV_READ: 247 case CDEV_WRITE: 248 case CDEV_IOCTL: 249 reply_mess.m_type = CDEV_REPLY; 250 reply_mess.m_lchardriver_vfs_reply.status = r; 251 reply_mess.m_lchardriver_vfs_reply.id = 252 mess->m_vfs_lchardriver_readwrite.id; 253 break; 254 255 case CDEV_CANCEL: /* For cancel, this is a reply to the original request! */ 256 reply_mess.m_type = CDEV_REPLY; 257 reply_mess.m_lchardriver_vfs_reply.status = r; 258 reply_mess.m_lchardriver_vfs_reply.id = 259 mess->m_vfs_lchardriver_cancel.id; 260 break; 261 262 case CDEV_SELECT: 263 reply_mess.m_type = CDEV_SEL1_REPLY; 264 reply_mess.m_lchardriver_vfs_sel1.status = r; 265 reply_mess.m_lchardriver_vfs_sel1.minor = 266 mess->m_vfs_lchardriver_select.minor; 267 break; 268 269 default: 270 panic("chardriver: unknown request %d", mess->m_type); 271 } 272 273 send_reply(mess->m_source, &reply_mess, ipc_status); 274 } 275 276 /*===========================================================================* 277 * do_open * 278 *===========================================================================*/ 279 static int do_open(const struct chardriver *cdp, message *m_ptr) 280 { 281 /* Open a minor device. */ 282 endpoint_t user_endpt; 283 devminor_t minor; 284 int r, bits; 285 286 /* Default action if no open hook is in place. */ 287 if (cdp->cdr_open == NULL) 288 return OK; 289 290 /* Call the open hook. */ 291 minor = m_ptr->m_vfs_lchardriver_openclose.minor; 292 bits = m_ptr->m_vfs_lchardriver_openclose.access; 293 user_endpt = m_ptr->m_vfs_lchardriver_openclose.user; 294 295 r = cdp->cdr_open(minor, bits, user_endpt); 296 297 /* If the device has been cloned, mark the new minor as open too. */ 298 if (r >= 0 && (r & CDEV_CLONED)) { 299 minor = r & ~(CDEV_CLONED | CDEV_CTTY); 300 if (!is_open_dev(minor)) 301 set_open_dev(minor); 302 } 303 304 return r; 305 } 306 307 /*===========================================================================* 308 * do_close * 309 *===========================================================================*/ 310 static int do_close(const struct chardriver *cdp, message *m_ptr) 311 { 312 /* Close a minor device. */ 313 devminor_t minor; 314 315 /* Default action if no close hook is in place. */ 316 if (cdp->cdr_close == NULL) 317 return OK; 318 319 /* Call the close hook. */ 320 minor = m_ptr->m_vfs_lchardriver_openclose.minor; 321 322 return cdp->cdr_close(minor); 323 } 324 325 /*===========================================================================* 326 * do_trasnfer * 327 *===========================================================================*/ 328 static int do_transfer(const struct chardriver *cdp, message *m_ptr, 329 int do_write) 330 { 331 /* Carry out a read or write task request. */ 332 devminor_t minor; 333 off_t position; 334 endpoint_t endpt; 335 cp_grant_id_t grant; 336 size_t size; 337 int flags; 338 cdev_id_t id; 339 ssize_t r; 340 341 minor = m_ptr->m_vfs_lchardriver_readwrite.minor; 342 position = m_ptr->m_vfs_lchardriver_readwrite.pos; 343 endpt = m_ptr->m_source; 344 grant = m_ptr->m_vfs_lchardriver_readwrite.grant; 345 size = m_ptr->m_vfs_lchardriver_readwrite.count; 346 flags = m_ptr->m_vfs_lchardriver_readwrite.flags; 347 id = m_ptr->m_vfs_lchardriver_readwrite.id; 348 349 /* Call the read/write hook, if the appropriate one is in place. */ 350 if (!do_write && cdp->cdr_read != NULL) 351 r = cdp->cdr_read(minor, position, endpt, grant, size, flags, id); 352 else if (do_write && cdp->cdr_write != NULL) 353 r = cdp->cdr_write(minor, position, endpt, grant, size, flags, id); 354 else 355 r = EIO; /* Default action if no read/write hook is in place. */ 356 357 return r; 358 } 359 360 /*===========================================================================* 361 * do_ioctl * 362 *===========================================================================*/ 363 static int do_ioctl(const struct chardriver *cdp, message *m_ptr) 364 { 365 /* Carry out an I/O control task request. */ 366 devminor_t minor; 367 unsigned long request; 368 cp_grant_id_t grant; 369 endpoint_t endpt, user_endpt; 370 int flags; 371 cdev_id_t id; 372 373 /* Default action if no ioctl hook is in place. */ 374 if (cdp->cdr_ioctl == NULL) 375 return ENOTTY; 376 377 /* Call the ioctl hook. */ 378 minor = m_ptr->m_vfs_lchardriver_readwrite.minor; 379 request = m_ptr->m_vfs_lchardriver_readwrite.request; 380 endpt = m_ptr->m_source; 381 grant = m_ptr->m_vfs_lchardriver_readwrite.grant; 382 flags = m_ptr->m_vfs_lchardriver_readwrite.flags; 383 user_endpt = m_ptr->m_vfs_lchardriver_readwrite.user; 384 id = m_ptr->m_vfs_lchardriver_readwrite.id; 385 386 return cdp->cdr_ioctl(minor, request, endpt, grant, flags, user_endpt, id); 387 } 388 389 /*===========================================================================* 390 * do_cancel * 391 *===========================================================================*/ 392 static int do_cancel(const struct chardriver *cdp, message *m_ptr) 393 { 394 /* Cancel a suspended (read, write, ioctl) task request. The original request 395 * may already have finished, in which case no reply should be sent. 396 */ 397 devminor_t minor; 398 endpoint_t endpt; 399 cdev_id_t id; 400 401 /* Default action if no cancel hook is in place: let the request finish. */ 402 if (cdp->cdr_cancel == NULL) 403 return EDONTREPLY; 404 405 /* Call the cancel hook. */ 406 minor = m_ptr->m_vfs_lchardriver_cancel.minor; 407 endpt = m_ptr->m_source; 408 id = m_ptr->m_vfs_lchardriver_cancel.id; 409 410 return cdp->cdr_cancel(minor, endpt, id); 411 } 412 413 /*===========================================================================* 414 * do_select * 415 *===========================================================================*/ 416 static int do_select(const struct chardriver *cdp, message *m_ptr) 417 { 418 /* Perform a select query on a minor device. */ 419 devminor_t minor; 420 unsigned int ops; 421 endpoint_t endpt; 422 423 /* Default action if no select hook is in place. */ 424 if (cdp->cdr_select == NULL) 425 return EBADF; 426 427 /* Call the select hook. */ 428 minor = m_ptr->m_vfs_lchardriver_select.minor; 429 ops = m_ptr->m_vfs_lchardriver_select.ops; 430 endpt = m_ptr->m_source; 431 432 return cdp->cdr_select(minor, ops, endpt); 433 } 434 435 /*===========================================================================* 436 * do_block_open * 437 *===========================================================================*/ 438 static void do_block_open(message *m_ptr, int ipc_status) 439 { 440 /* Reply to a block driver open request stating there is no such device. */ 441 message m_reply; 442 443 memset(&m_reply, 0, sizeof(m_reply)); 444 445 m_reply.m_type = BDEV_REPLY; 446 m_reply.m_lblockdriver_lbdev_reply.status = ENXIO; 447 m_reply.m_lblockdriver_lbdev_reply.id = m_ptr->m_lbdev_lblockdriver_msg.id; 448 449 send_reply(m_ptr->m_source, &m_reply, ipc_status); 450 } 451 452 /*===========================================================================* 453 * chardriver_process * 454 *===========================================================================*/ 455 void chardriver_process(const struct chardriver *cdp, message *m_ptr, 456 int ipc_status) 457 { 458 /* Call the appropiate driver function, based on the type of request. Send a 459 * reply to the caller if necessary. 460 */ 461 int r; 462 463 /* Check for notifications first. We never reply to notifications. */ 464 if (is_ipc_notify(ipc_status)) { 465 switch (_ENDPOINT_P(m_ptr->m_source)) { 466 case HARDWARE: 467 if (cdp->cdr_intr) 468 cdp->cdr_intr(m_ptr->m_notify.interrupts); 469 break; 470 471 case CLOCK: 472 if (cdp->cdr_alarm) 473 cdp->cdr_alarm(m_ptr->m_notify.timestamp); 474 break; 475 476 default: 477 if (cdp->cdr_other) 478 cdp->cdr_other(m_ptr, ipc_status); 479 } 480 481 return; /* do not send a reply */ 482 } 483 484 /* Reply to block driver open requests with an error code. Otherwise, if 485 * someone creates a block device node for a character driver, opening that 486 * device node will cause the corresponding VFS thread to block forever. 487 */ 488 if (m_ptr->m_type == BDEV_OPEN) { 489 do_block_open(m_ptr, ipc_status); 490 491 return; 492 } 493 494 if (IS_CDEV_RQ(m_ptr->m_type)) { 495 int minor; 496 497 /* Try to retrieve minor device number */ 498 r = chardriver_get_minor(m_ptr, &minor); 499 500 if (OK != r) 501 return; 502 503 /* We might get spurious requests if the driver has been restarted. 504 * Deny any requests on devices that have not previously been opened. 505 */ 506 if (!is_open_dev(minor)) { 507 /* Ignore spurious requests for unopened devices. */ 508 if (m_ptr->m_type != CDEV_OPEN) 509 return; /* do not send a reply */ 510 511 /* Mark the device as opened otherwise. */ 512 set_open_dev(minor); 513 } 514 } 515 516 /* Call the appropriate function(s) for this request. */ 517 switch (m_ptr->m_type) { 518 case CDEV_OPEN: r = do_open(cdp, m_ptr); break; 519 case CDEV_CLOSE: r = do_close(cdp, m_ptr); break; 520 case CDEV_READ: r = do_transfer(cdp, m_ptr, FALSE); break; 521 case CDEV_WRITE: r = do_transfer(cdp, m_ptr, TRUE); break; 522 case CDEV_IOCTL: r = do_ioctl(cdp, m_ptr); break; 523 case CDEV_CANCEL: r = do_cancel(cdp, m_ptr); break; 524 case CDEV_SELECT: r = do_select(cdp, m_ptr); break; 525 default: 526 if (cdp->cdr_other) 527 cdp->cdr_other(m_ptr, ipc_status); 528 return; /* do not send a reply */ 529 } 530 531 chardriver_reply(m_ptr, ipc_status, r); 532 } 533 534 /*===========================================================================* 535 * chardriver_terminate * 536 *===========================================================================*/ 537 void chardriver_terminate(void) 538 { 539 /* Break out of the main loop after finishing the current request. */ 540 541 running = FALSE; 542 543 sef_cancel(); 544 } 545 546 /*===========================================================================* 547 * chardriver_task * 548 *===========================================================================*/ 549 void chardriver_task(const struct chardriver *cdp) 550 { 551 /* Main program of any character device driver task. */ 552 int r, ipc_status; 553 message mess; 554 555 running = TRUE; 556 557 /* Here is the main loop of the character driver task. It waits for a 558 * message, carries it out, and sends a reply. 559 */ 560 while (running) { 561 if ((r = sef_receive_status(ANY, &mess, &ipc_status)) != OK) { 562 if (r == EINTR && !running) 563 break; 564 565 panic("chardriver: sef_receive_status failed: %d", r); 566 } 567 568 chardriver_process(cdp, &mess, ipc_status); 569 } 570 } 571 572 /*===========================================================================* 573 * chardriver_get_minor * 574 *===========================================================================*/ 575 int chardriver_get_minor(const message *m, devminor_t *minor) 576 { 577 assert(NULL != m); 578 assert(NULL != minor); 579 580 switch(m->m_type) 581 { 582 case CDEV_OPEN: 583 case CDEV_CLOSE: 584 *minor = m->m_vfs_lchardriver_openclose.minor; 585 return OK; 586 case CDEV_CANCEL: 587 *minor = m->m_vfs_lchardriver_cancel.minor; 588 return OK; 589 case CDEV_SELECT: 590 *minor = m->m_vfs_lchardriver_select.minor; 591 return OK; 592 case CDEV_READ: 593 case CDEV_WRITE: 594 case CDEV_IOCTL: 595 *minor = m->m_vfs_lchardriver_readwrite.minor; 596 return OK; 597 default: 598 return EINVAL; 599 } 600 } 601