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