1433d6423SLionel Sambuc /* This file contains the device independent character driver interface.
2433d6423SLionel Sambuc *
3433d6423SLionel Sambuc * Character drivers support the following requests. Message format m10 is
4433d6423SLionel Sambuc * used. Field names are prefixed with CDEV_. Separate field names are used for
5433d6423SLionel Sambuc * the "access", "ops", "user", and "request" fields.
6433d6423SLionel Sambuc *
7433d6423SLionel Sambuc * m_type MINOR GRANT COUNT FLAGS ID POS_LO POS_HI
8433d6423SLionel Sambuc * +-------------+-------+--------+-------+-------+------+---------+--------+
9433d6423SLionel Sambuc * | CDEV_OPEN | minor | access | user | | id | | |
10433d6423SLionel Sambuc * |-------------+-------+--------+-------+-------+------+---------+--------|
11433d6423SLionel Sambuc * | CDEV_CLOSE | minor | | | | id | | |
12433d6423SLionel Sambuc * |-------------+-------+--------+-------+-------+------+---------+--------|
13433d6423SLionel Sambuc * | CDEV_READ | minor | grant | bytes | flags | id | position |
14433d6423SLionel Sambuc * |-------------+-------+--------+-------+-------+------+---------+--------|
15433d6423SLionel Sambuc * | CDEV_WRITE | minor | grant | bytes | flags | id | position |
16433d6423SLionel Sambuc * |-------------+-------+--------+-------+-------+------+---------+--------|
17433d6423SLionel Sambuc * | CDEV_IOCTL | minor | grant | user | flags | id | request | |
18433d6423SLionel Sambuc * |-------------+-------+--------+-------+-------+------+---------+--------|
19433d6423SLionel Sambuc * | CDEV_CANCEL | minor | | | | id | | |
20433d6423SLionel Sambuc * |-------------+-------+--------+-------+-------+------+---------+--------|
21433d6423SLionel Sambuc * | CDEV_SELECT | minor | ops | | | | | |
22433d6423SLionel Sambuc * --------------------------------------------------------------------------
23433d6423SLionel Sambuc *
24433d6423SLionel Sambuc * The following reply messages are used.
25433d6423SLionel Sambuc *
26433d6423SLionel Sambuc * m_type MINOR STATUS ID
27433d6423SLionel Sambuc * +-----------------+-------+--------+-----+-----+------+---------+--------+
28433d6423SLionel Sambuc * | CDEV_REPLY | | status | | | id | | |
29433d6423SLionel Sambuc * |-----------------+-------+--------+-----+-----+------+---------+--------|
30433d6423SLionel Sambuc * | CDEV_SEL1_REPLY | minor | status | | | | | |
31433d6423SLionel Sambuc * |-----------------+-------+--------+-----+-----+------+---------+--------|
32433d6423SLionel Sambuc * | CDEV_SEL2_REPLY | minor | status | | | | | |
33433d6423SLionel Sambuc * --------------------------------------------------------------------------
34433d6423SLionel Sambuc *
35433d6423SLionel Sambuc * Changes:
36433d6423SLionel Sambuc * Sep 01, 2013 complete rewrite of the API (D.C. van Moolenboek)
37433d6423SLionel Sambuc * Aug 20, 2013 retire synchronous protocol (D.C. van Moolenbroek)
38433d6423SLionel Sambuc * Oct 16, 2011 split character and block protocol (D.C. van Moolenbroek)
39433d6423SLionel Sambuc * Aug 27, 2011 move common functions into driver.c (A. Welzel)
40433d6423SLionel Sambuc * Jul 25, 2005 added SYS_SIG type for signals (Jorrit N. Herder)
41433d6423SLionel Sambuc * Sep 15, 2004 added SYN_ALARM type for timeouts (Jorrit N. Herder)
42433d6423SLionel Sambuc * Jul 23, 2004 removed kernel dependencies (Jorrit N. Herder)
43433d6423SLionel Sambuc * Apr 02, 1992 constructed from AT wini and floppy driver (Kees J. Bot)
44433d6423SLionel Sambuc */
45433d6423SLionel Sambuc
46433d6423SLionel Sambuc #include <assert.h>
47433d6423SLionel Sambuc
48433d6423SLionel Sambuc #include <minix/drivers.h>
49433d6423SLionel Sambuc #include <minix/chardriver.h>
50433d6423SLionel Sambuc #include <minix/ds.h>
51433d6423SLionel Sambuc
52433d6423SLionel Sambuc static int running;
53433d6423SLionel Sambuc
54433d6423SLionel Sambuc /* Management data for opened devices. */
55433d6423SLionel Sambuc static devminor_t open_devs[MAX_NR_OPEN_DEVICES];
56433d6423SLionel Sambuc static int next_open_devs_slot = 0;
57433d6423SLionel Sambuc
58433d6423SLionel Sambuc /*===========================================================================*
59433d6423SLionel Sambuc * clear_open_devs *
60433d6423SLionel Sambuc *===========================================================================*/
clear_open_devs(void)61433d6423SLionel Sambuc static void clear_open_devs(void)
62433d6423SLionel Sambuc {
63433d6423SLionel Sambuc /* Reset the set of previously opened minor devices. */
64433d6423SLionel Sambuc next_open_devs_slot = 0;
65433d6423SLionel Sambuc }
66433d6423SLionel Sambuc
67433d6423SLionel Sambuc /*===========================================================================*
68433d6423SLionel Sambuc * is_open_dev *
69433d6423SLionel Sambuc *===========================================================================*/
is_open_dev(devminor_t minor)70433d6423SLionel Sambuc static int is_open_dev(devminor_t minor)
71433d6423SLionel Sambuc {
72433d6423SLionel Sambuc /* Check whether the given minor device has previously been opened. */
73433d6423SLionel Sambuc int i;
74433d6423SLionel Sambuc
75433d6423SLionel Sambuc for (i = 0; i < next_open_devs_slot; i++)
76433d6423SLionel Sambuc if (open_devs[i] == minor)
77433d6423SLionel Sambuc return TRUE;
78433d6423SLionel Sambuc
79433d6423SLionel Sambuc return FALSE;
80433d6423SLionel Sambuc }
81433d6423SLionel Sambuc
82433d6423SLionel Sambuc /*===========================================================================*
83433d6423SLionel Sambuc * set_open_dev *
84433d6423SLionel Sambuc *===========================================================================*/
set_open_dev(devminor_t minor)85433d6423SLionel Sambuc static void set_open_dev(devminor_t minor)
86433d6423SLionel Sambuc {
87433d6423SLionel Sambuc /* Mark the given minor device as having been opened. */
88433d6423SLionel Sambuc
89433d6423SLionel Sambuc if (next_open_devs_slot >= MAX_NR_OPEN_DEVICES)
90433d6423SLionel Sambuc panic("out of slots for open devices");
91433d6423SLionel Sambuc
92433d6423SLionel Sambuc open_devs[next_open_devs_slot] = minor;
93433d6423SLionel Sambuc next_open_devs_slot++;
94433d6423SLionel Sambuc }
95433d6423SLionel Sambuc
96433d6423SLionel Sambuc /*===========================================================================*
97433d6423SLionel Sambuc * chardriver_announce *
98433d6423SLionel Sambuc *===========================================================================*/
chardriver_announce(void)99433d6423SLionel Sambuc void chardriver_announce(void)
100433d6423SLionel Sambuc {
101433d6423SLionel Sambuc /* Announce we are up after a fresh start or restart. */
102433d6423SLionel Sambuc int r;
103433d6423SLionel Sambuc char key[DS_MAX_KEYLEN];
104433d6423SLionel Sambuc char label[DS_MAX_KEYLEN];
10565f76edbSDavid van Moolenbroek const char *driver_prefix = "drv.chr.";
106433d6423SLionel Sambuc
107433d6423SLionel Sambuc /* Callers are allowed to use ipc_sendrec to communicate with drivers.
108433d6423SLionel Sambuc * For this reason, there may blocked callers when a driver restarts.
109433d6423SLionel Sambuc * Ask the kernel to unblock them (if any).
110433d6423SLionel Sambuc */
11141022be1SCristiano Giuffrida if ((r = sys_statectl(SYS_STATE_CLEAR_IPC_REFS, 0, 0)) != OK)
112433d6423SLionel Sambuc panic("chardriver_announce: sys_statectl failed: %d", r);
113433d6423SLionel Sambuc
114433d6423SLionel Sambuc /* Publish a driver up event. */
115433d6423SLionel Sambuc if ((r = ds_retrieve_label_name(label, sef_self())) != OK)
116433d6423SLionel Sambuc panic("chardriver_announce: unable to get own label: %d", r);
117433d6423SLionel Sambuc
118433d6423SLionel Sambuc snprintf(key, DS_MAX_KEYLEN, "%s%s", driver_prefix, label);
119433d6423SLionel Sambuc if ((r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE)) != OK)
120433d6423SLionel Sambuc panic("chardriver_announce: unable to publish driver up event: %d", r);
121433d6423SLionel Sambuc
122433d6423SLionel Sambuc /* Expect an open for any device before serving regular driver requests. */
123433d6423SLionel Sambuc clear_open_devs();
124433d6423SLionel Sambuc }
125433d6423SLionel Sambuc
126433d6423SLionel Sambuc /*===========================================================================*
127433d6423SLionel Sambuc * chardriver_reply_task *
128433d6423SLionel Sambuc *===========================================================================*/
chardriver_reply_task(endpoint_t endpt,cdev_id_t id,int r)129433d6423SLionel Sambuc void chardriver_reply_task(endpoint_t endpt, cdev_id_t id, int r)
130433d6423SLionel Sambuc {
131433d6423SLionel Sambuc /* Reply to a (read, write, ioctl) task request that was suspended earlier.
132433d6423SLionel Sambuc * Not-so-well-written drivers may use this function to send a reply to a
133433d6423SLionel Sambuc * request that is being processed right now, and then return EDONTREPLY later.
134433d6423SLionel Sambuc */
135433d6423SLionel Sambuc message m_reply;
136433d6423SLionel Sambuc
137433d6423SLionel Sambuc if (r == EDONTREPLY || r == SUSPEND)
138433d6423SLionel Sambuc panic("chardriver: bad task reply: %d", r);
139433d6423SLionel Sambuc
140433d6423SLionel Sambuc memset(&m_reply, 0, sizeof(m_reply));
141433d6423SLionel Sambuc
142433d6423SLionel Sambuc m_reply.m_type = CDEV_REPLY;
143433d6423SLionel Sambuc m_reply.m_lchardriver_vfs_reply.status = r;
144433d6423SLionel Sambuc m_reply.m_lchardriver_vfs_reply.id = id;
145433d6423SLionel Sambuc
146433d6423SLionel Sambuc if ((r = asynsend3(endpt, &m_reply, AMF_NOREPLY)) != OK)
147433d6423SLionel Sambuc printf("chardriver_reply_task: send to %d failed: %d\n", endpt, r);
148433d6423SLionel Sambuc }
149433d6423SLionel Sambuc
150433d6423SLionel Sambuc /*===========================================================================*
151433d6423SLionel Sambuc * chardriver_reply_select *
152433d6423SLionel Sambuc *===========================================================================*/
chardriver_reply_select(endpoint_t endpt,devminor_t minor,int r)153433d6423SLionel Sambuc void chardriver_reply_select(endpoint_t endpt, devminor_t minor, int r)
154433d6423SLionel Sambuc {
155433d6423SLionel Sambuc /* Reply to a select request with a status update. This must not be used to
156433d6423SLionel Sambuc * reply to a select request that is being processed right now.
157433d6423SLionel Sambuc */
158433d6423SLionel Sambuc message m_reply;
159433d6423SLionel Sambuc
160433d6423SLionel Sambuc /* Replying with an error is allowed (if unusual). */
161433d6423SLionel Sambuc if (r == EDONTREPLY || r == SUSPEND)
162433d6423SLionel Sambuc panic("chardriver: bad select reply: %d", r);
163433d6423SLionel Sambuc
164433d6423SLionel Sambuc memset(&m_reply, 0, sizeof(m_reply));
165433d6423SLionel Sambuc
166433d6423SLionel Sambuc m_reply.m_type = CDEV_SEL2_REPLY;
167433d6423SLionel Sambuc m_reply.m_lchardriver_vfs_sel2.minor = minor;
168433d6423SLionel Sambuc m_reply.m_lchardriver_vfs_sel2.status = r;
169433d6423SLionel Sambuc
170433d6423SLionel Sambuc if ((r = asynsend3(endpt, &m_reply, AMF_NOREPLY)) != OK)
171433d6423SLionel Sambuc printf("chardriver_reply_select: send to %d failed: %d\n", endpt, r);
172433d6423SLionel Sambuc }
173433d6423SLionel Sambuc
174433d6423SLionel Sambuc /*===========================================================================*
175433d6423SLionel Sambuc * send_reply *
176433d6423SLionel Sambuc *===========================================================================*/
send_reply(endpoint_t endpt,message * m_ptr,int ipc_status)177433d6423SLionel Sambuc static void send_reply(endpoint_t endpt, message *m_ptr, int ipc_status)
178433d6423SLionel Sambuc {
179433d6423SLionel Sambuc /* Send a reply message to a request. */
180433d6423SLionel Sambuc int r;
181433d6423SLionel Sambuc
182433d6423SLionel Sambuc /* If we would block sending the message, send it asynchronously. */
183433d6423SLionel Sambuc if (IPC_STATUS_CALL(ipc_status) == SENDREC)
184433d6423SLionel Sambuc r = ipc_sendnb(endpt, m_ptr);
185433d6423SLionel Sambuc else
186433d6423SLionel Sambuc r = asynsend3(endpt, m_ptr, AMF_NOREPLY);
187433d6423SLionel Sambuc
188433d6423SLionel Sambuc if (r != OK)
189433d6423SLionel Sambuc printf("chardriver: unable to send reply to %d: %d\n", endpt, r);
190433d6423SLionel Sambuc }
191433d6423SLionel Sambuc
192433d6423SLionel Sambuc /*===========================================================================*
193433d6423SLionel Sambuc * chardriver_reply *
194433d6423SLionel Sambuc *===========================================================================*/
chardriver_reply(message * mess,int ipc_status,int r)195433d6423SLionel Sambuc static void chardriver_reply(message *mess, int ipc_status, int r)
196433d6423SLionel Sambuc {
197433d6423SLionel Sambuc /* Prepare and send a reply message. */
198433d6423SLionel Sambuc message reply_mess;
199433d6423SLionel Sambuc
200433d6423SLionel Sambuc /* If the EDONTREPLY pseudo-reply is given, we do not reply. This is however
201433d6423SLionel Sambuc * allowed only for blocking task calls. Perform a sanity check.
202433d6423SLionel Sambuc */
203433d6423SLionel Sambuc if (r == EDONTREPLY) {
204433d6423SLionel Sambuc switch (mess->m_type) {
205433d6423SLionel Sambuc case CDEV_READ:
206433d6423SLionel Sambuc case CDEV_WRITE:
207433d6423SLionel Sambuc case CDEV_IOCTL:
208433d6423SLionel Sambuc /* FIXME: we should be able to check FLAGS against
209433d6423SLionel Sambuc * CDEV_NONBLOCK here, but in practice, several drivers do not
210433d6423SLionel Sambuc * send a reply through this path (eg TTY) or simply do not
211*3ea34559SDavid van Moolenbroek * implement nonblocking calls properly (eg audio).
212433d6423SLionel Sambuc */
213433d6423SLionel Sambuc #if 0
214433d6423SLionel Sambuc if (mess->m_vfs_lchardriver_readwrite.flags & CDEV_NONBLOCK)
215433d6423SLionel Sambuc panic("chardriver: cannot suspend nonblocking I/O");
216433d6423SLionel Sambuc #endif
217433d6423SLionel Sambuc /*fall-through*/
218433d6423SLionel Sambuc case CDEV_CANCEL:
219433d6423SLionel Sambuc return; /* alright */
220433d6423SLionel Sambuc default:
221433d6423SLionel Sambuc panic("chardriver: cannot suspend request %d", mess->m_type);
222433d6423SLionel Sambuc }
223433d6423SLionel Sambuc }
224433d6423SLionel Sambuc
225433d6423SLionel Sambuc if (r == SUSPEND)
226433d6423SLionel Sambuc panic("chardriver: SUSPEND should not be used anymore");
227433d6423SLionel Sambuc
228433d6423SLionel Sambuc /* Do not reply with ERESTART. The only possible caller, VFS, will find out
229433d6423SLionel Sambuc * through other means when we have restarted, and is not (fully) ready to
230433d6423SLionel Sambuc * deal with ERESTART errors.
231433d6423SLionel Sambuc */
232433d6423SLionel Sambuc if (r == ERESTART)
233433d6423SLionel Sambuc return;
234433d6423SLionel Sambuc
235433d6423SLionel Sambuc memset(&reply_mess, 0, sizeof(reply_mess));
236433d6423SLionel Sambuc
237433d6423SLionel Sambuc switch (mess->m_type) {
238433d6423SLionel Sambuc case CDEV_OPEN:
239433d6423SLionel Sambuc case CDEV_CLOSE:
240433d6423SLionel Sambuc reply_mess.m_type = CDEV_REPLY;
241433d6423SLionel Sambuc reply_mess.m_lchardriver_vfs_reply.status = r;
242433d6423SLionel Sambuc reply_mess.m_lchardriver_vfs_reply.id =
243433d6423SLionel Sambuc mess->m_vfs_lchardriver_openclose.id;
244433d6423SLionel Sambuc break;
245433d6423SLionel Sambuc
246433d6423SLionel Sambuc case CDEV_READ:
247433d6423SLionel Sambuc case CDEV_WRITE:
248433d6423SLionel Sambuc case CDEV_IOCTL:
249433d6423SLionel Sambuc reply_mess.m_type = CDEV_REPLY;
250433d6423SLionel Sambuc reply_mess.m_lchardriver_vfs_reply.status = r;
251433d6423SLionel Sambuc reply_mess.m_lchardriver_vfs_reply.id =
252433d6423SLionel Sambuc mess->m_vfs_lchardriver_readwrite.id;
253433d6423SLionel Sambuc break;
254433d6423SLionel Sambuc
255433d6423SLionel Sambuc case CDEV_CANCEL: /* For cancel, this is a reply to the original request! */
256433d6423SLionel Sambuc reply_mess.m_type = CDEV_REPLY;
257433d6423SLionel Sambuc reply_mess.m_lchardriver_vfs_reply.status = r;
258433d6423SLionel Sambuc reply_mess.m_lchardriver_vfs_reply.id =
259433d6423SLionel Sambuc mess->m_vfs_lchardriver_cancel.id;
260433d6423SLionel Sambuc break;
261433d6423SLionel Sambuc
262433d6423SLionel Sambuc case CDEV_SELECT:
263433d6423SLionel Sambuc reply_mess.m_type = CDEV_SEL1_REPLY;
264433d6423SLionel Sambuc reply_mess.m_lchardriver_vfs_sel1.status = r;
265433d6423SLionel Sambuc reply_mess.m_lchardriver_vfs_sel1.minor =
266433d6423SLionel Sambuc mess->m_vfs_lchardriver_select.minor;
267433d6423SLionel Sambuc break;
268433d6423SLionel Sambuc
269433d6423SLionel Sambuc default:
270433d6423SLionel Sambuc panic("chardriver: unknown request %d", mess->m_type);
271433d6423SLionel Sambuc }
272433d6423SLionel Sambuc
273433d6423SLionel Sambuc send_reply(mess->m_source, &reply_mess, ipc_status);
274433d6423SLionel Sambuc }
275433d6423SLionel Sambuc
276433d6423SLionel Sambuc /*===========================================================================*
277433d6423SLionel Sambuc * do_open *
278433d6423SLionel Sambuc *===========================================================================*/
do_open(const struct chardriver * cdp,message * m_ptr)27991d07797SDavid van Moolenbroek static int do_open(const struct chardriver *cdp, message *m_ptr)
280433d6423SLionel Sambuc {
281433d6423SLionel Sambuc /* Open a minor device. */
282433d6423SLionel Sambuc endpoint_t user_endpt;
283433d6423SLionel Sambuc devminor_t minor;
2847c48de6cSDavid van Moolenbroek int r, bits;
285433d6423SLionel Sambuc
286433d6423SLionel Sambuc /* Default action if no open hook is in place. */
287433d6423SLionel Sambuc if (cdp->cdr_open == NULL)
288433d6423SLionel Sambuc return OK;
289433d6423SLionel Sambuc
290433d6423SLionel Sambuc /* Call the open hook. */
291433d6423SLionel Sambuc minor = m_ptr->m_vfs_lchardriver_openclose.minor;
2927c48de6cSDavid van Moolenbroek bits = m_ptr->m_vfs_lchardriver_openclose.access;
293433d6423SLionel Sambuc user_endpt = m_ptr->m_vfs_lchardriver_openclose.user;
294433d6423SLionel Sambuc
2957c48de6cSDavid van Moolenbroek r = cdp->cdr_open(minor, bits, user_endpt);
296433d6423SLionel Sambuc
297433d6423SLionel Sambuc /* If the device has been cloned, mark the new minor as open too. */
298433d6423SLionel Sambuc if (r >= 0 && (r & CDEV_CLONED)) {
299433d6423SLionel Sambuc minor = r & ~(CDEV_CLONED | CDEV_CTTY);
300433d6423SLionel Sambuc if (!is_open_dev(minor))
301433d6423SLionel Sambuc set_open_dev(minor);
302433d6423SLionel Sambuc }
303433d6423SLionel Sambuc
304433d6423SLionel Sambuc return r;
305433d6423SLionel Sambuc }
306433d6423SLionel Sambuc
307433d6423SLionel Sambuc /*===========================================================================*
308433d6423SLionel Sambuc * do_close *
309433d6423SLionel Sambuc *===========================================================================*/
do_close(const struct chardriver * cdp,message * m_ptr)31091d07797SDavid van Moolenbroek static int do_close(const struct chardriver *cdp, message *m_ptr)
311433d6423SLionel Sambuc {
312433d6423SLionel Sambuc /* Close a minor device. */
313433d6423SLionel Sambuc devminor_t minor;
314433d6423SLionel Sambuc
315433d6423SLionel Sambuc /* Default action if no close hook is in place. */
316433d6423SLionel Sambuc if (cdp->cdr_close == NULL)
317433d6423SLionel Sambuc return OK;
318433d6423SLionel Sambuc
319433d6423SLionel Sambuc /* Call the close hook. */
320433d6423SLionel Sambuc minor = m_ptr->m_vfs_lchardriver_openclose.minor;
321433d6423SLionel Sambuc
322433d6423SLionel Sambuc return cdp->cdr_close(minor);
323433d6423SLionel Sambuc }
324433d6423SLionel Sambuc
325433d6423SLionel Sambuc /*===========================================================================*
326433d6423SLionel Sambuc * do_trasnfer *
327433d6423SLionel Sambuc *===========================================================================*/
do_transfer(const struct chardriver * cdp,message * m_ptr,int do_write)32891d07797SDavid van Moolenbroek static int do_transfer(const struct chardriver *cdp, message *m_ptr,
32991d07797SDavid van Moolenbroek int do_write)
330433d6423SLionel Sambuc {
331433d6423SLionel Sambuc /* Carry out a read or write task request. */
332433d6423SLionel Sambuc devminor_t minor;
333433d6423SLionel Sambuc off_t position;
334433d6423SLionel Sambuc endpoint_t endpt;
335433d6423SLionel Sambuc cp_grant_id_t grant;
336433d6423SLionel Sambuc size_t size;
337433d6423SLionel Sambuc int flags;
338433d6423SLionel Sambuc cdev_id_t id;
339433d6423SLionel Sambuc ssize_t r;
340433d6423SLionel Sambuc
341433d6423SLionel Sambuc minor = m_ptr->m_vfs_lchardriver_readwrite.minor;
342433d6423SLionel Sambuc position = m_ptr->m_vfs_lchardriver_readwrite.pos;
343433d6423SLionel Sambuc endpt = m_ptr->m_source;
344433d6423SLionel Sambuc grant = m_ptr->m_vfs_lchardriver_readwrite.grant;
345433d6423SLionel Sambuc size = m_ptr->m_vfs_lchardriver_readwrite.count;
346433d6423SLionel Sambuc flags = m_ptr->m_vfs_lchardriver_readwrite.flags;
347433d6423SLionel Sambuc id = m_ptr->m_vfs_lchardriver_readwrite.id;
348433d6423SLionel Sambuc
349433d6423SLionel Sambuc /* Call the read/write hook, if the appropriate one is in place. */
350433d6423SLionel Sambuc if (!do_write && cdp->cdr_read != NULL)
351433d6423SLionel Sambuc r = cdp->cdr_read(minor, position, endpt, grant, size, flags, id);
352433d6423SLionel Sambuc else if (do_write && cdp->cdr_write != NULL)
353433d6423SLionel Sambuc r = cdp->cdr_write(minor, position, endpt, grant, size, flags, id);
354433d6423SLionel Sambuc else
355433d6423SLionel Sambuc r = EIO; /* Default action if no read/write hook is in place. */
356433d6423SLionel Sambuc
357433d6423SLionel Sambuc return r;
358433d6423SLionel Sambuc }
359433d6423SLionel Sambuc
360433d6423SLionel Sambuc /*===========================================================================*
361433d6423SLionel Sambuc * do_ioctl *
362433d6423SLionel Sambuc *===========================================================================*/
do_ioctl(const struct chardriver * cdp,message * m_ptr)36391d07797SDavid van Moolenbroek static int do_ioctl(const struct chardriver *cdp, message *m_ptr)
364433d6423SLionel Sambuc {
365433d6423SLionel Sambuc /* Carry out an I/O control task request. */
366433d6423SLionel Sambuc devminor_t minor;
367433d6423SLionel Sambuc unsigned long request;
368433d6423SLionel Sambuc cp_grant_id_t grant;
369433d6423SLionel Sambuc endpoint_t endpt, user_endpt;
370433d6423SLionel Sambuc int flags;
371433d6423SLionel Sambuc cdev_id_t id;
372433d6423SLionel Sambuc
373433d6423SLionel Sambuc /* Default action if no ioctl hook is in place. */
374433d6423SLionel Sambuc if (cdp->cdr_ioctl == NULL)
375433d6423SLionel Sambuc return ENOTTY;
376433d6423SLionel Sambuc
377433d6423SLionel Sambuc /* Call the ioctl hook. */
378433d6423SLionel Sambuc minor = m_ptr->m_vfs_lchardriver_readwrite.minor;
379433d6423SLionel Sambuc request = m_ptr->m_vfs_lchardriver_readwrite.request;
380433d6423SLionel Sambuc endpt = m_ptr->m_source;
381433d6423SLionel Sambuc grant = m_ptr->m_vfs_lchardriver_readwrite.grant;
382433d6423SLionel Sambuc flags = m_ptr->m_vfs_lchardriver_readwrite.flags;
383433d6423SLionel Sambuc user_endpt = m_ptr->m_vfs_lchardriver_readwrite.user;
384433d6423SLionel Sambuc id = m_ptr->m_vfs_lchardriver_readwrite.id;
385433d6423SLionel Sambuc
386433d6423SLionel Sambuc return cdp->cdr_ioctl(minor, request, endpt, grant, flags, user_endpt, id);
387433d6423SLionel Sambuc }
388433d6423SLionel Sambuc
389433d6423SLionel Sambuc /*===========================================================================*
390433d6423SLionel Sambuc * do_cancel *
391433d6423SLionel Sambuc *===========================================================================*/
do_cancel(const struct chardriver * cdp,message * m_ptr)39291d07797SDavid van Moolenbroek static int do_cancel(const struct chardriver *cdp, message *m_ptr)
393433d6423SLionel Sambuc {
394433d6423SLionel Sambuc /* Cancel a suspended (read, write, ioctl) task request. The original request
395433d6423SLionel Sambuc * may already have finished, in which case no reply should be sent.
396433d6423SLionel Sambuc */
397433d6423SLionel Sambuc devminor_t minor;
398433d6423SLionel Sambuc endpoint_t endpt;
399433d6423SLionel Sambuc cdev_id_t id;
400433d6423SLionel Sambuc
401433d6423SLionel Sambuc /* Default action if no cancel hook is in place: let the request finish. */
402433d6423SLionel Sambuc if (cdp->cdr_cancel == NULL)
403433d6423SLionel Sambuc return EDONTREPLY;
404433d6423SLionel Sambuc
405433d6423SLionel Sambuc /* Call the cancel hook. */
406433d6423SLionel Sambuc minor = m_ptr->m_vfs_lchardriver_cancel.minor;
407433d6423SLionel Sambuc endpt = m_ptr->m_source;
408433d6423SLionel Sambuc id = m_ptr->m_vfs_lchardriver_cancel.id;
409433d6423SLionel Sambuc
410433d6423SLionel Sambuc return cdp->cdr_cancel(minor, endpt, id);
411433d6423SLionel Sambuc }
412433d6423SLionel Sambuc
413433d6423SLionel Sambuc /*===========================================================================*
414433d6423SLionel Sambuc * do_select *
415433d6423SLionel Sambuc *===========================================================================*/
do_select(const struct chardriver * cdp,message * m_ptr)41691d07797SDavid van Moolenbroek static int do_select(const struct chardriver *cdp, message *m_ptr)
417433d6423SLionel Sambuc {
418433d6423SLionel Sambuc /* Perform a select query on a minor device. */
419433d6423SLionel Sambuc devminor_t minor;
420433d6423SLionel Sambuc unsigned int ops;
421433d6423SLionel Sambuc endpoint_t endpt;
422433d6423SLionel Sambuc
423433d6423SLionel Sambuc /* Default action if no select hook is in place. */
424433d6423SLionel Sambuc if (cdp->cdr_select == NULL)
425433d6423SLionel Sambuc return EBADF;
426433d6423SLionel Sambuc
427433d6423SLionel Sambuc /* Call the select hook. */
428433d6423SLionel Sambuc minor = m_ptr->m_vfs_lchardriver_select.minor;
429433d6423SLionel Sambuc ops = m_ptr->m_vfs_lchardriver_select.ops;
430433d6423SLionel Sambuc endpt = m_ptr->m_source;
431433d6423SLionel Sambuc
432433d6423SLionel Sambuc return cdp->cdr_select(minor, ops, endpt);
433433d6423SLionel Sambuc }
434433d6423SLionel Sambuc
435433d6423SLionel Sambuc /*===========================================================================*
436433d6423SLionel Sambuc * do_block_open *
437433d6423SLionel Sambuc *===========================================================================*/
do_block_open(message * m_ptr,int ipc_status)438433d6423SLionel Sambuc static void do_block_open(message *m_ptr, int ipc_status)
439433d6423SLionel Sambuc {
440433d6423SLionel Sambuc /* Reply to a block driver open request stating there is no such device. */
441433d6423SLionel Sambuc message m_reply;
442433d6423SLionel Sambuc
443433d6423SLionel Sambuc memset(&m_reply, 0, sizeof(m_reply));
444433d6423SLionel Sambuc
445433d6423SLionel Sambuc m_reply.m_type = BDEV_REPLY;
446433d6423SLionel Sambuc m_reply.m_lblockdriver_lbdev_reply.status = ENXIO;
447433d6423SLionel Sambuc m_reply.m_lblockdriver_lbdev_reply.id = m_ptr->m_lbdev_lblockdriver_msg.id;
448433d6423SLionel Sambuc
449433d6423SLionel Sambuc send_reply(m_ptr->m_source, &m_reply, ipc_status);
450433d6423SLionel Sambuc }
451433d6423SLionel Sambuc
452433d6423SLionel Sambuc /*===========================================================================*
453433d6423SLionel Sambuc * chardriver_process *
454433d6423SLionel Sambuc *===========================================================================*/
chardriver_process(const struct chardriver * cdp,message * m_ptr,int ipc_status)45591d07797SDavid van Moolenbroek void chardriver_process(const struct chardriver *cdp, message *m_ptr,
45691d07797SDavid van Moolenbroek int ipc_status)
457433d6423SLionel Sambuc {
458433d6423SLionel Sambuc /* Call the appropiate driver function, based on the type of request. Send a
459433d6423SLionel Sambuc * reply to the caller if necessary.
460433d6423SLionel Sambuc */
46165f76edbSDavid van Moolenbroek int r;
462433d6423SLionel Sambuc
463433d6423SLionel Sambuc /* Check for notifications first. We never reply to notifications. */
464433d6423SLionel Sambuc if (is_ipc_notify(ipc_status)) {
465433d6423SLionel Sambuc switch (_ENDPOINT_P(m_ptr->m_source)) {
466433d6423SLionel Sambuc case HARDWARE:
467433d6423SLionel Sambuc if (cdp->cdr_intr)
468433d6423SLionel Sambuc cdp->cdr_intr(m_ptr->m_notify.interrupts);
469433d6423SLionel Sambuc break;
470433d6423SLionel Sambuc
471433d6423SLionel Sambuc case CLOCK:
472433d6423SLionel Sambuc if (cdp->cdr_alarm)
473433d6423SLionel Sambuc cdp->cdr_alarm(m_ptr->m_notify.timestamp);
474433d6423SLionel Sambuc break;
475433d6423SLionel Sambuc
476433d6423SLionel Sambuc default:
477433d6423SLionel Sambuc if (cdp->cdr_other)
478433d6423SLionel Sambuc cdp->cdr_other(m_ptr, ipc_status);
479433d6423SLionel Sambuc }
480433d6423SLionel Sambuc
481433d6423SLionel Sambuc return; /* do not send a reply */
482433d6423SLionel Sambuc }
483433d6423SLionel Sambuc
484433d6423SLionel Sambuc /* Reply to block driver open requests with an error code. Otherwise, if
485433d6423SLionel Sambuc * someone creates a block device node for a character driver, opening that
486433d6423SLionel Sambuc * device node will cause the corresponding VFS thread to block forever.
487433d6423SLionel Sambuc */
488433d6423SLionel Sambuc if (m_ptr->m_type == BDEV_OPEN) {
489433d6423SLionel Sambuc do_block_open(m_ptr, ipc_status);
490433d6423SLionel Sambuc
491433d6423SLionel Sambuc return;
492433d6423SLionel Sambuc }
493433d6423SLionel Sambuc
494433d6423SLionel Sambuc if (IS_CDEV_RQ(m_ptr->m_type)) {
495433d6423SLionel Sambuc int minor;
496433d6423SLionel Sambuc
497433d6423SLionel Sambuc /* Try to retrieve minor device number */
498433d6423SLionel Sambuc r = chardriver_get_minor(m_ptr, &minor);
499433d6423SLionel Sambuc
500433d6423SLionel Sambuc if (OK != r)
501433d6423SLionel Sambuc return;
502433d6423SLionel Sambuc
503433d6423SLionel Sambuc /* We might get spurious requests if the driver has been restarted.
504433d6423SLionel Sambuc * Deny any requests on devices that have not previously been opened.
505433d6423SLionel Sambuc */
506433d6423SLionel Sambuc if (!is_open_dev(minor)) {
507433d6423SLionel Sambuc /* Ignore spurious requests for unopened devices. */
508433d6423SLionel Sambuc if (m_ptr->m_type != CDEV_OPEN)
509433d6423SLionel Sambuc return; /* do not send a reply */
510433d6423SLionel Sambuc
511433d6423SLionel Sambuc /* Mark the device as opened otherwise. */
512433d6423SLionel Sambuc set_open_dev(minor);
513433d6423SLionel Sambuc }
514433d6423SLionel Sambuc }
515433d6423SLionel Sambuc
516433d6423SLionel Sambuc /* Call the appropriate function(s) for this request. */
517433d6423SLionel Sambuc switch (m_ptr->m_type) {
518433d6423SLionel Sambuc case CDEV_OPEN: r = do_open(cdp, m_ptr); break;
519433d6423SLionel Sambuc case CDEV_CLOSE: r = do_close(cdp, m_ptr); break;
520433d6423SLionel Sambuc case CDEV_READ: r = do_transfer(cdp, m_ptr, FALSE); break;
521433d6423SLionel Sambuc case CDEV_WRITE: r = do_transfer(cdp, m_ptr, TRUE); break;
522433d6423SLionel Sambuc case CDEV_IOCTL: r = do_ioctl(cdp, m_ptr); break;
523433d6423SLionel Sambuc case CDEV_CANCEL: r = do_cancel(cdp, m_ptr); break;
524433d6423SLionel Sambuc case CDEV_SELECT: r = do_select(cdp, m_ptr); break;
525433d6423SLionel Sambuc default:
526433d6423SLionel Sambuc if (cdp->cdr_other)
527433d6423SLionel Sambuc cdp->cdr_other(m_ptr, ipc_status);
528433d6423SLionel Sambuc return; /* do not send a reply */
529433d6423SLionel Sambuc }
530433d6423SLionel Sambuc
531433d6423SLionel Sambuc chardriver_reply(m_ptr, ipc_status, r);
532433d6423SLionel Sambuc }
533433d6423SLionel Sambuc
534433d6423SLionel Sambuc /*===========================================================================*
535433d6423SLionel Sambuc * chardriver_terminate *
536433d6423SLionel Sambuc *===========================================================================*/
chardriver_terminate(void)537433d6423SLionel Sambuc void chardriver_terminate(void)
538433d6423SLionel Sambuc {
539433d6423SLionel Sambuc /* Break out of the main loop after finishing the current request. */
540433d6423SLionel Sambuc
541433d6423SLionel Sambuc running = FALSE;
542433d6423SLionel Sambuc
543433d6423SLionel Sambuc sef_cancel();
544433d6423SLionel Sambuc }
545433d6423SLionel Sambuc
546433d6423SLionel Sambuc /*===========================================================================*
547433d6423SLionel Sambuc * chardriver_task *
548433d6423SLionel Sambuc *===========================================================================*/
chardriver_task(const struct chardriver * cdp)54991d07797SDavid van Moolenbroek void chardriver_task(const struct chardriver *cdp)
550433d6423SLionel Sambuc {
551433d6423SLionel Sambuc /* Main program of any character device driver task. */
552433d6423SLionel Sambuc int r, ipc_status;
553433d6423SLionel Sambuc message mess;
554433d6423SLionel Sambuc
555433d6423SLionel Sambuc running = TRUE;
556433d6423SLionel Sambuc
557433d6423SLionel Sambuc /* Here is the main loop of the character driver task. It waits for a
558433d6423SLionel Sambuc * message, carries it out, and sends a reply.
559433d6423SLionel Sambuc */
560433d6423SLionel Sambuc while (running) {
561433d6423SLionel Sambuc if ((r = sef_receive_status(ANY, &mess, &ipc_status)) != OK) {
562433d6423SLionel Sambuc if (r == EINTR && !running)
563433d6423SLionel Sambuc break;
564433d6423SLionel Sambuc
565433d6423SLionel Sambuc panic("chardriver: sef_receive_status failed: %d", r);
566433d6423SLionel Sambuc }
567433d6423SLionel Sambuc
568433d6423SLionel Sambuc chardriver_process(cdp, &mess, ipc_status);
569433d6423SLionel Sambuc }
570433d6423SLionel Sambuc }
571433d6423SLionel Sambuc
572433d6423SLionel Sambuc /*===========================================================================*
573433d6423SLionel Sambuc * chardriver_get_minor *
574433d6423SLionel Sambuc *===========================================================================*/
chardriver_get_minor(const message * m,devminor_t * minor)57591d07797SDavid van Moolenbroek int chardriver_get_minor(const message *m, devminor_t *minor)
576433d6423SLionel Sambuc {
577433d6423SLionel Sambuc assert(NULL != m);
578433d6423SLionel Sambuc assert(NULL != minor);
579433d6423SLionel Sambuc
580433d6423SLionel Sambuc switch(m->m_type)
581433d6423SLionel Sambuc {
582433d6423SLionel Sambuc case CDEV_OPEN:
583433d6423SLionel Sambuc case CDEV_CLOSE:
584433d6423SLionel Sambuc *minor = m->m_vfs_lchardriver_openclose.minor;
585433d6423SLionel Sambuc return OK;
586433d6423SLionel Sambuc case CDEV_CANCEL:
587433d6423SLionel Sambuc *minor = m->m_vfs_lchardriver_cancel.minor;
588433d6423SLionel Sambuc return OK;
589433d6423SLionel Sambuc case CDEV_SELECT:
590433d6423SLionel Sambuc *minor = m->m_vfs_lchardriver_select.minor;
591433d6423SLionel Sambuc return OK;
592433d6423SLionel Sambuc case CDEV_READ:
593433d6423SLionel Sambuc case CDEV_WRITE:
594433d6423SLionel Sambuc case CDEV_IOCTL:
595433d6423SLionel Sambuc *minor = m->m_vfs_lchardriver_readwrite.minor;
596433d6423SLionel Sambuc return OK;
597433d6423SLionel Sambuc default:
598433d6423SLionel Sambuc return EINVAL;
599433d6423SLionel Sambuc }
600433d6423SLionel Sambuc }
601