2011-01-26 11:35:08 +02:00
#!/usr/bin/env perl
2014-04-22 21:39:47 +03:00
# Current Version : 1.3
# Copyright (C) 2008 - 2012 dloic (loic.dardant AT gmail DOT com)
2016-07-18 03:11:41 +03:00
# Copyright (C) 2008 - 2015 Arnaud Quette <arnaud.quette@free.fr>
2014-04-22 21:39:47 +03:00
# Copyright (C) 2013 - 2014 Charles Lepple <clepple+nut@gmail.com>
2010-03-26 01:20:59 +02:00
#
# Based on the usbdevice.pl script, made for the Ubuntu Media Center
# for the final use of the LIRC project.
#
# 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
2012-06-01 16:55:19 +03:00
# TODO list:
# - rewrite using glob, as in other helper scripts
# - manage deps in Makefile.am
2010-03-26 01:20:59 +02:00
use File::Find ;
use strict ;
# path to scan for USB_DEVICE pattern
my $ scanPath = "../drivers" ;
# Hotplug output file
my $ outputHotplug = "../scripts/hotplug/libhid.usermap" ;
# udev output file
my $ outputUdev = "../scripts/udev/nut-usbups.rules.in" ;
2014-04-22 21:39:47 +03:00
# BSD devd output file
my $ output_devd = "../scripts/devd/nut-usb.conf.in" ;
2011-01-26 11:35:08 +02:00
# UPower output file
my $ outputUPower = "../scripts/upower/95-upower-hid.rules" ;
2014-04-22 21:39:47 +03:00
2011-01-26 11:35:08 +02:00
# tmp output, to allow generating the ENV{UPOWER_VENDOR} header list
my $ tmpOutputUPower ;
2010-03-26 01:20:59 +02:00
# mfr header flag
2011-01-26 11:35:08 +02:00
my $ upowerMfrHeaderDone = 0 ;
2010-03-26 01:20:59 +02:00
2011-09-29 21:14:46 +03:00
# NUT device scanner - C header
my $ outputDevScanner = "./nut-scanner/nutscan-usb.h" ;
my $ GPL_header = " \
* Copyright ( C ) 2011 - Arnaud Quette <arnaud.quette\@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 " ;
2010-03-26 01:20:59 +02:00
# array of products indexed by vendorID
my % vendor ;
# contain for each vendor, its name (and...)
my % vendorName ;
################# MAIN #################
find ( \ & find_usbdevs , $ scanPath ) ;
& gen_usb_files ;
################# SUB METHOD #################
sub gen_usb_files
{
# Hotplug file header
open my $ outHotplug , ">$outputHotplug" || die "error $outputHotplug : $!" ;
print $ outHotplug '# This file is generated and installed by the Network UPS Tools package.' . "\n" ;
print $ outHotplug "#\n" ;
print $ outHotplug '# Sample entry (replace 0xVVVV and 0xPPPP with vendor ID and product ID respectively) :' . "\n" ;
print $ outHotplug '# libhidups 0x0003 0xVVVV 0xPPPP 0x0000 0x0000 0x00 0x00' ;
print $ outHotplug ' 0x00 0x00 0x00 0x00 0x00000000' . "\n" ;
print $ outHotplug "#\n" ;
print $ outHotplug '# usb module match_flags idVendor idProduct bcdDevice_lo bcdDevice_hi' ;
print $ outHotplug ' bDeviceClass bDeviceSubClass bDeviceProtocol bInterfaceClass bInterfaceSubClass' ;
print $ outHotplug ' bInterfaceProtocol driver_info' . "\n" ;
# Udev file header
open my $ outUdev , ">$outputUdev" || die "error $outputUdev : $!" ;
print $ outUdev '# This file is generated and installed by the Network UPS Tools package.' . "\n\n" ;
2011-01-26 11:35:08 +02:00
print $ outUdev 'ACTION!="add|change", GOTO="nut-usbups_rules_end"' . "\n" ;
2010-03-26 01:20:59 +02:00
print $ outUdev 'SUBSYSTEM=="usb_device", GOTO="nut-usbups_rules_real"' . "\n" ;
print $ outUdev 'SUBSYSTEM=="usb", GOTO="nut-usbups_rules_real"' . "\n" ;
2011-01-26 11:35:08 +02:00
print $ outUdev 'SUBSYSTEM!="usb", GOTO="nut-usbups_rules_end"' . "\n\n" ;
2010-03-26 01:20:59 +02:00
print $ outUdev 'LABEL="nut-usbups_rules_real"' . "\n" ;
2014-04-22 21:39:47 +03:00
open my $ out_devd , ">$output_devd" || die "error $output_devd : $!" ;
print $ out_devd '# This file is generated and installed by the Network UPS Tools package.' . "\n" ;
print $ out_devd "# Homepage: http://www.networkupstools.org/\n\n" ;
2011-01-26 11:35:08 +02:00
# UPower file header
open my $ outputUPower , ">$outputUPower" || die "error $outputUPower : $!" ;
print $ outputUPower '##############################################################################################################' . "\n" ;
print $ outputUPower '# Uninterruptible Power Supplies with USB HID interfaces' . "\n#\n" ;
2016-07-18 03:11:41 +03:00
print $ outputUPower '# This file was automatically generated by NUT:' . "\n#" . ' https://github.com/networkupstools/nut/' . "\n#\n" ;
print $ outputUPower '# To keep up to date, monitor upstream NUT' . "\n#" . ' https://github.com/networkupstools/nut/commits/master/scripts/upower/95-upower-hid.rules' . "\n" ;
print $ outputUPower "# or checkout the NUT repository and call 'tools/nut-usbinfo.pl'\n\n" ;
print $ outputUPower '# newer hiddev are part of the usbmisc class' . "\n" . 'SUBSYSTEM=="usbmisc", GOTO="up_hid_chkdev"' . "\n" ;
2011-01-26 11:35:08 +02:00
print $ outputUPower '# only support USB, else ignore' . "\n" . 'SUBSYSTEM!="usb", GOTO="up_hid_end"' . "\n\n" ;
2016-07-18 03:11:41 +03:00
print $ outputUPower '# if usbraw device, ignore' . "\n" . 'LABEL="up_hid_chkdev"' . "\n" . 'KERNEL!="hiddev*", GOTO="up_hid_end"' . "\n\n" ;
2011-01-26 11:35:08 +02:00
print $ outputUPower '# if an interface, ignore' . "\n" . 'ENV{DEVTYPE}=="usb_interface", GOTO="up_hid_end"' . "\n\n" ;
2010-03-26 01:20:59 +02:00
2011-09-29 21:14:46 +03:00
# Device scanner header
open my $ outputDevScanner , ">$outputDevScanner" || die "error $outputDevScanner : $!" ;
print $ outputDevScanner '/* nutscan-usb' . $ GPL_header . "\n */\n\n" ;
print $ outputDevScanner "#ifndef DEVSCAN_USB_H\n#define DEVSCAN_USB_H\n\n" ;
print $ outputDevScanner "#include <usb.h>\n" ;
print $ outputDevScanner "#include \"nut_stdint.h\"\t/* for uint16_t */\n\n" ;
# vid, pid, driver
print $ outputDevScanner "typedef struct {\n\tuint16_t\tvendorID;\n\tuint16_t\tproductID;\n\tchar*\tdriver_name;\n} usb_device_id_t;\n\n" ;
print $ outputDevScanner "/* USB IDs device table */\nstatic usb_device_id_t usb_device_table[] = {\n\n" ;
2010-03-26 01:20:59 +02:00
# generate the file in alphabetical order (first for VendorID, then for ProductID)
foreach my $ vendorId ( sort { lc $ a cmp lc $ b } keys % vendorName )
{
# Hotplug vendor header
if ( $ vendorName { $ vendorId } ) {
print $ outHotplug "\n# " . $ vendorName { $ vendorId } . "\n" ;
}
# udev vendor header
if ( $ vendorName { $ vendorId } ) {
print $ outUdev "\n# " . $ vendorName { $ vendorId } . "\n" ;
}
2014-04-22 21:39:47 +03:00
# devd vendor header
if ( $ vendorName { $ vendorId } ) {
print $ out_devd "\n# " . $ vendorName { $ vendorId } . "\n" ;
}
2011-01-26 11:35:08 +02:00
# UPower vendor header flag
$ upowerMfrHeaderDone = 0 ;
2010-03-26 01:20:59 +02:00
foreach my $ productId ( sort { lc $ a cmp lc $ b } keys % { $ vendor { $ vendorId } } )
{
# Hotplug device entry
print $ outHotplug "# " . $ vendor { $ vendorId } { $ productId } { "comment" } . "\n" ;
print $ outHotplug "libhidups 0x0003 " . $ vendorId . " " . $ productId . " 0x0000 0x0000 0x00" ;
print $ outHotplug " 0x00 0x00 0x00 0x00 0x00 0x00000000\n" ;
# udev device entry
print $ outUdev "# " . $ vendor { $ vendorId } { $ productId } { "comment" } . ' - ' . $ vendor { $ vendorId } { $ productId } { "driver" } . "\n" ;
print $ outUdev "ATTR{idVendor}==\"" . removeHexPrefix ( $ vendorId ) ;
print $ outUdev "\", ATTR{idProduct}==\"" . removeHexPrefix ( $ productId ) . "\"," ;
print $ outUdev ' MODE="664", GROUP="@RUN_AS_GROUP@"' . "\n" ;
2014-04-22 21:39:47 +03:00
# devd device entry
print $ out_devd "# " . $ vendor { $ vendorId } { $ productId } { "comment" } . ' - ' . $ vendor { $ vendorId } { $ productId } { "driver" } . "\n" ;
print $ out_devd "notify 100 {\n\tmatch \"system\"\t\t\"USB\";\n" ;
print $ out_devd "\tmatch \"subsystem\"\t\"DEVICE\";\n" ;
print $ out_devd "\tmatch \"type\"\t\t\"ATTACH\";\n" ;
print $ out_devd "\tmatch \"vendor\"\t\t\"$vendorId\";\n" ;
#
print $ out_devd "\tmatch \"product\"\t\t\"$productId\";\n" ;
2015-04-30 16:53:36 +03:00
print $ out_devd "\taction \"chgrp \@RUN_AS_GROUP\@ /dev/\$cdev; chmod g+rw /dev/\$cdev\";\n" ;
2014-04-22 21:39:47 +03:00
print $ out_devd "};\n" ;
2011-01-26 11:35:08 +02:00
# UPower device entry (only for USB/HID devices!)
2010-03-26 01:20:59 +02:00
if ( $ vendor { $ vendorId } { $ productId } { "driver" } eq "usbhid-ups" )
{
2011-01-26 11:35:08 +02:00
if ( $ upowerMfrHeaderDone == 0 )
2010-03-26 01:20:59 +02:00
{
2011-01-26 11:35:08 +02:00
# UPower vendor header
2010-03-26 01:20:59 +02:00
if ( $ vendorName { $ vendorId } ) {
2011-01-26 11:35:08 +02:00
$ tmpOutputUPower = $ tmpOutputUPower . "\n# " . $ vendorName { $ vendorId } . "\n" ;
2010-03-26 01:20:59 +02:00
}
2011-01-26 11:35:08 +02:00
print $ outputUPower "ATTRS{idVendor}==\"" . removeHexPrefix ( $ vendorId ) . "\", ENV{UPOWER_VENDOR}=\"" . $ vendorName { $ vendorId } . "\"\n" ;
$ upowerMfrHeaderDone = 1 ;
2010-03-26 01:20:59 +02:00
}
2011-01-26 11:35:08 +02:00
$ tmpOutputUPower = $ tmpOutputUPower . "ATTRS{idVendor}==\"" . removeHexPrefix ( $ vendorId ) ;
$ tmpOutputUPower = $ tmpOutputUPower . "\", ATTRS{idProduct}==\"" . removeHexPrefix ( $ productId ) . "\"," ;
$ tmpOutputUPower = $ tmpOutputUPower . ' ENV{UPOWER_BATTERY_TYPE}="ups"' . "\n" ;
2010-03-26 01:20:59 +02:00
}
2011-09-29 21:14:46 +03:00
# Device scanner entry
print $ outputDevScanner "\t{ " . $ vendorId . ', ' . $ productId . ", \"" . $ vendor { $ vendorId } { $ productId } { "driver" } . "\" },\n" ;
2010-03-26 01:20:59 +02:00
}
}
# Udev footer
print $ outUdev "\n" . 'LABEL="nut-usbups_rules_end"' . "\n" ;
2011-01-26 11:35:08 +02:00
# UPower...
2010-03-26 01:20:59 +02:00
# ...flush device table
2011-01-26 11:35:08 +02:00
print $ outputUPower $ tmpOutputUPower ;
2010-03-26 01:20:59 +02:00
# ...and print footer
2011-01-26 11:35:08 +02:00
print $ outputUPower "\n" . 'LABEL="up_hid_end"' . "\n" ;
2011-09-29 21:14:46 +03:00
# Device scanner footer
print $ outputDevScanner "\t/* Terminating entry */\n\t{ -1, -1, NULL }\n};\n#endif /* DEVSCAN_USB_H */\n\n" ;
2010-03-26 01:20:59 +02:00
}
sub find_usbdevs
{
2012-06-01 16:55:19 +03:00
# maybe there's an option to turn off all .* files, but anyway this is stupid
2015-04-30 16:53:36 +03:00
return $ File:: Find:: prune = 1 if ( $ _ eq '.svn' ) || ( $ _ =~ /^\.#/ ) || ( $ _ =~ /\.orig$/ ) ;
2010-03-26 01:20:59 +02:00
my $ nameFile = $ _ ;
my $ lastComment = "" ;
open my $ file , $ nameFile or die "error open file $nameFile" ;
while ( my $ line = <$file> )
{
# catch comment (should permit comment on the precedent or on the current line of USB_DEVICE declaration)
if ( $ line =~ /\s*\/\*(.+)\*\/\s*$/ )
{
$ lastComment = $ 1 ;
}
if ( $ line =~ /^\s*\{\s*USB_DEVICE\((.+)\,(.+)\)\s*/ ) # for example : { USB_DEVICE(MGE_VENDORID, 0x0001)... }
{
my $ VendorID = trim ( $ 1 ) ;
my $ ProductID = trim ( $ 2 ) ;
my $ VendorName = "" ;
# special thing for backward declaration using #DEFINE
2011-06-01 23:31:49 +03:00
# Format:
# /* vendor name */
# #define VENDORID 0x????
2010-03-26 01:20:59 +02:00
if ( ! ( $ VendorID =~ /\dx(\d|\w)+/ ) )
{
open my $ fh , $ nameFile or die "error open file $nameFile" ;
while ( my $ data = <$fh> )
{
# catch Vendor Name
if ( $ data =~ /\s*\/\*(.+)\*\/\s*$/ )
{
$ VendorName = $ 1 ;
}
# catch VendorID
if ( $ data =~ /(#define|#DEFINE)\s+$VendorID\s+(\dx(\d|\w)+)/ )
{
$ VendorID = $ 2 ;
last ;
}
}
}
# same thing for the productID
if ( ! ( $ ProductID =~ /\dx(\d|\w)+/ ) )
{
my $ data = do { open my $ fh , $ nameFile or die "error open file $nameFile" ; join '' , <$fh> } ;
if ( $ data =~ /(#define|#DEFINE)\s+$ProductID\s+(\dx(\d|\w)+)/ )
{
$ ProductID = $ 2 ;
}
else
{
die "In file $nameFile, for product $ProductID, can't find the declaration of the constant" ;
}
}
2011-06-01 23:31:49 +03:00
2015-04-30 16:53:36 +03:00
# store data (to be optimized)
# and don't overwrite actual vendor names with empty values
2011-09-29 21:14:46 +03:00
if ( ( ! $ vendorName { $ VendorID } ) or ( ( $ vendorName { $ VendorID } eq "" ) and ( $ VendorName ne "" ) ) )
2011-06-01 23:31:49 +03:00
{
$ vendorName { $ VendorID } = trim ( $ VendorName ) ;
}
2010-03-26 01:20:59 +02:00
$ vendor { $ VendorID } { $ ProductID } { "comment" } = $ lastComment ;
# process the driver name
my $ driver = $ nameFile ;
if ( $ nameFile =~ /(.+)-hid\.c/ ) {
$ driver = "usbhid-ups" ;
}
# FIXME: make a generic matching rule *.c => *
elsif ( $ nameFile eq "bcmxcp_usb.c" ) {
$ driver = "bcmxcp_usb" ;
}
elsif ( $ nameFile eq "tripplite_usb.c" ) {
$ driver = "tripplite_usb" ;
}
elsif ( $ nameFile eq "blazer_usb.c" ) {
$ driver = "blazer_usb" ;
}
elsif ( $ nameFile eq "richcomm_usb.c" ) {
$ driver = "richcomm_usb" ;
}
2014-04-22 21:39:47 +03:00
elsif ( $ nameFile eq "nutdrv_atcl_usb.c" ) {
$ driver = "nutdrv_atcl_usb" ;
}
2013-11-24 17:00:12 +02:00
elsif ( $ nameFile eq "riello_usb.c" ) {
$ driver = "riello_usb" ;
}
elsif ( $ nameFile eq "nutdrv_qx.c" ) {
$ driver = "nutdrv_qx" ;
}
2010-03-26 01:20:59 +02:00
else {
die "Unknown driver type: $nameFile" ;
}
$ vendor { $ VendorID } { $ ProductID } { "driver" } = $ driver ;
}
}
}
sub removeHexPrefix {
# make a local copy, not to alter the original entry
my $ string = $ _ [ 0 ] ;
$ string =~ s/0x// ;
return $ string ;
}
sub trim {
my ( $ str ) = shift =~ m !^\s*(.+?)\s*$!i ;
defined $ str ? return $ str : return '' ;
}