Hi Rodolfo,

[please reply to <nut-upsdev@lists.alioth.debian.org>, not nut-upsdev-owner, 
and if you have large attachments, upload them to a web server and post the 
URL. Thanks!]

On Jan 29, 2014, at 7:59 AM, rodo...@tsshara.com.br wrote:

> <blazer_usb.c.txt>
> Sorry, we forgot to send another file. We developed this code 
> (blazer_usb.c.txt) so that your software is able to communicate with our 
> hardware, because our hardware is not able to create a Virtual COM. In order 
> to do that, the file attached will perform the job of making our UPS 
> communicate with a Linux platform. 

Which version of NUT did you modify?

In the current version of NUT, we have an improved driver for Megatec Qx 
protocols called nutdrv_qx. Your modifications should work in the new driver, 
but we need to know what version of blazer_usb.c you started with.

> I am developing the possibility for the company I work called TS Shara, that 
> produces nobreaks (UPS) in São Paulo, Brazil, to use your software (NUT) in 
> order for our nobreaks (UPS) start working with a Linux platform/ 
> environment. The objective is to achieve a control/monitoring system for our 
> nobreaks (UPS), especially when they exchange information with a Linux 
> platform.  
> 
> We are really interested and pleased with the software results. However, it 
> was necessary to use a driver (tsshara.tar.gz) in order to work with our 
> nobreaks (UPS). This occurs because our USB hardware (located in our UPS) 
> does not create a Virtual Com when the UPS is connected to the computer.

Correct, all of the USB sub-drivers in blazer_usb and nutdrv_qx use libusb 
directly, and do not need a Virtual COM (/dev/ttyUSB*) port kernel driver. 

> We would like to know if it is possible to input the sub-routine 
> "sgs_command" located in blazer_usb.c.txt in the nut code? So that any user 
> is able to download and install the software directly from the NUT site, 
> without any other additional file like the one attached.

This should not be too hard. Is the major difference from cypress_command() 
that the commands are transferred in 7-byte chunks instead of 8-byte?

> We would also like that the software interface displaying all the status of 
> the UPS is fully written in Portuguese. Is that possible?

In order to keep the network protocol and drivers simple, the status is not 
translated. However, clients such as the Python NUT-Monitor script, the CGI 
programs, wmnut and WinNUT could be translated.

NUT-Monitor has already been translated into Italian and French: 
scripts/python/app/locale/

https://github.com/networkupstools/nut/tree/master/scripts/python/app/locale/

> We are available to help in order to solve any problem that might happen.

Good to know, thanks! We look forward to working with you.

> <tsshara.tar.gz>

I looked at these binaries, and they appear to be old (NUT v2.6.3). I think it 
would be better if we merged the source code.

Also, we do not mind if you distribute binaries of NUT, but the GPL requires 
you to provide a way to get the source code you used to build that version. In 
this case, if you are distributing tsshara.tar.gz, you could do this by:

 * including all of the NUT source code in that archive,
 * include a patch file, and say where to get the base version of NUT, or
 * provide a link to the modified source code

http://www.gnu.org/licenses/gpl-faq.html#GPLRequireSourcePostedPublic

-- 
Charles Lepple
clepple@gmail


/*
 * blazer_usb.c: support for Megatec/Q1 USB protocol based UPSes
 *
 * A document describing the protocol implemented by this driver can be
 * found online at "http://www.networkupstools.org/protocols/megatec.html";.
 *
 * Copyright (C) 2003-2009  Arjen de Korte <adkorte-gu...@alioth.debian.org>
 * Copyright (C) 2011-2012  Arnaud Quette <arnaud.que...@free.fr>
 *
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include "main.h"
#include "libusb.h"
#include "usb-common.h"
#include "blazer.h"
#include <syslog.h>

#define DRIVER_NAME     "Megatec/Q1 protocol USB driver"
#define DRIVER_VERSION  "0.09"

/* driver description structure */
upsdrv_info_t upsdrv_info = {
        DRIVER_NAME,
        DRIVER_VERSION,
        "Arjen de Korte <adkorte-gu...@alioth.debian.org>\n" \
        "Arnaud Quette <arnaud.que...@free.fr>",
        DRV_BETA,
        { NULL }
};

