source: rtems-libbsd/rtemsbsd/sys/fs/devfs/devfs_devs.c @ 817cbf7

5-freebsd-12
Last change on this file since 817cbf7 was 817cbf7, checked in by Kevin Kirspel <kevin-kirspel@…>, on May 12, 2017 at 12:16:27 PM

devfs: Fix some issues

Fix issue with cdev private data usage with RTEMS iop structure. Add
support for cdev alias device names. Add support for cdev fstat.

  • Property mode set to 100644
File size: 12.2 KB
Line 
1/*
2 * Copyright (c) 2016 embedded brains GmbH.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Dornierstr. 4
6 *  82178 Puchheim
7 *  Germany
8 *  <rtems@embedded-brains.de>
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <machine/rtems-bsd-kernel-space.h>
33
34#include <sys/types.h>
35#include <sys/conf.h>
36#include <sys/kernel.h>
37#include <sys/file.h>
38#include <sys/malloc.h>
39#include <sys/proc.h>
40#include <sys/poll.h>
41#include <stdlib.h>
42#include <string.h>
43#include <unistd.h>
44
45#include <fs/devfs/devfs_int.h>
46
47#include <machine/pcpu.h>
48
49#include <rtems/imfs.h>
50
51#define DEVFS_ROOTINO 2
52
53const char rtems_cdev_directory[] = RTEMS_CDEV_DIRECTORY;
54
55/*
56 * The one true (but secret) list of active devices in the system.
57 * Locked by dev_lock()/devmtx
58 */
59struct cdev_priv_list cdevp_list = TAILQ_HEAD_INITIALIZER(cdevp_list);
60
61struct unrhdr *devfs_inos;
62
63static MALLOC_DEFINE(M_CDEVP, "DEVFS1", "DEVFS cdev_priv storage");
64
65static struct cdev *
66devfs_imfs_get_context_by_iop(rtems_libio_t *iop)
67{
68        return IMFS_generic_get_context_by_iop(iop);
69}
70
71static int
72devfs_imfs_open(rtems_libio_t *iop, const char *path, int oflag, mode_t mode)
73{
74        struct cdev *cdev = devfs_imfs_get_context_by_iop(iop);
75        struct file *fp = rtems_bsd_iop_to_fp(iop);
76        struct thread *td = rtems_bsd_get_curthread_or_null();
77        struct file *fpop;
78        struct cdevsw *dsw;
79        int error, ref;
80
81        if (td != NULL) {
82                if (cdev == NULL) {
83                        error = ENXIO;
84                        goto err;
85                }
86                if (cdev->si_flags & SI_ALIAS) {
87                        cdev = cdev->si_parent;
88                }
89                dsw = dev_refthread(cdev, &ref);
90                if (dsw == NULL) {
91                        error = ENXIO;
92                        goto err;
93                }
94                if (fp == NULL) {
95                        dev_relthread(cdev, ref);
96                        error = ENXIO;
97                        goto err;
98                }
99                fpop = td->td_fpop;
100                curthread->td_fpop = fp;
101                error = dsw->d_open(cdev, oflag + 1, 0, td);
102                /* Clean up any cdevpriv upon error. */
103                if (error != 0)
104                        devfs_clear_cdevpriv();
105                curthread->td_fpop = fpop;
106                dev_relthread(cdev, ref);
107        } else {
108                error = ENOMEM;
109        }
110
111err:
112        return rtems_bsd_error_to_status_and_errno(error);
113}
114
115static int
116devfs_imfs_close(rtems_libio_t *iop)
117{
118        struct cdev *cdev = devfs_imfs_get_context_by_iop(iop);
119        struct file *fp = rtems_bsd_iop_to_fp(iop);
120        struct thread *td = rtems_bsd_get_curthread_or_null();
121        int flags = rtems_libio_to_fcntl_flags(iop->flags);
122        struct file *fpop;
123        struct cdevsw *dsw;
124        int error, ref;
125
126        if (td != NULL) {
127                if (cdev == NULL) {
128                        error = ENXIO;
129                        goto err;
130                }
131                if (cdev->si_flags & SI_ALIAS) {
132                        cdev = cdev->si_parent;
133                }
134                dsw = dev_refthread(cdev, &ref);
135                if (dsw == NULL) {
136                        error = ENXIO;
137                        goto err;
138                }
139                fpop = td->td_fpop;
140                curthread->td_fpop = fp;
141                error = dsw->d_close(cdev, flags, 0, td);
142                curthread->td_fpop = fpop;
143                dev_relthread(cdev, ref);
144                if (fp->f_cdevpriv != NULL)
145                        devfs_fpdrop(fp);
146        } else {
147                error = ENOMEM;
148        }
149
150err:
151        return rtems_bsd_error_to_status_and_errno(error);
152}
153
154static ssize_t
155devfs_imfs_readv(rtems_libio_t *iop, const struct iovec *iov, int iovcnt,
156    ssize_t total)
157{
158        struct cdev *cdev = devfs_imfs_get_context_by_iop(iop);
159        struct file *fp = rtems_bsd_iop_to_fp(iop);
160        struct thread *td = rtems_bsd_get_curthread_or_null();
161        struct uio uio = {
162                .uio_iov = __DECONST(struct iovec *, iov),
163                .uio_iovcnt = iovcnt,
164                .uio_offset = 0,
165                .uio_resid = total,
166                .uio_segflg = UIO_USERSPACE,
167                .uio_rw = UIO_READ,
168                .uio_td = td
169        };
170        struct file *fpop;
171        struct cdevsw *dsw;
172        int error, ref;
173
174        if (td != NULL) {
175                if (cdev == NULL) {
176                        error = ENXIO;
177                        goto err;
178                }
179                if (cdev->si_flags & SI_ALIAS) {
180                        cdev = cdev->si_parent;
181                }
182                dsw = dev_refthread(cdev, &ref);
183                if (dsw == NULL) {
184                        error = ENXIO;
185                        goto err;
186                }
187                fpop = td->td_fpop;
188                curthread->td_fpop = fp;
189                error = dsw->d_read(cdev, &uio,
190                    rtems_libio_to_fcntl_flags(iop->flags));
191                td->td_fpop = fpop;
192                dev_relthread(cdev, ref);
193        } else {
194                error = ENOMEM;
195        }
196
197err:
198        if (error == 0) {
199                return (total - uio.uio_resid);
200        } else {
201                rtems_set_errno_and_return_minus_one(error);
202        }
203}
204
205static ssize_t
206devfs_imfs_read(rtems_libio_t *iop, void *buffer, size_t count)
207{
208        struct iovec iov = {
209                .iov_base = buffer,
210                .iov_len = count
211        };
212
213        return (devfs_imfs_readv(iop, &iov, 1, count));
214}
215
216static ssize_t
217devfs_imfs_writev(rtems_libio_t *iop, const struct iovec *iov, int iovcnt,
218    ssize_t total)
219{
220        struct cdev *cdev = devfs_imfs_get_context_by_iop(iop);
221        struct file *fp = rtems_bsd_iop_to_fp(iop);
222        struct thread *td = rtems_bsd_get_curthread_or_null();
223        struct uio uio = {
224                .uio_iov = __DECONST(struct iovec *, iov),
225                .uio_iovcnt = iovcnt,
226                .uio_offset = 0,
227                .uio_resid = total,
228                .uio_segflg = UIO_USERSPACE,
229                .uio_rw = UIO_WRITE,
230                .uio_td = td
231        };
232        struct file *fpop;
233        struct cdevsw *dsw;
234        int error, ref;
235
236        if (td != NULL) {
237                if (cdev == NULL) {
238                        error = ENXIO;
239                        goto err;
240                }
241                if (cdev->si_flags & SI_ALIAS) {
242                        cdev = cdev->si_parent;
243                }
244                dsw = dev_refthread(cdev, &ref);
245                if (dsw == NULL) {
246                        error = ENXIO;
247                        goto err;
248                }
249                fpop = td->td_fpop;
250                curthread->td_fpop = fp;
251                error = dsw->d_write(cdev, &uio,
252                    rtems_libio_to_fcntl_flags(iop->flags));
253                td->td_fpop = fpop;
254                dev_relthread(cdev, ref);
255        } else {
256                error = ENOMEM;
257        }
258
259err:
260        if (error == 0) {
261                return (total - uio.uio_resid);
262        } else {
263                rtems_set_errno_and_return_minus_one(error);
264        }
265}
266
267static ssize_t
268devfs_imfs_write(rtems_libio_t *iop, const void *buffer, size_t count)
269{
270        struct iovec iov = {
271                .iov_base = __DECONST(void *, buffer),
272                .iov_len = count
273        };
274
275        return (devfs_imfs_writev(iop, &iov, 1, count));
276}
277
278static int
279devfs_imfs_ioctl(rtems_libio_t *iop, ioctl_command_t request, void *buffer)
280{
281        struct cdev *cdev = devfs_imfs_get_context_by_iop(iop);
282        struct file *fp = rtems_bsd_iop_to_fp(iop);
283        struct thread *td = rtems_bsd_get_curthread_or_null();
284        struct file *fpop;
285        struct cdevsw *dsw;
286        int error, ref;
287        int flags = rtems_libio_to_fcntl_flags(iop->flags);
288
289        if (td != 0) {
290                if (cdev == NULL) {
291                        error = ENXIO;
292                        goto err;
293                }
294                if (cdev->si_flags & SI_ALIAS) {
295                        cdev = cdev->si_parent;
296                }
297                dsw = dev_refthread(cdev, &ref);
298                if (dsw == NULL) {
299                        error = ENXIO;
300                        goto err;
301                }
302                fpop = td->td_fpop;
303                curthread->td_fpop = fp;
304                error = dsw->d_ioctl(cdev, request, buffer, flags,
305                    td);
306                td->td_fpop = fpop;
307                dev_relthread(cdev, ref);
308        } else {
309                error = ENOMEM;
310        }
311
312err:
313        return rtems_bsd_error_to_status_and_errno(error);
314}
315
316static int
317devfs_imfs_fstat(const rtems_filesystem_location_info_t *loc, struct stat *buf)
318{
319        int rv = 0;
320        const IMFS_jnode_t *the_dev = loc->node_access;
321
322        if (the_dev != NULL) {
323                buf->st_mode = the_dev->st_mode;
324        } else {
325                rv = rtems_filesystem_default_fstat(loc, buf);
326        }
327
328        return rv;
329}
330
331static int
332devfs_imfs_poll(rtems_libio_t *iop, int events)
333{
334        struct cdev *cdev = devfs_imfs_get_context_by_iop(iop);
335        struct file *fp = rtems_bsd_iop_to_fp(iop);
336        struct thread *td = rtems_bsd_get_curthread_or_wait_forever();
337        struct file *fpop;
338        struct cdevsw *dsw;
339        int error, ref;
340
341        if (cdev == NULL) {
342                return POLLERR;
343        }
344        if (cdev->si_flags & SI_ALIAS) {
345                cdev = cdev->si_parent;
346        }
347        dsw = dev_refthread(cdev, &ref);
348        if (dsw == NULL) {
349                return POLLERR;
350        }
351        fpop = td->td_fpop;
352        curthread->td_fpop = fp;
353        error = dsw->d_poll(cdev, events, td);
354        td->td_fpop = fpop;
355        dev_relthread(cdev, ref);
356
357        return error;
358}
359
360static int
361devfs_imfs_kqfilter(rtems_libio_t *iop, struct knote *kn)
362{
363        struct cdev *cdev = devfs_imfs_get_context_by_iop(iop);
364        struct file *fp = rtems_bsd_iop_to_fp(iop);
365        struct thread *td = rtems_bsd_get_curthread_or_wait_forever();
366        struct file *fpop;
367        struct cdevsw *dsw;
368        int error, ref;
369
370        if (cdev == NULL) {
371                return EINVAL;
372        }
373        if (cdev->si_flags & SI_ALIAS) {
374                cdev = cdev->si_parent;
375        }
376        dsw = dev_refthread(cdev, &ref);
377        if (dsw == NULL) {
378                return EINVAL;
379        }
380        fpop = td->td_fpop;
381        curthread->td_fpop = fp;
382        error = dsw->d_kqfilter(cdev, kn);
383        td->td_fpop = fpop;
384        dev_relthread(cdev, ref);
385
386        return error;
387}
388
389static const rtems_filesystem_file_handlers_r devfs_imfs_handlers = {
390        .open_h = devfs_imfs_open,
391        .close_h = devfs_imfs_close,
392        .read_h = devfs_imfs_read,
393        .write_h = devfs_imfs_write,
394        .ioctl_h = devfs_imfs_ioctl,
395        .lseek_h = rtems_filesystem_default_lseek_file,
396        .fstat_h = devfs_imfs_fstat,
397        .ftruncate_h = rtems_filesystem_default_ftruncate,
398        .fsync_h = rtems_filesystem_default_fsync_or_fdatasync,
399        .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync,
400        .fcntl_h = rtems_filesystem_default_fcntl,
401        .poll_h = devfs_imfs_poll,
402        .kqfilter_h = devfs_imfs_kqfilter,
403        .readv_h = devfs_imfs_readv,
404        .writev_h = devfs_imfs_writev,
405};
406
407static const IMFS_node_control devfs_imfs_control = IMFS_GENERIC_INITIALIZER(
408    &devfs_imfs_handlers, IMFS_node_initialize_generic,
409    IMFS_node_destroy_default);
410
411struct cdev *
412devfs_alloc(int flags)
413{
414        struct cdev_priv *cdp;
415        struct cdev *cdev;
416
417        cdp = malloc(sizeof *cdp, M_CDEVP, M_ZERO |
418            ((flags & MAKEDEV_NOWAIT) ? M_NOWAIT : M_WAITOK));
419        if (cdp == NULL)
420                return (NULL);
421
422        cdev = &cdp->cdp_c;
423        LIST_INIT(&cdev->si_children);
424
425        memcpy(cdev->si_path, rtems_cdev_directory, sizeof(cdev->si_path));
426        return (cdev);
427}
428
429void
430devfs_free(struct cdev *cdev)
431{
432        struct cdev_priv *cdp;
433
434        cdp = cdev2priv(cdev);
435        devfs_free_cdp_inode(cdp->cdp_inode);
436        free(cdp, M_CDEVP);
437}
438
439/*
440 * Create the directory for a device.
441 * Note: This don't uses dirname() because this function is not defined thread
442 * save in POSIX.
443 */
444static void
445devfs_create_directory(const char *devname)
446{
447        char *dir = NULL;
448        char *lastsep = NULL;
449        int rv;
450        mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
451
452        dir = strdup(devname, M_TEMP);
453        BSD_ASSERT(dir != NULL);
454
455        lastsep = strrchr(dir, '/');
456        if(lastsep != NULL) {
457                *lastsep = 0;
458                rv = rtems_mkdir(dir, mode);
459                BSD_ASSERT(rv == 0);
460        }
461
462        free(dir, M_TEMP);
463}
464
465/*
466 * devfs_create() and devfs_destroy() are called from kern_conf.c and
467 * in both cases the devlock() mutex is held, so no further locking
468 * is necesary and no sleeping allowed.
469 */
470
471void
472devfs_create(struct cdev *dev)
473{
474        struct cdev_priv *cdp;
475        int rv;
476        mode_t mode = S_IFCHR | S_IRWXU | S_IRWXG | S_IRWXO;
477
478        devfs_create_directory(dev->si_path);
479
480        rv = IMFS_make_generic_node(dev->si_path, mode, &devfs_imfs_control,
481            dev);
482        BSD_ASSERT(rv == 0);
483
484        cdp = cdev2priv(dev);
485        cdp->cdp_flags |= CDP_ACTIVE;
486        cdp->cdp_inode = alloc_unrl(devfs_inos);
487        dev_refl(dev);
488        TAILQ_INSERT_TAIL(&cdevp_list, cdp, cdp_list);
489}
490
491void
492devfs_destroy(struct cdev *dev)
493{
494        struct cdev_priv *cdp;
495        int rv;
496
497        cdp = cdev2priv(dev);
498        cdp->cdp_flags &= ~CDP_ACTIVE;
499
500        rv = unlink(dev->si_path);
501        BSD_ASSERT(rv == 0);
502
503        /* FIXME: Check if directory is empty and remove it. */
504}
505
506int
507devfs_dev_exists(const char *name)
508{
509        const size_t dirlen = sizeof(rtems_cdev_directory) - 1;
510        const size_t maxnamelen = SPECNAMELEN;
511
512        int rv;
513        char fullpath[dirlen + maxnamelen + 1];
514
515        memcpy(fullpath, rtems_cdev_directory, dirlen);
516        strncpy(fullpath + dirlen, name, maxnamelen);
517
518        rv = access(fullpath, F_OK);
519
520        if(rv == 0)
521                return 1;
522        else
523                return 0;
524}
525
526ino_t
527devfs_alloc_cdp_inode(void)
528{
529
530        return (alloc_unr(devfs_inos));
531}
532
533void
534devfs_free_cdp_inode(ino_t ino)
535{
536
537        if (ino > 0)
538                free_unr(devfs_inos, ino);
539}
540
541static void
542        devfs_devs_init(void *junk __unused)
543{
544
545        devfs_inos = new_unrhdr(DEVFS_ROOTINO + 1, INT_MAX, &devmtx);
546}
547
548SYSINIT(devfs_devs, SI_SUB_DEVFS, SI_ORDER_FIRST, devfs_devs_init, NULL);
Note: See TracBrowser for help on using the repository browser.