1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy.
2141cc406Sopenharmony_ci
3141cc406Sopenharmony_ci   pieusb_buffer.c
4141cc406Sopenharmony_ci
5141cc406Sopenharmony_ci   Copyright (C) 2012-2015 Jan Vleeshouwers, Michael Rickmann, Klaus Kaempf
6141cc406Sopenharmony_ci
7141cc406Sopenharmony_ci   This file is part of the SANE package.
8141cc406Sopenharmony_ci
9141cc406Sopenharmony_ci   This program is free software; you can redistribute it and/or
10141cc406Sopenharmony_ci   modify it under the terms of the GNU General Public License as
11141cc406Sopenharmony_ci   published by the Free Software Foundation; either version 2 of the
12141cc406Sopenharmony_ci   License, or (at your option) any later version.
13141cc406Sopenharmony_ci
14141cc406Sopenharmony_ci   This program is distributed in the hope that it will be useful, but
15141cc406Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
16141cc406Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17141cc406Sopenharmony_ci   General Public License for more details.
18141cc406Sopenharmony_ci
19141cc406Sopenharmony_ci   You should have received a copy of the GNU General Public License
20141cc406Sopenharmony_ci   along with this program.  If not, see <https://www.gnu.org/licenses/>.
21141cc406Sopenharmony_ci
22141cc406Sopenharmony_ci   As a special exception, the authors of SANE give permission for
23141cc406Sopenharmony_ci   additional uses of the libraries contained in this release of SANE.
24141cc406Sopenharmony_ci
25141cc406Sopenharmony_ci   The exception is that, if you link a SANE library with other files
26141cc406Sopenharmony_ci   to produce an executable, this does not by itself cause the
27141cc406Sopenharmony_ci   resulting executable to be covered by the GNU General Public
28141cc406Sopenharmony_ci   License.  Your use of that executable is in no way restricted on
29141cc406Sopenharmony_ci   account of linking the SANE library code into it.
30141cc406Sopenharmony_ci
31141cc406Sopenharmony_ci   This exception does not, however, invalidate any other reasons why
32141cc406Sopenharmony_ci   the executable file might be covered by the GNU General Public
33141cc406Sopenharmony_ci   License.
34141cc406Sopenharmony_ci
35141cc406Sopenharmony_ci   If you submit changes to SANE to the maintainers to be included in
36141cc406Sopenharmony_ci   a subsequent release, you agree by submitting the changes that
37141cc406Sopenharmony_ci   those changes may be distributed with this exception intact.
38141cc406Sopenharmony_ci
39141cc406Sopenharmony_ci   If you write modifications of your own for SANE, it is your choice
40141cc406Sopenharmony_ci   whether to permit this exception to apply to your modifications.
41141cc406Sopenharmony_ci   If you do not wish that, delete this exception notice.  */
42141cc406Sopenharmony_ci
43141cc406Sopenharmony_ci/* =========================================================================
44141cc406Sopenharmony_ci *
45141cc406Sopenharmony_ci * Read buffer
46141cc406Sopenharmony_ci *
47141cc406Sopenharmony_ci * Data obtained from the scanner cannot be presented to the frontend immediately.
48141cc406Sopenharmony_ci * The scanner returns data in the 'index' or 'line' color format, which means it
49141cc406Sopenharmony_ci * returns data in batches which contain a single color of a scan line.
50141cc406Sopenharmony_ci *
51141cc406Sopenharmony_ci * These must finally be converted into the SANE data format (data for a single
52141cc406Sopenharmony_ci * pixel in consecutive bytes). Apart from that, sane_read() must be able to
53141cc406Sopenharmony_ci * return any amount of data bytes.
54141cc406Sopenharmony_ci *
55141cc406Sopenharmony_ci * In between, data processing may be necessary, usually requiring the whole
56141cc406Sopenharmony_ci * image to be available.
57141cc406Sopenharmony_ci *
58141cc406Sopenharmony_ci * To accommodate all this, the buffer stores all samples as 16-bit values, even
59141cc406Sopenharmony_ci * if the original values are 8-bit or even 1 bit. This is a waste of space, but
60141cc406Sopenharmony_ci * makes processing much easier, and it is only temporary.
61141cc406Sopenharmony_ci *
62141cc406Sopenharmony_ci * The read buffer is constructed by a call to buffer_create(), which initializes
63141cc406Sopenharmony_ci * the buffer based on width, height, number of colors and depth. The buffer
64141cc406Sopenharmony_ci * contains data organized in color planes, with each plane consisting of lines,
65141cc406Sopenharmony_ci * each line of a fixed number of (single color) pixels, and each pixel of a fixed
66141cc406Sopenharmony_ci * number of bits (or bytes).
67141cc406Sopenharmony_ci *
68141cc406Sopenharmony_ci * The buffer maintains read and write pointers.
69141cc406Sopenharmony_ci *
70141cc406Sopenharmony_ci * Multi-color data with a bit depth of 1 are packed in single color bytes, so
71141cc406Sopenharmony_ci * the data obtained from the scanner does not need conversion.
72141cc406Sopenharmony_ci *
73141cc406Sopenharmony_ci * ========================================================================= */
74141cc406Sopenharmony_ci
75141cc406Sopenharmony_ci#define DEBUG_DECLARE_ONLY
76141cc406Sopenharmony_ci
77141cc406Sopenharmony_ci/* Configuration defines */
78141cc406Sopenharmony_ci#include "../include/sane/config.h"
79141cc406Sopenharmony_ci
80141cc406Sopenharmony_ci/* SANE includes */
81141cc406Sopenharmony_ci#include "../include/sane/sane.h"
82141cc406Sopenharmony_ci#include "../include/sane/saneopts.h"
83141cc406Sopenharmony_ci#include "../include/sane/sanei_usb.h"
84141cc406Sopenharmony_ci#include "../include/sane/sanei_config.h"
85141cc406Sopenharmony_ci
86141cc406Sopenharmony_ci/* Backend includes */
87141cc406Sopenharmony_ci#define BACKEND_NAME pieusb
88141cc406Sopenharmony_ci#include "../include/sane/sanei_backend.h"
89141cc406Sopenharmony_ci#include "pieusb.h"
90141cc406Sopenharmony_ci
91141cc406Sopenharmony_ci#include "pieusb_specific.h"
92141cc406Sopenharmony_ci#include "pieusb_buffer.h"
93141cc406Sopenharmony_ci
94141cc406Sopenharmony_ci#ifdef HAVE_ALLOCA_H
95141cc406Sopenharmony_ci#include <alloca.h>
96141cc406Sopenharmony_ci#endif
97141cc406Sopenharmony_ci
98141cc406Sopenharmony_ci#include <stdio.h>
99141cc406Sopenharmony_ci#include <fcntl.h>
100141cc406Sopenharmony_ci#include <sys/mman.h>
101141cc406Sopenharmony_ci
102141cc406Sopenharmony_ci#include "byteorder.h"
103141cc406Sopenharmony_ci
104141cc406Sopenharmony_cistatic void buffer_update_read_index(struct Pieusb_Read_Buffer* buffer, int increment);
105141cc406Sopenharmony_ci
106141cc406Sopenharmony_ci/* READER */
107141cc406Sopenharmony_ci
108141cc406Sopenharmony_ci/**
109141cc406Sopenharmony_ci * Initialize the buffer.
110141cc406Sopenharmony_ci * A scanner has a Pieusb_Read_Buffer struct as one of its members.
111141cc406Sopenharmony_ci *
112141cc406Sopenharmony_ci * @param buffer the buffer to initialize
113141cc406Sopenharmony_ci * @param width number of pixels on a line (row)
114141cc406Sopenharmony_ci * @param height number of lines in the buffer (pixels in a column)
115141cc406Sopenharmony_ci * @param colors bitmap specifying the colors in the scanned data (bitmap: 0000 IBGR)
116141cc406Sopenharmony_ci * @param depth number of bits of a color
117141cc406Sopenharmony_ci * @param bigendian how to store multi-byte values: bigendian if true
118141cc406Sopenharmony_ci * @param maximum_size maximum size of buffer (-1 = size of image)
119141cc406Sopenharmony_ci */
120141cc406Sopenharmony_ci
121141cc406Sopenharmony_ciSANE_Status
122141cc406Sopenharmony_cisanei_pieusb_buffer_create(struct Pieusb_Read_Buffer* buffer, SANE_Int width, SANE_Int height, SANE_Byte color_spec, SANE_Byte depth)
123141cc406Sopenharmony_ci{
124141cc406Sopenharmony_ci    int k, result;
125141cc406Sopenharmony_ci    unsigned int buffer_size_bytes;
126141cc406Sopenharmony_ci    unsigned char g;
127141cc406Sopenharmony_ci
128141cc406Sopenharmony_ci    /* Base parameters */
129141cc406Sopenharmony_ci    buffer->width = width;
130141cc406Sopenharmony_ci    buffer->height = height;
131141cc406Sopenharmony_ci    buffer->colors = 0;
132141cc406Sopenharmony_ci    if (color_spec & 0x01) { buffer->color_index_red = 0; buffer->colors++; } else { buffer->color_index_red = -1; }
133141cc406Sopenharmony_ci    if (color_spec & 0x02) { buffer->color_index_green = 1; buffer->colors++; } else { buffer->color_index_green = -1; }
134141cc406Sopenharmony_ci    if (color_spec & 0x04) { buffer->color_index_blue = 2; buffer->colors++; } else { buffer->color_index_blue = -1; }
135141cc406Sopenharmony_ci    if (color_spec & 0x08) { buffer->color_index_infrared = 3; buffer->colors++; } else { buffer->color_index_infrared = -1; }
136141cc406Sopenharmony_ci    if (buffer->colors == 0) {
137141cc406Sopenharmony_ci        DBG(DBG_error, "sanei_pieusb_buffer_create(): no colors specified\n");
138141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
139141cc406Sopenharmony_ci    }
140141cc406Sopenharmony_ci    buffer->depth = depth;
141141cc406Sopenharmony_ci    if (depth < 1 || depth > 16) {
142141cc406Sopenharmony_ci        DBG(DBG_error, "sanei_pieusb_buffer_create(): unsupported depth %d\n", depth);
143141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
144141cc406Sopenharmony_ci    }
145141cc406Sopenharmony_ci    buffer->packing_density = (depth == 1) ? 8 : 1; /* These are all the situations we have */
146141cc406Sopenharmony_ci
147141cc406Sopenharmony_ci    /* Derived*/
148141cc406Sopenharmony_ci    buffer->packet_size_bytes = (buffer->depth * buffer->packing_density + 7) / 8;
149141cc406Sopenharmony_ci    buffer->line_size_packets = (buffer->width + buffer->packing_density -1) / buffer->packing_density;
150141cc406Sopenharmony_ci    buffer->line_size_bytes = buffer->line_size_packets * buffer->packet_size_bytes;
151141cc406Sopenharmony_ci    buffer->image_size_bytes = buffer->colors * buffer->height * buffer->line_size_bytes;
152141cc406Sopenharmony_ci
153141cc406Sopenharmony_ci    /* Create empty file */
154141cc406Sopenharmony_ci    snprintf(buffer->buffer_name, L_tmpnam, "/tmp/sane.XXXXXX");
155141cc406Sopenharmony_ci    if (buffer->data_file != 0) /* might still be open from previous invocation */
156141cc406Sopenharmony_ci      close(buffer->data_file);
157141cc406Sopenharmony_ci    buffer->data_file = mkstemp(buffer->buffer_name);
158141cc406Sopenharmony_ci    if (buffer->data_file == -1) {
159141cc406Sopenharmony_ci        buffer->data_file = 0;
160141cc406Sopenharmony_ci        buffer->data = NULL;
161141cc406Sopenharmony_ci	perror("sanei_pieusb_buffer_create(): error opening image buffer file");
162141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
163141cc406Sopenharmony_ci    }
164141cc406Sopenharmony_ci    /* Stretch the file size */
165141cc406Sopenharmony_ci    buffer_size_bytes = buffer->width * buffer->height * buffer->colors * sizeof(SANE_Uint);
166141cc406Sopenharmony_ci    if (buffer_size_bytes == 0) {
167141cc406Sopenharmony_ci        close(buffer->data_file);
168141cc406Sopenharmony_ci        buffer->data_file = 0;
169141cc406Sopenharmony_ci        DBG(DBG_error, "sanei_pieusb_buffer_create(): buffer_size is zero: width %d, height %d, colors %d\n", buffer->width, buffer->height, buffer->colors);
170141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
171141cc406Sopenharmony_ci    }
172141cc406Sopenharmony_ci    result = lseek(buffer->data_file, buffer_size_bytes-1, SEEK_SET);
173141cc406Sopenharmony_ci    if (result == -1) {
174141cc406Sopenharmony_ci	close(buffer->data_file);
175141cc406Sopenharmony_ci        buffer->data_file = 0;
176141cc406Sopenharmony_ci        buffer->data = NULL;
177141cc406Sopenharmony_ci        DBG(DBG_error, "sanei_pieusb_buffer_create(): error calling lseek() to 'stretch' the file to %d bytes\n", buffer_size_bytes-1);
178141cc406Sopenharmony_ci	perror("sanei_pieusb_buffer_create(): error calling lseek()");
179141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
180141cc406Sopenharmony_ci    }
181141cc406Sopenharmony_ci    /* Write one byte at the end */
182141cc406Sopenharmony_ci    g = 0x00;
183141cc406Sopenharmony_ci    result = write(buffer->data_file, &g, 1);
184141cc406Sopenharmony_ci    if (result < 0) {
185141cc406Sopenharmony_ci	close(buffer->data_file);
186141cc406Sopenharmony_ci        buffer->data_file = 0;
187141cc406Sopenharmony_ci        buffer->data = NULL;
188141cc406Sopenharmony_ci	perror("sanei_pieusb_buffer_create(): error writing a byte at the end of the file");
189141cc406Sopenharmony_ci        return SANE_STATUS_IO_ERROR;
190141cc406Sopenharmony_ci    }
191141cc406Sopenharmony_ci#ifdef HAVE_MMAP
192141cc406Sopenharmony_ci    /* Create memory map */
193141cc406Sopenharmony_ci    buffer->data = mmap(NULL, buffer_size_bytes, PROT_WRITE | PROT_READ, MAP_SHARED, buffer->data_file, 0);
194141cc406Sopenharmony_ci    if (buffer->data == MAP_FAILED) {
195141cc406Sopenharmony_ci        close(buffer->data_file);
196141cc406Sopenharmony_ci        buffer->data = NULL;
197141cc406Sopenharmony_ci        perror("sanei_pieusb_buffer_create(): error mapping file");
198141cc406Sopenharmony_ci        return SANE_STATUS_INVAL;
199141cc406Sopenharmony_ci    }
200141cc406Sopenharmony_ci#else
201141cc406Sopenharmony_ci#error mmap(2) not available, aborting
202141cc406Sopenharmony_ci#endif
203141cc406Sopenharmony_ci    buffer->data_size = buffer_size_bytes;
204141cc406Sopenharmony_ci    /* Reading and writing */
205141cc406Sopenharmony_ci    buffer->p_read = calloc(buffer->colors, sizeof(SANE_Uint*));
206141cc406Sopenharmony_ci    if (buffer->p_read == NULL)
207141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
208141cc406Sopenharmony_ci    buffer->p_write = calloc(buffer->colors, sizeof(SANE_Uint*));
209141cc406Sopenharmony_ci    if (buffer->p_write == NULL)
210141cc406Sopenharmony_ci      return SANE_STATUS_NO_MEM;
211141cc406Sopenharmony_ci    for (k = 0; k < buffer->colors; k++) {
212141cc406Sopenharmony_ci        buffer->p_write[k] = buffer->data + k * buffer->height * buffer->width;
213141cc406Sopenharmony_ci        buffer->p_read[k] = buffer->p_write[k];
214141cc406Sopenharmony_ci    }
215141cc406Sopenharmony_ci    buffer->read_index[0] = 0;
216141cc406Sopenharmony_ci    buffer->read_index[1] = 0;
217141cc406Sopenharmony_ci    buffer->read_index[2] = 0;
218141cc406Sopenharmony_ci    buffer->read_index[3] = 0;
219141cc406Sopenharmony_ci
220141cc406Sopenharmony_ci    /* Statistics */
221141cc406Sopenharmony_ci    buffer->bytes_read = 0;
222141cc406Sopenharmony_ci    buffer->bytes_written = 0;
223141cc406Sopenharmony_ci    buffer->bytes_unread = 0;
224141cc406Sopenharmony_ci
225141cc406Sopenharmony_ci    DBG(DBG_info,"pieusb: Read buffer created: w=%d h=%d ncol=%d depth=%d in file %s\n",
226141cc406Sopenharmony_ci      buffer->width, buffer->height, buffer->colors, buffer->depth, buffer->buffer_name);
227141cc406Sopenharmony_ci  return SANE_STATUS_GOOD;
228141cc406Sopenharmony_ci}
229141cc406Sopenharmony_ci
230141cc406Sopenharmony_ci/**
231141cc406Sopenharmony_ci * Delete buffer and free its resources
232141cc406Sopenharmony_ci *
233141cc406Sopenharmony_ci * @param buffer
234141cc406Sopenharmony_ci */
235141cc406Sopenharmony_civoid
236141cc406Sopenharmony_cisanei_pieusb_buffer_delete(struct Pieusb_Read_Buffer* buffer)
237141cc406Sopenharmony_ci{
238141cc406Sopenharmony_ci#ifdef HAVE_MMAP
239141cc406Sopenharmony_ci    munmap(buffer->data, buffer->data_size);
240141cc406Sopenharmony_ci#else
241141cc406Sopenharmony_ci#error mmap(2) not available, aborting
242141cc406Sopenharmony_ci#endif
243141cc406Sopenharmony_ci    /* ftruncate(buffer->data_file,0); Can we delete given a file-descriptor? */
244141cc406Sopenharmony_ci    close(buffer->data_file);
245141cc406Sopenharmony_ci    /* remove fs entry */
246141cc406Sopenharmony_ci    unlink(buffer->buffer_name);
247141cc406Sopenharmony_ci    buffer->data_file = 0;
248141cc406Sopenharmony_ci    buffer->data_size = 0;
249141cc406Sopenharmony_ci    free(buffer->p_read);
250141cc406Sopenharmony_ci    free(buffer->p_write);
251141cc406Sopenharmony_ci    buffer->data = 0;
252141cc406Sopenharmony_ci    buffer->width = 0;
253141cc406Sopenharmony_ci    buffer->height = 0;
254141cc406Sopenharmony_ci    buffer->depth = 0;
255141cc406Sopenharmony_ci    buffer->colors = 0;
256141cc406Sopenharmony_ci    buffer->packing_density = 0;
257141cc406Sopenharmony_ci
258141cc406Sopenharmony_ci    DBG(DBG_info,"pieusb: Read buffer deleted\n");
259141cc406Sopenharmony_ci}
260141cc406Sopenharmony_ci
261141cc406Sopenharmony_ci/**
262141cc406Sopenharmony_ci * Add a line to the reader buffer, for the given color.
263141cc406Sopenharmony_ci * The buffer checks and decides how to interpret the data.
264141cc406Sopenharmony_ci *
265141cc406Sopenharmony_ci * @param buffer
266141cc406Sopenharmony_ci * @param color Color code for line
267141cc406Sopenharmony_ci * @param line
268141cc406Sopenharmony_ci * @param size Number of bytes in line
269141cc406Sopenharmony_ci * @return 1 if successful, 0 if not
270141cc406Sopenharmony_ci */
271141cc406Sopenharmony_ciSANE_Int
272141cc406Sopenharmony_cisanei_pieusb_buffer_put_single_color_line(struct Pieusb_Read_Buffer* buffer, SANE_Byte color, void* line, SANE_Int size)
273141cc406Sopenharmony_ci{
274141cc406Sopenharmony_ci
275141cc406Sopenharmony_ci    SANE_Int c, k, m, n;
276141cc406Sopenharmony_ci
277141cc406Sopenharmony_ci    /* Check index code */
278141cc406Sopenharmony_ci    c = -1;
279141cc406Sopenharmony_ci    switch (color) {
280141cc406Sopenharmony_ci        case 'R':
281141cc406Sopenharmony_ci            c = buffer->color_index_red;
282141cc406Sopenharmony_ci            break;
283141cc406Sopenharmony_ci        case 'G':
284141cc406Sopenharmony_ci            c = buffer->color_index_green;
285141cc406Sopenharmony_ci            break;
286141cc406Sopenharmony_ci        case 'B':
287141cc406Sopenharmony_ci            c = buffer->color_index_blue;
288141cc406Sopenharmony_ci            break;
289141cc406Sopenharmony_ci        case 'I':
290141cc406Sopenharmony_ci            c = buffer->color_index_infrared;
291141cc406Sopenharmony_ci            break;
292141cc406Sopenharmony_ci    }
293141cc406Sopenharmony_ci    if (c == -1) {
294141cc406Sopenharmony_ci        DBG(DBG_error, "sanei_pieusb_buffer_put_single_color_line(): color '%c' not specified when buffer was created\n", color);
295141cc406Sopenharmony_ci        return 0;
296141cc406Sopenharmony_ci    }
297141cc406Sopenharmony_ci    DBG(DBG_info_buffer, "sanei_pieusb_buffer_put_single_color_line() line color = %d (0=R, 1=G, 2=B, 3=I)\n",c);
298141cc406Sopenharmony_ci
299141cc406Sopenharmony_ci    /* Check line size (for a line with a single color) */
300141cc406Sopenharmony_ci    if (buffer->line_size_bytes != size) {
301141cc406Sopenharmony_ci        DBG(DBG_error, "sanei_pieusb_buffer_put_single_color_line(): incorrect line size, expecting %d, got %d\n", buffer->line_size_bytes, size);
302141cc406Sopenharmony_ci        return 0;
303141cc406Sopenharmony_ci    }
304141cc406Sopenharmony_ci
305141cc406Sopenharmony_ci    /* The general approach for all densities and packet sizes
306141cc406Sopenharmony_ci     * - process packet_size_bytes at a time
307141cc406Sopenharmony_ci     * - use packing_density to decode the full packet into separate values
308141cc406Sopenharmony_ci     * - now save these values as neighbouring pixels on the current line
309141cc406Sopenharmony_ci     * Use custom code for the 1-byte and 2-byte single sample cases,
310141cc406Sopenharmony_ci     * because the general approach is a bit overkill for them.
311141cc406Sopenharmony_ci     */
312141cc406Sopenharmony_ci
313141cc406Sopenharmony_ci    if (buffer->packet_size_bytes == 1 && buffer->packing_density == 1) {
314141cc406Sopenharmony_ci        uint8_t* p_packet = (uint8_t*)line;
315141cc406Sopenharmony_ci        n = 0;
316141cc406Sopenharmony_ci        while (n < size) {
317141cc406Sopenharmony_ci            /* Get next packet data & store in buffer */
318141cc406Sopenharmony_ci            *buffer->p_write[c]++ = *p_packet++;
319141cc406Sopenharmony_ci            n++;
320141cc406Sopenharmony_ci        }
321141cc406Sopenharmony_ci    } else if (buffer->packet_size_bytes == 2 && buffer->packing_density == 1) {
322141cc406Sopenharmony_ci        uint16_t* p_packet = (uint16_t*)line;
323141cc406Sopenharmony_ci        n = 0;
324141cc406Sopenharmony_ci        while (n < size) {
325141cc406Sopenharmony_ci            /* Get next packet data & store in buffer */
326141cc406Sopenharmony_ci            *buffer->p_write[c]++ = le16toh (*p_packet++);
327141cc406Sopenharmony_ci            n += 2;
328141cc406Sopenharmony_ci        }
329141cc406Sopenharmony_ci    } else {
330141cc406Sopenharmony_ci        uint8_t* p_packet = (uint8_t*)line;
331141cc406Sopenharmony_ci        uint8_t *packet = (uint8_t *)alloca(buffer->packet_size_bytes * sizeof(uint8_t));
332141cc406Sopenharmony_ci        SANE_Uint val;
333141cc406Sopenharmony_ci        uint8_t mask = ~(0xFF >> buffer->depth); /* byte with depth most significant bits set */
334141cc406Sopenharmony_ci        n = 0;
335141cc406Sopenharmony_ci        while (n < size) {
336141cc406Sopenharmony_ci            /* Get next packet data */
337141cc406Sopenharmony_ci            for (k = 0; k < buffer->packet_size_bytes; k++) packet[k] = *p_packet++;
338141cc406Sopenharmony_ci            /* Unpack packing_density samples from packet. Of course,
339141cc406Sopenharmony_ci             * buffer->depth * packing_density <= # bits in packet.
340141cc406Sopenharmony_ci             * That is checked at buffer creation. */
341141cc406Sopenharmony_ci            for (k = 0; k < buffer->packing_density; k++) {
342141cc406Sopenharmony_ci                /* Take 1st depth bits and store in val */
343141cc406Sopenharmony_ci                val = (packet[0] & mask) >> (8-buffer->depth);
344141cc406Sopenharmony_ci                /* Now shift packet bytes depth bits left */
345141cc406Sopenharmony_ci                for (m = 0; m < buffer->packet_size_bytes; m++) {
346141cc406Sopenharmony_ci                    /* Shift left one sample */
347141cc406Sopenharmony_ci                    packet[m] <<= buffer->depth;
348141cc406Sopenharmony_ci                    if (m < buffer->packet_size_bytes-1) {
349141cc406Sopenharmony_ci                        /* If there are more bytes, insert 1st depth bits of next byte */
350141cc406Sopenharmony_ci                        packet[m] |= (packet[m+1] >> (8-buffer->depth));
351141cc406Sopenharmony_ci                    }
352141cc406Sopenharmony_ci                }
353141cc406Sopenharmony_ci                /* Store in buffer */
354141cc406Sopenharmony_ci                *buffer->p_write[c]++ = val;
355141cc406Sopenharmony_ci            }
356141cc406Sopenharmony_ci            n += buffer->packet_size_bytes;
357141cc406Sopenharmony_ci        }
358141cc406Sopenharmony_ci    }
359141cc406Sopenharmony_ci
360141cc406Sopenharmony_ci    /* Update state & statistics */
361141cc406Sopenharmony_ci    buffer->bytes_written += size;
362141cc406Sopenharmony_ci    buffer->bytes_unread += size;
363141cc406Sopenharmony_ci
364141cc406Sopenharmony_ci    /* Output current buffer state */
365141cc406Sopenharmony_ci    /* buffer_output_state(buffer); */
366141cc406Sopenharmony_ci
367141cc406Sopenharmony_ci    return 1;
368141cc406Sopenharmony_ci}
369141cc406Sopenharmony_ci
370141cc406Sopenharmony_ci/**
371141cc406Sopenharmony_ci * Write line of full color pixels to the buffer.
372141cc406Sopenharmony_ci *
373141cc406Sopenharmony_ci * @param buffer Read buffer
374141cc406Sopenharmony_ci * @param pixel array of full color pixel data
375141cc406Sopenharmony_ci * @param size Number of bytes in the line
376141cc406Sopenharmony_ci * @return 1 if successful, 0 if not
377141cc406Sopenharmony_ci */
378141cc406Sopenharmony_ci/**
379141cc406Sopenharmony_ci *
380141cc406Sopenharmony_ci */
381141cc406Sopenharmony_ciSANE_Int
382141cc406Sopenharmony_cisanei_pieusb_buffer_put_full_color_line(struct Pieusb_Read_Buffer* buffer, void* line, int size)
383141cc406Sopenharmony_ci{
384141cc406Sopenharmony_ci    int k, c, m, n;
385141cc406Sopenharmony_ci
386141cc406Sopenharmony_ci    DBG(DBG_info_buffer, "sanei_pieusb_buffer_put_full_color_line() entered\n");
387141cc406Sopenharmony_ci
388141cc406Sopenharmony_ci    /* Check line size */
389141cc406Sopenharmony_ci    if (buffer->line_size_bytes * buffer->colors != size) {
390141cc406Sopenharmony_ci        DBG(DBG_error, "sanei_pieusb_buffer_put_full_color_line(): incorrect line size, expecting %d, got %d\n", buffer->line_size_bytes * buffer->colors, size);
391141cc406Sopenharmony_ci        return 0;
392141cc406Sopenharmony_ci    }
393141cc406Sopenharmony_ci
394141cc406Sopenharmony_ci    /* The general approach for all densities and packet sizes
395141cc406Sopenharmony_ci     * - process packet_size_bytes at a time
396141cc406Sopenharmony_ci     * - use packing_density to decode the full packet into separate values
397141cc406Sopenharmony_ci     * - now save these values as neighbouring pixels on the current line
398141cc406Sopenharmony_ci     * Use custom code for the 1-byte and 2-byte single sample cases,
399141cc406Sopenharmony_ci     * because the general approach is a bit overkill for them.
400141cc406Sopenharmony_ci     */
401141cc406Sopenharmony_ci
402141cc406Sopenharmony_ci    if (buffer->packet_size_bytes == 1 && buffer->packing_density == 1) {
403141cc406Sopenharmony_ci        uint8_t* p_packet = (uint8_t*)line;
404141cc406Sopenharmony_ci        n = 0;
405141cc406Sopenharmony_ci        while (n < size) {
406141cc406Sopenharmony_ci            /* Get next packet data & store in buffer */
407141cc406Sopenharmony_ci            for (c = 0; c < buffer->colors; c++) {
408141cc406Sopenharmony_ci                *buffer->p_write[c]++ = *p_packet++;
409141cc406Sopenharmony_ci                n++;
410141cc406Sopenharmony_ci            }
411141cc406Sopenharmony_ci        }
412141cc406Sopenharmony_ci    } else if (buffer->packet_size_bytes == 2 && buffer->packing_density == 1) {
413141cc406Sopenharmony_ci        uint16_t* p_packet = (uint16_t*)line;
414141cc406Sopenharmony_ci        n = 0;
415141cc406Sopenharmony_ci        while (n < size) {
416141cc406Sopenharmony_ci            /* Get next packet data & store in buffer */
417141cc406Sopenharmony_ci            for (c = 0; c < buffer->colors; c++) {
418141cc406Sopenharmony_ci                *buffer->p_write[c]++ = le16toh (*p_packet++);
419141cc406Sopenharmony_ci                n += 2;
420141cc406Sopenharmony_ci            }
421141cc406Sopenharmony_ci        }
422141cc406Sopenharmony_ci    } else {
423141cc406Sopenharmony_ci        uint8_t* p_packet = (uint8_t*)line;
424141cc406Sopenharmony_ci        uint8_t *packet = (uint8_t *)alloca(buffer->packet_size_bytes * sizeof(uint8_t));
425141cc406Sopenharmony_ci        SANE_Uint val;
426141cc406Sopenharmony_ci        uint8_t mask = ~(0xFF >> buffer->depth); /* byte with depth most significant bits set */
427141cc406Sopenharmony_ci        /* DBG(DBG_info,"buffer_put_full_color_line(): mask %02x\n",mask); */
428141cc406Sopenharmony_ci        n = 0;
429141cc406Sopenharmony_ci        while (n < size) {
430141cc406Sopenharmony_ci            /* Get next packet data */
431141cc406Sopenharmony_ci            for (c = 0; c < buffer->colors; c++) {
432141cc406Sopenharmony_ci                for (k = 0; k < buffer->packet_size_bytes; k++) {
433141cc406Sopenharmony_ci                    packet[k] = *p_packet++;
434141cc406Sopenharmony_ci                    /* DBG(DBG_info,"buffer_put_full_color_line(): packet[%d] = %02x\n",k,packet[k]); */
435141cc406Sopenharmony_ci                }
436141cc406Sopenharmony_ci                /* Unpack packing_density samples from packet. Of course,
437141cc406Sopenharmony_ci                 * buffer->depth * packing_density <= # bits in packet.
438141cc406Sopenharmony_ci                 * That is checked at buffer creation. */
439141cc406Sopenharmony_ci                for (k = 0; k < buffer->packing_density; k++) {
440141cc406Sopenharmony_ci                    /* Take 1st depth bits and store in val */
441141cc406Sopenharmony_ci                    val = (packet[0] & mask) >> (8-buffer->depth);
442141cc406Sopenharmony_ci                    /* DBG(DBG_info,"buffer_put_full_color_line(): val[%d] = %02x\n",k,val); */
443141cc406Sopenharmony_ci                    /* Now shift packet bytes depth bits left */
444141cc406Sopenharmony_ci                    for (m = 0; m < buffer->packet_size_bytes; m++) {
445141cc406Sopenharmony_ci                        /* Shift left one sample */
446141cc406Sopenharmony_ci                        packet[m] <<= buffer->depth;
447141cc406Sopenharmony_ci                        /* DBG(DBG_info,"buffer_put_full_color_line(): shift packet[%d] = %02x\n",m,packet[m]); */
448141cc406Sopenharmony_ci                        if (m < buffer->packet_size_bytes-1) {
449141cc406Sopenharmony_ci                            /* If there are more bytes, insert 1st depth bits of next byte */
450141cc406Sopenharmony_ci                            packet[m] |= (packet[m+1] >> (8-buffer->depth));
451141cc406Sopenharmony_ci                            /* DBG(DBG_info,"buffer_put_full_color_line(): shift packet[%d] = %02x\n",m,packet[m]); */
452141cc406Sopenharmony_ci                        }
453141cc406Sopenharmony_ci                    }
454141cc406Sopenharmony_ci                    /* Store in buffer */
455141cc406Sopenharmony_ci                    *buffer->p_write[c]++ = val;
456141cc406Sopenharmony_ci                }
457141cc406Sopenharmony_ci                n += buffer->packet_size_bytes;
458141cc406Sopenharmony_ci            }
459141cc406Sopenharmony_ci        }
460141cc406Sopenharmony_ci    }
461141cc406Sopenharmony_ci
462141cc406Sopenharmony_ci    /* Update state & statistics */
463141cc406Sopenharmony_ci    buffer->bytes_written += size;
464141cc406Sopenharmony_ci    buffer->bytes_unread += size;
465141cc406Sopenharmony_ci
466141cc406Sopenharmony_ci    /* Output current buffer state */
467141cc406Sopenharmony_ci    /* buffer_output_state(buffer); */
468141cc406Sopenharmony_ci
469141cc406Sopenharmony_ci    return 1;
470141cc406Sopenharmony_ci}
471141cc406Sopenharmony_ci
472141cc406Sopenharmony_ci/**
473141cc406Sopenharmony_ci * Return bytes from the buffer. Do not mind pixel boundaries.
474141cc406Sopenharmony_ci * Since the image data is organized in color planes, return bytes from the
475141cc406Sopenharmony_ci * planes in the defined order. Take care to return multi-byte values after
476141cc406Sopenharmony_ci * each other. Pack unpacked values.
477141cc406Sopenharmony_ci *
478141cc406Sopenharmony_ci * @param buffer Buffer to return bytes from.
479141cc406Sopenharmony_ci * @param data Byte array to return bytes in
480141cc406Sopenharmony_ci * @param max_len Maximum number of bytes returned
481141cc406Sopenharmony_ci * @param len Actual number of bytes returned
482141cc406Sopenharmony_ci */
483141cc406Sopenharmony_civoid
484141cc406Sopenharmony_cisanei_pieusb_buffer_get(struct Pieusb_Read_Buffer* buffer, SANE_Byte* data, SANE_Int max_len, SANE_Int* len)
485141cc406Sopenharmony_ci{
486141cc406Sopenharmony_ci    SANE_Byte *pdata;
487141cc406Sopenharmony_ci    SANE_Int n, i, n_bits, N;
488141cc406Sopenharmony_ci
489141cc406Sopenharmony_ci    DBG(DBG_info_buffer, "sanei_pieusb_buffer_get() entered\n");
490141cc406Sopenharmony_ci
491141cc406Sopenharmony_ci    /* Read from the p_read locations */
492141cc406Sopenharmony_ci    pdata = data;
493141cc406Sopenharmony_ci    n = 0;
494141cc406Sopenharmony_ci    N = buffer->width * buffer->height;
495141cc406Sopenharmony_ci    /* Determine bytes to return */
496141cc406Sopenharmony_ci    if (buffer->packet_size_bytes == 1 && buffer->packing_density == 1) {
497141cc406Sopenharmony_ci        /* Single byte values in buffer */
498141cc406Sopenharmony_ci        while (n < max_len && buffer->bytes_read < buffer->image_size_bytes) {
499141cc406Sopenharmony_ci            /* Return byte*/
500141cc406Sopenharmony_ci            *pdata++ = *(buffer->data + N*buffer->read_index[0] + buffer->width*buffer->read_index[1] + buffer->read_index[2]) & 0xFF;
501141cc406Sopenharmony_ci            /* Update read indices */
502141cc406Sopenharmony_ci            buffer_update_read_index(buffer,1);
503141cc406Sopenharmony_ci            /* Update number of bytes read */
504141cc406Sopenharmony_ci            buffer->bytes_read++;
505141cc406Sopenharmony_ci            n++;
506141cc406Sopenharmony_ci        }
507141cc406Sopenharmony_ci    } else if (buffer->packet_size_bytes == 1 && buffer->packing_density == 8) {
508141cc406Sopenharmony_ci        /* Unpacked bits in buffer: repack */
509141cc406Sopenharmony_ci        while (n < max_len && buffer->bytes_read < buffer->image_size_bytes) {
510141cc406Sopenharmony_ci            uint8_t val = 0;
511141cc406Sopenharmony_ci            /* How many bits to pack? At the end of a line it may be less than 8 */
512141cc406Sopenharmony_ci            n_bits = 8;
513141cc406Sopenharmony_ci            if (buffer->width - buffer->read_index[2] < 8) {
514141cc406Sopenharmony_ci                n_bits = buffer->width - buffer->read_index[2];
515141cc406Sopenharmony_ci            }
516141cc406Sopenharmony_ci            /* Pack n_bits samples from same color plane */
517141cc406Sopenharmony_ci            for (i = 0; i < n_bits; i++) {
518141cc406Sopenharmony_ci                if (*(buffer->data + N*buffer->read_index[0] + buffer->width*buffer->read_index[1] + buffer->read_index[2] + i) > 0) {
519141cc406Sopenharmony_ci                    val |= (0x80 >> i);
520141cc406Sopenharmony_ci                }
521141cc406Sopenharmony_ci            }
522141cc406Sopenharmony_ci            /* Return byte */
523141cc406Sopenharmony_ci            *pdata++ = val;
524141cc406Sopenharmony_ci            /* Update read indices */
525141cc406Sopenharmony_ci            buffer_update_read_index(buffer,n_bits);
526141cc406Sopenharmony_ci            /* Update number of bytes read */
527141cc406Sopenharmony_ci            buffer->bytes_read++;
528141cc406Sopenharmony_ci            n++;
529141cc406Sopenharmony_ci        }
530141cc406Sopenharmony_ci    } else if (buffer->packet_size_bytes == 2) {
531141cc406Sopenharmony_ci        /* Two-byte values in buffer */
532141cc406Sopenharmony_ci        while (n < max_len && buffer->bytes_read < buffer->image_size_bytes) {
533141cc406Sopenharmony_ci            /* Pointer to byte to return */
534141cc406Sopenharmony_ci            SANE_Uint val = *(buffer->data + N*buffer->read_index[0] + buffer->width*buffer->read_index[1] + buffer->read_index[2]);
535141cc406Sopenharmony_ci            /* Return byte */
536141cc406Sopenharmony_ci            if (buffer->read_index[3] == 0) {
537141cc406Sopenharmony_ci                *pdata++ = *((SANE_Byte*)(&val));
538141cc406Sopenharmony_ci            } else {
539141cc406Sopenharmony_ci                *pdata++ = *((SANE_Byte*)(&val)+1);
540141cc406Sopenharmony_ci            }
541141cc406Sopenharmony_ci            /* Update read indices */
542141cc406Sopenharmony_ci            buffer_update_read_index(buffer,1);
543141cc406Sopenharmony_ci            /* Update number of bytes read */
544141cc406Sopenharmony_ci            buffer->bytes_read++;
545141cc406Sopenharmony_ci            n++;
546141cc406Sopenharmony_ci        }
547141cc406Sopenharmony_ci    } else {
548141cc406Sopenharmony_ci        /* not implemented */
549141cc406Sopenharmony_ci        DBG(DBG_error, "buffer_put(): paccket size & density of %d/%d not implemented\n", buffer->packet_size_bytes, buffer->packing_density);
550141cc406Sopenharmony_ci        return;
551141cc406Sopenharmony_ci    }
552141cc406Sopenharmony_ci    *len = n;
553141cc406Sopenharmony_ci
554141cc406Sopenharmony_ci    /* Update statistics */
555141cc406Sopenharmony_ci    buffer->bytes_unread -= n;
556141cc406Sopenharmony_ci
557141cc406Sopenharmony_ci    /* Output current buffer state */
558141cc406Sopenharmony_ci    /* buffer_output_state(buffer); */
559141cc406Sopenharmony_ci}
560141cc406Sopenharmony_ci
561141cc406Sopenharmony_ci/**
562141cc406Sopenharmony_ci * Update read index to point a given number of bytes past the current position.
563141cc406Sopenharmony_ci *
564141cc406Sopenharmony_ci * @param buffer the buffer to initialize
565141cc406Sopenharmony_ci * @param increment the amount of bytes to move the index
566141cc406Sopenharmony_ci */
567141cc406Sopenharmony_cistatic void buffer_update_read_index(struct Pieusb_Read_Buffer* buffer, int increment)
568141cc406Sopenharmony_ci{
569141cc406Sopenharmony_ci    /* Update read indices
570141cc406Sopenharmony_ci     * [3] = byte-index in 2-byte value: increased first, if we have 2-byte data
571141cc406Sopenharmony_ci     * [2] = index of pixel on line: increased after color plane
572141cc406Sopenharmony_ci     * [1] = index of line: increased after line is complete
573141cc406Sopenharmony_ci     * [0] = color index: increased first since SANE requires full color pixels */
574141cc406Sopenharmony_ci    if (buffer->read_index[3] == 0 && buffer->packet_size_bytes == 2) {
575141cc406Sopenharmony_ci        buffer->read_index[3] = 1;
576141cc406Sopenharmony_ci    } else {
577141cc406Sopenharmony_ci        buffer->read_index[3] = 0;
578141cc406Sopenharmony_ci        buffer->read_index[0]++;
579141cc406Sopenharmony_ci        if (buffer->read_index[0] == buffer->colors) {
580141cc406Sopenharmony_ci            buffer->read_index[0] = 0;
581141cc406Sopenharmony_ci            buffer->read_index[2] += increment;
582141cc406Sopenharmony_ci            if (buffer->read_index[2] >= buffer->width) {
583141cc406Sopenharmony_ci                buffer->read_index[2] = 0;
584141cc406Sopenharmony_ci                buffer->read_index[1]++;
585141cc406Sopenharmony_ci            }
586141cc406Sopenharmony_ci        }
587141cc406Sopenharmony_ci    }
588141cc406Sopenharmony_ci}
589141cc406Sopenharmony_ci
590141cc406Sopenharmony_ci#if 0
591141cc406Sopenharmony_ci/**
592141cc406Sopenharmony_ci * Display the buffer state.
593141cc406Sopenharmony_ci *
594141cc406Sopenharmony_ci * @param buffer the buffer to initialize
595141cc406Sopenharmony_ci */
596141cc406Sopenharmony_ci
597141cc406Sopenharmony_cistatic void buffer_output_state(struct Pieusb_Read_Buffer* buffer)
598141cc406Sopenharmony_ci{
599141cc406Sopenharmony_ci    SANE_Int line_size;
600141cc406Sopenharmony_ci    SANE_Int N, k, loc[4];
601141cc406Sopenharmony_ci
602141cc406Sopenharmony_ci    line_size = buffer->line_size_bytes * buffer->colors; /* Full line size in bytes */
603141cc406Sopenharmony_ci
604141cc406Sopenharmony_ci    DBG(DBG_info_buffer, "Buffer data\n");
605141cc406Sopenharmony_ci    DBG(DBG_info_buffer,"  width/height/colors/depth = %d %d %d %d (buffer size %d)\n",
606141cc406Sopenharmony_ci        buffer->width, buffer->height, buffer->colors, buffer->depth, buffer->image_size_bytes);
607141cc406Sopenharmony_ci
608141cc406Sopenharmony_ci    /* Summary */
609141cc406Sopenharmony_ci    N = buffer->width * buffer->height;
610141cc406Sopenharmony_ci    for (k = 0; k < buffer->colors; k++) {
611141cc406Sopenharmony_ci        loc[k] = buffer->p_read[k] - buffer->data - k*N;
612141cc406Sopenharmony_ci    }
613141cc406Sopenharmony_ci    for (k = buffer->colors; k < 4; k++) {
614141cc406Sopenharmony_ci        loc[k] = 0;
615141cc406Sopenharmony_ci    }
616141cc406Sopenharmony_ci    DBG(DBG_info_buffer, "  reading at: lines = %d:%d:%d:%d\n", loc[0], loc[1], loc[2], loc[3]);
617141cc406Sopenharmony_ci    for (k = 0; k < buffer->colors; k++) {
618141cc406Sopenharmony_ci        loc[k] = buffer->p_write[k] - buffer->data - k*N;
619141cc406Sopenharmony_ci    }
620141cc406Sopenharmony_ci    for (k = buffer->colors; k < 4; k++) {
621141cc406Sopenharmony_ci        loc[k] = 0;
622141cc406Sopenharmony_ci    }
623141cc406Sopenharmony_ci    DBG(DBG_info_buffer, "  writing at: lines = %d:%d:%d:%d\n", loc[0], loc[1], loc[2], loc[3]);
624141cc406Sopenharmony_ci
625141cc406Sopenharmony_ci    /* Progress */
626141cc406Sopenharmony_ci    double fdata = (double)buffer->bytes_unread/buffer->image_size_bytes*100;
627141cc406Sopenharmony_ci    double fread = (double)buffer->bytes_read/buffer->image_size_bytes*100;
628141cc406Sopenharmony_ci    double fwritten = (double)buffer->bytes_written/buffer->image_size_bytes*100;
629141cc406Sopenharmony_ci    DBG(DBG_info_buffer, "  byte counts: image = %d, data = %d (%.0f%%), read = %d (%.0f%%), written = %d (%.0f%%)\n",
630141cc406Sopenharmony_ci        buffer->image_size_bytes, buffer->bytes_unread, fdata, buffer->bytes_read, fread, buffer->bytes_written, fwritten);
631141cc406Sopenharmony_ci    DBG(DBG_info_buffer, "  line counts: image = %.1f, data = %.1f, read = %.1f, written = %.1f\n",
632141cc406Sopenharmony_ci        (double)buffer->image_size_bytes/line_size, (double)buffer->bytes_unread/line_size, (double)buffer->bytes_read/line_size, (double)buffer->bytes_written/line_size);
633141cc406Sopenharmony_ci
634141cc406Sopenharmony_ci}
635141cc406Sopenharmony_ci#endif
636