Making a kmod

From: Aryeh Friedman <aryeh.friedman_at_gmail.com>
Date: Sat, 18 Oct 2025 21:05:00 UTC
I need to make a kernel mod that connects to a Vantech 2S1P PCIe
serial/par. combo card that has a 74LS02 hooked up to in roughly the
same way that I2C is interfaced to in lpbb(4) is for bit banging on
the LS02.   I got /dev/lpt0, etc. on the bhyve host I am used and have
passed it through and the guest sees the 2S1P but thats where I stand
and to farther it appears I neeed a kmod since the guest is seeing the
device but not attaching a driver:

pci0: <simple comms, UART> at device 3.0 (no driver attached)
pci0: <simple comms, UART> at device 5.0 (no driver attached)
pci0: <simple comms, parallel port> at device 6.0 (no driver attached)

none0@pci0:0:3:0:    class=0x070002 rev=0x00 hdr=0x00 vendor=0x9710
device=0x9912 subvendor=0xa000 subdevice=0x1000
    vendor     = 'MosChip Semiconductor Technology Ltd.'
    device     = 'PCIe 9912 Multi-I/O Controller'
    class      = simple comms
    subclass   = UART
none1@pci0:0:5:0:    class=0x070002 rev=0x00 hdr=0x00 vendor=0x9710
device=0x9912 subvendor=0xa000 subdevice=0x1000
    vendor     = 'MosChip Semiconductor Technology Ltd.'
    device     = 'PCIe 9912 Multi-I/O Controller'
    class      = simple comms
    subclass   = UART
none2@pci0:0:6:0:    class=0x070103 rev=0x00 hdr=0x00 vendor=0x9710
device=0x9912 subvendor=0xa000 subdevice=0x2000
    vendor     = 'MosChip Semiconductor Technology Ltd.'
    device     = 'PCIe 9912 Multi-I/O Controller'
    class      = simple comms
    subclass   = parallel port

I also have this simple kmod demo working:
aryeh@sarek2048% more ~root/foo.c
// io_test.c
// Usage: ./io_test <base-port-hex> [value-hex]
// Example: ./io_test 0xe010 0xff

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>

static inline void outb(uint16_t port, uint8_t val) {
    asm volatile ("outb %0, %1" : : "a"(val), "Nd"(port));
}
static inline uint8_t inb(uint16_t port) {
    uint8_t val;
    asm volatile ("inb %1, %0" : "=a"(val) : "Nd"(port));
    return val;
}

int main(int argc, char **argv) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <base-port-hex> [value-hex]\n", argv[0]);
        return 1;
    }

    uint16_t base = (uint16_t)strtoul(argv[1], NULL, 0);
    uint8_t val = (argc >= 3) ? (uint8_t)strtoul(argv[2], NULL, 0) : 0x01;

    int fd = open("/dev/io", O_RDWR);
    if (fd < 0) {
        perror("open(/dev/io)");
        return 1;
    }

    printf("Writing 0x%02x to data (port 0x%04x)\n", val, base);
    outb(base, val);
    usleep(1000); // tiny delay

    uint8_t status = inb(base + 1);
    uint8_t control = inb(base + 2);
    uint8_t data_read = inb(base); // some cards reflect reads

    printf("Read: data=0x%02x status=0x%02x control=0x%02x\n",
           data_read, status, control);

    close(fd);
    return 0;
}

-- 
Aryeh M. Friedman, Lead Developer, http://www.PetiteCloud.org