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
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>
45 /* Management data for opened devices. */
46 static int open_devs
[MAX_NR_OPEN_DEVICES
];
47 static int next_open_devs_slot
= 0;
49 /*===========================================================================*
51 *===========================================================================*/
52 static void clear_open_devs(void)
54 /* Reset the set of previously opened minor devices. */
55 next_open_devs_slot
= 0;
58 /*===========================================================================*
60 *===========================================================================*/
61 static int is_open_dev(int device
)
63 /* Check whether the given minor device has previously been opened. */
66 for (i
= 0; i
< next_open_devs_slot
; i
++)
67 if (open_devs
[i
] == device
)
73 /*===========================================================================*
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. */
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).
103 if ((r
= sys_statectl(SYS_STATE_CLEAR_IPC_REFS
)) != OK
)
104 panic("chardriver_init: sys_statectl failed: %d", r
);
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. */
119 /*===========================================================================*
121 *===========================================================================*/
122 static void async_reply(message
*mess
, int r
)
124 /* Send a reply using the asynchronous character device protocol. */
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.
134 memset(&reply_mess
, 0, sizeof(reply_mess
));
136 switch (mess
->m_type
) {
138 reply_mess
.m_type
= DEV_OPEN_REPL
;
139 reply_mess
.REP_ENDPT
= mess
->USER_ENDPT
;
140 reply_mess
.REP_STATUS
= r
;
144 reply_mess
.m_type
= DEV_CLOSE_REPL
;
145 reply_mess
.REP_ENDPT
= mess
->USER_ENDPT
;
146 reply_mess
.REP_STATUS
= r
;
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
;
163 /* The original request should send a reply. */
167 reply_mess
.m_type
= DEV_SEL_REPL1
;
168 reply_mess
.DEV_MINOR
= mess
->DEVICE
;
169 reply_mess
.DEV_SEL_OPS
= r
;
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
;
180 r
= asynsend(mess
->m_source
, &reply_mess
);
183 printf("asyn_reply: unable to asynsend reply to %d: %d\n",
187 /*===========================================================================*
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
;
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
);
207 r
= asynsend(caller_e
, m_ptr
);
210 printf("driver_reply: unable to send reply to %d: %d\n", caller_e
, r
);
213 /*===========================================================================*
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
)
223 if (type
== CHARDRIVER_ASYNC
)
224 async_reply(m_ptr
, reply
);
226 sync_reply(m_ptr
, ipc_status
, reply
);
229 /*===========================================================================*
231 *===========================================================================*/
232 static int do_rdwt(struct chardriver
*cdp
, message
*mp
)
234 /* Carry out a single read or write request. */
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
;
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 /*===========================================================================*
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
;
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
);
290 /* Prepare for I/O. */
291 if ((*cdp
->cdr_prepare
)(mp
->DEVICE
) == NULL
) return(ENXIO
);
293 /* Transfer bytes from/to the device. */
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
);
309 /*===========================================================================*
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
)) {
322 cdp
->cdr_alarm(m_ptr
);
327 (void) cdp
->cdr_other(m_ptr
);
331 /*===========================================================================*
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.
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
)
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;
363 case DEV_WRITE_S
: r
= do_rdwt(cdp
, m_ptr
); break;
365 case DEV_SCATTER_S
: r
= do_vrdwt(cdp
, m_ptr
); break;
368 r
= cdp
->cdr_other(m_ptr
);
373 /* Let the driver perform any cleanup. */
374 if (cdp
->cdr_cleanup
)
375 (*cdp
->cdr_cleanup
)();
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. */
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. */
395 r
= handle_request(cdp
, m_ptr
);
397 send_reply(driver_type
, m_ptr
, ipc_status
, r
);
401 /*===========================================================================*
403 *===========================================================================*/
404 void chardriver_task(struct chardriver
*cdp
, int driver_type
)
406 /* Main program of any device driver task. */
412 /* Here is the main loop of the disk task. It waits for a message, carries
413 * it out, and sends a reply.
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 /*===========================================================================*
425 *===========================================================================*/
426 int do_nop(message
*UNUSED(mp
))
431 /*===========================================================================*
433 *===========================================================================*/
434 int nop_ioctl(message
*UNUSED(mp
))
439 /*===========================================================================*
441 *===========================================================================*/
442 void nop_alarm(message
*UNUSED(mp
))
444 /* Ignore the leftover alarm. */
447 /*===========================================================================*
449 *===========================================================================*/
450 void nop_cleanup(void)
452 /* Nothing to clean up. */
455 /*===========================================================================*
457 *===========================================================================*/
458 int nop_cancel(message
*UNUSED(m
))
460 /* Nothing to do for cancel. */
464 /*===========================================================================*
466 *===========================================================================*/
467 int nop_select(message
*UNUSED(m
))
469 /* Nothing to do for select. */