static usb_communication_subdriver_t *usb = &usb_subdriver;
static usb_dev_handle           *udev = NULL;
static USBDevice_t              usbdevice;
static USBDeviceMatcher_t       *reopen_matcher = NULL;
static USBDeviceMatcher_t       *regex_matcher = NULL;
static int                                      langid_fix = -1;

static int (*subdriver_command)(const char *cmd, char *buf, size_t buflen) = 
NULL;

static int sgs_command(const char *cmd, char *buf, size_t buflen)
{
        char    tmp[SMALLBUF];
        int     ret;
        size_t  n,i;

        n = strlen(cmd);
        for (i < 0; i < n; i += ret-1 ) {
                memset(tmp, 0, 8);
                ret = MIN(7,n-i);
                tmp[0]=ret;
                memcpy(&tmp[1],&cmd[i],ret);
                /* Write data in 8-byte chunks */
                ret = usb_control_msg(udev, USB_ENDPOINT_OUT | USB_TYPE_CLASS | 
USB_RECIP_INTERFACE,
                        0x09, 0x200, 0, tmp, 8, 500);

                if (ret <= 0) {
                        upsdebugx(3, "send: %s", ret ? usb_strerror() : 
"timeout");
                        return ret;
                }
                ret--;
        }

        upsdebugx(3, "send: %s", cmd);

        memset(buf, 0, buflen);
        for (i = 0; (i <= buflen-8) ; i += ret) {
                /* Read data in 8-byte chunks */
                ret = usb_interrupt_read(udev, 0x81, tmp, 8, 500);
                if(ret == -110){ /* No error!!! */
                        break;
                }
                /*
                 * Any errors here mean that we are unable to read a reply 
(which
                 * will happen after successfully writing a command to the UPS)
                 */
                if (ret <= 0) {
                        upsdebugx(3, "read: %s", ret ? usb_strerror() : 
"timeout" );
                        return ret;
                }
                /* every call to read returns 8 byte */
                ret = tmp[0]; /* effective returned bytes */
                if (ret > 0) {
                        memcpy(&buf[i],&tmp[1],ret);
                }
        }
        upsdebugx(3, "read: %s", buf);
        return i;
}

static int cypress_command(const char *cmd, char *buf, size_t buflen)
{
        char    tmp[SMALLBUF];
        int     ret;
        size_t  i;

        memset(tmp, 0, sizeof(tmp));
        snprintf(tmp, sizeof(tmp), "%s", cmd);

        for (i = 0; i < strlen(tmp); i += ret) {

                /* Write data in 8-byte chunks */
                /* ret = usb->set_report(udev, 0, (unsigned char *)&tmp[i], 8); 
*/
                ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + 
USB_RECIP_INTERFACE,
                        0x09, 0x200, 0, &tmp[i], 8, 5000);

                if (ret <= 0) {
                        upsdebugx(3, "send: %s", ret ? usb_strerror() : 
"timeout");
                        return ret;
                }
        }

        upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp);

        memset(buf, 0, buflen);

        for (i = 0; (i <= buflen-8) && (strchr(buf, '\r') == NULL); i += ret) {

                /* Read data in 8-byte chunks */
                /* ret = usb->get_interrupt(udev, (unsigned char *)&buf[i], 8, 
1000); */
                ret = usb_interrupt_read(udev, 0x81, &buf[i], 8, 1000);

                /*
                 * Any errors here mean that we are unable to read a reply 
(which
                 * will happen after successfully writing a command to the UPS)
                 */
                if (ret <= 0) {
                        upsdebugx(3, "read: %s", ret ? usb_strerror() : 
"timeout");
                        return ret;
                }
        }

        upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
        return i;
}


