xref: /minix3/minix/lib/libchardriver/chardriver.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
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