Skip to contentSkip to conversation

Misc: add sensable phantom driver

PATCH In review Jiri Slaby 2 participants 5 messages May 2, 2007 – May 3, 2007

Subsystems (3)
lore
Share & tools
lore

Jiri Slaby

author
#0 lore raw

PATCH 1/1 Misc: add sensable phantom driver

Hi,

I'm attaching the phantom driver -v2. Please drop

input-ff-add-ff_raw-effect.patch
input-phantom-add-a-new-driver.patch

before applying this patch. Thanks.

--
add sensable phantom driver

---
commit d8cb58f904b80e250383e68832204fafaf02da8b
tree f3d95c2a3b0283d651799a2db797d133263b5a95
parent 3947dc9399ee9a24d293bf2d7ab92e434e2756fb
author Jiri Slaby <[email protected]> Wed, 02 May 2007 17:52:50 +0200 committer Jiri Slaby <[email protected]> Wed, 02 May 2007 17:52:50 +0200

 Documentation/ioctl-number.txt |    3 
 MAINTAINERS                    |    5 
 drivers/misc/Kconfig           |    9 +
 drivers/misc/Makefile          |    1 
 drivers/misc/phantom.c         |  459 ++++++++++++++++++++++++++++++++++++++++
 include/linux/phantom.h        |   42 ++++
 6 files changed, 518 insertions(+), 1 deletions(-)
Documentation/ioctl-number.txt +2 -1
index 8f750c0..3de7d37 100644
@@ -138,7 +138,8 @@ Code	Seq#	Include File		Comments
 'm'	00-1F	net/irda/irmod.h	conflict!
 'n'	00-7F	linux/ncp_fs.h
 'n'	E0-FF	video/matrox.h          matroxfb
-'p'	00-3F	linux/mc146818rtc.h
+'p'	00-0F	linux/phantom.h		conflict! (OpenHaptics needs this)
+'p'	00-3F	linux/mc146818rtc.h	conflict!
 'p'	40-7F	linux/nvram.h
 'p'	80-9F				user-space parport
 					<mailto:[email protected]>
MAINTAINERS +5 -0
index 6fda30d..af24ba3 100644
@@ -3092,6 +3092,11 @@ L: 	[email protected] (subscribers-only, general discussion)
 W:	http://www.nsa.gov/selinux
 S:	Supported
 
+SENSABLE PHANTOM
+P:	Jiri Slaby
+M:	[email protected]
+S:	Maintained
+
 SERIAL ATA (SATA) SUBSYSTEM:
 P:	Jeff Garzik
 M:	[email protected]
drivers/misc/Kconfig +9 -0
index bfb02c1..ce74306 100644
@@ -186,5 +186,14 @@ config BLINK
 	  output something to the screen like kexec kernels to give the user
 	  a visual indication that the kernel is doing something.
 
+config PHANTOM
+	tristate "Sensable PHANToM"
+	depends on PCI
+	help
+	  Say Y here if you want to build a driver for Sensable PHANToM device.
+
+	  If you choose to build module, its name will be phantom. If unsure,
+	  say N here.
+
 
 endmenu
drivers/misc/Makefile +1 -0
index ece6baf..b54420c 100644
@@ -14,3 +14,4 @@ obj-$(CONFIG_TIFM_7XX1)       	+= tifm_7xx1.o
 obj-$(CONFIG_SGI_IOC4)		+= ioc4.o
 obj-$(CONFIG_SONY_LAPTOP)	+= sony-laptop.o
 obj-$(CONFIG_THINKPAD_ACPI)	+= thinkpad_acpi.o
+obj-$(CONFIG_PHANTOM)		+= phantom.o
drivers/misc/phantom.c new file +459 -0

459 lines not on this page · raw message

include/linux/phantom.h new file +42 -0

42 lines not on this page · raw message

#1 lore raw
Quoted 1 line · On Wed, 2 May 2007 17:56:01 +0200 (CEST) Jiri Slaby <[email protected]>…

add sensable phantom driver

<googles>

Is that one of these?
http://www.est-kl.com/aufbau_general/index_hard_haptic.html?http://www.est-kl.com/hardware/haptic/sensable/phantom_omni.html

I don't think we've ever had a driver for a device like that before. I wonder how you designed the userspace interface to it.

drivers/misc/phantom.c line 73

