linux/drivers/staging/unisys/visorchipset/file.c
<<
>>
Prefs
   1/* file.c
   2 *
   3 * Copyright (C) 2010 - 2013 UNISYS CORPORATION
   4 * All rights reserved.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or (at
   9 * your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful, but
  12 * WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  14 * NON INFRINGEMENT.  See the GNU General Public License for more
  15 * details.
  16 */
  17
  18/* This contains the implementation that allows a usermode program to
  19 * communicate with the visorchipset driver using a device/file interface.
  20 */
  21
  22#include "globals.h"
  23#include "visorchannel.h"
  24#include <linux/mm.h>
  25#include <linux/fs.h>
  26#include "uisutils.h"
  27#include "file.h"
  28
  29#define CURRENT_FILE_PC VISOR_CHIPSET_PC_file_c
  30
  31static struct cdev file_cdev;
  32static struct visorchannel **file_controlvm_channel;
  33
  34void
  35visorchipset_file_cleanup(dev_t major_dev)
  36{
  37        if (file_cdev.ops != NULL)
  38                cdev_del(&file_cdev);
  39        file_cdev.ops = NULL;
  40        unregister_chrdev_region(major_dev, 1);
  41}
  42
  43static int
  44visorchipset_open(struct inode *inode, struct file *file)
  45{
  46        unsigned minor_number = iminor(inode);
  47
  48        if (minor_number != 0)
  49                return -ENODEV;
  50        file->private_data = NULL;
  51        return 0;
  52}
  53
  54static int
  55visorchipset_release(struct inode *inode, struct file *file)
  56{
  57        return 0;
  58}
  59
  60static int
  61visorchipset_mmap(struct file *file, struct vm_area_struct *vma)
  62{
  63        ulong physaddr = 0;
  64        ulong offset = vma->vm_pgoff << PAGE_SHIFT;
  65        GUEST_PHYSICAL_ADDRESS addr = 0;
  66
  67        /* sv_enable_dfp(); */
  68        if (offset & (PAGE_SIZE - 1))
  69                return -ENXIO;  /* need aligned offsets */
  70
  71        switch (offset) {
  72        case VISORCHIPSET_MMAP_CONTROLCHANOFFSET:
  73                vma->vm_flags |= VM_IO;
  74                if (*file_controlvm_channel == NULL) {
  75                        return -ENXIO;
  76                }
  77                visorchannel_read(*file_controlvm_channel,
  78                        offsetof(struct spar_controlvm_channel_protocol,
  79                                 gp_control_channel),
  80                        &addr, sizeof(addr));
  81                if (addr == 0) {
  82                        return -ENXIO;
  83                }
  84                physaddr = (ulong)addr;
  85                if (remap_pfn_range(vma, vma->vm_start,
  86                                    physaddr >> PAGE_SHIFT,
  87                                    vma->vm_end - vma->vm_start,
  88                                    /*pgprot_noncached */
  89                                    (vma->vm_page_prot))) {
  90                        return -EAGAIN;
  91                }
  92                break;
  93        default:
  94                return -ENOSYS;
  95        }
  96        return 0;
  97}
  98
  99static long visorchipset_ioctl(struct file *file, unsigned int cmd,
 100                                unsigned long arg)
 101{
 102        s64 adjustment;
 103        s64 vrtc_offset;
 104
 105        switch (cmd) {
 106        case VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET:
 107                /* get the physical rtc offset */
 108                vrtc_offset = issue_vmcall_query_guest_virtual_time_offset();
 109                if (copy_to_user
 110                    ((void __user *)arg, &vrtc_offset, sizeof(vrtc_offset))) {
 111                        return -EFAULT;
 112                }
 113                return SUCCESS;
 114        case VMCALL_UPDATE_PHYSICAL_TIME:
 115                if (copy_from_user
 116                    (&adjustment, (void __user *)arg, sizeof(adjustment))) {
 117                        return -EFAULT;
 118                }
 119                return issue_vmcall_update_physical_time(adjustment);
 120        default:
 121                return -EFAULT;
 122        }
 123}
 124
 125static const struct file_operations visorchipset_fops = {
 126        .owner = THIS_MODULE,
 127        .open = visorchipset_open,
 128        .read = NULL,
 129        .write = NULL,
 130        .unlocked_ioctl = visorchipset_ioctl,
 131        .release = visorchipset_release,
 132        .mmap = visorchipset_mmap,
 133};
 134
 135int
 136visorchipset_file_init(dev_t major_dev, struct visorchannel **controlvm_channel)
 137{
 138        int rc = 0;
 139
 140        file_controlvm_channel = controlvm_channel;
 141        cdev_init(&file_cdev, &visorchipset_fops);
 142        file_cdev.owner = THIS_MODULE;
 143        if (MAJOR(major_dev) == 0) {
 144                rc = alloc_chrdev_region(&major_dev, 0, 1, MYDRVNAME);
 145                /* dynamic major device number registration required */
 146                if (rc < 0)
 147                        return rc;
 148        } else {
 149                /* static major device number registration required */
 150                rc = register_chrdev_region(major_dev, 1, MYDRVNAME);
 151                if (rc < 0)
 152                        return rc;
 153        }
 154        rc = cdev_add(&file_cdev, MKDEV(MAJOR(major_dev), 0), 1);
 155        if (rc < 0) {
 156                unregister_chrdev_region(major_dev, 1);
 157                return rc;
 158        }
 159        return 0;
 160}
 161