+#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);