My Device Isn’t Supported : Is There A Quick Fix ?

By fieldstudy

As up to date and as diligent as any OS company tries to be with its software driver support, the rate of change of hardware, especially variants of existing devices, out paces anything that we can reasonably keep up with. So, what’s a developer to do when they buy a shiny new motherboard and QNX Neutrino doesn’t detect that new GigE port ?

I get asked this type of question at least once a week. Quite often, we’ve added support for the new device in our source tree, but haven’t released the new version of the driver yet. We do regular releases for sure, but not daily…

Well, there are already a bunch of drivers delivered as part of Neutrino, so it is quite possible that we *do* have a driver that would work, if it only ‘knew’ that the new device is just like one it already recognizes. There are a couple of easy ways to tell a driver about a new device, so here’s what to try if you hit this issue… No guarantees of course, but it can save a lot of heartburn.

Find the PCI IDs

Assuming that the new device is a PCI (or PCI-X or PCI-Express) device, which is a fair bet these days, it has 2 16-bit associated values called its device and vendor IDs, that together identify the device. It is these numbers that Neutrino uses to decide which driver to use to manage the device. Each driver has a (possibly extensive) list of devices that it knows are for it to manage, but it is also possible to pass a specific device/vendor ID pair to a driver and ask it to try to manage the device.

There are several databases that keep lists of these IDs

e.g. http://pcidatabase.com/

and QNX keeps a list of supported devices here:

http://www.qnx.com/developers/hardware_support/index.html

So, if a device isn’t running and you think a specific driver should work with it, the first thing to do is to find its PCI IDs. To do that, on a booted Neutrino system, in a shell run ‘pci -v | more’ to get output like this:

PCI version    = 2.10Class          = Bridge (Host/PCI)
Vendor ID      = 8086h, Intel Corporation
Device ID      = 7190h,  440BX/ZX/DX - 82443BX/ZX/DX Host bridge
PCI index      = 0h
Class Codes    = 060000h
Revision ID    = 1h
Bus number     = 0
Device number  = 0
Function num   = 0
Status Reg     = 210h
Command Reg    = 6h
Header type    = 0h Single-function
BIST           = 0h Build-in-self-test not supported
Latency Timer  = 0h
Cache Line Size= 0h
Subsystem Vendor ID = 15adh
Subsystem ID        = 1976h
Max Lat        = 0ns
Min Gnt        = 0ns
PCI Int Pin    = NC
Interrupt line = 0
Class          = Bridge (PCI/PCI)
Vendor ID      = 8086h, Intel Corporation
Device ID      = 7191h,  440BX/ZX/DX - 82443BX/ZX/DX AGP bridge
PCI index      = 0h
Class Codes    = 060400h
Revision ID    = 1h
Bus number     = 0
Device number  = 1
Function num   = 0
Status Reg     = 220h
Command Reg    = 11fh
Header type    = 1h Single-function
BIST           = 0h Build-in-self-test not supported
Latency Timer  = 0h
Cache Line Size= 0h
Primary Bus Number       = 0h
Secondary Bus Number     = 1h
Subordinate Bus Number   = 1h
Secondary Latency Timer  = 40h
I/O Base                 = f0h
I/O Limit                = 0h
Secondary Status         = 2a0h
Memory Base              = fff0h
Memory Limit             = 0h
Prefetchable Memory Base = fff0h
Prefetchable Memory Limit= 0h
Prefetchable Base Upper 32 Bits  = 0h
Prefetchable Limit Upper 32 Bits = 0h
I/O Base Upper 16 Bits   = 0h
I/O Limit Upper 16 Bits  = 0h
Bridge Control           = 80h
PCI Int Pin              = NC
Interrupt line           = 0
CPU Interrupt            = 0h
[Snip - this list can be *long* for a complex system]
Class          = Network (Ethernet)
Vendor ID      = 1022h, Advanced Micro Devices [AMD]
Device ID      = 2000h,  79c970 [PCnet32 LANCE]
PCI index      = 0h
Class Codes    = 020000h
Revision ID    = 10h
Bus number     = 0
Device number  = 17
Function num   = 0
Status Reg     = 280h
Command Reg    = 7h
Header type    = 0h Single-function
BIST           = 0h Build-in-self-test not supported
Latency Timer  = 40h
Cache Line Size= 0h
PCI IO Address  = 1080h length 128 enabled
Subsystem Vendor ID = 1022h
Subsystem ID        = 2000h
PCI Expansion ROM = 0h length 65536 disabled
Max Lat        = 255ns
Min Gnt        = 6ns
PCI Int Pin    = INT A
Interrupt line = 9
CPU Interrupt  = 9h

