xref: /third_party/NuttX/drivers/pipes/pipe.c (revision beacf11b)
1/****************************************************************************
2 * drivers/pipes/pipe.c
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements.  See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.  The
7 * ASF licenses this file to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance with the
9 * License.  You may obtain a copy of the License at
10 *
11 *   http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
16 * License for the specific language governing permissions and limitations
17 * under the License.
18 *
19 ****************************************************************************/
20
21/****************************************************************************
22 * Included Files
23 ****************************************************************************/
24#include "pipe_common.h"
25#include <assert.h>
26#include <errno.h>
27#include <fcntl.h>
28#include <semaphore.h>
29#include <stdio.h>
30#include <sys/types.h>
31#include <unistd.h>
32#include "fs/driver.h"
33#include "los_init.h"
34
35#if CONFIG_DEV_PIPE_SIZE > 0
36
37/****************************************************************************
38 * Pre-processor Definitions
39 ****************************************************************************/
40
41#define MAX_PIPES 32
42
43/****************************************************************************
44 * Private Types
45 ****************************************************************************/
46
47/****************************************************************************
48 * Private Function Prototypes
49 ****************************************************************************/
50
51static int pipe_close(struct file *filep);
52#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
53int pipe_unlink(struct Vnode *priv);
54#endif
55
56/****************************************************************************
57 * Private Data
58 ****************************************************************************/
59
60static ssize_t pipe_map(struct file* filep, LosVmMapRegion *region)
61{
62  PRINTK("%s %d, mmap is not support\n", __FUNCTION__, __LINE__);
63  return 0;
64}
65
66static const struct file_operations_vfs pipe_fops =
67{
68  .open = pipecommon_open,      /* open */
69  .close = pipe_close,          /* close */
70  .read = pipecommon_read,      /* read */
71  .write = pipecommon_write,    /* write */
72  .seek = NULL,                 /* seek */
73  .ioctl = NULL,                /* ioctl */
74  .mmap = pipe_map,             /* mmap */
75  .poll = pipecommon_poll,      /* poll */
76#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
77  .unlink = pipe_unlink,        /* unlink */
78#endif
79};
80
81static sem_t  g_pipesem       = {NULL};
82static uint32_t g_pipeset     = 0;
83static uint32_t g_pipecreated = 0;
84
85/****************************************************************************
86 * Private Functions
87 ****************************************************************************/
88
89/****************************************************************************
90 * Name: pipe_allocate
91 ****************************************************************************/
92
93static inline int pipe_allocate(void)
94{
95  int pipeno;
96  int ret = -ENFILE;
97
98  for (pipeno = 0; pipeno < MAX_PIPES; pipeno++)
99    {
100      if ((g_pipeset & (1 << pipeno)) == 0)
101        {
102          g_pipeset |= (1 << pipeno);
103          ret = pipeno;
104          break;
105        }
106    }
107
108  return ret;
109}
110
111/****************************************************************************
112 * Name: pipe_free
113 ****************************************************************************/
114
115static inline void pipe_free(int pipeno)
116{
117  int ret;
118
119  ret = sem_wait(&g_pipesem);
120  if (ret == OK)
121    {
122      g_pipeset &= ~(1 << pipeno);
123      (void)sem_post(&g_pipesem);
124    }
125}
126
127/****************************************************************************
128 * Name: pipe_close
129 ****************************************************************************/
130
131static int pipe_close(struct file *filep)
132{
133  struct Vnode *vnode    = filep->f_vnode;
134  struct pipe_dev_s *dev = (struct pipe_dev_s *)((struct drv_data *)vnode->data)->priv;
135  int ret;
136
137  if (dev == NULL)
138    {
139      return -EINVAL;
140    }
141
142  /* Perform common close operations */
143
144  ret = pipecommon_close(filep);
145  if (ret == 0 && vnode->useCount <= 1)
146    {
147      /* Release the pipe when there are no further open references to it. */
148
149      pipe_free(dev->d_pipeno);
150    }
151
152  return ret;
153}
154
155/****************************************************************************
156 * Name: pipe_unlink
157 ****************************************************************************/
158
159#ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
160 int pipe_unlink(struct Vnode *vnode)
161{
162  struct pipe_dev_s *dev = ((struct drv_data *)vnode->data)->priv;
163  uint8_t pipeno = 0;
164  int ret;
165
166  if (dev != NULL)
167    {
168        pipeno = dev->d_pipeno;
169    }
170  /* Perform common close operations */
171  ret = pipecommon_unlink(vnode);
172  if (ret == 0)
173    {
174      (void)sem_wait(&g_pipesem);
175      g_pipecreated &= ~(1 << pipeno);
176      (void)sem_post(&g_pipesem);
177      /* Release the pipe when there are no further open references to it. */
178      pipe_free(pipeno);
179    }
180  return ret;
181}
182#endif
183
184/****************************************************************************
185 * Public Functions
186 ****************************************************************************/
187
188/****************************************************************************
189 * Name: pipe2
190 *
191 * Description:
192 *   pipe() creates a pair of file descriptors, pointing to a pipe vnode,
193 *   and  places them in the array pointed to by 'fd'. fd[0] is for reading,
194 *   fd[1] is for writing.
195 *
196 *   NOTE: mkfifo2 is a special, non-standard, NuttX-only interface.  Since
197 *   the NuttX FIFOs are based in in-memory, circular buffers, the ability
198 *   to control the size of those buffers is critical for system tuning.
199 *
200 * Input Parameters:
201 *   fd[2] - The user provided array in which to catch the pipe file
202 *   descriptors
203 *   bufsize - The size of the in-memory, circular buffer in bytes.
204 *
205 * Returned Value:
206 *   0 is returned on success; otherwise, -1 is returned with errno set
207 *   appropriately.
208 *
209 ****************************************************************************/
210
211static void UpdateDev(struct pipe_dev_s *dev)
212{
213  int ret;
214  struct Vnode *vnode = NULL;
215  struct pipe_dev_s *olddev = NULL;
216  struct drv_data *data = NULL;
217
218  VnodeHold();
219  ret = VnodeLookup(dev->name, &vnode, 0);
220  if (ret != 0)
221    {
222      VnodeDrop();
223      PRINT_ERR("[%s,%d] failed. err: %d\n", __FUNCTION__, __LINE__, ret);
224      return;
225    }
226  data = (struct drv_data *)vnode->data;
227  olddev = (struct pipe_dev_s *)data->priv;
228  if (olddev != NULL)
229    {
230      if (olddev->d_buffer != NULL)
231        {
232          free(olddev->d_buffer);
233          olddev->d_buffer = NULL;
234        }
235      pipecommon_freedev(olddev);
236    }
237  data->priv = dev;
238  VnodeDrop();
239  return;
240}
241
242int pipe(int fd[2])
243{
244  struct pipe_dev_s *dev = NULL;
245  char devname[16];
246  int pipeno;
247  int errcode;
248  int ret;
249  struct file *filep = NULL;
250  size_t bufsize = 1024;
251
252  /* Get exclusive access to the pipe allocation data */
253
254  ret = sem_wait(&g_pipesem);
255  if (ret < 0)
256    {
257      errcode = -ret;
258      goto errout;
259    }
260
261  /* Allocate a minor number for the pipe device */
262
263  pipeno = pipe_allocate();
264  if (pipeno < 0)
265    {
266      (void)sem_post(&g_pipesem);
267      errcode = -pipeno;
268      goto errout;
269    }
270
271  /* Create a pathname to the pipe device */
272
273  snprintf_s(devname, sizeof(devname), sizeof(devname) - 1, "/dev/pipe%d", pipeno);
274
275  /* No.. Allocate and initialize a new device structure instance */
276
277  dev = pipecommon_allocdev(bufsize, devname);
278  if (!dev)
279    {
280      (void)sem_post(&g_pipesem);
281      errcode = ENOMEM;
282      goto errout_with_pipe;
283    }
284
285  dev->d_pipeno = pipeno;
286
287  /* Check if the pipe device has already been created */
288
289  if ((g_pipecreated & (1 << pipeno)) == 0)
290    {
291      /* Register the pipe device */
292
293      ret = register_driver(devname, &pipe_fops, 0660, (void *)dev);
294      if (ret != 0)
295        {
296          (void)sem_post(&g_pipesem);
297          errcode = -ret;
298          goto errout_with_dev;
299        }
300
301      /* Remember that we created this device */
302
303       g_pipecreated |= (1 << pipeno);
304    }
305  else
306    {
307       UpdateDev(dev);
308    }
309  (void)sem_post(&g_pipesem);
310
311  /* Get a write file descriptor */
312
313  fd[1] = open(devname, O_WRONLY);
314  if (fd[1] < 0)
315    {
316      errcode = -fd[1];
317      goto errout_with_driver;
318    }
319
320  /* Get a read file descriptor */
321
322  fd[0] = open(devname, O_RDONLY);
323  if (fd[0] < 0)
324    {
325      errcode = -fd[0];
326      goto errout_with_wrfd;
327    }
328
329  ret = fs_getfilep(fd[0], &filep);
330  filep->ops = &pipe_fops;
331
332  ret = fs_getfilep(fd[1], &filep);
333  filep->ops = &pipe_fops;
334
335  return OK;
336
337errout_with_wrfd:
338  close(fd[1]);
339
340errout_with_driver:
341  unregister_driver(devname);
342  (void)sem_wait(&g_pipesem);
343  g_pipecreated &= ~(1 << pipeno);
344  (void)sem_post(&g_pipesem);
345
346errout_with_dev:
347  if (dev)
348    {
349      pipecommon_freedev(dev);
350    }
351
352errout_with_pipe:
353  pipe_free(pipeno);
354
355errout:
356  set_errno(errcode);
357  return VFS_ERROR;
358}
359
360int pipe_init(void)
361{
362    int ret = sem_init(&g_pipesem, 0, 1);
363    if (ret != 0) {
364        dprintf("pipe_init failed!\n");
365    }
366    return ret;
367}
368
369LOS_MODULE_INIT(pipe_init, LOS_INIT_LEVEL_KMOD_EXTENDED);
370
371#endif /* CONFIG_DEV_PIPE_SIZE > 0 */
372