[Nut-upsdev] USB support for Sweex 1000 VA UPS (was: Help with

Peter van Valderen p.v.valderen at probu.nl
Wed May 23 02:18:31 UTC 2007


Hello Peter,

First off, thanks for your reply, it was very useful to me. I've
gotten a lot done so far; I'd say my driver is about 80% done

On 5/22/07, Peter Selinger <selinger at mathstat.dal.ca> wrote:

> That value that constantly fluctuates could be a voltage. Since you're
> in Europe, perhaps it's a straight decimal conversion? FC = 252, F5 =
> 245 etc? Seems a bit on the high side, as your nominal voltage is
> supposed to be 230V. It could be some other measurement value, or
> voltage on some other scale, or only a few of the bits are
> significant... In any case, it probably doesn't matter.

Tried figuring that out, didn't really get anywhere.

> What you should try, however, is what happens when the battery gets
> (1) low, and (2) empty. Aside from the "on power" flag, these are very
> important flags that most UPS's will signal. The idea is that you
> start shutting down your computer when the battery gets low.

I found out what happens when the battery goes to 'low', the following:

 01 04 02 F8 F5 FF                                 ...øõÿ

As compared to FA on battery, and FE on line. What I worked out from
this is that the 3rd bit from the 4th byte shows the power status, and
the 2nd bit shows the battery status.

What happened when the UPS shut down is a different story though. That
happened about 30 seconds after the battery went 'low', that's not a
lot to work with I suppose...

Here's what happened when the battery emptied:

002540: Bulk or Interrupt Transfer (UP), 23.05.2007 03:18:55.390
+0.718. Status: 0xc0000005

Pipe Handle: 0x8985485c (Endpoint Address: 0x81)
Get 0x0 bytes from the device

002541: Bulk or Interrupt Transfer (UP), 23.05.2007 03:18:55.390 +0.0.
Status: 0xc0007000

Pipe Handle: 0x8985485c (Endpoint Address: 0x81)
Get 0x0 bytes from the device

To be honest I haven't worked out what this means yet.

> Another important thing is to be able to shut off the UPS via a
> command. Namely, after your computer shuts down in the case of a power
> failure, you want to be able to turn the UPS power off. The idea is
> that it should stay off until the line power returns, at which point
> your computer reboots (if set to do so in the BIOS). Otherwise, you
> might end up in limbo if the power comes back before the battery runs
> out, but after your computer has shut down. If the Richcomm software
> can do a forced shutdown, you might be able to see what command it
> sends. (Perhaps not needless to say: don't plug your computer into the
> UPS during these tests. Plug it into the wall).

I found out about this as well, however, here's where it got a bit
confusing as well; in the richcomm utility there's not a lot you can
do, the only thing related is the 'UPS signal polarity' menu, in which
you can choose

AC failure: Cathode(default)/Anode
Battery voltage low: Cathode(default)/Anode
Shutdown UPS: Cathode/Anode(default)

When I change the first two around nothing gets sent to the UPS;
however the software seems to think that the AC is on when it's not,
and vice versa, same for the battery level.

When I change the shutdown to cathode when the UPS is not powered, it
will shut down immediately, however it can still be polled. When this
change is made with the power on, nothing seems to happen to the UPS.
The following packet gets sent:

002612: Class-Specific Request (DOWN), 23.05.2007 02:58:40.609 +0.078
Destination: Interface, Index 0
Reserved Bits: 34
Request: 0x9
Value: 0x200
Send 0x4 bytes to the device

 02 01 00 00                                       ....

When the polarity is on anode and the power is off, it's no longer
possible to turn the UPS back on with the button. When it is switched
to cathode again when the power is off, it's possible to turn it back
on manually. When it is switched to cathode and the power is, the UPS
will power itself back on.

The following packet gets sent (headers the same as the previous packet):

 02 00 00 00                                       ....

Now I don't know if this is the normal way it works, it just seems a
little confusing to me.

> NUT uses libusb (http://libusb.sourceforge.net/doc/) for all USB
> functions. Unfortunately, the interface is badly documented and
> underspecified, and impossible to read without the USB specification.
>
> Good luck! -- Peter

Well as I said I think I have most of it down now, I only just
finished so I haven't been able to test it 'live'; I have just been
running the driver with

cd ..;make;cd drivers;./sweex_usb -u root auto

Which seems to work. Basically I made my own standalone driver that
uses nut_usb.c, I had to make two small patches to that to get it to
work. I worked on the 2.1.0 SVN.

The changes for nut_usb entail putting the productid/vendorid in, and
detaching the kernel driver that somehow claims the USB device.

I hope it's okay posting them here, sorry if it isn't!

Hopefully there are some things I can do to finalise the driver and
maybe include it in the SVN?

Regards,
Peter

===========================================

Diff for nut_usb.c:

===========================================
59,65c59,63
<                         if ((dev->descriptor.bDeviceClass ==
USB_CLASS_PER_INTERFACE &&
<                             (dev->descriptor.idVendor == 0x0592 ||
<                              dev->descriptor.idVendor == 0x06da) &&
<                             dev->descriptor.idProduct == 0x0002) ||
<                             (dev->descriptor.idVendor == 0x0925 &&
<                              dev->descriptor.idProduct == 0x1234))
<                                 return usb_open(dev);
---
>                       if (dev->descriptor.bDeviceClass == USB_CLASS_PER_INTERFACE &&
>                           (dev->descriptor.idVendor == 0x0592 ||
>                            dev->descriptor.idVendor == 0x06da) &&
>                           dev->descriptor.idProduct == 0x0002)
>                               return usb_open(dev);
106,124d103
< #if LIBUSB_HAS_DETACH_KRNL_DRV
<         /* this method requires at least libusb 0.1.8:
<          * it force device claiming by unbinding
<          * attached driver... From libhid */
<         retry = 3;
<         while (usb_set_configuration(dev_h, 1) != 0 && retry-- > 0) {
<
<                 upsdebugx(2, "failed to claim USB device, trying %d
more time(s)...", retry);
<
<                 upsdebugx(2, "detaching kernel driver from USB device...");
<                 if (usb_detach_kernel_driver_np(dev_h, 0) < 0) {
<                         upsdebugx(2, "failed to detach kernel driver
from USB device...");
<                 }
<
<                 upsdebugx(2, "trying again to claim USB device...");
<         }
< #endif
<
< /*
130d108
< */
===========================================

sweex_usb.h:

===========================================

/* sweex_usb.h - driver for newer Sweex models, such as 'Sweex
   Manageable UPS 1000VA' (ca. 2006)

   May also work on 'Kebo UPS-650D', not tested as of 05/23/2007

   Copyright (C) 2007 Peter van Valderen <p.v.valderen at probu.nl>
                      Dirk Teurlings <dirk at upexia.nl>

   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
*/

/* driver version */
#define DRV_VERSION     "0.01"

/* driver definitions */
#define STATUS_REQUESTTYPE      0x21
#define REPLY_REQUESTTYPE       0x81
#define REPLY_PACKETSIZE        6
#define REQUEST_VALUE           0x09
#define MESSAGE_VALUE           0x200
#define INDEX_VALUE             0

===========================================

sweex_usb.c

===========================================
/* sweex_usb.h - driver for newer Sweex models, such as 'Sweex
   Manageable UPS 1000VA' (ca. 2006)

   May also work on 'Kebo UPS-650D', not tested as of 05/23/2007

   Copyright (C) 2007 Peter van Valderen <p.v.valderen at probu.nl>
                      Dirk Teurlings <dirk at upexia.nl>

   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 "nut_usb.h"
#include "sweex_usb.h"

usb_dev_handle *upsdev = NULL;

int query_ups (unsigned char *reply) {
        unsigned char buf[4];
        int ret;

        /*
         * This packet is a status request to the UPS
         */
        buf[0]=0x01;
        buf[1]=0x00;
        buf[2]=0x00;
        buf[3]=0x30;

        return execute_and_retrieve_query(buf, reply);
}

int execute_and_retrieve_query(unsigned char *query, unsigned char *reply) {
        int ret;

        ret = usb_control_msg(upsdev, STATUS_REQUESTTYPE, REQUEST_VALUE,
                MESSAGE_VALUE, INDEX_VALUE, query, sizeof(query), 1000);

        if (ret < 0) {
            return ret;
        }

        ret = usb_interrupt_read(upsdev, REPLY_REQUESTTYPE, reply,
sizeof(REPLY_PACKETSIZE), 1000);

        return ret;
}

void upsdrv_comm_good()
{
        nutusb_comm_good();
}

/*
 * Initialise the UPS
 */

void upsdrv_initups(void)
{
        unsigned char reply[REPLY_PACKETSIZE];
        int i;

        upsdev = nutusb_open("USB");

        /*
         * Read rubbish data a few times; the UPS doesn't seem to
respond properly
         * the first few times after connecting
         */

        for (i=0;i<5;i++) {
                query_ups(reply);
                sleep(1);
        }
}

void upsdrv_cleanup(void)
{
        upslogx(LOG_ERR, "CLOSING\n");
        nutusb_close(upsdev, "USB");
}

void upsdrv_reconnect(void)
{

        upslogx(LOG_WARNING, "RECONNECT USB DEVICE\n");
        nutusb_close(upsdev, "USB");
        upsdev = NULL;
        sleep(3);
        upsdrv_initups();
}

void upsdrv_initinfo(void)
{
        dstate_setinfo("driver.version.internal", "%s", DRV_VERSION);

        dstate_setinfo("ups.mfr", "Sweex");
        dstate_setinfo("ups.model","Unknown");
}

void upsdrv_updateinfo(void)
{
        unsigned char reply[REPLY_PACKETSIZE];
        int ret, online, battery_normal;

        unsigned char test;

        ret = query_ups(reply);

        if (ret < 0) {
                upslog_with_errno(LOG_INFO, "Query to UPS failed");
                dstate_datastale();
                return;
        }

        /*
         * 3rd bit of 4th byte indicates whether the UPS is on line (1)
         * or on battery (0)
         */
        online = (reply[3]&4)>>2;

        /*
         * 2nd bit of 4th byte indicates battery status; normal (1)
         * or low (0)
         */
        battery_normal = (reply[3]&2)>>1;

        status_init();

        if (online) {
            status_set("OL");
        }
        else {
            status_set("OB");
        }

        if (!battery_normal) {
            status_set("LB");
        }

        status_commit();
        dstate_dataok();
}

/*
 * The shutdown feature is a bit strange on this UPS IMHO, it
 * switches the polarity of the 'Shutdown UPS' signal, at which
 * point it will automatically power down once it loses power.
 *
 * It will still, however, be possible to poll the UPS and
 * reverse the polarity _again_, at which point it will
 * start back up once power comes back.
 *
 * Maybe this is the normal way, it just seems a bit strange.
 */
void upsdrv_shutdown(void)
{
        unsigned char buf[4];
        int ret;

        /*
         * This packet shuts down the UPS, that is, if it is
         * not currently on line power
         */

        buf[0]=0x02;
        buf[1]=0x00;
        buf[2]=0x00;
        buf[3]=0x00;

        execute_and_retrieve_query(buf, reply);
}

void upsdrv_help(void)
{
}

void upsdrv_makevartable(void)
{
}

void upsdrv_banner(void)
{
        printf("Network UPS Tools - Sweex USB UPS driver %s (%s)\n\n",
                DRV_VERSION, UPS_VERSION);
}
===========================================



More information about the Nut-upsdev mailing list