• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  * drivers/pipes/pipe_common.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 
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/ioctl.h>
28 #include <stdint.h>
29 #include <stdbool.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sched.h>
33 #include <semaphore.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <poll.h>
37 #include "linux/wait.h"
38 #include <assert.h>
39 
40 #ifdef CONFIG_DEBUG_FEATURES
41 #  include <nuttx/arch.h>
42 #endif
43 #include <semaphore.h>
44 #include <fs/driver.h>
45 #include "pipe_common.h"
46 #include "los_printf.h"
47 #include "user_copy.h"
48 #ifdef LOSCFG_KERNEL_PIPE
49 
50 /****************************************************************************
51  * Pre-processor Definitions
52  ****************************************************************************/
53 
54 /* CONFIG_DEV_PIPEDUMP will dump the contents of each transfer into and out
55  * of the pipe.
56  */
57 
58 #ifdef CONFIG_DEV_PIPEDUMP
59 #  define pipe_dumpbuffer(m,a,n) lib_dumpbuffer(m,a,n)
60 #else
61 #  define pipe_dumpbuffer(m,a,n)
62 #endif
63 
64 /****************************************************************************
65  * Private Function Prototypes
66  ****************************************************************************/
67 
68 static void pipecommon_semtake(sem_t *sem);
69 
70 /****************************************************************************
71  * Private Functions
72  ****************************************************************************/
73 
74 /****************************************************************************
75  * Name: pipecommon_semtake
76  ****************************************************************************/
77 
pipecommon_semtake(sem_t * sem)78 static void pipecommon_semtake(sem_t *sem)
79 {
80   int ret;
81 
82   do
83     {
84       /* Take the semaphore (perhaps waiting) */
85 
86       ret = sem_wait(sem);
87 
88     }
89   while (ret == -EINTR);
90 }
91 
92 /****************************************************************************
93  * Name: pipecommon_pollnotify
94  ****************************************************************************/
95 
pipecommon_pollnotify(struct pipe_dev_s * dev,pollevent_t eventset)96 static void pipecommon_pollnotify(struct pipe_dev_s *dev,
97                                   pollevent_t eventset)
98 {
99   if (eventset & POLLERR)
100     {
101       eventset &= ~(POLLOUT | POLLIN);
102     }
103 
104   notify_poll_with_key(&dev->wq, eventset);
105 }
106 
107 /****************************************************************************
108  * Public Functions
109  ****************************************************************************/
110 
111 /****************************************************************************
112  * Name: pipecommon_allocdev
113  ****************************************************************************/
114 
pipecommon_allocdev(size_t bufsize,const char * name)115 struct pipe_dev_s *pipecommon_allocdev(size_t bufsize, const char *name)
116 {
117   struct pipe_dev_s *dev = NULL;
118   int ret;
119 
120   if (bufsize > CONFIG_DEV_PIPE_MAXSIZE)
121     {
122       return NULL;
123     }
124 
125   /* Allocate a private structure to manage the pipe */
126 
127   dev = (struct pipe_dev_s *)malloc(sizeof(struct pipe_dev_s));
128   if (dev)
129     {
130       /* Initialize the private structure */
131 
132       (void)memset_s(dev, sizeof(struct pipe_dev_s), 0, sizeof(struct pipe_dev_s));
133       ret = strcpy_s(dev->name, sizeof(dev->name), name);
134       if (ret < 0)
135         {
136           free(dev);
137           return NULL;
138         }
139       sem_init(&dev->d_bfsem, 0, 1);
140       sem_init(&dev->d_rdsem, 0, 0);
141       sem_init(&dev->d_wrsem, 0, 0);
142       LOS_ListInit(&dev->wq.poll_queue);
143      /* The read/write wait semaphores are used for signaling and, hence,
144       * should not have priority inheritance enabled.
145       */
146 
147       dev->d_bufsize = bufsize;
148     }
149 
150   return dev;
151 }
152 
153 /****************************************************************************
154  * Name: pipecommon_freedev
155  ****************************************************************************/
156 
pipecommon_freedev(struct pipe_dev_s * dev)157 void pipecommon_freedev(struct pipe_dev_s *dev)
158 {
159   sem_destroy(&dev->d_bfsem);
160   sem_destroy(&dev->d_rdsem);
161   sem_destroy(&dev->d_wrsem);
162   free(dev);
163 }
164 
165 /****************************************************************************
166  * Name: pipecommon_open
167  ****************************************************************************/
168 
pipecommon_open(struct file * filep)169 int pipecommon_open(struct file *filep)
170 {
171   struct Vnode      *vnode = filep->f_vnode;
172   struct pipe_dev_s *dev   = (struct pipe_dev_s *)((struct drv_data *)vnode->data)->priv;
173   int                    sval;
174   int                    ret;
175 
176   if (dev == NULL)
177     {
178       return -EINVAL;
179     }
180 
181   /* Make sure that we have exclusive access to the device structure.  The
182    * sem_wait() call should fail only if we are awakened by a signal.
183    */
184 
185   ret = sem_wait(&dev->d_bfsem);
186   if (ret < 0)
187     {
188       PRINT_ERR("ERROR: sem_wait failed: %d\n", ret);
189       return ret;
190     }
191 
192   /* If this the first reference on the device, then allocate the buffer.
193    * In the case of policy 1, the buffer already be present when the pipe
194    * is first opened.
195    */
196 
197   if (vnode->useCount == 1 && dev->d_buffer == NULL)
198     {
199       dev->d_buffer = (uint8_t *)malloc(dev->d_bufsize);
200       if (!dev->d_buffer)
201         {
202           (void)sem_post(&dev->d_bfsem);
203           return -ENOMEM;
204         }
205     }
206 
207 
208   /* If opened for writing, increment the count of writers on the pipe instance */
209 
210   if ((filep->f_oflags & O_WRONLY) != 0)
211     {
212       dev->d_nwriters++;
213 
214       /* If this this is the first writer, then the read semaphore indicates the
215        * number of readers waiting for the first writer.  Wake them all up.
216        */
217 
218       if (dev->d_nwriters == 1)
219         {
220           while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval == 0)
221             {
222               sem_post(&dev->d_rdsem);
223             }
224         }
225     }
226 
227   /* If opened for reading, increment the count of reader on on the pipe instance */
228 
229   if ((filep->f_oflags & O_WRONLY) == 0)
230     {
231       dev->d_nreaders++;
232     }
233 
234   /* If opened for read-only, then wait for either (1) at least one writer
235    * on the pipe (policy == 0), or (2) until there is buffered data to be
236    * read (policy == 1).
237    */
238 
239   (void)sem_post(&dev->d_bfsem);
240 
241   if ((filep->f_oflags & O_WRONLY) == 0 &&       /* Read-only */
242       dev->d_nwriters < 1 &&                     /* No writers on the pipe */
243       dev->d_wrndx == dev->d_rdndx)              /* Buffer is empty */
244     {
245       /* NOTE: d_rdsem is normally used when the read logic waits for more
246        * data to be written.  But until the first writer has opened the
247        * pipe, the meaning is different: it is used prevent O_RDONLY open
248        * calls from returning until there is at least one writer on the pipe.
249        * This is required both by spec and also because it prevents
250        * subsequent read() calls from returning end-of-file because there is
251        * no writer on the pipe.
252        */
253 
254       ret = sem_wait(&dev->d_rdsem);
255       if (ret < 0)
256         {
257           /* The sem_wait() call should fail only if we are awakened by
258            * a signal.
259            */
260 
261           PRINT_ERR("ERROR: sem_wait failed: %d\n", ret);
262 
263           /* Immediately close the pipe that we just opened */
264 
265           (void)pipecommon_close(filep);
266         }
267     }
268   VnodeHold();
269   vnode->useCount++;
270   VnodeDrop();
271   return ret;
272 }
273 
274 /****************************************************************************
275  * Name: pipecommon_close
276  ****************************************************************************/
277 
pipecommon_close(struct file * filep)278 int pipecommon_close(struct file *filep)
279 {
280   struct Vnode      *vnode = filep->f_vnode;
281   struct pipe_dev_s *dev   = (struct pipe_dev_s *)((struct drv_data *)vnode->data)->priv;
282   int                    sval;
283 
284   if (dev == NULL || filep->f_vnode->useCount <= 1)
285     {
286       return -EINVAL;
287     }
288 
289   /* Make sure that we have exclusive access to the device structure.
290    * NOTE: close() is supposed to return EINTR if interrupted, however
291    * I've never seen anyone check that.
292    */
293 
294   pipecommon_semtake(&dev->d_bfsem);
295 
296   /* Decrement the number of references on the pipe.  Check if there are
297    * still outstanding references to the pipe.
298    */
299 
300   /* Check if the decremented vnode reference count would go to zero */
301 
302   if (vnode->useCount > 1)
303     {
304       /* More references.. If opened for writing, decrement the count of
305        * writers on the pipe instance.
306        */
307 
308       if ((filep->f_oflags & O_WRONLY) != 0)
309         {
310           /* If there are no longer any writers on the pipe, then notify all of the
311            * waiting readers that they must return end-of-file.
312            */
313 
314           if (--dev->d_nwriters <= 0)
315             {
316               while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval == 0)
317                 {
318                   sem_post(&dev->d_rdsem);
319                 }
320 
321               /* Inform poll readers that other end closed. */
322 
323               pipecommon_pollnotify(dev, POLLHUP);
324             }
325         }
326 
327       /* If opened for reading, decrement the count of readers on the pipe
328        * instance.
329        */
330 
331       if ((filep->f_oflags & O_WRONLY) == 0)
332         {
333           if (--dev->d_nreaders <= 0)
334             {
335               if (PIPE_IS_POLICY_0(dev->d_flags))
336                 {
337                   /* Inform poll writers that other end closed. */
338 
339                   pipecommon_pollnotify(dev, POLLERR);
340                 }
341             }
342         }
343     }
344 
345   /* What is the buffer management policy?  Do we free the buffer when the
346    * last client closes the pipe policy 0, or when the buffer becomes empty.
347    * In the latter case, the buffer data will remain valid and can be
348    * obtained when the pipe is re-opened.
349    */
350 
351   else if (PIPE_IS_POLICY_0(dev->d_flags) || dev->d_wrndx == dev->d_rdndx)
352     {
353       /* Policy 0 or the buffer is empty ... deallocate the buffer now. */
354 
355       free(dev->d_buffer);
356       dev->d_buffer = NULL;
357 
358       /* And reset all counts and indices */
359 
360       dev->d_wrndx    = 0;
361       dev->d_rdndx    = 0;
362       dev->d_nwriters = 0;
363       dev->d_nreaders = 0;
364     }
365   VnodeHold();
366   vnode->useCount--;
367   VnodeDrop();
368   sem_post(&dev->d_bfsem);
369   return OK;
370 }
371 
372 /****************************************************************************
373  * Name: pipecommon_read
374  ****************************************************************************/
375 
pipecommon_read(struct file * filep,char * buffer,size_t len)376 ssize_t pipecommon_read(struct file *filep, char *buffer, size_t len)
377 {
378   struct Vnode      *vnode  = filep->f_vnode;
379   struct pipe_dev_s *dev    = (struct pipe_dev_s *)((struct drv_data *)vnode->data)->priv;
380   ssize_t                nread;
381   int                    sval;
382   int                    ret;
383   volatile int           num;
384 
385   if (dev == NULL)
386     {
387       return -EINVAL;
388     }
389 
390   if (len == 0)
391     {
392       return 0;
393     }
394 
395   if (len > MAX_READ_WRITE_LEN)
396     {
397       return -EINVAL;
398     }
399 
400   /* Make sure that we have exclusive access to the device structure */
401 
402   ret = sem_wait(&dev->d_bfsem);
403   if (ret < 0)
404     {
405       return ret;
406     }
407 
408   /* If the pipe is empty, then wait for something to be written to it */
409 
410   while (dev->d_wrndx == dev->d_rdndx)
411     {
412       /* If O_NONBLOCK was set, then return EGAIN */
413 
414       if (filep->f_oflags & O_NONBLOCK)
415         {
416           sem_post(&dev->d_bfsem);
417           return -EAGAIN;
418         }
419 
420       /* If there are no writers on the pipe, then return end of file */
421 
422       if (dev->d_nwriters <= 0)
423         {
424           sem_post(&dev->d_bfsem);
425           return 0;
426         }
427 
428       /* Otherwise, wait for something to be written to the pipe */
429 
430       sem_post(&dev->d_bfsem);
431       ret = sem_wait(&dev->d_rdsem);
432 
433       if (ret < 0 || (ret = sem_wait(&dev->d_bfsem)) < 0)
434         {
435           return ret;
436         }
437     }
438 
439   /* Then return whatever is available in the pipe (which is at least one byte) */
440 
441   nread = 0;
442   for (; ; )
443     {
444       while ((size_t)nread < len && dev->d_wrndx != dev->d_rdndx)
445         {
446           ret = LOS_ArchCopyToUser(buffer, dev->d_buffer + dev->d_rdndx, sizeof(char));
447           if (ret != 0)
448             {
449               sem_post(&dev->d_bfsem);
450               return -EFAULT;
451             }
452           buffer++;
453           if (++dev->d_rdndx >= dev->d_bufsize)
454             {
455               dev->d_rdndx = 0;
456             }
457 
458           nread++;
459         }
460 
461       /* Is the read complete? */
462       if ((size_t)nread >= len)
463         {
464           break;
465         }
466 
467       /* Notify all waiting writers that bytes have been read from the buffer */
468 
469       num = 0;
470       while (sem_getvalue(&dev->d_wrsem, &sval) == 0 && sval == 0)
471         {
472           sem_post(&dev->d_wrsem);
473           num++;
474         }
475 
476       /* If there are no writers be awakened, then return */
477 
478       if (num == 0 || num == 1)
479         {
480           pipecommon_pollnotify(dev, POLLOUT);
481           sem_post(&dev->d_bfsem);
482           return nread;
483         }
484 
485       /* If there are no writers on the pipe, then return the number of read bytes */
486 
487       if (dev->d_nwriters <= 0)
488         {
489           sem_post(&dev->d_wrsem);
490           pipecommon_pollnotify(dev, POLLOUT);
491           sem_post(&dev->d_bfsem);
492           return nread;
493         }
494 
495       /* wait for something to be written to the pipe */
496       pipecommon_pollnotify(dev, POLLOUT);
497 
498       while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval != 0)
499         {
500           sem_wait(&dev->d_rdsem);
501         }
502 
503       sem_post(&dev->d_bfsem);
504 
505       ret = sem_wait(&dev->d_rdsem);
506       if (ret < 0)
507         {
508           return ret;
509         }
510 
511       ret = sem_wait(&dev->d_bfsem);
512       if (ret < 0)
513         {
514           return ret;
515         }
516     }
517 
518   /* Notify all waiting writers that bytes have been removed from the buffer */
519 
520   while (sem_getvalue(&dev->d_wrsem, &sval) == 0 && sval == 0)
521     {
522       sem_post(&dev->d_wrsem);
523     }
524 
525   /* Notify all poll/select waiters that they can write to the FIFO */
526 
527   pipecommon_pollnotify(dev, POLLOUT);
528   sem_post(&dev->d_bfsem);
529   return nread;
530 }
531 
532 /****************************************************************************
533  * Name: pipecommon_write
534  ****************************************************************************/
535 
pipecommon_write(struct file * filep,const char * buffer,size_t len)536 ssize_t pipecommon_write(struct file *filep, const char *buffer,
537                          size_t len)
538 {
539   struct Vnode      *vnode    = filep->f_vnode;
540   struct pipe_dev_s *dev      = (struct pipe_dev_s *)((struct drv_data *)vnode->data)->priv;
541   ssize_t                nwritten = 0;
542   ssize_t                last;
543   int                    nxtwrndx;
544   int                    sval;
545   int                    ret;
546 
547 
548   if (dev == NULL)
549     {
550       return -EINVAL;
551     }
552 
553   /* Handle zero-length writes */
554 
555   if (len == 0)
556     {
557       return 0;
558     }
559 
560   if (len > MAX_READ_WRITE_LEN)
561     {
562       return -EINVAL;
563     }
564 
565   /* REVISIT:  "If all file descriptors referring to the read end of a pipe
566    * have been closed, then a write will cause a SIGPIPE signal to be
567    * generated for the calling process.  If the calling process is ignoring
568    * this signal, then write(2) fails with the error EPIPE."
569    */
570 
571   if (dev->d_nreaders <= 0)
572     {
573       return -EPIPE;
574     }
575 
576   /* At present, this method cannot be called from interrupt handlers.  That
577    * is because it calls sem_wait (via pipecommon_semtake below) and
578    * sem_wait cannot be called from interrupt level.  This actually
579    * happens fairly commonly IF [a-z]err() is called from interrupt handlers
580    * and stdout is being redirected via a pipe.  In that case, the debug
581    * output will try to go out the pipe (interrupt handlers should use the
582    * _err() APIs).
583    *
584    * On the other hand, it would be very valuable to be able to feed the pipe
585    * from an interrupt handler!  TODO:  Consider disabling interrupts instead
586    * of taking semaphores so that pipes can be written from interrupt handlers
587    */
588 
589   /* Make sure that we have exclusive access to the device structure */
590 
591   ret = sem_wait(&dev->d_bfsem);
592   if (ret < 0)
593     {
594       return ret;
595     }
596 
597   /* Loop until all of the bytes have been written */
598 
599   last = 0;
600   for (; ; )
601     {
602       /* Calculate the write index AFTER the next byte is written */
603 
604       nxtwrndx = dev->d_wrndx + 1;
605       if (nxtwrndx >= dev->d_bufsize)
606         {
607           nxtwrndx = 0;
608         }
609 
610       /* Would the next write overflow the circular buffer? */
611 
612       if (nxtwrndx != dev->d_rdndx)
613         {
614           /* No... copy the byte */
615 
616 
617           ret = LOS_ArchCopyFromUser(dev->d_buffer + dev->d_wrndx, buffer, sizeof(char));
618           if (ret != 0)
619             {
620               sem_post(&dev->d_bfsem);
621               return -EFAULT;
622             }
623           buffer++;
624           dev->d_wrndx = nxtwrndx;
625 
626           /* Is the write complete? */
627 
628           nwritten++;
629           if ((size_t)nwritten >= len)
630             {
631               /* Yes.. Notify all of the waiting readers that more data is available */
632               while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval == 0)
633                 {
634                   sem_post(&dev->d_rdsem);
635                 }
636 
637               /* Notify all poll/select waiters that they can read from the FIFO */
638 
639               pipecommon_pollnotify(dev, POLLIN);
640 
641               /* Return the number of bytes written */
642 
643               sem_post(&dev->d_bfsem);
644               return len;
645             }
646         }
647       else
648         {
649           /* There is not enough room for the next byte.  Was anything written in this pass? */
650 
651           if (last < nwritten)
652             {
653               /* Yes.. Notify all of the waiting readers that more data is available */
654 
655               while (sem_getvalue(&dev->d_rdsem, &sval) == 0 && sval == 0)
656                 {
657                   sem_post(&dev->d_rdsem);
658                 }
659 
660               /* Notify all poll/select waiters that they can read from the FIFO */
661 
662               pipecommon_pollnotify(dev, POLLIN);
663             }
664 
665           last = nwritten;
666 
667           /* If O_NONBLOCK was set, then return partial bytes written or EGAIN */
668 
669           if (filep->f_oflags & O_NONBLOCK)
670             {
671               if (nwritten == 0)
672                 {
673                   nwritten = -EAGAIN;
674                 }
675 
676               sem_post(&dev->d_bfsem);
677               return nwritten;
678             }
679 
680           /* There is more to be written.. wait for data to be removed from the pipe */
681 
682           while (sem_getvalue(&dev->d_wrsem, &sval) == 0 && sval != 0)
683             {
684               pipecommon_semtake(&dev->d_wrsem);
685             }
686           sem_post(&dev->d_bfsem);
687 
688           pipecommon_semtake(&dev->d_wrsem);
689           pipecommon_semtake(&dev->d_bfsem);
690         }
691     }
692 }
693 
694 /****************************************************************************
695  * Name: pipecommon_poll
696  ****************************************************************************/
697 
pipecommon_poll(struct file * filep,poll_table * table)698 int pipecommon_poll(struct file *filep, poll_table *table)
699 {
700   struct Vnode      *vnode    = filep->f_vnode;
701   struct pipe_dev_s *dev      = (struct pipe_dev_s *)((struct drv_data *)vnode->data)->priv;
702   pollevent_t            eventset;
703   pipe_ndx_t             nbytes;
704   int                    ret;
705 
706   if (dev == NULL || table == NULL)
707     {
708       return -EINVAL;
709     }
710 
711   /* Are we setting up the poll?  Or tearing it down? */
712 
713   pipecommon_semtake(&dev->d_bfsem);
714 
715   /* Should immediately notify on any of the requested events?
716    * First, determine how many bytes are in the buffer
717    */
718 
719   if (dev->d_wrndx >= dev->d_rdndx)
720     {
721       nbytes = dev->d_wrndx - dev->d_rdndx;
722     }
723   else
724     {
725       nbytes = dev->d_bufsize + dev->d_wrndx - dev->d_rdndx;
726     }
727 
728   /* Notify the POLLOUT event if the pipe is not full, but only if
729    * there is readers.
730    */
731 
732   eventset = 0;
733   if (((filep->f_oflags & O_WRONLY) != 0) && (nbytes < (dev->d_bufsize - 1)))
734     {
735       eventset |= POLLOUT;
736     }
737 
738   /* Notify the POLLIN event if the pipe is not empty */
739 
740   if (((filep->f_oflags & O_WRONLY) == 0) && (nbytes > 0))
741     {
742       eventset |= POLLIN;
743     }
744 
745   /* Notify the POLLHUP event if the pipe is empty and no writers */
746 
747   if (nbytes == 0 && dev->d_nwriters <= 0)
748     {
749       eventset |= POLLHUP;
750     }
751 
752   /* Change POLLOUT to POLLERR, if no readers and policy 0. */
753 
754   if ((eventset | POLLOUT) &&
755       PIPE_IS_POLICY_0(dev->d_flags) &&
756       dev->d_nreaders <= 0)
757     {
758       eventset |= POLLERR;
759     }
760 
761   ret = eventset & table->key;
762   if (ret == 0)
763     {
764       poll_wait(NULL, &dev->wq, table);
765     }
766   sem_post(&dev->d_bfsem);
767 
768   return ret;
769 }
770 
771 /****************************************************************************
772  * Name: pipecommon_ioctl
773  ****************************************************************************/
774 
pipecommon_ioctl(struct file * filep,int cmd,unsigned long arg)775 int pipecommon_ioctl(struct file *filep, int cmd, unsigned long arg)
776 {
777   return -ENOSYS;
778 }
779 
780 /****************************************************************************
781  * Name: pipecommon_unlink
782  ****************************************************************************/
783 
784 #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
pipecommon_unlink(struct Vnode * vnode)785 int pipecommon_unlink(struct Vnode *vnode)
786 {
787   struct pipe_dev_s *dev = NULL;
788 
789   if (vnode == NULL || vnode->data == NULL)
790      {
791       return -EINVAL;
792      }
793 
794   dev = ((struct drv_data *)vnode->data)->priv;
795   if (dev == NULL)
796     {
797       return -EINVAL;
798     }
799 
800   if (dev->d_buffer)
801     {
802       free(dev->d_buffer);
803     }
804 
805   /* And free the device structure. */
806   unregister_driver(dev->name);
807   pipecommon_freedev(dev);
808 
809   return OK;
810 }
811 #endif
812 
813 #endif /* LOSCFG_KERNEL_PIPE */
814