diff options
Diffstat (limited to 'drivers/misc/phantom.c')
-rw-r--r-- | drivers/misc/phantom.c | 97 |
1 files changed, 79 insertions, 18 deletions
diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c index 5108b7c576df..cd221fd0fb94 100644 --- a/drivers/misc/phantom.c +++ b/drivers/misc/phantom.c | |||
@@ -9,6 +9,7 @@ | |||
9 | * You need an userspace library to cooperate with this driver. It (and other | 9 | * You need an userspace library to cooperate with this driver. It (and other |
10 | * info) may be obtained here: | 10 | * info) may be obtained here: |
11 | * http://www.fi.muni.cz/~xslaby/phantom.html | 11 | * http://www.fi.muni.cz/~xslaby/phantom.html |
12 | * or alternatively, you might use OpenHaptics provided by Sensable. | ||
12 | */ | 13 | */ |
13 | 14 | ||
14 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
@@ -24,13 +25,14 @@ | |||
24 | #include <asm/atomic.h> | 25 | #include <asm/atomic.h> |
25 | #include <asm/io.h> | 26 | #include <asm/io.h> |
26 | 27 | ||
27 | #define PHANTOM_VERSION "n0.9.5" | 28 | #define PHANTOM_VERSION "n0.9.7" |
28 | 29 | ||
29 | #define PHANTOM_MAX_MINORS 8 | 30 | #define PHANTOM_MAX_MINORS 8 |
30 | 31 | ||
31 | #define PHN_IRQCTL 0x4c /* irq control in caddr space */ | 32 | #define PHN_IRQCTL 0x4c /* irq control in caddr space */ |
32 | 33 | ||
33 | #define PHB_RUNNING 1 | 34 | #define PHB_RUNNING 1 |
35 | #define PHB_NOT_OH 2 | ||
34 | 36 | ||
35 | static struct class *phantom_class; | 37 | static struct class *phantom_class; |
36 | static int phantom_major; | 38 | static int phantom_major; |
@@ -47,7 +49,11 @@ struct phantom_device { | |||
47 | struct cdev cdev; | 49 | struct cdev cdev; |
48 | 50 | ||
49 | struct mutex open_lock; | 51 | struct mutex open_lock; |
50 | 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; | ||
51 | }; | 57 | }; |
52 | 58 | ||
53 | static unsigned char phantom_devices[PHANTOM_MAX_MINORS]; | 59 | static unsigned char phantom_devices[PHANTOM_MAX_MINORS]; |
@@ -82,6 +88,7 @@ static long phantom_ioctl(struct file *file, unsigned int cmd, | |||
82 | struct phm_regs rs; | 88 | struct phm_regs rs; |
83 | struct phm_reg r; | 89 | struct phm_reg r; |
84 | void __user *argp = (void __user *)arg; | 90 | void __user *argp = (void __user *)arg; |
91 | unsigned long flags; | ||
85 | unsigned int i; | 92 | unsigned int i; |
86 | 93 | ||
87 | if (_IOC_TYPE(cmd) != PH_IOC_MAGIC || | 94 | if (_IOC_TYPE(cmd) != PH_IOC_MAGIC || |
@@ -96,32 +103,45 @@ static long phantom_ioctl(struct file *file, unsigned int cmd, | |||
96 | if (r.reg > 7) | 103 | if (r.reg > 7) |
97 | return -EINVAL; | 104 | return -EINVAL; |
98 | 105 | ||
99 | spin_lock(&dev->ioctl_lock); | 106 | spin_lock_irqsave(&dev->regs_lock, flags); |
100 | if (r.reg == PHN_CONTROL && (r.value & PHN_CTL_IRQ) && | 107 | if (r.reg == PHN_CONTROL && (r.value & PHN_CTL_IRQ) && |
101 | phantom_status(dev, dev->status | PHB_RUNNING)){ | 108 | phantom_status(dev, dev->status | PHB_RUNNING)){ |
102 | spin_unlock(&dev->ioctl_lock); | 109 | spin_unlock_irqrestore(&dev->regs_lock, flags); |
103 | return -ENODEV; | 110 | return -ENODEV; |
104 | } | 111 | } |
105 | 112 | ||
106 | 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 | |||
107 | iowrite32(r.value, dev->iaddr + r.reg); | 122 | iowrite32(r.value, dev->iaddr + r.reg); |
108 | ioread32(dev->iaddr); /* PCI posting */ | 123 | ioread32(dev->iaddr); /* PCI posting */ |
109 | 124 | ||
110 | if (r.reg == PHN_CONTROL && !(r.value & PHN_CTL_IRQ)) | 125 | if (r.reg == PHN_CONTROL && !(r.value & PHN_CTL_IRQ)) |
111 | phantom_status(dev, dev->status & ~PHB_RUNNING); | 126 | phantom_status(dev, dev->status & ~PHB_RUNNING); |
112 | spin_unlock(&dev->ioctl_lock); | 127 | spin_unlock_irqrestore(&dev->regs_lock, flags); |
113 | break; | 128 | break; |
114 | case PHN_SET_REGS: | 129 | case PHN_SET_REGS: |
115 | if (copy_from_user(&rs, argp, sizeof(rs))) | 130 | if (copy_from_user(&rs, argp, sizeof(rs))) |
116 | return -EFAULT; | 131 | return -EFAULT; |
117 | 132 | ||
118 | 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); |
119 | spin_lock(&dev->ioctl_lock); | 134 | spin_lock_irqsave(&dev->regs_lock, flags); |
120 | for (i = 0; i < min(rs.count, 8U); i++) | 135 | if (dev->status & PHB_NOT_OH) |
121 | if ((1 << i) & rs.mask) | 136 | memcpy(&dev->oregs, &rs, sizeof(rs)); |
122 | iowrite32(rs.values[i], dev->oaddr + i); | 137 | else { |
123 | ioread32(dev->iaddr); /* PCI posting */ | 138 | u32 m = min(rs.count, 8U); |
124 | 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); | ||
125 | break; | 145 | break; |
126 | case PHN_GET_REG: | 146 | case PHN_GET_REG: |
127 | if (copy_from_user(&r, argp, sizeof(r))) | 147 | if (copy_from_user(&r, argp, sizeof(r))) |
@@ -135,20 +155,35 @@ static long phantom_ioctl(struct file *file, unsigned int cmd, | |||
135 | if (copy_to_user(argp, &r, sizeof(r))) | 155 | if (copy_to_user(argp, &r, sizeof(r))) |
136 | return -EFAULT; | 156 | return -EFAULT; |
137 | break; | 157 | break; |
138 | case PHN_GET_REGS: | 158 | case PHN_GET_REGS: { |
159 | u32 m; | ||
160 | |||
139 | if (copy_from_user(&rs, argp, sizeof(rs))) | 161 | if (copy_from_user(&rs, argp, sizeof(rs))) |
140 | return -EFAULT; | 162 | return -EFAULT; |
141 | 163 | ||
164 | m = min(rs.count, 8U); | ||
165 | |||
142 | 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); |
143 | spin_lock(&dev->ioctl_lock); | 167 | spin_lock_irqsave(&dev->regs_lock, flags); |
144 | for (i = 0; i < min(rs.count, 8U); i++) | 168 | for (i = 0; i < m; i++) |
145 | if ((1 << i) & rs.mask) | 169 | if (rs.mask & BIT(i)) |
146 | rs.values[i] = ioread32(dev->iaddr + i); | 170 | rs.values[i] = ioread32(dev->iaddr + i); |
147 | spin_unlock(&dev->ioctl_lock); | 171 | spin_unlock_irqrestore(&dev->regs_lock, flags); |
148 | 172 | ||
149 | if (copy_to_user(argp, &rs, sizeof(rs))) | 173 | if (copy_to_user(argp, &rs, sizeof(rs))) |
150 | return -EFAULT; | 174 | return -EFAULT; |
151 | 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; | ||
152 | default: | 187 | default: |
153 | return -ENOTTY; | 188 | return -ENOTTY; |
154 | } | 189 | } |
@@ -171,8 +206,11 @@ static int phantom_open(struct inode *inode, struct file *file) | |||
171 | return -EINVAL; | 206 | return -EINVAL; |
172 | } | 207 | } |
173 | 208 | ||
209 | WARN_ON(dev->status & PHB_NOT_OH); | ||
210 | |||
174 | file->private_data = dev; | 211 | file->private_data = dev; |
175 | 212 | ||
213 | atomic_set(&dev->counter, 0); | ||
176 | dev->opened++; | 214 | dev->opened++; |
177 | mutex_unlock(&dev->open_lock); | 215 | mutex_unlock(&dev->open_lock); |
178 | 216 | ||
@@ -187,6 +225,7 @@ static int phantom_release(struct inode *inode, struct file *file) | |||
187 | 225 | ||
188 | dev->opened = 0; | 226 | dev->opened = 0; |
189 | phantom_status(dev, dev->status & ~PHB_RUNNING); | 227 | phantom_status(dev, dev->status & ~PHB_RUNNING); |
228 | dev->status &= ~PHB_NOT_OH; | ||
190 | 229 | ||
191 | mutex_unlock(&dev->open_lock); | 230 | mutex_unlock(&dev->open_lock); |
192 | 231 | ||
@@ -220,12 +259,32 @@ static struct file_operations phantom_file_ops = { | |||
220 | static irqreturn_t phantom_isr(int irq, void *data) | 259 | static irqreturn_t phantom_isr(int irq, void *data) |
221 | { | 260 | { |
222 | struct phantom_device *dev = data; | 261 | struct phantom_device *dev = data; |
262 | unsigned int i; | ||
263 | u32 ctl; | ||
223 | 264 | ||
224 | 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); | ||
225 | return IRQ_NONE; | 269 | return IRQ_NONE; |
270 | } | ||
226 | 271 | ||
227 | iowrite32(0, dev->iaddr); | 272 | iowrite32(0, dev->iaddr); |
228 | 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 | |||
229 | ioread32(dev->iaddr); /* PCI posting */ | 288 | ioread32(dev->iaddr); /* PCI posting */ |
230 | 289 | ||
231 | atomic_inc(&dev->counter); | 290 | atomic_inc(&dev->counter); |
@@ -297,7 +356,7 @@ static int __devinit phantom_probe(struct pci_dev *pdev, | |||
297 | } | 356 | } |
298 | 357 | ||
299 | mutex_init(&pht->open_lock); | 358 | mutex_init(&pht->open_lock); |
300 | spin_lock_init(&pht->ioctl_lock); | 359 | spin_lock_init(&pht->regs_lock); |
301 | init_waitqueue_head(&pht->wait); | 360 | init_waitqueue_head(&pht->wait); |
302 | cdev_init(&pht->cdev, &phantom_file_ops); | 361 | cdev_init(&pht->cdev, &phantom_file_ops); |
303 | pht->cdev.owner = THIS_MODULE; | 362 | pht->cdev.owner = THIS_MODULE; |
@@ -378,6 +437,8 @@ static int phantom_suspend(struct pci_dev *pdev, pm_message_t state) | |||
378 | iowrite32(0, dev->caddr + PHN_IRQCTL); | 437 | iowrite32(0, dev->caddr + PHN_IRQCTL); |
379 | ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */ | 438 | ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */ |
380 | 439 | ||
440 | synchronize_irq(pdev->irq); | ||
441 | |||
381 | return 0; | 442 | return 0; |
382 | } | 443 | } |
383 | 444 | ||