Remove building with NOCRYPTO option
[minix3.git] / minix / lib / libchardriver / chardriver.c
blob0f47dbff20dc69ac9fbda6d41934117e34857aec
1 /* This file contains the device independent character driver interface.
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.
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 * --------------------------------------------------------------------------
24 * The following reply messages are used.
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 * --------------------------------------------------------------------------
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)
46 #include <assert.h>
48 #include <minix/drivers.h>
49 #include <minix/chardriver.h>
50 #include <minix/ds.h>
52 static int running;
54 /* Management data for opened devices. */
55 static devminor_t open_devs[MAX_NR_OPEN_DEVICES];
56 static int next_open_devs_slot = 0;
58 /*===========================================================================*
59 * clear_open_devs *
60 *===========================================================================*/
61 static void clear_open_devs(void)
63 /* Reset the set of previously opened minor devices. */
64 next_open_devs_slot = 0;
67 /*===========================================================================*
68 * is_open_dev *
69 *===========================================================================*/
70 static int is_open_dev(devminor_t minor)
72 /* Check whether the given minor device has previously been opened. */
73 int i;
75 for (i = 0; i < next_open_devs_slot; i++)
76 if (open_devs[i] == minor)
77 return TRUE;
79 return FALSE;
82 /*===========================================================================*
83 * set_open_dev *
84 *===========================================================================*/
85 static void set_open_dev(devminor_t minor)
87 /* Mark the given minor device as having been opened. */
89 if (next_open_devs_slot >= MAX_NR_OPEN_DEVICES)
90 panic("out of slots for open devices");
92 open_devs[next_open_devs_slot] = minor;
93 next_open_devs_slot++;
96 /*===========================================================================*
97 * chardriver_announce *
98 *===========================================================================*/
99 void chardriver_announce(void)
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.";
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).
111 if ((r = sys_statectl(SYS_STATE_CLEAR_IPC_REFS, 0, 0)) != OK)
112 panic("chardriver_announce: sys_statectl failed: %d", r);
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);
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);
122 /* Expect an open for any device before serving regular driver requests. */
123 clear_open_devs();
126 /*===========================================================================*
127 * chardriver_reply_task *
128 *===========================================================================*/
129 void chardriver_reply_task(endpoint_t endpt, cdev_id_t id, int r)
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.
135 message m_reply;
137 if (r == EDONTREPLY || r == SUSPEND)
138 panic("chardriver: bad task reply: %d", r);
140 memset(&m_reply, 0, sizeof(m_reply));
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;
146 if ((r = asynsend3(endpt, &m_reply, AMF_NOREPLY)) != OK)
147 printf("chardriver_reply_task: send to %d failed: %d\n", endpt, r);
150 /*===========================================================================*
151 * chardriver_reply_select *
152 *===========================================================================*/
153 void chardriver_reply_select(endpoint_t endpt, devminor_t minor, int r)
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.
158 message m_reply;
160 /* Replying with an error is allowed (if unusual). */
161 if (r == EDONTREPLY || r == SUSPEND)
162 panic("chardriver: bad select reply: %d", r);
164 memset(&m_reply, 0, sizeof(m_reply));
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;
170 if ((r = asynsend3(endpt, &m_reply, AMF_NOREPLY)) != OK)
171 printf("chardriver_reply_select: send to %d failed: %d\n", endpt, r);
174 /*===========================================================================*
175 * send_reply *
176 *===========================================================================*/
177 static void send_reply(endpoint_t endpt, message *m_ptr, int ipc_status)
179 /* Send a reply message to a request. */
180 int r;
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);
188 if (r != OK)
189 printf("chardriver: unable to send reply to %d: %d\n", endpt, r);
192 /*===========================================================================*
193 * chardriver_reply *
194 *===========================================================================*/
195 static void chardriver_reply(message *mess, int ipc_status, int r)
197 /* Prepare and send a reply message. */
198 message reply_mess;
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.
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).
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);
225 if (r == SUSPEND)
226 panic("chardriver: SUSPEND should not be used anymore");
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.
232 if (r == ERESTART)
233 return;
235 memset(&reply_mess, 0, sizeof(reply_mess));
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;
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;
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;
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;
269 default:
270 panic("chardriver: unknown request %d", mess->m_type);
273 send_reply(mess->m_source, &reply_mess, ipc_status);
276 /*===========================================================================*
277 * do_open *
278 *===========================================================================*/
279 static int do_open(const struct chardriver *cdp, message *m_ptr)
281 /* Open a minor device. */
282 endpoint_t user_endpt;
283 devminor_t minor;
284 int r, bits;
286 /* Default action if no open hook is in place. */
287 if (cdp->cdr_open == NULL)
288 return OK;
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;
295 r = cdp->cdr_open(minor, bits, user_endpt);
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);
304 return r;
307 /*===========================================================================*
308 * do_close *
309 *===========================================================================*/
310 static int do_close(const struct chardriver *cdp, message *m_ptr)
312 /* Close a minor device. */
313 devminor_t minor;
315 /* Default action if no close hook is in place. */
316 if (cdp->cdr_close == NULL)
317 return OK;
319 /* Call the close hook. */
320 minor = m_ptr->m_vfs_lchardriver_openclose.minor;
322 return cdp->cdr_close(minor);
325 /*===========================================================================*
326 * do_trasnfer *
327 *===========================================================================*/
328 static int do_transfer(const struct chardriver *cdp, message *m_ptr,
329 int do_write)
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;
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;
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. */
357 return r;
360 /*===========================================================================*
361 * do_ioctl *
362 *===========================================================================*/
363 static int do_ioctl(const struct chardriver *cdp, message *m_ptr)
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;
373 /* Default action if no ioctl hook is in place. */
374 if (cdp->cdr_ioctl == NULL)
375 return ENOTTY;
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;
386 return cdp->cdr_ioctl(minor, request, endpt, grant, flags, user_endpt, id);
389 /*===========================================================================*
390 * do_cancel *
391 *===========================================================================*/
392 static int do_cancel(const struct chardriver *cdp, message *m_ptr)
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.
397 devminor_t minor;
398 endpoint_t endpt;
399 cdev_id_t id;
401 /* Default action if no cancel hook is in place: let the request finish. */
402 if (cdp->cdr_cancel == NULL)
403 return EDONTREPLY;
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;
410 return cdp->cdr_cancel(minor, endpt, id);
413 /*===========================================================================*
414 * do_select *
415 *===========================================================================*/
416 static int do_select(const struct chardriver *cdp, message *m_ptr)
418 /* Perform a select query on a minor device. */
419 devminor_t minor;
420 unsigned int ops;
421 endpoint_t endpt;
423 /* Default action if no select hook is in place. */
424 if (cdp->cdr_select == NULL)
425 return EBADF;
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;
432 return cdp->cdr_select(minor, ops, endpt);
435 /*===========================================================================*
436 * do_block_open *
437 *===========================================================================*/
438 static void do_block_open(message *m_ptr, int ipc_status)
440 /* Reply to a block driver open request stating there is no such device. */
441 message m_reply;
443 memset(&m_reply, 0, sizeof(m_reply));
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;
449 send_reply(m_ptr->m_source, &m_reply, ipc_status);
452 /*===========================================================================*
453 * chardriver_process *
454 *===========================================================================*/
455 void chardriver_process(const struct chardriver *cdp, message *m_ptr,
456 int ipc_status)
458 /* Call the appropiate driver function, based on the type of request. Send a
459 * reply to the caller if necessary.
461 int r;
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;
471 case CLOCK:
472 if (cdp->cdr_alarm)
473 cdp->cdr_alarm(m_ptr->m_notify.timestamp);
474 break;
476 default:
477 if (cdp->cdr_other)
478 cdp->cdr_other(m_ptr, ipc_status);
481 return; /* do not send a reply */
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.
488 if (m_ptr->m_type == BDEV_OPEN) {
489 do_block_open(m_ptr, ipc_status);
491 return;
494 if (IS_CDEV_RQ(m_ptr->m_type)) {
495 int minor;
497 /* Try to retrieve minor device number */
498 r = chardriver_get_minor(m_ptr, &minor);
500 if (OK != r)
501 return;
503 /* We might get spurious requests if the driver has been restarted.
504 * Deny any requests on devices that have not previously been opened.
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 */
511 /* Mark the device as opened otherwise. */
512 set_open_dev(minor);
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 */
531 chardriver_reply(m_ptr, ipc_status, r);
534 /*===========================================================================*
535 * chardriver_terminate *
536 *===========================================================================*/
537 void chardriver_terminate(void)
539 /* Break out of the main loop after finishing the current request. */
541 running = FALSE;
543 sef_cancel();
546 /*===========================================================================*
547 * chardriver_task *
548 *===========================================================================*/
549 void chardriver_task(const struct chardriver *cdp)
551 /* Main program of any character device driver task. */
552 int r, ipc_status;
553 message mess;
555 running = TRUE;
557 /* Here is the main loop of the character driver task. It waits for a
558 * message, carries it out, and sends a reply.
560 while (running) {
561 if ((r = sef_receive_status(ANY, &mess, &ipc_status)) != OK) {
562 if (r == EINTR && !running)
563 break;
565 panic("chardriver: sef_receive_status failed: %d", r);
568 chardriver_process(cdp, &mess, ipc_status);
572 /*===========================================================================*
573 * chardriver_get_minor *
574 *===========================================================================*/
575 int chardriver_get_minor(const message *m, devminor_t *minor)
577 assert(NULL != m);
578 assert(NULL != minor);
580 switch(m->m_type)
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;