static int phoenix_command(const char *cmd, char *buf, size_t buflen)
{
        char    tmp[SMALLBUF];
        int     ret;
        size_t  i;

        for (i = 0; i < 8; i++) {

                /* Read data in 8-byte chunks */
                /* ret = usb->get_interrupt(udev, (unsigned char *)tmp, 8, 
1000); */
                ret = usb_interrupt_read(udev, 0x81, tmp, 8, 1000);

                /*
                 * This USB to serial implementation is crappy. In order to 
read correct
                 * replies we need to flush the output buffers of the converter 
until we
                 * get no more data (ie, it times out).
                 */
                switch (ret)
                {
                case -EPIPE:            /* Broken pipe */
                        usb_clear_halt(udev, 0x81);
                case -ETIMEDOUT:        /* Connection timed out */
                        break;
                }

                if (ret < 0) {
                        upsdebugx(3, "flush: %s", usb_strerror());
                        break;
                }

                upsdebug_hex(4, "dump", tmp, ret);
        }

        memset(tmp, 0, sizeof(tmp));
        snprintf(tmp, sizeof(tmp), "%s", cmd);

        for (i = 0; i < strlen(tmp); i += ret) {

                /* Write data in 8-byte chunks */
                /* ret = usb->set_report(udev, 0, (unsigned char *)&tmp[i], 8); 
*/
                ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + 
USB_RECIP_INTERFACE,
                        0x09, 0x200, 0, &tmp[i], 8, 1000);

                if (ret <= 0) {
                        upsdebugx(3, "send: %s", ret ? usb_strerror() : 
"timeout");
                        return ret;
                }
        }

        upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp);

        memset(buf, 0, buflen);

        for (i = 0; (i <= buflen-8) && (strchr(buf, '\r') == NULL); i += ret) {

                /* Read data in 8-byte chunks */
                /* ret = usb->get_interrupt(udev, (unsigned char *)&buf[i], 8, 
1000); */
                ret = usb_interrupt_read(udev, 0x81, &buf[i], 8, 1000);

                /*
                 * Any errors here mean that we are unable to read a reply 
(which
                 * will happen after successfully writing a command to the UPS)
                 */
                if (ret <= 0) {
                        upsdebugx(3, "read: %s", ret ? usb_strerror() : 
"timeout");
                        return ret;
                }
        }

        upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
        return i;
}


static int ippon_command(const char *cmd, char *buf, size_t buflen)
{
        char    tmp[64];
        int     ret;
        size_t  i;

        snprintf(tmp, sizeof(tmp), "%s", cmd);

        for (i = 0; i < strlen(tmp); i += ret) {

                /* Write data in 8-byte chunks */
                ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + 
USB_RECIP_INTERFACE,
                        0x09, 0x2, 0, &tmp[i], 8, 1000);

                if (ret <= 0) {
                        upsdebugx(3, "send: %s", (ret != -ETIMEDOUT) ? 
usb_strerror() : "Connection timed out");
                        return ret;
                }
        }

        upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp);

        /* Read all 64 bytes of the reply in one large chunk */
        ret = usb_interrupt_read(udev, 0x81, tmp, sizeof(tmp), 1000);

        /*
         * Any errors here mean that we are unable to read a reply (which
         * will happen after successfully writing a command to the UPS)
         */
        if (ret <= 0) {
                upsdebugx(3, "read: %s", (ret != -ETIMEDOUT) ? usb_strerror() : 
"Connection timed out");
                return ret;
        }

        snprintf(buf, buflen, "%.*s", ret, tmp);

        upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
        return ret;
}