Quoted 2 lines
drivers/misc/phantom.c new file +2 -0
+static int phantom_ioctl(struct inode *inode, struct file *file, u_int cmd,
+	u_long arg)

Could we please use standard-looking types in this driver? u32 and u64 and things?

However in this case, as it's an ioctl handler it should be "int" and "unsigned long".

Does this driver require compat handling? (Seems not?)

Quoted 13 lines

static int phantom_open(struct inode *inode, struct file *file)

{
	struct phantom_device *dev = container_of(inode->i_cdev,
			struct phantom_device, cdev);

	if (mutex_lock_interruptible(&dev->open_lock))
		return -ERESTARTSYS;

	if (dev->opened) {
		mutex_unlock(&dev->open_lock);
		return -EINVAL;
	}
Quoted 7 lines
	file->private_data = dev;

	dev->opened++;
	mutex_unlock(&dev->open_lock);

	return 0;
}

You should call nonseekable_open() in here.

Quoted 4 lines
drivers/misc/phantom.c new file +4 -0
+static int phantom_release(struct inode *inode, struct file *file)
+{
+	struct phantom_device *dev = file->private_data;
+

drivers/misc/phantom.c line 177

Quoted 10 lines
drivers/misc/phantom.c new file +10 -0
+	if (mutex_lock_interruptible(&dev->open_lock))
+		return -ERESTARTSYS;
+
+	dev->opened = 0;
+	phantom_status(dev, dev->status & ~PHB_RUNNING);
+
+	mutex_unlock(&dev->open_lock);
+
+	return 0;
+}

The mutex_lock_interruptible() is wrong, I think. This function is called when we do the final close(). If the signal _is_ taken then it's game
over: the device can no longer be opened.

Quoted 29 lines
drivers/misc/phantom.c new file +29 -0
+static unsigned int phantom_poll(struct file *file, poll_table *wait)
+{
+	struct phantom_device *dev = file->private_data;
+	unsigned int mask = 0;
+
+	pr_debug("phantom_poll: %u\n", dev->counter);
+	poll_wait(file, &dev->wait, wait);
+	if (dev->counter) {
+		mask = POLLIN | POLLRDNORM;
+		dev->counter--;
+	} else if ((dev->status & PHB_RUNNING) == 0)
+		mask = POLLIN | POLLRDNORM | POLLERR;
+	pr_debug("phantom_poll end: %x/%u\n", mask, dev->counter);
+
+	return mask;
+}
+
+static struct file_operations phantom_file_ops = {
+	.open = phantom_open,
+	.release = phantom_release,
+	.ioctl = phantom_ioctl,
+	.poll = phantom_poll,
+};
+
+static irqreturn_t phantom_isr(int irq, void *data)
+{
+	struct phantom_device *dev = data;
+
+	if (!(ioread32(dev->iaddr + PHN_CONTROL) & PHN_CTL_IRQ))

drivers/misc/phantom.c line 217

Quoted 10 lines
drivers/misc/phantom.c new file +10 -0
+		return IRQ_NONE;
+
+	iowrite32(0, dev->iaddr);
+	iowrite32(0xc0, dev->iaddr);
+
+	dev->counter++;
+	wake_up_interruptible(&dev->wait);
+
+	return IRQ_HANDLED;
+}

The access to dev->counter is racy even on non-preempt uniprocessor. An atomic_t would suit.

Quoted 4 lines
drivers/misc/phantom.c new file +4 -0
+/*
+ * Init and deinit driver
+ */
+

drivers/misc/phantom.c line 232

Quoted 10 lines
drivers/misc/phantom.c new file +10 -0
+static unsigned int __devinit phantom_get_free(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < PHANTOM_MAX_MINORS; i++)
+		if (phantom_devices[i] == 0)
+			break;
+
+	return i;
+}

This function assumes that the phantom_devices[] array will never have any holes in it. But if phantom_remove() is called out-of-order, it _will_ have holes. Perhaps - I didn't look too hard.

