1433d6423SLionel Sambuc /* This file contains the device independent character driver interface. 2433d6423SLionel Sambuc * 3433d6423SLionel Sambuc * Character drivers support the following requests. Message format m10 is 4433d6423SLionel Sambuc * used. Field names are prefixed with CDEV_. Separate field names are used for 5433d6423SLionel Sambuc * the "access", "ops", "user", and "request" fields. 6433d6423SLionel Sambuc * 7433d6423SLionel Sambuc * m_type MINOR GRANT COUNT FLAGS ID POS_LO POS_HI 8433d6423SLionel Sambuc * +-------------+-------+--------+-------+-------+------+---------+--------+ 9433d6423SLionel Sambuc * | CDEV_OPEN | minor | access | user | | id | | | 10433d6423SLionel Sambuc * |-------------+-------+--------+-------+-------+------+---------+--------| 11433d6423SLionel Sambuc * | CDEV_CLOSE | minor | | | | id | | | 12433d6423SLionel Sambuc * |-------------+-------+--------+-------+-------+------+---------+--------| 13433d6423SLionel Sambuc * | CDEV_READ | minor | grant | bytes | flags | id | position | 14433d6423SLionel Sambuc * |-------------+-------+--------+-------+-------+------+---------+--------| 15433d6423SLionel Sambuc * | CDEV_WRITE | minor | grant | bytes | flags | id | position | 16433d6423SLionel Sambuc * |-------------+-------+--------+-------+-------+------+---------+--------| 17433d6423SLionel Sambuc * | CDEV_IOCTL | minor | grant | user | flags | id | request | | 18433d6423SLionel Sambuc * |-------------+-------+--------+-------+-------+------+---------+--------| 19433d6423SLionel Sambuc * | CDEV_CANCEL | minor | | | | id | | | 20433d6423SLionel Sambuc * |-------------+-------+--------+-------+-------+------+---------+--------| 21433d6423SLionel Sambuc * | CDEV_SELECT | minor | ops | | | | | | 22433d6423SLionel Sambuc * -------------------------------------------------------------------------- 23433d6423SLionel Sambuc * 24433d6423SLionel Sambuc * The following reply messages are used. 25433d6423SLionel Sambuc * 26433d6423SLionel Sambuc * m_type MINOR STATUS ID 27433d6423SLionel Sambuc * +-----------------+-------+--------+-----+-----+------+---------+--------+ 28433d6423SLionel Sambuc * | CDEV_REPLY | | status | | | id | | | 29433d6423SLionel Sambuc * |-----------------+-------+--------+-----+-----+------+---------+--------| 30433d6423SLionel Sambuc * | CDEV_SEL1_REPLY | minor | status | | | | | | 31433d6423SLionel Sambuc * |-----------------+-------+--------+-----+-----+------+---------+--------| 32433d6423SLionel Sambuc * | CDEV_SEL2_REPLY | minor | status | | | | | | 33433d6423SLionel Sambuc * -------------------------------------------------------------------------- 34433d6423SLionel Sambuc * 35433d6423SLionel Sambuc * Changes: 36433d6423SLionel Sambuc * Sep 01, 2013 complete rewrite of the API (D.C. van Moolenboek) 37433d6423SLionel Sambuc * Aug 20, 2013 retire synchronous protocol (D.C. van Moolenbroek) 38433d6423SLionel Sambuc * Oct 16, 2011 split character and block protocol (D.C. van Moolenbroek) 39433d6423SLionel Sambuc * Aug 27, 2011 move common functions into driver.c (A. Welzel) 40433d6423SLionel Sambuc * Jul 25, 2005 added SYS_SIG type for signals (Jorrit N. Herder) 41433d6423SLionel Sambuc * Sep 15, 2004 added SYN_ALARM type for timeouts (Jorrit N. Herder) 42433d6423SLionel Sambuc * Jul 23, 2004 removed kernel dependencies (Jorrit N. Herder) 43433d6423SLionel Sambuc * Apr 02, 1992 constructed from AT wini and floppy driver (Kees J. Bot) 44433d6423SLionel Sambuc */ 45433d6423SLionel Sambuc 46433d6423SLionel Sambuc #include <assert.h> 47433d6423SLionel Sambuc 48433d6423SLionel Sambuc #include <minix/drivers.h> 49433d6423SLionel Sambuc #include <minix/chardriver.h> 50433d6423SLionel Sambuc #include <minix/ds.h> 51433d6423SLionel Sambuc 52433d6423SLionel Sambuc static int running; 53433d6423SLionel Sambuc 54433d6423SLionel Sambuc /* Management data for opened devices. */ 55433d6423SLionel Sambuc static devminor_t open_devs[MAX_NR_OPEN_DEVICES]; 56433d6423SLionel Sambuc static int next_open_devs_slot = 0; 57433d6423SLionel Sambuc 58433d6423SLionel Sambuc /*===========================================================================* 59433d6423SLionel Sambuc * clear_open_devs * 60433d6423SLionel Sambuc *===========================================================================*/ 61433d6423SLionel Sambuc static void clear_open_devs(void) 62433d6423SLionel Sambuc { 63433d6423SLionel Sambuc /* Reset the set of previously opened minor devices. */ 64433d6423SLionel Sambuc next_open_devs_slot = 0; 65433d6423SLionel Sambuc } 66433d6423SLionel Sambuc 67433d6423SLionel Sambuc /*===========================================================================* 68433d6423SLionel Sambuc * is_open_dev * 69433d6423SLionel Sambuc *===========================================================================*/ 70433d6423SLionel Sambuc static int is_open_dev(devminor_t minor) 71433d6423SLionel Sambuc { 72433d6423SLionel Sambuc /* Check whether the given minor device has previously been opened. */ 73433d6423SLionel Sambuc int i; 74433d6423SLionel Sambuc 75433d6423SLionel Sambuc for (i = 0; i < next_open_devs_slot; i++) 76433d6423SLionel Sambuc if (open_devs[i] == minor) 77433d6423SLionel Sambuc return TRUE; 78433d6423SLionel Sambuc 79433d6423SLionel Sambuc return FALSE; 80433d6423SLionel Sambuc } 81433d6423SLionel Sambuc 82433d6423SLionel Sambuc /*===========================================================================* 83433d6423SLionel Sambuc * set_open_dev * 84433d6423SLionel Sambuc *===========================================================================*/ 85433d6423SLionel Sambuc static void set_open_dev(devminor_t minor) 86433d6423SLionel Sambuc { 87433d6423SLionel Sambuc /* Mark the given minor device as having been opened. */ 88433d6423SLionel Sambuc 89433d6423SLionel Sambuc if (next_open_devs_slot >= MAX_NR_OPEN_DEVICES) 90433d6423SLionel Sambuc panic("out of slots for open devices"); 91433d6423SLionel Sambuc 92433d6423SLionel Sambuc open_devs[next_open_devs_slot] = minor; 93433d6423SLionel Sambuc next_open_devs_slot++; 94433d6423SLionel Sambuc } 95433d6423SLionel Sambuc 96433d6423SLionel Sambuc /*===========================================================================* 97433d6423SLionel Sambuc * chardriver_announce * 98433d6423SLionel Sambuc *===========================================================================*/ 99433d6423SLionel Sambuc void chardriver_announce(void) 100433d6423SLionel Sambuc { 101433d6423SLionel Sambuc /* Announce we are up after a fresh start or restart. */ 102433d6423SLionel Sambuc int r; 103433d6423SLionel Sambuc char key[DS_MAX_KEYLEN]; 104433d6423SLionel Sambuc char label[DS_MAX_KEYLEN]; 10565f76edbSDavid van Moolenbroek const char *driver_prefix = "drv.chr."; 106433d6423SLionel Sambuc 107433d6423SLionel Sambuc /* Callers are allowed to use ipc_sendrec to communicate with drivers. 108433d6423SLionel Sambuc * For this reason, there may blocked callers when a driver restarts. 109433d6423SLionel Sambuc * Ask the kernel to unblock them (if any). 110433d6423SLionel Sambuc */ 11141022be1SCristiano Giuffrida if ((r = sys_statectl(SYS_STATE_CLEAR_IPC_REFS, 0, 0)) != OK) 112433d6423SLionel Sambuc panic("chardriver_announce: sys_statectl failed: %d", r); 113433d6423SLionel Sambuc 114433d6423SLionel Sambuc /* Publish a driver up event. */ 115433d6423SLionel Sambuc if ((r = ds_retrieve_label_name(label, sef_self())) != OK) 116433d6423SLionel Sambuc panic("chardriver_announce: unable to get own label: %d", r); 117433d6423SLionel Sambuc 118433d6423SLionel Sambuc snprintf(key, DS_MAX_KEYLEN, "%s%s", driver_prefix, label); 119433d6423SLionel Sambuc if ((r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE)) != OK) 120433d6423SLionel Sambuc panic("chardriver_announce: unable to publish driver up event: %d", r); 121433d6423SLionel Sambuc 122433d6423SLionel Sambuc /* Expect an open for any device before serving regular driver requests. */ 123433d6423SLionel Sambuc clear_open_devs(); 124433d6423SLionel Sambuc } 125433d6423SLionel Sambuc 126433d6423SLionel Sambuc /*===========================================================================* 127433d6423SLionel Sambuc * chardriver_reply_task * 128433d6423SLionel Sambuc *===========================================================================*/ 129433d6423SLionel Sambuc void chardriver_reply_task(endpoint_t endpt, cdev_id_t id, int r) 130433d6423SLionel Sambuc { 131433d6423SLionel Sambuc /* Reply to a (read, write, ioctl) task request that was suspended earlier. 132433d6423SLionel Sambuc * Not-so-well-written drivers may use this function to send a reply to a 133433d6423SLionel Sambuc * request that is being processed right now, and then return EDONTREPLY later. 134433d6423SLionel Sambuc */ 135433d6423SLionel Sambuc message m_reply; 136433d6423SLionel Sambuc 137433d6423SLionel Sambuc if (r == EDONTREPLY || r == SUSPEND) 138433d6423SLionel Sambuc panic("chardriver: bad task reply: %d", r); 139433d6423SLionel Sambuc 140433d6423SLionel Sambuc memset(&m_reply, 0, sizeof(m_reply)); 141433d6423SLionel Sambuc 142433d6423SLionel Sambuc m_reply.m_type = CDEV_REPLY; 143433d6423SLionel Sambuc m_reply.m_lchardriver_vfs_reply.status = r; 144433d6423SLionel Sambuc m_reply.m_lchardriver_vfs_reply.id = id; 145433d6423SLionel Sambuc 146433d6423SLionel Sambuc if ((r = asynsend3(endpt, &m_reply, AMF_NOREPLY)) != OK) 147433d6423SLionel Sambuc printf("chardriver_reply_task: send to %d failed: %d\n", endpt, r); 148433d6423SLionel Sambuc } 149433d6423SLionel Sambuc 150433d6423SLionel Sambuc /*===========================================================================* 151433d6423SLionel Sambuc * chardriver_reply_select * 152433d6423SLionel Sambuc *===========================================================================*/ 153433d6423SLionel Sambuc void chardriver_reply_select(endpoint_t endpt, devminor_t minor, int r) 154433d6423SLionel Sambuc { 155433d6423SLionel Sambuc /* Reply to a select request with a status update. This must not be used to 156433d6423SLionel Sambuc * reply to a select request that is being processed right now. 157433d6423SLionel Sambuc */ 158433d6423SLionel Sambuc message m_reply; 159433d6423SLionel Sambuc 160433d6423SLionel Sambuc /* Replying with an error is allowed (if unusual). */ 161433d6423SLionel Sambuc if (r == EDONTREPLY || r == SUSPEND) 162433d6423SLionel Sambuc panic("chardriver: bad select reply: %d", r); 163433d6423SLionel Sambuc 164433d6423SLionel Sambuc memset(&m_reply, 0, sizeof(m_reply)); 165433d6423SLionel Sambuc 166433d6423SLionel Sambuc m_reply.m_type = CDEV_SEL2_REPLY; 167433d6423SLionel Sambuc m_reply.m_lchardriver_vfs_sel2.minor = minor; 168433d6423SLionel Sambuc m_reply.m_lchardriver_vfs_sel2.status = r; 169433d6423SLionel Sambuc 170433d6423SLionel Sambuc if ((r = asynsend3(endpt, &m_reply, AMF_NOREPLY)) != OK) 171433d6423SLionel Sambuc printf("chardriver_reply_select: send to %d failed: %d\n", endpt, r); 172433d6423SLionel Sambuc } 173433d6423SLionel Sambuc 174433d6423SLionel Sambuc /*===========================================================================* 175433d6423SLionel Sambuc * send_reply * 176433d6423SLionel Sambuc *===========================================================================*/ 177433d6423SLionel Sambuc static void send_reply(endpoint_t endpt, message *m_ptr, int ipc_status) 178433d6423SLionel Sambuc { 179433d6423SLionel Sambuc /* Send a reply message to a request. */ 180433d6423SLionel Sambuc int r; 181433d6423SLionel Sambuc 182433d6423SLionel Sambuc /* If we would block sending the message, send it asynchronously. */ 183433d6423SLionel Sambuc if (IPC_STATUS_CALL(ipc_status) == SENDREC) 184433d6423SLionel Sambuc r = ipc_sendnb(endpt, m_ptr); 185433d6423SLionel Sambuc else 186433d6423SLionel Sambuc r = asynsend3(endpt, m_ptr, AMF_NOREPLY); 187433d6423SLionel Sambuc 188433d6423SLionel Sambuc if (r != OK) 189433d6423SLionel Sambuc printf("chardriver: unable to send reply to %d: %d\n", endpt, r); 190433d6423SLionel Sambuc } 191433d6423SLionel Sambuc 192433d6423SLionel Sambuc /*===========================================================================* 193433d6423SLionel Sambuc * chardriver_reply * 194433d6423SLionel Sambuc *===========================================================================*/ 195433d6423SLionel Sambuc static void chardriver_reply(message *mess, int ipc_status, int r) 196433d6423SLionel Sambuc { 197433d6423SLionel Sambuc /* Prepare and send a reply message. */ 198433d6423SLionel Sambuc message reply_mess; 199433d6423SLionel Sambuc 200433d6423SLionel Sambuc /* If the EDONTREPLY pseudo-reply is given, we do not reply. This is however 201433d6423SLionel Sambuc * allowed only for blocking task calls. Perform a sanity check. 202433d6423SLionel Sambuc */ 203433d6423SLionel Sambuc if (r == EDONTREPLY) { 204433d6423SLionel Sambuc switch (mess->m_type) { 205433d6423SLionel Sambuc case CDEV_READ: 206433d6423SLionel Sambuc case CDEV_WRITE: 207433d6423SLionel Sambuc case CDEV_IOCTL: 208433d6423SLionel Sambuc /* FIXME: we should be able to check FLAGS against 209433d6423SLionel Sambuc * CDEV_NONBLOCK here, but in practice, several drivers do not 210433d6423SLionel Sambuc * send a reply through this path (eg TTY) or simply do not 211433d6423SLionel Sambuc * implement nonblocking calls properly (eg audio, LWIP). 212433d6423SLionel Sambuc */ 213433d6423SLionel Sambuc #if 0 214433d6423SLionel Sambuc if (mess->m_vfs_lchardriver_readwrite.flags & CDEV_NONBLOCK) 215433d6423SLionel Sambuc panic("chardriver: cannot suspend nonblocking I/O"); 216433d6423SLionel Sambuc #endif 217433d6423SLionel Sambuc /*fall-through*/ 218433d6423SLionel Sambuc case CDEV_CANCEL: 219433d6423SLionel Sambuc return; /* alright */ 220433d6423SLionel Sambuc default: 221433d6423SLionel Sambuc panic("chardriver: cannot suspend request %d", mess->m_type); 222433d6423SLionel Sambuc } 223433d6423SLionel Sambuc } 224433d6423SLionel Sambuc 225433d6423SLionel Sambuc if (r == SUSPEND) 226433d6423SLionel Sambuc panic("chardriver: SUSPEND should not be used anymore"); 227433d6423SLionel Sambuc 228433d6423SLionel Sambuc /* Do not reply with ERESTART. The only possible caller, VFS, will find out 229433d6423SLionel Sambuc * through other means when we have restarted, and is not (fully) ready to 230433d6423SLionel Sambuc * deal with ERESTART errors. 231433d6423SLionel Sambuc */ 232433d6423SLionel Sambuc if (r == ERESTART) 233433d6423SLionel Sambuc return; 234433d6423SLionel Sambuc 235433d6423SLionel Sambuc memset(&reply_mess, 0, sizeof(reply_mess)); 236433d6423SLionel Sambuc 237433d6423SLionel Sambuc switch (mess->m_type) { 238433d6423SLionel Sambuc case CDEV_OPEN: 239433d6423SLionel Sambuc case CDEV_CLOSE: 240433d6423SLionel Sambuc reply_mess.m_type = CDEV_REPLY; 241433d6423SLionel Sambuc reply_mess.m_lchardriver_vfs_reply.status = r; 242433d6423SLionel Sambuc reply_mess.m_lchardriver_vfs_reply.id = 243433d6423SLionel Sambuc mess->m_vfs_lchardriver_openclose.id; 244433d6423SLionel Sambuc break; 245433d6423SLionel Sambuc 246433d6423SLionel Sambuc case CDEV_READ: 247433d6423SLionel Sambuc case CDEV_WRITE: 248433d6423SLionel Sambuc case CDEV_IOCTL: 249433d6423SLionel Sambuc reply_mess.m_type = CDEV_REPLY; 250433d6423SLionel Sambuc reply_mess.m_lchardriver_vfs_reply.status = r; 251433d6423SLionel Sambuc reply_mess.m_lchardriver_vfs_reply.id = 252433d6423SLionel Sambuc mess->m_vfs_lchardriver_readwrite.id; 253433d6423SLionel Sambuc break; 254433d6423SLionel Sambuc 255433d6423SLionel Sambuc case CDEV_CANCEL: /* For cancel, this is a reply to the original request! */ 256433d6423SLionel Sambuc reply_mess.m_type = CDEV_REPLY; 257433d6423SLionel Sambuc reply_mess.m_lchardriver_vfs_reply.status = r; 258433d6423SLionel Sambuc reply_mess.m_lchardriver_vfs_reply.id = 259433d6423SLionel Sambuc mess->m_vfs_lchardriver_cancel.id; 260433d6423SLionel Sambuc break; 261433d6423SLionel Sambuc 262433d6423SLionel Sambuc case CDEV_SELECT: 263433d6423SLionel Sambuc reply_mess.m_type = CDEV_SEL1_REPLY; 264433d6423SLionel Sambuc reply_mess.m_lchardriver_vfs_sel1.status = r; 265433d6423SLionel Sambuc reply_mess.m_lchardriver_vfs_sel1.minor = 266433d6423SLionel Sambuc mess->m_vfs_lchardriver_select.minor; 267433d6423SLionel Sambuc break; 268433d6423SLionel Sambuc 269433d6423SLionel Sambuc default: 270433d6423SLionel Sambuc panic("chardriver: unknown request %d", mess->m_type); 271433d6423SLionel Sambuc } 272433d6423SLionel Sambuc 273433d6423SLionel Sambuc send_reply(mess->m_source, &reply_mess, ipc_status); 274433d6423SLionel Sambuc } 275433d6423SLionel Sambuc 276433d6423SLionel Sambuc /*===========================================================================* 277433d6423SLionel Sambuc * do_open * 278433d6423SLionel Sambuc *===========================================================================*/ 279433d6423SLionel Sambuc static int do_open(struct chardriver *cdp, message *m_ptr) 280433d6423SLionel Sambuc { 281433d6423SLionel Sambuc /* Open a minor device. */ 282433d6423SLionel Sambuc endpoint_t user_endpt; 283433d6423SLionel Sambuc devminor_t minor; 284*7c48de6cSDavid van Moolenbroek int r, bits; 285433d6423SLionel Sambuc 286433d6423SLionel Sambuc /* Default action if no open hook is in place. */ 287433d6423SLionel Sambuc if (cdp->cdr_open == NULL) 288433d6423SLionel Sambuc return OK; 289433d6423SLionel Sambuc 290433d6423SLionel Sambuc /* Call the open hook. */ 291433d6423SLionel Sambuc minor = m_ptr->m_vfs_lchardriver_openclose.minor; 292*7c48de6cSDavid van Moolenbroek bits = m_ptr->m_vfs_lchardriver_openclose.access; 293433d6423SLionel Sambuc user_endpt = m_ptr->m_vfs_lchardriver_openclose.user; 294433d6423SLionel Sambuc 295*7c48de6cSDavid van Moolenbroek r = cdp->cdr_open(minor, bits, user_endpt); 296433d6423SLionel Sambuc 297433d6423SLionel Sambuc /* If the device has been cloned, mark the new minor as open too. */ 298433d6423SLionel Sambuc if (r >= 0 && (r & CDEV_CLONED)) { 299433d6423SLionel Sambuc minor = r & ~(CDEV_CLONED | CDEV_CTTY); 300433d6423SLionel Sambuc if (!is_open_dev(minor)) 301433d6423SLionel Sambuc set_open_dev(minor); 302433d6423SLionel Sambuc } 303433d6423SLionel Sambuc 304433d6423SLionel Sambuc return r; 305433d6423SLionel Sambuc } 306433d6423SLionel Sambuc 307433d6423SLionel Sambuc /*===========================================================================* 308433d6423SLionel Sambuc * do_close * 309433d6423SLionel Sambuc *===========================================================================*/ 310433d6423SLionel Sambuc static int do_close(struct chardriver *cdp, message *m_ptr) 311433d6423SLionel Sambuc { 312433d6423SLionel Sambuc /* Close a minor device. */ 313433d6423SLionel Sambuc devminor_t minor; 314433d6423SLionel Sambuc 315433d6423SLionel Sambuc /* Default action if no close hook is in place. */ 316433d6423SLionel Sambuc if (cdp->cdr_close == NULL) 317433d6423SLionel Sambuc return OK; 318433d6423SLionel Sambuc 319433d6423SLionel Sambuc /* Call the close hook. */ 320433d6423SLionel Sambuc minor = m_ptr->m_vfs_lchardriver_openclose.minor; 321433d6423SLionel Sambuc 322433d6423SLionel Sambuc return cdp->cdr_close(minor); 323433d6423SLionel Sambuc } 324433d6423SLionel Sambuc 325433d6423SLionel Sambuc /*===========================================================================* 326433d6423SLionel Sambuc * do_trasnfer * 327433d6423SLionel Sambuc *===========================================================================*/ 328433d6423SLionel Sambuc static int do_transfer(struct chardriver *cdp, message *m_ptr, int do_write) 329433d6423SLionel Sambuc { 330433d6423SLionel Sambuc /* Carry out a read or write task request. */ 331433d6423SLionel Sambuc devminor_t minor; 332433d6423SLionel Sambuc off_t position; 333433d6423SLionel Sambuc endpoint_t endpt; 334433d6423SLionel Sambuc cp_grant_id_t grant; 335433d6423SLionel Sambuc size_t size; 336433d6423SLionel Sambuc int flags; 337433d6423SLionel Sambuc cdev_id_t id; 338433d6423SLionel Sambuc ssize_t r; 339433d6423SLionel Sambuc 340433d6423SLionel Sambuc minor = m_ptr->m_vfs_lchardriver_readwrite.minor; 341433d6423SLionel Sambuc position = m_ptr->m_vfs_lchardriver_readwrite.pos; 342433d6423SLionel Sambuc endpt = m_ptr->m_source; 343433d6423SLionel Sambuc grant = m_ptr->m_vfs_lchardriver_readwrite.grant; 344433d6423SLionel Sambuc size = m_ptr->m_vfs_lchardriver_readwrite.count; 345433d6423SLionel Sambuc flags = m_ptr->m_vfs_lchardriver_readwrite.flags; 346433d6423SLionel Sambuc id = m_ptr->m_vfs_lchardriver_readwrite.id; 347433d6423SLionel Sambuc 348433d6423SLionel Sambuc /* Call the read/write hook, if the appropriate one is in place. */ 349433d6423SLionel Sambuc if (!do_write && cdp->cdr_read != NULL) 350433d6423SLionel Sambuc r = cdp->cdr_read(minor, position, endpt, grant, size, flags, id); 351433d6423SLionel Sambuc else if (do_write && cdp->cdr_write != NULL) 352433d6423SLionel Sambuc r = cdp->cdr_write(minor, position, endpt, grant, size, flags, id); 353433d6423SLionel Sambuc else 354433d6423SLionel Sambuc r = EIO; /* Default action if no read/write hook is in place. */ 355433d6423SLionel Sambuc 356433d6423SLionel Sambuc return r; 357433d6423SLionel Sambuc } 358433d6423SLionel Sambuc 359433d6423SLionel Sambuc /*===========================================================================* 360433d6423SLionel Sambuc * do_ioctl * 361433d6423SLionel Sambuc *===========================================================================*/ 362433d6423SLionel Sambuc static int do_ioctl(struct chardriver *cdp, message *m_ptr) 363433d6423SLionel Sambuc { 364433d6423SLionel Sambuc /* Carry out an I/O control task request. */ 365433d6423SLionel Sambuc devminor_t minor; 366433d6423SLionel Sambuc unsigned long request; 367433d6423SLionel Sambuc cp_grant_id_t grant; 368433d6423SLionel Sambuc endpoint_t endpt, user_endpt; 369433d6423SLionel Sambuc int flags; 370433d6423SLionel Sambuc cdev_id_t id; 371433d6423SLionel Sambuc 372433d6423SLionel Sambuc /* Default action if no ioctl hook is in place. */ 373433d6423SLionel Sambuc if (cdp->cdr_ioctl == NULL) 374433d6423SLionel Sambuc return ENOTTY; 375433d6423SLionel Sambuc 376433d6423SLionel Sambuc /* Call the ioctl hook. */ 377433d6423SLionel Sambuc minor = m_ptr->m_vfs_lchardriver_readwrite.minor; 378433d6423SLionel Sambuc request = m_ptr->m_vfs_lchardriver_readwrite.request; 379433d6423SLionel Sambuc endpt = m_ptr->m_source; 380433d6423SLionel Sambuc grant = m_ptr->m_vfs_lchardriver_readwrite.grant; 381433d6423SLionel Sambuc flags = m_ptr->m_vfs_lchardriver_readwrite.flags; 382433d6423SLionel Sambuc user_endpt = m_ptr->m_vfs_lchardriver_readwrite.user; 383433d6423SLionel Sambuc id = m_ptr->m_vfs_lchardriver_readwrite.id; 384433d6423SLionel Sambuc 385433d6423SLionel Sambuc return cdp->cdr_ioctl(minor, request, endpt, grant, flags, user_endpt, id); 386433d6423SLionel Sambuc } 387433d6423SLionel Sambuc 388433d6423SLionel Sambuc /*===========================================================================* 389433d6423SLionel Sambuc * do_cancel * 390433d6423SLionel Sambuc *===========================================================================*/ 391433d6423SLionel Sambuc static int do_cancel(struct chardriver *cdp, message *m_ptr) 392433d6423SLionel Sambuc { 393433d6423SLionel Sambuc /* Cancel a suspended (read, write, ioctl) task request. The original request 394433d6423SLionel Sambuc * may already have finished, in which case no reply should be sent. 395433d6423SLionel Sambuc */ 396433d6423SLionel Sambuc devminor_t minor; 397433d6423SLionel Sambuc endpoint_t endpt; 398433d6423SLionel Sambuc cdev_id_t id; 399433d6423SLionel Sambuc 400433d6423SLionel Sambuc /* Default action if no cancel hook is in place: let the request finish. */ 401433d6423SLionel Sambuc if (cdp->cdr_cancel == NULL) 402433d6423SLionel Sambuc return EDONTREPLY; 403433d6423SLionel Sambuc 404433d6423SLionel Sambuc /* Call the cancel hook. */ 405433d6423SLionel Sambuc minor = m_ptr->m_vfs_lchardriver_cancel.minor; 406433d6423SLionel Sambuc endpt = m_ptr->m_source; 407433d6423SLionel Sambuc id = m_ptr->m_vfs_lchardriver_cancel.id; 408433d6423SLionel Sambuc 409433d6423SLionel Sambuc return cdp->cdr_cancel(minor, endpt, id); 410433d6423SLionel Sambuc } 411433d6423SLionel Sambuc 412433d6423SLionel Sambuc /*===========================================================================* 413433d6423SLionel Sambuc * do_select * 414433d6423SLionel Sambuc *===========================================================================*/ 415433d6423SLionel Sambuc static int do_select(struct chardriver *cdp, message *m_ptr) 416433d6423SLionel Sambuc { 417433d6423SLionel Sambuc /* Perform a select query on a minor device. */ 418433d6423SLionel Sambuc devminor_t minor; 419433d6423SLionel Sambuc unsigned int ops; 420433d6423SLionel Sambuc endpoint_t endpt; 421433d6423SLionel Sambuc 422433d6423SLionel Sambuc /* Default action if no select hook is in place. */ 423433d6423SLionel Sambuc if (cdp->cdr_select == NULL) 424433d6423SLionel Sambuc return EBADF; 425433d6423SLionel Sambuc 426433d6423SLionel Sambuc /* Call the select hook. */ 427433d6423SLionel Sambuc minor = m_ptr->m_vfs_lchardriver_select.minor; 428433d6423SLionel Sambuc ops = m_ptr->m_vfs_lchardriver_select.ops; 429433d6423SLionel Sambuc endpt = m_ptr->m_source; 430433d6423SLionel Sambuc 431433d6423SLionel Sambuc return cdp->cdr_select(minor, ops, endpt); 432433d6423SLionel Sambuc } 433433d6423SLionel Sambuc 434433d6423SLionel Sambuc /*===========================================================================* 435433d6423SLionel Sambuc * do_block_open * 436433d6423SLionel Sambuc *===========================================================================*/ 437433d6423SLionel Sambuc static void do_block_open(message *m_ptr, int ipc_status) 438433d6423SLionel Sambuc { 439433d6423SLionel Sambuc /* Reply to a block driver open request stating there is no such device. */ 440433d6423SLionel Sambuc message m_reply; 441433d6423SLionel Sambuc 442433d6423SLionel Sambuc memset(&m_reply, 0, sizeof(m_reply)); 443433d6423SLionel Sambuc 444433d6423SLionel Sambuc m_reply.m_type = BDEV_REPLY; 445433d6423SLionel Sambuc m_reply.m_lblockdriver_lbdev_reply.status = ENXIO; 446433d6423SLionel Sambuc m_reply.m_lblockdriver_lbdev_reply.id = m_ptr->m_lbdev_lblockdriver_msg.id; 447433d6423SLionel Sambuc 448433d6423SLionel Sambuc send_reply(m_ptr->m_source, &m_reply, ipc_status); 449433d6423SLionel Sambuc } 450433d6423SLionel Sambuc 451433d6423SLionel Sambuc /*===========================================================================* 452433d6423SLionel Sambuc * chardriver_process * 453433d6423SLionel Sambuc *===========================================================================*/ 454433d6423SLionel Sambuc void chardriver_process(struct chardriver *cdp, message *m_ptr, int ipc_status) 455433d6423SLionel Sambuc { 456433d6423SLionel Sambuc /* Call the appropiate driver function, based on the type of request. Send a 457433d6423SLionel Sambuc * reply to the caller if necessary. 458433d6423SLionel Sambuc */ 45965f76edbSDavid van Moolenbroek int r; 460433d6423SLionel Sambuc 461433d6423SLionel Sambuc /* Check for notifications first. We never reply to notifications. */ 462433d6423SLionel Sambuc if (is_ipc_notify(ipc_status)) { 463433d6423SLionel Sambuc switch (_ENDPOINT_P(m_ptr->m_source)) { 464433d6423SLionel Sambuc case HARDWARE: 465433d6423SLionel Sambuc if (cdp->cdr_intr) 466433d6423SLionel Sambuc cdp->cdr_intr(m_ptr->m_notify.interrupts); 467433d6423SLionel Sambuc break; 468433d6423SLionel Sambuc 469433d6423SLionel Sambuc case CLOCK: 470433d6423SLionel Sambuc if (cdp->cdr_alarm) 471433d6423SLionel Sambuc cdp->cdr_alarm(m_ptr->m_notify.timestamp); 472433d6423SLionel Sambuc break; 473433d6423SLionel Sambuc 474433d6423SLionel Sambuc default: 475433d6423SLionel Sambuc if (cdp->cdr_other) 476433d6423SLionel Sambuc cdp->cdr_other(m_ptr, ipc_status); 477433d6423SLionel Sambuc } 478433d6423SLionel Sambuc 479433d6423SLionel Sambuc return; /* do not send a reply */ 480433d6423SLionel Sambuc } 481433d6423SLionel Sambuc 482433d6423SLionel Sambuc /* Reply to block driver open requests with an error code. Otherwise, if 483433d6423SLionel Sambuc * someone creates a block device node for a character driver, opening that 484433d6423SLionel Sambuc * device node will cause the corresponding VFS thread to block forever. 485433d6423SLionel Sambuc */ 486433d6423SLionel Sambuc if (m_ptr->m_type == BDEV_OPEN) { 487433d6423SLionel Sambuc do_block_open(m_ptr, ipc_status); 488433d6423SLionel Sambuc 489433d6423SLionel Sambuc return; 490433d6423SLionel Sambuc } 491433d6423SLionel Sambuc 492433d6423SLionel Sambuc if (IS_CDEV_RQ(m_ptr->m_type)) { 493433d6423SLionel Sambuc int minor; 494433d6423SLionel Sambuc 495433d6423SLionel Sambuc /* Try to retrieve minor device number */ 496433d6423SLionel Sambuc r = chardriver_get_minor(m_ptr, &minor); 497433d6423SLionel Sambuc 498433d6423SLionel Sambuc if (OK != r) 499433d6423SLionel Sambuc return; 500433d6423SLionel Sambuc 501433d6423SLionel Sambuc /* We might get spurious requests if the driver has been restarted. 502433d6423SLionel Sambuc * Deny any requests on devices that have not previously been opened. 503433d6423SLionel Sambuc */ 504433d6423SLionel Sambuc if (!is_open_dev(minor)) { 505433d6423SLionel Sambuc /* Ignore spurious requests for unopened devices. */ 506433d6423SLionel Sambuc if (m_ptr->m_type != CDEV_OPEN) 507433d6423SLionel Sambuc return; /* do not send a reply */ 508433d6423SLionel Sambuc 509433d6423SLionel Sambuc /* Mark the device as opened otherwise. */ 510433d6423SLionel Sambuc set_open_dev(minor); 511433d6423SLionel Sambuc } 512433d6423SLionel Sambuc } 513433d6423SLionel Sambuc 514433d6423SLionel Sambuc /* Call the appropriate function(s) for this request. */ 515433d6423SLionel Sambuc switch (m_ptr->m_type) { 516433d6423SLionel Sambuc case CDEV_OPEN: r = do_open(cdp, m_ptr); break; 517433d6423SLionel Sambuc case CDEV_CLOSE: r = do_close(cdp, m_ptr); break; 518433d6423SLionel Sambuc case CDEV_READ: r = do_transfer(cdp, m_ptr, FALSE); break; 519433d6423SLionel Sambuc case CDEV_WRITE: r = do_transfer(cdp, m_ptr, TRUE); break; 520433d6423SLionel Sambuc case CDEV_IOCTL: r = do_ioctl(cdp, m_ptr); break; 521433d6423SLionel Sambuc case CDEV_CANCEL: r = do_cancel(cdp, m_ptr); break; 522433d6423SLionel Sambuc case CDEV_SELECT: r = do_select(cdp, m_ptr); break; 523433d6423SLionel Sambuc default: 524433d6423SLionel Sambuc if (cdp->cdr_other) 525433d6423SLionel Sambuc cdp->cdr_other(m_ptr, ipc_status); 526433d6423SLionel Sambuc return; /* do not send a reply */ 527433d6423SLionel Sambuc } 528433d6423SLionel Sambuc 529433d6423SLionel Sambuc chardriver_reply(m_ptr, ipc_status, r); 530433d6423SLionel Sambuc } 531433d6423SLionel Sambuc 532433d6423SLionel Sambuc /*===========================================================================* 533433d6423SLionel Sambuc * chardriver_terminate * 534433d6423SLionel Sambuc *===========================================================================*/ 535433d6423SLionel Sambuc void chardriver_terminate(void) 536433d6423SLionel Sambuc { 537433d6423SLionel Sambuc /* Break out of the main loop after finishing the current request. */ 538433d6423SLionel Sambuc 539433d6423SLionel Sambuc running = FALSE; 540433d6423SLionel Sambuc 541433d6423SLionel Sambuc sef_cancel(); 542433d6423SLionel Sambuc } 543433d6423SLionel Sambuc 544433d6423SLionel Sambuc /*===========================================================================* 545433d6423SLionel Sambuc * chardriver_task * 546433d6423SLionel Sambuc *===========================================================================*/ 547433d6423SLionel Sambuc void chardriver_task(struct chardriver *cdp) 548433d6423SLionel Sambuc { 549433d6423SLionel Sambuc /* Main program of any character device driver task. */ 550433d6423SLionel Sambuc int r, ipc_status; 551433d6423SLionel Sambuc message mess; 552433d6423SLionel Sambuc 553433d6423SLionel Sambuc running = TRUE; 554433d6423SLionel Sambuc 555433d6423SLionel Sambuc /* Here is the main loop of the character driver task. It waits for a 556433d6423SLionel Sambuc * message, carries it out, and sends a reply. 557433d6423SLionel Sambuc */ 558433d6423SLionel Sambuc while (running) { 559433d6423SLionel Sambuc if ((r = sef_receive_status(ANY, &mess, &ipc_status)) != OK) { 560433d6423SLionel Sambuc if (r == EINTR && !running) 561433d6423SLionel Sambuc break; 562433d6423SLionel Sambuc 563433d6423SLionel Sambuc panic("chardriver: sef_receive_status failed: %d", r); 564433d6423SLionel Sambuc } 565433d6423SLionel Sambuc 566433d6423SLionel Sambuc chardriver_process(cdp, &mess, ipc_status); 567433d6423SLionel Sambuc } 568433d6423SLionel Sambuc } 569433d6423SLionel Sambuc 570433d6423SLionel Sambuc /*===========================================================================* 571433d6423SLionel Sambuc * chardriver_get_minor * 572433d6423SLionel Sambuc *===========================================================================*/ 573433d6423SLionel Sambuc int chardriver_get_minor(message *m, devminor_t *minor) 574433d6423SLionel Sambuc { 575433d6423SLionel Sambuc assert(NULL != m); 576433d6423SLionel Sambuc assert(NULL != minor); 577433d6423SLionel Sambuc 578433d6423SLionel Sambuc switch(m->m_type) 579433d6423SLionel Sambuc { 580433d6423SLionel Sambuc case CDEV_OPEN: 581433d6423SLionel Sambuc case CDEV_CLOSE: 582433d6423SLionel Sambuc *minor = m->m_vfs_lchardriver_openclose.minor; 583433d6423SLionel Sambuc return OK; 584433d6423SLionel Sambuc case CDEV_CANCEL: 585433d6423SLionel Sambuc *minor = m->m_vfs_lchardriver_cancel.minor; 586433d6423SLionel Sambuc return OK; 587433d6423SLionel Sambuc case CDEV_SELECT: 588433d6423SLionel Sambuc *minor = m->m_vfs_lchardriver_select.minor; 589433d6423SLionel Sambuc return OK; 590433d6423SLionel Sambuc case CDEV_READ: 591433d6423SLionel Sambuc case CDEV_WRITE: 592433d6423SLionel Sambuc case CDEV_IOCTL: 593433d6423SLionel Sambuc *minor = m->m_vfs_lchardriver_readwrite.minor; 594433d6423SLionel Sambuc return OK; 595433d6423SLionel Sambuc default: 596433d6423SLionel Sambuc return EINVAL; 597433d6423SLionel Sambuc } 598433d6423SLionel Sambuc } 599