1 /*
2 *
3 * Copyright (c) 2003 The Regents of the University of California. All
4 * rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * - Neither the name of the University nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 */
30
31
32 /*
33 * FUSD - The Framework for UserSpace Devices - Example program
34 *
35 * Jeremy Elson <jelson@circlemud.org>
36 *
37 * pagerd: simple daemon to accept page signals from underlying page
38 * devices, and redistribute those pages to applications.
39 *
40 * The application itself is not especially useful, but this example
41 * program has proved very valuable as a generic template for FUSD
42 * drivers that service multiple clients, and implement both blocking
43 * and selectable devices. This file is a good place to start for
44 * writing drivers. See logring.c for a more complex real-world
45 * application based on this template.
46 *
47 * How to use the pager:
48 *
49 * Interface for devices that generate pages: write "page" to
50 * /dev/pager/input
51 *
52 * Interface for programs waiting for pages: read from (or, select on
53 * and then read from) /dev/pager/notify. reads will unblock when a
54 * page arrives. Note that if more than one page arrives before you
55 * read, you'll only get the most recent one. In other words, you are
56 * guaranteed to get at least one page.
57 *
58 * Important: in order to guarantee that you do not miss any pages,
59 * you MUST NOT close the file descriptor in between reads/selects.
60 * If you close the FD and then reopen it, there will be a race (pages
61 * that arrive between the close and open will not be delivered).
62 *
63 * $Id: pager.c,v 1.10 2007-12-08 04:05:01 girod Exp $
64 */
65
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <errno.h>
70 #include <fcntl.h>
71
72 #include <fusd.h>
73
74
75 /* EXAMPLE START pager-open.c */
76 /* per-client structure to keep track of who has an open FD to us */
77 struct pager_client {
78 int last_page_seen; /* seq. no. of last page this client has seen */
79 struct fusd_file_info *read; /* outstanding read request, if any */
80 struct fusd_file_info *polldiff; /* outstanding polldiff request */
81 struct pager_client *next; /* to construct the linked list */
82 };
83
84 struct pager_client *client_list = NULL; /* list of clients (open FDs) */
85 int last_page = 0; /* seq. no. of the most recent page to arrive */
86
87 /* EXAMPLE STOP pager-open.c */
88
89 void pager_notify_complete_read(struct pager_client *c);
90 void pager_notify_complete_polldiff(struct pager_client *c);
91
92
93 /************************************************************************/
94
95 /*
96 * this function removes an element from a linked list. the
97 * pointer-manipulation insanity below is a trick that prevents the
98 * "element to be removed is the head of the list" from being a
99 * special case.
100 */
101 void client_list_remove(struct pager_client *c)
102 {
103 struct pager_client **ptr;
104
105 if (c == NULL || client_list == NULL)
106 return;
107
108 for (ptr = &client_list; *ptr != c; ptr = &((**ptr).next)) {
109 if (!*ptr) {
110 fprintf(stderr, "trying to remove a client that isn't in the list\n");
111 return;
112 }
113 }
114 *ptr = c->next;
115 }
116
117
118 /* EXAMPLE START pager-open.c */
119 /* open on /dev/pager/notify: create state for this client */
120 static int pager_notify_open(struct fusd_file_info *file)
121 {
122 /* create state for this client */
123 struct pager_client *c = malloc(sizeof(struct pager_client));
124
125 if (c == NULL)
126 return -ENOBUFS;
127
128 /* initialize fields of this client state */
129 memset(c, 0, sizeof(struct pager_client));
130 c->last_page_seen = last_page;
131
132 /* save the pointer to this state so it gets returned to us later */
133 file->private_data = c;
134
135 /* add this client to the client list */
136 c->next = client_list;
137 client_list = c;
138
139 return 0;
140 }
141 /* EXAMPLE STOP pager-open.c */
142
143
144 /* EXAMPLE START pager-close.c */
145 /* close on /dev/pager/notify: destroy state for this client */
146 static int pager_notify_close(struct fusd_file_info *file)
147 {
148 struct pager_client *c;
149
150 if ((c = (struct pager_client *) file->private_data) != NULL) {
151
152 /* take this client off our client list */
153 client_list_remove(c);
154
155 /* if there is a read outstanding, free the state */
156 if (c->read != NULL) {
157 fusd_destroy(c->read);
158 c->read = NULL;
159 }
160 /* destroy any outstanding polldiffs */
161 if (c->polldiff != NULL) {
162 fusd_destroy(c->polldiff);
163 c->polldiff = NULL;
164 }
165
166 /* get rid of the struct */
167 free(c);
168 file->private_data = NULL;
169 }
170 return 0;
171 }
172 /* EXAMPLE STOP pager-close.c */
173
174
175 /*
176 * read on /dev/pager/notify: store the fusd_file_info pointer. then call
177 * complete_read, which will immediately call fusd_return, if there is
178 * a page already waiting.
179 *
180 * Note that this shows a trick we use commonly in FUSD drivers: you
181 * are allowed to call fusd_return() from within a callback as long as
182 * you return -FUSD_NOREPLY. In other words, a driver can EITHER
183 * return a real return value from its callback, OR call fusd_return
184 * explicitly, but not both.
185 */
186 /* EXAMPLE START pager-read.c */
187 ssize_t pager_notify_read(struct fusd_file_info *file, char *buffer,
188 size_t len, loff_t *offset)
189 {
190 struct pager_client *c = (struct pager_client *) file->private_data;
191
192 if (c == NULL || c->read != NULL) {
193 fprintf(stderr, "pager_read's arguments are confusd, alas");
194 return -EINVAL;
195 }
196
197 c->read = file;
198 pager_notify_complete_read(c);
199 return -FUSD_NOREPLY;
200 }
201
202 /* EXAMPLE STOP pager-read.c */
203
204 /*
205 * This function "completes" a read: that is, matches up a client who
206 * is requesting data with data that's waiting to be served.
207 *
208 * This function is called in two cases:
209 *
210 * 1- When a new read request comes in. The driver might be able to
211 * complete immediately, if a page arrived between the time the
212 * process opened the device and performed the read. This is the
213 * common case for clients that use select. hasn't seen yet - this
214 * is normal if )
215 *
216 * 2- When a new page arrives, all readers are unblocked
217 */
218 /* EXAMPLE START pager-read.c */
219 void pager_notify_complete_read(struct pager_client *c)
220 {
221 /* if there is no outstanding read, do nothing */
222 if (c == NULL || c->read == NULL)
223 return;
224
225 /* if there are no outstanding pages, do nothing */
226 if (c->last_page_seen >= last_page)
227 return;
228
229 /* bring this client up to date with the most recent page */
230 c->last_page_seen = last_page;
231
232 /* and notify the client by unblocking the read (read returns 0) */
233 fusd_return(c->read, 0);
234 c->read = NULL;
235 }
236 /* EXAMPLE STOP pager-read.c */
237
238
239 /* This function is only called on behalf of clients who are trying to
240 * use select(). The kernel keeps us up to date on what it thinks the
241 * current "poll state" is, i.e. readable and/or writable. The kernel
242 * calls this function every time its assumption about the current
243 * poll state changes. Every time the driver's notion of the state
244 * differs from what the kernel's cached value, it should return the
245 * poll_diff request with the updated state. Note that a 2nd request
246 * may come from the kernel before the driver has returned the first
247 * one; if this happens, use fusd_destroy() to get rid of the older one.
248 */
249 /* EXAMPLE START pager-polldiff.c */
250 int pager_notify_polldiff(struct fusd_file_info *file,
251 unsigned int cached_state)
252 {
253 struct pager_client *c = (struct pager_client *) file->private_data;
254
255 if (c == NULL)
256 return -EINVAL;
257
258 /* if we're already holding a polldiff request that we haven't
259 * replied to yet, destroy the old one and hold onto only the new
260 * one */
261 if (c->polldiff != NULL) {
262 fusd_destroy(c->polldiff);
263 c->polldiff = NULL;
264 }
265
266 c->polldiff = file;
267 pager_notify_complete_polldiff(c);
268 return -FUSD_NOREPLY;
269 }
270
271 /* EXAMPLE STOP pager-polldiff.c */
272
273
274 /*
275 * complete_polldiff: if a client has an outstanding 'polldiff'
276 * request, possibly return updated poll-state information to the
277 * kernel, if indeed the state has changed.
278 */
279 /* EXAMPLE START pager-polldiff.c */
280 void pager_notify_complete_polldiff(struct pager_client *c)
281 {
282 int curr_state, cached_state;
283
284 /* if there is no outstanding polldiff, do nothing */
285 if (c == NULL || c->polldiff == NULL)
286 return;
287
288 /* figure out the "current" state: i.e. whether or not the pager
289 * is readable for this client based on the last page it saw */
290 if (c->last_page_seen < last_page)
291 curr_state = FUSD_NOTIFY_INPUT; /* readable */
292 else
293 curr_state = 0; /* not readable or writable */
294
295 /* cached_state is what the kernel *thinks* the state is */
296 cached_state = fusd_get_poll_diff_cached_state(c->polldiff);
297
298 /* if the state is not what the kernel thinks it is, notify the
299 kernel of the change */
300 if (curr_state != cached_state) {
301 fusd_return(c->polldiff, curr_state);
302 c->polldiff = NULL;
303 }
304 }
305 /* EXAMPLE STOP pager-polldiff.c */
306
307
308
309 /*
310 * this handles a write on /dev/pager/input. this is called by one of
311 * the underlying page devices when a page arrives. if a device
312 * writes "page" to this interface, a page is queued for everyone
313 * using the notify interface.
314 */
315 #define CASE(x) if ((found == 0) && !strcmp(tmp, x) && (found = 1))
316 /* EXAMPLE START pager-read.c */
317
318 ssize_t pager_input_write(struct fusd_file_info *file,
319 const char *buffer, size_t len, loff_t *offset)
320 {
321 struct pager_client *c;
322
323 /* ... */
324 /* EXAMPLE STOP pager-read.c */
325 char tmp[1024];
326 int found = 0;
327
328 if (len > sizeof(tmp) - 1)
329 len = sizeof(tmp) - 1;
330
331 strncpy(tmp, buffer, len);
332 tmp[len] = '\0';
333
334 /* strip trailing \n's */
335 while (tmp[len-1] == '\n')
336 tmp[--len] = '\0';
337
338 /* EXAMPLE START pager-read.c */
339
340 CASE("page") {
341 last_page++;
342
343 for (c = client_list; c != NULL; c = c->next) {
344 pager_notify_complete_polldiff(c);
345 pager_notify_complete_read(c);
346 }
347 }
348 /* EXAMPLE STOP pager-read.c */
349
350 /* other commands (if there ever are any) can go here */
351
352 if (!found)
353 return -EINVAL;
354 else
355 return len;
356 }
357 #undef CASE
358
359
360 static int fusd_success(struct fusd_file_info *file)
361 {
362 return 0;
363 }
364
365
366 int main(int argc, char *argv[])
367 {
368 /* register the input device */
369 fusd_simple_register("/dev/pager/input", 0666, NULL,
370 open: fusd_success, close: fusd_success,
371 write: pager_input_write);
372
373 /* register the notification device */
374 fusd_simple_register("/dev/pager/notify", 0666, NULL,
375 open: pager_notify_open,
376 close: pager_notify_close,
377 read: pager_notify_read,
378 poll_diff: pager_notify_polldiff);
379
380 printf("calling fusd_run; reads from /dev/pager/notify will now block\n"
381 "until someone writes 'page' to /dev/pager/input...\n");
382 fusd_run();
383
384 return 0;
385 }
386
387
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.