Quoted 127 lines
drivers/misc/phantom.c new file +127 -0
+static int __devinit phantom_probe(struct pci_dev *pdev,
+	const struct pci_device_id *pci_id)
+{
+	struct phantom_device *pht;
+	unsigned int minor;
+	int retval;
+
+	retval = pci_enable_device(pdev);
+	if (retval)
+		goto err;
+
+	minor = phantom_get_free();
+	if (minor == PHANTOM_MAX_MINORS) {
+		dev_err(&pdev->dev, "too many devices found!\n");
+		retval = -EIO;
+		goto err_dis;
+	}
+
+	phantom_devices[minor] = 1;
+
+	retval = pci_request_regions(pdev, "phantom");
+	if (retval)
+		goto err_null;
+
+	retval = -ENOMEM;
+	pht = kzalloc(sizeof(*pht), GFP_KERNEL);
+	if (pht == NULL) {
+		dev_err(&pdev->dev, "unable to allocate device\n");
+		goto err_reg;
+	}
+
+	pht->caddr = pci_iomap(pdev, 0, 0);
+	if (pht->caddr == NULL) {
+		dev_err(&pdev->dev, "can't remap conf space\n");
+		goto err_fr;
+	}
+	pht->iaddr = pci_iomap(pdev, 2, 0);
+	if (pht->iaddr == NULL) {
+		dev_err(&pdev->dev, "can't remap input space\n");
+		goto err_unmc;
+	}
+	pht->oaddr = pci_iomap(pdev, 3, 0);
+	if (pht->oaddr == NULL) {
+		dev_err(&pdev->dev, "can't remap output space\n");
+		goto err_unmi;
+	}
+
+	mutex_init(&pht->open_lock);
+	init_waitqueue_head(&pht->wait);
+	cdev_init(&pht->cdev, &phantom_file_ops);
+	pht->cdev.owner = THIS_MODULE;
+
+	iowrite32(0, pht->caddr + PHN_IRQCTL);
+	retval = request_irq(pdev->irq, phantom_isr,
+			IRQF_SHARED | IRQF_DISABLED, "phantom", pht);
+	if (retval) {
+		dev_err(&pdev->dev, "can't establish ISR\n");
+		goto err_unmo;
+	}
+
+	retval = cdev_add(&pht->cdev, MKDEV(phantom_major, minor), 1);
+	if (retval) {
+		dev_err(&pdev->dev, "chardev registration failed\n");
+		goto err_irq;
+	}
+
+	if (IS_ERR(device_create(phantom_class, &pdev->dev, MKDEV(phantom_major,
+			minor), "phantom%u", minor)))
+		dev_err(&pdev->dev, "can't create device\n");
+
+	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;
+}
+
+static void __devexit phantom_remove(struct pci_dev *pdev)
+{
+	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);
+	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);
+}
+
+#ifdef CONFIG_PM
+static int phantom_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct phantom_device *dev = pci_get_drvdata(pdev);
+
+	iowrite32(0, dev->caddr + PHN_IRQCTL);
+
+	return 0;
+}

drivers/misc/phantom.c line 370

Quoted 10 lines
drivers/misc/phantom.c new file +10 -0
+
+static int phantom_resume(struct pci_dev *pdev)
+{
+	struct phantom_device *dev = pci_get_drvdata(pdev);
+
+	iowrite32(0, dev->caddr + PHN_IRQCTL);
+
+	return 0;
+}
+#endif

It's conventional to do

#else
#define phantom_suspend NULL
#define phantom_resume NULL
#endif

Quoted 7 lines
drivers/misc/phantom.c new file +7 -0
+static struct pci_device_id phantom_pci_tbl[] __devinitdata = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
+		.class = PCI_CLASS_BRIDGE_OTHER << 8, .class_mask = 0xffff00 },
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, phantom_pci_tbl);
+

drivers/misc/phantom.c line 388

Quoted 10 lines
drivers/misc/phantom.c new file +10 -0
+static struct pci_driver phantom_pci_driver = {
+	.name = "phantom",
+	.id_table = phantom_pci_tbl,
+	.probe = phantom_probe,
+	.remove = __devexit_p(phantom_remove),
+#ifdef CONFIG_PM
+	.suspend = phantom_suspend,
+	.resume = phantom_resume
+#endif
+};

thus making these ifdefs unnecessary.

Quoted 20 lines

index 0000000..d3ebbfa

