Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Misc: add sensable phantom driver

Add sensable phantom driver

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>

authored by

Jiri Slaby and committed by
Linus Torvalds
cef2cf07 6f7f02e7

+523 -1
+2 -1
Documentation/ioctl-number.txt
··· 138 138 'm' 00-1F net/irda/irmod.h conflict! 139 139 'n' 00-7F linux/ncp_fs.h 140 140 'n' E0-FF video/matrox.h matroxfb 141 - 'p' 00-3F linux/mc146818rtc.h 141 + 'p' 00-0F linux/phantom.h conflict! (OpenHaptics needs this) 142 + 'p' 00-3F linux/mc146818rtc.h conflict! 142 143 'p' 40-7F linux/nvram.h 143 144 'p' 80-9F user-space parport 144 145 <mailto:tim@cyberelk.net>
+5
MAINTAINERS
··· 3108 3108 W: http://www.nsa.gov/selinux 3109 3109 S: Supported 3110 3110 3111 + SENSABLE PHANTOM 3112 + P: Jiri Slaby 3113 + M: jirislaby@gmail.com 3114 + S: Maintained 3115 + 3111 3116 SERIAL ATA (SATA) SUBSYSTEM: 3112 3117 P: Jeff Garzik 3113 3118 M: jgarzik@pobox.com
+9
drivers/misc/Kconfig
··· 25 25 information on the specific driver level and support statement 26 26 for your IBM server. 27 27 28 + config PHANTOM 29 + tristate "Sensable PHANToM" 30 + depends on PCI 31 + help 32 + Say Y here if you want to build a driver for Sensable PHANToM device. 33 + 34 + If you choose to build module, its name will be phantom. If unsure, 35 + say N here. 36 + 28 37 29 38 If unsure, say N. 30 39
+1
drivers/misc/Makefile
··· 11 11 obj-$(CONFIG_LKDTM) += lkdtm.o 12 12 obj-$(CONFIG_TIFM_CORE) += tifm_core.o 13 13 obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o 14 + obj-$(CONFIG_PHANTOM) += phantom.o 14 15 obj-$(CONFIG_SGI_IOC4) += ioc4.o 15 16 obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o 16 17 obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
+463
drivers/misc/phantom.c
··· 1 + /* 2 + * Copyright (C) 2005-2007 Jiri Slaby <jirislaby@gmail.com> 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + * 9 + * You need an userspace library to cooperate with this driver. It (and other 10 + * info) may be obtained here: 11 + * http://www.fi.muni.cz/~xslaby/phantom.html 12 + */ 13 + 14 + #include <linux/kernel.h> 15 + #include <linux/module.h> 16 + #include <linux/device.h> 17 + #include <linux/pci.h> 18 + #include <linux/fs.h> 19 + #include <linux/poll.h> 20 + #include <linux/interrupt.h> 21 + #include <linux/cdev.h> 22 + #include <linux/phantom.h> 23 + 24 + #include <asm/atomic.h> 25 + #include <asm/io.h> 26 + 27 + #define PHANTOM_VERSION "n0.9.5" 28 + 29 + #define PHANTOM_MAX_MINORS 8 30 + 31 + #define PHN_IRQCTL 0x4c /* irq control in caddr space */ 32 + 33 + #define PHB_RUNNING 1 34 + 35 + static struct class *phantom_class; 36 + static int phantom_major; 37 + 38 + struct phantom_device { 39 + unsigned int opened; 40 + void __iomem *caddr; 41 + u32 __iomem *iaddr; 42 + u32 __iomem *oaddr; 43 + unsigned long status; 44 + atomic_t counter; 45 + 46 + wait_queue_head_t wait; 47 + struct cdev cdev; 48 + 49 + struct mutex open_lock; 50 + }; 51 + 52 + static unsigned char phantom_devices[PHANTOM_MAX_MINORS]; 53 + 54 + static int phantom_status(struct phantom_device *dev, unsigned long newstat) 55 + { 56 + pr_debug("phantom_status %lx %lx\n", dev->status, newstat); 57 + 58 + if (!(dev->status & PHB_RUNNING) && (newstat & PHB_RUNNING)) { 59 + atomic_set(&dev->counter, 0); 60 + iowrite32(PHN_CTL_IRQ, dev->iaddr + PHN_CONTROL); 61 + iowrite32(0x43, dev->caddr + PHN_IRQCTL); 62 + } else if ((dev->status & PHB_RUNNING) && !(newstat & PHB_RUNNING)) 63 + iowrite32(0, dev->caddr + PHN_IRQCTL); 64 + 65 + dev->status = newstat; 66 + 67 + return 0; 68 + } 69 + 70 + /* 71 + * File ops 72 + */ 73 + 74 + static int phantom_ioctl(struct inode *inode, struct file *file, u_int cmd, 75 + u_long arg) 76 + { 77 + struct phantom_device *dev = file->private_data; 78 + struct phm_regs rs; 79 + struct phm_reg r; 80 + void __user *argp = (void __user *)arg; 81 + unsigned int i; 82 + 83 + if (_IOC_TYPE(cmd) != PH_IOC_MAGIC || 84 + _IOC_NR(cmd) > PH_IOC_MAXNR) 85 + return -ENOTTY; 86 + 87 + switch (cmd) { 88 + case PHN_SET_REG: 89 + if (copy_from_user(&r, argp, sizeof(r))) 90 + return -EFAULT; 91 + 92 + if (r.reg > 7) 93 + return -EINVAL; 94 + 95 + if (r.reg == PHN_CONTROL && (r.value & PHN_CTL_IRQ) && 96 + phantom_status(dev, dev->status | PHB_RUNNING)) 97 + return -ENODEV; 98 + 99 + pr_debug("phantom: writing %x to %u\n", r.value, r.reg); 100 + iowrite32(r.value, dev->iaddr + r.reg); 101 + 102 + if (r.reg == PHN_CONTROL && !(r.value & PHN_CTL_IRQ)) 103 + phantom_status(dev, dev->status & ~PHB_RUNNING); 104 + break; 105 + case PHN_SET_REGS: 106 + if (copy_from_user(&rs, argp, sizeof(rs))) 107 + return -EFAULT; 108 + 109 + pr_debug("phantom: SRS %u regs %x\n", rs.count, rs.mask); 110 + for (i = 0; i < min(rs.count, 8U); i++) 111 + if ((1 << i) & rs.mask) 112 + iowrite32(rs.values[i], dev->oaddr + i); 113 + break; 114 + case PHN_GET_REG: 115 + if (copy_from_user(&r, argp, sizeof(r))) 116 + return -EFAULT; 117 + 118 + if (r.reg > 7) 119 + return -EINVAL; 120 + 121 + r.value = ioread32(dev->iaddr + r.reg); 122 + 123 + if (copy_to_user(argp, &r, sizeof(r))) 124 + return -EFAULT; 125 + break; 126 + case PHN_GET_REGS: 127 + if (copy_from_user(&rs, argp, sizeof(rs))) 128 + return -EFAULT; 129 + 130 + pr_debug("phantom: GRS %u regs %x\n", rs.count, rs.mask); 131 + for (i = 0; i < min(rs.count, 8U); i++) 132 + if ((1 << i) & rs.mask) 133 + rs.values[i] = ioread32(dev->iaddr + i); 134 + 135 + if (copy_to_user(argp, &rs, sizeof(rs))) 136 + return -EFAULT; 137 + break; 138 + default: 139 + return -ENOTTY; 140 + } 141 + 142 + return 0; 143 + } 144 + 145 + static int phantom_open(struct inode *inode, struct file *file) 146 + { 147 + struct phantom_device *dev = container_of(inode->i_cdev, 148 + struct phantom_device, cdev); 149 + 150 + nonseekable_open(inode, file); 151 + 152 + if (mutex_lock_interruptible(&dev->open_lock)) 153 + return -ERESTARTSYS; 154 + 155 + if (dev->opened) { 156 + mutex_unlock(&dev->open_lock); 157 + return -EINVAL; 158 + } 159 + 160 + file->private_data = dev; 161 + 162 + dev->opened++; 163 + mutex_unlock(&dev->open_lock); 164 + 165 + return 0; 166 + } 167 + 168 + static int phantom_release(struct inode *inode, struct file *file) 169 + { 170 + struct phantom_device *dev = file->private_data; 171 + 172 + mutex_lock(&dev->open_lock); 173 + 174 + dev->opened = 0; 175 + phantom_status(dev, dev->status & ~PHB_RUNNING); 176 + 177 + mutex_unlock(&dev->open_lock); 178 + 179 + return 0; 180 + } 181 + 182 + static unsigned int phantom_poll(struct file *file, poll_table *wait) 183 + { 184 + struct phantom_device *dev = file->private_data; 185 + unsigned int mask = 0; 186 + 187 + pr_debug("phantom_poll: %d\n", atomic_read(&dev->counter)); 188 + poll_wait(file, &dev->wait, wait); 189 + if (atomic_read(&dev->counter)) { 190 + mask = POLLIN | POLLRDNORM; 191 + atomic_dec(&dev->counter); 192 + } else if ((dev->status & PHB_RUNNING) == 0) 193 + mask = POLLIN | POLLRDNORM | POLLERR; 194 + pr_debug("phantom_poll end: %x/%d\n", mask, atomic_read(&dev->counter)); 195 + 196 + return mask; 197 + } 198 + 199 + static struct file_operations phantom_file_ops = { 200 + .open = phantom_open, 201 + .release = phantom_release, 202 + .ioctl = phantom_ioctl, 203 + .poll = phantom_poll, 204 + }; 205 + 206 + static irqreturn_t phantom_isr(int irq, void *data) 207 + { 208 + struct phantom_device *dev = data; 209 + 210 + if (!(ioread32(dev->iaddr + PHN_CONTROL) & PHN_CTL_IRQ)) 211 + return IRQ_NONE; 212 + 213 + iowrite32(0, dev->iaddr); 214 + iowrite32(0xc0, dev->iaddr); 215 + 216 + atomic_inc(&dev->counter); 217 + wake_up_interruptible(&dev->wait); 218 + 219 + return IRQ_HANDLED; 220 + } 221 + 222 + /* 223 + * Init and deinit driver 224 + */ 225 + 226 + static unsigned int __devinit phantom_get_free(void) 227 + { 228 + unsigned int i; 229 + 230 + for (i = 0; i < PHANTOM_MAX_MINORS; i++) 231 + if (phantom_devices[i] == 0) 232 + break; 233 + 234 + return i; 235 + } 236 + 237 + static int __devinit phantom_probe(struct pci_dev *pdev, 238 + const struct pci_device_id *pci_id) 239 + { 240 + struct phantom_device *pht; 241 + unsigned int minor; 242 + int retval; 243 + 244 + retval = pci_enable_device(pdev); 245 + if (retval) 246 + goto err; 247 + 248 + minor = phantom_get_free(); 249 + if (minor == PHANTOM_MAX_MINORS) { 250 + dev_err(&pdev->dev, "too many devices found!\n"); 251 + retval = -EIO; 252 + goto err_dis; 253 + } 254 + 255 + phantom_devices[minor] = 1; 256 + 257 + retval = pci_request_regions(pdev, "phantom"); 258 + if (retval) 259 + goto err_null; 260 + 261 + retval = -ENOMEM; 262 + pht = kzalloc(sizeof(*pht), GFP_KERNEL); 263 + if (pht == NULL) { 264 + dev_err(&pdev->dev, "unable to allocate device\n"); 265 + goto err_reg; 266 + } 267 + 268 + pht->caddr = pci_iomap(pdev, 0, 0); 269 + if (pht->caddr == NULL) { 270 + dev_err(&pdev->dev, "can't remap conf space\n"); 271 + goto err_fr; 272 + } 273 + pht->iaddr = pci_iomap(pdev, 2, 0); 274 + if (pht->iaddr == NULL) { 275 + dev_err(&pdev->dev, "can't remap input space\n"); 276 + goto err_unmc; 277 + } 278 + pht->oaddr = pci_iomap(pdev, 3, 0); 279 + if (pht->oaddr == NULL) { 280 + dev_err(&pdev->dev, "can't remap output space\n"); 281 + goto err_unmi; 282 + } 283 + 284 + mutex_init(&pht->open_lock); 285 + init_waitqueue_head(&pht->wait); 286 + cdev_init(&pht->cdev, &phantom_file_ops); 287 + pht->cdev.owner = THIS_MODULE; 288 + 289 + iowrite32(0, pht->caddr + PHN_IRQCTL); 290 + retval = request_irq(pdev->irq, phantom_isr, 291 + IRQF_SHARED | IRQF_DISABLED, "phantom", pht); 292 + if (retval) { 293 + dev_err(&pdev->dev, "can't establish ISR\n"); 294 + goto err_unmo; 295 + } 296 + 297 + retval = cdev_add(&pht->cdev, MKDEV(phantom_major, minor), 1); 298 + if (retval) { 299 + dev_err(&pdev->dev, "chardev registration failed\n"); 300 + goto err_irq; 301 + } 302 + 303 + if (IS_ERR(device_create(phantom_class, &pdev->dev, MKDEV(phantom_major, 304 + minor), "phantom%u", minor))) 305 + dev_err(&pdev->dev, "can't create device\n"); 306 + 307 + pci_set_drvdata(pdev, pht); 308 + 309 + return 0; 310 + err_irq: 311 + free_irq(pdev->irq, pht); 312 + err_unmo: 313 + pci_iounmap(pdev, pht->oaddr); 314 + err_unmi: 315 + pci_iounmap(pdev, pht->iaddr); 316 + err_unmc: 317 + pci_iounmap(pdev, pht->caddr); 318 + err_fr: 319 + kfree(pht); 320 + err_reg: 321 + pci_release_regions(pdev); 322 + err_null: 323 + phantom_devices[minor] = 0; 324 + err_dis: 325 + pci_disable_device(pdev); 326 + err: 327 + return retval; 328 + } 329 + 330 + static void __devexit phantom_remove(struct pci_dev *pdev) 331 + { 332 + struct phantom_device *pht = pci_get_drvdata(pdev); 333 + unsigned int minor = MINOR(pht->cdev.dev); 334 + 335 + device_destroy(phantom_class, MKDEV(phantom_major, minor)); 336 + 337 + cdev_del(&pht->cdev); 338 + 339 + iowrite32(0, pht->caddr + PHN_IRQCTL); 340 + free_irq(pdev->irq, pht); 341 + 342 + pci_iounmap(pdev, pht->oaddr); 343 + pci_iounmap(pdev, pht->iaddr); 344 + pci_iounmap(pdev, pht->caddr); 345 + 346 + kfree(pht); 347 + 348 + pci_release_regions(pdev); 349 + 350 + phantom_devices[minor] = 0; 351 + 352 + pci_disable_device(pdev); 353 + } 354 + 355 + #ifdef CONFIG_PM 356 + static int phantom_suspend(struct pci_dev *pdev, pm_message_t state) 357 + { 358 + struct phantom_device *dev = pci_get_drvdata(pdev); 359 + 360 + iowrite32(0, dev->caddr + PHN_IRQCTL); 361 + 362 + return 0; 363 + } 364 + 365 + static int phantom_resume(struct pci_dev *pdev) 366 + { 367 + struct phantom_device *dev = pci_get_drvdata(pdev); 368 + 369 + iowrite32(0, dev->caddr + PHN_IRQCTL); 370 + 371 + return 0; 372 + } 373 + #else 374 + #define phantom_suspend NULL 375 + #define phantom_resume NULL 376 + #endif 377 + 378 + static struct pci_device_id phantom_pci_tbl[] __devinitdata = { 379 + { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050), 380 + .class = PCI_CLASS_BRIDGE_OTHER << 8, .class_mask = 0xffff00 }, 381 + { 0, } 382 + }; 383 + MODULE_DEVICE_TABLE(pci, phantom_pci_tbl); 384 + 385 + static struct pci_driver phantom_pci_driver = { 386 + .name = "phantom", 387 + .id_table = phantom_pci_tbl, 388 + .probe = phantom_probe, 389 + .remove = __devexit_p(phantom_remove), 390 + .suspend = phantom_suspend, 391 + .resume = phantom_resume 392 + }; 393 + 394 + static ssize_t phantom_show_version(struct class *cls, char *buf) 395 + { 396 + return sprintf(buf, PHANTOM_VERSION "\n"); 397 + } 398 + 399 + static CLASS_ATTR(version, 0444, phantom_show_version, NULL); 400 + 401 + static int __init phantom_init(void) 402 + { 403 + int retval; 404 + dev_t dev; 405 + 406 + phantom_class = class_create(THIS_MODULE, "phantom"); 407 + if (IS_ERR(phantom_class)) { 408 + retval = PTR_ERR(phantom_class); 409 + printk(KERN_ERR "phantom: can't register phantom class\n"); 410 + goto err; 411 + } 412 + retval = class_create_file(phantom_class, &class_attr_version); 413 + if (retval) { 414 + printk(KERN_ERR "phantom: can't create sysfs version file\n"); 415 + goto err_class; 416 + } 417 + 418 + retval = alloc_chrdev_region(&dev, 0, PHANTOM_MAX_MINORS, "phantom"); 419 + if (retval) { 420 + printk(KERN_ERR "phantom: can't register character device\n"); 421 + goto err_attr; 422 + } 423 + phantom_major = MAJOR(dev); 424 + 425 + retval = pci_register_driver(&phantom_pci_driver); 426 + if (retval) { 427 + printk(KERN_ERR "phantom: can't register pci driver\n"); 428 + goto err_unchr; 429 + } 430 + 431 + printk(KERN_INFO "Phantom Linux Driver, version " PHANTOM_VERSION ", " 432 + "init OK\n"); 433 + 434 + return 0; 435 + err_unchr: 436 + unregister_chrdev_region(dev, PHANTOM_MAX_MINORS); 437 + err_attr: 438 + class_remove_file(phantom_class, &class_attr_version); 439 + err_class: 440 + class_destroy(phantom_class); 441 + err: 442 + return retval; 443 + } 444 + 445 + static void __exit phantom_exit(void) 446 + { 447 + pci_unregister_driver(&phantom_pci_driver); 448 + 449 + unregister_chrdev_region(MKDEV(phantom_major, 0), PHANTOM_MAX_MINORS); 450 + 451 + class_remove_file(phantom_class, &class_attr_version); 452 + class_destroy(phantom_class); 453 + 454 + pr_debug("phantom: module successfully removed\n"); 455 + } 456 + 457 + module_init(phantom_init); 458 + module_exit(phantom_exit); 459 + 460 + MODULE_AUTHOR("Jiri Slaby <jirislaby@gmail.com>"); 461 + MODULE_DESCRIPTION("Sensable Phantom driver"); 462 + MODULE_LICENSE("GPL"); 463 + MODULE_VERSION(PHANTOM_VERSION);
+1
include/linux/Kbuild
··· 121 121 header-y += personality.h 122 122 header-y += pfkeyv2.h 123 123 header-y += pg.h 124 + header-y += phantom.h 124 125 header-y += pkt_cls.h 125 126 header-y += pkt_sched.h 126 127 header-y += posix_types.h
+42
include/linux/phantom.h
··· 1 + /* 2 + * Copyright (C) 2005-2007 Jiri Slaby <jirislaby@gmail.com> 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + */ 9 + 10 + #ifndef __PHANTOM_H 11 + #define __PHANTOM_H 12 + 13 + #include <asm/types.h> 14 + 15 + /* PHN_(G/S)ET_REG param */ 16 + struct phm_reg { 17 + __u32 reg; 18 + __u32 value; 19 + }; 20 + 21 + /* PHN_(G/S)ET_REGS param */ 22 + struct phm_regs { 23 + __u32 count; 24 + __u32 mask; 25 + __u32 values[8]; 26 + }; 27 + 28 + #define PH_IOC_MAGIC 'p' 29 + #define PHN_GET_REG _IOWR(PH_IOC_MAGIC, 0, 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 *) 32 + #define PHN_SET_REGS _IOW (PH_IOC_MAGIC, 3, struct phm_regs *) 33 + #define PH_IOC_MAXNR 3 34 + 35 + #define PHN_CONTROL 0x6 /* control byte in iaddr space */ 36 + #define PHN_CTL_AMP 0x1 /* switch after torques change */ 37 + #define PHN_CTL_BUT 0x2 /* is button switched */ 38 + #define PHN_CTL_IRQ 0x10 /* is irq enabled */ 39 + 40 + #define PHN_ZERO_FORCE 2048 /* zero torque on motor */ 41 + 42 + #endif