X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fmisc%2Fphantom.c;h=cd221fd0fb94b89a4c54ac3cc52da09cf7a13dee;hb=1fba38703d0ce8a5ff0fad9df3eccc6b55cf2cfb;hp=35b139b0e5f2a01a97082d15514a0bd4fc6a93fc;hpb=18062a91d2ddc40e19fc674afeb7cad58cfa23ab;p=linux-2.6-omap-h63xx.git diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index 35b139b0e5f..cd221fd0fb9 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c @@ -9,6 +9,7 @@ * You need an userspace library to cooperate with this driver. It (and other * info) may be obtained here: * http://www.fi.muni.cz/~xslaby/phantom.html + * or alternatively, you might use OpenHaptics provided by Sensable. */ #include @@ -24,13 +25,14 @@ #include #include -#define PHANTOM_VERSION "n0.9.5" +#define PHANTOM_VERSION "n0.9.7" #define PHANTOM_MAX_MINORS 8 #define PHN_IRQCTL 0x4c /* irq control in caddr space */ #define PHB_RUNNING 1 +#define PHB_NOT_OH 2 static struct class *phantom_class; static int phantom_major; @@ -47,6 +49,11 @@ struct phantom_device { struct cdev cdev; struct mutex open_lock; + spinlock_t regs_lock; + + /* used in NOT_OH mode */ + struct phm_regs oregs; + u32 ctl_reg; }; static unsigned char phantom_devices[PHANTOM_MAX_MINORS]; @@ -59,8 +66,11 @@ static int phantom_status(struct phantom_device *dev, unsigned long newstat) atomic_set(&dev->counter, 0); iowrite32(PHN_CTL_IRQ, dev->iaddr + PHN_CONTROL); iowrite32(0x43, dev->caddr + PHN_IRQCTL); - } else if ((dev->status & PHB_RUNNING) && !(newstat & PHB_RUNNING)) + ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */ + } else if ((dev->status & PHB_RUNNING) && !(newstat & PHB_RUNNING)) { iowrite32(0, dev->caddr + PHN_IRQCTL); + ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */ + } dev->status = newstat; @@ -71,13 +81,14 @@ static int phantom_status(struct phantom_device *dev, unsigned long newstat) * File ops */ -static int phantom_ioctl(struct inode *inode, struct file *file, u_int cmd, - u_long arg) +static long phantom_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { struct phantom_device *dev = file->private_data; struct phm_regs rs; struct phm_reg r; void __user *argp = (void __user *)arg; + unsigned long flags; unsigned int i; if (_IOC_TYPE(cmd) != PH_IOC_MAGIC || @@ -92,24 +103,45 @@ static int phantom_ioctl(struct inode *inode, struct file *file, u_int cmd, if (r.reg > 7) return -EINVAL; + spin_lock_irqsave(&dev->regs_lock, flags); if (r.reg == PHN_CONTROL && (r.value & PHN_CTL_IRQ) && - phantom_status(dev, dev->status | PHB_RUNNING)) + phantom_status(dev, dev->status | PHB_RUNNING)){ + spin_unlock_irqrestore(&dev->regs_lock, flags); return -ENODEV; + } pr_debug("phantom: writing %x to %u\n", r.value, r.reg); + + /* preserve amp bit (don't allow to change it when in NOT_OH) */ + if (r.reg == PHN_CONTROL && (dev->status & PHB_NOT_OH)) { + r.value &= ~PHN_CTL_AMP; + r.value |= dev->ctl_reg & PHN_CTL_AMP; + dev->ctl_reg = r.value; + } + iowrite32(r.value, dev->iaddr + r.reg); + ioread32(dev->iaddr); /* PCI posting */ if (r.reg == PHN_CONTROL && !(r.value & PHN_CTL_IRQ)) phantom_status(dev, dev->status & ~PHB_RUNNING); + spin_unlock_irqrestore(&dev->regs_lock, flags); break; case PHN_SET_REGS: if (copy_from_user(&rs, argp, sizeof(rs))) return -EFAULT; pr_debug("phantom: SRS %u regs %x\n", rs.count, rs.mask); - for (i = 0; i < min(rs.count, 8U); i++) - if ((1 << i) & rs.mask) - iowrite32(rs.values[i], dev->oaddr + i); + spin_lock_irqsave(&dev->regs_lock, flags); + if (dev->status & PHB_NOT_OH) + memcpy(&dev->oregs, &rs, sizeof(rs)); + else { + u32 m = min(rs.count, 8U); + for (i = 0; i < m; i++) + if (rs.mask & BIT(i)) + iowrite32(rs.values[i], dev->oaddr + i); + ioread32(dev->iaddr); /* PCI posting */ + } + spin_unlock_irqrestore(&dev->regs_lock, flags); break; case PHN_GET_REG: if (copy_from_user(&r, argp, sizeof(r))) @@ -123,18 +155,35 @@ static int phantom_ioctl(struct inode *inode, struct file *file, u_int cmd, if (copy_to_user(argp, &r, sizeof(r))) return -EFAULT; break; - case PHN_GET_REGS: + case PHN_GET_REGS: { + u32 m; + if (copy_from_user(&rs, argp, sizeof(rs))) return -EFAULT; + m = min(rs.count, 8U); + pr_debug("phantom: GRS %u regs %x\n", rs.count, rs.mask); - for (i = 0; i < min(rs.count, 8U); i++) - if ((1 << i) & rs.mask) + spin_lock_irqsave(&dev->regs_lock, flags); + for (i = 0; i < m; i++) + if (rs.mask & BIT(i)) rs.values[i] = ioread32(dev->iaddr + i); + spin_unlock_irqrestore(&dev->regs_lock, flags); if (copy_to_user(argp, &rs, sizeof(rs))) return -EFAULT; break; + } case PHN_NOT_OH: + spin_lock_irqsave(&dev->regs_lock, flags); + if (dev->status & PHB_RUNNING) { + printk(KERN_ERR "phantom: you need to set NOT_OH " + "before you start the device!\n"); + spin_unlock_irqrestore(&dev->regs_lock, flags); + return -EINVAL; + } + dev->status |= PHB_NOT_OH; + spin_unlock_irqrestore(&dev->regs_lock, flags); + break; default: return -ENOTTY; } @@ -157,8 +206,11 @@ static int phantom_open(struct inode *inode, struct file *file) return -EINVAL; } + WARN_ON(dev->status & PHB_NOT_OH); + file->private_data = dev; + atomic_set(&dev->counter, 0); dev->opened++; mutex_unlock(&dev->open_lock); @@ -173,6 +225,7 @@ static int phantom_release(struct inode *inode, struct file *file) dev->opened = 0; phantom_status(dev, dev->status & ~PHB_RUNNING); + dev->status &= ~PHB_NOT_OH; mutex_unlock(&dev->open_lock); @@ -199,20 +252,41 @@ static unsigned int phantom_poll(struct file *file, poll_table *wait) static struct file_operations phantom_file_ops = { .open = phantom_open, .release = phantom_release, - .ioctl = phantom_ioctl, + .unlocked_ioctl = phantom_ioctl, .poll = phantom_poll, }; static irqreturn_t phantom_isr(int irq, void *data) { struct phantom_device *dev = data; + unsigned int i; + u32 ctl; - if (!(ioread32(dev->iaddr + PHN_CONTROL) & PHN_CTL_IRQ)) + spin_lock(&dev->regs_lock); + ctl = ioread32(dev->iaddr + PHN_CONTROL); + if (!(ctl & PHN_CTL_IRQ)) { + spin_unlock(&dev->regs_lock); return IRQ_NONE; + } iowrite32(0, dev->iaddr); iowrite32(0xc0, dev->iaddr); + if (dev->status & PHB_NOT_OH) { + struct phm_regs *r = &dev->oregs; + u32 m = min(r->count, 8U); + + for (i = 0; i < m; i++) + if (r->mask & BIT(i)) + iowrite32(r->values[i], dev->oaddr + i); + + dev->ctl_reg ^= PHN_CTL_AMP; + iowrite32(dev->ctl_reg, dev->iaddr + PHN_CONTROL); + } + spin_unlock(&dev->regs_lock); + + ioread32(dev->iaddr); /* PCI posting */ + atomic_inc(&dev->counter); wake_up_interruptible(&dev->wait); @@ -282,11 +356,13 @@ static int __devinit phantom_probe(struct pci_dev *pdev, } mutex_init(&pht->open_lock); + spin_lock_init(&pht->regs_lock); init_waitqueue_head(&pht->wait); cdev_init(&pht->cdev, &phantom_file_ops); pht->cdev.owner = THIS_MODULE; iowrite32(0, pht->caddr + PHN_IRQCTL); + ioread32(pht->caddr + PHN_IRQCTL); /* PCI posting */ retval = request_irq(pdev->irq, phantom_isr, IRQF_SHARED | IRQF_DISABLED, "phantom", pht); if (retval) { @@ -337,6 +413,7 @@ static void __devexit phantom_remove(struct pci_dev *pdev) cdev_del(&pht->cdev); iowrite32(0, pht->caddr + PHN_IRQCTL); + ioread32(pht->caddr + PHN_IRQCTL); /* PCI posting */ free_irq(pdev->irq, pht); pci_iounmap(pdev, pht->oaddr); @@ -358,6 +435,9 @@ static int phantom_suspend(struct pci_dev *pdev, pm_message_t state) struct phantom_device *dev = pci_get_drvdata(pdev); iowrite32(0, dev->caddr + PHN_IRQCTL); + ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */ + + synchronize_irq(pdev->irq); return 0; }