include/linux/phantom.h new file +16 -0
@@ -0,0 +1,42 @@
+/*
+ *  Copyright (C) 2005-2007 Jiri Slaby <[email protected]>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ */
+
+#ifndef __PHANTOM_H
+#define __PHANTOM_H
+
+#include <asm/types.h>
+
+/* PHN_(G/S)ET_REG param */
+struct phm_reg {

include/linux/phantom.h line 26

Quoted 10 lines
include/linux/phantom.h new file +10 -0
+	__u32 reg;
+	__u32 value;
+};
+
+/* PHN_(G/S)ET_REGS param */
+struct phm_regs {
+	__u32 count;
+	__u32 mask;
+	__u32 values[8];
+};

No, I guess it doesn't need compat handling.

Quoted 5 lines
include/linux/phantom.h new file +5 -0
+#define PH_IOC_MAGIC		'p'
+#define PHN_GET_REG		_IOWR(PH_IOC_MAGIC, 0, struct phm_reg *)
+#define PHN_SET_REG		_IOW (PH_IOC_MAGIC, 1, struct phm_reg *)
+#define PHN_GET_REGS		_IOWR(PH_IOC_MAGIC, 2, struct phm_regs *)
+#define PHN_SET_REGS		_IOW (PH_IOC_MAGIC, 3, struct phm_regs *)

include/linux/phantom.h line 42

Quoted 10 lines
include/linux/phantom.h new file +10 -0
+#define PH_IOC_MAXNR		3
+
+#define PHN_CONTROL		0x6     /* control byte in iaddr space */
+#define PHN_CTL_AMP		0x1     /*   switch after torques change */
+#define PHN_CTL_BUT		0x2     /*   is button switched */
+#define PHN_CTL_IRQ		0x10    /*   is irq enabled */
+
+#define PHN_ZERO_FORCE		2048	/* zero torque on motor */
+
+#endif

Exported to userspace? If so, it'll need a mention in include/linux/Kbuild?

author
#2 lore raw

Andrew Morton napsal(a):

Quoted 8 lines · On Wed, 2 May 2007 17:56:01 +0200 (CEST) Jiri Slaby <[email protected]>…
Quoted 2 lines

add sensable phantom driver

<googles>

Is that one of these?
http://www.est-kl.com/aufbau_general/index_hard_haptic.html?http://www.est-kl.com/hardware/haptic/sensable/phantom_omni.html

Omni is a small ieee1394 device. This is much bigger phantom premium pci version:
http://www.sensable.com/haptic-phantom-premium-6dof.htm

Quoted 2 lines

I don't think we've ever had a driver for a device like that before. I wonder how you designed the userspace interface to it.

I used the userspace libs that worked on 2.4 linux and rewrote them a little bit. However this is the old way, obsoleted I think, but it's still used in our human-computer interaction lab.

The other one is through supported OpenHaptics suite, which was added in this version -- that is the original Sensable (the device manufacturer) product, but they provide 2.4 linux drivers only with it.

Quoted 5 lines

drivers/misc/phantom.c line 73

Quoted 2 lines
drivers/misc/phantom.c new file +2 -0
+static int phantom_ioctl(struct inode *inode, struct file *file, u_int cmd,
+	u_long arg)

Could we please use standard-looking types in this driver? u32 and u64 and things?

Huh, wonders how this got there, thanks!

Quoted 16 lines

However in this case, as it's an ioctl handler it should be "int" and "unsigned long".

Does this driver require compat handling? (Seems not?)

Quoted 10 lines
drivers/misc/phantom.c new file +10 -0
+static int phantom_release(struct inode *inode, struct file *file)
+{
+	struct phantom_device *dev = file->private_data;
+
+	if (mutex_lock_interruptible(&dev->open_lock))
+		return -ERESTARTSYS;
+
+	dev->opened = 0;
+	phantom_status(dev, dev->status & ~PHB_RUNNING);
+
Quoted 8 lines

drivers/misc/phantom.c line 162

Quoted 4 lines
drivers/misc/phantom.c new file +4 -0
+	mutex_unlock(&dev->open_lock);
+
+	return 0;
+}

The mutex_lock_interruptible() is wrong, I think. This function is called when we do the final close(). If the signal _is_ taken then it's game
over: the device can no longer be opened.

Yes, starving at sys_close, I think so.

<fast searching in drivers/*>

Wouldn't this be a problem here too:
drivers/media/dvb/cinergyT2/cinergyT2.c drivers/usb/misc/auerswald.c
at least that the device won't be stopped or something like that?

Quoted 10 lines
Quoted 10 lines
drivers/misc/phantom.c new file +10 -0
+/*
+ * Init and deinit driver
+ */
+
+static unsigned int __devinit phantom_get_free(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < PHANTOM_MAX_MINORS; i++)
+		if (phantom_devices[i] == 0)
Quoted 8 lines

