linux/drivers/misc/phantom.c
<<
>>
Prefs
   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 a 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 *  or alternatively, you might use OpenHaptics provided by Sensable.
  13 */
  14
  15#include <linux/compat.h>
  16#include <linux/kernel.h>
  17#include <linux/module.h>
  18#include <linux/device.h>
  19#include <linux/pci.h>
  20#include <linux/fs.h>
  21#include <linux/poll.h>
  22#include <linux/interrupt.h>
  23#include <linux/cdev.h>
  24#include <linux/slab.h>
  25#include <linux/phantom.h>
  26#include <linux/sched.h>
  27#include <linux/mutex.h>
  28
  29#include <linux/atomic.h>
  30#include <asm/io.h>
  31
  32#define PHANTOM_VERSION         "n0.9.8"
  33
  34#define PHANTOM_MAX_MINORS      8
  35
  36#define PHN_IRQCTL              0x4c    /* irq control in caddr space */
  37
  38#define PHB_RUNNING             1
  39#define PHB_NOT_OH              2
  40
  41static DEFINE_MUTEX(phantom_mutex);
  42static struct class *phantom_class;
  43static int phantom_major;
  44
  45struct phantom_device {
  46        unsigned int opened;
  47        void __iomem *caddr;
  48        u32 __iomem *iaddr;
  49        u32 __iomem *oaddr;
  50        unsigned long status;
  51        atomic_t counter;
  52
  53        wait_queue_head_t wait;
  54        struct cdev cdev;
  55
  56        struct mutex open_lock;
  57        spinlock_t regs_lock;
  58
  59        /* used in NOT_OH mode */
  60        struct phm_regs oregs;
  61        u32 ctl_reg;
  62};
  63
  64static unsigned char phantom_devices[PHANTOM_MAX_MINORS];
  65
  66static int phantom_status(struct phantom_device *dev, unsigned long newstat)
  67{
  68        pr_debug("phantom_status %lx %lx\n", dev->status, newstat);
  69
  70        if (!(dev->status & PHB_RUNNING) && (newstat & PHB_RUNNING)) {
  71                atomic_set(&dev->counter, 0);
  72                iowrite32(PHN_CTL_IRQ, dev->iaddr + PHN_CONTROL);
  73                iowrite32(0x43, dev->caddr + PHN_IRQCTL);
  74                ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */
  75        } else if ((dev->status & PHB_RUNNING) && !(newstat & PHB_RUNNING)) {
  76                iowrite32(0, dev->caddr + PHN_IRQCTL);
  77                ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */
  78        }
  79
  80        dev->status = newstat;
  81
  82        return 0;
  83}
  84
  85/*
  86 * File ops
  87 */
  88
  89static long phantom_ioctl(struct file *file, unsigned int cmd,
  90                unsigned long arg)
  91{
  92        struct phantom_device *dev = file->private_data;
  93        struct phm_regs rs;
  94        struct phm_reg r;
  95        void __user *argp = (void __user *)arg;
  96        unsigned long flags;
  97        unsigned int i;
  98
  99        switch (cmd) {
 100        case PHN_SETREG:
 101        case PHN_SET_REG:
 102                if (copy_from_user(&r, argp, sizeof(r)))
 103                        return -EFAULT;
 104
 105                if (r.reg > 7)
 106                        return -EINVAL;
 107
 108                spin_lock_irqsave(&dev->regs_lock, flags);
 109                if (r.reg == PHN_CONTROL && (r.value & PHN_CTL_IRQ) &&
 110                                phantom_status(dev, dev->status | PHB_RUNNING)){
 111                        spin_unlock_irqrestore(&dev->regs_lock, flags);
 112                        return -ENODEV;
 113                }
 114
 115                pr_debug("phantom: writing %x to %u\n", r.value, r.reg);
 116
 117                /* preserve amp bit (don't allow to change it when in NOT_OH) */
 118                if (r.reg == PHN_CONTROL && (dev->status & PHB_NOT_OH)) {
 119                        r.value &= ~PHN_CTL_AMP;
 120                        r.value |= dev->ctl_reg & PHN_CTL_AMP;
 121                        dev->ctl_reg = r.value;
 122                }
 123
 124                iowrite32(r.value, dev->iaddr + r.reg);
 125                ioread32(dev->iaddr); /* PCI posting */
 126
 127                if (r.reg == PHN_CONTROL && !(r.value & PHN_CTL_IRQ))
 128                        phantom_status(dev, dev->status & ~PHB_RUNNING);
 129                spin_unlock_irqrestore(&dev->regs_lock, flags);
 130                break;
 131        case PHN_SETREGS:
 132        case PHN_SET_REGS:
 133                if (copy_from_user(&rs, argp, sizeof(rs)))
 134                        return -EFAULT;
 135
 136                pr_debug("phantom: SRS %u regs %x\n", rs.count, rs.mask);
 137                spin_lock_irqsave(&dev->regs_lock, flags);
 138                if (dev->status & PHB_NOT_OH)
 139                        memcpy(&dev->oregs, &rs, sizeof(rs));
 140                else {
 141                        u32 m = min(rs.count, 8U);
 142                        for (i = 0; i < m; i++)
 143                                if (rs.mask & BIT(i))
 144                                        iowrite32(rs.values[i], dev->oaddr + i);
 145                        ioread32(dev->iaddr); /* PCI posting */
 146                }
 147                spin_unlock_irqrestore(&dev->regs_lock, flags);
 148                break;
 149        case PHN_GETREG:
 150        case PHN_GET_REG:
 151                if (copy_from_user(&r, argp, sizeof(r)))
 152                        return -EFAULT;
 153
 154                if (r.reg > 7)
 155                        return -EINVAL;
 156
 157                r.value = ioread32(dev->iaddr + r.reg);
 158
 159                if (copy_to_user(argp, &r, sizeof(r)))
 160                        return -EFAULT;
 161                break;
 162        case PHN_GETREGS:
 163        case PHN_GET_REGS: {
 164                u32 m;
 165
 166                if (copy_from_user(&rs, argp, sizeof(rs)))
 167                        return -EFAULT;
 168
 169                m = min(rs.count, 8U);
 170
 171                pr_debug("phantom: GRS %u regs %x\n", rs.count, rs.mask);
 172                spin_lock_irqsave(&dev->regs_lock, flags);
 173                for (i = 0; i < m; i++)
 174                        if (rs.mask & BIT(i))
 175                                rs.values[i] = ioread32(dev->iaddr + i);
 176                atomic_set(&dev->counter, 0);
 177                spin_unlock_irqrestore(&dev->regs_lock, flags);
 178
 179                if (copy_to_user(argp, &rs, sizeof(rs)))
 180                        return -EFAULT;
 181                break;
 182        } case PHN_NOT_OH:
 183                spin_lock_irqsave(&dev->regs_lock, flags);
 184                if (dev->status & PHB_RUNNING) {
 185                        printk(KERN_ERR "phantom: you need to set NOT_OH "
 186                                        "before you start the device!\n");
 187                        spin_unlock_irqrestore(&dev->regs_lock, flags);
 188                        return -EINVAL;
 189                }
 190                dev->status |= PHB_NOT_OH;
 191                spin_unlock_irqrestore(&dev->regs_lock, flags);
 192                break;
 193        default:
 194                return -ENOTTY;
 195        }
 196
 197        return 0;
 198}
 199
 200#ifdef CONFIG_COMPAT
 201static long phantom_compat_ioctl(struct file *filp, unsigned int cmd,
 202                unsigned long arg)
 203{
 204        if (_IOC_NR(cmd) <= 3 && _IOC_SIZE(cmd) == sizeof(compat_uptr_t)) {
 205                cmd &= ~(_IOC_SIZEMASK << _IOC_SIZESHIFT);
 206                cmd |= sizeof(void *) << _IOC_SIZESHIFT;
 207        }
 208        return phantom_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
 209}
 210#else
 211#define phantom_compat_ioctl NULL
 212#endif
 213
 214static int phantom_open(struct inode *inode, struct file *file)
 215{
 216        struct phantom_device *dev = container_of(inode->i_cdev,
 217                        struct phantom_device, cdev);
 218
 219        mutex_lock(&phantom_mutex);
 220        nonseekable_open(inode, file);
 221
 222        if (mutex_lock_interruptible(&dev->open_lock)) {
 223                mutex_unlock(&phantom_mutex);
 224                return -ERESTARTSYS;
 225        }
 226
 227        if (dev->opened) {
 228                mutex_unlock(&dev->open_lock);
 229                mutex_unlock(&phantom_mutex);
 230                return -EINVAL;
 231        }
 232
 233        WARN_ON(dev->status & PHB_NOT_OH);
 234
 235        file->private_data = dev;
 236
 237        atomic_set(&dev->counter, 0);
 238        dev->opened++;
 239        mutex_unlock(&dev->open_lock);
 240        mutex_unlock(&phantom_mutex);
 241        return 0;
 242}
 243
 244static int phantom_release(struct inode *inode, struct file *file)
 245{
 246        struct phantom_device *dev = file->private_data;
 247
 248        mutex_lock(&dev->open_lock);
 249
 250        dev->opened = 0;
 251        phantom_status(dev, dev->status & ~PHB_RUNNING);
 252        dev->status &= ~PHB_NOT_OH;
 253
 254        mutex_unlock(&dev->open_lock);
 255
 256        return 0;
 257}
 258
 259static unsigned int phantom_poll(struct file *file, poll_table *wait)
 260{
 261        struct phantom_device *dev = file->private_data;
 262        unsigned int mask = 0;
 263
 264        pr_debug("phantom_poll: %d\n", atomic_read(&dev->counter));
 265        poll_wait(file, &dev->wait, wait);
 266
 267        if (!(dev->status & PHB_RUNNING))
 268                mask = POLLERR;
 269        else if (atomic_read(&dev->counter))
 270                mask = POLLIN | POLLRDNORM;
 271
 272        pr_debug("phantom_poll end: %x/%d\n", mask, atomic_read(&dev->counter));
 273
 274        return mask;
 275}
 276
 277static const struct file_operations phantom_file_ops = {
 278        .open = phantom_open,
 279        .release = phantom_release,
 280        .unlocked_ioctl = phantom_ioctl,
 281        .compat_ioctl = phantom_compat_ioctl,
 282        .poll = phantom_poll,
 283        .llseek = no_llseek,
 284};
 285
 286static irqreturn_t phantom_isr(int irq, void *data)
 287{
 288        struct phantom_device *dev = data;
 289        unsigned int i;
 290        u32 ctl;
 291
 292        spin_lock(&dev->regs_lock);
 293        ctl = ioread32(dev->iaddr + PHN_CONTROL);
 294        if (!(ctl & PHN_CTL_IRQ)) {
 295                spin_unlock(&dev->regs_lock);
 296                return IRQ_NONE;
 297        }
 298
 299        iowrite32(0, dev->iaddr);
 300        iowrite32(0xc0, dev->iaddr);
 301
 302        if (dev->status & PHB_NOT_OH) {
 303                struct phm_regs *r = &dev->oregs;
 304                u32 m = min(r->count, 8U);
 305
 306                for (i = 0; i < m; i++)
 307                        if (r->mask & BIT(i))
 308                                iowrite32(r->values[i], dev->oaddr + i);
 309
 310                dev->ctl_reg ^= PHN_CTL_AMP;
 311                iowrite32(dev->ctl_reg, dev->iaddr + PHN_CONTROL);
 312        }
 313        spin_unlock(&dev->regs_lock);
 314
 315        ioread32(dev->iaddr); /* PCI posting */
 316
 317        atomic_inc(&dev->counter);
 318        wake_up_interruptible(&dev->wait);
 319
 320        return IRQ_HANDLED;
 321}
 322
 323/*
 324 * Init and deinit driver
 325 */
 326
 327static unsigned int phantom_get_free(void)
 328{
 329        unsigned int i;
 330
 331        for (i = 0; i < PHANTOM_MAX_MINORS; i++)
 332                if (phantom_devices[i] == 0)
 333                        break;
 334
 335        return i;
 336}
 337
 338static int phantom_probe(struct pci_dev *pdev,
 339        const struct pci_device_id *pci_id)
 340{
 341        struct phantom_device *pht;
 342        unsigned int minor;
 343        int retval;
 344
 345        retval = pci_enable_device(pdev);
 346        if (retval) {
 347                dev_err(&pdev->dev, "pci_enable_device failed!\n");
 348                goto err;
 349        }
 350
 351        minor = phantom_get_free();
 352        if (minor == PHANTOM_MAX_MINORS) {
 353                dev_err(&pdev->dev, "too many devices found!\n");
 354                retval = -EIO;
 355                goto err_dis;
 356        }
 357
 358        phantom_devices[minor] = 1;
 359
 360        retval = pci_request_regions(pdev, "phantom");
 361        if (retval) {
 362                dev_err(&pdev->dev, "pci_request_regions failed!\n");
 363                goto err_null;
 364        }
 365
 366        retval = -ENOMEM;
 367        pht = kzalloc(sizeof(*pht), GFP_KERNEL);
 368        if (pht == NULL) {
 369                dev_err(&pdev->dev, "unable to allocate device\n");
 370                goto err_reg;
 371        }
 372
 373        pht->caddr = pci_iomap(pdev, 0, 0);
 374        if (pht->caddr == NULL) {
 375                dev_err(&pdev->dev, "can't remap conf space\n");
 376                goto err_fr;
 377        }
 378        pht->iaddr = pci_iomap(pdev, 2, 0);
 379        if (pht->iaddr == NULL) {
 380                dev_err(&pdev->dev, "can't remap input space\n");
 381                goto err_unmc;
 382        }
 383        pht->oaddr = pci_iomap(pdev, 3, 0);
 384        if (pht->oaddr == NULL) {
 385                dev_err(&pdev->dev, "can't remap output space\n");
 386                goto err_unmi;
 387        }
 388
 389        mutex_init(&pht->open_lock);
 390        spin_lock_init(&pht->regs_lock);
 391        init_waitqueue_head(&pht->wait);
 392        cdev_init(&pht->cdev, &phantom_file_ops);
 393        pht->cdev.owner = THIS_MODULE;
 394
 395        iowrite32(0, pht->caddr + PHN_IRQCTL);
 396        ioread32(pht->caddr + PHN_IRQCTL); /* PCI posting */
 397        retval = request_irq(pdev->irq, phantom_isr,
 398                        IRQF_SHARED, "phantom", pht);
 399        if (retval) {
 400                dev_err(&pdev->dev, "can't establish ISR\n");
 401                goto err_unmo;
 402        }
 403
 404        retval = cdev_add(&pht->cdev, MKDEV(phantom_major, minor), 1);
 405        if (retval) {
 406                dev_err(&pdev->dev, "chardev registration failed\n");
 407                goto err_irq;
 408        }
 409
 410        if (IS_ERR(device_create(phantom_class, &pdev->dev,
 411                                 MKDEV(phantom_major, minor), NULL,
 412                                 "phantom%u", minor)))
 413                dev_err(&pdev->dev, "can't create device\n");
 414
 415        pci_set_drvdata(pdev, pht);
 416
 417        return 0;
 418err_irq:
 419        free_irq(pdev->irq, pht);
 420err_unmo:
 421        pci_iounmap(pdev, pht->oaddr);
 422err_unmi:
 423        pci_iounmap(pdev, pht->iaddr);
 424err_unmc:
 425        pci_iounmap(pdev, pht->caddr);
 426err_fr:
 427        kfree(pht);
 428err_reg:
 429        pci_release_regions(pdev);
 430err_null:
 431        phantom_devices[minor] = 0;
 432err_dis:
 433        pci_disable_device(pdev);
 434err:
 435        return retval;
 436}
 437
 438static void phantom_remove(struct pci_dev *pdev)
 439{
 440        struct phantom_device *pht = pci_get_drvdata(pdev);
 441        unsigned int minor = MINOR(pht->cdev.dev);
 442
 443        device_destroy(phantom_class, MKDEV(phantom_major, minor));
 444
 445        cdev_del(&pht->cdev);
 446
 447        iowrite32(0, pht->caddr + PHN_IRQCTL);
 448        ioread32(pht->caddr + PHN_IRQCTL); /* PCI posting */
 449        free_irq(pdev->irq, pht);
 450
 451        pci_iounmap(pdev, pht->oaddr);
 452        pci_iounmap(pdev, pht->iaddr);
 453        pci_iounmap(pdev, pht->caddr);
 454
 455        kfree(pht);
 456
 457        pci_release_regions(pdev);
 458
 459        phantom_devices[minor] = 0;
 460
 461        pci_disable_device(pdev);
 462}
 463
 464#ifdef CONFIG_PM
 465static int phantom_suspend(struct pci_dev *pdev, pm_message_t state)
 466{
 467        struct phantom_device *dev = pci_get_drvdata(pdev);
 468
 469        iowrite32(0, dev->caddr + PHN_IRQCTL);
 470        ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */
 471
 472        synchronize_irq(pdev->irq);
 473
 474        return 0;
 475}
 476
 477static int phantom_resume(struct pci_dev *pdev)
 478{
 479        struct phantom_device *dev = pci_get_drvdata(pdev);
 480
 481        iowrite32(0, dev->caddr + PHN_IRQCTL);
 482
 483        return 0;
 484}
 485#else
 486#define phantom_suspend NULL
 487#define phantom_resume  NULL
 488#endif
 489
 490static struct pci_device_id phantom_pci_tbl[] = {
 491        { .vendor = PCI_VENDOR_ID_PLX, .device = PCI_DEVICE_ID_PLX_9050,
 492          .subvendor = PCI_VENDOR_ID_PLX, .subdevice = PCI_DEVICE_ID_PLX_9050,
 493          .class = PCI_CLASS_BRIDGE_OTHER << 8, .class_mask = 0xffff00 },
 494        { 0, }
 495};
 496MODULE_DEVICE_TABLE(pci, phantom_pci_tbl);
 497
 498static struct pci_driver phantom_pci_driver = {
 499        .name = "phantom",
 500        .id_table = phantom_pci_tbl,
 501        .probe = phantom_probe,
 502        .remove = phantom_remove,
 503        .suspend = phantom_suspend,
 504        .resume = phantom_resume
 505};
 506
 507static CLASS_ATTR_STRING(version, 0444, PHANTOM_VERSION);
 508
 509static int __init phantom_init(void)
 510{
 511        int retval;
 512        dev_t dev;
 513
 514        phantom_class = class_create(THIS_MODULE, "phantom");
 515        if (IS_ERR(phantom_class)) {
 516                retval = PTR_ERR(phantom_class);
 517                printk(KERN_ERR "phantom: can't register phantom class\n");
 518                goto err;
 519        }
 520        retval = class_create_file(phantom_class, &class_attr_version.attr);
 521        if (retval) {
 522                printk(KERN_ERR "phantom: can't create sysfs version file\n");
 523                goto err_class;
 524        }
 525
 526        retval = alloc_chrdev_region(&dev, 0, PHANTOM_MAX_MINORS, "phantom");
 527        if (retval) {
 528                printk(KERN_ERR "phantom: can't register character device\n");
 529                goto err_attr;
 530        }
 531        phantom_major = MAJOR(dev);
 532
 533        retval = pci_register_driver(&phantom_pci_driver);
 534        if (retval) {
 535                printk(KERN_ERR "phantom: can't register pci driver\n");
 536                goto err_unchr;
 537        }
 538
 539        printk(KERN_INFO "Phantom Linux Driver, version " PHANTOM_VERSION ", "
 540                        "init OK\n");
 541
 542        return 0;
 543err_unchr:
 544        unregister_chrdev_region(dev, PHANTOM_MAX_MINORS);
 545err_attr:
 546        class_remove_file(phantom_class, &class_attr_version.attr);
 547err_class:
 548        class_destroy(phantom_class);
 549err:
 550        return retval;
 551}
 552
 553static void __exit phantom_exit(void)
 554{
 555        pci_unregister_driver(&phantom_pci_driver);
 556
 557        unregister_chrdev_region(MKDEV(phantom_major, 0), PHANTOM_MAX_MINORS);
 558
 559        class_remove_file(phantom_class, &class_attr_version.attr);
 560        class_destroy(phantom_class);
 561
 562        pr_debug("phantom: module successfully removed\n");
 563}
 564
 565module_init(phantom_init);
 566module_exit(phantom_exit);
 567
 568MODULE_AUTHOR("Jiri Slaby <jirislaby@gmail.com>");
 569MODULE_DESCRIPTION("Sensable Phantom driver (PCI devices)");
 570MODULE_LICENSE("GPL");
 571MODULE_VERSION(PHANTOM_VERSION);
 572