+= driver
authorsithglan <sithglan>
Sat, 10 Mar 2007 22:35:54 +0000 (22:35 +0000)
committersithglan <sithglan>
Sat, 10 Mar 2007 22:35:54 +0000 (22:35 +0000)
dhwk/driver/Makefile [new file with mode: 0644]
dhwk/driver/build [new file with mode: 0755]
dhwk/driver/dhwk.c [new file with mode: 0644]

diff --git a/dhwk/driver/Makefile b/dhwk/driver/Makefile
new file mode 100644 (file)
index 0000000..27882d4
--- /dev/null
@@ -0,0 +1 @@
+obj-m := dhwk.o
diff --git a/dhwk/driver/build b/dhwk/driver/build
new file mode 100755 (executable)
index 0000000..86e47d9
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+make -C /lib/modules/`uname -r`/build M=`pwd` modules
diff --git a/dhwk/driver/dhwk.c b/dhwk/driver/dhwk.c
new file mode 100644 (file)
index 0000000..c7095f8
--- /dev/null
@@ -0,0 +1,419 @@
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/delay.h>
+
+static const char *version = "$Id: dhwk.c,v 1.1 2007-03-10 22:35:54 sithglan Exp $";
+static unsigned long ioaddr;
+static unsigned long locks;
+
+#define BUFFER_SIZE 32768
+
+static unsigned char send_buffer[BUFFER_SIZE];
+static unsigned char recv_buffer[BUFFER_SIZE];
+
+static wait_queue_head_t send_wq;
+static wait_queue_head_t receive_wq;
+static u8 irq; /* FIXME: Get it from the pci struct */
+static int my_dhwk_dev_id;
+
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define MODULE_NAME "dhwk"
+#define DHWK_MAJOR 42
+
+#define INTERRUPTS_PLEASE 1
+
+#define SDR (ioaddr + 0)
+#define RDR (ioaddr + 2)
+#define ISR (ioaddr + 4)
+#define CTR (ioaddr + 6)
+
+static int
+dhwk_open(struct inode *inode, struct file *f)
+{
+        if (iminor(inode) != 0) {
+                return -ENXIO;
+        }
+
+dhwk_open_again:
+
+        if (( (f->f_flags & O_ACCMODE) == O_RDONLY
+           || (f->f_flags & O_ACCMODE) == O_RDWR)
+        && (locks & 0x01)) {
+                if (f->f_flags & O_NONBLOCK) {
+                        return -EBUSY;
+
+                } else {
+                        schedule();
+                        goto dhwk_open_again;
+                }
+        }
+
+        if (( (f->f_flags & O_ACCMODE) == O_WRONLY
+           || (f->f_flags & O_ACCMODE) == O_RDWR)
+         && (locks & 0x02)) {
+                if (f->f_flags & O_NONBLOCK) {
+                        return -EBUSY;
+
+                } else {
+                        schedule();
+                        goto dhwk_open_again;
+                }
+        }
+
+        if (( (f->f_flags & O_ACCMODE) == O_RDONLY
+           || (f->f_flags & O_ACCMODE) == O_RDWR)) {
+                test_and_set_bit(0, &locks);
+        }
+
+        if (( (f->f_flags & O_ACCMODE) == O_WRONLY
+           || (f->f_flags & O_ACCMODE) == O_RDWR)) {
+                test_and_set_bit(1, &locks);
+        }
+
+        return 0;
+}
+
+static int
+dhwk_release(struct inode *i, struct file *f)
+{
+
+        if (( (f->f_flags & O_ACCMODE) == O_RDONLY
+           || (f->f_flags & O_ACCMODE) == O_RDWR)) {
+                locks &= ~ 0x01;
+        }
+
+        if (( (f->f_flags & O_ACCMODE) == O_WRONLY
+           || (f->f_flags & O_ACCMODE) == O_RDWR)) {
+                locks &= ~ 0x02;
+        }
+
+        return 0;
+}
+
+static ssize_t
+dhwk_read(struct file *f, char __user *u, size_t size, loff_t *offset)
+{
+        size_t count = 0;
+        short word = 0;
+        short status = 0;
+        int next_status = 0;
+        
+        if (! access_ok(VERIFY_WRITE, u, size)) {
+                return -EFAULT;
+        }
+
+        while (count < size) {
+
+dhwk_read_again:
+
+                if (next_status == 0) {
+                        status = inw(ISR);
+                        if (! (status & 0x0200)) {
+                                /* Receive FIFO Empty */
+                                if (f->f_flags & O_NONBLOCK) {
+                                        __copy_to_user(u + count - (count % BUFFER_SIZE),
+                                                recv_buffer, count % BUFFER_SIZE + 1);
+                                        return count;
+
+                                } else {
+#if INTERRUPTS_PLEASE
+                                        wait_event_interruptible(receive_wq, inw(ISR) & 0x0200);
+                                        if (signal_pending(current)) {
+                                                return -ERESTARTSYS;
+                                        }
+#else
+                                        schedule();
+#endif
+                                        goto dhwk_read_again;
+                                }
+
+                        } else if (! (status & 0x0400)) {
+                                /* Receive FIFO Half Full */
+                                next_status = 2048;
+                        }
+
+                } else {
+                        next_status--;
+                }
+
+                word = inw(RDR);
+#if 0
+
+                if ((word & 0xFF) != 0xc3) {
+                        short debug_enable = inw(ioaddr + 8);
+                        printk(KERN_ALERT "dhwk: Fehler: 0x%08x\n", 0x00 | word);
+                }
+#endif
+
+                recv_buffer[count % BUFFER_SIZE] = (unsigned char) word;
+
+                if (count % BUFFER_SIZE == (BUFFER_SIZE - 1)
+                || count == (size - 1)) {
+                        __copy_to_user(u + count - (count % BUFFER_SIZE),
+                                recv_buffer, count % BUFFER_SIZE + 1);
+                };
+
+                count++;
+        }
+
+        return count;
+}
+
+static ssize_t
+dhwk_write(struct file *f, const char __user *u, size_t size, loff_t *offset)
+{
+        size_t count = 0;
+        size_t grabbed = 0;
+        short status = 0;
+        int next_status = 0;
+
+        if (! access_ok(VERIFY_READ, u, size)) {
+                return -EFAULT;
+        }
+
+        while (count < size) {
+                if (grabbed <= count) {
+                        __copy_from_user(send_buffer, u + count,
+                                (size - count) < BUFFER_SIZE ? (size - count) : BUFFER_SIZE);
+                        grabbed += (size - count) < BUFFER_SIZE ? (size - count) : BUFFER_SIZE;
+                }
+
+dhwk_write_again:
+
+                if (next_status == 0) {
+                        status = inw(ISR);
+                        if (! (status & 0x8000)) {
+                                /* Send FIFO FULL */
+                                if (f->f_flags & O_NONBLOCK) {
+                                        return count;
+
+                                } else {
+#if INTERRUPTS_PLEASE
+                                        wait_event_interruptible(send_wq, inw(ISR) & 0x0800);
+                                        if (signal_pending(current)) {
+                                                return -ERESTARTSYS;
+                                        }
+#else
+                                        schedule();
+#endif
+                                        goto dhwk_write_again;
+                                }
+
+                        } else if (status & 0x4000) {
+                                /* Send FIFO not HALF Full */
+                                next_status = 2048;
+                        }
+                } else {
+                        next_status--;
+                }
+
+                outw(0x0000 | send_buffer[count % BUFFER_SIZE], SDR);
+                count++;
+        }
+
+        return count;
+}
+
+static unsigned int
+dhwk_poll(struct file *f, struct poll_table_struct *pts)
+{
+        unsigned int mask;
+        unsigned short reg;
+
+dhwk_poll_again:
+        mask = 0;
+        reg = inw(ISR);
+
+        if (( (f->f_flags & O_ACCMODE) == O_RDONLY
+           || (f->f_flags & O_ACCMODE) == O_RDWR)) {
+                if (f->f_flags & O_NONBLOCK) {
+                        if (reg & 0x0200) {
+                                mask |= POLLIN | POLLRDNORM;
+
+                        } else {
+                                return -EAGAIN;
+                        }
+
+                } else {
+                        schedule();
+                        goto dhwk_poll_again;
+                }
+        }
+
+        if (( (f->f_flags & O_ACCMODE) == O_WRONLY
+           || (f->f_flags & O_ACCMODE) == O_RDWR)) {
+                if ((f->f_flags & O_NONBLOCK)) {
+                        /* If write is possible */
+                        if (! (reg & 0x8000)) {
+                                mask |= POLLOUT | POLLWRNORM;
+
+                        } else {
+                                return -EAGAIN;
+                        }
+
+                } else {
+                        schedule();
+                        goto dhwk_poll_again;
+                }
+
+        }
+
+        return mask;
+}
+
+static int __devinit
+dhwk_pci_init(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+        int ret;
+
+        printk(KERN_ALERT "dhwk: Gruppe 2: %s\n", version);
+
+        ret = pci_enable_device(pdev);
+        if (ret) {
+                printk(KERN_ERR "dhwk: Could not enable pci device.\n");
+                pci_disable_device(pdev);
+                return 1;
+        }
+
+        ret = pci_request_regions(pdev, MODULE_NAME);
+        if (ret) {
+                printk(KERN_ERR "dhwk: Could not request regions.\n");
+                pci_disable_device(pdev);
+                return 1;
+        }
+
+        irq = pdev->irq;
+
+        ioaddr = pci_resource_start(pdev, 0);
+        printk(KERN_ALERT "dhwk: ioaddr at 0x%08x irq 0x%02x\n", (unsigned int) ioaddr, irq);
+
+        /* Pull Reset (twice) */
+        outw(0x0100, CTR);
+        outw(0x0100, CTR);
+
+        udelay(10);
+
+        /* CLEAR WRONG STATUS */
+        ret = inw(ISR);
+        ret = inw(ISR);
+
+        /* Enable Sender and Receiver and all available Interrupts */
+        while ((inw(CTR) & 0x06FF) != 0x06FF) {
+                outw(0x06FF, CTR);
+        }
+        return 0;
+}
+
+static void __devexit
+dhwk_pci_exit(struct pci_dev *pdev)
+{
+        int ret;
+        outw(0x0000, CTR);
+        ret = inw(ISR); /* Get pending interrupts down */
+        ret = inw(ISR);
+        pci_release_regions(pdev);
+        pci_disable_device(pdev);
+        pci_set_drvdata(pdev, NULL);
+        printk(KERN_ALERT "dhwk: Gruppe 2: Module unloaded.\n");
+}
+
+static struct pci_device_id dhwk_pci_tbl[] = {
+        {0x2222, 0xaffe, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* FIXME */
+#if 0
+        {0xbaff, 0xaffe, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* FIXME */
+#endif
+        {0,},
+};
+
+static struct pci_driver dhwk_pci_driver = {
+        .name           = MODULE_NAME,
+        .id_table       = dhwk_pci_tbl,
+        .probe          = dhwk_pci_init,
+        .remove         = dhwk_pci_exit,
+};
+
+struct file_operations dhwk_fops = {
+        .owner = THIS_MODULE,
+        .read = dhwk_read,
+        .poll = dhwk_poll,
+        .write = dhwk_write,
+        .open = dhwk_open,
+        .release = dhwk_release,
+};
+
+static irqreturn_t
+interrupt_handler(int irq_to_check, void *dev_id, struct pt_regs *regs)
+{
+        register unsigned short status;
+        status = inw(ISR);
+
+        if (status & 0x0200) {
+                wake_up_interruptible(& receive_wq);
+        }
+
+        if (status & 0x8000) {
+                wake_up_interruptible(& send_wq);
+        }
+
+        if (irq_to_check == irq) { 
+                return IRQ_HANDLED;
+
+        } else {
+                return IRQ_NONE;
+        }
+}
+
+static int
+dhwk_init(void)
+{
+        int ret;
+
+        ret = pci_module_init(& dhwk_pci_driver);
+        if (ret) {
+                return -EIO;
+        }
+
+        ret = register_chrdev(DHWK_MAJOR, MODULE_NAME, & dhwk_fops);
+        if (ret) {
+                printk (KERN_ALERT "dhwk: unable to get major %d\n", DHWK_MAJOR);
+                pci_unregister_driver(& dhwk_pci_driver);
+                return -EIO;
+        }
+
+        init_waitqueue_head(& send_wq);
+        init_waitqueue_head(& receive_wq);
+
+#if INTERRUPTS_PLEASE
+#if 0
+        ret = request_irq(irq, interrupt_handler, SA_INTERRUPT, "dhwk", &my_dhwk_dev_id);
+#endif
+        ret = request_irq(irq, interrupt_handler, SA_SHIRQ, "dhwk", &my_dhwk_dev_id);
+        if (ret) {
+                printk (KERN_ALERT "dhwk: unable to register interrupt %d\n", irq);
+                pci_unregister_driver(& dhwk_pci_driver);
+                unregister_chrdev(DHWK_MAJOR, MODULE_NAME);
+                return -EIO;
+        }
+#endif
+
+        return ret;
+}
+
+static void
+dhwk_exit(void)
+{
+        free_irq(irq, &my_dhwk_dev_id);
+        pci_unregister_driver(& dhwk_pci_driver);
+        unregister_chrdev(DHWK_MAJOR, MODULE_NAME);
+}
+
+module_init(dhwk_init);
+module_exit(dhwk_exit);
Impressum, Datenschutz