VM: simplify slab allocator
[minix.git] / lib / libchardriver / chardriver.c
blob13fa620b071a2114be7b23854214a217e32a9991
1 /* This file contains the device independent character driver interface.
3 * The drivers support the following operations (using message format m2):
5 * m_type DEVICE USER_ENDPT COUNT POSITION HIGHPOS IO_GRANT
6 * ----------------------------------------------------------------------------
7 * | DEV_OPEN | device | proc nr | mode | | | |
8 * |---------------+--------+---------+---------+--------+--------+-----------|
9 * | DEV_CLOSE | device | proc nr | | | | |
10 * |---------------+--------+---------+---------+--------+--------+-----------|
11 * | DEV_READ_S | device | proc nr | bytes | off lo | off hi i buf grant |
12 * |---------------+--------+---------+---------+--------+--------+-----------|
13 * | DEV_WRITE_S | device | proc nr | bytes | off lo | off hi | buf grant |
14 * |---------------+--------+---------+---------+--------+--------+-----------|
15 * | DEV_GATHER_S | device | proc nr | iov len | off lo | off hi | iov grant |
16 * |---------------+--------+---------+---------+--------+--------+-----------|
17 * | DEV_SCATTER_S | device | proc nr | iov len | off lo | off hi | iov grant |
18 * |---------------+--------+---------+---------+--------+--------+-----------|
19 * | DEV_IOCTL_S | device | proc nr | request | | | buf grant |
20 * |---------------+--------+---------+---------+--------+--------+-----------|
21 * | CANCEL | device | proc nr | r/w | | | grant |
22 * |---------------+--------+---------+---------+--------+--------+-----------|
23 * | DEV_SELECT | device | ops | | | | |
24 * ----------------------------------------------------------------------------
26 * The entry points into this file are:
27 * driver_task: the main message loop of the driver
28 * driver_receive: message receive interface for drivers
30 * Changes:
31 * Oct 16, 2011 split character and block protocol (D.C. van Moolenbroek)
32 * Aug 27, 2011 move common functions into driver.c (A. Welzel)
33 * Jul 25, 2005 added SYS_SIG type for signals (Jorrit N. Herder)
34 * Sep 15, 2004 added SYN_ALARM type for timeouts (Jorrit N. Herder)
35 * Jul 23, 2004 removed kernel dependencies (Jorrit N. Herder)
36 * Apr 02, 1992 constructed from AT wini and floppy driver (Kees J. Bot)
39 #include <minix/drivers.h>
40 #include <minix/chardriver.h>
41 #include <minix/ds.h>
43 static int running;
45 /* Management data for opened devices. */
46 static int open_devs[MAX_NR_OPEN_DEVICES];
47 static int next_open_devs_slot = 0;
49 /*===========================================================================*
50 * clear_open_devs *
51 *===========================================================================*/
52 static void clear_open_devs(void)
54 /* Reset the set of previously opened minor devices. */
55 next_open_devs_slot = 0;
58 /*===========================================================================*
59 * is_open_dev *
60 *===========================================================================*/
61 static int is_open_dev(int device)
63 /* Check whether the given minor device has previously been opened. */
64 int i;
66 for (i = 0; i < next_open_devs_slot; i++)
67 if (open_devs[i] == device)
68 return TRUE;
70 return FALSE;
73 /*===========================================================================*
74 * set_open_dev *
75 *===========================================================================*/
76 static void set_open_dev(int device)
78 /* Mark the given minor device as having been opened. */
80 if (next_open_devs_slot >= MAX_NR_OPEN_DEVICES)
81 panic("out of slots for open devices");
83 open_devs[next_open_devs_slot] = device;
84 next_open_devs_slot++;
87 /*===========================================================================*
88 * chardriver_announce *
89 *===========================================================================*/
90 void chardriver_announce(void)
92 /* Announce we are up after a fresh start or restart. */
93 int r;
94 char key[DS_MAX_KEYLEN];
95 char label[DS_MAX_KEYLEN];
96 char *driver_prefix = "drv.chr.";
98 /* Callers are allowed to use sendrec to communicate with drivers.
99 * For this reason, there may blocked callers when a driver restarts.
100 * Ask the kernel to unblock them (if any).
102 #if USE_STATECTL
103 if ((r = sys_statectl(SYS_STATE_CLEAR_IPC_REFS)) != OK)
104 panic("chardriver_init: sys_statectl failed: %d", r);
105 #endif
107 /* Publish a driver up event. */
108 if ((r = ds_retrieve_label_name(label, getprocnr())) != OK)
109 panic("chardriver_init: unable to get own label: %d", r);
111 snprintf(key, DS_MAX_KEYLEN, "%s%s", driver_prefix, label);
112 if ((r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE)) != OK)
113 panic("chardriver_init: unable to publish driver up event: %d", r);
115 /* Expect a DEV_OPEN for any device before serving regular driver requests. */
116 clear_open_devs();
119 /*===========================================================================*
120 * async_reply *
121 *===========================================================================*/
122 static void async_reply(message *mess, int r)
124 /* Send a reply using the asynchronous character device protocol. */
125 message reply_mess;
127 /* Do not reply with ERESTART in this protocol. The only possible caller,
128 * VFS, will find out through other means when we have restarted, and is not
129 * (fully) ready to deal with ERESTART errors.
131 if (r == ERESTART)
132 return;
134 memset(&reply_mess, 0, sizeof(reply_mess));
136 switch (mess->m_type) {
137 case DEV_OPEN:
138 reply_mess.m_type = DEV_OPEN_REPL;
139 reply_mess.REP_ENDPT = mess->USER_ENDPT;
140 reply_mess.REP_STATUS = r;
141 break;
143 case DEV_CLOSE:
144 reply_mess.m_type = DEV_CLOSE_REPL;
145 reply_mess.REP_ENDPT = mess->USER_ENDPT;
146 reply_mess.REP_STATUS = r;
147 break;
149 case DEV_READ_S:
150 case DEV_WRITE_S:
151 case DEV_IOCTL_S:
152 if (r == SUSPEND)
153 printf("driver_task: reviving %d (%d) with SUSPEND\n",
154 mess->m_source, mess->USER_ENDPT);
156 reply_mess.m_type = DEV_REVIVE;
157 reply_mess.REP_ENDPT = mess->USER_ENDPT;
158 reply_mess.REP_IO_GRANT = (cp_grant_id_t) mess->IO_GRANT;
159 reply_mess.REP_STATUS = r;
160 break;
162 case CANCEL:
163 /* The original request should send a reply. */
164 return;
166 case DEV_SELECT:
167 reply_mess.m_type = DEV_SEL_REPL1;
168 reply_mess.DEV_MINOR = mess->DEVICE;
169 reply_mess.DEV_SEL_OPS = r;
170 break;
172 default:
173 reply_mess.m_type = TASK_REPLY;
174 reply_mess.REP_ENDPT = mess->USER_ENDPT;
175 /* Status is # of bytes transferred or error code. */
176 reply_mess.REP_STATUS = r;
177 break;
180 r = asynsend(mess->m_source, &reply_mess);
182 if (r != OK)
183 printf("asyn_reply: unable to asynsend reply to %d: %d\n",
184 mess->m_source, r);
187 /*===========================================================================*
188 * sync_reply *
189 *===========================================================================*/
190 static void sync_reply(message *m_ptr, int ipc_status, int reply)
192 /* Reply to a message sent to the driver. */
193 endpoint_t caller_e, user_e;
194 int r;
196 caller_e = m_ptr->m_source;
197 user_e = m_ptr->USER_ENDPT;
199 m_ptr->m_type = TASK_REPLY;
200 m_ptr->REP_ENDPT = user_e;
201 m_ptr->REP_STATUS = reply;
203 /* If we would block sending the message, send it asynchronously. */
204 if (IPC_STATUS_CALL(ipc_status) == SENDREC)
205 r = sendnb(caller_e, m_ptr);
206 else
207 r = asynsend(caller_e, m_ptr);
209 if (r != OK)
210 printf("driver_reply: unable to send reply to %d: %d\n", caller_e, r);
213 /*===========================================================================*
214 * send_reply *
215 *===========================================================================*/
216 static void send_reply(int type, message *m_ptr, int ipc_status, int reply)
218 /* Prepare and send a reply message. */
220 if (reply == EDONTREPLY)
221 return;
223 if (type == CHARDRIVER_ASYNC)
224 async_reply(m_ptr, reply);
225 else
226 sync_reply(m_ptr, ipc_status, reply);
229 /*===========================================================================*
230 * do_rdwt *
231 *===========================================================================*/
232 static int do_rdwt(struct chardriver *cdp, message *mp)
234 /* Carry out a single read or write request. */
235 iovec_t iovec1;
236 int r, opcode;
237 u64_t position;
239 /* Disk address? Address and length of the user buffer? */
240 if (mp->COUNT < 0) return(EINVAL);
242 /* Prepare for I/O. */
243 if ((*cdp->cdr_prepare)(mp->DEVICE) == NULL) return(ENXIO);
245 /* Create a one element scatter/gather vector for the buffer. */
246 if(mp->m_type == DEV_READ_S)
247 opcode = DEV_GATHER_S;
248 else
249 opcode = DEV_SCATTER_S;
251 iovec1.iov_addr = (vir_bytes) mp->IO_GRANT;
252 iovec1.iov_size = mp->COUNT;
254 /* Transfer bytes from/to the device. */
255 position= make64(mp->POSITION, mp->HIGHPOS);
256 r = (*cdp->cdr_transfer)(mp->m_source, opcode, position, &iovec1, 1,
257 mp->USER_ENDPT, mp->FLAGS);
259 /* Return the number of bytes transferred or an error code. */
260 return(r == OK ? (int) (mp->COUNT - iovec1.iov_size) : r);
263 /*===========================================================================*
264 * do_vrdwt *
265 *===========================================================================*/
266 static int do_vrdwt(struct chardriver *cdp, message *mp)
268 /* Carry out an device read or write to/from a vector of user addresses.
269 * The "user addresses" are assumed to be safe, i.e. FS transferring to/from
270 * its own buffers, so they are not checked.
272 iovec_t iovec[NR_IOREQS];
273 phys_bytes iovec_size;
274 unsigned nr_req;
275 int r, opcode;
276 u64_t position;
278 nr_req = mp->COUNT; /* Length of I/O vector */
280 /* Copy the vector from the caller to kernel space. */
281 if (nr_req > NR_IOREQS) nr_req = NR_IOREQS;
282 iovec_size = (phys_bytes) (nr_req * sizeof(iovec[0]));
284 if (OK != sys_safecopyfrom(mp->m_source, (vir_bytes) mp->IO_GRANT,
285 0, (vir_bytes) iovec, iovec_size)) {
286 printf("bad I/O vector by: %d\n", mp->m_source);
287 return(EINVAL);
290 /* Prepare for I/O. */
291 if ((*cdp->cdr_prepare)(mp->DEVICE) == NULL) return(ENXIO);
293 /* Transfer bytes from/to the device. */
294 opcode = mp->m_type;
295 position= make64(mp->POSITION, mp->HIGHPOS);
296 r = (*cdp->cdr_transfer)(mp->m_source, opcode, position, iovec, nr_req,
297 mp->USER_ENDPT, mp->FLAGS);
299 /* Copy the I/O vector back to the caller. */
300 if (OK != sys_safecopyto(mp->m_source, (vir_bytes) mp->IO_GRANT,
301 0, (vir_bytes) iovec, iovec_size)) {
302 printf("couldn't return I/O vector: %d\n", mp->m_source);
303 return(EINVAL);
306 return(r);
309 /*===========================================================================*
310 * handle_notify *
311 *===========================================================================*/
312 static void handle_notify(struct chardriver *cdp, message *m_ptr)
314 /* Take care of the notifications (interrupt and clock messages) by calling the
315 * appropiate callback functions. Never send a reply.
318 /* Call the appropriate function for this notification. */
319 switch (_ENDPOINT_P(m_ptr->m_source)) {
320 case CLOCK:
321 if (cdp->cdr_alarm)
322 cdp->cdr_alarm(m_ptr);
323 break;
325 default:
326 if (cdp->cdr_other)
327 (void) cdp->cdr_other(m_ptr);
331 /*===========================================================================*
332 * handle_request *
333 *===========================================================================*/
334 static int handle_request(struct chardriver *cdp, message *m_ptr)
336 /* Call the appropiate driver function, based on the type of request. Return
337 * the result code that is to be sent back to the caller, or EDONTREPLY if no
338 * reply is to be sent.
340 int r;
342 /* We might get spurious requests if the driver has been restarted. Deny any
343 * requests on devices that have not previously been opened, signaling the
344 * caller that something went wrong.
346 if (IS_CDEV_MINOR_RQ(m_ptr->m_type) && !is_open_dev(m_ptr->DEVICE)) {
347 /* Reply ERESTART to spurious requests for unopened devices. */
348 if (m_ptr->m_type != DEV_OPEN)
349 return ERESTART;
351 /* Mark the device as opened otherwise. */
352 set_open_dev(m_ptr->DEVICE);
355 /* Call the appropriate function(s) for this request. */
356 switch (m_ptr->m_type) {
357 case DEV_OPEN: r = (*cdp->cdr_open)(m_ptr); break;
358 case DEV_CLOSE: r = (*cdp->cdr_close)(m_ptr); break;
359 case DEV_IOCTL_S: r = (*cdp->cdr_ioctl)(m_ptr); break;
360 case CANCEL: r = (*cdp->cdr_cancel)(m_ptr); break;
361 case DEV_SELECT: r = (*cdp->cdr_select)(m_ptr); break;
362 case DEV_READ_S:
363 case DEV_WRITE_S: r = do_rdwt(cdp, m_ptr); break;
364 case DEV_GATHER_S:
365 case DEV_SCATTER_S: r = do_vrdwt(cdp, m_ptr); break;
366 default:
367 if (cdp->cdr_other)
368 r = cdp->cdr_other(m_ptr);
369 else
370 r = EINVAL;
373 /* Let the driver perform any cleanup. */
374 if (cdp->cdr_cleanup)
375 (*cdp->cdr_cleanup)();
377 return r;
380 /*===========================================================================*
381 * chardriver_process *
382 *===========================================================================*/
383 void chardriver_process(struct chardriver *cdp, int driver_type,
384 message *m_ptr, int ipc_status)
386 /* Handle the given received message. */
387 int r;
389 /* Process the notification or request. */
390 if (is_ipc_notify(ipc_status)) {
391 handle_notify(cdp, m_ptr);
393 /* Do not reply to notifications. */
394 } else {
395 r = handle_request(cdp, m_ptr);
397 send_reply(driver_type, m_ptr, ipc_status, r);
401 /*===========================================================================*
402 * chardriver_task *
403 *===========================================================================*/
404 void chardriver_task(struct chardriver *cdp, int driver_type)
406 /* Main program of any device driver task. */
407 int r, ipc_status;
408 message mess;
410 running = TRUE;
412 /* Here is the main loop of the disk task. It waits for a message, carries
413 * it out, and sends a reply.
415 while (running) {
416 if ((r = sef_receive_status(ANY, &mess, &ipc_status)) != OK)
417 panic("driver_receive failed: %d", r);
419 chardriver_process(cdp, driver_type, &mess, ipc_status);
423 /*===========================================================================*
424 * do_nop *
425 *===========================================================================*/
426 int do_nop(message *UNUSED(mp))
428 return(OK);
431 /*===========================================================================*
432 * nop_ioctl *
433 *===========================================================================*/
434 int nop_ioctl(message *UNUSED(mp))
436 return(ENOTTY);
439 /*===========================================================================*
440 * nop_alarm *
441 *===========================================================================*/
442 void nop_alarm(message *UNUSED(mp))
444 /* Ignore the leftover alarm. */
447 /*===========================================================================*
448 * nop_cleanup *
449 *===========================================================================*/
450 void nop_cleanup(void)
452 /* Nothing to clean up. */
455 /*===========================================================================*
456 * nop_cancel *
457 *===========================================================================*/
458 int nop_cancel(message *UNUSED(m))
460 /* Nothing to do for cancel. */
461 return(OK);
464 /*===========================================================================*
465 * nop_select *
466 *===========================================================================*/
467 int nop_select(message *UNUSED(m))
469 /* Nothing to do for select. */
470 return(OK);