kopia lustrzana https://gitlab.com/sane-project/backends
310 wiersze
7.0 KiB
C++
310 wiersze
7.0 KiB
C++
/* sane - Scanner Access Now Easy.
|
|
|
|
BACKEND brother_mfp
|
|
|
|
Copyright (C) 2022 Ralph Little <skelband@gmail.com>
|
|
|
|
This file is part of the SANE package.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
This file implements a SANE backend for Brother Multifunction Devices.
|
|
*/
|
|
|
|
#define DEBUG_DECLARE_ONLY
|
|
|
|
#include "../include/sane/config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <algorithm>
|
|
|
|
#ifdef HAVE_SYS_SOCKET_H
|
|
#include <sys/socket.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_NETINET_IN_H
|
|
#include <netinet/in.h>
|
|
#endif
|
|
|
|
#include <netdb.h>
|
|
#include <netinet/tcp.h>
|
|
#include <arpa/inet.h>
|
|
|
|
|
|
|
|
#include "../include/sane/sane.h"
|
|
#include "../include/sane/sanei.h"
|
|
#include "../include/sane/saneopts.h"
|
|
#include "../include/sane/sanei_config.h"
|
|
#include "../include/sane/sanei_debug.h"
|
|
|
|
#include "brother_mfp-common.h"
|
|
#include "brother_mfp-driver.h"
|
|
|
|
#define BROTHER_READ_BUFFER_LEN (16 * 1024)
|
|
|
|
|
|
/*-----------------------------------------------------------------*/
|
|
|
|
union BrotherSocketAddr
|
|
{
|
|
struct sockaddr_storage storage;
|
|
struct sockaddr addr;
|
|
struct sockaddr_in ipv4;
|
|
struct sockaddr_in6 ipv6;
|
|
};
|
|
|
|
SANE_Status BrotherNetworkDriver::Connect ()
|
|
{
|
|
int val;
|
|
|
|
if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
|
|
{
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
val = 1;
|
|
setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
|
|
|
|
/*
|
|
* Using TCP_NODELAY improves responsiveness, especially on systems
|
|
* with a slow loopback interface...
|
|
*/
|
|
|
|
val = 1;
|
|
setsockopt (sockfd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
|
|
|
|
/*
|
|
* Close this socket when starting another process...
|
|
*/
|
|
|
|
fcntl (sockfd, F_SETFD, FD_CLOEXEC);
|
|
|
|
BrotherSocketAddr adr_inet;
|
|
memset (&adr_inet, 0, sizeof adr_inet);
|
|
|
|
adr_inet.ipv4.sin_family = AF_INET;
|
|
adr_inet.ipv4.sin_port = htons (54921);
|
|
|
|
if (!inet_aton ("192.168.1.19", &adr_inet.ipv4.sin_addr))
|
|
{
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
if (connect (sockfd, (struct sockaddr*) &adr_inet, sizeof(adr_inet.ipv4)) < 0)
|
|
{
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
SANE_Status init_res = Init ();
|
|
|
|
if (init_res != SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_SERIOUS, "BrotherUSBDriver::Connect: failed to send init session: %d\n", init_res);
|
|
Disconnect ();
|
|
}
|
|
|
|
/*
|
|
* Register buttons.
|
|
*
|
|
*/
|
|
(void)SNMPRegisterButtons();
|
|
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
|
|
#if HAVE_LIBSNMP
|
|
#include <net-snmp/net-snmp-config.h>
|
|
#include <net-snmp/net-snmp-includes.h>
|
|
#endif
|
|
|
|
SANE_Status BrotherNetworkDriver::SNMPRegisterButtons ()
|
|
{
|
|
#if HAVE_LIBSNMP
|
|
init_snmp ("brother_mfp");
|
|
|
|
/*
|
|
* Initialize a "session" that defines who we're going to talk to
|
|
*/
|
|
struct snmp_session session;
|
|
|
|
snmp_sess_init (&session); /* set up defaults */
|
|
session.peername = (char*) "192.168.1.19";
|
|
|
|
session.version = SNMP_VERSION_1;
|
|
|
|
/* set the SNMPv1 community name used for authentication */
|
|
session.community = (u_char*) "internal";
|
|
session.community_len = strlen ("internal");
|
|
|
|
SOCK_STARTUP;
|
|
|
|
/*
|
|
* Open the session
|
|
*/
|
|
struct snmp_session *ss = snmp_open (&session);
|
|
|
|
if (!ss)
|
|
{
|
|
snmp_perror ("ack");
|
|
snmp_log (LOG_ERR, "something horrible happened!!!\n");
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
netsnmp_pdu *pdu;
|
|
oid the_oid[MAX_OID_LEN];
|
|
size_t oid_len;
|
|
const char *oid_str = ".1.3.6.1.4.1.2435.2.3.9.2.11.1.1.0";
|
|
|
|
pdu = snmp_pdu_create (SNMP_MSG_SET);
|
|
oid_len = MAX_OID_LEN;
|
|
|
|
// Parse the OID
|
|
if (snmp_parse_oid (oid_str, the_oid, &oid_len) == 0)
|
|
{
|
|
snmp_perror (oid_str);
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
struct ButtonApp
|
|
{
|
|
const char *function;
|
|
unsigned int app_num;
|
|
} button_apps[] =
|
|
{
|
|
{ "IMAGE", 1 },
|
|
{ "OCR", 3 },
|
|
{ "FILE", 5 },
|
|
{ "EMAIL", 2 },
|
|
{ nullptr, 0 } };
|
|
|
|
const char *backend_host_addr = "192.168.1.12";
|
|
unsigned int backend_host_port = 54925;
|
|
const char *backend_host_name = "BACKEND_HOST";
|
|
unsigned int reg_lifetime = 360;
|
|
|
|
for (const ButtonApp *app = button_apps; app->function; app++)
|
|
{
|
|
(void) snprintf ((char*) small_buffer,
|
|
sizeof(small_buffer),
|
|
"TYPE=BR;BUTTON=SCAN;USER=\"%s\";FUNC=%s;HOST=%s:%u;APPNUM=%u;DURATION=%u;",
|
|
backend_host_name,
|
|
app->function,
|
|
backend_host_addr,
|
|
backend_host_port,
|
|
app->app_num,
|
|
reg_lifetime);
|
|
|
|
if (snmp_pdu_add_variable (pdu, the_oid, oid_len, ASN_OCTET_STR,
|
|
(const char*) small_buffer, strlen ((const char*) small_buffer))
|
|
== nullptr)
|
|
{
|
|
snmp_perror ("failed");
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
}
|
|
|
|
// Send the request
|
|
if (snmp_send (ss, pdu) == 0)
|
|
{
|
|
snmp_perror ("SNMP: Error while sending!");
|
|
snmp_free_pdu (pdu);
|
|
return SANE_STATUS_IO_ERROR;
|
|
}
|
|
|
|
// TODO: cleanup and shutdown of SNMP stuff!!!!!
|
|
|
|
return SANE_STATUS_NO_MEM;
|
|
#else
|
|
return SANE_STATUS_GOOD;
|
|
#endif
|
|
}
|
|
|
|
SANE_Status BrotherNetworkDriver::Disconnect ()
|
|
{
|
|
DBG (DBG_EVENT, "BrotherNetworkDriver::Disconnect: `%s'\n", devicename);
|
|
|
|
if (sockfd)
|
|
{
|
|
close(sockfd);
|
|
sockfd = 0;
|
|
}
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
SANE_Status BrotherNetworkDriver::Init ()
|
|
{
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
SANE_Status BrotherNetworkDriver::CancelScan ()
|
|
{
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
SANE_Status BrotherNetworkDriver::StartScan ()
|
|
{
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
SANE_Status BrotherNetworkDriver::CheckSensor (BrotherSensor &status)
|
|
{
|
|
(void)status;
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
SANE_Status BrotherNetworkDriver::ReadScanData (SANE_Byte *data, size_t max_length, size_t *length)
|
|
{
|
|
(void)data;
|
|
(void)max_length;
|
|
(void)length;
|
|
return SANE_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
BrotherNetworkDriver::BrotherNetworkDriver (const char *devicename, BrotherFamily family,
|
|
SANE_Word capabilities) :
|
|
BrotherDriver (family, capabilities),
|
|
is_open (false),
|
|
in_session (false),
|
|
is_scanning (false),
|
|
was_cancelled (false),
|
|
devicename (nullptr),
|
|
next_frame_number (0),
|
|
small_buffer { 0 },
|
|
data_buffer (nullptr),
|
|
data_buffer_bytes (0),
|
|
out_of_docs (false),
|
|
sockfd (0)
|
|
{
|
|
this->devicename = strdup(devicename);
|
|
}
|
|
|
|
BrotherNetworkDriver::~BrotherNetworkDriver ()
|
|
{
|
|
delete[] data_buffer;
|
|
free (devicename);
|
|
}
|