kopia lustrzana https://gitlab.com/sane-project/backends
brother_mfp: Added brother2 decoder + button support
Also: - Fixed major issue in gray decoder that caused data loss. - Button support added for Brother2 decoder.brother_mfp_backend
rodzic
437bfd27f3
commit
0055214614
|
@ -50,6 +50,7 @@
|
|||
*/
|
||||
#define BROTHER_USB_REQ_STARTSESSION 1
|
||||
#define BROTHER_USB_REQ_STOPSESSION 2
|
||||
#define BROTHER_USB_REQ_BUTTONSTATE 3
|
||||
|
||||
#define BROTHER_READ_BUFFER_LEN (16 * 1024)
|
||||
|
||||
|
@ -804,65 +805,68 @@ SANE_Status BrotherUSBDriver::StartScan ()
|
|||
*/
|
||||
packet_len = 0;
|
||||
res = encoder->EncodeADFBlock (small_buffer, sizeof(small_buffer), &packet_len);
|
||||
if (res != SANE_STATUS_GOOD)
|
||||
if (res != SANE_STATUS_UNSUPPORTED)
|
||||
{
|
||||
DBG (DBG_SERIOUS,
|
||||
"BrotherUSBDriver::StartScan: failed to generate ADF block: %d\n",
|
||||
res);
|
||||
(void) CancelScan ();
|
||||
return SANE_STATUS_INVAL;
|
||||
}
|
||||
if (res != SANE_STATUS_GOOD)
|
||||
{
|
||||
DBG (DBG_SERIOUS,
|
||||
"BrotherUSBDriver::StartScan: failed to generate ADF block: %d\n",
|
||||
res);
|
||||
(void) CancelScan ();
|
||||
return SANE_STATUS_INVAL;
|
||||
}
|
||||
|
||||
buf_size = packet_len;
|
||||
res = sanei_usb_write_bulk (fd, small_buffer, &buf_size);
|
||||
if (res != SANE_STATUS_GOOD)
|
||||
{
|
||||
DBG (DBG_SERIOUS, "BrotherUSBDriver::StartScan: failed to send ADF block: %d\n", res);
|
||||
(void)CancelScan();
|
||||
return res;
|
||||
}
|
||||
buf_size = packet_len;
|
||||
res = sanei_usb_write_bulk (fd, small_buffer, &buf_size);
|
||||
if (res != SANE_STATUS_GOOD)
|
||||
{
|
||||
DBG (DBG_SERIOUS, "BrotherUSBDriver::StartScan: failed to send ADF block: %d\n", res);
|
||||
(void)CancelScan();
|
||||
return res;
|
||||
}
|
||||
|
||||
if (buf_size != packet_len)
|
||||
{
|
||||
DBG (DBG_SERIOUS, "BrotherUSBDriver::StartScan: failed to write ADF block\n");
|
||||
(void)CancelScan();
|
||||
return SANE_STATUS_IO_ERROR;
|
||||
}
|
||||
if (buf_size != packet_len)
|
||||
{
|
||||
DBG (DBG_SERIOUS, "BrotherUSBDriver::StartScan: failed to write ADF block\n");
|
||||
(void)CancelScan();
|
||||
return SANE_STATUS_IO_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to read the response.
|
||||
*
|
||||
*/
|
||||
buf_size = sizeof(small_buffer);
|
||||
timeout = TIMEOUT_SECS(8);
|
||||
/*
|
||||
* Try to read the response.
|
||||
*
|
||||
*/
|
||||
buf_size = sizeof(small_buffer);
|
||||
timeout = TIMEOUT_SECS(8);
|
||||
|
||||
res = PollForRead(small_buffer, &buf_size, &timeout);
|
||||
if (res != SANE_STATUS_GOOD)
|
||||
{
|
||||
DBG (DBG_SERIOUS, "BrotherUSBDriver::StartScan: failed to read ADF block response: %d\n", res);
|
||||
(void)CancelScan();
|
||||
return res;
|
||||
}
|
||||
res = PollForRead(small_buffer, &buf_size, &timeout);
|
||||
if (res != SANE_STATUS_GOOD)
|
||||
{
|
||||
DBG (DBG_SERIOUS, "BrotherUSBDriver::StartScan: failed to read ADF block response: %d\n", res);
|
||||
(void)CancelScan();
|
||||
return res;
|
||||
}
|
||||
|
||||
BrotherADFResponse adf_resp;
|
||||
BrotherADFResponse adf_resp;
|
||||
|
||||
res = encoder->DecodeADFBlockResp (small_buffer, buf_size, adf_resp);
|
||||
if (res != SANE_STATUS_GOOD)
|
||||
{
|
||||
DBG (DBG_SERIOUS,
|
||||
"BrotherUSBDriver::StartScan: ADF block response block invalid: %d\n",
|
||||
res);
|
||||
(void) CancelScan ();
|
||||
return res;
|
||||
}
|
||||
res = encoder->DecodeADFBlockResp (small_buffer, buf_size, adf_resp);
|
||||
if (res != SANE_STATUS_GOOD)
|
||||
{
|
||||
DBG (DBG_SERIOUS,
|
||||
"BrotherUSBDriver::StartScan: ADF block response block invalid: %d\n",
|
||||
res);
|
||||
(void) CancelScan ();
|
||||
return res;
|
||||
}
|
||||
|
||||
if (adf_resp.resp_code != 0xc2)
|
||||
{
|
||||
DBG (DBG_SERIOUS,
|
||||
"BrotherUSBDriver::StartScan: ADF block response invalid: %u\n",
|
||||
(unsigned int)adf_resp.resp_code);
|
||||
(void) CancelScan ();
|
||||
return res;
|
||||
if (adf_resp.resp_code != 0xc2)
|
||||
{
|
||||
DBG (DBG_SERIOUS,
|
||||
"BrotherUSBDriver::StartScan: ADF block response invalid: %u\n",
|
||||
(unsigned int)adf_resp.resp_code);
|
||||
(void) CancelScan ();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -922,6 +926,102 @@ SANE_Status BrotherUSBDriver::StartScan ()
|
|||
return SANE_STATUS_GOOD;
|
||||
}
|
||||
|
||||
SANE_Status BrotherUSBDriver::CheckSensor (BrotherSensor &status)
|
||||
{
|
||||
SANE_Status res = sanei_usb_control_msg (fd, USB_DIR_IN | USB_TYPE_VENDOR,
|
||||
BROTHER_USB_REQ_BUTTONSTATE,
|
||||
0, 0, 255, small_buffer);
|
||||
|
||||
if (res != SANE_STATUS_GOOD)
|
||||
{
|
||||
DBG (DBG_WARN,
|
||||
"BrotherUSBDriver::CheckSensor: failed to send check sensor sequence: %s.\n",
|
||||
devicename);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode the response.
|
||||
*
|
||||
* TODO: How do we detect a short response?
|
||||
*
|
||||
*/
|
||||
BrotherButtonQueryResponse resp;
|
||||
|
||||
res = encoder->DecodeButtonQueryResp (small_buffer, 4, resp);
|
||||
if (res != SANE_STATUS_GOOD)
|
||||
{
|
||||
DBG (DBG_WARN,
|
||||
"BrotherUSBDriver::CheckSensor: button state response is invalid.\n");
|
||||
return SANE_STATUS_IO_ERROR;
|
||||
}
|
||||
|
||||
if (resp.has_button_press == BROTHER_SENSOR_NONE)
|
||||
{
|
||||
status = BROTHER_SENSOR_NONE;
|
||||
return SANE_STATUS_GOOD;
|
||||
}
|
||||
|
||||
DBG (DBG_DETAIL,
|
||||
"BrotherUSBDriver::CheckSensor: scanner indicates a button was pushed.\n");
|
||||
|
||||
/*
|
||||
* Now we query the button state.
|
||||
* The request is the same apparently but we get a different response.
|
||||
* Not really sure I understand because it seems stateful this but it is what we observe.
|
||||
*
|
||||
*/
|
||||
res = sanei_usb_control_msg (fd, USB_DIR_IN | USB_TYPE_VENDOR,
|
||||
BROTHER_USB_REQ_BUTTONSTATE,
|
||||
0, 0, 255, small_buffer);
|
||||
|
||||
if (res != SANE_STATUS_GOOD)
|
||||
{
|
||||
DBG (DBG_WARN,
|
||||
"BrotherUSBDriver::CheckSensor: failed to send check sensor sequence: %s.\n",
|
||||
devicename);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* And the second response is a bit longer and button specific.
|
||||
*
|
||||
*/
|
||||
BrotherButtonStateResponse state_resp;
|
||||
|
||||
res = encoder->DecodeButtonStateResp (small_buffer, 9, state_resp);
|
||||
if (res != SANE_STATUS_GOOD)
|
||||
{
|
||||
DBG (DBG_WARN, "BrotherUSBDriver::CheckSensor: button info response is invalid.\n");
|
||||
return SANE_STATUS_IO_ERROR;
|
||||
}
|
||||
|
||||
switch (state_resp.button_value)
|
||||
{
|
||||
case 0x05:
|
||||
status = BROTHER_SENSOR_FILE;
|
||||
break;
|
||||
|
||||
case 0x02:
|
||||
status = BROTHER_SENSOR_OCR;
|
||||
break;
|
||||
|
||||
case 0x03:
|
||||
status = BROTHER_SENSOR_IMAGE;
|
||||
break;
|
||||
|
||||
case 0x08:
|
||||
status = BROTHER_SENSOR_EMAIL;
|
||||
break;
|
||||
|
||||
default:
|
||||
DBG (DBG_WARN, "BrotherUSBDriver::CheckSensor: unknown button code: %d.\n", state_resp.button_value);
|
||||
break;
|
||||
}
|
||||
|
||||
return SANE_STATUS_GOOD;
|
||||
}
|
||||
|
||||
BrotherDriver::BrotherDriver (BrotherFamily family) :
|
||||
family (family),
|
||||
encoder(nullptr)
|
||||
|
@ -932,8 +1032,11 @@ BrotherDriver::BrotherDriver (BrotherFamily family) :
|
|||
encoder = new BrotherEncoderFamily4();
|
||||
break;
|
||||
|
||||
case BROTHER_FAMILY_2:
|
||||
encoder = new BrotherEncoderFamily2();
|
||||
break;
|
||||
|
||||
// case BROTHER_FAMILY_1:
|
||||
// case BROTHER_FAMILY_2:
|
||||
// case BROTHER_FAMILY_3:
|
||||
// case BROTHER_FAMILY_5:
|
||||
case BROTHER_FAMILY_NONE:
|
||||
|
|
|
@ -96,6 +96,8 @@ public:
|
|||
|
||||
virtual SANE_Status CancelScan () = 0;
|
||||
|
||||
virtual SANE_Status CheckSensor(BrotherSensor &status) = 0;
|
||||
|
||||
virtual SANE_Status ReadScanData (SANE_Byte *data, size_t data_len,
|
||||
size_t *bytes_read) = 0;
|
||||
|
||||
|
@ -142,7 +144,6 @@ protected:
|
|||
BrotherEncoder *encoder;
|
||||
};
|
||||
|
||||
|
||||
class BrotherUSBDriver : public BrotherDriver
|
||||
{
|
||||
public:
|
||||
|
@ -158,6 +159,8 @@ public:
|
|||
|
||||
SANE_Status CancelScan () override;
|
||||
|
||||
SANE_Status CheckSensor(BrotherSensor &status) override;
|
||||
|
||||
SANE_Status ReadScanData (SANE_Byte *data, size_t data_len,
|
||||
size_t *bytes_read) override;
|
||||
|
||||
|
|
|
@ -49,10 +49,14 @@
|
|||
*
|
||||
*/
|
||||
#define BROTHER_DATA_BLOCK_NO_DATA 0x80
|
||||
#define BROTHER_DATA_BLOCK_MORE_FRAMES 0x81
|
||||
#define BROTHER_DATA_BLOCK_END_OF_FRAME 0x82
|
||||
#define BROTHER_DATA_BLOCK_JPEG 0x64
|
||||
#define BROTHER_DATA_BLOCK_GRAY_RLENGTH 0x42
|
||||
#define BROTHER_DATA_BLOCK_GRAY_RAW 0x40
|
||||
#define BROTHER_DATA_BLOCK_GRAY_RLENGTH 0x42
|
||||
#define BROTHER_DATA_BLOCK_RED_RAW 0x44
|
||||
#define BROTHER_DATA_BLOCK_GREEN_RAW 0x48
|
||||
#define BROTHER_DATA_BLOCK_BLUE_RAW 0x4c
|
||||
|
||||
const char* BrotherEncoder::ScanModeToText (BrotherScanMode scan_mode)
|
||||
{
|
||||
|
@ -133,6 +137,441 @@ BrotherEncoder::SetScanDimensions (SANE_Int pixel_x_offset,
|
|||
return SANE_STATUS_GOOD;
|
||||
}
|
||||
|
||||
/*
|
||||
* --------------------------------------------
|
||||
* Family 2 Encoding
|
||||
* --------------------------------------------
|
||||
*
|
||||
*/
|
||||
SANE_Status BrotherEncoderFamily2::DecodeSessionResp (const SANE_Byte *data, size_t data_len,
|
||||
BrotherSessionResponse &response)
|
||||
{
|
||||
/*
|
||||
* Formatting and content checks.
|
||||
*
|
||||
*
|
||||
*/
|
||||
if ((data_len != 5)
|
||||
|| ((memcmp (data, "\x05" "\x10" "\x01" "\x02", 4) != 0)
|
||||
&& (memcmp (data, "\x05" "\x10" "\x02" "\x02", 4) != 0)))
|
||||
|
||||
{
|
||||
DBG (DBG_SERIOUS,
|
||||
"BrotherEncoderFamily2::DecodeSessionResp: invalid session response block: len = %zu\n",
|
||||
data_len);
|
||||
|
||||
return SANE_STATUS_IO_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Check the valid status values are 0x00 or 0x20
|
||||
*
|
||||
*/
|
||||
if ((data[4] != 0x00) && (data[4] != 0x80))
|
||||
|
||||
{
|
||||
DBG (DBG_SERIOUS,
|
||||
"BrotherEncoderFamily2::DecodeSessionResp: invalid session response: data[4]=%u\n",
|
||||
(unsigned int) data[4]);
|
||||
|
||||
return SANE_STATUS_IO_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: figure out what the rest of the packet is supposed to be.
|
||||
*
|
||||
*/
|
||||
response.ready = data[4] == 0x00? SANE_TRUE: SANE_FALSE;
|
||||
|
||||
return SANE_STATUS_GOOD;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE:
|
||||
* Supply a buffer that is at least 1 char longer than is necessary
|
||||
* as snprintf() will require space for a terminating NUL.
|
||||
*
|
||||
*/
|
||||
SANE_Status BrotherEncoderFamily2::EncodeBasicParameterBlock (SANE_Byte *data, size_t data_len,
|
||||
size_t *length)
|
||||
{
|
||||
const char *mode_text = ScanModeToText (scan_params.param_scan_mode);
|
||||
if (nullptr == mode_text)
|
||||
{
|
||||
DBG (DBG_SERIOUS,
|
||||
"BrotherEncoderFamily2::EncodeBasicParameterBlock: failed to get scan mode text for mode %d\n",
|
||||
scan_params.param_scan_mode);
|
||||
return SANE_STATUS_INVAL;
|
||||
}
|
||||
|
||||
*length = snprintf ((char*) data,
|
||||
data_len,
|
||||
"\x1b" "I\nR=%u,%u\nM=%s\n" "\x80",
|
||||
(unsigned int) scan_params.param_x_res,
|
||||
(unsigned int) scan_params.param_y_res,
|
||||
mode_text);
|
||||
|
||||
if (*length > data_len)
|
||||
{
|
||||
DBG (DBG_SERIOUS,
|
||||
"BrotherEncoderFamily2::EncodeBasicParameterBlock: parameter block too long for buffer: %zu\n",
|
||||
*length);
|
||||
return SANE_STATUS_INVAL;
|
||||
}
|
||||
|
||||
return SANE_STATUS_GOOD;
|
||||
}
|
||||
|
||||
|
||||
SANE_Status BrotherEncoderFamily2::DecodeBasicParameterBlockResp (const SANE_Byte *data, size_t data_len,
|
||||
BrotherBasicParamResponse &response)
|
||||
{
|
||||
/*
|
||||
* TODO: Decode this block.
|
||||
* Might contain some useful stuff, like limits for selected parameters.
|
||||
*
|
||||
*/
|
||||
(void)data;
|
||||
(void)data_len;
|
||||
(void)response;
|
||||
|
||||
return SANE_STATUS_GOOD;
|
||||
}
|
||||
|
||||
|
||||
|
||||
SANE_Status BrotherEncoderFamily2::EncodeParameterBlock (SANE_Byte *data, size_t data_len,
|
||||
size_t *length)
|
||||
{
|
||||
const char *mode_text = ScanModeToText (scan_params.param_scan_mode);
|
||||
if (nullptr == mode_text)
|
||||
{
|
||||
DBG (DBG_SERIOUS,
|
||||
"BrotherEncoderFamily2::EncodeBasicParameterBlock: failed to get scan mode text for mode %d\n",
|
||||
scan_params.param_scan_mode);
|
||||
return SANE_STATUS_INVAL;
|
||||
}
|
||||
|
||||
*length = snprintf ((char*) data,
|
||||
data_len,
|
||||
"\x1b" "X\nR=%u,%u\nM=%s\n"
|
||||
"%s\n"
|
||||
"B=%u\nN=%u\n"
|
||||
"A=%u,%u,%u,%u\n"
|
||||
"D=SIN\n"
|
||||
"\x80",
|
||||
(unsigned int) scan_params.param_x_res,
|
||||
(unsigned int) scan_params.param_y_res,
|
||||
mode_text,
|
||||
(scan_params.param_scan_mode == BROTHER_SCAN_MODE_COLOR) ?
|
||||
"C=JPEG\nJ=MID" : "C=RLENGTH",
|
||||
(unsigned int) (scan_params.param_brightness + 50),
|
||||
(unsigned int) (scan_params.param_contrast + 50),
|
||||
(unsigned int) (scan_params.param_pixel_x_offset),
|
||||
(unsigned int) (scan_params.param_pixel_y_offset),
|
||||
(unsigned int) (scan_params.param_pixel_x_offset
|
||||
+ scan_params.param_pixel_x_width),
|
||||
(unsigned int) (scan_params.param_pixel_y_offset
|
||||
+ scan_params.param_pixel_y_height));
|
||||
|
||||
if (*length > data_len)
|
||||
{
|
||||
DBG (DBG_SERIOUS,
|
||||
"BrotherEncoderFamily2::EncodeBasicParameterBlock: parameter block too long for buffer: %zu\n",
|
||||
*length);
|
||||
return SANE_STATUS_INVAL;
|
||||
}
|
||||
|
||||
return SANE_STATUS_GOOD;
|
||||
}
|
||||
|
||||
|
||||
SANE_Status BrotherEncoderFamily2::DecodeScanData (const SANE_Byte *src_data, size_t src_data_len,
|
||||
size_t *src_data_consumed, SANE_Byte *dest_data,
|
||||
size_t dest_data_len, size_t *dest_data_written)
|
||||
{
|
||||
DBG (DBG_EVENT, "BrotherEncoderFamily2::DecodeScanData: \n");
|
||||
SANE_Status ret_status = SANE_STATUS_GOOD;
|
||||
|
||||
*src_data_consumed = 0;
|
||||
*dest_data_written = 0;
|
||||
|
||||
/*
|
||||
* We will use this to compute the bytes consumed at the end.
|
||||
*
|
||||
*/
|
||||
size_t orig_src_data_len = src_data_len;
|
||||
size_t orig_dest_data_len = dest_data_len;
|
||||
|
||||
/*
|
||||
* Now take note here that we do not *necessarily* need src_data to be able to decode.
|
||||
* We might have terminated the previous call not because we ran out of input, but because
|
||||
* we ran out of output space. Some blocks are just repetitions that are unrolled and require
|
||||
* no additional src data. So we enter this loop *even if there is nothing in the src_data buffer*.
|
||||
*
|
||||
* All of the functions that we are about to call must be able to deal with src_data_len == 0
|
||||
* intelligently.
|
||||
*
|
||||
*/
|
||||
for (;;)
|
||||
{
|
||||
/*
|
||||
* If we are not in a block, then decode the next block.
|
||||
*
|
||||
* We might not have enough bytes to fully decode the header.
|
||||
* In which case, we wait for some more data.
|
||||
*
|
||||
*/
|
||||
bool new_block = false;
|
||||
|
||||
if (current_header.block_type == 0)
|
||||
{
|
||||
size_t header_consumed = 0;
|
||||
DecodeStatus res = DecodeScanDataHeader(src_data, src_data_len, &header_consumed, current_header);
|
||||
if (res == DECODE_STATUS_TRUNCATED)
|
||||
{
|
||||
/*
|
||||
* This means we don't have enough data to decode a header yet.
|
||||
* Try again next time if we have read more data.
|
||||
*
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect special case situations.
|
||||
*
|
||||
* TODO: We need to be able to alert the difference between these
|
||||
* two things to the caller somehow. They are not the same!!
|
||||
*
|
||||
*/
|
||||
if (res == DECODE_STATUS_ENDOFDATA)
|
||||
{
|
||||
DBG (DBG_IMPORTANT, "BrotherEncoderFamily2::DecodeScanData: end of data detected\n");
|
||||
current_header.block_type = 0;
|
||||
|
||||
/*
|
||||
* If we have data in hand, then we will send this up first.
|
||||
* Otherwise, send SANE_STATUS_EOF to trigger stop of scan.
|
||||
*
|
||||
* We don't consume this header so we will see it the next time around.
|
||||
*
|
||||
*/
|
||||
ret_status = orig_dest_data_len > dest_data_len? SANE_STATUS_GOOD: SANE_STATUS_EOF;
|
||||
break;
|
||||
}
|
||||
if (res == DECODE_STATUS_ENDOFFRAME)
|
||||
{
|
||||
DBG (DBG_IMPORTANT, "BrotherEncoderFamily2::DecodeScanData: end of frame detected\n");
|
||||
current_header.block_type = 0;
|
||||
ret_status = orig_dest_data_len > dest_data_len? SANE_STATUS_GOOD: SANE_STATUS_EOF;
|
||||
break;
|
||||
}
|
||||
if (res != DECODE_STATUS_GOOD)
|
||||
{
|
||||
DBG (DBG_IMPORTANT, "BrotherEncoderFamily2::DecodeScanData: failed to decode header\n");
|
||||
current_header.block_type = 0;
|
||||
ret_status = SANE_STATUS_IO_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
DBG (DBG_IMPORTANT,
|
||||
"BrotherEncoderFamily2::DecodeScanData: decoded new block header: 0x%2.2x, length=%zu\n",
|
||||
current_header.block_type,
|
||||
current_header.block_len);
|
||||
|
||||
src_data += header_consumed;
|
||||
src_data_len -= header_consumed;
|
||||
|
||||
new_block = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to decode the data block.
|
||||
* We will exit the function if we can only partially
|
||||
* decode it, hoping to do more next time.
|
||||
*
|
||||
*/
|
||||
DecodeStatus res;
|
||||
size_t bytes_consumed = 0;
|
||||
size_t bytes_written = 0;
|
||||
size_t in_len = MIN(src_data_len, current_header.block_len);
|
||||
|
||||
if (current_header.block_type == BROTHER_DATA_BLOCK_JPEG)
|
||||
{
|
||||
if (new_block)
|
||||
{
|
||||
jfif_decoder.NewBlock ();
|
||||
}
|
||||
|
||||
res = jfif_decoder.DecodeScanData (src_data,
|
||||
in_len,
|
||||
&bytes_consumed,
|
||||
dest_data,
|
||||
dest_data_len,
|
||||
&bytes_written);
|
||||
}
|
||||
else if (current_header.block_type == BROTHER_DATA_BLOCK_GRAY_RLENGTH)
|
||||
{
|
||||
if (new_block)
|
||||
{
|
||||
gray_decoder.NewBlock ();
|
||||
}
|
||||
|
||||
res = gray_decoder.DecodeScanData (src_data,
|
||||
in_len,
|
||||
&bytes_consumed,
|
||||
dest_data,
|
||||
dest_data_len,
|
||||
&bytes_written);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG (DBG_IMPORTANT,
|
||||
"BrotherEncoderFamily2::DecodeScanData: unknown block encountered: 0x%2.2x\n",
|
||||
(unsigned int) current_header.block_type);
|
||||
ret_status = SANE_STATUS_IO_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
DBG (DBG_DETAIL,
|
||||
"BrotherEncoderFamily2::DecodeScanData: written=%zu\n",
|
||||
bytes_written);
|
||||
if (res != DECODE_STATUS_GOOD)
|
||||
{
|
||||
current_header.block_type = 0;
|
||||
ret_status = SANE_STATUS_IO_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
src_data += bytes_consumed;
|
||||
src_data_len -= bytes_consumed;
|
||||
current_header.block_len -= bytes_consumed;
|
||||
|
||||
DBG (DBG_IMPORTANT,
|
||||
"BrotherEncoderFamily2::DecodeScanData: current block bytes remaining after decode=%zu\n",
|
||||
current_header.block_len);
|
||||
|
||||
dest_data += bytes_written;
|
||||
dest_data_len -= bytes_written;
|
||||
|
||||
/*
|
||||
* Perhaps we have completed the block.
|
||||
*
|
||||
*/
|
||||
if (current_header.block_len == 0)
|
||||
{
|
||||
current_header.block_type = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deal with special situation whereby we have input
|
||||
* and output buffer space, but we couldn't decode anything.
|
||||
* It might be that there is data in the input buffer
|
||||
* but we need more to proceed.
|
||||
*
|
||||
* If we haven't written anything, we cannot have made any progress.
|
||||
*
|
||||
*/
|
||||
if (bytes_written == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret_status == SANE_STATUS_GOOD)
|
||||
{
|
||||
*src_data_consumed = orig_src_data_len - src_data_len;
|
||||
*dest_data_written = orig_dest_data_len - dest_data_len;
|
||||
}
|
||||
|
||||
return ret_status;
|
||||
}
|
||||
|
||||
/*
|
||||
* EndOfFrame (0x82) is 10 bytes long because it doesn't have the
|
||||
* 2-byte packet length that the others do, so we could assume that
|
||||
* the packet length is not part of the header really.
|
||||
* However, it is convenient in coding terms to assume that it does.
|
||||
*
|
||||
* 0x80 - single byte end of data.
|
||||
* 0x82 - end of frame, with 10 bytes
|
||||
* Others - 12 bytes, including little endian length at the end.
|
||||
*
|
||||
*/
|
||||
DecodeStatus BrotherEncoderFamily2::DecodeScanDataHeader (const SANE_Byte *src_data,
|
||||
size_t src_data_len,
|
||||
size_t *src_data_consumed,
|
||||
ScanDataHeader &header)
|
||||
{
|
||||
if (src_data_len == 0)
|
||||
{
|
||||
return DECODE_STATUS_TRUNCATED;
|
||||
}
|
||||
|
||||
header.block_type = src_data[0];
|
||||
|
||||
header.block_len = 0;
|
||||
|
||||
if (header.block_type == BROTHER_DATA_BLOCK_NO_DATA)
|
||||
{
|
||||
*src_data_consumed = 1;
|
||||
return DECODE_STATUS_ENDOFDATA;
|
||||
}
|
||||
|
||||
if (header.block_type == BROTHER_DATA_BLOCK_END_OF_FRAME)
|
||||
{
|
||||
if (src_data_len < 10)
|
||||
{
|
||||
return DECODE_STATUS_TRUNCATED;
|
||||
}
|
||||
|
||||
*src_data_consumed = 10;
|
||||
return DECODE_STATUS_ENDOFFRAME;
|
||||
}
|
||||
|
||||
/*
|
||||
* Other data-carrying packets.
|
||||
*
|
||||
*/
|
||||
if (src_data_len < 3)
|
||||
{
|
||||
return DECODE_STATUS_TRUNCATED;
|
||||
}
|
||||
|
||||
*src_data_consumed = 3;
|
||||
|
||||
header.block_len = src_data[1] + (src_data[2] << 8);
|
||||
|
||||
return DECODE_STATUS_GOOD;
|
||||
}
|
||||
|
||||
|
||||
SANE_Status BrotherEncoderFamily2::DecodeButtonQueryResp (const SANE_Byte *data, size_t data_len,
|
||||
BrotherButtonQueryResponse &response)
|
||||
{
|
||||
if ((data_len != 4) || (data[0] != 0x04) ||(data[1] != 0x10) || (data[2] != 0x03))
|
||||
{
|
||||
return SANE_STATUS_IO_ERROR;
|
||||
}
|
||||
|
||||
response.has_button_press = data[3] == 0x10;
|
||||
|
||||
return SANE_STATUS_GOOD;
|
||||
}
|
||||
|
||||
SANE_Status BrotherEncoderFamily2::DecodeButtonStateResp (const SANE_Byte *data, size_t data_len,
|
||||
BrotherButtonStateResponse &response)
|
||||
{
|
||||
if ((data_len != 9) || (memcmp(data, "\x09" "\x10" "\x03" "\x20", 4) != 0))
|
||||
{
|
||||
return SANE_STATUS_IO_ERROR;
|
||||
}
|
||||
response.button_value = data[4];
|
||||
|
||||
return SANE_STATUS_GOOD;
|
||||
}
|
||||
|
||||
/*
|
||||
* --------------------------------------------
|
||||
* Family 4 Encoding
|
||||
|
@ -579,15 +1018,38 @@ DecodeStatus BrotherEncoderFamily4::DecodeScanDataHeader (const SANE_Byte *src_d
|
|||
return DECODE_STATUS_GOOD;
|
||||
}
|
||||
|
||||
void BrotherGrayRLengthDecoder::NewBlock ()
|
||||
SANE_Status BrotherEncoderFamily4::DecodeButtonQueryResp (const SANE_Byte *data, size_t data_len,
|
||||
BrotherButtonQueryResponse &response)
|
||||
{
|
||||
decode_state = BROTHER_DECODE_RLEN_INIT;
|
||||
decode_expand_char = 0;
|
||||
block_bytes_left = 0;
|
||||
if ((data_len != 4) || (data[0] != 0x04) ||(data[1] != 0x10) || (data[2] != 0x03))
|
||||
{
|
||||
return SANE_STATUS_IO_ERROR;
|
||||
}
|
||||
|
||||
response.has_button_press = data[3] == 0x10;
|
||||
|
||||
return SANE_STATUS_GOOD;
|
||||
}
|
||||
|
||||
void BrotherGrayRLengthDecoder::NewPage ()
|
||||
SANE_Status BrotherEncoderFamily4::DecodeButtonStateResp (const SANE_Byte *data, size_t data_len,
|
||||
BrotherButtonStateResponse &response)
|
||||
{
|
||||
if ((data_len != 9) || (memcmp(data, "\x09" "\x10" "\x03" "\x20", 4) != 0))
|
||||
{
|
||||
return SANE_STATUS_IO_ERROR;
|
||||
}
|
||||
response.button_value = data[4];
|
||||
|
||||
return SANE_STATUS_GOOD;
|
||||
}
|
||||
|
||||
void BrotherGrayRLengthDecoder::NewBlock ()
|
||||
{
|
||||
}
|
||||
|
||||
void BrotherGrayRLengthDecoder::NewPage(const BrotherParameters ¶ms)
|
||||
{
|
||||
decode_params = params;
|
||||
NewBlock();
|
||||
}
|
||||
|
||||
|
@ -620,106 +1082,102 @@ DecodeStatus BrotherGrayRLengthDecoder::DecodeScanData (const SANE_Byte *in_buff
|
|||
do
|
||||
{
|
||||
/*
|
||||
* Check the current state to see what we should do.
|
||||
* We need src to be able to decode.
|
||||
*
|
||||
*/
|
||||
if (decode_state == BROTHER_DECODE_RLEN_INIT)
|
||||
if (in_buffer_len == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* INIT state: we are ready to start to process another sub-block.
|
||||
*
|
||||
* If there is data then we should see a block length.
|
||||
*
|
||||
* If byte & 0x80 then it is a compressed byte block.
|
||||
* Else it is a sequence of uncompressed bytes.
|
||||
*
|
||||
*/
|
||||
size_t consumed_input = 0;
|
||||
size_t written_output = 0;
|
||||
|
||||
if (in_buffer[0] & 0x80)
|
||||
{
|
||||
/*
|
||||
* We need src to be able to decode.
|
||||
* To process the expansion miniblock, we have to see the next byte.
|
||||
* We might not have it yet. We will just have to wait for more bytes to come in.
|
||||
*
|
||||
*/
|
||||
if (in_buffer_len == 0)
|
||||
if (in_buffer_len < 2)
|
||||
{
|
||||
DBG (DBG_IMPORTANT, "Brother_decode_gray_rlength: in_buffer_len < 2: can't decode yet.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* INIT state: we are ready to start to process another sub-block.
|
||||
*
|
||||
* If there is data then we should see a block length.
|
||||
*
|
||||
* If byte & 0x80 then it is a compressed byte block.
|
||||
* Else it is a sequence of uncompressed bytes.
|
||||
* Check that we have enough output space for the expanded mini block.
|
||||
*
|
||||
*/
|
||||
if (in_buffer[0] & 0x80)
|
||||
size_t output_req = 0xff - in_buffer[0] + 2;
|
||||
if (output_req > out_buffer_len)
|
||||
{
|
||||
/*
|
||||
* To move into the BROTHER_DECODE_RLEN_IN_EXPAND, we have to see the next byte.
|
||||
* We might not have it yet. We will just have to wait for more bytes to come in.
|
||||
*
|
||||
*/
|
||||
if (in_buffer_len < 2)
|
||||
{
|
||||
DBG (DBG_IMPORTANT, "Brother_decode_gray_rlength: in_buffer_len < 2\n");
|
||||
break;
|
||||
}
|
||||
|
||||
decode_state = BROTHER_DECODE_RLEN_IN_EXPAND;
|
||||
block_bytes_left = 0xff - in_buffer[0] + 2;
|
||||
decode_expand_char = in_buffer[1];
|
||||
|
||||
in_buffer += 2;
|
||||
in_buffer_len -= 2;
|
||||
|
||||
DBG (DBG_IMPORTANT,
|
||||
"Brother_decode_gray_rlength: expand block: %zu bytes of 0x%2.2x\n",
|
||||
block_bytes_left,
|
||||
(int) decode_expand_char);
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
|
||||
SANE_Byte decode_expand_char = in_buffer[1];
|
||||
|
||||
in_buffer += 2;
|
||||
in_buffer_len -= 2;
|
||||
|
||||
(void) memset (out_buffer, decode_expand_char, output_req);
|
||||
written_output = output_req;
|
||||
|
||||
DBG (DBG_IMPORTANT,
|
||||
"Brother_decode_gray_rlength: expand block: %zu bytes of 0x%2.2x\n",
|
||||
output_req,
|
||||
(int) decode_expand_char);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* Check that we have enough space for the expanded mini block
|
||||
* and that we have the entire miniblock in the src buffer.
|
||||
*
|
||||
*/
|
||||
size_t output_req = in_buffer[0] + 1;
|
||||
if ((output_req > out_buffer_len) || (in_buffer_len < output_req + 1))
|
||||
{
|
||||
decode_state = BROTHER_DECODE_RLEN_IN_BYTES;
|
||||
block_bytes_left = in_buffer[0] + 1;
|
||||
|
||||
in_buffer += 1;
|
||||
in_buffer_len -= 1;
|
||||
|
||||
DBG (DBG_IMPORTANT,
|
||||
"Brother_decode_gray_rlength: bytes block: %zu bytes\n",
|
||||
block_bytes_left);
|
||||
break;
|
||||
}
|
||||
|
||||
in_buffer += 1;
|
||||
in_buffer_len -= 1;
|
||||
|
||||
(void) memcpy (out_buffer, in_buffer, output_req);
|
||||
consumed_input = output_req;
|
||||
written_output = output_req;
|
||||
|
||||
DBG (DBG_IMPORTANT,
|
||||
"Brother_decode_gray_rlength: bytes block: %zu bytes\n",
|
||||
output_req);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extraction phase, where we try to decode data.
|
||||
* We should be in a data extraction mode now.
|
||||
* Adjust for the consumed input and written output.
|
||||
*
|
||||
*/
|
||||
size_t bytes_to_copy = MIN(out_buffer_len, block_bytes_left);
|
||||
in_buffer += consumed_input;
|
||||
in_buffer_len -= consumed_input;
|
||||
out_buffer += written_output;
|
||||
out_buffer_len -= written_output;
|
||||
|
||||
if (bytes_to_copy)
|
||||
{
|
||||
size_t consumed = 0;
|
||||
|
||||
if (decode_state == BROTHER_DECODE_RLEN_IN_BYTES)
|
||||
{
|
||||
bytes_to_copy = MIN(bytes_to_copy, in_buffer_len);
|
||||
(void) memcpy (out_buffer, in_buffer, bytes_to_copy);
|
||||
consumed = bytes_to_copy;
|
||||
}
|
||||
else if (decode_state == BROTHER_DECODE_RLEN_IN_EXPAND)
|
||||
{
|
||||
(void) memset (out_buffer, decode_expand_char, bytes_to_copy);
|
||||
}
|
||||
|
||||
in_buffer += consumed;
|
||||
in_buffer_len -= consumed;
|
||||
out_buffer += bytes_to_copy;
|
||||
out_buffer_len -= bytes_to_copy;
|
||||
block_bytes_left -= bytes_to_copy;
|
||||
if (block_bytes_left == 0)
|
||||
{
|
||||
decode_state = BROTHER_DECODE_RLEN_INIT;
|
||||
}
|
||||
}
|
||||
} while (in_buffer_len && out_buffer_len);
|
||||
|
||||
if (!in_buffer_len || !out_buffer_len)
|
||||
{
|
||||
DBG (DBG_IMPORTANT,
|
||||
DBG (DBG_DETAIL,
|
||||
"Brother_decode_gray_rlength: ran out of buffer: in %zu out %zu\n",
|
||||
in_buffer_len,
|
||||
out_buffer_len);
|
||||
|
@ -740,8 +1198,10 @@ void BrotherJFIFDecoder::NewBlock()
|
|||
// Nothing to do.
|
||||
}
|
||||
|
||||
void BrotherJFIFDecoder::NewPage ()
|
||||
void BrotherJFIFDecoder::NewPage (const BrotherParameters ¶ms)
|
||||
{
|
||||
decode_params = params;
|
||||
|
||||
/*
|
||||
* Aborting a non-running state should be OK.
|
||||
*
|
||||
|
@ -1052,9 +1512,9 @@ DecodeStatus BrotherJFIFDecoder::DecodeScanData_CompressBuffer (const SANE_Byte
|
|||
}
|
||||
|
||||
|
||||
void BrotherGrayRawDecoder::NewPage ()
|
||||
void BrotherGrayRawDecoder::NewPage(const BrotherParameters ¶ms)
|
||||
{
|
||||
// Nothing to do.
|
||||
decode_params = params;
|
||||
}
|
||||
|
||||
void BrotherGrayRawDecoder::NewBlock()
|
||||
|
|
|
@ -66,7 +66,7 @@ typedef enum
|
|||
{
|
||||
BROTHER_FAMILY_NONE,
|
||||
// BROTHER_FAMILY_1,
|
||||
// BROTHER_FAMILY_2,
|
||||
BROTHER_FAMILY_2,
|
||||
// BROTHER_FAMILY_3,
|
||||
BROTHER_FAMILY_4,
|
||||
// BROTHER_FAMILY_5
|
||||
|
@ -82,12 +82,29 @@ typedef enum
|
|||
BROTHER_SCAN_MODE_TEXT
|
||||
} BrotherScanMode;
|
||||
|
||||
typedef SANE_Int BrotherSensor;
|
||||
|
||||
#define BROTHER_SENSOR_NONE 0
|
||||
#define BROTHER_SENSOR_EMAIL (1 << 0)
|
||||
#define BROTHER_SENSOR_FILE (1 << 1)
|
||||
#define BROTHER_SENSOR_OCR (1 << 2)
|
||||
#define BROTHER_SENSOR_IMAGE (1 << 3)
|
||||
|
||||
struct BrotherSessionResponse
|
||||
{
|
||||
SANE_Bool ready;
|
||||
};
|
||||
|
||||
struct BrotherButtonQueryResponse
|
||||
{
|
||||
SANE_Bool has_button_press;
|
||||
};
|
||||
|
||||
struct BrotherButtonStateResponse
|
||||
{
|
||||
BrotherSensor button_value; // raw value we get from the packet
|
||||
};
|
||||
|
||||
struct BrotherParameters
|
||||
{
|
||||
BrotherParameters():
|
||||
|
@ -101,7 +118,6 @@ struct BrotherParameters
|
|||
param_x_res(100),
|
||||
param_y_res(100)
|
||||
{
|
||||
|
||||
}
|
||||
SANE_Int param_brightness;
|
||||
SANE_Int param_contrast;
|
||||
|
@ -186,6 +202,12 @@ public:
|
|||
virtual SANE_Status DecodeSessionResp (const SANE_Byte *data, size_t data_len,
|
||||
BrotherSessionResponse &response) = 0;
|
||||
|
||||
virtual SANE_Status DecodeButtonQueryResp (const SANE_Byte *data, size_t data_len,
|
||||
BrotherButtonQueryResponse &response) = 0;
|
||||
|
||||
virtual SANE_Status DecodeButtonStateResp (const SANE_Byte *data, size_t data_len,
|
||||
BrotherButtonStateResponse &response) = 0;
|
||||
|
||||
virtual SANE_Status EncodeBasicParameterBlock (SANE_Byte *data, size_t data_len,
|
||||
size_t *length) = 0;
|
||||
|
||||
|
@ -203,6 +225,8 @@ public:
|
|||
size_t *src_data_consumed, SANE_Byte *dst_data,
|
||||
size_t dest_data_len, size_t *dest_data_written) = 0;
|
||||
|
||||
// virtual SANE_Status CheckSensor(BrotherSensor &status) = 0;
|
||||
|
||||
protected:
|
||||
BrotherParameters scan_params;
|
||||
};
|
||||
|
@ -251,7 +275,7 @@ public:
|
|||
}
|
||||
|
||||
void NewBlock();
|
||||
void NewPage();
|
||||
void NewPage(const BrotherParameters ¶ms);
|
||||
|
||||
DecodeStatus DecodeScanData (const SANE_Byte *src_data, size_t src_data_len,
|
||||
size_t *src_data_consumed, SANE_Byte *dst_data, size_t dest_data_len,
|
||||
|
@ -282,36 +306,26 @@ private:
|
|||
// TODO: Move me to the state.
|
||||
static jmp_buf my_env;
|
||||
|
||||
BrotherParameters decode_params;
|
||||
|
||||
};
|
||||
|
||||
class BrotherGrayRLengthDecoder
|
||||
{
|
||||
public:
|
||||
BrotherGrayRLengthDecoder():
|
||||
decode_state(BROTHER_DECODE_RLEN_INIT),
|
||||
decode_expand_char(0),
|
||||
block_bytes_left(0)
|
||||
BrotherGrayRLengthDecoder()
|
||||
{
|
||||
}
|
||||
void NewBlock();
|
||||
void NewPage();
|
||||
|
||||
void NewPage(const BrotherParameters ¶ms);
|
||||
|
||||
DecodeStatus DecodeScanData (const SANE_Byte *src_data, size_t src_data_len,
|
||||
size_t *src_data_consumed, SANE_Byte *dst_data, size_t dest_data_len,
|
||||
size_t *dest_data_written);
|
||||
|
||||
private:
|
||||
enum BrotherDecodeState
|
||||
{
|
||||
BROTHER_DECODE_RLEN_INIT,
|
||||
BROTHER_DECODE_RLEN_IN_EXPAND,
|
||||
BROTHER_DECODE_RLEN_IN_BYTES
|
||||
};
|
||||
|
||||
// Block decoding state
|
||||
BrotherDecodeState decode_state;
|
||||
SANE_Byte decode_expand_char;
|
||||
size_t block_bytes_left;
|
||||
BrotherParameters decode_params;
|
||||
};
|
||||
|
||||
|
||||
|
@ -322,13 +336,78 @@ public:
|
|||
{
|
||||
}
|
||||
void NewBlock();
|
||||
void NewPage();
|
||||
|
||||
void NewPage(const BrotherParameters ¶ms);
|
||||
|
||||
DecodeStatus DecodeScanData (const SANE_Byte *src_data, size_t src_data_len,
|
||||
size_t *src_data_consumed, SANE_Byte *dst_data, size_t dest_data_len,
|
||||
size_t *dest_data_written);
|
||||
|
||||
private:
|
||||
BrotherParameters decode_params;
|
||||
};
|
||||
|
||||
class BrotherEncoderFamily2 : public BrotherEncoder
|
||||
{
|
||||
public:
|
||||
~BrotherEncoderFamily2 ()
|
||||
{
|
||||
}
|
||||
|
||||
void NewPage() override
|
||||
{
|
||||
current_header.block_type = 0;
|
||||
|
||||
jfif_decoder.NewPage(scan_params);
|
||||
gray_decoder.NewPage(scan_params);
|
||||
}
|
||||
|
||||
SANE_Status DecodeSessionResp (const SANE_Byte *data, size_t data_len,
|
||||
BrotherSessionResponse &response) override;
|
||||
|
||||
SANE_Status EncodeBasicParameterBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
|
||||
|
||||
SANE_Status DecodeBasicParameterBlockResp (const SANE_Byte *data, size_t data_len,
|
||||
BrotherBasicParamResponse &response) override;
|
||||
|
||||
SANE_Status EncodeADFBlock (SANE_Byte *data, size_t data_len, size_t *length) override
|
||||
{
|
||||
(void)data;
|
||||
(void)data_len;
|
||||
(void)length;
|
||||
return SANE_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
SANE_Status DecodeADFBlockResp (const SANE_Byte *data, size_t data_len,
|
||||
BrotherADFResponse &response) override
|
||||
{
|
||||
(void)data;
|
||||
(void)data_len;
|
||||
(void)response;
|
||||
|
||||
return SANE_STATUS_UNSUPPORTED;
|
||||
}
|
||||
|
||||
SANE_Status EncodeParameterBlock (SANE_Byte *data, size_t data_len, size_t *length) override;
|
||||
|
||||
SANE_Status DecodeScanData (const SANE_Byte *src_data, size_t src_data_len,
|
||||
size_t *src_data_consumed, SANE_Byte *dst_data,
|
||||
size_t dest_data_len, size_t *dest_data_written) override;
|
||||
|
||||
SANE_Status DecodeButtonQueryResp (const SANE_Byte *data, size_t data_len,
|
||||
BrotherButtonQueryResponse &response) override;
|
||||
|
||||
SANE_Status DecodeButtonStateResp (const SANE_Byte *data, size_t data_len,
|
||||
BrotherButtonStateResponse &response) override;
|
||||
|
||||
private:
|
||||
DecodeStatus DecodeScanDataHeader (const SANE_Byte *src_data, size_t src_data_len,
|
||||
size_t *src_data_consumed, ScanDataHeader &header);
|
||||
|
||||
ScanDataHeader current_header;
|
||||
|
||||
BrotherJFIFDecoder jfif_decoder;
|
||||
BrotherGrayRLengthDecoder gray_decoder;
|
||||
};
|
||||
|
||||
class BrotherEncoderFamily4 : public BrotherEncoder
|
||||
|
@ -342,9 +421,9 @@ public:
|
|||
{
|
||||
current_header.block_type = 0;
|
||||
|
||||
jfif_decoder.NewPage();
|
||||
gray_decoder.NewPage();
|
||||
gray_raw_decoder.NewPage();
|
||||
jfif_decoder.NewPage(scan_params);
|
||||
gray_decoder.NewPage(scan_params);
|
||||
gray_raw_decoder.NewPage(scan_params);
|
||||
}
|
||||
|
||||
SANE_Status DecodeSessionResp (const SANE_Byte *data, size_t data_len,
|
||||
|
@ -366,9 +445,19 @@ public:
|
|||
size_t *src_data_consumed, SANE_Byte *dst_data,
|
||||
size_t dest_data_len, size_t *dest_data_written) override;
|
||||
|
||||
SANE_Status DecodeButtonQueryResp (const SANE_Byte *data, size_t data_len,
|
||||
BrotherButtonQueryResponse &response) override;
|
||||
|
||||
SANE_Status DecodeButtonStateResp (const SANE_Byte *data, size_t data_len,
|
||||
BrotherButtonStateResponse &response) override;
|
||||
|
||||
|
||||
|
||||
// SANE_Status CheckSensor(BrotherSensor &status) override;
|
||||
|
||||
private:
|
||||
DecodeStatus DecodeScanDataHeader (const SANE_Byte *src_data, size_t src_data_len,
|
||||
size_t *src_data_consumed, ScanDataHeader &header);
|
||||
size_t *src_data_consumed, ScanDataHeader &header);
|
||||
|
||||
ScanDataHeader current_header;
|
||||
|
||||
|
|
|
@ -69,11 +69,32 @@
|
|||
#define CAP_MODE_GRAY_DITHER (1u << 2)
|
||||
#define CAP_MODE_BW (1u << 3)
|
||||
|
||||
#define CAP_ENCODE_JPEG (1u << 4)
|
||||
#define CAP_ENCODE_RLENCODE (1u << 5)
|
||||
#define CAP_MODE_BUTTON_SCAN_EMAIL (1u << 4)
|
||||
#define CAP_MODE_BUTTON_SCAN_OCR (1u << 5)
|
||||
#define CAP_MODE_BUTTON_SCAN_FILE (1u << 6)
|
||||
#define CAP_MODE_BUTTON_SCAN_IMAGE (1u << 7)
|
||||
|
||||
/*
|
||||
* Messages.
|
||||
*
|
||||
*/
|
||||
#define SANE_VALUE_SCAN_MODE_GRAY_DITHER SANE_I18N("Gray (Dithered)")
|
||||
|
||||
#define SANE_NAME_FILE_BUTTON "file-sensor"
|
||||
#define SANE_NAME_EMAIL_BUTTON "email-sensor"
|
||||
#define SANE_NAME_OCR_BUTTON "ocr-sensor"
|
||||
#define SANE_NAME_IMAGE_BUTTON "image-sensor"
|
||||
|
||||
#define SANE_TITLE_FILE_BUTTON "File button"
|
||||
#define SANE_TITLE_EMAIL_BUTTON "Email button"
|
||||
#define SANE_TITLE_OCR_BUTTON "OCR button"
|
||||
#define SANE_TITLE_IMAGE_BUTTON "Image button"
|
||||
|
||||
#define SANE_DESC_FILE_BUTTON SANE_I18N("File button")
|
||||
#define SANE_DESC_EMAIL_BUTTON SANE_I18N("Email button")
|
||||
#define SANE_DESC_OCR_BUTTON SANE_I18N("OCR button")
|
||||
#define SANE_DESC_IMAGE_BUTTON SANE_I18N("Image button")
|
||||
|
||||
enum Brother_Option
|
||||
{
|
||||
OPT_NUM_OPTS = 0,
|
||||
|
@ -95,6 +116,12 @@ enum Brother_Option
|
|||
OPT_BRIGHTNESS,
|
||||
OPT_CONTRAST,
|
||||
|
||||
OPT_SENSOR_GROUP,
|
||||
OPT_SENSOR_EMAIL,
|
||||
OPT_SENSOR_FILE,
|
||||
OPT_SENSOR_IMAGE,
|
||||
OPT_SENSOR_OCR,
|
||||
|
||||
/* must come last: */
|
||||
NUM_OPTIONS
|
||||
};
|
||||
|
@ -120,15 +147,19 @@ typedef struct Brother_Model
|
|||
|
||||
static Brother_Model models[] =
|
||||
{
|
||||
// { "Brother", "MFC-465CN", BROTHER_FAMILY_2, 0x04f9, 0x01d7,
|
||||
// { 0, SANE_FIX(210), 0 },
|
||||
// { 0, SANE_FIX(295), 0 },
|
||||
// { 5, 100, 150, 200, 300, 600 },
|
||||
// { 7, 100, 150, 200, 300, 600, 1200, 2400 },
|
||||
// CAP_MODE_COLOUR |
|
||||
// CAP_MODE_GRAY |
|
||||
// CAP_MODE_GRAY_DITHER |
|
||||
// CAP_MODE_BW },
|
||||
{ "Brother", "MFC-465CN", BROTHER_FAMILY_2, 0x04f9, 0x01d7,
|
||||
{ 0, SANE_FIX(210), 0 },
|
||||
{ 0, SANE_FIX(295), 0 },
|
||||
{ 5, 100, 150, 200, 300, 600 },
|
||||
{ 7, 100, 150, 200, 300, 600, 1200, 2400 },
|
||||
CAP_MODE_COLOUR |
|
||||
CAP_MODE_GRAY |
|
||||
CAP_MODE_GRAY_DITHER |
|
||||
CAP_MODE_BW |
|
||||
CAP_MODE_BUTTON_SCAN_EMAIL |
|
||||
CAP_MODE_BUTTON_SCAN_FILE |
|
||||
CAP_MODE_BUTTON_SCAN_OCR |
|
||||
CAP_MODE_BUTTON_SCAN_IMAGE },
|
||||
|
||||
{ "Brother", "MFC-J4320DW", BROTHER_FAMILY_4, 0x04f9, 0x033a,
|
||||
{ 0, SANE_FIX(213.9), 0 },
|
||||
|
@ -138,9 +169,7 @@ static Brother_Model models[] =
|
|||
CAP_MODE_COLOUR |
|
||||
CAP_MODE_GRAY |
|
||||
CAP_MODE_GRAY_DITHER |
|
||||
CAP_MODE_BW |
|
||||
CAP_ENCODE_JPEG |
|
||||
CAP_ENCODE_RLENCODE },
|
||||
CAP_MODE_BW},
|
||||
|
||||
{NULL, NULL, BROTHER_FAMILY_NONE, 0, 0, {0, 0, 0}, {0, 0, 0}, {0}, {0}, 0}
|
||||
};
|
||||
|
@ -160,7 +189,8 @@ struct BrotherDevice
|
|||
#ifdef BROTHER_ENABLE_SCAN_FILE
|
||||
scan_file (nullptr),
|
||||
#endif
|
||||
driver (nullptr)
|
||||
driver (nullptr),
|
||||
current_sensor_states(BROTHER_SENSOR_NONE)
|
||||
{
|
||||
(void)memset(opt, 0, sizeof(opt));
|
||||
}
|
||||
|
@ -186,6 +216,7 @@ struct BrotherDevice
|
|||
|
||||
BrotherDriver *driver;
|
||||
|
||||
BrotherSensor current_sensor_states;
|
||||
};
|
||||
|
||||
static BrotherDevice *first_dev = NULL;
|
||||
|
@ -519,6 +550,75 @@ init_options (BrotherDevice *device)
|
|||
od->constraint.range = &constraint_brightness_contrast;
|
||||
device->val[OPT_CONTRAST].w = 0;
|
||||
|
||||
// Sensor group.
|
||||
od = &device->opt[OPT_SENSOR_GROUP];
|
||||
od->name = SANE_NAME_SENSORS;
|
||||
od->title = SANE_TITLE_SENSORS;
|
||||
od->desc = SANE_DESC_SENSORS;
|
||||
od->type = SANE_TYPE_GROUP;
|
||||
od->cap = SANE_CAP_ADVANCED;
|
||||
od->unit = SANE_UNIT_NONE;
|
||||
od->size = 0;
|
||||
od->constraint_type = SANE_CONSTRAINT_NONE;
|
||||
od->constraint.range = 0;
|
||||
device->val[OPT_SENSOR_GROUP].w = 0;
|
||||
|
||||
od = &device->opt[OPT_SENSOR_EMAIL];
|
||||
od->name = SANE_NAME_EMAIL_BUTTON;
|
||||
od->title = SANE_TITLE_EMAIL_BUTTON;
|
||||
od->desc = SANE_DESC_EMAIL_BUTTON;
|
||||
od->type = SANE_TYPE_BOOL;
|
||||
od->unit = SANE_UNIT_NONE;
|
||||
od->size = 1 * sizeof(SANE_Bool);
|
||||
if (device->model->capabilities & CAP_MODE_BUTTON_SCAN_EMAIL)
|
||||
od->cap =
|
||||
SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
|
||||
else
|
||||
od->cap = SANE_CAP_INACTIVE;
|
||||
device->val[OPT_SENSOR_EMAIL].b = SANE_FALSE;
|
||||
|
||||
od = &device->opt[OPT_SENSOR_FILE];
|
||||
od->name = SANE_NAME_FILE_BUTTON;
|
||||
od->title = SANE_TITLE_FILE_BUTTON;
|
||||
od->desc = SANE_DESC_FILE_BUTTON;
|
||||
od->type = SANE_TYPE_BOOL;
|
||||
od->unit = SANE_UNIT_NONE;
|
||||
od->size = 1 * sizeof(SANE_Bool);
|
||||
if (device->model->capabilities & CAP_MODE_BUTTON_SCAN_FILE)
|
||||
od->cap =
|
||||
SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
|
||||
else
|
||||
od->cap = SANE_CAP_INACTIVE;
|
||||
device->val[OPT_SENSOR_FILE].b = SANE_FALSE;
|
||||
|
||||
od = &device->opt[OPT_SENSOR_IMAGE];
|
||||
od->name = SANE_NAME_IMAGE_BUTTON;
|
||||
od->title = SANE_TITLE_IMAGE_BUTTON;
|
||||
od->desc = SANE_DESC_IMAGE_BUTTON;
|
||||
od->type = SANE_TYPE_BOOL;
|
||||
od->unit = SANE_UNIT_NONE;
|
||||
od->size = 1 * sizeof(SANE_Bool);
|
||||
if (device->model->capabilities & CAP_MODE_BUTTON_SCAN_IMAGE)
|
||||
od->cap =
|
||||
SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
|
||||
else
|
||||
od->cap = SANE_CAP_INACTIVE;
|
||||
device->val[OPT_SENSOR_IMAGE].b = SANE_FALSE;
|
||||
|
||||
od = &device->opt[OPT_SENSOR_OCR];
|
||||
od->name = SANE_NAME_OCR_BUTTON;
|
||||
od->title = SANE_TITLE_OCR_BUTTON;
|
||||
od->desc = SANE_DESC_OCR_BUTTON;
|
||||
od->type = SANE_TYPE_BOOL;
|
||||
od->unit = SANE_UNIT_NONE;
|
||||
od->size = 1 * sizeof(SANE_Bool);
|
||||
if (device->model->capabilities & CAP_MODE_BUTTON_SCAN_OCR)
|
||||
od->cap =
|
||||
SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED;
|
||||
else
|
||||
od->cap = SANE_CAP_INACTIVE;
|
||||
device->val[OPT_SENSOR_OCR].b = SANE_FALSE;
|
||||
|
||||
DBG (2, "end init_options: dev=%s\n", device->name);
|
||||
|
||||
return SANE_STATUS_GOOD;
|
||||
|
@ -913,6 +1013,51 @@ sane_control_option (SANE_Handle handle, SANE_Int option, SANE_Action action,
|
|||
option, device->opt[option].name, *(SANE_Int *) value);
|
||||
break;
|
||||
|
||||
case OPT_SENSOR_EMAIL:
|
||||
case OPT_SENSOR_OCR:
|
||||
case OPT_SENSOR_FILE:
|
||||
case OPT_SENSOR_IMAGE:
|
||||
/*
|
||||
* Check the sensor value and update our status value.
|
||||
*
|
||||
*/
|
||||
{
|
||||
BrotherSensor sensor = BROTHER_SENSOR_NONE;
|
||||
if (device->driver->CheckSensor(sensor) == SANE_STATUS_GOOD)
|
||||
{
|
||||
device->current_sensor_states |= sensor;
|
||||
}
|
||||
switch (option)
|
||||
{
|
||||
case OPT_SENSOR_EMAIL:
|
||||
*(SANE_Bool*) value =
|
||||
device->current_sensor_states & BROTHER_SENSOR_EMAIL ? SANE_TRUE : SANE_FALSE;
|
||||
break;
|
||||
|
||||
case OPT_SENSOR_OCR:
|
||||
*(SANE_Bool*) value =
|
||||
device->current_sensor_states & BROTHER_SENSOR_OCR ? SANE_TRUE : SANE_FALSE;
|
||||
break;
|
||||
|
||||
case OPT_SENSOR_FILE:
|
||||
*(SANE_Bool*) value =
|
||||
device->current_sensor_states & BROTHER_SENSOR_FILE ? SANE_TRUE : SANE_FALSE;
|
||||
break;
|
||||
|
||||
case OPT_SENSOR_IMAGE:
|
||||
*(SANE_Bool*) value =
|
||||
device->current_sensor_states & BROTHER_SENSOR_IMAGE ? SANE_TRUE : SANE_FALSE;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DBG (DBG_DETAIL, "sane_control_option: get option %d (%s), value=%s\n",
|
||||
option, device->opt[option].name, *(SANE_Bool *) value? "TRUE": "FALSE");
|
||||
break;
|
||||
|
||||
default:
|
||||
DBG (DBG_SERIOUS, "sane_control_option: trying to get unexpected option\n");
|
||||
return SANE_STATUS_INVAL;
|
||||
|
|
|
@ -44,7 +44,7 @@ static void test_family4_decode_session_resp()
|
|||
ASSERT_TRUE(sess_resp.ready);
|
||||
|
||||
// BUSY status
|
||||
data = (const SANE_Byte *)"\x05" "\x10" "\x01" "\x02" "\x20";
|
||||
data = (const SANE_Byte *)"\x05" "\x10" "\x01" "\x02" "\x80";
|
||||
|
||||
sane_resp = encoder.DecodeSessionResp (data, 5, sess_resp);
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ static void test_gray_rlength_no_compress()
|
|||
* Decodes broken through src exhaustion (fragmentation of input).
|
||||
*
|
||||
*/
|
||||
// Break after length: can decode the length but nothing else.
|
||||
// Break after length: can't decode: need full miniblock.
|
||||
src_data = (const SANE_Byte *)"\x04";
|
||||
|
||||
res_code = decoder.DecodeScanData (src_data,
|
||||
|
@ -88,25 +88,10 @@ static void test_gray_rlength_no_compress()
|
|||
&dest_data_written);
|
||||
|
||||
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
|
||||
ASSERT_EQ(src_data_consumed, (size_t)1);
|
||||
ASSERT_EQ(src_data_consumed, (size_t)0);
|
||||
ASSERT_EQ(dest_data_written, (size_t)0);
|
||||
|
||||
src_data = (const SANE_Byte *)"MNOPQ";
|
||||
|
||||
res_code = decoder.DecodeScanData (src_data,
|
||||
5,
|
||||
&src_data_consumed,
|
||||
dest_data,
|
||||
sizeof(dest_data),
|
||||
&dest_data_written);
|
||||
|
||||
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
|
||||
ASSERT_EQ(src_data_consumed, (size_t)5);
|
||||
ASSERT_EQ(dest_data_written, (size_t)5);
|
||||
|
||||
ASSERT_EQ(memcmp(dest_data, "MNOPQ", 5), 0);
|
||||
|
||||
// BREAK part way through the data portion.
|
||||
// BREAK part way through the data portion: can't decode: need full miniblock.
|
||||
src_data = (const SANE_Byte *)"\x05" "ABC";
|
||||
|
||||
res_code = decoder.DecodeScanData (src_data,
|
||||
|
@ -117,31 +102,14 @@ static void test_gray_rlength_no_compress()
|
|||
&dest_data_written);
|
||||
|
||||
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
|
||||
ASSERT_EQ(src_data_consumed, (size_t)4);
|
||||
ASSERT_EQ(dest_data_written, (size_t)3);
|
||||
|
||||
ASSERT_EQ(memcmp(dest_data, "ABC", 3), 0);
|
||||
|
||||
src_data = (const SANE_Byte *)"DEF";
|
||||
|
||||
res_code = decoder.DecodeScanData (src_data,
|
||||
3,
|
||||
&src_data_consumed,
|
||||
dest_data,
|
||||
sizeof(dest_data),
|
||||
&dest_data_written);
|
||||
|
||||
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
|
||||
ASSERT_EQ(src_data_consumed, (size_t)3);
|
||||
ASSERT_EQ(dest_data_written, (size_t)3);
|
||||
|
||||
ASSERT_EQ(memcmp(dest_data, "DEF", 3), 0);
|
||||
ASSERT_EQ(src_data_consumed, (size_t)0);
|
||||
ASSERT_EQ(dest_data_written, (size_t)0);
|
||||
|
||||
/*
|
||||
* Decodes broken by exhausted output buffer.
|
||||
*
|
||||
*/
|
||||
// BREAK part way through the data portion.
|
||||
// BREAK part way through the data portion: can't decode: need full miniblock.
|
||||
src_data = (const SANE_Byte *)"\x05" "MNOPQR";
|
||||
memset(dest_data, 0, sizeof(dest_data));
|
||||
|
||||
|
@ -153,122 +121,8 @@ static void test_gray_rlength_no_compress()
|
|||
&dest_data_written);
|
||||
|
||||
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
|
||||
ASSERT_EQ(src_data_consumed, (size_t)2);
|
||||
ASSERT_EQ(dest_data_written, (size_t)1);
|
||||
|
||||
ASSERT_EQ(memcmp(dest_data, "M", 1), 0);
|
||||
|
||||
src_data = (const SANE_Byte *)"NOPQR";
|
||||
memset(dest_data, 0, sizeof(dest_data));
|
||||
|
||||
res_code = decoder.DecodeScanData (src_data,
|
||||
5,
|
||||
&src_data_consumed,
|
||||
dest_data,
|
||||
2,
|
||||
&dest_data_written);
|
||||
|
||||
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
|
||||
ASSERT_EQ(src_data_consumed, (size_t)2);
|
||||
ASSERT_EQ(dest_data_written, (size_t)2);
|
||||
|
||||
ASSERT_EQ(memcmp(dest_data, "NO", 1), 0);
|
||||
|
||||
src_data = (const SANE_Byte *)"PQR";
|
||||
memset(dest_data, 0, sizeof(dest_data));
|
||||
|
||||
res_code = decoder.DecodeScanData (src_data,
|
||||
3,
|
||||
&src_data_consumed,
|
||||
dest_data,
|
||||
3,
|
||||
&dest_data_written);
|
||||
|
||||
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
|
||||
ASSERT_EQ(src_data_consumed, (size_t)3);
|
||||
ASSERT_EQ(dest_data_written, (size_t)3);
|
||||
|
||||
ASSERT_EQ(memcmp(dest_data, "PQR", 3), 0);
|
||||
|
||||
/*
|
||||
* Test reset by page.
|
||||
* This should reset the state of the decoder.
|
||||
*
|
||||
*/
|
||||
src_data = (const SANE_Byte *)"\x04" "ABCDE";
|
||||
memset(dest_data, 0, sizeof(dest_data));
|
||||
|
||||
res_code = decoder.DecodeScanData (src_data,
|
||||
3,
|
||||
&src_data_consumed,
|
||||
dest_data,
|
||||
sizeof(dest_data),
|
||||
&dest_data_written);
|
||||
|
||||
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
|
||||
ASSERT_EQ(src_data_consumed, (size_t)3);
|
||||
ASSERT_EQ(dest_data_written, (size_t)2);
|
||||
|
||||
ASSERT_EQ(memcmp(dest_data, "AB", 2), 0);
|
||||
|
||||
// Partial decode should be flushed.
|
||||
decoder.NewPage();
|
||||
|
||||
src_data = (const SANE_Byte *)"\x04" "GHIJK";
|
||||
memset(dest_data, 0, sizeof(dest_data));
|
||||
|
||||
res_code = decoder.DecodeScanData (src_data,
|
||||
6,
|
||||
&src_data_consumed,
|
||||
dest_data,
|
||||
sizeof(dest_data),
|
||||
&dest_data_written);
|
||||
|
||||
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
|
||||
ASSERT_EQ(src_data_consumed, (size_t)6);
|
||||
ASSERT_EQ(dest_data_written, (size_t)5);
|
||||
|
||||
ASSERT_EQ(memcmp(dest_data, "GHIJK", 5), 0);
|
||||
|
||||
/*
|
||||
* Test reset by block.
|
||||
* This should reset the state of the decoder.
|
||||
*
|
||||
*/
|
||||
src_data = (const SANE_Byte *)"\x04" "ABCDE";
|
||||
memset(dest_data, 0, sizeof(dest_data));
|
||||
|
||||
res_code = decoder.DecodeScanData (src_data,
|
||||
3,
|
||||
&src_data_consumed,
|
||||
dest_data,
|
||||
sizeof(dest_data),
|
||||
&dest_data_written);
|
||||
|
||||
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
|
||||
ASSERT_EQ(src_data_consumed, (size_t)3);
|
||||
ASSERT_EQ(dest_data_written, (size_t)2);
|
||||
|
||||
ASSERT_EQ(memcmp(dest_data, "AB", 2), 0);
|
||||
|
||||
// Partial decode should be flushed.
|
||||
decoder.NewBlock();
|
||||
|
||||
src_data = (const SANE_Byte *)"\x04" "GHIJK";
|
||||
memset(dest_data, 0, sizeof(dest_data));
|
||||
|
||||
res_code = decoder.DecodeScanData (src_data,
|
||||
6,
|
||||
&src_data_consumed,
|
||||
dest_data,
|
||||
sizeof(dest_data),
|
||||
&dest_data_written);
|
||||
|
||||
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
|
||||
ASSERT_EQ(src_data_consumed, (size_t)6);
|
||||
ASSERT_EQ(dest_data_written, (size_t)5);
|
||||
|
||||
ASSERT_EQ(memcmp(dest_data, "GHIJK", 5), 0);
|
||||
ASSERT_EQ(src_data_consumed, (size_t)0);
|
||||
ASSERT_EQ(dest_data_written, (size_t)0);
|
||||
}
|
||||
|
||||
static void test_gray_rlength_compression()
|
||||
|
@ -335,7 +189,7 @@ static void test_gray_rlength_compression()
|
|||
ASSERT_EQ(strrchr((const char *)dest_data, 'Y'), (const char *)&dest_data[128]);
|
||||
|
||||
/*
|
||||
* Fragmentation due to dest exhaustion.
|
||||
* Fragmentation due to dest exhaustion. Can't decode: need full miniblock.
|
||||
*
|
||||
*/
|
||||
src_data = (const SANE_Byte *)"\xfd" "Z";
|
||||
|
@ -348,33 +202,9 @@ static void test_gray_rlength_compression()
|
|||
1,
|
||||
&dest_data_written);
|
||||
|
||||
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
|
||||
ASSERT_EQ(src_data_consumed, (size_t)2);
|
||||
ASSERT_EQ(dest_data_written, (size_t)1);
|
||||
|
||||
ASSERT_EQ(memcmp(dest_data, "Z", 1), 0);
|
||||
|
||||
src_data = (const SANE_Byte *)"\xfd" "Z";
|
||||
memset(dest_data, 0, sizeof(dest_data));
|
||||
|
||||
/*
|
||||
* Can decode, but we haven't any additional src data.
|
||||
* This is a special case of decompression where we do not require
|
||||
* additional input from the src; just some output space.
|
||||
*
|
||||
*/
|
||||
res_code = decoder.DecodeScanData (src_data,
|
||||
0,
|
||||
&src_data_consumed,
|
||||
dest_data,
|
||||
3,
|
||||
&dest_data_written);
|
||||
|
||||
ASSERT_EQ(res_code, DECODE_STATUS_GOOD);
|
||||
ASSERT_EQ(src_data_consumed, (size_t)0);
|
||||
ASSERT_EQ(dest_data_written, (size_t)3);
|
||||
|
||||
ASSERT_EQ(memcmp(dest_data, "ZZZ", 3), 0);
|
||||
ASSERT_EQ(dest_data_written, (size_t)0);
|
||||
|
||||
/*
|
||||
* Fragmentation due to src exhaustion.
|
||||
|
|
Ładowanie…
Reference in New Issue