Попался мне в руки бесперебойник UPS FSP EP1000. Хорошая вещь, два аккумулятора, но вот софт - супер комбайн на java. А мне всего то и надо, состояние о питании дернуть и команду на отключение послать. Два интерфейса rs232 (2400 8N1) и usb (hiddev). На любезную просьбу выслать протокол фирма не отреагировала, ну да и ладно. Аппаратным снифером последовательно порта были получены знакомые команды (еще Ippon с такими идет, более того при подключении usb idVedor = 0665, idProduct = 5161 что тоже совпадает Описание Serial протокола IPPON (протокол Megatek для COM порта
http://www.networkupstools.org/protocols/megatec.html). Но присутствуют некоторые отличия:
запрос состояния в указанном протоколе Q1, а в этом девайсе QS (снифер рулит!). Что еще работает:
"F", "C", "Q", "T", "SnnRmmmm" - совпадает с описанным протоколом
Байтполя состояния кажись тоже совпадают. Еще интересно "D" - но что делает не разбирался, не зачем.
Так что в простейшем случае RS232 отправляем "QS" и получаем всю необходимую инфу. В случае hiddev
чуть сложнее, но похожие исходники где-то есть в сети (clear_report, send_report, recive_report в этом духе, ответ в виде 46 байт состояния)
Не буду жадничать, отдаю как есть. Компилируется и запускается.
ups_new -F /dev/usb/hiddev1 -V 1637 -P 20833
цифры - это соответственно вендор и продукт в десятичной системе. Кому надо, допилите до лучшей кондиции.
================================== ups_new.c =================================================
/*
* 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 <stdlib.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <asm/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/hiddev.h>
#include <string.h>
#include <signal.h>
#define TIMEOUT 2 /* это таймаут получения инфо, т.к. прожка может висеть долго */
int fd;
int vend;
int prod;
int verb;
int debug;
void alarm_handler(int signo) {
printf("timeout\n");
fprintf(stderr,"timeout\n");
//if (verb==1) printf("timeout");
//if (debug==1) fprintf(stderr,"timeout\n");
exit(0);
}
//отправляем репорт - буфер и длинна значащих значений
static int send_report(const char *buf, int size)
{
struct hiddev_report_info rinfo;
struct hiddev_usage_ref uref;
int i,ret,err;
if (verb==1) printf("Sending report...");
if (debug==1) fprintf(stderr,"Sending report ...\n");
//заполняем репорт значениями
for (i=0; i<8; i++) {
memset(&uref, 0, sizeof(uref));
uref.report_type = HID_REPORT_TYPE_OUTPUT;
uref.report_id = 0x00;
uref.field_index= 0;
uref.usage_index= i; //байт за байтом из буфера- остальные делаем нулями
uref.usage_code = 0xff000002;
uref.value=0;
if (i < size ) uref.value=buf[i];
err=ioctl(fd, HIDIOCSUSAGE, &uref);
if (err<0 ) return err;
}
memset(&rinfo, 0, sizeof(rinfo));
rinfo.report_type = HID_REPORT_TYPE_OUTPUT;
rinfo.report_id = 0x00;
rinfo.num_fields = 1;
//отправляем репорт
err = ioctl(fd, HIDIOCSREPORT,&rinfo);
if (verb==1) printf(" Done\n");
if (debug==1) fprintf(stderr,"Sending report - Dione\n");
return err;
}
//стартовый запрос инфо об устройстве и сравнение с заданым Вендором и продуктом
static int start_req(int type)
{
struct hiddev_devinfo device_info;
ioctl(fd, HIDIOCGDEVINFO, &device_info);
if (verb==1) printf("Device is Vendor=%i, Product=%i\n",device_info.vendor,device_info.product);
if (debug==1) fprintf(stderr,"Start_req ...\n");
if ((device_info.vendor==vend)&&(device_info.product==prod)) {
return 1;
}
else {
return -1;
}
}
//принимаем репорт 6 (num) строк по 8 байт
static int recive_report(int num, char *buf)
{
struct hiddev_report_info rinfo;
struct hiddev_usage_ref uref;
struct hiddev_event ev[8];
struct timeval tv;
fd_set fdset;
int err,ret,i,j;
int rd;
if (verb==1) printf("Reciving report ...");
if (debug==1) fprintf(stderr,"Reciving report ...\n");
tv.tv_sec = 0;
tv.tv_usec = 300000;
// tv.tv_usec = 100000;
FD_ZERO (&fdset);
FD_SET(fd,&fdset);
rd = select((fd+1),&fdset,NULL,NULL,&tv);
if (debug==1) fprintf(stderr,"recive_report: select - ok\n");
// printf("select = %d\n",rd);
if (rd>0) {
for (i=0; i<num; i++) { //6
rinfo.report_type = HID_REPORT_TYPE_INPUT;
rinfo.report_id = 0;
ret = ioctl(fd, HIDIOCGREPORT,&rinfo);
if (debug==1) fprintf(stderr,"recive_report: ioctl(fd... - ok\n");
if (debug==1) fprintf(stderr,"recive_report: ret=%d\n",ret);
rd = read(fd, ev , sizeof(ev) );
if (debug==1) fprintf(stderr,"recive_report: read(fd... - ok\n");
if (debug==1) fprintf(stderr,"recive_report: rd=%d\n",rd);
for (j=0; j<8; j++) {
buf[i*8+j]=ev[j].value;
//printf("%X ",ev[j].value);
}
// printf("\n");
}
ret = ioctl(fd, HIDIOCGREPORT,&rinfo);
if (debug==1) fprintf(stderr,"recive_report: ret = ioctl(fd... - ok\n");
}
if (verb==1) printf(" Done\n");
if (debug==1) fprintf(stderr,"Reciving report - Done\n");
return 777;
}
static int clear_report(int type)
{
struct hiddev_report_info rinfo;
struct hiddev_event ev[8];
struct timeval tv;
fd_set fdset;
int err,ret,i,j;
int rd;
if (verb==1) printf("Clearing report ...");
if (debug==1) fprintf(stderr,"Clear report ...\n");
tv.tv_sec = 0;
tv.tv_usec = 300000;
// tv.tv_usec = 100000;
FD_ZERO (&fdset);
FD_SET(fd,&fdset);
rd=1;
while ( rd > 0 ) {
rinfo.report_type = HID_REPORT_TYPE_INPUT;
rinfo.report_id = 0;
// ret = ioctl(fd, HIDIOCINITREPORT);
ret = ioctl(fd, HIDIOCGREPORT,&rinfo);
rd = select((fd+1),&fdset,NULL,NULL,&tv);
if (rd>0) {
rd = read(fd, ev , sizeof(ev));
}
}
if (verb==1) printf(" Done\n");
if (debug==1) fprintf(stderr,"Clear report - Done\n");
return 777;
}
void print_help (void) {
printf("======================================================================\n");
printf("Start with command line arguments:\n");
printf("-V n (VENDOR decemal)\n");
printf("-P n (PRODUCT decemal)\n");
printf("-F file_name (device file name /dev/usb/hiddev0)\n");
printf("-D dir_name (dir name for search devices /dev/usb)\n");
printf("-v verbose mode\n");
printf("-h this help\n");
printf("======================================================================\n");
printf("\n");
}
int open_device (char * file) {
if (verb==1) printf("Probe %s... ",file);
if (debug==1) fprintf(stderr,"Open devise - Done\n");
struct stat buf;
if ( stat( file,&buf)==0 ) { //файл есть
if (verb==1) printf("Device is exists\n");
if ((fd = open(file, O_RDONLY)) >= 0) {
if (verb==1) printf("Open fd %s successfuly\n",file);
if (start_req(0)<0) { //не наш вендор
close(fd);
if (verb==1) printf("Device %s is not Vendor=%i, Product=%i. Skip.\n",file,vend,prod);
return (0);
}
else {
if (verb==1) printf("Device %s is Vendor=%i, Product=%i\n",file,vend,prod);
if (verb==1) printf("fd=%i\n",fd);
return (fd);
}
}
else {
perror("hiddev open");
if (verb==1) printf("Device %s is not opened\n",file);
return (0);
}
}
else {
if (verb==1) printf("Device is not exists\n");
}
return (0);
}
int dir_mode (char * dir) {
char dev_name[20];
struct stat buf;
if ( strlen(dir)<2 ) { //не задан каталог для поиска
strcpy(dir,"/dev/usb");
}
if ( stat( dir,&buf)==0 ) { //каталог есть
int i;
for (i=0; i<4; i++) {
if ( strlen(dir)>1 ) sprintf(dev_name,"%s/hiddev%i",dir,i);
else sprintf(dev_name,"/dev/usb/hiddev%i",i);
int fd=open_device(dev_name);
if ( fd >= 0 ) { //нашли нужное устройство и успешно его открыли
return (fd);
}
}
}
else {
if (verb==1) printf("Dir %s is not exists\n",dir);
return (0);
}
}
int main (int argc, char **argv) {
verb=0;
vend=0;
prod=0;
debug=0;
char file[300];
char dir[300];
file[0]=0; //нулевая длина строки
dir[0]=0; //нулевая длина строки
int rez=0;
while ( (rez = getopt(argc,argv,"V:P:F:D:vhd")) != -1){
switch (rez){
case 'V':
vend=atoi(optarg);
break;
case 'P':
prod=atoi(optarg);
break;
case 'F':
strcpy(file,optarg);
break;
case 'D':
strcpy(dir,optarg);
break;
case 'v':
verb=1;
break;
case 'd':
debug=1;
break;
case 'h':
print_help;
exit(0);
break;
case '?':
printf("Comand line Error found !\n");
break;
}
}
if ((vend == 0) || (prod == 0)) {
print_help();
exit(0);
}
// exit(0);
if (verb==1) printf("Verbose mode\n");
if (debug==1) fprintf(stderr,"\n\n====================\n");
fd = -1;
if ( strlen(file)>1 ) { //есть конретное имя устройства
if (verb==1) printf("File name mode - %s\n",file);
fd=open_device(file);
}
else {
if ((verb==1) && (strlen(dir)>1)) printf("Dir name mode - %s\n",dir);
fd=dir_mode(dir);
}
if ( fd < 0 ) { //так и не смогли ничего подходящего открыть
if (verb==1) printf("Device not found\n");
exit(1);
}
if (verb==1) printf("fd %i\n",fd);
char str_cmd_T[8]; //команда теста
char str_cmd_QS[8]; //запрос состояния
str_cmd_T[0]= 84;
str_cmd_T[1]= 13;
str_cmd_QS[0]= 81;
str_cmd_QS[1]= 83;
str_cmd_QS[2]= 13;
str_cmd_QS[3]= 0;
str_cmd_QS[4]= 0;
str_cmd_QS[5]= 0;
str_cmd_QS[6]= 0;
str_cmd_QS[7]= 0;
int err;
int i;
signal(SIGALRM, alarm_handler);
alarm(TIMEOUT);
//start_report(0);
err=clear_report(0);
err=send_report(str_cmd_QS,8);
char rezault[64];
err = recive_report(6,rezault);
for (i=0; i<46; i++) printf("%c",rezault[i]); //последние два байта нам не нужны
// printf(" ok");
printf ("\n");
close(fd);
if (debug==1) fprintf(stderr,"Done All\n");
exit(0);
}