static int krauler_command(const char *cmd, char *buf, size_t buflen)
{
        /*
         * Still not implemented:
         * 0x6  T<n>    (don't know how to pass the parameter)
         * 0x68 and 0x69 both cause shutdown after an undefined interval
        */
        const struct {
                const char      *str;           /* Megatec command */
                const int       index;  /* Krauler string index for this 
command */
                const char      prefix; /* character to replace the first byte 
in reply */
        }  command[] = {
                { "Q1\r", 0x03, '(' },
                { "F\r",  0x0d, '#' },
                { "I\r",  0x0c, '#' },
                { "T\r",  0x04, '\r' },
                { "TL\r", 0x05, '\r' },
                { "Q\r",  0x07, '\r' },
                { "C\r",  0x0b, '\r' },
                { "CT\r", 0x0b, '\r' },
                { NULL }
        };

        int     i;

        upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd);

        for (i = 0; command[i].str; i++) {
                int     retry;

                if (strcmp(cmd, command[i].str)) {
                        continue;
                }

                for (retry = 0; retry < 10; retry++) {
                        int     ret;

                        if (langid_fix != -1) {
                                /* Apply langid_fix value */
                                ret = usb_get_string(udev, command[i].index, 
langid_fix, buf, buflen);
                        }
                        else {
                                ret = usb_get_string_simple(udev, 
command[i].index, buf, buflen);
                        }

                        if (ret <= 0) {
                                upsdebugx(3, "read: %s", ret ? usb_strerror() : 
"timeout");
                                return ret;
                        }

                        /* this may serve in the future */
                        upsdebugx(1, "received %d (%d)", ret, buf[0]);

                        if (langid_fix != -1) {
                                /* Limit this check, at least for now */
                                /* Invalid receive size - message corrupted */
                                if (ret != buf[0]) 
                                {
                                        upsdebugx(1, "size mismatch: %d / %d", 
ret, buf[0]);
                                        continue;
                                }

                                /* Simple unicode -> ASCII inplace conversion
                                 * FIXME: this code is at least shared with 
mge-shut/libshut
                                 * Create a common function? */
                                unsigned int di, si, size = buf[0];
                                for (di = 0, si = 2; si < size; si += 2) {
                                        if (di >= (buflen - 1))
                                                break;

                                        if (buf[si + 1]) /* high byte */
                                                buf[di++] = '?';
                                        else
                                                buf[di++] = buf[si];
                                }
                                buf[di] = 0;
                                ret = di;
                        }

                        /* "UPS No Ack" has a special meaning */
                        if (!strcasecmp(buf, "UPS No Ack")) {
                                upsdebugx(3, "read: %.*s", (int)strcspn(buf, 
"\r"), buf);
                                continue;
                        }

                        /* Replace the first byte of what we received with the 
correct one */
                        buf[0] = command[i].prefix;

                        upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), 
buf);
                        return ret;
                }

                return 0;
        }

        /* echo the unknown command back */
        upsdebugx(3, "read: %.*s", (int)strcspn(cmd, "\r"), cmd);
        return snprintf(buf, buflen, "%s", cmd);
}

static void *sgs_subdriver(void)
{
        subdriver_command = &sgs_command;
        return NULL;
}

static void *cypress_subdriver(void)
{
        subdriver_command = &cypress_command;
        return NULL;
}


static void *ippon_subdriver(void)
{
        subdriver_command = &ippon_command;
        return NULL;
}


static void *krauler_subdriver(void)
{
        subdriver_command = &krauler_command;
        return NULL;
}


static void *phoenix_subdriver(void)
{
        subdriver_command = &phoenix_command;
        return NULL;
}


static usb_device_id_t blazer_usb_id[] = {
        { USB_DEVICE(0x05b8, 0x0000), &cypress_subdriver },     /* Agiler UPS */
        { USB_DEVICE(0x0001, 0x0000), &krauler_subdriver },     /* Krauler 
UP-M500VA */
        { USB_DEVICE(0xffff, 0x0000), &krauler_subdriver },     /* Ablerex 625L 
USB */
        { USB_DEVICE(0x0665, 0x5161), &cypress_subdriver },     /* Belkin 
F6C1200-UNV */
        { USB_DEVICE(0x06da, 0x0002), &cypress_subdriver },     /* Online Yunto 
YQ450 */
        { USB_DEVICE(0x06da, 0x0003), &ippon_subdriver },       /* Mustek 
Powermust */
        { USB_DEVICE(0x06da, 0x0004), &cypress_subdriver },     /* Phoenixtec 
Innova 3/1 T */
        { USB_DEVICE(0x06da, 0x0005), &cypress_subdriver },     /* Phoenixtec 
Innova RT */
        { USB_DEVICE(0x06da, 0x0201), &cypress_subdriver },     /* Phoenixtec 
Innova T */
        { USB_DEVICE(0x06da, 0x0601), &phoenix_subdriver },     /* Online Zinto 
A */
        { USB_DEVICE(0x0f03, 0x0001), &cypress_subdriver },     /* Unitek Alpha 
1200Sx */
        { USB_DEVICE(0x14f0, 0x00c9), &phoenix_subdriver },     /* GE EP series 
*/
        { USB_DEVICE(0x0483, 0x0035), &sgs_subdriver },         /* TS Shara UPS 
*/
        /* end of list */
        {-1, -1, NULL}
};


static int device_match_func(USBDevice_t *hd, void *privdata)
{
        if (subdriver_command) {
                return 1;
        }

        switch (is_usb_device_supported(blazer_usb_id, hd->VendorID, 
hd->ProductID))
        {
        case SUPPORTED:
                return 1;

        case POSSIBLY_SUPPORTED:
        case NOT_SUPPORTED:
        default:
                return 0;
        }
}


