3 * Copyright (c) 2003 The Regents of the University of California. All
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
33 * fusd userspace library: functions that know how to properly talk
34 * to the fusd kernel module
36 * authors: jelson and girod
41 char libfusd_c_id
[] = "$Id$";
46 #include <sys/types.h>
50 #include <sys/ioctl.h>
59 #define MIN(a, b) ((a) < (b) ? (a) : (b))
61 /* maximum number of messages processed by a single call to fusd_dispatch */
62 #define MAX_MESSAGES_PER_DISPATCH 40
64 /* used for fusd_run */
65 static fd_set fusd_fds
;
67 /* default prefix of devices (often "/dev/") */
68 char *dev_root
= NULL
;
71 * fusd_fops_set is an array that keeps track of the file operations
72 * struct for each fusd fd.
74 static fusd_file_operations_t fusd_fops_set
[FD_SETSIZE
];
75 fusd_file_operations_t null_fops
= { NULL
};
80 #define FUSD_GET_FOPS(fd) \
81 (fusd_fops_set + (fd))
83 #define FUSD_SET_FOPS(fd,fi) \
84 (fusd_fops_set[(fd)]=(*fi))
86 #define FUSD_FD_VALID(fd) \
88 ((fd)<FD_SETSIZE) && \
89 (memcmp(FUSD_GET_FOPS(fd), &null_fops, sizeof(fusd_file_operations_t))))
95 * this is called automatically before the first
100 static int fusd_init_needed
= 1;
102 if (fusd_init_needed
) {
105 fusd_init_needed
= 0;
107 for (i
= 0; i
< FD_SETSIZE
; i
++)
108 FUSD_SET_FOPS(i
, &null_fops
);
111 dev_root
= DEFAULT_DEV_ROOT
;
116 int fusd_register(const char *name
, const char* clazz
, const char* devname
, mode_t mode
, void *device_info
,
117 struct fusd_file_operations
*fops
)
119 int fd
= -1, retval
= 0;
122 /* need initialization? */
125 /* make sure the name is valid and we have a valid set of fops... */
126 if (name
== NULL
|| fops
== NULL
) {
127 fprintf(stderr
, "fusd_register: invalid name or fops argument\n");
133 * convenience: if the first characters of the name you're trying
134 * to register are SKIP_PREFIX (usually "/dev/"), skip over them.
136 if (dev_root
!= NULL
&& strlen(name
) > strlen(dev_root
) &&
137 !strncmp(name
, dev_root
, strlen(dev_root
))) {
138 name
+= strlen(dev_root
);
141 if (strlen(name
) > FUSD_MAX_NAME_LENGTH
) {
142 fprintf(stderr
, "name '%s' too long, sorry :(", name
);
147 /* open the fusd control channel */
148 if ((fd
= open(FUSD_CONTROL_DEVNAME
, O_RDWR
| O_NONBLOCK
)) < 0) {
150 /* if the problem is that /dev/fusd does not exist, return the
151 * message "Package not installed", which is hopefully more
152 * illuminating than "no such file or directory" */
153 if (errno
== ENOENT
) {
154 fprintf(stderr
, "libfusd: %s does not exist; ensure FUSD's kernel module is installed\n",
155 FUSD_CONTROL_DEVNAME
);
158 perror("libfusd: trying to open FUSD control channel");
165 if (FUSD_FD_VALID(fd
)) {
170 /* set up the message */
171 memset(&message
, 0, sizeof(message
));
172 message
.magic
= FUSD_MSG_MAGIC
;
173 message
.cmd
= FUSD_REGISTER_DEVICE
;
175 strcpy(message
.parm
.register_msg
.name
, name
);
176 strcpy(message
.parm
.register_msg
.clazz
, clazz
);
177 strcpy(message
.parm
.register_msg
.devname
, devname
);
178 message
.parm
.register_msg
.mode
= mode
;
179 message
.parm
.register_msg
.device_info
= device_info
;
181 /* make the request */
182 if (write(fd
, &message
, sizeof(fusd_msg_t
)) < 0) {
187 /* OK, store the new file state */
188 FUSD_SET_FOPS(fd
, fops
);
189 FD_SET(fd
, &fusd_fds
);
207 int fusd_unregister(int fd
)
209 if (FUSD_FD_VALID(fd
)) {
210 /* clear fd location */
211 FUSD_SET_FOPS(fd
, &null_fops
);
212 FD_CLR(fd
, &fusd_fds
);
225 * fusd_run: a convenience function for automatically running a FUSD
226 * driver, for drivers that don't want to manually select on file
227 * descriptors and call fusd_dispatch. This function will
228 * automatically select on all devices the user has registered and
229 * call fusd_dispatch on any one that becomes readable.
238 /* locate maxmimum fd in use */
239 for (maxfd
=0, i
=0; i
< FD_SETSIZE
; i
++) {
240 if (FD_ISSET(i
, &fusd_fds
)) {
249 memmove(&tfds
, &fusd_fds
, sizeof(fd_set
));
250 status
= select(maxfd
, &tfds
, NULL
, NULL
, NULL
);
254 perror("libfusd: fusd_run: error on select");
259 for (i
= 0; i
< maxfd
; i
++)
260 if (FD_ISSET(i
, &tfds
))
266 /************************************************************************/
269 /* reads a fusd kernel-to-userspace message from fd, and puts a
270 * fusd_msg into the memory pointed to by msg (we assume we are passed
271 * a buffer managed by the caller). if there is a data portion to the
272 * message (msg->datalen > 0), we allocate memory for it, set data to
273 * point to that memory. the returned data pointer must also be
274 * managed by the caller. */
275 static int fusd_get_message(int fd
, fusd_msg_t
*msg
)
277 /* read the header part into the kernel */
278 if (read(fd
, msg
, sizeof(fusd_msg_t
)) < 0) {
280 perror("error talking to FUSD control channel on header read");
283 msg
->data
= NULL
; /* pointers in kernelspace have no meaning */
285 if (msg
->magic
!= FUSD_MSG_MAGIC
) {
286 fprintf(stderr
, "libfusd magic number failure\n");
290 /* if there's a data part to the message, read it from the kernel. */
292 if ((msg
->data
= malloc(msg
->datalen
+ 1)) == NULL
) {
293 fprintf(stderr
, "libfusd: can't allocate memory\n");
294 return -ENOMEM
; /* this is bad, we are now unsynced */
297 if (read(fd
, msg
->data
, msg
->datalen
) < 0) {
298 perror("error talking to FUSD control channel on data read");
304 /* For convenience, we now ensure that the byte *after* the buffer
305 * is set to 0. (Note we malloc'd one extra byte above.) */
306 msg
->data
[msg
->datalen
] = '\0';
314 * fusd_fdset_add: given an FDSET and "max", add the currently valid
315 * FUSD fds to the set and update max accordingly.
317 void fusd_fdset_add(fd_set
*set
, int *max
)
321 for (i
= 0; i
< FD_SETSIZE
; i
++) {
322 if (FD_ISSET(i
, &fusd_fds
)) {
334 * fusd_dispatch_fdset: given an fd_set full of descriptors, call
335 * fusd_dispatch on every descriptor in the set which is a valid FUSD
338 void fusd_dispatch_fdset(fd_set
*set
)
342 for (i
= 0; i
< FD_SETSIZE
; i
++)
343 if (FD_ISSET(i
, set
) && FD_ISSET(i
, &fusd_fds
))
349 * fusd_dispatch_one() -- read a single kernel-to-userspace message
350 * from fd, then call the appropriate userspace callback function,
351 * based on the message that was read. finally, return the result
352 * back to the kernel, IF the return value from the callback is not
355 * On success, returns 0.
356 * On failure, returns a negative number indicating the errno.
358 static int fusd_dispatch_one(int fd
, fusd_file_operations_t
*fops
)
360 fusd_file_info_t
*file
= NULL
;
361 fusd_msg_t
*msg
= NULL
;
362 int driver_retval
= 0; /* returned to the FUSD driver */
363 int user_retval
= 0; /* returned to the user who made the syscall */
365 /* check for valid, look up ops */
367 fprintf(stderr
, "fusd_dispatch: no fops provided!\n");
368 driver_retval
= -EBADF
;
372 /* allocate memory for fusd_msg_t */
373 if ((msg
= malloc(sizeof(fusd_msg_t
))) == NULL
) {
374 driver_retval
= -ENOMEM
;
375 fprintf(stderr
, "libfusd: can't allocate memory\n");
378 memset(msg
, '\0', sizeof(fusd_msg_t
));
380 /* read header and data, if it's there */
381 if ((driver_retval
= fusd_get_message(fd
, msg
)) < 0)
384 /* allocate file info struct */
385 file
= malloc(sizeof(fusd_file_info_t
));
387 fprintf(stderr
, "libfusd: can't allocate memory\n");
388 driver_retval
= -ENOMEM
;
392 /* fill the file info struct */
393 memset(file
, '\0', sizeof(fusd_file_info_t
));
395 file
->device_info
= msg
->parm
.fops_msg
.device_info
;
396 file
->private_data
= msg
->parm
.fops_msg
.private_info
;
397 file
->flags
= msg
->parm
.fops_msg
.flags
;
398 file
->pid
= msg
->parm
.fops_msg
.pid
;
399 file
->uid
= msg
->parm
.fops_msg
.uid
;
400 file
->gid
= msg
->parm
.fops_msg
.gid
;
401 file
->fusd_msg
= msg
;
403 /* right now we only handle fops requests */
404 if (msg
->cmd
!= FUSD_FOPS_CALL
&& msg
->cmd
!= FUSD_FOPS_NONBLOCK
&&
405 msg
->cmd
!= FUSD_FOPS_CALL_DROPREPLY
) {
406 fprintf(stderr
, "libfusd: got unknown msg->cmd from kernel\n");
407 user_retval
= -EINVAL
;
411 /* dispatch on operation type */
412 user_retval
= -ENOSYS
;
413 switch (msg
->subcmd
) {
415 if (fops
&& fops
->open
)
416 user_retval
= fops
->open(file
);
419 if (fops
&& fops
->close
)
420 user_retval
= fops
->close(file
);
423 /* allocate a buffer and make the call */
424 if (fops
&& fops
->read
) {
425 if ((msg
->data
= malloc(msg
->parm
.fops_msg
.length
)) == NULL
) {
426 user_retval
= -ENOMEM
;
427 fprintf(stderr
, "libfusd: can't allocate memory\n");
429 msg
->datalen
= msg
->parm
.fops_msg
.length
;
430 user_retval
= fops
->read(file
, msg
->data
, msg
->datalen
,
431 &msg
->parm
.fops_msg
.offset
);
436 if (fops
&& fops
->write
)
437 user_retval
= fops
->write(file
, msg
->data
, msg
->datalen
,
438 &msg
->parm
.fops_msg
.offset
);
441 if (fops
&& fops
->mmap
)
443 user_retval
= fops
->mmap(file
, msg
->parm
.fops_msg
.offset
, msg
->parm
.fops_msg
.length
, msg
->parm
.fops_msg
.flags
,
444 &msg
->parm
.fops_msg
.arg
.ptr_arg
, &msg
->parm
.fops_msg
.length
);
448 if (fops
&& fops
->ioctl
) {
449 /* in the case of an ioctl read, allocate a buffer for the
450 * driver to write to, IF there isn't already a buffer. (there
451 * might already be a buffer if this is a read+write) */
452 if ((_IOC_DIR(msg
->parm
.fops_msg
.cmd
) & _IOC_READ
) &&
454 msg
->datalen
= _IOC_SIZE(msg
->parm
.fops_msg
.cmd
);
455 if ((msg
->data
= malloc(msg
->datalen
)) == NULL
) {
456 user_retval
= -ENOMEM
;
460 if (msg
->data
!= NULL
)
461 user_retval
= fops
->ioctl(file
, msg
->parm
.fops_msg
.cmd
, msg
->data
);
463 user_retval
= fops
->ioctl(file
, msg
->parm
.fops_msg
.cmd
,
464 (void *) msg
->parm
.fops_msg
.arg
.ptr_arg
);
469 /* This callback requests notification when an event occurs on a file,
470 * e.g. becoming readable or writable */
471 if (fops
&& fops
->poll_diff
)
472 user_retval
= fops
->poll_diff(file
, msg
->parm
.fops_msg
.cmd
);
476 /* This callback is called when a system call is interrupted */
477 if (fops
&& fops
->unblock
)
478 user_retval
= fops
->unblock(file
);
482 fprintf(stderr
, "libfusd: Got unsupported operation\n");
483 user_retval
= -ENOSYS
;
490 /* out_noreply is only used for handling errors */
492 if (msg
->data
!= NULL
)
498 /* send_reply is only used for success */
500 if (-user_retval
<= 0xff) {
501 /* 0xff is the maximum legal return value (?) - return val to user */
502 driver_retval
= fusd_return(file
, user_retval
);
504 /* if we got a FUSD_NOREPLY, don't free the msg structure */
508 /* this is common to both errors and success */
510 if (driver_retval
< 0) {
511 errno
= -driver_retval
;
514 return driver_retval
;
518 /* fusd_dispatch is now a wrapper around fusd_dispatch_one that calls
519 * it repeatedly, until it fails. this helps a lot with bulk data
520 * transfer since there is no intermediate select in between the
521 * reads. (the kernel module helps by running the user process in
524 * This function now prints an error to stderr in case of error,
525 * instead of returning a -1.
527 void fusd_dispatch(int fd
)
529 int retval
, num_dispatches
= 0;
530 fusd_file_operations_t
*fops
= NULL
;
532 /* make sure we have a valid FD, and get its fops structure */
533 if (!FUSD_FD_VALID(fd
)) {
538 fops
= FUSD_GET_FOPS(fd
);
540 /* now keep dispatching until a dispatch returns an error */
542 retval
= fusd_dispatch_one(fd
, fops
);
546 } while (retval
>= 0 && num_dispatches
<= MAX_MESSAGES_PER_DISPATCH
);
548 /* if we've dispatched at least one message successfully, and then
549 * stopped because of EAGAIN - do not report an error. this is the
551 if (num_dispatches
> 0 && errno
== EAGAIN
) {
557 if (retval
< 0 && errno
!= EPIPE
)
558 fprintf(stderr
, "libfusd: fusd_dispatch error on fd %d: %m\n", fd
);
563 * fusd_destroy destroys all state associated with a fusd_file_info
564 * pointer. (It is implicitly called by fusd_return.) If a driver
565 * saves a fusd_file_info pointer by calling -FUSD_NOREPLY in order to
566 * block a read, but gets a "close" request on the file before the
567 * pointer is returned with fusd_return, it should be thrown away
568 * using fusd_destroy.
570 void fusd_destroy(struct fusd_file_info
*file
)
575 if (file
->fusd_msg
->data
!= NULL
)
576 free(file
->fusd_msg
->data
);
577 free(file
->fusd_msg
);
583 * construct a user-to-kernel message in reply to a file function
586 * On success, returns 0.
587 * On failure, returns a negative number indicating the errno.
589 int fusd_return(fusd_file_info_t
*file
, ssize_t retval
)
591 fusd_msg_t
*msg
= NULL
;
593 int driver_retval
= 0;
597 fprintf(stderr
, "fusd_return: NULL file\n");
602 if (!FUSD_FD_VALID(fd
)) {
603 fprintf(stderr
, "fusd_return: badfd (fd %d)\n", fd
);
607 if ((msg
= file
->fusd_msg
) == NULL
) {
608 fprintf(stderr
, "fusd_return: fusd_msg is gone\n");
612 /* if this was a "DONTREPLY" message, just free the struct */
613 if (msg
->cmd
== FUSD_FOPS_CALL_DROPREPLY
)
616 /* do we copy data back to kernel? how much? */
617 switch(msg
->subcmd
) {
619 /* these operations can return data to userspace */
621 msg
->datalen
= MIN(retval
, msg
->parm
.fops_msg
.length
);
622 retval
= msg
->datalen
;
628 /* ioctl CAN (in read mode) return data to userspace */
630 (_IOC_DIR(msg
->parm
.fops_msg
.cmd
) & _IOC_READ
))
631 msg
->datalen
= _IOC_SIZE(msg
->parm
.fops_msg
.cmd
);
636 /* open, close, write, etc. do not return data */
641 /* fill the file info struct */
642 msg
->cmd
++; /* change FOPS_CALL to FOPS_REPLY; NONBLOCK to NONBLOCK_REPLY */
643 msg
->parm
.fops_msg
.retval
= retval
;
644 msg
->parm
.fops_msg
.device_info
= file
->device_info
;
645 msg
->parm
.fops_msg
.private_info
= file
->private_data
;
646 msg
->parm
.fops_msg
.flags
= file
->flags
;
647 /* pid is NOT copied back. */
649 /* send message to kernel */
650 if (msg
->datalen
&& msg
->data
!= NULL
) {
651 iov
[0].iov_base
= msg
;
652 iov
[0].iov_len
= sizeof(fusd_msg_t
);
653 iov
[1].iov_base
= msg
->data
;
654 iov
[1].iov_len
= msg
->datalen
;
655 driver_retval
= writev(fd
, iov
, 2);
658 driver_retval
= write(fd
, msg
, sizeof(fusd_msg_t
));
664 if (driver_retval
< 0)
671 /* returns static string representing the flagset (e.g. RWE) */
673 char *fusd_unparse_flags(int flags
)
676 static char ringbuf
[RING
][5];
677 char *s
= ringbuf
[i
];
681 (flags
& FUSD_NOTIFY_INPUT
)?'R':'-',
682 (flags
& FUSD_NOTIFY_OUTPUT
)?'W':'-',
683 (flags
& FUSD_NOTIFY_EXCEPT
)?'E':'-');