From d89af0691edfd07fcbb268bfff79804b60a10e02 Mon Sep 17 00:00:00 2001 From: Rolf Bensch Date: Fri, 20 Mar 2020 19:38:31 +0100 Subject: [PATCH] pixma: new axis driver moved to new pixma folder provided by Ondrej Zary see https://alioth-lists.debian.net/pipermail/sane-devel/2020-March/037734.html --- backend/Makefile.am | 3 + backend/pixma/pixma_axis.c | 576 +++++++++++++++++++++++++++++ backend/pixma/pixma_axis.h | 154 ++++++++ backend/pixma/pixma_axis_private.h | 89 +++++ backend/pixma/pixma_io_sanei.c | 59 ++- 5 files changed, 877 insertions(+), 4 deletions(-) create mode 100644 backend/pixma/pixma_axis.c create mode 100644 backend/pixma/pixma_axis.h create mode 100644 backend/pixma/pixma_axis_private.h diff --git a/backend/Makefile.am b/backend/Makefile.am index 18094b8d2..977d1f0c0 100644 --- a/backend/Makefile.am +++ b/backend/Makefile.am @@ -906,6 +906,9 @@ libsane_pint_la_LIBADD = $(COMMON_LIBS) libpint.la ../sanei/sanei_init_debug.lo libpixma_la_SOURCES = pixma/pixma.c \ pixma/pixma.h \ + pixma/pixma_axis.c \ + pixma/pixma_axis.h \ + pixma/pixma_axis_private.h \ pixma/pixma_io_sanei.c \ pixma/pixma_io.h \ pixma/pixma_common.c \ diff --git a/backend/pixma/pixma_axis.c b/backend/pixma/pixma_axis.c new file mode 100644 index 000000000..e90a8bb19 --- /dev/null +++ b/backend/pixma/pixma_axis.c @@ -0,0 +1,576 @@ +#undef BACKEND_NAME +#define BACKEND_NAME axis + +#include "../../include/sane/config.h" +#include "../../include/sane/sane.h" + +/* + * Standard types etc + */ +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif + +/* + * networking stuff + */ +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#include +#include +#include +#include +#ifdef HAVE_IFADDRS_H +#include +#endif +#ifdef HAVE_SYS_SELSECT_H +#include +#endif +#ifdef HAVE_PWD_H +#include +#endif +#include +#ifdef HAVE_FCNTL_H +#include +#endif + +#include "pixma_axis_private.h" +#include "pixma_axis.h" +#include "pixma.h" +#include "pixma_common.h" + +#define MAX_PACKET_DATA_SIZE 65535 +#define RECEIVE_TIMEOUT 2 + +/* static data */ +static axis_device_t device[AXIS_NO_DEVICES]; +static int axis_no_devices = 0; + +extern void +sanei_axis_init (void) +{ + DBG_INIT(); + axis_no_devices = 0; +} + +static char *getusername(void) { + static char noname[] = "sane_pixma"; + struct passwd *pwdent; + +#ifdef HAVE_PWD_H + if (((pwdent = getpwuid(geteuid())) != NULL) && (pwdent->pw_name != NULL)) + return pwdent->pw_name; +#endif + return noname; +} + +static ssize_t receive_packet(int socket, void *packet, size_t len, struct sockaddr_in *from) { + fd_set rfds; + struct timeval tv; + ssize_t received; + socklen_t from_len = sizeof(struct sockaddr_in); + + tv.tv_sec = RECEIVE_TIMEOUT; + tv.tv_usec = 0; + /* Watch socket to see when it has input. */ + FD_ZERO(&rfds); + FD_SET(socket, &rfds); + + switch (select(socket + 1, &rfds, NULL, NULL, &tv)) { + case 0: + return 0; + case -1: + DBG(LOG_CRIT, "select() failed"); + return 0; + default: + received = recvfrom(socket, packet, len, 0, (struct sockaddr *)from, &from_len); + if (received < 0) { + DBG(LOG_CRIT, "Error receiving packet"); + exit(2); + } +/*#ifdef DEBUG + int i; + for (i = 0; i < received; i++) + fprintf(stderr, "%.2hhX ",((char *)packet)[i]); + fprintf(stderr, "\n"); +#endif*/ + return received; + } +} + +static ssize_t axis_send_wimp(int udp_socket, uint8_t cmd, void *data, uint16_t len, struct sockaddr *addr, socklen_t addrlen) { + uint8_t packet[MAX_PACKET_DATA_SIZE]; + struct axis_wimp_header *header = (void *)packet; + ssize_t ret; + + header->type = cmd; + header->magic = 0x03; + header->zero = 0x00; + memcpy(packet + sizeof(struct axis_wimp_header), data, len); + ret = sendto(udp_socket, packet, sizeof(struct axis_wimp_header) + len, 0, addr, addrlen); + if (ret != (int)sizeof(struct axis_wimp_header) + len) { + DBG(LOG_CRIT, "Unable to send UDP packet"); + return ret; + } + + return 0; +} + +static ssize_t axis_wimp_get(int udp_socket, uint16_t remote_port, uint8_t cmd, uint8_t idx, char *data_out, uint16_t len_out) { + ssize_t ret; + uint16_t len; + struct axis_wimp_get wimp_get; + struct axis_wimp_header *reply = (void *)data_out; + struct axis_wimp_get_reply *str = (void *)(data_out + sizeof(struct axis_wimp_header)); + + wimp_get.port = cpu_to_le16(remote_port), + wimp_get.magic = 0x02, + wimp_get.zero = 0; + wimp_get.cmd = cmd, + wimp_get.idx = idx, + ret = axis_send_wimp(udp_socket, WIMP_SERVER_STATUS, &wimp_get, sizeof(wimp_get), NULL, 0); + if (ret) + return ret; + + ret = receive_packet(udp_socket, data_out, len_out, NULL); + if (ret < (int)sizeof(struct axis_wimp_header)) { + DBG(LOG_NOTICE, "Received packet is too short\n"); + return -1; + } + if (reply->type != (WIMP_SERVER_STATUS | WIMP_REPLY)) { + DBG(LOG_NOTICE, "Received invalid reply\n"); + return -1; + } + len = le16_to_cpu(str->len) - 2; + memmove(data_out, data_out + sizeof(struct axis_wimp_header) + sizeof(struct axis_wimp_get_reply), len); + data_out[len] = '\0'; + + return 0; +} + +static int create_udp_socket(uint32_t addr, uint16_t *source_port) { + int udp_socket; + int enable = 1; + struct sockaddr_in address; + socklen_t sock_len; + + udp_socket = socket(AF_INET, SOCK_DGRAM, 0); + if (udp_socket < 0) { + DBG(LOG_CRIT, "Unable to create UDP socket"); + return -1; + } + + if (setsockopt(udp_socket, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(enable))) { + DBG(LOG_CRIT, "Unable to enable broadcast"); + return -1; + } + + address.sin_family = AF_INET; + address.sin_port = 0; /* random */ + address.sin_addr.s_addr = addr; + if (bind(udp_socket, (struct sockaddr *)&address, sizeof(address)) < 0) { + DBG(LOG_CRIT, "Unable to bind UDP socket"); + return -1; + } + + /* get assigned source port */ + sock_len = sizeof(address); + getsockname(udp_socket, (struct sockaddr *)&address, &sock_len); + *source_port = ntohs(address.sin_port); + + return udp_socket; +} + +static int get_server_status(int udp_socket, uint32_t addr, uint16_t remote_port) { + char buf[MAX_PACKET_DATA_SIZE]; + struct sockaddr_in address; + + address.sin_family = AF_INET; + address.sin_port = htons(AXIS_WIMP_PORT); + address.sin_addr.s_addr = addr; + + /* connect the socket to this print server only */ + + if (connect(udp_socket, (struct sockaddr *)&address, sizeof(address)) < 0) { + DBG(LOG_CRIT, "Unable to connect UDP socket"); + return -1; + } + + /* get device status (IDLE/BUSY) */ + if (axis_wimp_get(udp_socket, remote_port, WIMP_GET_STATUS, 1, buf, sizeof(buf))) + DBG(LOG_NOTICE, "Error getting device status\n"); + DBG(LOG_INFO, "device status=%s\n", buf); + + /* get username if BUSY */ + if (!strcmp((char *)buf, "BUSY_TXT")) { + if (axis_wimp_get(udp_socket, remote_port, WIMP_GET_STATUS, 2, buf, sizeof(buf))) + DBG(LOG_NOTICE, "Error getting user name\n"); + DBG(LOG_INFO, "username=%s\n", buf); + return 1; + } + + return 0; +} + +static int get_device_name(int udp_socket, uint32_t addr, uint16_t remote_port, char *devname, int devname_len) { + char buf[MAX_PACKET_DATA_SIZE]; + struct sockaddr_in address; + + address.sin_family = AF_INET; + address.sin_port = htons(AXIS_WIMP_PORT); + address.sin_addr.s_addr = addr; + + /* connect the socket to this print server only */ + + if (connect(udp_socket, (struct sockaddr *)&address, sizeof(address)) < 0) { + DBG(LOG_CRIT, "Unable to connect UDP socket"); + return -1; + } + + /* get device name */ + if (axis_wimp_get(udp_socket, remote_port, WIMP_GET_NAME, 1, buf, sizeof(buf))) + DBG(LOG_NOTICE, "Error getting device name\n"); + DBG(LOG_INFO, "name=%s\n", buf); + + strncpy(devname, buf, devname_len); + + return 0; +} + +static int send_discover(int udp_socket, uint32_t addr, uint16_t source_port) { + int ret; + struct sockaddr_in address; + uint8_t get_info[2]; + + address.sin_family = AF_INET; + address.sin_port = htons(AXIS_WIMP_PORT); + address.sin_addr.s_addr = addr; + + get_info[0] = source_port & 0xff; + get_info[1] = source_port >> 8; + + ret = axis_send_wimp(udp_socket, WIMP_SERVER_INFO, get_info, sizeof(get_info), (struct sockaddr *)&address, sizeof(address)); + if (ret) + DBG(LOG_CRIT, "Unable to send discover packet"); + + return ret; +} + +static int send_broadcasts(int udp_socket, uint16_t source_port) { + struct ifaddrs *ifaddr, *ifa; + int num_sent = 0; + + if (getifaddrs(&ifaddr) == -1) { + DBG(LOG_CRIT, "Unable to obtain network interface list"); + return -1; + } + + /* Walk through all interfaces */ + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + /* we're interested only in broadcast-capable IPv4 interfaces */ + if (ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET && ifa->ifa_flags & IFF_BROADCAST) { + struct sockaddr_in *bcast = (struct sockaddr_in *)ifa->ifa_ifu.ifu_broadaddr; + DBG(LOG_INFO, "%s: %s\n", ifa->ifa_name, inet_ntoa(bcast->sin_addr)); + if (send_discover(udp_socket, bcast->sin_addr.s_addr, source_port) == 0) + num_sent++; + } + } + + freeifaddrs(ifaddr); + + return num_sent; +} + +int axis_send_cmd(int tcp_socket, uint8_t cmd, void *data, uint16_t len) { + uint8_t packet[MAX_PACKET_DATA_SIZE]; + struct axis_header *header = (void *)packet; + int ret; + DBG(LOG_INFO, "%s(0x%02x, %d)\n", __func__, cmd, len); + + header->type = AXIS_HDR_REQUEST; + header->len = cpu_to_le16(len + sizeof(struct axis_cmd)); + ret = send(tcp_socket, packet, sizeof(struct axis_header), 0); + for (int i = 0; i < ret; i++) + fprintf(stderr, "%02x ", packet[i]); + fprintf(stderr, "\n"); + if (ret < 0) { + perror("Error sending packet"); + return ret; + } + + struct axis_cmd *command = (void *)packet; + command->cmd = cmd; + command->len = cpu_to_le16(len); + memcpy(packet + sizeof(struct axis_cmd), data, len); + ret = send(tcp_socket, packet, sizeof(struct axis_cmd) + len, 0); + for (int i = 0; i < ret; i++) + fprintf(stderr, "%02x ", packet[i]); + fprintf(stderr, "\n"); + if (ret < 0) { + perror("Error sending packet"); + return ret; + } + + return 0; +} + +int axis_recv(SANE_Int dn, void *data, size_t *len) { + uint8_t packet[MAX_PACKET_DATA_SIZE]; + struct axis_header *header = (void *)packet; + struct axis_reply *reply = (void *)packet; + ssize_t ret; + int i; + +retry: + + ret = recv(device[dn].tcp_socket, packet, sizeof(struct axis_header), 0); + fprintf(stderr, "got1: "); + for (i = 0; i < ret; i++) { + fprintf(stderr, "%02x ", packet[i]); + } + fprintf(stderr, "\n"); + + if (header->type != AXIS_HDR_REPLY) { + fprintf(stderr, "not reply!\n"); + return -1; + } + *len = le16_to_cpu(header->len); + fprintf(stderr, "len=0x%x\n", *len); + ret = recv(device[dn].tcp_socket, packet, *len, 0); + fprintf(stderr, "got2: "); + for (i = 0; i < ret; i++) { + fprintf(stderr, "%02x ", packet[i]); + } + fprintf(stderr, "\n"); + *len = le16_to_cpu(reply->len); + if (reply->cmd == AXIS_CMD_UNKNOWN2) { /// interrupt??? + fprintf(stderr, "interrupt?????\n"); + memcpy(device[dn].int_data, packet + sizeof(struct axis_reply), *len); + device[dn].int_size = *len; + goto retry; + } + memcpy(data, packet + sizeof(struct axis_reply), *len); + if (reply->status != 0) { + fprintf(stderr, "status=0x%x\n", le16_to_cpu(reply->status)); + return SANE_STATUS_IO_ERROR; + } + + return 0; +} + + +/** + * Find AXIS printservers with Canon support + * + * The function attach is called for every device which has been found. + * + * @param attach attach function + * + * @return SANE_STATUS_GOOD - on success (even if no scanner was found) + */ +extern SANE_Status +sanei_axis_find_devices (const char **conf_devices, + SANE_Status (*attach_axis) + (SANE_String_Const devname, + SANE_String_Const makemodel, + SANE_String_Const serial, + const struct pixma_config_t * + const pixma_devices[]), + const struct pixma_config_t *const pixma_devices[]) +{ + char devname[256]; + char uri[256]; + uint8_t packet[MAX_PACKET_DATA_SIZE]; + struct sockaddr_in from; + uint16_t source_port, remote_port; + int udp_socket, num_ifaces; + + udp_socket = create_udp_socket(htonl(INADDR_ANY), &source_port); + if (udp_socket < 0) + return SANE_STATUS_IO_ERROR; + DBG(LOG_INFO, "source port=%d\n", source_port); + + /* send broadcast discover packets to all interfaces */ + num_ifaces = send_broadcasts(udp_socket, source_port); + DBG(LOG_INFO, "sent broadcasts to %d interfaces\n", num_ifaces); + + /* wait for response packets */ + while (receive_packet(udp_socket, packet, sizeof(packet), &from) != 0) { + struct axis_wimp_header *header = (void *)packet; +// struct axis_wimp_server_info *s_info = (void *)(packet + sizeof(struct axis_wimp_header)); + + DBG(LOG_INFO, "got reply from %s\n", inet_ntoa(from.sin_addr)); + /* get remote port */ + remote_port = ntohs(from.sin_port); + DBG(LOG_INFO, "remote port=%d\n", remote_port); + if (header->type != (WIMP_SERVER_INFO | WIMP_REPLY)) { + DBG(LOG_NOTICE, "Received invalid reply\n"); + continue; + } + + get_device_name(udp_socket, from.sin_addr.s_addr, remote_port, devname, sizeof(devname)); + /* construct URI */ + sprintf (uri, "%s://%s:%d", "axis", inet_ntoa(from.sin_addr), AXIS_SCAN_PORT); + + device[axis_no_devices++].addr = from.sin_addr; + attach_axis(uri, devname, inet_ntoa(from.sin_addr), pixma_devices); + } + + return SANE_STATUS_GOOD; +} + +extern SANE_Status +sanei_axis_open (SANE_String_Const devname, SANE_Int * dn) +{ + const char *uri_ip, *uri_port; + char ip[16]; + size_t ip_len; + int port = AXIS_SCAN_PORT; + struct in_addr addr; + int i; + char *username; + struct sockaddr_in address; + + DBG(LOG_INFO, "%s(%s, %d)\n", __func__, devname, *dn); + if (strncmp(devname, "axis://", 7)) { + DBG(LOG_CRIT, "Invalid protocol in devname"); + return SANE_STATUS_INVAL; + } + uri_ip = devname + 7; + + uri_port = strchr(uri_ip, ':'); + if (uri_port) { + sscanf(uri_port, ":%d", &port); + ip_len = uri_port - uri_ip; + } else + ip_len = strlen(uri_ip); + if (ip_len > sizeof(ip)) + ip_len = sizeof(ip); + strncpy(ip, uri_ip, ip_len); + ip[ip_len] = '\0'; + + if (inet_aton(ip, &addr) == 0) { + DBG(LOG_CRIT, "Invalid IP address in devname"); + return SANE_STATUS_INVAL; + } + DBG(LOG_INFO, "ip=%s, port=%d\n", inet_ntoa(addr), port); + + for (i = 0; i < axis_no_devices; i++) + if (device[i].addr.s_addr == addr.s_addr) { + DBG(LOG_INFO, "found device at position %d\n", i); + *dn = i; + /* connect */ + int tcp_socket = socket(AF_INET, SOCK_STREAM, 0); + if (tcp_socket < 0) { + perror("Unable to create TCP socket"); + return SANE_STATUS_IO_ERROR; + } + address.sin_family = AF_INET; + /* set TCP destination port and address */ + address.sin_port = htons(AXIS_SCAN_PORT); + address.sin_addr.s_addr = addr.s_addr; + if (connect(tcp_socket, (struct sockaddr *) &address, sizeof(address)) < 0) { + perror("Unable to connect"); + return SANE_STATUS_IO_ERROR; + } + DBG(LOG_INFO, "connected\n"); + + device[i].tcp_socket = tcp_socket; + + username = getusername(); + axis_send_cmd(tcp_socket, AXIS_CMD_CONNECT, username, strlen(username) + 1); + const SANE_Byte *dummy_buf[MAX_PACKET_DATA_SIZE]; + size_t dummy_len; + axis_recv(i, dummy_buf, &dummy_len); + + uint8_t timeout[] = { 0x0e, 0x01, 0x00, 0x00 }; + axis_send_cmd(tcp_socket, AXIS_CMD_UNKNOWN3, timeout, sizeof(timeout)); + axis_recv(i, dummy_buf, &dummy_len); + + axis_send_cmd(tcp_socket, AXIS_CMD_UNKNOWN, NULL, 0); + axis_recv(i, dummy_buf, &dummy_len); + + return SANE_STATUS_GOOD; + } +/*FIXME: add to table */ + return SANE_STATUS_INVAL; +} + +void +sanei_axis_close (SANE_Int dn) +{ + DBG(LOG_INFO, "%s(%d)\n", __func__, dn); +} + +extern void +sanei_axis_set_timeout (SANE_Int dn, SANE_Int timeout) +{ + DBG(LOG_INFO, "%s(%d, %d)\n", __func__, dn, timeout); + device[dn].axis_timeout = timeout; +} + +extern SANE_Status +sanei_axis_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size) +{ + int i; + DBG(LOG_INFO, "%s(%d, %p, %d)\n", __func__, dn, buffer, *size); +// uint8_t buf_read[] = { 0x40, 0x00 }; + uint16_t read_size = cpu_to_le16(*size); +// axis_send_cmd(device[dn].tcp_socket, AXIS_CMD_READ, buf_read, sizeof(buf_read)); + axis_send_cmd(device[dn].tcp_socket, AXIS_CMD_READ, &read_size, sizeof(read_size)); + axis_recv(dn, buffer, size); ////FIXME + fprintf(stderr, "sanei_axis_read_bulk: "); + for (i = 0; i < *size; i++) { + fprintf(stderr, "%02x ", buffer[i]); + } + fprintf(stderr, "\n"); + return SANE_STATUS_GOOD; +} + +extern SANE_Status +sanei_axis_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size) +{ + const SANE_Byte *dummy_buf[MAX_PACKET_DATA_SIZE]; + size_t dummy_len; + int i; + DBG(LOG_INFO, "%s(%d, %p, %d)\n", __func__, dn, buffer, *size); + fprintf(stderr, "sanei_axis_write_bulk: "); + for (i = 0; i < *size; i++) { + fprintf(stderr, "%02x ", buffer[i]); + } + fprintf(stderr, "\n"); + axis_send_cmd(device[dn].tcp_socket, AXIS_CMD_WRITE, buffer, *size); + axis_recv(dn, dummy_buf, &dummy_len); ////FIXME + return SANE_STATUS_GOOD; +} + +extern SANE_Status +sanei_axis_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size) +{ + DBG(LOG_INFO, "%s(%d, %p, %d)\n", __func__, dn, buffer, *size); + if (!device[dn].int_size) + return SANE_STATUS_EOF; + memcpy(buffer, device[dn].int_data, device[dn].int_size); + *size = device[dn].int_size; + device[dn].int_size = 0; + return SANE_STATUS_GOOD; +// return SANE_STATUS_EOF; +} diff --git a/backend/pixma/pixma_axis.h b/backend/pixma/pixma_axis.h new file mode 100644 index 000000000..224f33216 --- /dev/null +++ b/backend/pixma/pixma_axis.h @@ -0,0 +1,154 @@ +#ifndef sanei_axis_h +#define sanei_axis_h + +#include "../include/sane/config.h" +#include "../include/sane/sane.h" +#include "pixma.h" + +#ifdef HAVE_STDLIB_H +#include /* for size_t */ +#endif + +/** Initialize sanei_axis. + * + * Call this before any other sanei_axis function. + */ +extern void sanei_axis_init (void); + +/** Find scanners responding to a AXIS broadcast. + * + * The function attach is called for every device which has been found. + * Serial is the address of the scanner in human readable form of max + * SHORT_HOSTNAME_MAX characters + * @param conf_devices list of pre-configures device URI's to attach + * @param attach attach function + * @param pixma_devices device informatio needed by attach function + * + * @return SANE_STATUS_GOOD - on success (even if no scanner was found) + */ + +#define SHORT_HOSTNAME_MAX 16 + +extern SANE_Status +sanei_axis_find_devices (const char **conf_devices, + SANE_Status (*attach_axis) + (SANE_String_Const devname, + SANE_String_Const makemodel, + SANE_String_Const serial, + const struct pixma_config_t * + const pixma_devices[]), + const struct pixma_config_t *const pixma_devices[]); + +/** Open a AXIS device. + * + * The device is opened by its name devname and the device number is + * returned in dn on success. + * + * Device names consist of an URI + * Where: + * method = axis + * hostname = resolvable name or IP-address + * port = 8612 for a scanner + * An example could look like this: axis://host.domain:8612 + * + * @param devname name of the device to open + * @param dn device number + * + * @return + * - SANE_STATUS_GOOD - on success + * - SANE_STATUS_ACCESS_DENIED - if the file couldn't be accessed due to + * permissions + * - SANE_STATUS_INVAL - on every other error + */ +extern SANE_Status sanei_axis_open (SANE_String_Const devname, SANE_Int * dn); + +/** Close a AXIS device. + * + * @param dn device number + */ + +extern void sanei_axis_close (SANE_Int dn); + +/** Activate a AXIS device connection + * + * @param dn device number + */ + +extern SANE_Status sanei_axis_activate (SANE_Int dn); + +/** De-activate a AXIS device connection + * + * @param dn device number + */ + +extern SANE_Status sanei_axis_deactivate (SANE_Int dn); + +/** Set the libaxis timeout for bulk and interrupt reads. + * + * @param devno device number + * @param timeout the new timeout in ms + */ +extern void sanei_axis_set_timeout (SANE_Int devno, SANE_Int timeout); + +/** Check if sanei_axis_set_timeout() is available. + */ +#define HAVE_SANEI_AXIS_SET_TIMEOUT + +/** Initiate a bulk transfer read. + * + * Read up to size bytes from the device to buffer. After the read, size + * contains the number of bytes actually read. + * + * @param dn device number + * @param buffer buffer to store read data in + * @param size size of the data + * + * @return + * - SANE_STATUS_GOOD - on succes + * - SANE_STATUS_EOF - if zero bytes have been read + * - SANE_STATUS_IO_ERROR - if an error occured during the read + * - SANE_STATUS_INVAL - on every other error + * + */ +extern SANE_Status +sanei_axis_read_bulk (SANE_Int dn, SANE_Byte * buffer, size_t * size); + +/** Initiate a bulk transfer write. + * + * Write up to size bytes from buffer to the device. After the write size + * contains the number of bytes actually written. + * + * @param dn device number + * @param buffer buffer to write to device + * @param size size of the data + * + * @return + * - SANE_STATUS_GOOD - on succes + * - SANE_STATUS_IO_ERROR - if an error occured during the write + * - SANE_STATUS_INVAL - on every other error + */ +extern SANE_Status +sanei_axis_write_bulk (SANE_Int dn, const SANE_Byte * buffer, size_t * size); + +/** Initiate a interrupt transfer read. + * + * Read up to size bytes from the interrupt endpoint from the device to + * buffer. After the read, size contains the number of bytes actually read. + * + * @param dn device number + * @param buffer buffer to store read data in + * @param size size of the data + * + * @return + * - SANE_STATUS_GOOD - on succes + * - SANE_STATUS_EOF - if zero bytes have been read + * - SANE_STATUS_IO_ERROR - if an error occured during the read + * - SANE_STATUS_INVAL - on every other error + * + */ + +extern SANE_Status +sanei_axis_read_int (SANE_Int dn, SANE_Byte * buffer, size_t * size); + +/*------------------------------------------------------*/ +#endif /* sanei_axis_h */ diff --git a/backend/pixma/pixma_axis_private.h b/backend/pixma/pixma_axis_private.h new file mode 100644 index 000000000..96f18ca90 --- /dev/null +++ b/backend/pixma/pixma_axis_private.h @@ -0,0 +1,89 @@ +#include "../include/sane/sanei_debug.h" +#define LOG_CRIT 0 +#define LOG_NOTICE 1 +#define LOG_INFO 2 +#define LOG_DEBUG 3 +#define LOG_DEBUG2 4 +#define LOG_DEBUG3 5 + +#define AXIS_NO_DEVICES 16 + +#define AXIS_WIMP_PORT 10260 /* UDP port for discovery */ + +#define cpu_to_le16(x) (x) +#define le16_to_cpu(x) (x) + +#define WIMP_SERVER_INFO 0x24 +#define WIMP_SERVER_STATUS 0x30 +#define WIMP_REPLY (1 << 0) +struct axis_wimp_header { + uint8_t type; /* 0x24, 0x30 = request, 0x25, 0x31 = reply */ + uint8_t magic; /* 0x03 */ + uint8_t zero; +} __attribute__((__packed__)); + +#define WIMP_GET_NAME 0x02 +#define WIMP_GET_STATUS 0x03 +struct axis_wimp_get { + uint16_t port; + uint8_t magic; /* 0x02 */ + uint8_t zero; + uint8_t cmd; /* 0x02 = device name, 0x03 = device status */ + uint8_t idx; /* 0x01 = first, 0x02 = second */ +} __attribute__((__packed__)); + +#define ETHER_ADDR_LEN 6 +struct axis_wimp_server_info { + uint8_t mac[ETHER_ADDR_LEN]; /* print server MAC address */ + char name[16]; /* print server name (max. 15 chars), zero-terminated */ + uint8_t unknown2[67]; +} __attribute__((__packed__)); + +struct axis_wimp_get_reply { + uint16_t len; + uint16_t unknown; +} __attribute__((__packed__)); + +#define AXIS_SCAN_PORT 49152 /* TCP port for scan data */ + +#define AXIS_HDR_REQUEST 0x27 +#define AXIS_HDR_REPLY 0x28 + +struct axis_header { + uint8_t type; + uint32_t len; /* little endian */ +} __attribute__((__packed__)); + +#define AXIS_CMD_READ 0x01 +#define AXIS_CMD_WRITE 0x02 +#define AXIS_CMD_UNKNOWN 0x03 /* ??? */ +#define AXIS_CMD_UNKNOWN2 0x04 /* interrupt ??? only seen as reply */ +#define AXIS_CMD_CONNECT 0x10 +#define AXIS_CMD_DISCONNECT 0x11 +#define AXIS_CMD_UNKNOWN3 0x12 /* ??? */ + +struct axis_cmd { + uint8_t cmd; + uint32_t len; /* little endian */ +} __attribute__((__packed__)); + +struct axis_reply { + uint8_t cmd; + uint32_t status;/* little endian */ + uint32_t len; /* little endian */ +}__attribute__((__packed__)); + +/* + * Device information for opened devices + */ + +typedef struct device_s +{ + int open; /* connection to scanner is opened */ + int tcp_socket; /* open tcp socket for communcation to scannner */ + /* device information */ + struct in_addr addr; /* IP address of the scanner */ + int axis_timeout; /* timeout (msec) for next poll command */ + int int_size; /* size of interrupt data */ + uint8_t int_data[16]; /* interrupt data */ +} axis_device_t; diff --git a/backend/pixma/pixma_io_sanei.c b/backend/pixma/pixma_io_sanei.c index c30b4044c..2a0ff2340 100644 --- a/backend/pixma/pixma_io_sanei.c +++ b/backend/pixma/pixma_io_sanei.c @@ -53,6 +53,7 @@ #include "pixma_common.h" #include "pixma_io.h" #include "pixma_bjnp.h" +#include "pixma_axis.h" #include "../include/sane/sanei_usb.h" #include "../include/sane/sane.h" @@ -91,6 +92,7 @@ typedef struct scanner_info_t #define INT_USB 0 #define INT_BJNP 1 +#define INT_AXIS 2 static scanner_info_t *first_scanner = NULL; static pixma_io_t *first_io = NULL; @@ -127,9 +129,9 @@ attach (SANE_String_Const devname) static SANE_Status -attach_bjnp (SANE_String_Const devname, +attach_net (SANE_String_Const devname, SANE_String_Const serial, - const struct pixma_config_t *cfg) + const struct pixma_config_t *cfg, int interface) { scanner_info_t *si; @@ -142,13 +144,29 @@ attach_bjnp (SANE_String_Const devname, si->cfg = cfg; sprintf(si->serial, "%s_%s", cfg->model, serial); - si -> interface = INT_BJNP; + si -> interface = interface; si->next = first_scanner; first_scanner = si; nscanners++; return SANE_STATUS_GOOD; } +static SANE_Status +attach_bjnp (SANE_String_Const devname, + SANE_String_Const serial, + const struct pixma_config_t *cfg) +{ + return attach_net(devname, serial, cfg, INT_BJNP); +} + +static SANE_Status +attach_axis (SANE_String_Const devname, + SANE_String_Const serial, + const struct pixma_config_t *cfg) +{ + return attach_net(devname, serial, cfg, INT_AXIS); +} + static void clear_scanner_list (void) { @@ -296,6 +314,7 @@ pixma_io_init (void) { sanei_usb_init (); sanei_bjnp_init(); + sanei_axis_init(); nscanners = 0; return 0; } @@ -338,6 +357,16 @@ pixma_collect_devices (const char **conf_devices, if (! local_only) sanei_bjnp_find_devices(conf_devices, attach_bjnp, pixma_devices); + si = first_scanner; + while (j < nscanners) + { + PDBG (pixma_dbg (3, "pixma_collect_devices() found %s at %s\n", + si->cfg->name, si->devname)); + si = si->next; + j++; + + } + sanei_axis_find_devices(conf_devices, attach_axis, pixma_devices); si = first_scanner; while (j < nscanners) { @@ -378,6 +407,8 @@ pixma_connect (unsigned devnr, pixma_io_t ** handle) return PIXMA_EINVAL; if (si-> interface == INT_BJNP) error = map_error (sanei_bjnp_open (si->devname, &dev)); + else if (si-> interface == INT_AXIS) + error = map_error (sanei_axis_open (si->devname, &dev)); else error = map_error (sanei_usb_open (si->devname, &dev)); @@ -388,6 +419,8 @@ pixma_connect (unsigned devnr, pixma_io_t ** handle) { if (si -> interface == INT_BJNP) sanei_bjnp_close (dev); + else if (si -> interface == INT_AXIS) + sanei_axis_close (dev); else sanei_usb_close (dev); return PIXMA_ENOMEM; @@ -416,6 +449,8 @@ pixma_disconnect (pixma_io_t * io) return; if (io-> interface == INT_BJNP) sanei_bjnp_close (io->dev); + else if (io-> interface == INT_AXIS) + sanei_axis_close (io->dev); else sanei_usb_close (io->dev); *p = io->next; @@ -467,6 +502,11 @@ pixma_write (pixma_io_t * io, const void *cmd, unsigned len) sanei_bjnp_set_timeout (io->dev, PIXMA_BULKOUT_TIMEOUT); error = map_error (sanei_bjnp_write_bulk (io->dev, cmd, &count)); } + else if (io->interface == INT_AXIS) + { + sanei_axis_set_timeout (io->dev, PIXMA_BULKOUT_TIMEOUT); + error = map_error (sanei_axis_write_bulk (io->dev, cmd, &count)); + } else { #ifdef HAVE_SANEI_USB_SET_TIMEOUT @@ -499,6 +539,11 @@ pixma_read (pixma_io_t * io, void *buf, unsigned size) sanei_bjnp_set_timeout (io->dev, PIXMA_BULKIN_TIMEOUT); error = map_error (sanei_bjnp_read_bulk (io->dev, buf, &count)); } + else if (io-> interface == INT_AXIS) + { + sanei_axis_set_timeout (io->dev, PIXMA_BULKIN_TIMEOUT); + error = map_error (sanei_axis_read_bulk (io->dev, buf, &count)); + } else { #ifdef HAVE_SANEI_USB_SET_TIMEOUT @@ -531,6 +576,11 @@ pixma_wait_interrupt (pixma_io_t * io, void *buf, unsigned size, int timeout) sanei_bjnp_set_timeout (io->dev, timeout); error = map_error (sanei_bjnp_read_int (io->dev, buf, &count)); } + else if (io-> interface == INT_AXIS) + { + sanei_axis_set_timeout (io->dev, timeout); + error = map_error (sanei_axis_read_int (io->dev, buf, &count)); + } else { #ifdef HAVE_SANEI_USB_SET_TIMEOUT @@ -539,7 +589,8 @@ pixma_wait_interrupt (pixma_io_t * io, void *buf, unsigned size, int timeout) error = map_error (sanei_usb_read_int (io->dev, buf, &count)); } if (error == PIXMA_EIO || - (io->interface == INT_BJNP && error == PIXMA_EOF)) /* EOF is a bjnp timeout error! */ + (io->interface == INT_BJNP && error == PIXMA_EOF) || /* EOF is a bjnp timeout error! */ + (io->interface == INT_AXIS && error == PIXMA_EOF)) /* EOF is a bjnp timeout error! */ error = PIXMA_ETIMEDOUT; /* FIXME: SANE doesn't have ETIMEDOUT!! */ if (error == 0) error = count;