static USBDeviceMatcher_t device_matcher = {
        &device_match_func,
        NULL,
        NULL
};


/*
 * Generic command processing function. Send a command and read a reply.
 * Returns < 0 on error, 0 on timeout and the number of bytes read on
 * success.
 */
int blazer_command(const char *cmd, char *buf, size_t buflen)
{
#ifndef TESTING
        int     ret;

        if (udev == NULL) {
                ret = usb->open(&udev, &usbdevice, reopen_matcher, NULL);

                if (ret < 1) {
                        return ret;
                }
        }

        ret = (*subdriver_command)(cmd, buf, buflen);
        if (ret >= 0) {
                return ret;
        }

        switch (ret)
        {
        case -EBUSY:            /* Device or resource busy */
                fatal_with_errno(EXIT_FAILURE, "Got disconnected by another 
driver");

        case -EPERM:            /* Operation not permitted */
                fatal_with_errno(EXIT_FAILURE, "Permissions problem");

        case -EPIPE:            /* Broken pipe */
                if (usb_clear_halt(udev, 0x81) == 0) {
                        upsdebugx(1, "Stall condition cleared");
                        break;
                }
#ifdef ETIME
        case -ETIME:            /* Timer expired */
#endif
                if (usb_reset(udev) == 0) {
                        upsdebugx(1, "Device reset handled");
                }
        case -ENODEV:           /* No such device */
        case -EACCES:           /* Permission denied */
        case -EIO:              /* I/O error */
        case -ENXIO:            /* No such device or address */
        case -ENOENT:           /* No such file or directory */
                /* Uh oh, got to reconnect! */
                usb->close(udev);
                udev = NULL;
                break;

        case -ETIMEDOUT:        /* Connection timed out */
        case -EOVERFLOW:        /* Value too large for defined data type */
        case -EPROTO:           /* Protocol error */
        default:
                break;
        }

        return ret;
#else
        const struct {
                const char      *command;
                const char      *answer;
        } testing[] = {
                { "Q1\r", "(215.0 195.0 230.0 014 49.0 2.27 30.0 00101000\r" },
                { "F\r",  "#230.0 000 024.0 50.0\r" },
                { "I\r",  "#-------------   ------     VT12046Q  \r" },
                { NULL }
        };

        int     i;

        memset(buf, 0, buflen);

        for (i = 0; testing[i].command; i++) {

                if (strcasecmp(cmd, testing[i].command)) {
                        continue;
                }

                return snprintf(buf, buflen, "%s", testing[i].answer);
        }

        return snprintf(buf, buflen, "%s", testing[i].command);
#endif
}


void upsdrv_help(void)
{
        printf("Read The Fine Manual ('man 8 blazer')\n");
}


void upsdrv_makevartable(void)
{
        addvar(VAR_VALUE, "subdriver", "Serial-over-USB subdriver selection");
        addvar(VAR_VALUE, "vendorid", "Regular expression to match UPS 
Manufacturer numerical ID (4 digits hexadecimal)");
        addvar(VAR_VALUE, "productid", "Regular expression to match UPS Product 
numerical ID (4 digits hexadecimal)");

        addvar(VAR_VALUE, "vendor", "Regular expression to match UPS 
Manufacturer string");
        addvar(VAR_VALUE, "product", "Regular expression to match UPS Product 
string");
        addvar(VAR_VALUE, "serial", "Regular expression to match UPS Serial 
number");

        addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name");

        addvar(VAR_VALUE, "langid_fix", "Apply the language ID workaround to 
the krauler subdriver (0x409 or 0x4095)");

        blazer_makevartable();
}


void upsdrv_initups(void)
{
#ifndef TESTING
        const struct {
                const char      *name;
                int             (*command)(const char *cmd, char *buf, size_t 
buflen);
        } subdriver[] = {
                { "cypress", &cypress_command },
                { "phoenix", &phoenix_command },
                { "ippon", &ippon_command },
                { "krauler", &krauler_command },
                { "sgs", &sgs_command },
                { NULL }
        };

        int     ret, langid;
        char    tbuf[255]; /* Some devices choke on size > 255 */
        char    *regex_array[6];

        char    *subdrv = getval("subdriver");

        regex_array[0] = getval("vendorid");
        regex_array[1] = getval("productid");
        regex_array[2] = getval("vendor");
        regex_array[3] = getval("product");
        regex_array[4] = getval("serial");
        regex_array[5] = getval("bus");

        /* check for language ID workaround (#1) */
        if (getval("langid_fix")) {
                /* skip "0x" prefix and set back to hexadecimal */
                if (sscanf(getval("langid_fix") + 2, "%x", &langid_fix) != 1) {
                        upslogx(LOG_NOTICE, "Error enabling language ID 
workaround");
                }
                else {
                        upsdebugx(2, "language ID workaround enabled (using 
'0x%x')", langid_fix);
                }
        }

        /* pick up the subdriver name if set explicitly */
        if (subdrv) {
                int     i;

                if (!regex_array[0] || !regex_array[1]) {
                        fatalx(EXIT_FAILURE, "When specifying a subdriver, 
'vendorid' and 'productid' are mandatory.");
                }

                for (i = 0; subdriver[i].name; i++) {

                        if (strcasecmp(subdrv, subdriver[i].name)) {
                                continue;
                        }

                        subdriver_command =  subdriver[i].command;
                        break;
                }

                if (!subdriver_command) {
                        fatalx(EXIT_FAILURE, "Subdriver \"%s\" not found!", 
subdrv);
                }
        }

        ret = USBNewRegexMatcher(&regex_matcher, regex_array, REG_ICASE | 
REG_EXTENDED);
        switch (ret)
        {
        case -1:
                fatal_with_errno(EXIT_FAILURE, "USBNewRegexMatcher");
        case 0:
                break;  /* all is well */
        default:
                fatalx(EXIT_FAILURE, "invalid regular expression: %s", 
regex_array[ret]);
        }


        /* link the matchers */
        regex_matcher->next = &device_matcher;

        ret = usb->open(&udev, &usbdevice, regex_matcher, NULL);
        if (ret < 0) {
                fatalx(EXIT_FAILURE,
                        "No supported devices found. Please check your device 
availability with 'lsusb'\n"
                        "and make sure you have an up-to-date version of NUT. 
If this does not help,\n"
                        "try running the driver with at least 'subdriver', 
'vendorid' and 'productid'\n"
                        "options specified. Please refer to the man page for 
details about these options\n"
                        "(man 8 blazer).\n");
        }

        if (!subdriver_command) {
                fatalx(EXIT_FAILURE, "No subdriver selected");
        }

        /* create a new matcher for later reopening */
        ret = USBNewExactMatcher(&reopen_matcher, &usbdevice);
        if (ret) {
                fatal_with_errno(EXIT_FAILURE, "USBNewExactMatcher");
        }

        /* link the matchers */
        reopen_matcher->next = regex_matcher;

        dstate_setinfo("ups.vendorid", "%04x", usbdevice.VendorID);
        dstate_setinfo("ups.productid", "%04x", usbdevice.ProductID);

        /* check for language ID workaround (#2) */
        if (langid_fix != -1) {
                /* Future improvement:
                 *   Asking for the zero'th index is special - it returns a 
string
                 *   descriptor that contains all the language IDs supported by 
the
                 *   device. Typically there aren't many - often only one. The
                 *   language IDs are 16 bit numbers, and they start at the 
third byte
                 *   in the descriptor. See USB 2.0 specification, section 
9.6.7, for
                 *   more information on this.
                 * This should allow automatic application of the workaround */
                ret = usb_get_string(udev, 0, 0, tbuf, sizeof(tbuf));
                if (ret >= 4) {
                        langid = tbuf[2] | (tbuf[3] << 8);
                        upsdebugx(1, "First supported language ID: 0x%x (please 
report to the NUT maintainer!)", langid);
                }
        }
#endif
        blazer_initups();
}


void upsdrv_initinfo(void)
{
        blazer_initinfo();
}


void upsdrv_cleanup(void)
{
#ifndef TESTING
        usb->close(udev);
        USBFreeExactMatcher(reopen_matcher);
        USBFreeRegexMatcher(regex_matcher);
        free(usbdevice.Vendor);
        free(usbdevice.Product);
        free(usbdevice.Serial);
        free(usbdevice.Bus);
#endif
}
_______________________________________________
Nut-upsdev mailing list
Nut-upsdev@lists.alioth.debian.org
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/nut-upsdev

Reply via email to