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