The simplest network driver

Hi, I am trying to write the simplest network driver that would send whatever through cable.

My configuration is:
Linux machine with some Intel network adapter
Another machine with WireShark

I connected Intel network adapter to second machine and want anything to pop up at wireshark.

For now I have compiled that driver:
LXR / The Linux Cross Reference
and want to refactor it to allow bind to Intel network adapter and send 1 packet in a simplest way.

Expected result is that after 'insmod dummy.ko' I will see 1 packet leaving Intel network adapter in WireShark.

Could you point me to some necessary steps I should do to achieve that?
I guess handling 'ping' with my refactored dummy driver is harder task...
Thanks

Humm, you are not going to get anywhere by refactoring the dummy network driver. The necessary code is not in that driver for you to connect to any physical hardware.

I suggest you look at the code for the e1000 or e1000e driver.

Ok, as I said before expected result is to send one packet. Another thing is that I would like to learn linux networking details.

e1000e driver is quite complicated.
For example at the begining of driver life in kernel space it does only:

init_module {
pci_register_driver(&e1000_driver);
}

No more routines are done.
Well... maybe they are done, but I have no idea where to even look for them.

That's just how device drivers work. It gives the kernel a list of functions when it's loaded, the kernel decides which to call when. It's not like a userspace program where it all starts in main()...

You may need to study the organization of the kernel, not just the source code for one particular driver, to understand it.

Studying the source for some other driver won't tell you how the driver you want works, usually.

May I count on you a bit?
I see, that while performing:

static int __init e1000_init_module(void)
{
       return  pci_register_driver(&e1000_driver);
}

function pointed by .probe is called.

/* PCI Device API Driver */
static struct pci_driver e1000_driver = {
       .name     = e1000e_driver_name,
       .id_table = e1000e_pci_tbl,
       .probe    = e1000_probe,
       .remove   = __devexit_p(e1000_remove),
       .shutdown = e1000_shutdown,
};

Is that behaviour described somewhere?
As I remember, exactly at this moment (pci_register_driver) driver announce set of device IDs he support, and kernel looking at this announcement and into PCI config space of every device binds proper driver to every device. Am I correct?

You really should take the time to read the book Linux Device Drivers by Corbet, Rubini and Kroah-Hartman. Chapter 12 describes PCI drivers in considerable detail.

I suppose you've checked inside Documentation/PCI/ inside the kernel source code?

My understanding of them is far from perfect, mostly limited to editing in extra PCI ID's when manufacturers do shell games me. I don't perfectly understand the internal details, and if I ever did my knowledge would go obsolete in weeks. ;p But I've done a lot of programming making/using/fixing plugin libraries, which makes a lot of what I see in device drivers look familiar.

Imagine a plugin for a media player -- maybe a software visualizer of some sort. The plugin would look like this:

// these names don't get exported in the library.  They're local-only.
static int visualizer_open(void) { return(0); }
static int visualizer_close(void) { return(0); }
static int visualizer_handledata(void *data, int len)
{
        fprintf(stderr, "%d bytes at %p\n", len, data);
        show_music_on_screen_in_amusing_manner(data, len);
        return(0);
}

struct function
{
        char *formats_supported[4];
        int (*open)(void);
        int (*close)(void);
        int (*handledata)(void *data, int len);
} functions={
       { "wav", "mp3", "ogg", NULL },
       visualizer_open, visualizer_close, visualizer_handledata
};

// This function IS exported.
struct function *get_callbacks(void)  {        return(&functions); }

To use it, the media player opens the library, looks up get_callbacks and calls it.

struct function
{
        char *formats_supported[4];
        int (*open)(void);
        int (*close)(void);
        int (*handledata)(void *data, int len);
} *functions;

void *lib=dlopen("crazylinesvisualizer.so", RTLD_NOW);
void *(*getcallback)=dlsym(lib, "get_callbacks");
dlclose(lib);

functions=getcallback();

if(strcmp(functions->formats_supported[0], "wav") != 0)
{
        fprintf(stderr, "visualizer doesn't support wav");
        exit(1);
}

functions->open();
whle(music) functions->handledata(data, len);
functions->close();

Bundling them in a structure like that makes it more efficient -- one load operation instead of one for each function -- and more organized.

Kernel device drivers work just like this. Look in a device driver for a character device and you'll find a big structure containing pointers the driver's own personal open(), close(), read(), write(), ioctl(), mmap(), kitchen_sink(), and other such functions which get called whenever a user calls open, read, write, ioctl, mmap, etc. on that device file in userspace. Even when you build drivers in, they're still in a great big table. That's how the kernel finds them, how the kernel decides what order to initialize them, and how it decides which to use for what PCI id.

I don't think the driver "announces" it, it just keeps a list in that big structure. The kernel checks if it matches any present devices and, if not, doesn't bother initializing it.

Non-PNP things might have to do manual probing to see if a device exists.

2 Likes

