views:

377

answers:

3

I am trying to communicate with a disk drive using inb(), inw(), outb() and outw() commands so I can find specific information about the drive. However, to use these commands, I need the correct I/O ports for the device. When I have the correct I/O ports, I can find the information I am looking for very easily, however, I do not know a way to find the base address of a device's I/O ports in Linux.

In DOS, I am able to use Hdat2 to find the device's base address, however, I am trying to find the address in Linux. Is there a way to find which device maps to which I/O port in Linux?

There is a file in /proc called ioports that contains some information but I don't how to associate this information with specific devices.

Any help would be greatly appreciated. Thanks!

A: 

The device is most probably hanging off of the PCI bus, so lspci(8) is the first to look at. Then figure out where under /sys the controller is described. Here, for example, I have:


~$ lspci
...
03:00.0 RAID bus controller: LSI Logic / Symbios Logic MegaRAID SAS 1078 (rev 04)
...
~$ ll /sys/bus/pci/devices/0000\:03\:00.0/
total 0
drwxr-xr-x 4 root root      0 Dec 16 11:57 ./
drwxr-xr-x 6 root root      0 Dec 16 11:57 ../
-rw-r--r-- 1 root root   4096 Dec 16 11:57 broken_parity_status
lrwxrwxrwx 1 root root      0 Dec 16 11:57 bus -> ../../../../bus/pci/
-r--r--r-- 1 root root   4096 Dec 16 11:57 class
-rw-r--r-- 1 root root   4096 Dec 16 11:57 config
-r--r--r-- 1 root root   4096 Dec 16 11:57 device
lrwxrwxrwx 1 root root      0 Dec 16 11:57 driver -> ../../../../bus/pci/drivers/megaraid_sas/
-rw------- 1 root root   4096 Dec 16 11:57 enable
drwxr-xr-x 5 root root      0 Dec 16 11:57 host0/
-r--r--r-- 1 root root   4096 Dec 16 11:57 irq
-r--r--r-- 1 root root   4096 Dec 16 11:57 local_cpus
-r--r--r-- 1 root root   4096 Dec 16 11:57 modalias
-r--r--r-- 1 root root   4096 Dec 16 11:57 pools
drwxr-xr-x 2 root root      0 Dec 16 11:57 power/
-r--r--r-- 1 root root   4096 Dec 16 11:57 resource
-rw------- 1 root root 262144 Dec 16 11:57 resource0
-rw------- 1 root root    256 Dec 16 11:57 resource2
-rw------- 1 root root 262144 Dec 16 11:57 resource3
-r-------- 1 root root  32768 Dec 16 11:57 rom
lrwxrwxrwx 1 root root      0 Dec 16 11:57 subsystem -> ../../../../bus/pci/
-r--r--r-- 1 root root   4096 Dec 16 11:57 subsystem_device
-r--r--r-- 1 root root   4096 Dec 16 11:57 subsystem_vendor
--w------- 1 root root   4096 Dec 16 11:57 uevent
-r--r--r-- 1 root root   4096 Dec 16 11:57 vendor

This shows controller's PCI configuration space. See the details in Linux Device Drivers, Third Edition. Chapter 12: PCI Drivers.

Edit:

Take a look into this partition and mass-storage naming howto for help on Linux drive naming.

Nikolai N Fetissov
Thanks Nikolai, the link to the Linux Device Drivers book really helped. I understand how to get the address of the I/O port that I am looking for when I have the signature of the PCI device. However, I don't understand how you go from "/dev/sda" to "0000:03:00.0"?Thanks again for all your help!
Kevin S
You can go from `sda` to a pci id by looking at the symlink `/sys/block/sda/device`.
Eric Seppanen
A: 

Are you accessing hardware from a userspace program or from a kernel module?

If you're doing it from userspace, the reason it's hard to find physical address information is that nobody accesses hardware that way; anything that needs to touch raw hardware lives in the kernel.

If you're writing a kernel module, you get address information from in-kernel structures, not by accessing /sys/...

Eric Seppanen
Thanks Eric, I am doing this from user space. I hate the way I am having to communicate with the drive but that is the only way that the hardware vendor allows users to find the information I am looking for.
Kevin S
A: 

So I did find something, although it isn't the most elegant solution and it definitely doesn't work everywhere, it has worked on my hardware so I figured I would share.

First, you have to get the address of the SATA Controller from the lspci command like Nikolai showed (the -D just shows the full domain numbers):

# lspci -D
...
0000:00:1f.2 SATA controller: Intel Corporation 82801IR 6 port SATA AHCI Controller
...

Now with this address (0000:00:1f.2) you can go into /sys.

In /sys/bus/pci/devices, your device should be listed:

# ls -l /sys/bus/pci/devices
...
lrwxrwxrwx 1 root root 0 Jan 14 12:35 0000:00:1f.2 -> ../../../devices/pci0000:00/0000:00:1f.2

Now in this directory there will be several hostX directories.

# ls -l /sys/bus/pci/devices/0000\:00\:1f.2/
...
drwxr-xr-x 4 root root    0 Jan 13 12:40 host0
drwxr-xr-x 4 root root    0 Jan 13 12:40 host1
drwxr-xr-x 3 root root    0 Jan 13 12:40 host2
drwxr-xr-x 3 root root    0 Jan 13 12:40 host3
drwxr-xr-x 3 root root    0 Jan 13 12:40 host4
drwxr-xr-x 4 root root    0 Jan 14 08:21 host5
...

In one of these hostX directories, there will be a targetX:X:X directory. This targetX:X:X directory will then have a directory called X:X:X:X (the X's are numbers that can vary).

# ls -R /sys/bus/pci/devices/0000\:00\:1f.2/host0
/sys/bus/pci/devices/0000:00:1f.2/host0:
power  scsi_host:host0  target0:0:0  uevent

/sys/bus/pci/devices/0000:00:1f.2/host0/target0:0:0:
0:0:0:0  power  uevent
...

In the X:X:X:X directory, there is a link named "block:sdX" (where X is a letter). This sdX is the name of the drive that this directory corresponds to.

# ls -l /sys/bus/pci/devices/0000\:00\:1f.2/host0/target0\:0\:0/0\:0\:0\:0/
lrwxrwxrwx 1 root root    0 Jan 14 15:01 block:sda -> ../../../../../../block/sda

So /dev/sda corresponds to host 0 on the SATA Controller at 0000:00:1f.2. Now to find the address that we can use to talk to /dev/sda through inb() and outb() commands, we look in the file named "resource" in /sys/bus/pci/devices/0000:00:1f.2/.

# cat /sys/bus/pci/devices/0000\:00\:1f.2/resource
0x000000000000fe00 0x000000000000fe07 0x0000000000000101
0x000000000000fe10 0x000000000000fe13 0x0000000000000101
0x000000000000fe20 0x000000000000fe27 0x0000000000000101
0x000000000000fe30 0x000000000000fe33 0x0000000000000101
0x000000000000fec0 0x000000000000fedf 0x0000000000000101
0x00000000ff970000 0x00000000ff9707ff 0x0000000000000200
0x0000000000000000 0x0000000000000000 0x0000000000000000

The address we are looking for is fe00, which is on the first line. We want the first line because it is host 0, if it were host 1, we would look on the second line, and host 2 the third line, and so on. The host number was given by the hostX directory that we found earlier. Each line in the resource file is separated into 3 columns:

Column 1 = beginning address Column 2 = end address Column 3 = flags

So this is how I get from /dev/sda to 0xfe00 in order to send commands to the device.

If anybody know any better way to do this, I am all ears...

Kevin S