From 58f9fe39aa49f2f0b9c76b68d6f03b2106c285e8 Mon Sep 17 00:00:00 2001 From: Frodo Looijaard Date: Wed, 17 Feb 1999 20:55:23 +0000 Subject: [PATCH] IOCTL access to /dev/i2c-* files from Perl implemented It needs to be expanded a bit and tested, but this means I can start to write chip detection routines from Perl! git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@238 7894878c-1315-0410-8ee3-d5d059ff63e0 --- prog/detect/detect.pl | 260 +++++++++++++++++++++++++++---------- prog/detect/sensors-detect | 260 +++++++++++++++++++++++++++---------- 2 files changed, 390 insertions(+), 130 deletions(-) diff --git a/prog/detect/detect.pl b/prog/detect/detect.pl index ec45fb6d..b97501a0 100755 --- a/prog/detect/detect.pl +++ b/prog/detect/detect.pl @@ -1,62 +1,45 @@ #!/usr/bin/perl +# +# detect.pl - Detect PCI bus and chips +# Copyright (c) 1998, 1999 Frodo Looijaard +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# + + +# A Perl wizard really ought to look upon this; the PCI and I2C stuff should +# each be put in a separate file, using modules and packages. That is beyond +# me. + use strict; -# This function returns a reference to an array of hashes. Each hash has some -# PCI information (more than we will ever need, probably). The most important -# fields are 'bus', 'slot', 'func' (they uniquely identify a PCI device in -# a computer) and 'vendid','devid' (they uniquely identify a type of device). -# /proc/bus/pci/devices is only available on late 2.1 and 2.2 kernels. -sub read_proc_dev_pci -{ - my ($dfn,$vend,@pci_list); - open INPUTFILE, "/proc/bus/pci/devices" or return; - while () { - my $record = {}; - ($dfn,$vend,$record->{irq},$record->{base_addr0},$record->{base_addr1}, - $record->{base_addr2},$record->{base_addr3},$record->{base_addr4}, - $record->{base_addr5},$record->{rom_base_addr}) = - map { oct "0x$_" } split; - $record->{bus} = $dfn >> 8; - $record->{slot} = ($dfn & 0xf8) >> 3; - $record->{func} = $dfn & 0x07; - $record->{vendid} = $vend >> 16; - $record->{devid} = $vend & 0xffff; - push @pci_list,$record; - } - close INPUTFILE or return; - return \@pci_list; -} +use vars qw(@pci_list @pci_adapters); -# This function returns a reference to an array of hashes. Each hash has some -# PCI information. The important fields here are 'bus', 'slot', 'func' (they -# uniquely identify a PCI device in a computer) and 'desc' (a functional -# description of the PCI device). If this is an 'unknown device', the -# vendid and devid fields are set instead. -sub read_proc_pci -{ - my @pci_list; - open INPUTFILE, "/proc/pci" or return; - while () { - my $record = {}; - if (($record->{bus},$record->{slot},$record->{func}) = - /^\s*Bus\s*(\S)+\s*,\s*device\s*(\S+)\s*,\s*function\s*(\S+)\s*:\s*$/) { - my $desc = ; - unless (($desc =~ /Unknown device/) and - (($record->{vendid},$record->{devid}) = - /^\s*Vendor id=(\S+)\.\s*Device id=(\S+)\.$/)) { - $record->{desc} = $desc; - } - push @pci_list,$record; - } - } - close INPUTFILE or return; - return \@pci_list; -} +######################### +# CONSTANT DECLARATIONS # +######################### -sub adapter_pci_detection -{ - my @pci_adapters = ( +# This is the list of SMBus or I2C adapters we recognize by their PCI +# signature. This is an easy and fast way to determine which SMBus or I2C +# adapters should be present. +# Each entry must have a vindid (Vendor ID), devid (Device ID), func (PCI +# Function) and procid (string as appears in /proc/pci; see linux/driver/pci, +# either pci.c or oldproc.c). If no driver is written yet, omit the +# driver (Driver Name) field. +@pci_adapters = ( { vendid => 0x8086, devid => 0x7113, @@ -84,22 +67,84 @@ sub adapter_pci_detection funcid => 0, procid => "Acer Labs M7101", } - ); +); - my $pci_list_ref = read_proc_dev_pci; - my $old_pci_interface = 0; + +############## +# PCI ACCESS # +############## + +# This function returns a list of hashes. Each hash has some PCI information +# (more than we will ever need, probably). The most important +# fields are 'bus', 'slot', 'func' (they uniquely identify a PCI device in +# a computer) and 'vendid','devid' (they uniquely identify a type of device). +# /proc/bus/pci/devices is only available on late 2.1 and 2.2 kernels. +sub read_proc_dev_pci +{ + my ($dfn,$vend,@pci_list); + open INPUTFILE, "/proc/bus/pci/devices" or return; + while () { + my $record = {}; + ($dfn,$vend,$record->{irq},$record->{base_addr0},$record->{base_addr1}, + $record->{base_addr2},$record->{base_addr3},$record->{base_addr4}, + $record->{base_addr5},$record->{rom_base_addr}) = + map { oct "0x$_" } split; + $record->{bus} = $dfn >> 8; + $record->{slot} = ($dfn & 0xf8) >> 3; + $record->{func} = $dfn & 0x07; + $record->{vendid} = $vend >> 16; + $record->{devid} = $vend & 0xffff; + push @pci_list,$record; + } + close INPUTFILE or return; + return @pci_list; +} + +# This function returns a list of hashes. Each hash has some PCI +# information. The important fields here are 'bus', 'slot', 'func' (they +# uniquely identify a PCI device in a computer) and 'desc' (a functional +# description of the PCI device). If this is an 'unknown device', the +# vendid and devid fields are set instead. +sub read_proc_pci +{ + my @pci_list; + open INPUTFILE, "/proc/pci" or return; + while () { + my $record = {}; + if (($record->{bus},$record->{slot},$record->{func}) = + /^\s*Bus\s*(\S)+\s*,\s*device\s*(\S+)\s*,\s*function\s*(\S+)\s*:\s*$/) { + my $desc = ; + unless (($desc =~ /Unknown device/) and + (($record->{vendid},$record->{devid}) = + /^\s*Vendor id=(\S+)\.\s*Device id=(\S+)\.$/)) { + $record->{desc} = $desc; + } + push @pci_list,$record; + } + } + close INPUTFILE or return; + return @pci_list; +} + +sub intialize_proc_pci +{ + @pci_list = read_proc_dev_pci; + @pci_list = read_proc_pci if not defined @pci_list; + die "Can't access either /proc/bus/pci/ or /proc/pci!" + if not defined @pci_list; +} + +##################### +# ADAPTER DETECTION # +##################### + + +sub adapter_pci_detection +{ my ($device,$try,@res); - print "Probing for PCI bus adapters...\n"; - if (! defined @$pci_list_ref) { - $pci_list_ref = read_proc_pci; - die "Can't access either /proc/bus/pci/ or /proc/pci!" - if ! defined @$pci_list_ref; - $old_pci_interface = 1; - } - - foreach $device (@$pci_list_ref) { + foreach $device (@pci_list) { foreach $try (@pci_adapters) { if ((defined($device->{vendid}) and $try->{vendid} == $device->{vendid} and @@ -123,4 +168,89 @@ sub adapter_pci_detection return @res; } +############################# +# I2C AND SMBUS /DEV ACCESS # +############################# + +use vars qw($IOCTL_I2C_RETRIES $IOCTL_I2C_TIMEOUT $IOCTL_I2C_UDELAY + $IOCTL_I2C_MDELAY $IOCTL_I2C_SLAVE $IOCTL_I2C_TENBIT + $IOCTL_I2C_SMBUS); + +# These are copied from and + +# For bit-adapters: +$IOCTL_I2C_RETRIES = 0x0701; +$IOCTL_I2C_TIMEOUT = 0x0702; +$IOCTL_I2C_UDELAY = 0x0705; +$IOCTL_I2C_MDELAY = 0x0706; + +# General ones +$IOCTL_I2C_SLAVE = 0x0703; +$IOCTL_I2C_TENBIT = 0x0704; +$IOCTL_I2C_SMBUS = 0x0720; + +use vars qw($SMBUS_READ $SMBUS_WRITE $SMBUS_QUICK $SMBUS_BYTE $SMBUS_BYTE_DATA + $SMBUS_WORD_DATA $SMBUS_PROC_CALL $SMBUS_BLOCK_DATA); +# These are copied from +$SMBUS_READ = 1; +$SMBUS_WRITE = 0; +$SMBUS_QUICK = 0; +$SMBUS_BYTE = 1; +$SMBUS_BYTE_DATA = 2; +$SMBUS_WORD_DATA = 3; +$SMBUS_PROC_CALL = 4; +$SMBUS_BLOCK_DATA = 5; + +sub i2c_set_slave_addr +{ + my ($file,$addr) = @_; + ioctl $file, $IOCTL_I2C_SLAVE, $addr or return 0; + return 1; +} + +# i2c_smbus_access is based upon the corresponding C function (see +# ). Note that 'data' is a reference to a list, +# while 'file' is a reference to a filehandle. Also, it seems that in +# a struct, chars are encoded as shorts. This is all very tricky and +# depends on C compiler internals... +sub i2c_smbus_access +{ + my ($file,$read_write,$command,$size,$data) = @_; + my $data_array = pack "C32", @$data; + my $ioctl_data = pack "SSIp", ($read_write,$command,$size,$data_array); + ioctl $file, $IOCTL_I2C_SMBUS, $ioctl_data or return 0; + $_[4] = [ unpack "C32",$data_array ]; + return 1; +} + +sub i2c_read_byte_data +{ + my ($file,$command) = @_; + my @data = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); + i2c_smbus_access $file, $SMBUS_READ, $command, $SMBUS_BYTE_DATA, \@data + or return -1; + printf "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", @data; + return $data[0]; +} + +sub i2c_read_word_data +{ + my ($file,$command) = @_; + my $data = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; + i2c_smbus_access $file, $SMBUS_READ, $command, $SMBUS_WORD_DATA, $data + or return -1; + return $$data[0] + 256 * $$data[1]; +} + + +################ +# MAIN PROGRAM # +################ + +intialize_proc_pci; adapter_pci_detection; + +# TEST! +#open FILE, "+>/dev/i2c-0" or die "Can't open /dev/i2c-0!"; +#i2c_set_slave_addr \*FILE, 0x49 or die "Couldn't set slave addr!"; +#print (i2c_read_word_data \*FILE, 0), "\n"; diff --git a/prog/detect/sensors-detect b/prog/detect/sensors-detect index ec45fb6d..b97501a0 100755 --- a/prog/detect/sensors-detect +++ b/prog/detect/sensors-detect @@ -1,62 +1,45 @@ #!/usr/bin/perl +# +# detect.pl - Detect PCI bus and chips +# Copyright (c) 1998, 1999 Frodo Looijaard +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# + + +# A Perl wizard really ought to look upon this; the PCI and I2C stuff should +# each be put in a separate file, using modules and packages. That is beyond +# me. + use strict; -# This function returns a reference to an array of hashes. Each hash has some -# PCI information (more than we will ever need, probably). The most important -# fields are 'bus', 'slot', 'func' (they uniquely identify a PCI device in -# a computer) and 'vendid','devid' (they uniquely identify a type of device). -# /proc/bus/pci/devices is only available on late 2.1 and 2.2 kernels. -sub read_proc_dev_pci -{ - my ($dfn,$vend,@pci_list); - open INPUTFILE, "/proc/bus/pci/devices" or return; - while () { - my $record = {}; - ($dfn,$vend,$record->{irq},$record->{base_addr0},$record->{base_addr1}, - $record->{base_addr2},$record->{base_addr3},$record->{base_addr4}, - $record->{base_addr5},$record->{rom_base_addr}) = - map { oct "0x$_" } split; - $record->{bus} = $dfn >> 8; - $record->{slot} = ($dfn & 0xf8) >> 3; - $record->{func} = $dfn & 0x07; - $record->{vendid} = $vend >> 16; - $record->{devid} = $vend & 0xffff; - push @pci_list,$record; - } - close INPUTFILE or return; - return \@pci_list; -} +use vars qw(@pci_list @pci_adapters); -# This function returns a reference to an array of hashes. Each hash has some -# PCI information. The important fields here are 'bus', 'slot', 'func' (they -# uniquely identify a PCI device in a computer) and 'desc' (a functional -# description of the PCI device). If this is an 'unknown device', the -# vendid and devid fields are set instead. -sub read_proc_pci -{ - my @pci_list; - open INPUTFILE, "/proc/pci" or return; - while () { - my $record = {}; - if (($record->{bus},$record->{slot},$record->{func}) = - /^\s*Bus\s*(\S)+\s*,\s*device\s*(\S+)\s*,\s*function\s*(\S+)\s*:\s*$/) { - my $desc = ; - unless (($desc =~ /Unknown device/) and - (($record->{vendid},$record->{devid}) = - /^\s*Vendor id=(\S+)\.\s*Device id=(\S+)\.$/)) { - $record->{desc} = $desc; - } - push @pci_list,$record; - } - } - close INPUTFILE or return; - return \@pci_list; -} +######################### +# CONSTANT DECLARATIONS # +######################### -sub adapter_pci_detection -{ - my @pci_adapters = ( +# This is the list of SMBus or I2C adapters we recognize by their PCI +# signature. This is an easy and fast way to determine which SMBus or I2C +# adapters should be present. +# Each entry must have a vindid (Vendor ID), devid (Device ID), func (PCI +# Function) and procid (string as appears in /proc/pci; see linux/driver/pci, +# either pci.c or oldproc.c). If no driver is written yet, omit the +# driver (Driver Name) field. +@pci_adapters = ( { vendid => 0x8086, devid => 0x7113, @@ -84,22 +67,84 @@ sub adapter_pci_detection funcid => 0, procid => "Acer Labs M7101", } - ); +); - my $pci_list_ref = read_proc_dev_pci; - my $old_pci_interface = 0; + +############## +# PCI ACCESS # +############## + +# This function returns a list of hashes. Each hash has some PCI information +# (more than we will ever need, probably). The most important +# fields are 'bus', 'slot', 'func' (they uniquely identify a PCI device in +# a computer) and 'vendid','devid' (they uniquely identify a type of device). +# /proc/bus/pci/devices is only available on late 2.1 and 2.2 kernels. +sub read_proc_dev_pci +{ + my ($dfn,$vend,@pci_list); + open INPUTFILE, "/proc/bus/pci/devices" or return; + while () { + my $record = {}; + ($dfn,$vend,$record->{irq},$record->{base_addr0},$record->{base_addr1}, + $record->{base_addr2},$record->{base_addr3},$record->{base_addr4}, + $record->{base_addr5},$record->{rom_base_addr}) = + map { oct "0x$_" } split; + $record->{bus} = $dfn >> 8; + $record->{slot} = ($dfn & 0xf8) >> 3; + $record->{func} = $dfn & 0x07; + $record->{vendid} = $vend >> 16; + $record->{devid} = $vend & 0xffff; + push @pci_list,$record; + } + close INPUTFILE or return; + return @pci_list; +} + +# This function returns a list of hashes. Each hash has some PCI +# information. The important fields here are 'bus', 'slot', 'func' (they +# uniquely identify a PCI device in a computer) and 'desc' (a functional +# description of the PCI device). If this is an 'unknown device', the +# vendid and devid fields are set instead. +sub read_proc_pci +{ + my @pci_list; + open INPUTFILE, "/proc/pci" or return; + while () { + my $record = {}; + if (($record->{bus},$record->{slot},$record->{func}) = + /^\s*Bus\s*(\S)+\s*,\s*device\s*(\S+)\s*,\s*function\s*(\S+)\s*:\s*$/) { + my $desc = ; + unless (($desc =~ /Unknown device/) and + (($record->{vendid},$record->{devid}) = + /^\s*Vendor id=(\S+)\.\s*Device id=(\S+)\.$/)) { + $record->{desc} = $desc; + } + push @pci_list,$record; + } + } + close INPUTFILE or return; + return @pci_list; +} + +sub intialize_proc_pci +{ + @pci_list = read_proc_dev_pci; + @pci_list = read_proc_pci if not defined @pci_list; + die "Can't access either /proc/bus/pci/ or /proc/pci!" + if not defined @pci_list; +} + +##################### +# ADAPTER DETECTION # +##################### + + +sub adapter_pci_detection +{ my ($device,$try,@res); - print "Probing for PCI bus adapters...\n"; - if (! defined @$pci_list_ref) { - $pci_list_ref = read_proc_pci; - die "Can't access either /proc/bus/pci/ or /proc/pci!" - if ! defined @$pci_list_ref; - $old_pci_interface = 1; - } - - foreach $device (@$pci_list_ref) { + foreach $device (@pci_list) { foreach $try (@pci_adapters) { if ((defined($device->{vendid}) and $try->{vendid} == $device->{vendid} and @@ -123,4 +168,89 @@ sub adapter_pci_detection return @res; } +############################# +# I2C AND SMBUS /DEV ACCESS # +############################# + +use vars qw($IOCTL_I2C_RETRIES $IOCTL_I2C_TIMEOUT $IOCTL_I2C_UDELAY + $IOCTL_I2C_MDELAY $IOCTL_I2C_SLAVE $IOCTL_I2C_TENBIT + $IOCTL_I2C_SMBUS); + +# These are copied from and + +# For bit-adapters: +$IOCTL_I2C_RETRIES = 0x0701; +$IOCTL_I2C_TIMEOUT = 0x0702; +$IOCTL_I2C_UDELAY = 0x0705; +$IOCTL_I2C_MDELAY = 0x0706; + +# General ones +$IOCTL_I2C_SLAVE = 0x0703; +$IOCTL_I2C_TENBIT = 0x0704; +$IOCTL_I2C_SMBUS = 0x0720; + +use vars qw($SMBUS_READ $SMBUS_WRITE $SMBUS_QUICK $SMBUS_BYTE $SMBUS_BYTE_DATA + $SMBUS_WORD_DATA $SMBUS_PROC_CALL $SMBUS_BLOCK_DATA); +# These are copied from +$SMBUS_READ = 1; +$SMBUS_WRITE = 0; +$SMBUS_QUICK = 0; +$SMBUS_BYTE = 1; +$SMBUS_BYTE_DATA = 2; +$SMBUS_WORD_DATA = 3; +$SMBUS_PROC_CALL = 4; +$SMBUS_BLOCK_DATA = 5; + +sub i2c_set_slave_addr +{ + my ($file,$addr) = @_; + ioctl $file, $IOCTL_I2C_SLAVE, $addr or return 0; + return 1; +} + +# i2c_smbus_access is based upon the corresponding C function (see +# ). Note that 'data' is a reference to a list, +# while 'file' is a reference to a filehandle. Also, it seems that in +# a struct, chars are encoded as shorts. This is all very tricky and +# depends on C compiler internals... +sub i2c_smbus_access +{ + my ($file,$read_write,$command,$size,$data) = @_; + my $data_array = pack "C32", @$data; + my $ioctl_data = pack "SSIp", ($read_write,$command,$size,$data_array); + ioctl $file, $IOCTL_I2C_SMBUS, $ioctl_data or return 0; + $_[4] = [ unpack "C32",$data_array ]; + return 1; +} + +sub i2c_read_byte_data +{ + my ($file,$command) = @_; + my @data = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); + i2c_smbus_access $file, $SMBUS_READ, $command, $SMBUS_BYTE_DATA, \@data + or return -1; + printf "%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", @data; + return $data[0]; +} + +sub i2c_read_word_data +{ + my ($file,$command) = @_; + my $data = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; + i2c_smbus_access $file, $SMBUS_READ, $command, $SMBUS_WORD_DATA, $data + or return -1; + return $$data[0] + 256 * $$data[1]; +} + + +################ +# MAIN PROGRAM # +################ + +intialize_proc_pci; adapter_pci_detection; + +# TEST! +#open FILE, "+>/dev/i2c-0" or die "Can't open /dev/i2c-0!"; +#i2c_set_slave_addr \*FILE, 0x49 or die "Couldn't set slave addr!"; +#print (i2c_read_word_data \*FILE, 0), "\n";