Blame view

drivers/misc/phantom.c 13 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
cef2cf072   Jiri Slaby   Misc: add sensabl...
2
3
4
  /*
   *  Copyright (C) 2005-2007 Jiri Slaby <jirislaby@gmail.com>
   *
0211a9c85   Frederik Schwarzer   trivial: fix an -...
5
   *  You need a userspace library to cooperate with this driver. It (and other
cef2cf072   Jiri Slaby   Misc: add sensabl...
6
7
   *  info) may be obtained here:
   *  http://www.fi.muni.cz/~xslaby/phantom.html
b2afe3317   Jiri Slaby   Misc: phantom, ad...
8
   *  or alternatively, you might use OpenHaptics provided by Sensable.
cef2cf072   Jiri Slaby   Misc: add sensabl...
9
   */
7e4e8e689   Jiri Slaby   Misc: phantom, ad...
10
  #include <linux/compat.h>
cef2cf072   Jiri Slaby   Misc: add sensabl...
11
12
13
14
15
16
17
18
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/device.h>
  #include <linux/pci.h>
  #include <linux/fs.h>
  #include <linux/poll.h>
  #include <linux/interrupt.h>
  #include <linux/cdev.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
19
  #include <linux/slab.h>
cef2cf072   Jiri Slaby   Misc: add sensabl...
20
  #include <linux/phantom.h>
d43c36dc6   Alexey Dobriyan   headers: remove s...
21
  #include <linux/sched.h>
613655fa3   Arnd Bergmann   drivers: autoconv...
22
  #include <linux/mutex.h>
cef2cf072   Jiri Slaby   Misc: add sensabl...
23

60063497a   Arun Sharma   atomic: use <linu...
24
  #include <linux/atomic.h>
cef2cf072   Jiri Slaby   Misc: add sensabl...
25
  #include <asm/io.h>
82f560874   Jiri Slaby   phantom: don't gr...
26
  #define PHANTOM_VERSION		"n0.9.8"
cef2cf072   Jiri Slaby   Misc: add sensabl...
27
28
29
30
31
32
  
  #define PHANTOM_MAX_MINORS	8
  
  #define PHN_IRQCTL		0x4c    /* irq control in caddr space */
  
  #define PHB_RUNNING		1
bc552f771   Jiri Slaby   Misc: phantom, im...
33
  #define PHB_NOT_OH		2
cef2cf072   Jiri Slaby   Misc: add sensabl...
34

613655fa3   Arnd Bergmann   drivers: autoconv...
35
  static DEFINE_MUTEX(phantom_mutex);
