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 */ |