2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-02-01 19:30:31 +00:00
fhem-mirror/fhem/contrib/HM-USB-CFG/DevIo.pm
rudolfkoenig 92fb192e05 Perliminary (read-only) HM-USB Support
git-svn-id: https://svn.fhem.de/fhem/trunk@1405 2b470e98-0d58-463d-a4d8-8e2adae1ed80
2012-04-02 11:04:52 +00:00

443 lines
11 KiB
Perl

##############################################
# $Id$
package main;
use Device::USB;
my $timeout = 1000 ;
sub DevIo_SimpleRead($);
sub DevIo_TimeoutRead($$);
sub DevIo_SimpleWrite($$);
sub DevIo_OpenDev($$$);
sub DevIo_CloseDev($);
sub DevIo_Disconnected($);
########################
sub
DevIo_DoSimpleRead($)
{
my ($hash) = @_;
my ($buf, $res);
if($hash->{USBDev}) {
$buf = $hash->{USBDev}->input();
} elsif($hash->{DIODev}) {
$res = sysread($hash->{DIODev}, $buf, 256);
$buf = undef if(!defined($res));
} elsif($hash->{TCPDev}) {
$res = sysread($hash->{TCPDev}, $buf, 256);
$buf = undef if(!defined($res));
}
######################################### HID by peterp
elsif($hash->{HIDDev}) {
my $r; #raw message
my $b=0; #raw message payload
my $c=0; # ignore counter
my $d=0; #HM message length
my $s = 0; #start counter
my $start = 0; #raw header flag
my $typ ="";
$res = sysread($hash->{HIDDev}, $buf, 512);
$buf = undef if(!defined($res));
## HID specific
for (my $i=0; $i<64;$i++)
{
if ($start != 0)
{
$r .= unpack('H*', substr($buf,4+$i*8,1)); #copy to raw HMmessage
if ($typ eq "E")
{
if ($b > 12) #raw message payload
{
$d--;
if ($d == 0)
{
$r .= "\n"; #form a raw HMmessage for parse like HMLAN
$start = 0;
# Log 4, "HMUSB HMmessage:$r";
}
}
$b++;
}
else
{
$d--;
if ($d == 0)
{
Log 2, "HMUSB HMmessage:$r";
$r .= "\n"; #form a raw HMmessage for parse like HMLAN
$start = 0;
}
}
}
else
{
# $r = unpack('H*', substr($buf,4+$i*8,1));
# Log 4, "$r\t";
if ( ord(substr($buf,4+$i*8,1)) == 69)
{
$start = 1; #raw header found
$r = "E"; #start a raw HMmessage for parse like HMLAN
$d = ord(substr($buf,4+($i+13)*8,1)); #calc HM message length
$s = $i;
$typ ="E";
# Log 4, "HMUSB ReadSimple Magic found HMlen:$d";
}
elsif ( ord(substr($buf,4+$i*8,1)) == 73)
{
Log 2, "HMUSB ReadSimple Magic >I< found i:$i b:$b ";
$start = 1; #raw header found
$typ = "I";
$d = 4;
}
elsif ( ord(substr($buf,4+$i*8,1)) == 82)
{
Log 2, "HMUSB ReadSimple Magic >R< found i:$i b:$b ";
}
elsif ( ord(substr($buf,4+$i*8,1)) == 72)
{
Log 2, "HMUSB ReadSimple Magic >H< USB-IF found i:$i b:$b";
$start = 1; #raw header found
$typ = "H";
$d = 40;
}
else
{
$c++; #ignore counter
}
}
}
# Log 4, "HMUSB ReadSimple all >$r< (raw Start $s ignored $c)";
if ($typ eq "E")
{
my ($src, $status, $msec, $d2, $rssi, $msg);
$r =~ m/^E(......)(....)(........)(..)(....)(.*)/;
($src, $status, $msec, $d2, $rssi, $msg) =
($1, $2, $3, $4, $5, $6);
my $cmsg = "E".$src.",".$status.",".$msec.",".$d2.",".$rssi.",".$msg."\n";
Log 4, "HMUSB ReadSimple converted $cmsg";
return $cmsg;
}
elsif ($typ eq "H")
{
Log 4, "HMUSB ReadSimple Wakup found";
my ($vers, $serno, $d1, $owner, $msec, $d2);
$r =~ m/^HHM-USB-IF(....)(..........)(......)(......)(........)(....)/;
($vers, $serno, $d1, $owner, $msec, $d2) =
(hex($1), $2, $3, $4, $5, $6);
my $wmsg = "HHM-USB-IF".",".$vers.",".$serno.",".$d1.",".$owner.",".$msec.",".$d2."\n";
Log 2, "HMUSB ReadSimple Wakeup converted $wmsg";
return $wmsg;
}
elsif ($typ eq "I")
{
$r =~ m/^I00.*/;
return $r;
}
######################################### HIDDEV by peterp
}
return $buf;
}
########################
sub
DevIo_SimpleRead($)
{
my ($hash) = @_;
my $buf = DevIo_DoSimpleRead($hash);
###########
# Lets' try again: Some drivers return len(0) on the first read...
if(defined($buf) && length($buf) == 0) {
$buf = DevIo_DoSimpleRead($hash);
}
if(!defined($buf) || length($buf) == 0) {
DevIo_Disconnected($hash);
return undef;
}
return $buf;
}
########################
# Read until you get the timeout. Use it with care
sub
DevIo_TimeoutRead($$)
{
my ($hash, $timeout) = @_;
my $answer = "";
for(;;) {
my $rin = "";
vec($rin, $hash->{FD}, 1) = 1;
my $nfound = select($rin, undef, undef, $timeout);
last if($nfound <= 0);
my $r = DevIo_DoSimpleRead($hash);
last if(!defined($r));
$answer .= $r;
}
return $answer;
}
########################
# Input is HEX, with header and CRC
sub
DevIo_SimpleWrite($$)
{
my ($hash, $msg) = @_;
return if(!$hash);
my $name = $hash->{NAME};
my $ll5 = GetLogLevel($name,3);
Log $ll5, "DevIo SW: $msg";
####################################################
if($hash->{HIDDev}) #added for HM-USB-CFG by peterp
{
$msg =~ s/,//g;
my $msg1 = substr($msg,0,1);
my $msg2 = pack('H*', substr($msg,1));
$msg = $msg1 . $msg2 . "\r\n";
syswrite($hash->{HIDDev}, $msg);
my $tmsg = unpack('H*', $msg);
Log 2, "DevIo_SimpleWrite: $tmsg";
}
else
####################################################
{
$msg = pack('H*', $msg) if($ishex);
$hash->{USBDev}->write($msg) if($hash->{USBDev});
syswrite($hash->{TCPDev}, $msg) if($hash->{TCPDev});
syswrite($hash->{DIODev}, $msg) if($hash->{DIODev});
}
select(undef, undef, undef, 0.001);
}
########################
sub
DevIo_OpenDev($$$)
{
my ($hash, $reopen, $initfn) = @_;
my $dev = $hash->{DeviceName};
my $name = $hash->{NAME};
my $po;
my $baudrate;
($dev, $baudrate) = split("@", $dev);
$hash->{PARTIAL} = "";
Log 4, "DEVIO OpenDev $name device $dev"
if(!$reopen);
if($dev =~ m/^(.+):([0-9]+)$/) { # host:port
# This part is called every time the timeout (5sec) is expired _OR_
# somebody is communicating over another TCP connection. As the connect
# for non-existent devices has a delay of 3 sec, we are sitting all the
# time in this connect. NEXT_OPEN tries to avoid this problem.
if($hash->{NEXT_OPEN} && time() < $hash->{NEXT_OPEN}) {
return;
}
my $conn = IO::Socket::INET->new(PeerAddr => $dev);
if($conn) {
delete($hash->{NEXT_OPEN})
} else {
Log(3, "Can't connect to IPDEV $dev: $!") if(!$reopen);
$readyfnlist{"$name.$dev"} = $hash;
$hash->{STATE} = "disconnected";
$hash->{NEXT_OPEN} = time()+60;
return "";
}
$hash->{TCPDev} = $conn;
$hash->{FD} = $conn->fileno();
delete($readyfnlist{"$name.$dev"});
$selectlist{"$name.$dev"} = $hash;
} elsif($baudrate && lc($baudrate) eq "directio") { # Without Device::SerialPort
if(!open($po, "+<$dev")) {
return undef if($reopen);
Log(3, "Can't open $dev: $!");
$readyfnlist{"$name.$dev"} = $hash;
$hash->{STATE} = "disconnected";
return "";
}
$hash->{DIODev} = $po;
if( $^O =~ /Win/ ) {
$readyfnlist{"$name.$dev"} = $hash;
} else {
$hash->{FD} = fileno($po);
delete($readyfnlist{"$name.$dev"});
$selectlist{"$name.$dev"} = $hash;
}
#################################################### HIDDEV by peterp
} elsif($dev =~ m/^\/dev\/usb\/hiddev[0-9]$/)
{
if(!open($po, "+<$dev"))
{
return undef if($reopen);
Log(3, "Can't open HIDD $dev: $!");
$readyfnlist{"$name.$dev"} = $hash;
$hash->{STATE} = "disconnected";
return "";
}
Log(2, "DevIo opened HID $dev"); #peterp
$hash->{HIDDev} = $po;
if( $^O =~ /Win/ ) {
$readyfnlist{"$name.$dev"} = $hash;
} else {
$hash->{FD} = fileno($po);
delete($readyfnlist{"$name.$dev"});
$selectlist{"$name.$dev"} = $hash;
}
#################################################### HIDDEV by peterp
} else { # USB/Serial device
if ($^O=~/Win/) {
require Win32::SerialPort;
$po = new Win32::SerialPort ($dev);
} else {
require Device::SerialPort;
$po = new Device::SerialPort ($dev);
}
if(!$po) {
return undef if($reopen);
Log(3, "Can't open USB/Seriell $dev: $!");
$readyfnlist{"$name.$dev"} = $hash;
$hash->{STATE} = "disconnected";
return "";
}
$hash->{USBDev} = $po;
if( $^O =~ /Win/ ) {
$readyfnlist{"$name.$dev"} = $hash;
} else {
$hash->{FD} = $po->FILENO;
delete($readyfnlist{"$name.$dev"});
$selectlist{"$name.$dev"} = $hash;
}
if($baudrate) {
$po->reset_error();
Log 3, "Setting $name baudrate to $baudrate";
$po->baudrate($baudrate);
$po->databits(8);
$po->parity('none');
$po->stopbits(1);
$po->handshake('none');
# This part is for some Linux kernel versions whih has strange default
# settings. Device::SerialPort is nice: if the flag is not defined for your
# OS then it will be ignored.
$po->stty_icanon(0);
#$po->stty_parmrk(0); # The debian standard install does not have it
$po->stty_icrnl(0);
$po->stty_echoe(0);
$po->stty_echok(0);
$po->stty_echoctl(0);
# Needed for some strange distros
$po->stty_echo(0);
$po->stty_icanon(0);
$po->stty_isig(0);
$po->stty_opost(0);
$po->stty_icrnl(0);
}
$po->write_settings;
}
if($reopen) {
Log 1, "$dev reappeared ($name)";
} else {
Log 3, "$name device $dev opened";
}
$hash->{STATE}="opened";
my $ret;
if($initfn) {
my $ret = &$initfn($hash);
if($ret) {
DevIo_CloseDev($hash);
Log 1, "Cannot init $dev, ignoring it";
}
}
DoTrigger($name, "CONNECTED") if($reopen);
return $ret;
}
########################
sub
DevIo_CloseDev($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
my $dev = $hash->{DeviceName};
return if(!$dev);
if($hash->{TCPDev}) {
$hash->{TCPDev}->close();
delete($hash->{TCPDev});
} elsif($hash->{USBDev}) {
$hash->{USBDev}->close() ;
delete($hash->{USBDev});
} elsif($hash->{DIODev}) {
close($hash->{DIODev});
delete($hash->{DIODev});
} elsif($hash->{HIDDev}) { #added for HM-USB-CFG by peterp
close($hash->{HIDDev});
delete($hash->{HIDDev});
}
($dev, undef) = split("@", $dev); # Remove the baudrate
delete($selectlist{"$name.$dev"});
delete($readyfnlist{"$name.$dev"});
delete($hash->{FD});
}
sub
DevIo_Disconnected($)
{
my $hash = shift;
my $dev = $hash->{DeviceName};
my $name = $hash->{NAME};
my $baudrate;
($dev, $baudrate) = split("@", $dev);
return if(!defined($hash->{FD})); # Already deleted or RFR
Log 1, "$dev disconnected, waiting to reappear";
DevIo_CloseDev($hash);
$readyfnlist{"$name.$dev"} = $hash; # Start polling
$hash->{STATE} = "disconnected";
# Without the following sleep the open of the device causes a SIGSEGV,
# and following opens block infinitely. Only a reboot helps.
sleep(5);
DoTrigger($name, "DISCONNECTED");
}
1;