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