~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
cvs/emstar/fusd/examples/pager.c


  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.