Find the device that you’re interested in and record the vendor and device PCI IDs that are listed for it.

Then choose one of the 2 methods below to get the driver going…

Quick command line test

Drivers in QNX Neutrino can be stopped/killed/restarted independently, so one thing to do is to kill and restart the driver that you think is the right one to use, passing the PCI IDs to it as command line parameters:

For instance, there are *many* variants of the Intel Gigabit Ethernet device that we support via our devn-i82544.so device driver. Happily, our driver generally supports the new variants very nicely.

So, a simple test is to run:

slay io-net

[slay is a 'kill by process name' executable that will end the io-net network manager process, which runs network device drivers.]

Then, restart io-net, specifying the i82544 driver and the specific ID that needs to be provided:

io-net -ptcpip -di82544 vid=<VendorID>,did=<DeviceID>

For this driver, the Vendor ID is typically 0×8086, since this is Intel’s main vendor ID – very droll.

The ‘-ptcpip’ part means ‘run the TCP/IP protocol stack’. You might add ‘-pqnet’ too if you want to run QNET.

If this works, your new device will be created in /dev/io-net, and running ‘ifconfig -a’ will show your new device or devices.

Make The Change Permanent

So, if you tried the command line option, and things worked well, you probably want to make the change permanent, so the driver starts up properly every time you reboot.

If you make your own custom boot image, you can just embed the io-net command from above into your build file, rebuild and you’re done.

More often, on X86 boxes, customers tend to use our diskboot process to manage driver startup. This uses a set of tables in /etc/system/enum/devices – one for audio devices, one for net(work) devices etc.

These tables look a bit confusing at first, but adding a new device is pretty simple – you just copy an existing line or lines for the driver you want to work with, modify it for your IDs, then reboot to see if it works…

e.g. to add a line for the Intel Gigabit Ethernet driver, you edit (e.g. with ‘vi’ ) /etc/system/enum/devices/net

and search for 82544

You’ll see a bunch of lines like this:

device(pci, ven=$(PCI_VEND_INTEL), dev=1008)    # Intel 82544EI Gigabit
device(pci, ven=$(PCI_VEND_INTEL), dev=1009)    # Intel 82544EI Gigabit
device(pci, ven=$(PCI_VEND_INTEL), dev=100c)    # Intel 82544GC Gigabit

… lots more similar lines

Ending with:

tag(devn)
 append(legacy, ",nonet")
 requires($(IONET_CMD),)
 uniq(netnum, devn-en, 0)
 mount(-Tio-net "-opci=$(index),vid=0x$(ven),did=0x$(dev)" /lib/dll/devn-i82544.so, "/dev/io-net/en$(netnum)")
 use(symbolic=netmgr)

This last set of lines are the magic that launch the i82544 driver with the correct vendor and device IDs.

All you need to do to add a new device to this list is copy one of the device lines and modify the dev=<xxxx> lines

e.g.

device(pci, ven=$(PCI_VEND_INTEL), dev=1099)

where 0×1099 is the new device’s PCI device ID.

Save the file, and then reboot (because diskboot only runs at startup) and hopefully your device will show up…

If it does, great – you’re done, and can get back to your real job of getting your product built and tested.

If it doesn’t, let QNX know – it really helps us to know what you need !

Leave a Reply