1 /****************************************************************************
2 * drivers/pipes/pipe.c
3 *
4 * Copyright (C) 2008-2009, 2015, 2018 Gregory Nutt. All rights reserved.
5 * Author: Gregory Nutt <gnutt@nuttx.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name NuttX nor the names of its contributors may be
18 * used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 ****************************************************************************/
35
36 /****************************************************************************
37 * Included Files
38 ****************************************************************************/
39 #include "pipe_common.h"
40 #include <assert.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <semaphore.h>
44 #include <stdio.h>
45 #include <sys/types.h>
46 #include <unistd.h>
47 #include "fs/driver.h"
48 #include "los_init.h"
49
50 #if CONFIG_DEV_PIPE_SIZE > 0
51
52 /****************************************************************************
53 * Pre-processor Definitions
54 ****************************************************************************/
55
56 #define MAX_PIPES 32
57
58 /****************************************************************************
59 * Private Types
60 ****************************************************************************/
61
62 /****************************************************************************
63 * Private Function Prototypes
64 ****************************************************************************/
65
66 static int pipe_close(struct file *filep);
67 #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
68 int pipe_unlink(struct Vnode *priv);
69 #endif
70
71 /****************************************************************************
72 * Private Data
73 ****************************************************************************/
74
pipe_map(struct file * filep,LosVmMapRegion * region)75 static ssize_t pipe_map(struct file* filep, LosVmMapRegion *region)
76 {
77 PRINTK("%s %d, mmap is not support\n", __FUNCTION__, __LINE__);
78 return 0;
79 }
80
81 static const struct file_operations_vfs pipe_fops =
82 {
83 .open = pipecommon_open, /* open */
84 .close = pipe_close, /* close */
85 .read = pipecommon_read, /* read */
86 .write = pipecommon_write, /* write */
87 .seek = NULL, /* seek */
88 .ioctl = NULL, /* ioctl */
89 .mmap = pipe_map, /* mmap */
90 .poll = pipecommon_poll, /* poll */
91 #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
92 .unlink = pipe_unlink, /* unlink */
93 #endif
94 };
95
96 static sem_t g_pipesem = {NULL};
97 static uint32_t g_pipeset = 0;
98 static uint32_t g_pipecreated = 0;
99
100 /****************************************************************************
101 * Private Functions
102 ****************************************************************************/
103
104 /****************************************************************************
105 * Name: pipe_allocate
106 ****************************************************************************/
107
pipe_allocate(void)108 static inline int pipe_allocate(void)
109 {
110 int pipeno;
111 int ret = -ENFILE;
112
113 for (pipeno = 0; pipeno < MAX_PIPES; pipeno++)
114 {
115 if ((g_pipeset & (1 << pipeno)) == 0)
116 {
117 g_pipeset |= (1 << pipeno);
118 ret = pipeno;
119 break;
120 }
121 }
122
123 return ret;
124 }
125
126 /****************************************************************************
127 * Name: pipe_free
128 ****************************************************************************/
129
pipe_free(int pipeno)130 static inline void pipe_free(int pipeno)
131 {
132 int ret;
133
134 ret = sem_wait(&g_pipesem);
135 if (ret == OK)
136 {
137 g_pipeset &= ~(1 << pipeno);
138 (void)sem_post(&g_pipesem);
139 }
140 }
141
142 /****************************************************************************
143 * Name: pipe_close
144 ****************************************************************************/
145
pipe_close(struct file * filep)146 static int pipe_close(struct file *filep)
147 {
148 struct Vnode *vnode = filep->f_vnode;
149 struct pipe_dev_s *dev = (struct pipe_dev_s *)((struct drv_data *)vnode->data)->priv;
150 int ret;
151
152 if (dev == NULL)
153 {
154 return -EINVAL;
155 }
156
157 /* Perform common close operations */
158
159 ret = pipecommon_close(filep);
160 if (ret == 0 && vnode->useCount <= 1)
161 {
162 /* Release the pipe when there are no further open references to it. */
163
164 pipe_free(dev->d_pipeno);
165 }
166
167 return ret;
168 }
169
170 /****************************************************************************
171 * Name: pipe_unlink
172 ****************************************************************************/
173
174 #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
pipe_unlink(struct Vnode * vnode)175 int pipe_unlink(struct Vnode *vnode)
176 {
177 struct pipe_dev_s *dev = ((struct drv_data *)vnode->data)->priv;
178 uint8_t pipeno = 0;
179 int ret;
180
181 if (dev != NULL)
182 {
183 pipeno = dev->d_pipeno;
184 }
185 /* Perform common close operations */
186 ret = pipecommon_unlink(vnode);
187 if (ret == 0)
188 {
189 (void)sem_wait(&g_pipesem);
190 g_pipecreated &= ~(1 << pipeno);
191 (void)sem_post(&g_pipesem);
192 /* Release the pipe when there are no further open references to it. */
193 pipe_free(pipeno);
194 }
195 return ret;
196 }
197 #endif
198
199 /****************************************************************************
200 * Public Functions
201 ****************************************************************************/
202
203 /****************************************************************************
204 * Name: pipe2
205 *
206 * Description:
207 * pipe() creates a pair of file descriptors, pointing to a pipe vnode,
208 * and places them in the array pointed to by 'fd'. fd[0] is for reading,
209 * fd[1] is for writing.
210 *
211 * NOTE: mkfifo2 is a special, non-standard, NuttX-only interface. Since
212 * the NuttX FIFOs are based in in-memory, circular buffers, the ability
213 * to control the size of those buffers is critical for system tuning.
214 *
215 * Input Parameters:
216 * fd[2] - The user provided array in which to catch the pipe file
217 * descriptors
218 * bufsize - The size of the in-memory, circular buffer in bytes.
219 *
220 * Returned Value:
221 * 0 is returned on success; otherwise, -1 is returned with errno set
222 * appropriately.
223 *
224 ****************************************************************************/
225
UpdateDev(struct pipe_dev_s * dev)226 static void UpdateDev(struct pipe_dev_s *dev)
227 {
228 int ret;
229 struct Vnode *vnode = NULL;
230 struct pipe_dev_s *olddev = NULL;
231 struct drv_data *data = NULL;
232
233 VnodeHold();
234 ret = VnodeLookup(dev->name, &vnode, 0);
235 if (ret != 0)
236 {
237 VnodeDrop();
238 PRINT_ERR("[%s,%d] failed. err: %d\n", __FUNCTION__, __LINE__, ret);
239 return;
240 }
241 data = (struct drv_data *)vnode->data;
242 olddev = (struct pipe_dev_s *)data->priv;
243 if (olddev != NULL)
244 {
245 if (olddev->d_buffer != NULL)
246 {
247 free(olddev->d_buffer);
248 olddev->d_buffer = NULL;
249 }
250 pipecommon_freedev(olddev);
251 }
252 data->priv = dev;
253 VnodeDrop();
254 return;
255 }
256
pipe(int fd[2])257 int pipe(int fd[2])
258 {
259 struct pipe_dev_s *dev = NULL;
260 char devname[16];
261 int pipeno;
262 int errcode;
263 int ret;
264 struct file *filep = NULL;
265 size_t bufsize = 1024;
266
267 /* Get exclusive access to the pipe allocation data */
268
269 ret = sem_wait(&g_pipesem);
270 if (ret < 0)
271 {
272 errcode = -ret;
273 goto errout;
274 }
275
276 /* Allocate a minor number for the pipe device */
277
278 pipeno = pipe_allocate();
279 if (pipeno < 0)
280 {
281 (void)sem_post(&g_pipesem);
282 errcode = -pipeno;
283 goto errout;
284 }
285
286 /* Create a pathname to the pipe device */
287
288 snprintf_s(devname, sizeof(devname), sizeof(devname) - 1, "/dev/pipe%d", pipeno);
289
290 /* No.. Allocate and initialize a new device structure instance */
291
292 dev = pipecommon_allocdev(bufsize, devname);
293 if (!dev)
294 {
295 (void)sem_post(&g_pipesem);
296 errcode = ENOMEM;
297 goto errout_with_pipe;
298 }
299
300 dev->d_pipeno = pipeno;
301
302 /* Check if the pipe device has already been created */
303
304 if ((g_pipecreated & (1 << pipeno)) == 0)
305 {
306 /* Register the pipe device */
307
308 ret = register_driver(devname, &pipe_fops, 0660, (void *)dev);
309 if (ret != 0)
310 {
311 (void)sem_post(&g_pipesem);
312 errcode = -ret;
313 goto errout_with_dev;
314 }
315
316 /* Remember that we created this device */
317
318 g_pipecreated |= (1 << pipeno);
319 }
320 else
321 {
322 UpdateDev(dev);
323 }
324 (void)sem_post(&g_pipesem);
325
326 /* Get a write file descriptor */
327
328 fd[1] = open(devname, O_WRONLY);
329 if (fd[1] < 0)
330 {
331 errcode = -fd[1];
332 goto errout_with_driver;
333 }
334
335 /* Get a read file descriptor */
336
337 fd[0] = open(devname, O_RDONLY);
338 if (fd[0] < 0)
339 {
340 errcode = -fd[0];
341 goto errout_with_wrfd;
342 }
343
344 ret = fs_getfilep(fd[0], &filep);
345 filep->ops = &pipe_fops;
346
347 ret = fs_getfilep(fd[1], &filep);
348 filep->ops = &pipe_fops;
349
350 return OK;
351
352 errout_with_wrfd:
353 close(fd[1]);
354
355 errout_with_driver:
356 unregister_driver(devname);
357 (void)sem_wait(&g_pipesem);
358 g_pipecreated &= ~(1 << pipeno);
359 (void)sem_post(&g_pipesem);
360
361 errout_with_dev:
362 if (dev)
363 {
364 pipecommon_freedev(dev);
365 }
366
367 errout_with_pipe:
368 pipe_free(pipeno);
369
370 errout:
371 set_errno(errcode);
372 return VFS_ERROR;
373 }
374
pipe_init()375 int pipe_init()
376 {
377 int ret = sem_init(&g_pipesem, 0, 1);
378 if (ret != 0) {
379 dprintf("pipe_init failed!\n");
380 }
381 return ret;
382 }
383
384 LOS_MODULE_INIT(pipe_init, LOS_INIT_LEVEL_KMOD_EXTENDED);
385
386 #endif /* CONFIG_DEV_PIPE_SIZE > 0 */
387