# Programming unknown parallel port card



## RalfPeg (Aug 13, 2018)

Hello!

I've been trying to play around with my parallel port which has no driver or something. According to my research in the internet, "pciconf -l -b" shows necessary information (i.e. the physical address) to mmap it.
The output is this:

`...
none1@pci0:5:7:0:     class=0x070101 card=0x21701c00 chip=0x21701c00 rev=0x0f hdr=0x00
     bar     [10] = type I/O Port, range 32, base 0xb010, size 8, enabled
     bar     [14] = type I/O Port, range 32, base 0xb000, size 8, enabled
...`

The code I'm using for creating some output is this:

```
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>

int main()
{
    int fd;
    unsigned char *p1;
    unsigned char *p2;
    int i;
    
    fd = open("/dev/mem", O_RDWR);
    if(fd == -1)
    {
        printf("error1\n");
        return 1;
    }
    
    p1 = mmap(NULL, sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0xb000);
    p2 = p1 + 10;
    
    getchar();
    
    for(i = 0; i < 4; i++)
    {
        printf("%X\n", *(p1 + i));
        
        *(p1 + i) = 0;
        
        printf("%X\n", *(p1 + i));
        
        getchar();
    }
    
    return 0;
}
```

Write-operations seem to work, which means that the register's values will be 0 until reboot, but the port doesn't put out any signals. What's wrong?


----------



## nalinux (Aug 13, 2018)

Not sure this help, because I'm bad for code, but here's what I use for basic read/write operations

Read :
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <dev/ppbus/ppi.h>
#include <dev/ppbus/ppbconf.h>

int main()
{
static int ppi_fd;
u_int8_t val, n;
char port[] = "/dev/ppi0";

ppi_fd = open(port, O_RDWR);
if( ppi_fd < 0 ) {
    perror(port);
    exit(1);
}

n = ioctl(ppi_fd, PPIGDATA, &val);
if( n < 0 ) {
    perror("ioctl PPIGDATA");
    exit(1);
    }

printf("Valeur sur le port: %d 0x%X \n", val, val);

return val;
}

##############
Write :
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <dev/ppbus/ppi.h>
#include <dev/ppbus/ppbconf.h>

int main(int argc, char **argv)
{
  char port[] = "/dev/ppi0";
  int n;
  int val;
  int ppi_fd;

if (argc!=2) 
      printf("USAGE: pp_write [DATA], ie. :\n pp_write 128\n pp_write 0xff\n"), exit(1); 
if (sscanf(argv[1],"%i",&val)!=1)
      fprintf(stderr, "Error: DATA entered is not a number.\n"), exit(1);

ppi_fd = open(port, O_RDWR);
if( ppi_fd < 0 ) {
    perror(port);
    exit(1);
}

n = ioctl(ppi_fd, PPISDATA, &val);
}

###########

Must be used as root.


----------



## nalinux (Aug 13, 2018)

# dmesg | grep ppc
ppc0: <Parallel port> port 0x378-0x37f irq 7 on acpi0
ppc0: Generic chipset (NIBBLE-only) in COMPATIBLE mode
ppbus0: <Parallel port bus> on ppc0


----------



## RalfPeg (Aug 13, 2018)

There is no device driver and therefore no /dev/parallelport. 

I expected to have some output by writing to these physical addresses , but somehow it doesn't work.


----------



## SirDice (Aug 14, 2018)

It's been quite a while since I bit-banged actual hardware but I do believe these ports use port-mapped I/O, not memory-mapped I/O.

https://en.wikipedia.org/wiki/Memory-mapped_I/O


----------



## RalfPeg (Aug 14, 2018)

But this would mean that I'll have to use /dev/port (afaik) but I don't have /dev/port. How can I get it?

By the way: After I finished work I started back to Windows (FreeBSD is terminal only) and my sound card didn't work anymore and Windows' trouble shooting made the LED glow. WTF?


----------



## SirDice (Aug 14, 2018)

RalfPeg said:


> But this would mean that I'll have to use /dev/port (afaik) but I don't have /dev/port. How can I get it?


No, that's not what I meant. PMIO uses specific I/O CPU instructions to move data. MMIO uses memory addresses, so you can use the 'normal' CPU instructions to move data to/from those memory addresses. 

But to be perfectly honest, that's based on really old hardware I had to learn from. I'm quite fluent in 6502/6510 and 680x0 assembler, both used memory-mapped I/O. I've also dabbled a bit with Z80 and 8085 based micro-controllers, which use port-mapped I/O. The way you code for PMIO and MMIO is quite different because you need different CPU instructions. 

The 8086 (and by extension everything that's based on it, including the latest cores) is in essence a PMIO CPU (it has specific CPU instructions for accessing I/O hardware). And the parallel port is probably as old as the first IBM PCs, so I suspect those were programmed using PMIO instructions. Now comes the tricky part though, there's nothing stopping you from using MMIO on a PMIO CPU. They're just memory addresses you read from or write to after all. So it's perfectly fine to build MMIO hardware for a PMIO CPU and use it. And that's where it gets fuzzy for me. I'm not sure exactly _how_ the parallel port fits into the design of a PC. Because that will determine if you need to use PMIO CPU instructions to read/write from it or if you can just treat it as a piece of 'memory'. 

Normally all this is handled by the driver and you just read/write to the "device". It's the driver's job to translate this to signals the actual hardware needs to push your data around.


----------



## RalfPeg (Aug 15, 2018)

The problem about the driver is that there is no driver available; dmesg says "no driver attached". It's some no name card for 10€ and the manufacturer's homepage is in chinese with deadlinks only. And writing a driver on my own will be the next step after getting signals directly, I guess.

Anyway, I searched the Internet about using PMIO and, after doing some experiments, it works now at least for the data port; control port and state port somehow don't work.

That's the test program for reading and writing to the parallel port's data pins:


```
#include <unistd.h>
#include <fcntl.h>

#include <stdio.h>

#include <sys/types.h>
#include <sys/ioctl.h>
#include <dev/io/iodev.h>
#include <machine/iodev.h>

int main()
{
    int fd;
    
    struct iodev_pio_req io;
    
    fd = open("/dev/io", O_RDWR);
    
    io.access = IODEV_PIO_WRITE;
    io.port = 0xb0000;
    io.width = 1;
    io.val = 0xFF;
    
    ioctl(fd, IODEV_PIO, &io); //Set pins to high
    
    io.access = IODEV_PIO_READ;
    
    for(;;)
    {
        ioctl(fd, IODEV_PIO, &io); //Reads pins' signal
        
        printf("value: %X\n", io.val);
        
        getchar();
    }
    
    return 0;
}
```

use "man io" and "man ioctl" for details.

Thanks for your help.


----------

