From: sithglan Date: Sat, 10 Mar 2007 22:35:54 +0000 (+0000) Subject: += driver X-Git-Url: https://git.zerfleddert.de/cgi-bin/gitweb.cgi/raggedstone/commitdiff_plain/a10e578285a0d47f60c93f5dc47762446f6e1061?ds=sidebyside += driver --- diff --git a/dhwk/driver/Makefile b/dhwk/driver/Makefile new file mode 100644 index 0000000..27882d4 --- /dev/null +++ b/dhwk/driver/Makefile @@ -0,0 +1 @@ +obj-m := dhwk.o diff --git a/dhwk/driver/build b/dhwk/driver/build new file mode 100755 index 0000000..86e47d9 --- /dev/null +++ b/dhwk/driver/build @@ -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 index 0000000..c7095f8 --- /dev/null +++ b/dhwk/driver/dhwk.c @@ -0,0 +1,419 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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);