drivers/misc/phantom.c line 232

Quoted 4 lines
drivers/misc/phantom.c new file +4 -0
+			break;
+
+	return i;
+}

This function assumes that the phantom_devices[] array will never have any holes in it. But if phantom_remove() is called out-of-order, it _will_ have holes. Perhaps - I didn't look too hard.

I'm afraid I didn't get this. It goes through all phantom_devices and returns first free index. If a hole is present it will return "i" which corresponds to the hole, because phantom_devices[i] is 0. Note that it is unsigned char array.

thanks for the clues,

Signature · 4 lines
http://www.fi.muni.cz/~xslaby/            Jiri Slaby
faculty of informatics, masaryk university, brno, cz
e-mail: jirislaby gmail com, gpg pubkey fingerprint:
 B674 9967 0407 CE62 ACC8  22A0 32CC 55C3 39D4 7A7E
#3 lore raw
Quoted 19 lines · On Thu, 03 May 2007 14:40:01 +0200 Jiri Slaby <[email protected]> wrote:
Quoted 10 lines
Quoted 10 lines
drivers/misc/phantom.c new file +10 -0
+static int phantom_release(struct inode *inode, struct file *file)
+{
+	struct phantom_device *dev = file->private_data;
+
+	if (mutex_lock_interruptible(&dev->open_lock))
+		return -ERESTARTSYS;
+
+	dev->opened = 0;
+	phantom_status(dev, dev->status & ~PHB_RUNNING);
+
Quoted 8 lines

drivers/misc/phantom.c line 162

Quoted 4 lines
drivers/misc/phantom.c new file +4 -0
+	mutex_unlock(&dev->open_lock);
+
+	return 0;
+}

The mutex_lock_interruptible() is wrong, I think. This function is called when we do the final close(). If the signal _is_ taken then it's game
over: the device can no longer be opened.

Quoted 8 lines

Yes, starving at sys_close, I think so.

<fast searching in drivers/*>

Wouldn't this be a problem here too:
drivers/media/dvb/cinergyT2/cinergyT2.c drivers/usb/misc/auerswald.c
at least that the device won't be stopped or something like that?

Yes, those drivers appear to have the same problem. I'll fix 'em.

Quoted 19 lines
Quoted 10 lines
Quoted 10 lines
drivers/misc/phantom.c new file +10 -0
+/*
+ * Init and deinit driver
+ */
+
+static unsigned int __devinit phantom_get_free(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < PHANTOM_MAX_MINORS; i++)
+		if (phantom_devices[i] == 0)
Quoted 8 lines

drivers/misc/phantom.c line 232

Quoted 4 lines
drivers/misc/phantom.c new file +4 -0
+			break;
+
+	return i;
+}

This function assumes that the phantom_devices[] array will never have any holes in it. But if phantom_remove() is called out-of-order, it _will_ have holes. Perhaps - I didn't look too hard.

Quoted 5 lines

I'm afraid I didn't get this. It goes through all phantom_devices and returns first free index. If a hole is present it will return "i" which corresponds to the hole, because phantom_devices[i] is 0. Note that it is unsigned char array.

I guess I must have misread the code.

author
#4 lore raw
In reply to Andrew Morton

Andrew Morton napsal(a):

Quoted 8 lines

drivers/misc/phantom.c line 73

Quoted 2 lines
drivers/misc/phantom.c new file +2 -0
+static int phantom_ioctl(struct inode *inode, struct file *file, u_int cmd,
+	u_long arg)

Could we please use standard-looking types in this driver? u32 and u64 and things?

However in this case, as it's an ioctl handler it should be "int" and "unsigned long".

actually, according to file_operations struct:

int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

"*unsigned* int" and "unsigned long" are correct types...

regards,

Signature · 4 lines
http://www.fi.muni.cz/~xslaby/            Jiri Slaby
faculty of informatics, masaryk university, brno, cz
e-mail: jirislaby gmail com, gpg pubkey fingerprint:
 B674 9967 0407 CE62 ACC8  22A0 32CC 55C3 39D4 7A7E