diff options
| author | Jiri Slaby <jirislaby@gmail.com> | 2007-10-19 02:40:24 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-19 14:53:41 -0400 |
| commit | bc552f77157d1bae79d0d3a5541da9579c39cb70 (patch) | |
| tree | 3874dee446b831d1ef6fa3ff81ce941138604b0a | |
| parent | b2afe3317099afe0843e3cece6be60664e6033ea (diff) | |
Misc: phantom, improved data passing
This new version guarantees amb_bit switch in small enough intervals, so that
the device won't stop working in the middle of a movement anymore. However it
preserves old (openhaptics) functionality.
Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | drivers/misc/phantom.c | 94 | ||||
| -rw-r--r-- | include/linux/phantom.h | 6 |
2 files changed, 81 insertions, 19 deletions
diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index bc532ac8b7d8..cd221fd0fb94 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c | |||
| @@ -25,13 +25,14 @@ | |||
| 25 | #include <asm/atomic.h> | 25 | #include <asm/atomic.h> |
| 26 | #include <asm/io.h> | 26 | #include <asm/io.h> |
| 27 | 27 | ||
| 28 | #define PHANTOM_VERSION "n0.9.5" | 28 | #define PHANTOM_VERSION "n0.9.7" |
| 29 | 29 | ||
| 30 | #define PHANTOM_MAX_MINORS 8 | 30 | #define PHANTOM_MAX_MINORS 8 |
| 31 | 31 | ||
| 32 | #define PHN_IRQCTL 0x4c /* irq control in caddr space */ | 32 | #define PHN_IRQCTL 0x4c /* irq control in caddr space */ |
| 33 | 33 | ||
| 34 | #define PHB_RUNNING 1 | 34 | #define PHB_RUNNING 1 |
| 35 | #define PHB_NOT_OH 2 | ||
| 35 | 36 | ||
| 36 | static struct class *phantom_class; | 37 | static struct class *phantom_class; |
| 37 | static int phantom_major; | 38 | static int phantom_major; |
| @@ -48,7 +49,11 @@ struct phantom_device { | |||
| 48 | struct cdev cdev; | 49 | struct cdev cdev; |
| 49 | 50 | ||
| 50 | struct mutex open_lock; | 51 | struct mutex open_lock; |
| 51 | spinlock_t ioctl_lock; | 52 | spinlock_t regs_lock; |
| 53 | |||
| 54 | /* used in NOT_OH mode */ | ||
| 55 | struct phm_regs oregs; | ||
| 56 | u32 ctl_reg; | ||
| 52 | }; | 57 | }; |
| 53 | 58 | ||
| 54 | static unsigned char phantom_devices[PHANTOM_MAX_MINORS]; | 59 | static unsigned char phantom_devices[PHANTOM_MAX_MINORS]; |
| @@ -83,6 +88,7 @@ static long phantom_ioctl(struct file *file, unsigned int cmd, | |||
| 83 | struct phm_regs rs; | 88 | struct phm_regs rs; |
| 84 | struct phm_reg r; | 89 | struct phm_reg r; |
| 85 | void __user *argp = (void __user *)arg; | 90 | void __user *argp = (void __user *)arg; |
| 91 | unsigned long flags; | ||
| 86 | unsigned int i; | 92 | unsigned int i; |
| 87 | 93 | ||
| 88 | if (_IOC_TYPE(cmd) != PH_IOC_MAGIC || | 94 | if (_IOC_TYPE(cmd) != PH_IOC_MAGIC || |
| @@ -97,32 +103,45 @@ static long phantom_ioctl(struct file *file, unsigned int cmd, | |||
| 97 | if (r.reg > 7) | 103 | if (r.reg > 7) |
| 98 | return -EINVAL; | 104 | return -EINVAL; |
| 99 | 105 | ||
| 100 | spin_lock(&dev->ioctl_lock); | 106 | spin_lock_irqsave(&dev->regs_lock, flags); |
| 101 | if (r.reg == PHN_CONTROL && (r.value & PHN_CTL_IRQ) && | 107 | if (r.reg == PHN_CONTROL && (r.value & PHN_CTL_IRQ) && |
| 102 | phantom_status(dev, dev->status | PHB_RUNNING)){ | 108 | phantom_status(dev, dev->status | PHB_RUNNING)){ |
| 103 | spin_unlock(&dev->ioctl_lock); | 109 | spin_unlock_irqrestore(&dev->regs_lock, flags); |
| 104 | return -ENODEV; | 110 | return -ENODEV; |
| 105 | } | 111 | } |
| 106 | 112 | ||
| 107 | pr_debug("phantom: writing %x to %u\n", r.value, r.reg); | 113 | pr_debug("phantom: writing %x to %u\n", r.value, r.reg); |
| 114 | |||
| 115 | /* preserve amp bit (don't allow to change it when in NOT_OH) */ | ||
| 116 | if (r.reg == PHN_CONTROL && (dev->status & PHB_NOT_OH)) { | ||
| 117 | r.value &= ~PHN_CTL_AMP; | ||
| 118 | r.value |= dev->ctl_reg & PHN_CTL_AMP; | ||
| 119 | dev->ctl_reg = r.value; | ||
| 120 | } | ||
| 121 | |||
| 108 | iowrite32(r.value, dev->iaddr + r.reg); | 122 | iowrite32(r.value, dev->iaddr + r.reg); |
| 109 | ioread32(dev->iaddr); /* PCI posting */ | 123 | ioread32(dev->iaddr); /* PCI posting */ |
| 110 | 124 | ||
| 111 | if (r.reg == PHN_CONTROL && !(r.value & PHN_CTL_IRQ)) | 125 | if (r.reg == PHN_CONTROL && !(r.value & PHN_CTL_IRQ)) |
| 112 | phantom_status(dev, dev->status & ~PHB_RUNNING); | 126 | phantom_status(dev, dev->status & ~PHB_RUNNING); |
| 113 | spin_unlock(&dev->ioctl_lock); | 127 | spin_unlock_irqrestore(&dev->regs_lock, flags); |
| 114 | break; | 128 | break; |
| 115 | case PHN_SET_REGS: | 129 | case PHN_SET_REGS: |
| 116 | if (copy_from_user(&rs, argp, sizeof(rs))) | 130 | if (copy_from_user(&rs, argp, sizeof(rs))) |
| 117 | return -EFAULT; | 131 | return -EFAULT; |
| 118 | 132 | ||
| 119 | pr_debug("phantom: SRS %u regs %x\n", rs.count, rs.mask); | 133 | pr_debug("phantom: SRS %u regs %x\n", rs.count, rs.mask); |
| 120 | spin_lock(&dev->ioctl_lock); | 134 | spin_lock_irqsave(&dev->regs_lock, flags); |
| 121 | for (i = 0; i < min(rs.count, 8U); i++) | 135 | if (dev->status & PHB_NOT_OH) |
| 122 | if ((1 << i) & rs.mask) | 136 | memcpy(&dev->oregs, &rs, sizeof(rs)); |
| 123 | iowrite32(rs.values[i], dev->oaddr + i); | 137 | else { |
| 124 | ioread32(dev->iaddr); /* PCI posting */ | 138 | u32 m = min(rs.count, 8U); |
| 125 | spin_unlock(&dev->ioctl_lock); | 139 | for (i = 0; i < m; i++) |
| 140 | if (rs.mask & BIT(i)) | ||
| 141 | iowrite32(rs.values[i], dev->oaddr + i); | ||
| 142 | ioread32(dev->iaddr); /* PCI posting */ | ||
| 143 | } | ||
| 144 | spin_unlock_irqrestore(&dev->regs_lock, flags); | ||
| 126 | break; | 145 | break; |
| 127 | case PHN_GET_REG: | 146 | case PHN_GET_REG: |
| 128 | if (copy_from_user(&r, argp, sizeof(r))) | 147 | if (copy_from_user(&r, argp, sizeof(r))) |
| @@ -136,20 +155,35 @@ static long phantom_ioctl(struct file *file, unsigned int cmd, | |||
| 136 | if (copy_to_user(argp, &r, sizeof(r))) | 155 | if (copy_to_user(argp, &r, sizeof(r))) |
| 137 | return -EFAULT; | 156 | return -EFAULT; |
| 138 | break; | 157 | break; |
| 139 | case PHN_GET_REGS: | 158 | case PHN_GET_REGS: { |
| 159 | u32 m; | ||
| 160 | |||
| 140 | if (copy_from_user(&rs, argp, sizeof(rs))) | 161 | if (copy_from_user(&rs, argp, sizeof(rs))) |
| 141 | return -EFAULT; | 162 | return -EFAULT; |
| 142 | 163 | ||
| 164 | m = min(rs.count, 8U); | ||
| 165 | |||
| 143 | pr_debug("phantom: GRS %u regs %x\n", rs.count, rs.mask); | 166 | pr_debug("phantom: GRS %u regs %x\n", rs.count, rs.mask); |
| 144 | spin_lock(&dev->ioctl_lock); | 167 | spin_lock_irqsave(&dev->regs_lock, flags); |
| 145 | for (i = 0; i < min(rs.count, 8U); i++) | 168 | for (i = 0; i < m; i++) |
| 146 | if ((1 << i) & rs.mask) | 169 | if (rs.mask & BIT(i)) |
| 147 | rs.values[i] = ioread32(dev->iaddr + i); | 170 | rs.values[i] = ioread32(dev->iaddr + i); |
| 148 | spin_unlock(&dev->ioctl_lock); | 171 | spin_unlock_irqrestore(&dev->regs_lock, flags); |
| 149 | 172 | ||
| 150 | if (copy_to_user(argp, &rs, sizeof(rs))) | 173 | if (copy_to_user(argp, &rs, sizeof(rs))) |
| 151 | return -EFAULT; | 174 | return -EFAULT; |
| 152 | break; | 175 | break; |
| 176 | } case PHN_NOT_OH: | ||
| 177 | spin_lock_irqsave(&dev->regs_lock, flags); | ||
| 178 | if (dev->status & PHB_RUNNING) { | ||
| 179 | printk(KERN_ERR "phantom: you need to set NOT_OH " | ||
| 180 | "before you start the device!\n"); | ||
| 181 | spin_unlock_irqrestore(&dev->regs_lock, flags); | ||
| 182 | return -EINVAL; | ||
| 183 | } | ||
| 184 | dev->status |= PHB_NOT_OH; | ||
| 185 | spin_unlock_irqrestore(&dev->regs_lock, flags); | ||
| 186 | break; | ||
| 153 | default: | 187 | default: |
| 154 | return -ENOTTY; | 188 | return -ENOTTY; |
| 155 | } | 189 | } |
| @@ -172,8 +206,11 @@ static int phantom_open(struct inode *inode, struct file *file) | |||
| 172 | return -EINVAL; | 206 | return -EINVAL; |
| 173 | } | 207 | } |
| 174 | 208 | ||
| 209 | WARN_ON(dev->status & PHB_NOT_OH); | ||
| 210 | |||
| 175 | file->private_data = dev; | 211 | file->private_data = dev; |
| 176 | 212 | ||
| 213 | atomic_set(&dev->counter, 0); | ||
| 177 | dev->opened++; | 214 | dev->opened++; |
| 178 | mutex_unlock(&dev->open_lock); | 215 | mutex_unlock(&dev->open_lock); |
| 179 | 216 | ||
| @@ -188,6 +225,7 @@ static int phantom_release(struct inode *inode, struct file *file) | |||
| 188 | 225 | ||
| 189 | dev->opened = 0; | 226 | dev->opened = 0; |
| 190 | phantom_status(dev, dev->status & ~PHB_RUNNING); | 227 | phantom_status(dev, dev->status & ~PHB_RUNNING); |
| 228 | dev->status &= ~PHB_NOT_OH; | ||
| 191 | 229 | ||
| 192 | mutex_unlock(&dev->open_lock); | 230 | mutex_unlock(&dev->open_lock); |
| 193 | 231 | ||
| @@ -221,12 +259,32 @@ static struct file_operations phantom_file_ops = { | |||
| 221 | static irqreturn_t phantom_isr(int irq, void *data) | 259 | static irqreturn_t phantom_isr(int irq, void *data) |
| 222 | { | 260 | { |
| 223 | struct phantom_device *dev = data; | 261 | struct phantom_device *dev = data; |
| 262 | unsigned int i; | ||
| 263 | u32 ctl; | ||
| 224 | 264 | ||
| 225 | if (!(ioread32(dev->iaddr + PHN_CONTROL) & PHN_CTL_IRQ)) | 265 | spin_lock(&dev->regs_lock); |
| 266 | ctl = ioread32(dev->iaddr + PHN_CONTROL); | ||
| 267 | if (!(ctl & PHN_CTL_IRQ)) { | ||
| 268 | spin_unlock(&dev->regs_lock); | ||
| 226 | return IRQ_NONE; | 269 | return IRQ_NONE; |
| 270 | } | ||
| 227 | 271 | ||
| 228 | iowrite32(0, dev->iaddr); | 272 | iowrite32(0, dev->iaddr); |
| 229 | iowrite32(0xc0, dev->iaddr); | 273 | iowrite32(0xc0, dev->iaddr); |
| 274 | |||
| 275 | if (dev->status & PHB_NOT_OH) { | ||
| 276 | struct phm_regs *r = &dev->oregs; | ||
| 277 | u32 m = min(r->count, 8U); | ||
| 278 | |||
| 279 | for (i = 0; i < m; i++) | ||
| 280 | if (r->mask & BIT(i)) | ||
| 281 | iowrite32(r->values[i], dev->oaddr + i); | ||
| 282 | |||
| 283 | dev->ctl_reg ^= PHN_CTL_AMP; | ||
| 284 | iowrite32(dev->ctl_reg, dev->iaddr + PHN_CONTROL); | ||
| 285 | } | ||
| 286 | spin_unlock(&dev->regs_lock); | ||
| 287 | |||
| 230 | ioread32(dev->iaddr); /* PCI posting */ | 288 | ioread32(dev->iaddr); /* PCI posting */ |
| 231 | 289 | ||
| 232 | atomic_inc(&dev->counter); | 290 | atomic_inc(&dev->counter); |
| @@ -298,7 +356,7 @@ static int __devinit phantom_probe(struct pci_dev *pdev, | |||
| 298 | } | 356 | } |
| 299 | 357 | ||
| 300 | mutex_init(&pht->open_lock); | 358 | mutex_init(&pht->open_lock); |
| 301 | spin_lock_init(&pht->ioctl_lock); | 359 | spin_lock_init(&pht->regs_lock); |
| 302 | init_waitqueue_head(&pht->wait); | 360 | init_waitqueue_head(&pht->wait); |
| 303 | cdev_init(&pht->cdev, &phantom_file_ops); | 361 | cdev_init(&pht->cdev, &phantom_file_ops); |
| 304 | pht->cdev.owner = THIS_MODULE; | 362 | pht->cdev.owner = THIS_MODULE; |
diff --git a/include/linux/phantom.h b/include/linux/phantom.h index d3ebbfae6903..96f4048a6cc3 100644 --- a/include/linux/phantom.h +++ b/include/linux/phantom.h | |||
| @@ -30,7 +30,11 @@ struct phm_regs { | |||
| 30 | #define PHN_SET_REG _IOW (PH_IOC_MAGIC, 1, struct phm_reg *) | 30 | #define PHN_SET_REG _IOW (PH_IOC_MAGIC, 1, struct phm_reg *) |
| 31 | #define PHN_GET_REGS _IOWR(PH_IOC_MAGIC, 2, struct phm_regs *) | 31 | #define PHN_GET_REGS _IOWR(PH_IOC_MAGIC, 2, struct phm_regs *) |
| 32 | #define PHN_SET_REGS _IOW (PH_IOC_MAGIC, 3, struct phm_regs *) | 32 | #define PHN_SET_REGS _IOW (PH_IOC_MAGIC, 3, struct phm_regs *) |
| 33 | #define PH_IOC_MAXNR 3 | 33 | /* this ioctl tells the driver, that the caller is not OpenHaptics and might |
| 34 | * use improved registers update (no more phantom switchoffs when using | ||
| 35 | * libphantom) */ | ||
| 36 | #define PHN_NOT_OH _IO (PH_IOC_MAGIC, 4) | ||
| 37 | #define PH_IOC_MAXNR 4 | ||
| 34 | 38 | ||
| 35 | #define PHN_CONTROL 0x6 /* control byte in iaddr space */ | 39 | #define PHN_CONTROL 0x6 /* control byte in iaddr space */ |
| 36 | #define PHN_CTL_AMP 0x1 /* switch after torques change */ | 40 | #define PHN_CTL_AMP 0x1 /* switch after torques change */ |
