1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy.
2141cc406Sopenharmony_ci
3141cc406Sopenharmony_ci   Copyright (C) 2002 Sergey Vlasov <vsu@altlinux.ru>
4141cc406Sopenharmony_ci
5141cc406Sopenharmony_ci   This file is part of the SANE package.
6141cc406Sopenharmony_ci
7141cc406Sopenharmony_ci   This program is free software; you can redistribute it and/or
8141cc406Sopenharmony_ci   modify it under the terms of the GNU General Public License as
9141cc406Sopenharmony_ci   published by the Free Software Foundation; either version 2 of the
10141cc406Sopenharmony_ci   License, or (at your option) any later version.
11141cc406Sopenharmony_ci
12141cc406Sopenharmony_ci   This program is distributed in the hope that it will be useful, but
13141cc406Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
14141cc406Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15141cc406Sopenharmony_ci   General Public License for more details.
16141cc406Sopenharmony_ci
17141cc406Sopenharmony_ci   You should have received a copy of the GNU General Public License
18141cc406Sopenharmony_ci   along with this program.  If not, see <https://www.gnu.org/licenses/>.
19141cc406Sopenharmony_ci
20141cc406Sopenharmony_ci   As a special exception, the authors of SANE give permission for
21141cc406Sopenharmony_ci   additional uses of the libraries contained in this release of SANE.
22141cc406Sopenharmony_ci
23141cc406Sopenharmony_ci   The exception is that, if you link a SANE library with other files
24141cc406Sopenharmony_ci   to produce an executable, this does not by itself cause the
25141cc406Sopenharmony_ci   resulting executable to be covered by the GNU General Public
26141cc406Sopenharmony_ci   License.  Your use of that executable is in no way restricted on
27141cc406Sopenharmony_ci   account of linking the SANE library code into it.
28141cc406Sopenharmony_ci
29141cc406Sopenharmony_ci   This exception does not, however, invalidate any other reasons why
30141cc406Sopenharmony_ci   the executable file might be covered by the GNU General Public
31141cc406Sopenharmony_ci   License.
32141cc406Sopenharmony_ci
33141cc406Sopenharmony_ci   If you submit changes to SANE to the maintainers to be included in
34141cc406Sopenharmony_ci   a subsequent release, you agree by submitting the changes that
35141cc406Sopenharmony_ci   those changes may be distributed with this exception intact.
36141cc406Sopenharmony_ci
37141cc406Sopenharmony_ci   If you write modifications of your own for SANE, it is your choice
38141cc406Sopenharmony_ci   whether to permit this exception to apply to your modifications.
39141cc406Sopenharmony_ci   If you do not wish that, delete this exception notice.
40141cc406Sopenharmony_ci*/
41141cc406Sopenharmony_ci
42141cc406Sopenharmony_ci/** @file
43141cc406Sopenharmony_ci * @brief Shared memory channel implementation.
44141cc406Sopenharmony_ci */
45141cc406Sopenharmony_ci
46141cc406Sopenharmony_ci#include "gt68xx_shm_channel.h"
47141cc406Sopenharmony_ci
48141cc406Sopenharmony_ci#include <sys/types.h>
49141cc406Sopenharmony_ci#include <sys/ipc.h>
50141cc406Sopenharmony_ci#include <sys/shm.h>
51141cc406Sopenharmony_ci#include <unistd.h>
52141cc406Sopenharmony_ci#include <fcntl.h>
53141cc406Sopenharmony_ci#include <errno.h>
54141cc406Sopenharmony_ci
55141cc406Sopenharmony_ci#ifndef SHM_R
56141cc406Sopenharmony_ci#define SHM_R 0
57141cc406Sopenharmony_ci#endif
58141cc406Sopenharmony_ci
59141cc406Sopenharmony_ci#ifndef SHM_W
60141cc406Sopenharmony_ci#define SHM_W 0
61141cc406Sopenharmony_ci#endif
62141cc406Sopenharmony_ci
63141cc406Sopenharmony_ci/** Shared memory channel.
64141cc406Sopenharmony_ci *
65141cc406Sopenharmony_ci */
66141cc406Sopenharmony_cistruct Shm_Channel
67141cc406Sopenharmony_ci{
68141cc406Sopenharmony_ci  SANE_Int buf_size;			/**< Size of each buffer */
69141cc406Sopenharmony_ci  SANE_Int buf_count;			/**< Number of buffers */
70141cc406Sopenharmony_ci  void *shm_area;			/**< Address of shared memory area */
71141cc406Sopenharmony_ci  SANE_Byte **buffers;			/**< Array of pointers to buffers */
72141cc406Sopenharmony_ci  SANE_Int *buffer_bytes;		/**< Array of buffer byte counts */
73141cc406Sopenharmony_ci  int writer_put_pipe[2];		/**< Notification pipe from writer */
74141cc406Sopenharmony_ci  int reader_put_pipe[2];		/**< Notification pipe from reader */
75141cc406Sopenharmony_ci};
76141cc406Sopenharmony_ci
77141cc406Sopenharmony_ci/** Dummy union to find out the needed alignment */
78141cc406Sopenharmony_ciunion Shm_Channel_Align
79141cc406Sopenharmony_ci{
80141cc406Sopenharmony_ci  int i;
81141cc406Sopenharmony_ci  long l;
82141cc406Sopenharmony_ci  void *ptr;
83141cc406Sopenharmony_ci  void (*func_ptr) (void);
84141cc406Sopenharmony_ci  double d;
85141cc406Sopenharmony_ci};
86141cc406Sopenharmony_ci
87141cc406Sopenharmony_ci/** Check if shm_channel is valid */
88141cc406Sopenharmony_ci#define SHM_CHANNEL_CHECK(shm_channel, func_name)               \
89141cc406Sopenharmony_ci  do {                                                          \
90141cc406Sopenharmony_ci    if ((shm_channel) == NULL)                                  \
91141cc406Sopenharmony_ci      {                                                         \
92141cc406Sopenharmony_ci        DBG (3, "%s: BUG: shm_channel==NULL\n", (func_name));   \
93141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;                               \
94141cc406Sopenharmony_ci      }                                                         \
95141cc406Sopenharmony_ci  } while (SANE_FALSE)
96141cc406Sopenharmony_ci
97141cc406Sopenharmony_ci/** Alignment for shared memory contents */
98141cc406Sopenharmony_ci#define SHM_CHANNEL_ALIGNMENT   (sizeof (union Shm_Channel_Align))
99141cc406Sopenharmony_ci
100141cc406Sopenharmony_ci/** Align the given size up to a multiple of the given alignment */
101141cc406Sopenharmony_ci#define SHM_CHANNEL_ROUND_UP(size, align) \
102141cc406Sopenharmony_ci  ( ((size) % (align)) ? ((size)/(align) + 1)*(align) : (size) )
103141cc406Sopenharmony_ci
104141cc406Sopenharmony_ci/** Align the size using SHM_CHANNEL_ALIGNMENT */
105141cc406Sopenharmony_ci#define SHM_CHANNEL_ALIGN(size) \
106141cc406Sopenharmony_ci  SHM_CHANNEL_ROUND_UP((size_t) (size), SHM_CHANNEL_ALIGNMENT)
107141cc406Sopenharmony_ci
108141cc406Sopenharmony_ci/** Close a file descriptor if it is currently open.
109141cc406Sopenharmony_ci *
110141cc406Sopenharmony_ci * This function checks if the file descriptor is not -1, and sets it to -1
111141cc406Sopenharmony_ci * after close (so that it will not be closed twice).
112141cc406Sopenharmony_ci *
113141cc406Sopenharmony_ci * @param fd_var Pointer to a variable holding the file descriptor.
114141cc406Sopenharmony_ci */
115141cc406Sopenharmony_cistatic void
116141cc406Sopenharmony_cishm_channel_fd_safe_close (int *fd_var)
117141cc406Sopenharmony_ci{
118141cc406Sopenharmony_ci  if (*fd_var != -1)
119141cc406Sopenharmony_ci    {
120141cc406Sopenharmony_ci      close (*fd_var);
121141cc406Sopenharmony_ci      *fd_var = -1;
122141cc406Sopenharmony_ci    }
123141cc406Sopenharmony_ci}
124141cc406Sopenharmony_ci
125141cc406Sopenharmony_cistatic SANE_Status
126141cc406Sopenharmony_cishm_channel_fd_set_close_on_exec (int fd)
127141cc406Sopenharmony_ci{
128141cc406Sopenharmony_ci  long value;
129141cc406Sopenharmony_ci
130141cc406Sopenharmony_ci  value = fcntl (fd, F_GETFD, 0L);
131141cc406Sopenharmony_ci  if (value == -1)
132141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
133141cc406Sopenharmony_ci  if (fcntl (fd, F_SETFD, value | FD_CLOEXEC) == -1)
134141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
135141cc406Sopenharmony_ci
136141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
137141cc406Sopenharmony_ci}
138141cc406Sopenharmony_ci
139141cc406Sopenharmony_ci#if 0
140141cc406Sopenharmony_cistatic SANE_Status
141141cc406Sopenharmony_cishm_channel_fd_set_non_blocking (int fd, SANE_Bool non_blocking)
142141cc406Sopenharmony_ci{
143141cc406Sopenharmony_ci  long value;
144141cc406Sopenharmony_ci
145141cc406Sopenharmony_ci  value = fcntl (fd, F_GETFL, 0L);
146141cc406Sopenharmony_ci  if (value == -1)
147141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
148141cc406Sopenharmony_ci
149141cc406Sopenharmony_ci  if (non_blocking)
150141cc406Sopenharmony_ci    value |= O_NONBLOCK;
151141cc406Sopenharmony_ci  else
152141cc406Sopenharmony_ci    value &= ~O_NONBLOCK;
153141cc406Sopenharmony_ci
154141cc406Sopenharmony_ci  if (fcntl (fd, F_SETFL, value) == -1)
155141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
156141cc406Sopenharmony_ci
157141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
158141cc406Sopenharmony_ci}
159141cc406Sopenharmony_ci#endif
160141cc406Sopenharmony_ci
161141cc406Sopenharmony_ci/** Create a new shared memory channel.
162141cc406Sopenharmony_ci *
163141cc406Sopenharmony_ci * This function should be called before the fork to set up the shared memory.
164141cc406Sopenharmony_ci *
165141cc406Sopenharmony_ci * @param buf_size  Size of each shared memory buffer in bytes.
166141cc406Sopenharmony_ci * @param buf_count Number of shared memory buffers (up to 255).
167141cc406Sopenharmony_ci * @param shm_channel_return Returned shared memory channel object.
168141cc406Sopenharmony_ci */
169141cc406Sopenharmony_ciSANE_Status
170141cc406Sopenharmony_cishm_channel_new (SANE_Int buf_size,
171141cc406Sopenharmony_ci		 SANE_Int buf_count, Shm_Channel ** shm_channel_return)
172141cc406Sopenharmony_ci{
173141cc406Sopenharmony_ci  Shm_Channel *shm_channel;
174141cc406Sopenharmony_ci  void *shm_area;
175141cc406Sopenharmony_ci  SANE_Byte *shm_data;
176141cc406Sopenharmony_ci  int shm_buffer_bytes_size, shm_buffer_size;
177141cc406Sopenharmony_ci  int shm_size;
178141cc406Sopenharmony_ci  int shm_id;
179141cc406Sopenharmony_ci  int i;
180141cc406Sopenharmony_ci
181141cc406Sopenharmony_ci  if (buf_size <= 0)
182141cc406Sopenharmony_ci    {
183141cc406Sopenharmony_ci      DBG (3, "shm_channel_new: invalid buf_size=%d\n", buf_size);
184141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
185141cc406Sopenharmony_ci    }
186141cc406Sopenharmony_ci  if (buf_count <= 0 || buf_count > 255)
187141cc406Sopenharmony_ci    {
188141cc406Sopenharmony_ci      DBG (3, "shm_channel_new: invalid buf_count=%d\n", buf_count);
189141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
190141cc406Sopenharmony_ci    }
191141cc406Sopenharmony_ci  if (!shm_channel_return)
192141cc406Sopenharmony_ci    {
193141cc406Sopenharmony_ci      DBG (3, "shm_channel_new: BUG: shm_channel_return==NULL\n");
194141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
195141cc406Sopenharmony_ci    }
196141cc406Sopenharmony_ci
197141cc406Sopenharmony_ci  *shm_channel_return = NULL;
198141cc406Sopenharmony_ci
199141cc406Sopenharmony_ci  shm_channel = (Shm_Channel *) malloc (sizeof (Shm_Channel));
200141cc406Sopenharmony_ci  if (!shm_channel)
201141cc406Sopenharmony_ci    {
202141cc406Sopenharmony_ci      DBG (3, "shm_channel_new: no memory for Shm_Channel\n");
203141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
204141cc406Sopenharmony_ci    }
205141cc406Sopenharmony_ci
206141cc406Sopenharmony_ci  shm_channel->buf_size = buf_size;
207141cc406Sopenharmony_ci  shm_channel->buf_count = buf_count;
208141cc406Sopenharmony_ci  shm_channel->shm_area = NULL;
209141cc406Sopenharmony_ci  shm_channel->buffers = NULL;
210141cc406Sopenharmony_ci  shm_channel->buffer_bytes = NULL;
211141cc406Sopenharmony_ci  shm_channel->writer_put_pipe[0] = shm_channel->writer_put_pipe[1] = -1;
212141cc406Sopenharmony_ci  shm_channel->reader_put_pipe[0] = shm_channel->reader_put_pipe[1] = -1;
213141cc406Sopenharmony_ci
214141cc406Sopenharmony_ci  shm_channel->buffers =
215141cc406Sopenharmony_ci    (SANE_Byte **) malloc (sizeof (SANE_Byte *) * buf_count);
216141cc406Sopenharmony_ci  if (!shm_channel->buffers)
217141cc406Sopenharmony_ci    {
218141cc406Sopenharmony_ci      DBG (3, "shm_channel_new: no memory for buffer pointers\n");
219141cc406Sopenharmony_ci      shm_channel_free (shm_channel);
220141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
221141cc406Sopenharmony_ci    }
222141cc406Sopenharmony_ci
223141cc406Sopenharmony_ci  if (pipe (shm_channel->writer_put_pipe) == -1)
224141cc406Sopenharmony_ci    {
225141cc406Sopenharmony_ci      DBG (3, "shm_channel_new: cannot create writer put pipe: %s\n",
226141cc406Sopenharmony_ci	   strerror (errno));
227141cc406Sopenharmony_ci      shm_channel_free (shm_channel);
228141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
229141cc406Sopenharmony_ci    }
230141cc406Sopenharmony_ci
231141cc406Sopenharmony_ci  if (pipe (shm_channel->reader_put_pipe) == -1)
232141cc406Sopenharmony_ci    {
233141cc406Sopenharmony_ci      DBG (3, "shm_channel_new: cannot create reader put pipe: %s\n",
234141cc406Sopenharmony_ci	   strerror (errno));
235141cc406Sopenharmony_ci      shm_channel_free (shm_channel);
236141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
237141cc406Sopenharmony_ci    }
238141cc406Sopenharmony_ci
239141cc406Sopenharmony_ci  shm_channel_fd_set_close_on_exec (shm_channel->reader_put_pipe[0]);
240141cc406Sopenharmony_ci  shm_channel_fd_set_close_on_exec (shm_channel->reader_put_pipe[1]);
241141cc406Sopenharmony_ci  shm_channel_fd_set_close_on_exec (shm_channel->writer_put_pipe[0]);
242141cc406Sopenharmony_ci  shm_channel_fd_set_close_on_exec (shm_channel->writer_put_pipe[1]);
243141cc406Sopenharmony_ci
244141cc406Sopenharmony_ci  shm_buffer_bytes_size = SHM_CHANNEL_ALIGN (sizeof (SANE_Int) * buf_count);
245141cc406Sopenharmony_ci  shm_buffer_size = SHM_CHANNEL_ALIGN (buf_size);
246141cc406Sopenharmony_ci  shm_size = shm_buffer_bytes_size + buf_count * shm_buffer_size;
247141cc406Sopenharmony_ci
248141cc406Sopenharmony_ci  shm_id = shmget (IPC_PRIVATE, shm_size, IPC_CREAT | SHM_R | SHM_W);
249141cc406Sopenharmony_ci  if (shm_id == -1)
250141cc406Sopenharmony_ci    {
251141cc406Sopenharmony_ci      DBG (3, "shm_channel_new: cannot create shared memory segment: %s\n",
252141cc406Sopenharmony_ci	   strerror (errno));
253141cc406Sopenharmony_ci      shm_channel_free (shm_channel);
254141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
255141cc406Sopenharmony_ci    }
256141cc406Sopenharmony_ci
257141cc406Sopenharmony_ci  shm_area = shmat (shm_id, NULL, 0);
258141cc406Sopenharmony_ci  if (shm_area == (void *) -1)
259141cc406Sopenharmony_ci    {
260141cc406Sopenharmony_ci      DBG (3, "shm_channel_new: cannot attach to shared memory segment: %s\n",
261141cc406Sopenharmony_ci	   strerror (errno));
262141cc406Sopenharmony_ci      shmctl (shm_id, IPC_RMID, NULL);
263141cc406Sopenharmony_ci      shm_channel_free (shm_channel);
264141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
265141cc406Sopenharmony_ci    }
266141cc406Sopenharmony_ci
267141cc406Sopenharmony_ci  if (shmctl (shm_id, IPC_RMID, NULL) == -1)
268141cc406Sopenharmony_ci    {
269141cc406Sopenharmony_ci      DBG (3, "shm_channel_new: cannot remove shared memory segment id: %s\n",
270141cc406Sopenharmony_ci	   strerror (errno));
271141cc406Sopenharmony_ci      shmdt (shm_area);
272141cc406Sopenharmony_ci      shmctl (shm_id, IPC_RMID, NULL);
273141cc406Sopenharmony_ci      shm_channel_free (shm_channel);
274141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
275141cc406Sopenharmony_ci    }
276141cc406Sopenharmony_ci
277141cc406Sopenharmony_ci  shm_channel->shm_area = shm_area;
278141cc406Sopenharmony_ci
279141cc406Sopenharmony_ci  shm_channel->buffer_bytes = (SANE_Int *) shm_area;
280141cc406Sopenharmony_ci  shm_data = ((SANE_Byte *) shm_area) + shm_buffer_bytes_size;
281141cc406Sopenharmony_ci  for (i = 0; i < shm_channel->buf_count; ++i)
282141cc406Sopenharmony_ci    {
283141cc406Sopenharmony_ci      shm_channel->buffers[i] = shm_data;
284141cc406Sopenharmony_ci      shm_data += shm_buffer_size;
285141cc406Sopenharmony_ci    }
286141cc406Sopenharmony_ci
287141cc406Sopenharmony_ci  *shm_channel_return = shm_channel;
288141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
289141cc406Sopenharmony_ci}
290141cc406Sopenharmony_ci
291141cc406Sopenharmony_ci/** Close the shared memory channel and release associated resources.
292141cc406Sopenharmony_ci *
293141cc406Sopenharmony_ci * @param shm_channel Shared memory channel object.
294141cc406Sopenharmony_ci */
295141cc406Sopenharmony_ciSANE_Status
296141cc406Sopenharmony_cishm_channel_free (Shm_Channel * shm_channel)
297141cc406Sopenharmony_ci{
298141cc406Sopenharmony_ci  SHM_CHANNEL_CHECK (shm_channel, "shm_channel_free");
299141cc406Sopenharmony_ci
300141cc406Sopenharmony_ci  if (shm_channel->shm_area)
301141cc406Sopenharmony_ci    {
302141cc406Sopenharmony_ci      shmdt (shm_channel->shm_area);
303141cc406Sopenharmony_ci      shm_channel->shm_area = NULL;
304141cc406Sopenharmony_ci    }
305141cc406Sopenharmony_ci
306141cc406Sopenharmony_ci  if (shm_channel->buffers)
307141cc406Sopenharmony_ci    {
308141cc406Sopenharmony_ci      free (shm_channel->buffers);
309141cc406Sopenharmony_ci      shm_channel->buffers = NULL;
310141cc406Sopenharmony_ci    }
311141cc406Sopenharmony_ci
312141cc406Sopenharmony_ci  shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[0]);
313141cc406Sopenharmony_ci  shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[1]);
314141cc406Sopenharmony_ci  shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[0]);
315141cc406Sopenharmony_ci  shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[1]);
316141cc406Sopenharmony_ci
317141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
318141cc406Sopenharmony_ci}
319141cc406Sopenharmony_ci
320141cc406Sopenharmony_ci/** Initialize the shared memory channel in the writer process.
321141cc406Sopenharmony_ci *
322141cc406Sopenharmony_ci * This function should be called after the fork in the process which will
323141cc406Sopenharmony_ci * write data to the channel.
324141cc406Sopenharmony_ci *
325141cc406Sopenharmony_ci * @param shm_channel Shared memory channel object.
326141cc406Sopenharmony_ci */
327141cc406Sopenharmony_ciSANE_Status
328141cc406Sopenharmony_cishm_channel_writer_init (Shm_Channel * shm_channel)
329141cc406Sopenharmony_ci{
330141cc406Sopenharmony_ci  SHM_CHANNEL_CHECK (shm_channel, "shm_channel_writer_init");
331141cc406Sopenharmony_ci
332141cc406Sopenharmony_ci  shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[0]);
333141cc406Sopenharmony_ci  shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[1]);
334141cc406Sopenharmony_ci
335141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
336141cc406Sopenharmony_ci}
337141cc406Sopenharmony_ci
338141cc406Sopenharmony_ci/** Get a free shared memory buffer for writing.
339141cc406Sopenharmony_ci *
340141cc406Sopenharmony_ci * This function may block waiting for a free buffer (if the reader process
341141cc406Sopenharmony_ci * does not process the data fast enough).
342141cc406Sopenharmony_ci *
343141cc406Sopenharmony_ci * After successful call to this function the writer process should fill the
344141cc406Sopenharmony_ci * buffer with the data and pass the buffer identifier from @a buffer_id_return
345141cc406Sopenharmony_ci * to shm_channel_writer_put_buffer() to give the buffer to the reader process.
346141cc406Sopenharmony_ci *
347141cc406Sopenharmony_ci * @param shm_channel Shared memory channel object.
348141cc406Sopenharmony_ci * @param buffer_id_return Returned buffer identifier.
349141cc406Sopenharmony_ci * @param buffer_addr_return Returned buffer address.
350141cc406Sopenharmony_ci *
351141cc406Sopenharmony_ci * @return
352141cc406Sopenharmony_ci * - SANE_STATUS_GOOD - a free buffer was available (or became available after
353141cc406Sopenharmony_ci *   waiting for it); @a buffer_id_return and @a buffer_addr_return are filled
354141cc406Sopenharmony_ci *   with valid values.
355141cc406Sopenharmony_ci * - SANE_STATUS_EOF - the reader process has closed its half of the channel.
356141cc406Sopenharmony_ci * - SANE_STATUS_IO_ERROR - an I/O error occurred.
357141cc406Sopenharmony_ci */
358141cc406Sopenharmony_ciSANE_Status
359141cc406Sopenharmony_cishm_channel_writer_get_buffer (Shm_Channel * shm_channel,
360141cc406Sopenharmony_ci			       SANE_Int * buffer_id_return,
361141cc406Sopenharmony_ci			       SANE_Byte ** buffer_addr_return)
362141cc406Sopenharmony_ci{
363141cc406Sopenharmony_ci  SANE_Byte buf_index;
364141cc406Sopenharmony_ci  int bytes_read;
365141cc406Sopenharmony_ci
366141cc406Sopenharmony_ci  SHM_CHANNEL_CHECK (shm_channel, "shm_channel_writer_get_buffer");
367141cc406Sopenharmony_ci
368141cc406Sopenharmony_ci  do
369141cc406Sopenharmony_ci    bytes_read = read (shm_channel->reader_put_pipe[0], &buf_index, 1);
370141cc406Sopenharmony_ci  while (bytes_read == -1 && errno == EINTR);
371141cc406Sopenharmony_ci
372141cc406Sopenharmony_ci  if (bytes_read == 1)
373141cc406Sopenharmony_ci    {
374141cc406Sopenharmony_ci      SANE_Int index = buf_index;
375141cc406Sopenharmony_ci      if (index < shm_channel->buf_count)
376141cc406Sopenharmony_ci	{
377141cc406Sopenharmony_ci	  *buffer_id_return = index;
378141cc406Sopenharmony_ci	  *buffer_addr_return = shm_channel->buffers[index];
379141cc406Sopenharmony_ci	  return SANE_STATUS_GOOD;
380141cc406Sopenharmony_ci	}
381141cc406Sopenharmony_ci    }
382141cc406Sopenharmony_ci
383141cc406Sopenharmony_ci  *buffer_id_return = -1;
384141cc406Sopenharmony_ci  *buffer_addr_return = NULL;
385141cc406Sopenharmony_ci  if (bytes_read == 0)
386141cc406Sopenharmony_ci    return SANE_STATUS_EOF;
387141cc406Sopenharmony_ci  else
388141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
389141cc406Sopenharmony_ci}
390141cc406Sopenharmony_ci
391141cc406Sopenharmony_ci/** Pass a filled shared memory buffer to the reader process.
392141cc406Sopenharmony_ci *
393141cc406Sopenharmony_ci * @param shm_channel Shared memory channel object.
394141cc406Sopenharmony_ci * @param buffer_id Buffer identifier from shm_channel_writer_put_buffer().
395141cc406Sopenharmony_ci * @param buffer_bytes Number of data bytes in the buffer.
396141cc406Sopenharmony_ci *
397141cc406Sopenharmony_ci * @return
398141cc406Sopenharmony_ci * - SANE_STATUS_GOOD - the buffer was successfully queued.
399141cc406Sopenharmony_ci * - SANE_STATUS_IO_ERROR - the reader process has closed its half of the
400141cc406Sopenharmony_ci *   channel, or another I/O error occurred.
401141cc406Sopenharmony_ci */
402141cc406Sopenharmony_ciSANE_Status
403141cc406Sopenharmony_cishm_channel_writer_put_buffer (Shm_Channel * shm_channel,
404141cc406Sopenharmony_ci			       SANE_Int buffer_id, SANE_Int buffer_bytes)
405141cc406Sopenharmony_ci{
406141cc406Sopenharmony_ci  SANE_Byte buf_index;
407141cc406Sopenharmony_ci  int bytes_written;
408141cc406Sopenharmony_ci
409141cc406Sopenharmony_ci  SHM_CHANNEL_CHECK (shm_channel, "shm_channel_writer_put_buffer");
410141cc406Sopenharmony_ci
411141cc406Sopenharmony_ci  if (buffer_id < 0 || buffer_id >= shm_channel->buf_count)
412141cc406Sopenharmony_ci    {
413141cc406Sopenharmony_ci      DBG (3, "shm_channel_writer_put_buffer: BUG: buffer_id=%d\n",
414141cc406Sopenharmony_ci	   buffer_id);
415141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
416141cc406Sopenharmony_ci    }
417141cc406Sopenharmony_ci
418141cc406Sopenharmony_ci  shm_channel->buffer_bytes[buffer_id] = buffer_bytes;
419141cc406Sopenharmony_ci
420141cc406Sopenharmony_ci  buf_index = (SANE_Byte) buffer_id;
421141cc406Sopenharmony_ci  do
422141cc406Sopenharmony_ci    bytes_written = write (shm_channel->writer_put_pipe[1], &buf_index, 1);
423141cc406Sopenharmony_ci  while ((bytes_written == 0) || (bytes_written == -1 && errno == EINTR));
424141cc406Sopenharmony_ci
425141cc406Sopenharmony_ci  if (bytes_written == 1)
426141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
427141cc406Sopenharmony_ci  else
428141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
429141cc406Sopenharmony_ci}
430141cc406Sopenharmony_ci
431141cc406Sopenharmony_ci/** Close the writing half of the shared memory channel.
432141cc406Sopenharmony_ci *
433141cc406Sopenharmony_ci * @param shm_channel Shared memory channel object.
434141cc406Sopenharmony_ci */
435141cc406Sopenharmony_ciSANE_Status
436141cc406Sopenharmony_cishm_channel_writer_close (Shm_Channel * shm_channel)
437141cc406Sopenharmony_ci{
438141cc406Sopenharmony_ci  SHM_CHANNEL_CHECK (shm_channel, "shm_channel_writer_close");
439141cc406Sopenharmony_ci
440141cc406Sopenharmony_ci  shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[1]);
441141cc406Sopenharmony_ci
442141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
443141cc406Sopenharmony_ci}
444141cc406Sopenharmony_ci
445141cc406Sopenharmony_ci
446141cc406Sopenharmony_ci/** Initialize the shared memory channel in the reader process.
447141cc406Sopenharmony_ci *
448141cc406Sopenharmony_ci * This function should be called after the fork in the process which will
449141cc406Sopenharmony_ci * read data from the channel.
450141cc406Sopenharmony_ci *
451141cc406Sopenharmony_ci * @param shm_channel Shared memory channel object.
452141cc406Sopenharmony_ci */
453141cc406Sopenharmony_ciSANE_Status
454141cc406Sopenharmony_cishm_channel_reader_init (Shm_Channel * shm_channel)
455141cc406Sopenharmony_ci{
456141cc406Sopenharmony_ci  SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_init");
457141cc406Sopenharmony_ci
458141cc406Sopenharmony_ci  shm_channel_fd_safe_close (&shm_channel->writer_put_pipe[1]);
459141cc406Sopenharmony_ci
460141cc406Sopenharmony_ci  /* Don't close reader_put_pipe[0] here.  Otherwise, if the channel writer
461141cc406Sopenharmony_ci   * process dies early, this process might get SIGPIPE - and I don't want to
462141cc406Sopenharmony_ci   * mess with signals in the main process. */
463141cc406Sopenharmony_ci  /* shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[0]); */
464141cc406Sopenharmony_ci
465141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
466141cc406Sopenharmony_ci}
467141cc406Sopenharmony_ci
468141cc406Sopenharmony_ci#if 0
469141cc406Sopenharmony_ci/** Set non-blocking or blocking mode for the reading half of the shared memory
470141cc406Sopenharmony_ci * channel.
471141cc406Sopenharmony_ci *
472141cc406Sopenharmony_ci * @param shm_channel Shared memory channel object.
473141cc406Sopenharmony_ci * @param non_blocking SANE_TRUE to make the channel non-blocking, SANE_FALSE
474141cc406Sopenharmony_ci * to set blocking mode.
475141cc406Sopenharmony_ci *
476141cc406Sopenharmony_ci * @return
477141cc406Sopenharmony_ci * - SANE_STATUS_GOOD - the requested mode was set successfully.
478141cc406Sopenharmony_ci * - SANE_STATUS_IO_ERROR - error setting the requested mode.
479141cc406Sopenharmony_ci */
480141cc406Sopenharmony_ciSANE_Status
481141cc406Sopenharmony_cishm_channel_reader_set_io_mode (Shm_Channel * shm_channel,
482141cc406Sopenharmony_ci				SANE_Bool non_blocking)
483141cc406Sopenharmony_ci{
484141cc406Sopenharmony_ci  SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_set_io_mode");
485141cc406Sopenharmony_ci
486141cc406Sopenharmony_ci  return shm_channel_fd_set_non_blocking (shm_channel->writer_put_pipe[0],
487141cc406Sopenharmony_ci					  non_blocking);
488141cc406Sopenharmony_ci}
489141cc406Sopenharmony_ci
490141cc406Sopenharmony_ci/** Get the file descriptor which will signal when some data is available in
491141cc406Sopenharmony_ci * the shared memory channel.
492141cc406Sopenharmony_ci *
493141cc406Sopenharmony_ci * The returned file descriptor can be used in select() or poll().  When one of
494141cc406Sopenharmony_ci * these functions signals that the file descriptor is ready for reading,
495141cc406Sopenharmony_ci * shm_channel_reader_get_buffer() should return some data without blocking.
496141cc406Sopenharmony_ci *
497141cc406Sopenharmony_ci * @param shm_channel Shared memory channel object.
498141cc406Sopenharmony_ci * @param fd_return The returned file descriptor.
499141cc406Sopenharmony_ci *
500141cc406Sopenharmony_ci * @return
501141cc406Sopenharmony_ci * - SANE_STATUS_GOOD - the file descriptor was returned.
502141cc406Sopenharmony_ci */
503141cc406Sopenharmony_ciSANE_Status
504141cc406Sopenharmony_cishm_channel_reader_get_select_fd (Shm_Channel * shm_channel,
505141cc406Sopenharmony_ci				  SANE_Int * fd_return)
506141cc406Sopenharmony_ci{
507141cc406Sopenharmony_ci  SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_get_select_fd");
508141cc406Sopenharmony_ci
509141cc406Sopenharmony_ci  *fd_return = shm_channel->writer_put_pipe[0];
510141cc406Sopenharmony_ci
511141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
512141cc406Sopenharmony_ci}
513141cc406Sopenharmony_ci#endif
514141cc406Sopenharmony_ci
515141cc406Sopenharmony_ci/** Start reading from the shared memory channel.
516141cc406Sopenharmony_ci *
517141cc406Sopenharmony_ci * A newly initialized shared memory channel is stopped - the writer process
518141cc406Sopenharmony_ci * will block on shm_channel_writer_get_buffer().  This function will pass all
519141cc406Sopenharmony_ci * available buffers to the writer process, starting the transfer through the
520141cc406Sopenharmony_ci * channel.
521141cc406Sopenharmony_ci *
522141cc406Sopenharmony_ci * @param shm_channel Shared memory channel object.
523141cc406Sopenharmony_ci */
524141cc406Sopenharmony_ciSANE_Status
525141cc406Sopenharmony_cishm_channel_reader_start (Shm_Channel * shm_channel)
526141cc406Sopenharmony_ci{
527141cc406Sopenharmony_ci  int i, bytes_written;
528141cc406Sopenharmony_ci  SANE_Byte buffer_id;
529141cc406Sopenharmony_ci
530141cc406Sopenharmony_ci  SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_start");
531141cc406Sopenharmony_ci
532141cc406Sopenharmony_ci  for (i = 0; i < shm_channel->buf_count; ++i)
533141cc406Sopenharmony_ci    {
534141cc406Sopenharmony_ci      buffer_id = i;
535141cc406Sopenharmony_ci      do
536141cc406Sopenharmony_ci	bytes_written =
537141cc406Sopenharmony_ci	  write (shm_channel->reader_put_pipe[1], &buffer_id, 1);
538141cc406Sopenharmony_ci      while ((bytes_written == 0) || (bytes_written == -1 && errno == EINTR));
539141cc406Sopenharmony_ci
540141cc406Sopenharmony_ci      if (bytes_written == -1)
541141cc406Sopenharmony_ci	{
542141cc406Sopenharmony_ci	  DBG (3, "shm_channel_reader_start: write error at buffer %d: %s\n",
543141cc406Sopenharmony_ci	       i, strerror (errno));
544141cc406Sopenharmony_ci	  return SANE_STATUS_IO_ERROR;
545141cc406Sopenharmony_ci	}
546141cc406Sopenharmony_ci    }
547141cc406Sopenharmony_ci
548141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
549141cc406Sopenharmony_ci}
550141cc406Sopenharmony_ci
551141cc406Sopenharmony_ci/** Get the next shared memory buffer passed from the writer process.
552141cc406Sopenharmony_ci *
553141cc406Sopenharmony_ci * If the channel was not set to non-blocking mode, this function will block
554141cc406Sopenharmony_ci * until the data buffer arrives from the writer process.  In non-blocking mode
555141cc406Sopenharmony_ci * this function will place NULL in @a *buffer_addr_return and return
556141cc406Sopenharmony_ci * SANE_STATUS_GOOD if a buffer is not available immediately.
557141cc406Sopenharmony_ci *
558141cc406Sopenharmony_ci * After successful completion of this function (return value is
559141cc406Sopenharmony_ci * SANE_STATUS_GOOD and @a *buffer_addr_return is not NULL) the reader process
560141cc406Sopenharmony_ci * should process the data in the buffer and then call
561141cc406Sopenharmony_ci * shm_channel_reader_put_buffer() to release the buffer.
562141cc406Sopenharmony_ci *
563141cc406Sopenharmony_ci * @param shm_channel Shared memory channel object.
564141cc406Sopenharmony_ci * @param buffer_id_return Returned buffer identifier.
565141cc406Sopenharmony_ci * @param buffer_addr_return Returned buffer address.
566141cc406Sopenharmony_ci * @param buffer_bytes_return Returned number of data bytes in the buffer.
567141cc406Sopenharmony_ci *
568141cc406Sopenharmony_ci * @return
569141cc406Sopenharmony_ci * - SANE_STATUS_GOOD - no error.  If the channel was in non-blocking mode, @a
570141cc406Sopenharmony_ci *   *buffer_id_return may be NULL, indicating that no data was available.
571141cc406Sopenharmony_ci *   Otherwise, @a *buffer_id_return, @a *buffer_addr_return and @a
572141cc406Sopenharmony_ci *   *buffer_bytes return are filled with valid values.
573141cc406Sopenharmony_ci * - SANE_STATUS_EOF - the writer process has closed its half of the channel.
574141cc406Sopenharmony_ci * - SANE_STATUS_IO_ERROR - an I/O error occurred.
575141cc406Sopenharmony_ci */
576141cc406Sopenharmony_ciSANE_Status
577141cc406Sopenharmony_cishm_channel_reader_get_buffer (Shm_Channel * shm_channel,
578141cc406Sopenharmony_ci			       SANE_Int * buffer_id_return,
579141cc406Sopenharmony_ci			       SANE_Byte ** buffer_addr_return,
580141cc406Sopenharmony_ci			       SANE_Int * buffer_bytes_return)
581141cc406Sopenharmony_ci{
582141cc406Sopenharmony_ci  SANE_Byte buf_index;
583141cc406Sopenharmony_ci  int bytes_read;
584141cc406Sopenharmony_ci
585141cc406Sopenharmony_ci  SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_get_buffer");
586141cc406Sopenharmony_ci
587141cc406Sopenharmony_ci  do
588141cc406Sopenharmony_ci    bytes_read = read (shm_channel->writer_put_pipe[0], &buf_index, 1);
589141cc406Sopenharmony_ci  while (bytes_read == -1 && errno == EINTR);
590141cc406Sopenharmony_ci
591141cc406Sopenharmony_ci  if (bytes_read == 1)
592141cc406Sopenharmony_ci    {
593141cc406Sopenharmony_ci      SANE_Int index = buf_index;
594141cc406Sopenharmony_ci      if (index < shm_channel->buf_count)
595141cc406Sopenharmony_ci	{
596141cc406Sopenharmony_ci	  *buffer_id_return = index;
597141cc406Sopenharmony_ci	  *buffer_addr_return = shm_channel->buffers[index];
598141cc406Sopenharmony_ci	  *buffer_bytes_return = shm_channel->buffer_bytes[index];
599141cc406Sopenharmony_ci	  return SANE_STATUS_GOOD;
600141cc406Sopenharmony_ci	}
601141cc406Sopenharmony_ci    }
602141cc406Sopenharmony_ci
603141cc406Sopenharmony_ci  *buffer_id_return = -1;
604141cc406Sopenharmony_ci  *buffer_addr_return = NULL;
605141cc406Sopenharmony_ci  *buffer_bytes_return = 0;
606141cc406Sopenharmony_ci  if (bytes_read == 0)
607141cc406Sopenharmony_ci    return SANE_STATUS_EOF;
608141cc406Sopenharmony_ci  else
609141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
610141cc406Sopenharmony_ci}
611141cc406Sopenharmony_ci
612141cc406Sopenharmony_ci/** Release a shared memory buffer received by the reader process.
613141cc406Sopenharmony_ci *
614141cc406Sopenharmony_ci * This function must be called after shm_channel_reader_get_buffer() to
615141cc406Sopenharmony_ci * release the buffer and make it available for transferring the next portion
616141cc406Sopenharmony_ci * of data.
617141cc406Sopenharmony_ci *
618141cc406Sopenharmony_ci * After calling this function the reader process must not access the buffer
619141cc406Sopenharmony_ci * contents; any data which may be needed later should be copied into some
620141cc406Sopenharmony_ci * other place beforehand.
621141cc406Sopenharmony_ci *
622141cc406Sopenharmony_ci * @param shm_channel Shared memory channel object.
623141cc406Sopenharmony_ci * @param buffer_id Buffer identifier from shm_channel_reader_get_buffer().
624141cc406Sopenharmony_ci *
625141cc406Sopenharmony_ci * @return
626141cc406Sopenharmony_ci * - SANE_STATUS_GOOD - the buffer was successfully released.
627141cc406Sopenharmony_ci * - SANE_STATUS_IO_ERROR - the writer process has closed its half of the
628141cc406Sopenharmony_ci *   channel, or an unexpected I/O error occurred.
629141cc406Sopenharmony_ci */
630141cc406Sopenharmony_ciSANE_Status
631141cc406Sopenharmony_cishm_channel_reader_put_buffer (Shm_Channel * shm_channel, SANE_Int buffer_id)
632141cc406Sopenharmony_ci{
633141cc406Sopenharmony_ci  SANE_Byte buf_index;
634141cc406Sopenharmony_ci  int bytes_written;
635141cc406Sopenharmony_ci
636141cc406Sopenharmony_ci  SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_put_buffer");
637141cc406Sopenharmony_ci
638141cc406Sopenharmony_ci  if (buffer_id < 0 || buffer_id >= shm_channel->buf_count)
639141cc406Sopenharmony_ci    {
640141cc406Sopenharmony_ci      DBG (3, "shm_channel_reader_put_buffer: BUG: buffer_id=%d\n",
641141cc406Sopenharmony_ci	   buffer_id);
642141cc406Sopenharmony_ci      return SANE_STATUS_INVAL;
643141cc406Sopenharmony_ci    }
644141cc406Sopenharmony_ci
645141cc406Sopenharmony_ci  buf_index = (SANE_Byte) buffer_id;
646141cc406Sopenharmony_ci  do
647141cc406Sopenharmony_ci    bytes_written = write (shm_channel->reader_put_pipe[1], &buf_index, 1);
648141cc406Sopenharmony_ci  while ((bytes_written == 0) || (bytes_written == -1 && errno == EINTR));
649141cc406Sopenharmony_ci
650141cc406Sopenharmony_ci  if (bytes_written == 1)
651141cc406Sopenharmony_ci    return SANE_STATUS_GOOD;
652141cc406Sopenharmony_ci  else
653141cc406Sopenharmony_ci    return SANE_STATUS_IO_ERROR;
654141cc406Sopenharmony_ci}
655141cc406Sopenharmony_ci
656141cc406Sopenharmony_ci#if 0
657141cc406Sopenharmony_ci/** Close the reading half of the shared memory channel.
658141cc406Sopenharmony_ci *
659141cc406Sopenharmony_ci * @param shm_channel Shared memory channel object.
660141cc406Sopenharmony_ci */
661141cc406Sopenharmony_ciSANE_Status
662141cc406Sopenharmony_cishm_channel_reader_close (Shm_Channel * shm_channel)
663141cc406Sopenharmony_ci{
664141cc406Sopenharmony_ci  SHM_CHANNEL_CHECK (shm_channel, "shm_channel_reader_close");
665141cc406Sopenharmony_ci
666141cc406Sopenharmony_ci  shm_channel_fd_safe_close (&shm_channel->reader_put_pipe[1]);
667141cc406Sopenharmony_ci
668141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
669141cc406Sopenharmony_ci}
670141cc406Sopenharmony_ci#endif
671141cc406Sopenharmony_ci/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */
672