| 
									
										
										
										
											2002-10-25 12:16:15 +00:00
										 |  |  | /* sane - Scanner Access Now Easy.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru> | 
					
						
							| 
									
										
										
										
											2017-05-27 05:27:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-10-25 12:16:15 +00:00
										 |  |  |    This file is part of the SANE package. | 
					
						
							| 
									
										
										
										
											2017-05-27 05:27:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-10-25 12:16:15 +00:00
										 |  |  |    This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  |    modify it under the terms of the GNU General Public License as | 
					
						
							|  |  |  |    published by the Free Software Foundation; either version 2 of the | 
					
						
							|  |  |  |    License, or (at your option) any later version. | 
					
						
							| 
									
										
										
										
											2017-05-27 05:27:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-10-25 12:16:15 +00:00
										 |  |  |    This program is distributed in the hope that it will be useful, but | 
					
						
							|  |  |  |    WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
					
						
							|  |  |  |    General Public License for more details. | 
					
						
							| 
									
										
										
										
											2017-05-27 05:27:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-10-25 12:16:15 +00:00
										 |  |  |    You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  |    along with this program; if not, write to the Free Software | 
					
						
							|  |  |  |    Foundation, Inc., 59 Temple Place - Suite 330, Boston, | 
					
						
							|  |  |  |    MA 02111-1307, USA. | 
					
						
							| 
									
										
										
										
											2017-05-27 05:27:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-10-25 12:16:15 +00:00
										 |  |  |    As a special exception, the authors of SANE give permission for | 
					
						
							|  |  |  |    additional uses of the libraries contained in this release of SANE. | 
					
						
							| 
									
										
										
										
											2017-05-27 05:27:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-10-25 12:16:15 +00:00
										 |  |  |    The exception is that, if you link a SANE library with other files | 
					
						
							|  |  |  |    to produce an executable, this does not by itself cause the | 
					
						
							|  |  |  |    resulting executable to be covered by the GNU General Public | 
					
						
							|  |  |  |    License.  Your use of that executable is in no way restricted on | 
					
						
							|  |  |  |    account of linking the SANE library code into it. | 
					
						
							| 
									
										
										
										
											2017-05-27 05:27:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-10-25 12:16:15 +00:00
										 |  |  |    This exception does not, however, invalidate any other reasons why | 
					
						
							|  |  |  |    the executable file might be covered by the GNU General Public | 
					
						
							|  |  |  |    License. | 
					
						
							| 
									
										
										
										
											2017-05-27 05:27:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-10-25 12:16:15 +00:00
										 |  |  |    If you submit changes to SANE to the maintainers to be included in | 
					
						
							|  |  |  |    a subsequent release, you agree by submitting the changes that | 
					
						
							|  |  |  |    those changes may be distributed with this exception intact. | 
					
						
							| 
									
										
										
										
											2017-05-27 05:27:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-10-25 12:16:15 +00:00
										 |  |  |    If you write modifications of your own for SANE, it is your choice | 
					
						
							|  |  |  |    whether to permit this exception to apply to your modifications. | 
					
						
							| 
									
										
										
										
											2017-05-27 05:27:22 +00:00
										 |  |  |    If you do not wish that, delete this exception notice. | 
					
						
							| 
									
										
										
										
											2002-10-25 12:16:15 +00:00
										 |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** @file
 | 
					
						
							|  |  |  |  * @brief Shared memory channel implementation. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "gt68xx_shm_channel.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <sys/types.h>
 | 
					
						
							|  |  |  | #include <sys/ipc.h>
 | 
					
						
							|  |  |  | #include <sys/shm.h>
 | 
					
						
							|  |  |  | #include <unistd.h>
 | 
					
						
							|  |  |  | #include <fcntl.h>
 | 
					
						
							|  |  |  | #include <errno.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-09-03 14:06:19 +00:00
										 |  |  | #ifndef SHM_R
 | 
					
						
							|  |  |  | #define SHM_R 0
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifndef SHM_W
 | 
					
						
							|  |  |  | #define SHM_W 0
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2002-10-25 12:16:15 +00:00
										 |  |  | /** Shared memory channel.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | struct Shm_Channel | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   SANE_Int buf_size;			/**< Size of each buffer */ | 
					
						
							|  |  |  |   SANE_Int buf_count;			/**< Number of buffers */ | 
					
						
							|  |  |  |   void *shm_area;			/**< Address of shared memory area */ | 
					
						
							|  |  |  |   SANE_Byte **buffers;			/**< Array of pointers to buffers */ | 
					
						
							|  |  |  |   SANE_Int *buffer_bytes;		/**< Array of buffer byte counts */ | 
					
						
							|  |  |  |   int writer_put_pipe[2];		/**< Notification pipe from writer */ | 
					
						
							|  |  |  |   int reader_put_pipe[2];		/**< Notification pipe from reader */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Dummy union to find out the needed alignment */ | 
					
						
							|  |  |  | union Shm_Channel_Align | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   int i; | 
					
						
							|  |  |  |   long l; | 
					
						
							|  |  |  |   void *ptr; | 
					
						
							|  |  |  |   void (*func_ptr) (void); | 
					
						
							|  |  |  |   double d; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Check if shm_channel is valid */ | 
					
						
							|  |  |  | #define SHM_CHANNEL_CHECK(shm_channel, func_name)               \
 | 
					
						
							|  |  |  |   do {                                                          \ | 
					
						
							|  |  |  |     if ((shm_channel) == NULL)                                  \ | 
					
						
							|  |  |  |       {                                                         \ | 
					
						
							|  |  |  |         DBG (3, "%s: BUG: shm_channel==NULL\n", (func_name));   \ | 
					
						
							|  |  |  |         return SANE_STATUS_INVAL;                               \ | 
					
						
							|  |  |  |       }                                                         \ | 
					
						
							|  |  |  |   } while (SANE_FALSE) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Alignment for shared memory contents */ | 
					
						
							|  |  |  | #define SHM_CHANNEL_ALIGNMENT   (sizeof (union Shm_Channel_Align))
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Align the given size up to a multiple of the given alignment */ | 
					
						
							|  |  |  | #define SHM_CHANNEL_ROUND_UP(size, align) \
 | 
					
						
							|  |  |  |   ( ((size) % (align)) ? ((size)/(align) + 1)*(align) : (size) ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Align the size using SHM_CHANNEL_ALIGNMENT */ | 
					
						
							|  |  |  | #define SHM_CHANNEL_ALIGN(size) \
 | 
					
						
							|  |  |  |   SHM_CHANNEL_ROUND_UP((size_t) (size), SHM_CHANNEL_ALIGNMENT) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Close a file descriptor if it is currently open.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function checks if the file descriptor is not -1, and sets it to -1 | 
					
						
							|  |  |  |  * after close (so that it will not be closed twice). | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param fd_var Pointer to a variable holding the file descriptor. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | shm_channel_fd_safe_close (int *fd_var) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (*fd_var != -1) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       close (*fd_var); | 
					
						
							|  |  |  |       *fd_var = -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static SANE_Status | 
					
						
							|  |  |  | shm_channel_fd_set_close_on_exec (int fd) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   long value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   value = fcntl (fd, F_GETFD, 0L); | 
					
						
							|  |  |  |   if (value == -1) | 
					
						
							|  |  |  |     return SANE_STATUS_IO_ERROR; | 
					
						
							|  |  |  |   if (fcntl (fd, F_SETFD, value | FD_CLOEXEC) == -1) | 
					
						
							|  |  |  |     return SANE_STATUS_IO_ERROR; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return SANE_STATUS_GOOD; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  | static SANE_Status | 
					
						
							|  |  |  | shm_channel_fd_set_non_blocking (int fd, SANE_Bool non_blocking) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   long value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   value = fcntl (fd, F_GETFL, 0L); | 
					
						
							|  |  |  |   if (value == -1) | 
					
						
							|  |  |  |     return SANE_STATUS_IO_ERROR; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (non_blocking) | 
					
						
							|  |  |  |     value |= O_NONBLOCK; | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     value &= ~O_NONBLOCK; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (fcntl (fd, F_SETFL, value) == -1) | 
					
						
							|  |  |  |     return SANE_STATUS_IO_ERROR; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return SANE_STATUS_GOOD; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Create a new shared memory channel.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function should be called before the fork to set up the shared memory. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param buf_size  Size of each shared memory buffer in bytes. | 
					
						
							|  |  |  |  * @param buf_count Number of shared memory buffers (up to 255). | 
					
						
							|  |  |  |  * @param shm_channel_return Returned shared memory channel object. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | SANE_Status | 
					
						
							|  |  |  | shm_channel_new (SANE_Int buf_size, | 
					
						
							|  |  |  | 		 SANE_Int buf_count, Shm_Channel ** shm_channel_return) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   Shm_Channel *shm_channel; | 
					
						
							|  |  |  |   void *shm_area; | 
					
						
							|  |  |  |   SANE_Byte *shm_data; | 
					
						
							|  |  |  |   int shm_buffer_bytes_size, shm_buffer_size; | 
					
						
							|  |  |  |   int shm_size; | 
					
						
							|  |  |  |   int shm_id; | 
					
						
							|  |  |  |   int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (buf_size <= 0) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       DBG (3, "shm_channel_new: invalid buf_size=%d\n", buf_size); | 
					
						
							|  |  |  |       return SANE_STATUS_INVAL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   if (buf_count <= 0 || buf_count > 255) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       DBG (3, "shm_channel_new: invalid buf_count=%d\n", buf_count); | 
					
						
							|  |  |  |       return SANE_STATUS_INVAL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   if (!shm_channel_return) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       DBG (3, "shm_channel_new: BUG: shm_channel_return==NULL\n"); | 
					
						
							|  |  |  |       return SANE_STATUS_INVAL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   *shm_channel_return = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   shm_channel = (Shm_Channel *) malloc (sizeof (Shm_Channel)); | 
					
						
							|  |  |  |   if (!shm_channel) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       DBG (3, "shm_channel_new: no memory for Shm_Channel\n"); | 
					
						
							|  |  |  |       return SANE_STATUS_NO_MEM; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   shm_channel->buf_size = buf_size; | 
					
						
							|  |  |  |   shm_channel->buf_count = buf_count; | 
					
						
							|  |  |  |   shm_channel->shm_area = NULL; | 
					
						
							|  |  |  |   shm_channel->buffers = NULL; | 
					
						
							|  |  |  |   shm_channel->buffer_bytes = NULL; | 
					
						
							|  |  |  |   shm_channel->writer_put_pipe[0] = shm_channel->writer_put_pipe[1] = -1; | 
					
						
							|  |  |  |   shm_channel->reader_put_pipe[0] = shm_channel->reader_put_pipe[1] = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   shm_channel->buffers = | 
					
						
							|  |  |  |     (SANE_Byte **) malloc (sizeof (SANE_Byte *) * buf_count); | 
					
						
							|  |  |  |   if (!shm_channel->buffers) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       DBG (3, "shm_channel_new: no memory for buffer pointers\n"); | 
					
						
							|  |  |  |       shm_channel_free (shm_channel); | 
					
						
							|  |  |  |       return SANE_STATUS_NO_MEM; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (pipe (shm_channel->writer_put_pipe) == -1) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       DBG (3, "shm_channel_new: cannot create writer put pipe: %s\n", | 
					
						
							|  |  |  | 	   strerror (errno)); | 
					
						
							|  |  |  |       shm_channel_free (shm_channel); | 
					
						
							|  |  |  |       return SANE_STATUS_NO_MEM; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (pipe (shm_channel->reader_put_pipe) == -1) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       DBG (3, "shm_channel_new: cannot create reader put pipe: %s\n", | 
					
						
							|  |  |  | 	   strerror (errno)); | 
					
						
							|  |  |  |       shm_channel_free (shm_channel); | 
					
						
							|  |  |  |       return SANE_STATUS_NO_MEM; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   shm_channel_fd_set_close_on_exec (shm_channel->reader_put_pipe[0]); | 
					
						
							|  |  |  |   shm_channel_fd_set_close_on_exec (shm_channel->reader_put_pipe[1]); | 
					
						
							|  |  |  |   shm_channel_fd_set_close_on_exec (shm_channel->writer_put_pipe[0]); | 
					
						
							|  |  |  |   shm_channel_fd_set_close_on_exec (shm_channel->writer_put_pipe[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   shm_buffer_bytes_size = SHM_CHANNEL_ALIGN (sizeof (SANE_Int) * buf_count); | 
					
						
							|  |  |  |   shm_buffer_size = SHM_CHANNEL_ALIGN (buf_size); | 
					
						
							|  |  |  |   shm_size = shm_buffer_bytes_size + buf_count * shm_buffer_size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   shm_id = shmget (IPC_PRIVATE, shm_size, IPC_CREAT | SHM_R | SHM_W); | 
					
						
							|  |  |  |   if (shm_id == -1) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       DBG (3, "shm_channel_new: cannot create shared memory segment: %s\n", | 
					
						
							|  |  |  | 	   strerror (errno)); | 
					
						
							|  |  |  |       shm_channel_free (shm_channel); | 
					
						
							|  |  |  |       return SANE_STATUS_NO_MEM; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   shm_area = shmat (shm_id, NULL, 0); | 
					
						
							|  |  |  |   if (shm_area == (void *) -1) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       DBG (3, "shm_channel_new: cannot attach to shared memory segment: %s\n", | 
					
						
							|  |  |  | 	   strerror (errno)); | 
					
						
							|  |  |  |       shmctl (shm_id, IPC_RMID, NULL); | 
					
						
							|  |  |  |       shm_channel_free (shm_channel); | 
					
						
							|  |  |  |       return SANE_STATUS_NO_MEM; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (shmctl (shm_id, IPC_RMID, NULL) == -1) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       DBG (3, "shm_channel_new: cannot remove shared memory segment id: %s\n", | 
					
						
							|  |  |  | 	   strerror (errno)); | 
					
						
							|  |  |  |       shmdt (shm_area); | 
					
						
							|  |  |  |       shmctl (shm_id, IPC_RMID, NULL); | 
					
						
							|  |  |  |       shm_channel_free (shm_channel); | 
					
						
							|  |  |  |       return SANE_STATUS_NO_MEM; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   shm_channel->shm_area = shm_area; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   shm_channel->buffer_bytes = (SANE_Int *) shm_area; | 
					
						
							|  |  |  |   shm_data = ((SANE_Byte *) shm_area) + shm_buffer_bytes_size; | 
					
						
							|  |  |  |   for (i = 0; i < shm_channel->buf_count; ++i) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       shm_channel->buffers[i] = shm_data; | 
					
						
							|  |  |  |       shm_data += shm_buffer_size; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   *shm_channel_return = shm_channel; | 
					
						
							|  |  |  |   return SANE_STATUS_GOOD; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Close the shared memory channel and release associated resources.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param shm_channel Shared memory channel object. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | SANE_Status | 
					
						
							|  |  |  | shm_channel_free (Shm_Channel * shm_channel) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   SHM_CHANNEL_CHECK (shm_channel, "shm_channel_free"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (shm_channel->shm_area) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       shmdt (shm_channel->shm_area); | 
					
						
							|  |  |  |       shm_channel->shm_area = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (shm_channel->buffers) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       free (shm_channel->buffers); | 
					
						
							|  |  |  |       shm_channel->buffers = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[0]); | 
					
						
							|  |  |  |   shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[1]); | 
					
						
							|  |  |  |   shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[0]); | 
					
						
							|  |  |  |   shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return SANE_STATUS_GOOD; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Initialize the shared memory channel in the writer process.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function should be called after the fork in the process which will | 
					
						
							|  |  |  |  * write data to the channel. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param shm_channel Shared memory channel object. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | SANE_Status | 
					
						
							|  |  |  | shm_channel_writer_init (Shm_Channel * shm_channel) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   SHM_CHANNEL_CHECK (shm_channel, "shm_channel_writer_init"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[0]); | 
					
						
							|  |  |  |   shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return SANE_STATUS_GOOD; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Get a free shared memory buffer for writing.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function may block waiting for a free buffer (if the reader process | 
					
						
							|  |  |  |  * does not process the data fast enough). | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * After successfull call to this function the writer process should fill the | 
					
						
							|  |  |  |  * buffer with the data and pass the buffer identifier from @a buffer_id_return | 
					
						
							|  |  |  |  * to shm_channel_writer_put_buffer() to give the buffer to the reader process. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param shm_channel Shared memory channel object. | 
					
						
							|  |  |  |  * @param buffer_id_return Returned buffer identifier. | 
					
						
							|  |  |  |  * @param buffer_addr_return Returned buffer address. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @return | 
					
						
							|  |  |  |  * - SANE_STATUS_GOOD - a free buffer was available (or became available after | 
					
						
							|  |  |  |  *   waiting for it); @a buffer_id_return and @a buffer_addr_return are filled | 
					
						
							|  |  |  |  *   with valid values. | 
					
						
							|  |  |  |  * - SANE_STATUS_EOF - the reader process has closed its half of the channel. | 
					
						
							|  |  |  |  * - SANE_STATUS_IO_ERROR - an I/O error occured. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | SANE_Status | 
					
						
							|  |  |  | shm_channel_writer_get_buffer (Shm_Channel * shm_channel, | 
					
						
							|  |  |  | 			       SANE_Int * buffer_id_return, | 
					
						
							|  |  |  | 			       SANE_Byte ** buffer_addr_return) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   SANE_Byte buf_index; | 
					
						
							|  |  |  |   int bytes_read; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   SHM_CHANNEL_CHECK (shm_channel, "shm_channel_writer_get_buffer"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   do | 
					
						
							|  |  |  |     bytes_read = read (shm_channel->reader_put_pipe[0], &buf_index, 1); | 
					
						
							|  |  |  |   while (bytes_read == -1 && errno == EINTR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (bytes_read == 1) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       SANE_Int index = buf_index; | 
					
						
							|  |  |  |       if (index < shm_channel->buf_count) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 	  *buffer_id_return = index; | 
					
						
							|  |  |  | 	  *buffer_addr_return = shm_channel->buffers[index]; | 
					
						
							|  |  |  | 	  return SANE_STATUS_GOOD; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   *buffer_id_return = -1; | 
					
						
							|  |  |  |   *buffer_addr_return = NULL; | 
					
						
							|  |  |  |   if (bytes_read == 0) | 
					
						
							|  |  |  |     return SANE_STATUS_EOF; | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     return SANE_STATUS_IO_ERROR; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Pass a filled shared memory buffer to the reader process.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param shm_channel Shared memory channel object. | 
					
						
							|  |  |  |  * @param buffer_id Buffer identifier from shm_channel_writer_put_buffer(). | 
					
						
							|  |  |  |  * @param buffer_bytes Number of data bytes in the buffer. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @return | 
					
						
							|  |  |  |  * - SANE_STATUS_GOOD - the buffer was successfully queued. | 
					
						
							|  |  |  |  * - SANE_STATUS_IO_ERROR - the reader process has closed its half of the | 
					
						
							|  |  |  |  *   channel, or another I/O error occured. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | SANE_Status | 
					
						
							|  |  |  | shm_channel_writer_put_buffer (Shm_Channel * shm_channel, | 
					
						
							|  |  |  | 			       SANE_Int buffer_id, SANE_Int buffer_bytes) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   SANE_Byte buf_index; | 
					
						
							|  |  |  |   int bytes_written; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   SHM_CHANNEL_CHECK (shm_channel, "shm_channel_writer_put_buffer"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (buffer_id < 0 || buffer_id >= shm_channel->buf_count) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       DBG (3, "shm_channel_writer_put_buffer: BUG: buffer_id=%d\n", | 
					
						
							|  |  |  | 	   buffer_id); | 
					
						
							|  |  |  |       return SANE_STATUS_INVAL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   shm_channel->buffer_bytes[buffer_id] = buffer_bytes; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   buf_index = (SANE_Byte) buffer_id; | 
					
						
							|  |  |  |   do | 
					
						
							|  |  |  |     bytes_written = write (shm_channel->writer_put_pipe[1], &buf_index, 1); | 
					
						
							|  |  |  |   while ((bytes_written == 0) || (bytes_written == -1 && errno == EINTR)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (bytes_written == 1) | 
					
						
							|  |  |  |     return SANE_STATUS_GOOD; | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     return SANE_STATUS_IO_ERROR; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Close the writing half of the shared memory channel.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param shm_channel Shared memory channel object. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | SANE_Status | 
					
						
							|  |  |  | shm_channel_writer_close (Shm_Channel * shm_channel) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   SHM_CHANNEL_CHECK (shm_channel, "shm_channel_writer_close"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return SANE_STATUS_GOOD; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Initialize the shared memory channel in the reader process.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function should be called after the fork in the process which will | 
					
						
							|  |  |  |  * read data from the channel. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param shm_channel Shared memory channel object. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | SANE_Status | 
					
						
							|  |  |  | shm_channel_reader_init (Shm_Channel * shm_channel) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_init"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /* Don't close reader_put_pipe[0] here.  Otherwise, if the channel writer
 | 
					
						
							|  |  |  |    * process dies early, this process might get SIGPIPE - and I don't want to | 
					
						
							|  |  |  |    * mess with signals in the main process. */ | 
					
						
							|  |  |  |   /* shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[0]); */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return SANE_STATUS_GOOD; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  | /** Set non-blocking or blocking mode for the reading half of the shared memory
 | 
					
						
							|  |  |  |  * channel. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param shm_channel Shared memory channel object. | 
					
						
							|  |  |  |  * @param non_blocking SANE_TRUE to make the channel non-blocking, SANE_FALSE | 
					
						
							|  |  |  |  * to set blocking mode. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @return | 
					
						
							|  |  |  |  * - SANE_STATUS_GOOD - the requested mode was set successfully. | 
					
						
							|  |  |  |  * - SANE_STATUS_IO_ERROR - error setting the requested mode. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | SANE_Status | 
					
						
							|  |  |  | shm_channel_reader_set_io_mode (Shm_Channel * shm_channel, | 
					
						
							|  |  |  | 				SANE_Bool non_blocking) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_set_io_mode"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return shm_channel_fd_set_non_blocking (shm_channel->writer_put_pipe[0], | 
					
						
							|  |  |  | 					  non_blocking); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Get the file descriptor which will signal when some data is available in
 | 
					
						
							|  |  |  |  * the shared memory channel. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The returned file descriptor can be used in select() or poll().  When one of | 
					
						
							|  |  |  |  * these functions signals that the file descriptor is ready for reading, | 
					
						
							|  |  |  |  * shm_channel_reader_get_buffer() should return some data without blocking. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param shm_channel Shared memory channel object. | 
					
						
							|  |  |  |  * @param fd_return The returned file descriptor. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @return | 
					
						
							|  |  |  |  * - SANE_STATUS_GOOD - the file descriptor was returned. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | SANE_Status | 
					
						
							|  |  |  | shm_channel_reader_get_select_fd (Shm_Channel * shm_channel, | 
					
						
							|  |  |  | 				  SANE_Int * fd_return) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_get_select_fd"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   *fd_return = shm_channel->writer_put_pipe[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return SANE_STATUS_GOOD; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Start reading from the shared memory channel.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * A newly initialized shared memory channel is stopped - the writer process | 
					
						
							|  |  |  |  * will block on shm_channel_writer_get_buffer().  This function will pass all | 
					
						
							|  |  |  |  * available buffers to the writer process, starting the transfer through the | 
					
						
							|  |  |  |  * channel. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param shm_channel Shared memory channel object. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | SANE_Status | 
					
						
							|  |  |  | shm_channel_reader_start (Shm_Channel * shm_channel) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   int i, bytes_written; | 
					
						
							|  |  |  |   SANE_Byte buffer_id; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_start"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   for (i = 0; i < shm_channel->buf_count; ++i) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       buffer_id = i; | 
					
						
							|  |  |  |       do | 
					
						
							|  |  |  | 	bytes_written = | 
					
						
							|  |  |  | 	  write (shm_channel->reader_put_pipe[1], &buffer_id, 1); | 
					
						
							|  |  |  |       while ((bytes_written == 0) || (bytes_written == -1 && errno == EINTR)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (bytes_written == -1) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 	  DBG (3, "shm_channel_reader_start: write error at buffer %d: %s\n", | 
					
						
							|  |  |  | 	       i, strerror (errno)); | 
					
						
							|  |  |  | 	  return SANE_STATUS_IO_ERROR; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return SANE_STATUS_GOOD; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Get the next shared memory buffer passed from the writer process.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * If the channel was not set to non-blocking mode, this function will block | 
					
						
							|  |  |  |  * until the data buffer arrives from the writer process.  In non-blocking mode | 
					
						
							|  |  |  |  * this function will place NULL in @a *buffer_addr_return and return | 
					
						
							|  |  |  |  * SANE_STATUS_GOOD if a buffer is not available immediately. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * After successful completion of this function (return value is | 
					
						
							|  |  |  |  * SANE_STATUS_GOOD and @a *buffer_addr_return is not NULL) the reader process | 
					
						
							|  |  |  |  * should process the data in the buffer and then call | 
					
						
							|  |  |  |  * shm_channel_reader_put_buffer() to release the buffer. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param shm_channel Shared memory channel object. | 
					
						
							|  |  |  |  * @param buffer_id_return Returned buffer identifier. | 
					
						
							|  |  |  |  * @param buffer_addr_return Returned buffer address. | 
					
						
							|  |  |  |  * @param buffer_bytes_return Returned number of data bytes in the buffer. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @return | 
					
						
							|  |  |  |  * - SANE_STATUS_GOOD - no error.  If the channel was in non-blocking mode, @a | 
					
						
							|  |  |  |  *   *buffer_id_return may be NULL, indicating that no data was available. | 
					
						
							|  |  |  |  *   Otherwise, @a *buffer_id_return, @a *buffer_addr_return and @a | 
					
						
							|  |  |  |  *   *buffer_bytes return are filled with valid values. | 
					
						
							|  |  |  |  * - SANE_STATUS_EOF - the writer process has closed its half of the channel. | 
					
						
							|  |  |  |  * - SANE_STATUS_IO_ERROR - an I/O error occured. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | SANE_Status | 
					
						
							|  |  |  | shm_channel_reader_get_buffer (Shm_Channel * shm_channel, | 
					
						
							|  |  |  | 			       SANE_Int * buffer_id_return, | 
					
						
							|  |  |  | 			       SANE_Byte ** buffer_addr_return, | 
					
						
							|  |  |  | 			       SANE_Int * buffer_bytes_return) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   SANE_Byte buf_index; | 
					
						
							|  |  |  |   int bytes_read; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_get_buffer"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   do | 
					
						
							|  |  |  |     bytes_read = read (shm_channel->writer_put_pipe[0], &buf_index, 1); | 
					
						
							|  |  |  |   while (bytes_read == -1 && errno == EINTR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (bytes_read == 1) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       SANE_Int index = buf_index; | 
					
						
							|  |  |  |       if (index < shm_channel->buf_count) | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 	  *buffer_id_return = index; | 
					
						
							|  |  |  | 	  *buffer_addr_return = shm_channel->buffers[index]; | 
					
						
							|  |  |  | 	  *buffer_bytes_return = shm_channel->buffer_bytes[index]; | 
					
						
							|  |  |  | 	  return SANE_STATUS_GOOD; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   *buffer_id_return = -1; | 
					
						
							|  |  |  |   *buffer_addr_return = NULL; | 
					
						
							|  |  |  |   *buffer_bytes_return = 0; | 
					
						
							|  |  |  |   if (bytes_read == 0) | 
					
						
							|  |  |  |     return SANE_STATUS_EOF; | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     return SANE_STATUS_IO_ERROR; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** Release a shared memory buffer received by the reader process.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This function must be called after shm_channel_reader_get_buffer() to | 
					
						
							|  |  |  |  * release the buffer and make it available for transferring the next portion | 
					
						
							|  |  |  |  * of data. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * After calling this function the reader process must not access the buffer | 
					
						
							|  |  |  |  * contents; any data which may be needed later should be copied into some | 
					
						
							|  |  |  |  * other place beforehand. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param shm_channel Shared memory channel object. | 
					
						
							|  |  |  |  * @param buffer_id Buffer identifier from shm_channel_reader_get_buffer(). | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @return | 
					
						
							|  |  |  |  * - SANE_STATUS_GOOD - the buffer was successfully released. | 
					
						
							|  |  |  |  * - SANE_STATUS_IO_ERROR - the writer process has closed its half of the | 
					
						
							|  |  |  |  *   channel, or an unexpected I/O error occured. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | SANE_Status | 
					
						
							|  |  |  | shm_channel_reader_put_buffer (Shm_Channel * shm_channel, SANE_Int buffer_id) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   SANE_Byte buf_index; | 
					
						
							|  |  |  |   int bytes_written; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_put_buffer"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (buffer_id < 0 || buffer_id >= shm_channel->buf_count) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       DBG (3, "shm_channel_reader_put_buffer: BUG: buffer_id=%d\n", | 
					
						
							|  |  |  | 	   buffer_id); | 
					
						
							|  |  |  |       return SANE_STATUS_INVAL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   buf_index = (SANE_Byte) buffer_id; | 
					
						
							|  |  |  |   do | 
					
						
							|  |  |  |     bytes_written = write (shm_channel->reader_put_pipe[1], &buf_index, 1); | 
					
						
							|  |  |  |   while ((bytes_written == 0) || (bytes_written == -1 && errno == EINTR)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (bytes_written == 1) | 
					
						
							|  |  |  |     return SANE_STATUS_GOOD; | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     return SANE_STATUS_IO_ERROR; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  | /** Close the reading half of the shared memory channel.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @param shm_channel Shared memory channel object. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | SANE_Status | 
					
						
							|  |  |  | shm_channel_reader_close (Shm_Channel * shm_channel) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_close"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return SANE_STATUS_GOOD; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | /* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ |