http://lwn.net/Articles/315832/

/*
   uio_helper.c
   UIO helper functions.

   Copyright (C) 2007 Hans J. Koch

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 2 as
   published by the Free Software Foundation.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>

#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

#include "uio_helper.h"

int uio_get_mem_size(struct uio_info_t* info, int map_num)
{
    int ret;
    char filename[64];
    info->maps[map_num].size = UIO_INVALID_SIZE;
    sprintf(filename, "/sys/class/uio/uio%d/maps/map%d/size",
        info->uio_num, map_num);
    FILE* file = fopen(filename,"r");
    if (!file) return -1;
    ret = fscanf(file,"0x%x",&info->maps[map_num].size);
    fclose(file);
    if (ret<0) return -2;
    return 0;
}

int uio_get_mem_addr(struct uio_info_t* info, int map_num)
{
    int ret;
    char filename[64];
    info->maps[map_num].addr = UIO_INVALID_ADDR;
    sprintf(filename, "/sys/class/uio/uio%d/maps/map%d/addr",
        info->uio_num, map_num);
    FILE* file = fopen(filename,"r");
    if (!file) return -1;
    ret = fscanf(file,"0x%lx",&info->maps[map_num].addr);
    fclose(file);
    if (ret<0) return -2;
    return 0;
}

int uio_get_port_size(struct uio_info_t* info, int port_num)
{
    int ret;
    char filename[64];
    info->ports[port_num].size = UIO_INVALID_SIZE;
    sprintf(filename, "/sys/class/uio/uio%d/portio/port%d/size",
        info->uio_num, port_num);
    FILE* file = fopen(filename,"r");
    if (!file) return -1;
    ret = fscanf(file,"0x%x",&info->ports[port_num].size);
    fclose(file);
    if (ret<0) return -2;
    return 0;
}

int uio_get_port_start(struct uio_info_t* info, int port_num)
{
    int ret;
    char filename[64];
    info->ports[port_num].start = UIO_INVALID_ADDR;
    sprintf(filename, "/sys/class/uio/uio%d/portio/port%d/start",
        info->uio_num, port_num);
    FILE* file = fopen(filename,"r");
    if (!file) return -1;
    ret = fscanf(file,"0x%lx",&info->ports[port_num].start);
    fclose(file);
    if (ret<0) return -2;
    return 0;
}


int uio_get_event_count(struct uio_info_t* info)
{
    int ret;
    char filename[64];
    info->event_count = 0;
    sprintf(filename, "/sys/class/uio/uio%d/event", info->uio_num);
    FILE* file = fopen(filename,"r");
    if (!file) return -1;
    ret = fscanf(file,"%lu",&info->event_count);
    fclose(file);
    if (ret<0) return -2;
    return 0;
}

int line_from_file(char *filename, char *linebuf)
{
    char *s;
    int i;
    memset(linebuf, 0, UIO_MAX_NAME_SIZE);
    FILE* file = fopen(filename,"r");
    if (!file) return -1;
    s = fgets(linebuf,UIO_MAX_NAME_SIZE,file);
    if (!s) return -2;
    for (i=0; (*s)&&(i<UIO_MAX_NAME_SIZE); i++) {
        if (*s == '\n') *s = 0;
        s++;
    }
    return 0;
}

int uio_get_name(struct uio_info_t* info)
{
    char filename[64];
    sprintf(filename, "/sys/class/uio/uio%d/name", info->uio_num);

    return line_from_file(filename, info->name);
}

int uio_get_version(struct uio_info_t* info)
{
    char filename[64];
    sprintf(filename, "/sys/class/uio/uio%d/version", info->uio_num);

    return line_from_file(filename, info->version);
}

int uio_get_all_info(struct uio_info_t* info)
{
    int i;
    if (!info)
        return -1;
    if ((info->uio_num < 0)||(info->uio_num > UIO_MAX_NUM))
        return -1;
    for (i = 0; i < MAX_UIO_MAPS; i++) {
        uio_get_mem_size(info, i);
        uio_get_mem_addr(info, i);
        uio_get_port_start(info, i);
        uio_get_port_size(info, i);
    }
    uio_get_event_count(info);
    uio_get_name(info);
    uio_get_version(info);
    return 0;
}

int dev_attr_filter(char *filename)
{
    struct stat filestat;

    if (lstat(filename, &filestat))
        return 0;
    if (S_ISREG(filestat.st_mode))
        return 1;
    return 0;
}

int uio_get_device_attributes(struct uio_info_t* info)
{
    struct dirent **namelist;
    struct uio_dev_attr_t *attr, *last = NULL;
    char fullname[96];
    int n;

    info->dev_attrs = NULL;
    sprintf(fullname, "/sys/class/uio/uio%d/device", info->uio_num);
    n = scandir(fullname, &namelist, 0, alphasort);
    if (n < 0)
        return -1;

    while(n--) {
        sprintf(fullname, "/sys/class/uio/uio%d/device/%s",
            info->uio_num, namelist[n]->d_name);
        if (!dev_attr_filter(fullname))
            continue;
        attr = malloc(sizeof(struct uio_dev_attr_t));
        if (!attr)
            return -1;
        strncpy(attr->name, namelist[n]->d_name, UIO_MAX_NAME_SIZE);
        free(namelist[n]);
        if (line_from_file(fullname, attr->value)) {
            free(attr);
            continue;
        }

        if (!info->dev_attrs)
            info->dev_attrs = attr;
        else
            last->next = attr;
        attr->next = NULL;
        last = attr;
    }
    free(namelist);

    return 0;
}

void uio_free_dev_attrs(struct uio_info_t* info)
{
    struct uio_dev_attr_t *p1, *p2;
    p1 = info->dev_attrs;
    while (p1) {
        p2 = p1->next;
        free(p1);
        p1 = p2;
    }
    info->dev_attrs = NULL;
}

void uio_free_info(struct uio_info_t* info)
{
    struct uio_info_t *p1,*p2;
    p1 = info;
    while (p1) {
        uio_free_dev_attrs(p1);
        p2 = p1->next;
        free(p1);
        p1 = p2;
    }
}

int uio_num_from_filename(char* name)
{
    enum scan_states { ss_u, ss_i, ss_o, ss_num, ss_err };
    enum scan_states state = ss_u;
    int i=0, num = -1;
    char ch = name[0];
    while (ch && (state != ss_err)) {
        switch (ch) {
            case 'u': if (state == ss_u) state = ss_i;
                  else state = ss_err;
                  break;
            case 'i': if (state == ss_i) state = ss_o;
                  else state = ss_err;
                  break;
            case 'o': if (state == ss_o) state = ss_num;
                  else state = ss_err;
                  break;
            default:  if (  (ch>='0') && (ch<='9')
                      &&(state == ss_num) ) {
                    if (num < 0) num = (ch - '0');
                    else num = (num * 10) + (ch - '0');
                  }
                  else state = ss_err;
        }
        i++;
        ch = name[i];
    }
    if (state == ss_err) num = -1;
    return num;
}

static struct uio_info_t* info_from_name(char* name, int filter_num)
{
    struct uio_info_t* info;
    int num = uio_num_from_filename(name);
    if (num < 0)
        return NULL;
    if ((filter_num >= 0) && (num != filter_num))
        return NULL;

    info = malloc(sizeof(struct uio_info_t));
    if (!info)
        return NULL;
    memset(info,0,sizeof(struct uio_info_t));
    info->uio_num = num;

    return info;
}

struct uio_info_t* uio_find_devices(int filter_num)
{
    struct dirent **namelist;
    struct uio_info_t *infolist = NULL, *infp, *last = NULL;
    int n;

    n = scandir("/sys/class/uio", &namelist, 0, alphasort);
    if (n < 0)
        return NULL;

    while(n--) {
        infp = info_from_name(namelist[n]->d_name, filter_num);
        free(namelist[n]);
        if (!infp)
            continue;
        if (!infolist)
            infolist = infp;
        else
            last->next = infp;
        last = infp;
    }
    free(namelist);

    return infolist;
}

void uio_single_mmap_test(struct uio_info_t* info, int map_num)
{
    info->maps[map_num].mmap_result = UIO_MMAP_NOT_DONE;
    if (info->maps[map_num].size <= 0)
        return;
    info->maps[map_num].mmap_result = UIO_MMAP_FAILED;
    char dev_name[16];
    sprintf(dev_name,"/dev/uio%d",info->uio_num);
    int fd = open(dev_name,O_RDONLY);
    if (fd < 0)
        return;
    void* map_addr = mmap( NULL,
                   info->maps[map_num].size,
                   PROT_READ,
                   MAP_SHARED,
                   fd,
                   map_num*getpagesize());
    if (map_addr != MAP_FAILED) {
        info->maps[map_num].mmap_result = UIO_MMAP_OK;
        munmap(map_addr, info->maps[map_num].size);
    }
    close(fd);
}

void uio_mmap_test(struct uio_info_t* info)
{
    int map_num;
    for (map_num= 0; map_num < MAX_UIO_MAPS; map_num++)
        uio_single_mmap_test(info, map_num);
}


/*
   uioport.c - UIO I/O port utility

   usage: ./uioport uiostring inb   addr
          ./uioport uiostring outb  addr val

   git clone git://ifup.org/philips/uioport.git

   Copyright (C) 2009 Brandon D. Philips <bran...@ifup.org>

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License version 2 as
   published by the Free Software Foundation.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/capability.h>
#include <sys/io.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include "uio_helper.h"

#define NUMGROUPS 255

int uio_filter = -1;

/*
 * uid_has_write_perms - ensure the executing user has write permission to the
 * /dev/uid* node
 */
int uid_has_write_perms(char *path)
{
    gid_t *groups;
    struct passwd *pw;
    cap_t cap;
    cap_flag_value_t cap_val;
    uid_t uid;
    struct stat st;
    int ngroups = NUMGROUPS;
    int ret = -EPERM;
    int j;
   
    cap = cap_get_proc();
    cap_get_flag(cap, CAP_SYS_RAWIO, CAP_PERMITTED, &cap_val);
    if (cap_val != CAP_SET) {
        printf("must be ran with CAP_SYS_RAWIO\n");
        return -EPERM;
    }

    uid = getuid();
    pw = getpwuid(uid);
    if (pw == NULL) {
        perror("getpwnam");
        exit(EXIT_FAILURE);
    }

    groups = malloc(ngroups * sizeof (gid_t));
    if (groups == NULL) {
        perror("malloc");
        exit(EXIT_FAILURE);
    }

    if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups) == -1) {
        fprintf(stderr, "getgrouplist() returned -1");
        exit(EXIT_FAILURE);
    }
   
    if (stat(path, &st) < 0) {
        perror("stat");   
        exit(EXIT_FAILURE);
    }

    /* If user has write permissions to path and matches the executing user
     * then allow io port operations */
    if (st.st_mode & S_IWUSR) {
        if (uid == st.st_uid) {
            ret = 0;
            goto out_free;
        }
    }
   
    /* If group has write permissions to path and user belongs to that
     * group allow io port operations */
    if (st.st_mode & S_IWGRP) {
        for (j = 0; j < ngroups; j++) {
            if (groups[j] == st.st_gid) {
                ret = 0;
                goto out_free;
            }
        }
    }

    if (st.st_mode & S_IWOTH) {
        ret = 0;
        goto out_free;
    }

out_free:
    free(groups);
    cap_free(cap);

    return ret;
}

int addr_in_range(struct uio_info_t *p, int op_addr, int op_size)
{
    int base = p->ports[0].start;
    int size = p->ports[0].size;

    if ((op_addr > (base + size)) || ((op_addr + op_size) > (base + size))) {
        fprintf(stderr, "addr too large (%x, %x) for range (%x, %x)\n",
             op_addr, op_addr + op_size, base, base + size);
        return -1;
    }

    if ((op_addr < base) || ((op_addr + op_size) < base)) {
        fprintf(stderr, "addr too small (%x, %x) for range (%x, %x)\n",
            op_addr, op_addr + op_size, base, base + size);
        return -1;
    }

    return 0;
}

int do_out(struct uio_info_t *p, char *op, int addr, int val)
{
    if (strcmp(op, "outb") == 0) {
        if (addr_in_range(p, addr, 1) < 0)
            return -1;
        outb(val, addr);
    } else if (strcmp(op, "outw") == 0) {
        if (addr_in_range(p, addr, 2) < 0)
            return -1;
        outw(val, addr);
    } else if (strcmp(op, "outl") == 0) {
        if (addr_in_range(p, addr, 4) < 0)
            return -1;
        outl(val, addr);
    } else {
        fprintf(stderr, "Invalid out operation\n");
        return -1;
    }

    return 0;
}

int do_in(struct uio_info_t *p, char *op, int addr)
{
    if (strcmp(op, "inb") == 0) {
        if (addr_in_range(p, addr, 1) < 0)
            return -1;
        printf("%x\n", inb(addr));
    } else if (strcmp(op, "inw") == 0) {
        if (addr_in_range(p, addr, 2) < 0)
            return -1;
        printf("%x\n", inw(addr));
    } else if (strcmp(op, "inl") == 0) {
        if (addr_in_range(p, addr, 4) < 0)
            return -1;
        printf("%x\n", inl(addr));
    } else {
        fprintf(stderr, "Invalid in operation\n");
        return -1;
    }

    return 0;
}

int main(int argc, char **argv)
{
    struct uio_info_t *info_list, *p;
    /* TODO: make this safer */
    char dev_name[16];
    char *uioname, *op;
    int addr, val, ret = 0;

    if (argc < 4) {
        fprintf(stderr, "Not enough parameters\n");
        return -1;
    }

    uioname = argv[1];
    op = argv[2];
    if (sscanf(argv[3], "%x", &addr) < 1) {
        fprintf(stderr, "Invalid address parameter\n");
        return -1;
    }

    /* TODO: Add filter argument */
        info_list = uio_find_devices(uio_filter);
        if (!info_list) {
                printf("No UIO devices.\n");
        return -ENODEV;
    }

        p = info_list;

        while (p) {
                uio_get_all_info(p);
                uio_get_device_attributes(p);
        if(strcmp(p->name, uioname) == 0)
            break;
                p = p->next;
        }

    if (!p) {
        fprintf(stderr, "No UIO device with name: %s\n", uioname);
        return -ENODEV;
    }

    sprintf(dev_name,"/dev/uio%d", p->uio_num);
   
    if (uid_has_write_perms(dev_name) < 0) {
        fprintf(stderr, "Insufficient permissions\n");
        return -EPERM;
    }

    if (iopl(3)) {
        fprintf(stderr, "Unable to iopl(3)\n");
        return -EPERM;
    }

    if ((strcmp(op, "out") > 0) && (argc == 5)) {
        if (sscanf(argv[4], "%x", &val) < 1) {
            fprintf(stderr, "Invalid value parameter\n");
            return -1;
        }

        ret = do_out(p, op, addr, val);
    }
   
    if ((strcmp(op, "in") > 0) && (argc == 4)) {
        ret = do_in(p, op, addr);
    }

    return ret;
}



====================

uio: add the uio_aec driver

From:   Brandon Philips <bran...@ifup.org>
To:   h...@linutronix.de, Greg KH <gre...@suse.de>
Subject:   uio: add the uio_aec driver
Date:   Tue, 20 Jan 2009 12:47:29 -0800
Message-ID:   <20090120204729.ga26...@jenkins.ifup.org>
Cc:   linux-ker...@vger.kernel.org
Archive-link:   Article, Thread

UIO driver for the Adrienne Electronics Corporation PCI time code
device.

This device differs from other UIO devices since it uses I/O ports instead of
memory mapped I/O. In order to make it possible for UIO to work with this
device a utility, uioport, can be used to read and write the ports.

uioport is designed to be a setuid program and checks the permissions of
the /dev/uio* node and if the user has write permissions it will use
iopl and out*/in* to access the device.

[1] git clone git://ifup.org/philips/uioport.git