I have read "Linux Device Drivers", chapter "Network Drivers", and "PCI Drivers".
Thanks Corona688 for your response.

What if there are in kernel space two drivers supporting the same device id?
.probe function would be run for all of them?

I haven't tried it, but my suspicion is that "probe" would be run for everything. Probing is how you detect non-plug-and-play things which don't advertise their presence, so PCI ID's wouldn't seem to apply.

But PCI drivers, usually not needing to probe, mostly don't probe at all. I think the PCI NE2000 network driver can, but has to be forced to do so by module options at load-time because blindly poking around at I/O addresses can be dangerous. Something unexpected might be occupying the bus resources it expects which could lock the PC or worse. On some IBM laptops, BIOS resources were found at addresses lm_sensors was expecting temperature sensors at, causing probes to brick the system!

One PCI ID for two drivers is a rare situation. Plug-and-play is supposed to be one model to one PCI ID, no ambiguity. If there's two drivers for the same ID, whichever gets loaded first wins. If they're built-in, whichever gets checked first on boot wins. Since the order of built-ins is arbitrary, if you want to control the order, you must make the drivers modules.

A rare situation but it still happens. There's two incompatible revisions of the old realtek 8139 network chip and two different linux kernel drivers -- 8139cp and 8139too. If you load the wrong one, it complains, and tells you to load the other in dmesg. Easy when they're modules, impossible when they're not.

Setup:

  • Intel network card
  • e1000e standard linux driver in autorun
  • myModule; customized e1000e driver by me for debugging reasons

Steps:

  • after power on execute 'rmmod e1000e'
  • 'insmod myModule'
    From my debuging experience I see, that .probe function is fired always after 'insmod myModule' (when e1000e is not present).

Steps

  • after power on do not remove 'e1000e' driver
  • 'insmod myModule'
    Now .probe function from myModule is not executed. Only init_module function operates.

What if I add myModule to autorun? Will .probe function be then executed? I don't know. I have no idea how to add myModule to autorun. :).

I guess .probe is 'must be', because there are important lines like:

.probe function:
{
 netdev = alloc_etherdev
 SET_MODULE_OWNER(netdev);
  netdev->netdev_ops        = &e1000e_netdev_ops;
 err = register_netdev(netdev);
}

static const struct net_device_ops e1000e_netdev_ops = {
    .ndo_open        = e1000_open,    // e.g. ifconfig up
    .ndo_stop        = e1000_close,    // e.g. ifconfig down
    .ndo_start_xmit        = e1000_xmit_frame,
    .ndo_get_stats        = e1000_get_stats,
    .ndo_tx_timeout        = e1000_tx_timeout,
    .ndo_validate_addr    = eth_validate_addr,
};

Your experience trumps my guess. Device ID's trump "probe". If you want to override/ignore PCI IDs, check out the PCI NE2000 driver.

To what?

The kernel doesn't care about which thing calls insmod/modprobe.

If you want to never load the intel one, add the intel one to modules.blacklist

I have checked out PCI NE2000 driver:
LXR / The Linux Cross Reference
Do you mean using 'PCI_ANY_ID' ?

static DEFINE_PCI_DEVICE_TABLE(ne2k_pci_tbl) = {
        { 0x10ec, 0x8029, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RealTek_RTL_8029 },
        { 0x1050, 0x0940, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940 },
        { 0x11f6, 0x1401, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Compex_RL2000 },
        { 0x8e2e, 0x3000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_KTI_ET32P2 },
        { 0x4a14, 0x5000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_NetVin_NV5000SC },
        { 0x1106, 0x0926, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Via_86C926 },
        { 0x10bd, 0x0e34, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_SureCom_NE34 },
        { 0x1050, 0x5a5a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_W89C940F },
        { 0x12c3, 0x0058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80232 },
        { 0x12c3, 0x5598, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Holtek_HT80229 },
        { 0x8c4a, 0x1980, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_Winbond_89C940_8c4a },
        { 0, }

I tried myModule with 'PCI_ANY_ID'. It is visible in dmesg how myModule run .probe for few devices and failed trying to do 'ioremap' (inside .probe routine). Those are:

Host bridge: Intel Corporation 440FX - 82441FX PMC [Natoma] (rev 02)
Subsystem: Red Hat, Inc Qemu virtual machine
Flags: fast devsel

ISA bridge: Intel Corporation 82371SB PIIX3 ISA [Natoma/Triton II]
Subsystem: Red Hat, Inc Qemu virtual machine
Flags: medium devsel

Only for my Intel newtork card whole .probe routine went well.

No, I don't. It was an option that had to be forced with insmod/modprobe like I said.

I don't see the right messages in that driver, it may be a different one. I'll look for it.

---------- Post updated at 09:48 AM ---------- Previous update was at 09:44 AM ----------

eepro.c has these lines:

                printk(KERN_WARNING "eepro_init_module: Probe is very dangerous in ISA boards!\n");
                printk(KERN_WARNING "eepro_init_module: Please add \"autodetect=1\" to force probe\n");