cef2cf072   Jiri Slaby   Misc: add sensabl...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  static struct class *phantom_class;
  static int phantom_major;
  
  struct phantom_device {
  	unsigned int opened;
  	void __iomem *caddr;
  	u32 __iomem *iaddr;
  	u32 __iomem *oaddr;
  	unsigned long status;
  	atomic_t counter;
  
  	wait_queue_head_t wait;
  	struct cdev cdev;
  
  	struct mutex open_lock;
bc552f771   Jiri Slaby   Misc: phantom, im...
51
52
53
54
55
  	spinlock_t regs_lock;
  
  	/* used in NOT_OH mode */
  	struct phm_regs oregs;
  	u32 ctl_reg;
cef2cf072   Jiri Slaby   Misc: add sensabl...
56
57
58
59
60
61
62
63
64
65
66
67
68
  };
  
  static unsigned char phantom_devices[PHANTOM_MAX_MINORS];
  
  static int phantom_status(struct phantom_device *dev, unsigned long newstat)
  {
  	pr_debug("phantom_status %lx %lx
  ", dev->status, newstat);
  
  	if (!(dev->status & PHB_RUNNING) && (newstat & PHB_RUNNING)) {
  		atomic_set(&dev->counter, 0);
  		iowrite32(PHN_CTL_IRQ, dev->iaddr + PHN_CONTROL);
  		iowrite32(0x43, dev->caddr + PHN_IRQCTL);
c8511f949   Jiri Slaby   Misc: phantom, ta...
69
70
  		ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */
  	} else if ((dev->status & PHB_RUNNING) && !(newstat & PHB_RUNNING)) {
cef2cf072   Jiri Slaby   Misc: add sensabl...
71
  		iowrite32(0, dev->caddr + PHN_IRQCTL);
c8511f949   Jiri Slaby   Misc: phantom, ta...
72
73
  		ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */
  	}
cef2cf072   Jiri Slaby   Misc: add sensabl...
74
75
76
77
78
79
80
81
82
  
  	dev->status = newstat;
  
  	return 0;
  }
  
  /*
   * File ops
   */
c15395c0d   Jiri Slaby   phantom: move to ...
83
84
  static long phantom_ioctl(struct file *file, unsigned int cmd,
  		unsigned long arg)
cef2cf072   Jiri Slaby   Misc: add sensabl...
85
86
87
88
89
  {
  	struct phantom_device *dev = file->private_data;
  	struct phm_regs rs;
  	struct phm_reg r;
  	void __user *argp = (void __user *)arg;
bc552f771   Jiri Slaby   Misc: phantom, im...
90
  	unsigned long flags;
cef2cf072   Jiri Slaby   Misc: add sensabl...
91
  	unsigned int i;
cef2cf072   Jiri Slaby   Misc: add sensabl...
92
  	switch (cmd) {
7e4e8e689   Jiri Slaby   Misc: phantom, ad...
93
  	case PHN_SETREG:
cef2cf072   Jiri Slaby   Misc: add sensabl...
94
95
96
97
98
99
  	case PHN_SET_REG:
  		if (copy_from_user(&r, argp, sizeof(r)))
  			return -EFAULT;
  
  		if (r.reg > 7)
  			return -EINVAL;
bc552f771   Jiri Slaby   Misc: phantom, im...
100
  		spin_lock_irqsave(&dev->regs_lock, flags);
cef2cf072   Jiri Slaby   Misc: add sensabl...
101
  		if (r.reg == PHN_CONTROL && (r.value & PHN_CTL_IRQ) &&
c15395c0d   Jiri Slaby   phantom: move to ...
102
  				phantom_status(dev, dev->status | PHB_RUNNING)){
bc552f771   Jiri Slaby   Misc: phantom, im...
103
  			spin_unlock_irqrestore(&dev->regs_lock, flags);
cef2cf072   Jiri Slaby   Misc: add sensabl...
104
  			return -ENODEV;
c15395c0d   Jiri Slaby   phantom: move to ...
105
  		}
cef2cf072   Jiri Slaby   Misc: add sensabl...
106
107
108
  
  		pr_debug("phantom: writing %x to %u
  ", r.value, r.reg);
bc552f771   Jiri Slaby   Misc: phantom, im...
109
110
111
112
113
114
115
  
  		/* 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;
  		}
cef2cf072   Jiri Slaby   Misc: add sensabl...
116
  		iowrite32(r.value, dev->iaddr + r.reg);
c8511f949   Jiri Slaby   Misc: phantom, ta...
117
  		ioread32(dev->iaddr); /* PCI posting */
cef2cf072   Jiri Slaby   Misc: add sensabl...
118
119
120
  
  		if (r.reg == PHN_CONTROL && !(r.value & PHN_CTL_IRQ))
  			phantom_status(dev, dev->status & ~PHB_RUNNING);
bc552f771   Jiri Slaby   Misc: phantom, im...
121
  		spin_unlock_irqrestore(&dev->regs_lock, flags);
cef2cf072   Jiri Slaby   Misc: add sensabl...
122
  		break;
7e4e8e689   Jiri Slaby   Misc: phantom, ad...
123
  	case PHN_SETREGS:
cef2cf072   Jiri Slaby   Misc: add sensabl...
124
125
126
127
128
129
  	case PHN_SET_REGS:
  		if (copy_from_user(&rs, argp, sizeof(rs)))
  			return -EFAULT;
  
  		pr_debug("phantom: SRS %u regs %x
  ", rs.count, rs.mask);
bc552f771   Jiri Slaby   Misc: phantom, im...
130
131
132
133
134
135
136
137
138
139
140
  		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);
cef2cf072   Jiri Slaby   Misc: add sensabl...
141
  		break;
7e4e8e689   Jiri Slaby   Misc: phantom, ad...
142
  	case PHN_GETREG:
cef2cf072   Jiri Slaby   Misc: add sensabl...
143
144
145
146
147
148
149
150
151
152
153
154
  	case PHN_GET_REG:
  		if (copy_from_user(&r, argp, sizeof(r)))
  			return -EFAULT;
  
  		if (r.reg > 7)
  			return -EINVAL;
  
  		r.value = ioread32(dev->iaddr + r.reg);
  
  		if (copy_to_user(argp, &r, sizeof(r)))
  			return -EFAULT;
  		break;
7e4e8e689   Jiri Slaby   Misc: phantom, ad...
155
  	case PHN_GETREGS:
bc552f771   Jiri Slaby   Misc: phantom, im...
156
157
  	case PHN_GET_REGS: {
  		u32 m;
cef2cf072   Jiri Slaby   Misc: add sensabl...
158
159
  		if (copy_from_user(&rs, argp, sizeof(rs)))
  			return -EFAULT;
bc552f771   Jiri Slaby   Misc: phantom, im...
160
  		m = min(rs.count, 8U);
cef2cf072   Jiri Slaby   Misc: add sensabl...
161
162
  		pr_debug("phantom: GRS %u regs %x
  ", rs.count, rs.mask);
bc552f771   Jiri Slaby   Misc: phantom, im...
163
164
165
  		spin_lock_irqsave(&dev->regs_lock, flags);
  		for (i = 0; i < m; i++)
  			if (rs.mask & BIT(i))
cef2cf072   Jiri Slaby   Misc: add sensabl...
166
  				rs.values[i] = ioread32(dev->iaddr + i);
7d4f9f094   Jiri Slaby   Misc, phantom, fi...
167
  		atomic_set(&dev->counter, 0);
bc552f771   Jiri Slaby   Misc: phantom, im...
168
  		spin_unlock_irqrestore(&dev->regs_lock, flags);
cef2cf072   Jiri Slaby   Misc: add sensabl...
169
170
171
172
  
  		if (copy_to_user(argp, &rs, sizeof(rs)))
  			return -EFAULT;
  		break;
bc552f771   Jiri Slaby   Misc: phantom, im...
173
174
175
176
177
178
179
180
181
182
183
184
  	} 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!
  ");
  			spin_unlock_irqrestore(&dev->regs_lock, flags);
  			return -EINVAL;
  		}
  		dev->status |= PHB_NOT_OH;
  		spin_unlock_irqrestore(&dev->regs_lock, flags);
  		break;
cef2cf072   Jiri Slaby   Misc: add sensabl...
185
186
187
188
189
190
  	default:
  		return -ENOTTY;
  	}
  
  	return 0;
  }
7e4e8e689   Jiri Slaby   Misc: phantom, ad...
191
192
193
194
195
196
197
198
199
200
201
202
203
  #ifdef CONFIG_COMPAT
  static long phantom_compat_ioctl(struct file *filp, unsigned int cmd,
  		unsigned long arg)
  {
  	if (_IOC_NR(cmd) <= 3 && _IOC_SIZE(cmd) == sizeof(compat_uptr_t)) {
  		cmd &= ~(_IOC_SIZEMASK << _IOC_SIZESHIFT);
  		cmd |= sizeof(void *) << _IOC_SIZESHIFT;
  	}
  	return phantom_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
  }
  #else
  #define phantom_compat_ioctl NULL
  #endif
cef2cf072   Jiri Slaby   Misc: add sensabl...
204
205
206
207
  static int phantom_open(struct inode *inode, struct file *file)
  {
  	struct phantom_device *dev = container_of(inode->i_cdev,
  			struct phantom_device, cdev);
613655fa3   Arnd Bergmann   drivers: autoconv...
208
  	mutex_lock(&phantom_mutex);
cef2cf072   Jiri Slaby   Misc: add sensabl...
209
  	nonseekable_open(inode, file);
4541b5ec9   Jonathan Corbet   phantom: BKL push...
210
  	if (mutex_lock_interruptible(&dev->open_lock)) {
613655fa3   Arnd Bergmann   drivers: autoconv...
211
  		mutex_unlock(&phantom_mutex);
cef2cf072   Jiri Slaby   Misc: add sensabl...
212
  		return -ERESTARTSYS;
4541b5ec9   Jonathan Corbet   phantom: BKL push...
213
  	}
cef2cf072   Jiri Slaby   Misc: add sensabl...
214
215
216
  
  	if (dev->opened) {
  		mutex_unlock(&dev->open_lock);
613655fa3   Arnd Bergmann   drivers: autoconv...
217
  		mutex_unlock(&phantom_mutex);
cef2cf072   Jiri Slaby   Misc: add sensabl...
218
219
  		return -EINVAL;
  	}
bc552f771   Jiri Slaby   Misc: phantom, im...
220
  	WARN_ON(dev->status & PHB_NOT_OH);
cef2cf072   Jiri Slaby   Misc: add sensabl...
221
  	file->private_data = dev;
bc552f771   Jiri Slaby   Misc: phantom, im...
222
  	atomic_set(&dev->counter, 0);
cef2cf072   Jiri Slaby   Misc: add sensabl...
223
224
  	dev->opened++;
  	mutex_unlock(&dev->open_lock);
613655fa3   Arnd Bergmann   drivers: autoconv...
225
  	mutex_unlock(&phantom_mutex);
cef2cf072   Jiri Slaby   Misc: add sensabl...
226
227
228
229
230
231
232
233
234
235
236
  	return 0;
  }
  
  static int phantom_release(struct inode *inode, struct file *file)
  {
  	struct phantom_device *dev = file->private_data;
  
  	mutex_lock(&dev->open_lock);
  
  	dev->opened = 0;
  	phantom_status(dev, dev->status & ~PHB_RUNNING);
bc552f771   Jiri Slaby   Misc: phantom, im...
237
  	dev->status &= ~PHB_NOT_OH;
cef2cf072   Jiri Slaby   Misc: add sensabl...
238
239
240
241
242
  
  	mutex_unlock(&dev->open_lock);
  
  	return 0;
  }
afc9a42b7   Al Viro   the rest of drive...
243
  static __poll_t phantom_poll(struct file *file, poll_table *wait)
cef2cf072   Jiri Slaby   Misc: add sensabl...
244
245
  {
  	struct phantom_device *dev = file->private_data;
afc9a42b7   Al Viro   the rest of drive...
246
  	__poll_t mask = 0;
cef2cf072   Jiri Slaby   Misc: add sensabl...
247
248
249
250
  
  	pr_debug("phantom_poll: %d
  ", atomic_read(&dev->counter));
  	poll_wait(file, &dev->wait, wait);
7d4f9f094   Jiri Slaby   Misc, phantom, fi...
251
252
  
  	if (!(dev->status & PHB_RUNNING))
a9a08845e   Linus Torvalds   vfs: do bulk POLL...
253
  		mask = EPOLLERR;
7d4f9f094   Jiri Slaby   Misc, phantom, fi...
254
  	else if (atomic_read(&dev->counter))
a9a08845e   Linus Torvalds   vfs: do bulk POLL...
255
  		mask = EPOLLIN | EPOLLRDNORM;
7d4f9f094   Jiri Slaby   Misc, phantom, fi...
256

cef2cf072   Jiri Slaby   Misc: add sensabl...
257
258
259
260
261
  	pr_debug("phantom_poll end: %x/%d
  ", mask, atomic_read(&dev->counter));
  
  	return mask;
  }
828c09509   Alexey Dobriyan   const: constify r...
262
  static const struct file_operations phantom_file_ops = {
cef2cf072   Jiri Slaby   Misc: add sensabl...
263
264
  	.open = phantom_open,
  	.release = phantom_release,
c15395c0d   Jiri Slaby   phantom: move to ...
265
  	.unlocked_ioctl = phantom_ioctl,
7e4e8e689   Jiri Slaby   Misc: phantom, ad...
266
  	.compat_ioctl = phantom_compat_ioctl,
cef2cf072   Jiri Slaby   Misc: add sensabl...
267
  	.poll = phantom_poll,
6038f373a   Arnd Bergmann   llseek: automatic...
268
  	.llseek = no_llseek,
cef2cf072   Jiri Slaby   Misc: add sensabl...
269
270
271
272
273
  };
  
  static irqreturn_t phantom_isr(int irq, void *data)
  {
  	struct phantom_device *dev = data;
bc552f771   Jiri Slaby   Misc: phantom, im...
274
275
  	unsigned int i;
  	u32 ctl;
cef2cf072   Jiri Slaby   Misc: add sensabl...
276

bc552f771   Jiri Slaby   Misc: phantom, im...
277
278
279
280
  	spin_lock(&dev->regs_lock);
  	ctl = ioread32(dev->iaddr + PHN_CONTROL);
  	if (!(ctl & PHN_CTL_IRQ)) {
  		spin_unlock(&dev->regs_lock);
cef2cf072   Jiri Slaby   Misc: add sensabl...
281
  		return IRQ_NONE;
bc552f771   Jiri Slaby   Misc: phantom, im...
282
  	}
cef2cf072   Jiri Slaby   Misc: add sensabl...
283
284
285
  
  	iowrite32(0, dev->iaddr);
  	iowrite32(0xc0, dev->iaddr);
bc552f771   Jiri Slaby   Misc: phantom, im...
286
287
288
289
290
291
292
293
294
295
296
297
298
  
  	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);
c8511f949   Jiri Slaby   Misc: phantom, ta...
299
  	ioread32(dev->iaddr); /* PCI posting */
cef2cf072   Jiri Slaby   Misc: add sensabl...
300
301
302
303
304
305
306
307
308
309
  
  	atomic_inc(&dev->counter);
  	wake_up_interruptible(&dev->wait);
  
  	return IRQ_HANDLED;
  }
  
  /*
   * Init and deinit driver
   */
80c8ae289   Bill Pemberton   misc: remove use ...
310
  static unsigned int phantom_get_free(void)
cef2cf072   Jiri Slaby   Misc: add sensabl...
311
312
313
314
315
316
317
318
319
  {
  	unsigned int i;
  
  	for (i = 0; i < PHANTOM_MAX_MINORS; i++)
  		if (phantom_devices[i] == 0)
  			break;
  
  	return i;
  }
80c8ae289   Bill Pemberton   misc: remove use ...
320
  static int phantom_probe(struct pci_dev *pdev,
cef2cf072   Jiri Slaby   Misc: add sensabl...
321
322
323
324
325
326
327
  	const struct pci_device_id *pci_id)
  {
  	struct phantom_device *pht;
  	unsigned int minor;
  	int retval;
  
  	retval = pci_enable_device(pdev);
10ad5278b   Rahul Ruikar   drivers/misc/phan...
328
329
330
  	if (retval) {
  		dev_err(&pdev->dev, "pci_enable_device failed!
  ");
cef2cf072   Jiri Slaby   Misc: add sensabl...
331
  		goto err;
10ad5278b   Rahul Ruikar   drivers/misc/phan...
332
  	}
cef2cf072   Jiri Slaby   Misc: add sensabl...
333
334
335
336
337
338
339
340
341
342
343
344
  
  	minor = phantom_get_free();
  	if (minor == PHANTOM_MAX_MINORS) {
  		dev_err(&pdev->dev, "too many devices found!
  ");
  		retval = -EIO;
  		goto err_dis;
  	}
  
  	phantom_devices[minor] = 1;
  
  	retval = pci_request_regions(pdev, "phantom");
10ad5278b   Rahul Ruikar   drivers/misc/phan...
345
346
347
  	if (retval) {
  		dev_err(&pdev->dev, "pci_request_regions failed!
  ");
cef2cf072   Jiri Slaby   Misc: add sensabl...
348
  		goto err_null;
10ad5278b   Rahul Ruikar   drivers/misc/phan...
349
  	}
cef2cf072   Jiri Slaby   Misc: add sensabl...
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
  
  	retval = -ENOMEM;
  	pht = kzalloc(sizeof(*pht), GFP_KERNEL);
  	if (pht == NULL) {
  		dev_err(&pdev->dev, "unable to allocate device
  ");
  		goto err_reg;
  	}
  
  	pht->caddr = pci_iomap(pdev, 0, 0);
  	if (pht->caddr == NULL) {
  		dev_err(&pdev->dev, "can't remap conf space
  ");
  		goto err_fr;
  	}
  	pht->iaddr = pci_iomap(pdev, 2, 0);
  	if (pht->iaddr == NULL) {
  		dev_err(&pdev->dev, "can't remap input space
  ");
  		goto err_unmc;
  	}
  	pht->oaddr = pci_iomap(pdev, 3, 0);
  	if (pht->oaddr == NULL) {
  		dev_err(&pdev->dev, "can't remap output space
  ");
  		goto err_unmi;
  	}
  
  	mutex_init(&pht->open_lock);
bc552f771   Jiri Slaby   Misc: phantom, im...
379
  	spin_lock_init(&pht->regs_lock);
cef2cf072   Jiri Slaby   Misc: add sensabl...
380
381
382
383
384
  	init_waitqueue_head(&pht->wait);
  	cdev_init(&pht->cdev, &phantom_file_ops);
  	pht->cdev.owner = THIS_MODULE;
  
  	iowrite32(0, pht->caddr + PHN_IRQCTL);
c8511f949   Jiri Slaby   Misc: phantom, ta...
385
  	ioread32(pht->caddr + PHN_IRQCTL); /* PCI posting */
cef2cf072   Jiri Slaby   Misc: add sensabl...
386
  	retval = request_irq(pdev->irq, phantom_isr,
bb9da88d7   Michael Opdenacker   misc: phantom: re...
387
  			IRQF_SHARED, "phantom", pht);
cef2cf072   Jiri Slaby   Misc: add sensabl...
388
389
390
391
392
393
394
395
396
397
398
399
  	if (retval) {
  		dev_err(&pdev->dev, "can't establish ISR
  ");
  		goto err_unmo;
  	}
  
  	retval = cdev_add(&pht->cdev, MKDEV(phantom_major, minor), 1);
  	if (retval) {
  		dev_err(&pdev->dev, "chardev registration failed
  ");
  		goto err_irq;
  	}
a9b12619f   Greg Kroah-Hartman   device create: mi...
400
401
402
  	if (IS_ERR(device_create(phantom_class, &pdev->dev,
  				 MKDEV(phantom_major, minor), NULL,
  				 "phantom%u", minor)))
cef2cf072   Jiri Slaby   Misc: add sensabl...
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
  		dev_err(&pdev->dev, "can't create device
  ");
  
  	pci_set_drvdata(pdev, pht);
  
  	return 0;
  err_irq:
  	free_irq(pdev->irq, pht);
  err_unmo:
  	pci_iounmap(pdev, pht->oaddr);
  err_unmi:
  	pci_iounmap(pdev, pht->iaddr);
  err_unmc:
  	pci_iounmap(pdev, pht->caddr);
  err_fr:
  	kfree(pht);
  err_reg:
  	pci_release_regions(pdev);
  err_null:
  	phantom_devices[minor] = 0;
  err_dis:
  	pci_disable_device(pdev);
  err:
  	return retval;
  }
486a5c28c   Bill Pemberton   misc: remove use ...
428
  static void phantom_remove(struct pci_dev *pdev)
cef2cf072   Jiri Slaby   Misc: add sensabl...
429
430
431
432
433
434
435
436
437
  {
  	struct phantom_device *pht = pci_get_drvdata(pdev);
  	unsigned int minor = MINOR(pht->cdev.dev);
  
  	device_destroy(phantom_class, MKDEV(phantom_major, minor));
  
  	cdev_del(&pht->cdev);
  
  	iowrite32(0, pht->caddr + PHN_IRQCTL);
c8511f949   Jiri Slaby   Misc: phantom, ta...
438
  	ioread32(pht->caddr + PHN_IRQCTL); /* PCI posting */
cef2cf072   Jiri Slaby   Misc: add sensabl...
439
440
441
442
443
444
445
446
447
448
449
450
451
452
  	free_irq(pdev->irq, pht);
  
  	pci_iounmap(pdev, pht->oaddr);
  	pci_iounmap(pdev, pht->iaddr);
  	pci_iounmap(pdev, pht->caddr);
  
  	kfree(pht);
  
  	pci_release_regions(pdev);
  
  	phantom_devices[minor] = 0;
  
  	pci_disable_device(pdev);
  }
6bbf52566   Vaibhav Gupta   misc/phantom.c: u...
453
  static int __maybe_unused phantom_suspend(struct device *dev_d)
cef2cf072   Jiri Slaby   Misc: add sensabl...
454
  {
6bbf52566   Vaibhav Gupta   misc/phantom.c: u...
455
  	struct phantom_device *dev = dev_get_drvdata(dev_d);
cef2cf072   Jiri Slaby   Misc: add sensabl...
456
457
  
  	iowrite32(0, dev->caddr + PHN_IRQCTL);
c8511f949   Jiri Slaby   Misc: phantom, ta...
458
  	ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */
cef2cf072   Jiri Slaby   Misc: add sensabl...
459

6bbf52566   Vaibhav Gupta   misc/phantom.c: u...
460
  	synchronize_irq(to_pci_dev(dev_d)->irq);
aee8447cb   Jiri Slaby   Misc: phantom, sy...
461

cef2cf072   Jiri Slaby   Misc: add sensabl...
462
463
  	return 0;
  }
6bbf52566   Vaibhav Gupta   misc/phantom.c: u...
464
  static int __maybe_unused phantom_resume(struct device *dev_d)
cef2cf072   Jiri Slaby   Misc: add sensabl...
465
  {
6bbf52566   Vaibhav Gupta   misc/phantom.c: u...
466
  	struct phantom_device *dev = dev_get_drvdata(dev_d);
cef2cf072   Jiri Slaby   Misc: add sensabl...
467
468
469
470
471
  
  	iowrite32(0, dev->caddr + PHN_IRQCTL);
  
  	return 0;
  }
cef2cf072   Jiri Slaby   Misc: add sensabl...
472

2c6850641   Bill Pemberton   misc: remove use ...
473
  static struct pci_device_id phantom_pci_tbl[] = {
82f560874   Jiri Slaby   phantom: don't gr...
474
475
476
  	{ .vendor = PCI_VENDOR_ID_PLX, .device = PCI_DEVICE_ID_PLX_9050,
  	  .subvendor = PCI_VENDOR_ID_PLX, .subdevice = PCI_DEVICE_ID_PLX_9050,
  	  .class = PCI_CLASS_BRIDGE_OTHER << 8, .class_mask = 0xffff00 },
cef2cf072   Jiri Slaby   Misc: add sensabl...
477
478
479
  	{ 0, }
  };
  MODULE_DEVICE_TABLE(pci, phantom_pci_tbl);
6bbf52566   Vaibhav Gupta   misc/phantom.c: u...
480
  static SIMPLE_DEV_PM_OPS(phantom_pm_ops, phantom_suspend, phantom_resume);
cef2cf072   Jiri Slaby   Misc: add sensabl...
481
482
483
484
  static struct pci_driver phantom_pci_driver = {
  	.name = "phantom",
  	.id_table = phantom_pci_tbl,
  	.probe = phantom_probe,
2d6bed9ca   Bill Pemberton   drivers/misc: rem...
485
  	.remove = phantom_remove,
6bbf52566   Vaibhav Gupta   misc/phantom.c: u...
486
  	.driver.pm = &phantom_pm_ops,
cef2cf072   Jiri Slaby   Misc: add sensabl...
487
  };
0933e2d98   Andi Kleen   driver core: Conv...
488
  static CLASS_ATTR_STRING(version, 0444, PHANTOM_VERSION);
cef2cf072   Jiri Slaby   Misc: add sensabl...
489
490
491
492
493
494
495
496
497
498
499
500
501
  
  static int __init phantom_init(void)
  {
  	int retval;
  	dev_t dev;
  
  	phantom_class = class_create(THIS_MODULE, "phantom");
  	if (IS_ERR(phantom_class)) {
  		retval = PTR_ERR(phantom_class);
  		printk(KERN_ERR "phantom: can't register phantom class
  ");
  		goto err;
  	}
0933e2d98   Andi Kleen   driver core: Conv...
502
  	retval = class_create_file(phantom_class, &class_attr_version.attr);
cef2cf072   Jiri Slaby   Misc: add sensabl...
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
  	if (retval) {
  		printk(KERN_ERR "phantom: can't create sysfs version file
  ");
  		goto err_class;
  	}
  
  	retval = alloc_chrdev_region(&dev, 0, PHANTOM_MAX_MINORS, "phantom");
  	if (retval) {
  		printk(KERN_ERR "phantom: can't register character device
  ");
  		goto err_attr;
  	}
  	phantom_major = MAJOR(dev);
  
  	retval = pci_register_driver(&phantom_pci_driver);
  	if (retval) {
  		printk(KERN_ERR "phantom: can't register pci driver
  ");
  		goto err_unchr;
  	}
  
  	printk(KERN_INFO "Phantom Linux Driver, version " PHANTOM_VERSION ", "
  			"init OK
  ");
  
  	return 0;
  err_unchr:
  	unregister_chrdev_region(dev, PHANTOM_MAX_MINORS);
  err_attr:
0933e2d98   Andi Kleen   driver core: Conv...
532
  	class_remove_file(phantom_class, &class_attr_version.attr);
cef2cf072   Jiri Slaby   Misc: add sensabl...
533
534
535
536
537
538
539
540
541
542
543
  err_class:
  	class_destroy(phantom_class);
  err:
  	return retval;
  }
  
  static void __exit phantom_exit(void)
  {
  	pci_unregister_driver(&phantom_pci_driver);
  
  	unregister_chrdev_region(MKDEV(phantom_major, 0), PHANTOM_MAX_MINORS);
0933e2d98   Andi Kleen   driver core: Conv...
544
  	class_remove_file(phantom_class, &class_attr_version.attr);
cef2cf072   Jiri Slaby   Misc: add sensabl...
545
546
547
548
549
550
551
552
553
554
  	class_destroy(phantom_class);
  
  	pr_debug("phantom: module successfully removed
  ");
  }
  
  module_init(phantom_init);
  module_exit(phantom_exit);
  
  MODULE_AUTHOR("Jiri Slaby <jirislaby@gmail.com>");
ec905a186   Jiri Slaby   drivers/misc/phan...
555
  MODULE_DESCRIPTION("Sensable Phantom driver (PCI devices)");
cef2cf072   Jiri Slaby   Misc: add sensabl...
556
557
  MODULE_LICENSE("GPL");
  MODULE_VERSION(PHANTOM_VERSION);