Commit bc552f77157d1bae79d0d3a5541da9579c39cb70

Authored by Jiri Slaby
Committed by Linus Torvalds
1 parent b2afe33170

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>

Showing 2 changed files with 81 additions and 19 deletions Side-by-side Diff

drivers/misc/phantom.c
... ... @@ -25,13 +25,14 @@
25 25 #include <asm/atomic.h>
26 26 #include <asm/io.h>
27 27  
28   -#define PHANTOM_VERSION "n0.9.5"
  28 +#define PHANTOM_VERSION "n0.9.7"
29 29  
30 30 #define PHANTOM_MAX_MINORS 8
31 31  
32 32 #define PHN_IRQCTL 0x4c /* irq control in caddr space */
33 33  
34 34 #define PHB_RUNNING 1
  35 +#define PHB_NOT_OH 2
35 36  
36 37 static struct class *phantom_class;
37 38 static int phantom_major;
... ... @@ -48,7 +49,11 @@
48 49 struct cdev cdev;
49 50  
50 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 59 static unsigned char phantom_devices[PHANTOM_MAX_MINORS];
... ... @@ -83,6 +88,7 @@
83 88 struct phm_regs rs;
84 89 struct phm_reg r;
85 90 void __user *argp = (void __user *)arg;
  91 + unsigned long flags;
86 92 unsigned int i;
87 93  
88 94 if (_IOC_TYPE(cmd) != PH_IOC_MAGIC ||
89 95  
90 96  
91 97  
92 98  
... ... @@ -97,32 +103,45 @@
97 103 if (r.reg > 7)
98 104 return -EINVAL;
99 105  
100   - spin_lock(&dev->ioctl_lock);
  106 + spin_lock_irqsave(&dev->regs_lock, flags);
101 107 if (r.reg == PHN_CONTROL && (r.value & PHN_CTL_IRQ) &&
102 108 phantom_status(dev, dev->status | PHB_RUNNING)){
103   - spin_unlock(&dev->ioctl_lock);
  109 + spin_unlock_irqrestore(&dev->regs_lock, flags);
104 110 return -ENODEV;
105 111 }
106 112  
107 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 122 iowrite32(r.value, dev->iaddr + r.reg);
109 123 ioread32(dev->iaddr); /* PCI posting */
110 124  
111 125 if (r.reg == PHN_CONTROL && !(r.value & PHN_CTL_IRQ))
112 126 phantom_status(dev, dev->status & ~PHB_RUNNING);
113   - spin_unlock(&dev->ioctl_lock);
  127 + spin_unlock_irqrestore(&dev->regs_lock, flags);
114 128 break;
115 129 case PHN_SET_REGS:
116 130 if (copy_from_user(&rs, argp, sizeof(rs)))
117 131 return -EFAULT;
118 132  
119 133 pr_debug("phantom: SRS %u regs %x\n", rs.count, rs.mask);
120   - spin_lock(&dev->ioctl_lock);
121   - for (i = 0; i < min(rs.count, 8U); i++)
122   - if ((1 << i) & rs.mask)
123   - iowrite32(rs.values[i], dev->oaddr + i);
124   - ioread32(dev->iaddr); /* PCI posting */
125   - spin_unlock(&dev->ioctl_lock);
  134 + spin_lock_irqsave(&dev->regs_lock, flags);
  135 + if (dev->status & PHB_NOT_OH)
  136 + memcpy(&dev->oregs, &rs, sizeof(rs));
  137 + else {
  138 + u32 m = min(rs.count, 8U);
  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 145 break;
127 146 case PHN_GET_REG:
128 147 if (copy_from_user(&r, argp, sizeof(r)))
129 148  
130 149  
131 150  
132 151  
... ... @@ -136,20 +155,35 @@
136 155 if (copy_to_user(argp, &r, sizeof(r)))
137 156 return -EFAULT;
138 157 break;
139   - case PHN_GET_REGS:
  158 + case PHN_GET_REGS: {
  159 + u32 m;
  160 +
140 161 if (copy_from_user(&rs, argp, sizeof(rs)))
141 162 return -EFAULT;
142 163  
  164 + m = min(rs.count, 8U);
  165 +
143 166 pr_debug("phantom: GRS %u regs %x\n", rs.count, rs.mask);
144   - spin_lock(&dev->ioctl_lock);
145   - for (i = 0; i < min(rs.count, 8U); i++)
146   - if ((1 << i) & rs.mask)
  167 + spin_lock_irqsave(&dev->regs_lock, flags);
  168 + for (i = 0; i < m; i++)
  169 + if (rs.mask & BIT(i))
147 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 173 if (copy_to_user(argp, &rs, sizeof(rs)))
151 174 return -EFAULT;
152 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 187 default:
154 188 return -ENOTTY;
155 189 }
156 190  
... ... @@ -172,8 +206,11 @@
172 206 return -EINVAL;
173 207 }
174 208  
  209 + WARN_ON(dev->status & PHB_NOT_OH);
  210 +
175 211 file->private_data = dev;
176 212  
  213 + atomic_set(&dev->counter, 0);
177 214 dev->opened++;
178 215 mutex_unlock(&dev->open_lock);
179 216  
... ... @@ -188,6 +225,7 @@
188 225  
189 226 dev->opened = 0;
190 227 phantom_status(dev, dev->status & ~PHB_RUNNING);
  228 + dev->status &= ~PHB_NOT_OH;
191 229  
192 230 mutex_unlock(&dev->open_lock);
193 231  
194 232  
195 233  
196 234  
... ... @@ -221,12 +259,32 @@
221 259 static irqreturn_t phantom_isr(int irq, void *data)
222 260 {
223 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 269 return IRQ_NONE;
  270 + }
227 271  
228 272 iowrite32(0, dev->iaddr);
229 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 288 ioread32(dev->iaddr); /* PCI posting */
231 289  
232 290 atomic_inc(&dev->counter);
... ... @@ -298,7 +356,7 @@
298 356 }
299 357  
300 358 mutex_init(&pht->open_lock);
301   - spin_lock_init(&pht->ioctl_lock);
  359 + spin_lock_init(&pht->regs_lock);
302 360 init_waitqueue_head(&pht->wait);
303 361 cdev_init(&pht->cdev, &phantom_file_ops);
304 362 pht->cdev.owner = THIS_MODULE;
include/linux/phantom.h
... ... @@ -30,7 +30,11 @@
30 30 #define PHN_SET_REG _IOW (PH_IOC_MAGIC, 1, struct phm_reg *)
31 31 #define PHN_GET_REGS _IOWR(PH_IOC_MAGIC, 2, struct phm_regs *)
32 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 39 #define PHN_CONTROL 0x6 /* control byte in iaddr space */
36 40 #define PHN_CTL_AMP 0x1 /* switch after torques change */