Signed-off-by: Brandon Philips <bran...@ifup.org>

---
 drivers/uio/Kconfig        |   18 ++++
 drivers/uio/Makefile       |    1 
 drivers/uio/uio_aec.c      |  177 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/uio_driver.h |    1 
 4 files changed, 197 insertions(+)

Index: linux-2.6/drivers/uio/Makefile
===================================================================
--- linux-2.6.orig/drivers/uio/Makefile
+++ linux-2.6/drivers/uio/Makefile
@@ -3,4 +3,5 @@ obj-$(CONFIG_UIO_CIF)	+= uio_cif.o
 obj-$(CONFIG_UIO_PDRV)	+= uio_pdrv.o
 obj-$(CONFIG_UIO_PDRV_GENIRQ)	+= uio_pdrv_genirq.o
 obj-$(CONFIG_UIO_SMX)	+= uio_smx.o
+obj-$(CONFIG_UIO_AEC)	+= uio_aec.o
 obj-$(CONFIG_UIO_SERCOS3)	+= uio_sercos3.o
Index: linux-2.6/include/linux/uio_driver.h
===================================================================
--- linux-2.6.orig/include/linux/uio_driver.h
+++ linux-2.6/include/linux/uio_driver.h
@@ -91,5 +91,6 @@ extern void uio_event_notify(struct uio_
 #define UIO_MEM_PHYS	1
 #define UIO_MEM_LOGICAL	2
 #define UIO_MEM_VIRTUAL 3
+#define UIO_MEM_PORT	4
 
 #endif /* _LINUX_UIO_DRIVER_H_ */
Index: linux-2.6/drivers/uio/Kconfig
===================================================================
--- linux-2.6.orig/drivers/uio/Kconfig
+++ linux-2.6/drivers/uio/Kconfig
@@ -58,6 +58,24 @@ config UIO_SMX
 
 	  If you compile this as a module, it will be called uio_smx.
 
+config UIO_AEC
+	tristate "AEC video timestamp device"
+	depends on PCI
+	default n
+	help
+
+	  UIO driver for the Adrienne Electronics Corporation PCI time
+	  code device.
+
+	  This device differs from other UIO devices since it uses I/O
+	  ports instead of memory mapped I/O. In order to make it
+	  possible for UIO to work with this device a utility, uioport,
+	  can be used to read and write the ports:
+
+	    git clone git://ifup.org/philips/uioport.git
+
+	  If you compile this as a module, it will be called uio_aec.
+
 config UIO_SERCOS3
 	tristate "Automata Sercos III PCI card driver"
 	default n
Index: linux-2.6/drivers/uio/uio_aec.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/uio/uio_aec.c
@@ -0,0 +1,177 @@
+/*
+ * uio_aec.c -- simple driver for Adrienne Electronics Corp time code PCI device
+ *
+ * Copyright (C) 2008 Brandon Philips <bran...@ifup.org>
+ *
+ *   This program is free software; you can redistribute it and/or modify it
+ *   under the terms of the GNU General Public License version 2 as published
+ *   by the Free Software Foundation.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License along
+ *   with this program; if not, write to the Free Software Foundation, Inc., 59
+ *   Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <linux/uio_driver.h>
+
+#define PCI_VENDOR_ID_AEC 0xaecb
+#define PCI_DEVICE_ID_AEC_VITCLTC 0x6250
+
+#define INT_ENABLE_ADDR		0xFC
+#define INT_ENABLE		0x10
+#define INT_DISABLE		0x0
+
+#define INT_MASK_ADDR		0x2E
+#define INT_MASK_ALL		0x3F
+
+#define INTA_DRVR_ADDR		0xFE
+#define INTA_ENABLED_FLAG	0x08
+#define INTA_FLAG		0x01
+
+#define MAILBOX			0x0F
+
+static struct pci_device_id ids[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_AEC, PCI_DEVICE_ID_AEC_VITCLTC), },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, ids);
+
+static irqreturn_t aectc_irq(int irq, struct uio_info *dev_info)
+{
+	void __iomem *int_flag = dev_info->mem[0].internal_addr
+					+ INTA_DRVR_ADDR;
+	unsigned char status = ioread8(int_flag);
+
+
+	if ((status & INTA_ENABLED_FLAG) && (status & INTA_FLAG)) {
+		/* application writes 0x00 to 0x2F to get next interrupt */
+		status = ioread8(dev_info->mem[0].internal_addr + MAILBOX);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static void print_board_data(struct uio_info *i)
+{
+	printk(KERN_INFO "PCI-TC board vendor: %x%x number: %x%x"
+		" revision: %c%c\n",
+		ioread8(i->mem[0].internal_addr + 0x01),
+		ioread8(i->mem[0].internal_addr + 0x00),
+		ioread8(i->mem[0].internal_addr + 0x03),
+		ioread8(i->mem[0].internal_addr + 0x02),
+		ioread8(i->mem[0].internal_addr + 0x06),
+		ioread8(i->mem[0].internal_addr + 0x07));
+}
+
+static int __devinit probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	struct uio_info *info;
+	int ret;
+
+	info = kzalloc(sizeof(struct uio_info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	if (pci_enable_device(dev))
+		goto out_free;
+
+	if (pci_request_regions(dev, "aectc"))
+		goto out_disable;
+
+	info->name = "aectc";
+	info->mem[0].addr = pci_resource_start(dev, 0);
+	if (!info->mem[0].addr)
+		goto out_release;
+	info->mem[0].internal_addr = pci_iomap(dev, 0, 0);
+	if (!info->mem[0].internal_addr)
+		goto out_release;
+	info->mem[0].size = pci_resource_len(dev, 0);
+	info->mem[0].memtype = UIO_MEM_PORT;
+
+	info->version = "0.0.1";
+	info->irq = dev->irq;
+	info->irq_flags = IRQF_DISABLED | IRQF_SHARED;
+	info->handler = aectc_irq;
+
+	print_board_data(info);
+	ret = uio_register_device(&dev->dev, info);
+	if (ret)
+		goto out_unmap;
+
+	iowrite32(INT_ENABLE, info->mem[0].internal_addr + INT_ENABLE_ADDR);
+	iowrite8(INT_MASK_ALL, info->mem[0].internal_addr + INT_MASK_ADDR);
+	if (ioread8(info->mem[0].internal_addr + INTA_DRVR_ADDR)
+			& INTA_ENABLED_FLAG)
+		printk(KERN_INFO "aectc: interrupts successfully enabled\n");
+
+	pci_set_drvdata(dev, info);
+
+	return 0;
+
+out_unmap:
+	pci_iounmap(dev, info->mem[0].internal_addr);
+out_release:
+	pci_release_regions(dev);
+out_disable:
+	pci_disable_device(dev);
+out_free:
+	kfree(info);
+	return -ENODEV;
+}
+
+static void remove(struct pci_dev *dev)
+{
+	struct uio_info *info = pci_get_drvdata(dev);
+
+	/* disable interrupts */
+	iowrite8(INT_DISABLE, info->mem[0].internal_addr + INT_MASK_ADDR);
+	iowrite32(INT_DISABLE, info->mem[0].internal_addr + INT_ENABLE_ADDR);
+	/* read mailbox to ensure board drops irq */
+	ioread8(info->mem[0].internal_addr + MAILBOX);
+
+	uio_unregister_device(info);
+	pci_release_regions(dev);
+	pci_disable_device(dev);
+	pci_set_drvdata(dev, NULL);
+	iounmap(info->mem[0].internal_addr);
+
+	kfree(info);
+}
+
+static struct pci_driver pci_driver = {
+	.name = "aectc",
+	.id_table = ids,
+	.probe = probe,
+	.remove = remove,
+};
+
+static int __init aectc_init(void)
+{
+	return pci_register_driver(&pci_driver);
+}
+
+static void __exit aectc_exit(void)
+{
+	pci_unregister_driver(&pci_driver);
+}
+
+MODULE_LICENSE("GPL");
+
+module_init(aectc_init);
+module_exit(aectc_exit);

Reply via email to