mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-02-01 01:09:47 +00:00
92fb192e05
git-svn-id: https://svn.fhem.de/fhem/trunk@1405 2b470e98-0d58-463d-a4d8-8e2adae1ed80
443 lines
11 KiB
Perl
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; |