5c5d35f104713d6c5d5656fb5c01dad87a057fef
[deliverable/linux.git] / drivers / staging / unisys / visorchipset / file.c
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
31 static struct cdev file_cdev;
32 static struct visorchannel **file_controlvm_channel;
33
34 static int visorchipset_open(struct inode *inode, struct file *file);
35 static int visorchipset_release(struct inode *inode, struct file *file);
36 static int visorchipset_mmap(struct file *file, struct vm_area_struct *vma);
37 static long visorchipset_ioctl(struct file *file, unsigned int cmd,
38 unsigned long arg);
39
40 static const struct file_operations visorchipset_fops = {
41 .owner = THIS_MODULE,
42 .open = visorchipset_open,
43 .read = NULL,
44 .write = NULL,
45 .unlocked_ioctl = visorchipset_ioctl,
46 .release = visorchipset_release,
47 .mmap = visorchipset_mmap,
48 };
49
50 int
51 visorchipset_file_init(dev_t major_dev, struct visorchannel **controlvm_channel)
52 {
53 int rc = 0;
54
55 file_controlvm_channel = controlvm_channel;
56 cdev_init(&file_cdev, &visorchipset_fops);
57 file_cdev.owner = THIS_MODULE;
58 if (MAJOR(major_dev) == 0) {
59 /* dynamic major device number registration required */
60 if (alloc_chrdev_region(&major_dev, 0, 1, MYDRVNAME) < 0)
61 return -1;
62 } else {
63 /* static major device number registration required */
64 if (register_chrdev_region(major_dev, 1, MYDRVNAME) < 0)
65 return -1;
66 }
67 rc = cdev_add(&file_cdev, MKDEV(MAJOR(major_dev), 0), 1);
68 if (rc < 0) {
69 unregister_chrdev_region(major_dev, 1);
70 return -1;
71 }
72 return 0;
73 }
74
75 void
76 visorchipset_file_cleanup(dev_t major_dev)
77 {
78 if (file_cdev.ops != NULL)
79 cdev_del(&file_cdev);
80 file_cdev.ops = NULL;
81 unregister_chrdev_region(major_dev, 1);
82 }
83
84 static int
85 visorchipset_open(struct inode *inode, struct file *file)
86 {
87 unsigned minor_number = iminor(inode);
88
89 if (minor_number != 0)
90 return -ENODEV;
91 file->private_data = NULL;
92 return 0;
93 }
94
95 static int
96 visorchipset_release(struct inode *inode, struct file *file)
97 {
98 return 0;
99 }
100
101 static int
102 visorchipset_mmap(struct file *file, struct vm_area_struct *vma)
103 {
104 ulong physaddr = 0;
105 ulong offset = vma->vm_pgoff << PAGE_SHIFT;
106 GUEST_PHYSICAL_ADDRESS addr = 0;
107
108 /* sv_enable_dfp(); */
109 if (offset & (PAGE_SIZE - 1))
110 return -ENXIO; /* need aligned offsets */
111
112 switch (offset) {
113 case VISORCHIPSET_MMAP_CONTROLCHANOFFSET:
114 vma->vm_flags |= VM_IO;
115 if (*file_controlvm_channel == NULL) {
116 return -ENXIO;
117 }
118 visorchannel_read(*file_controlvm_channel,
119 offsetof(struct spar_controlvm_channel_protocol,
120 gp_control_channel),
121 &addr, sizeof(addr));
122 if (addr == 0) {
123 return -ENXIO;
124 }
125 physaddr = (ulong)addr;
126 if (remap_pfn_range(vma, vma->vm_start,
127 physaddr >> PAGE_SHIFT,
128 vma->vm_end - vma->vm_start,
129 /*pgprot_noncached */
130 (vma->vm_page_prot))) {
131 return -EAGAIN;
132 }
133 break;
134 default:
135 return -ENOSYS;
136 }
137 return 0;
138 }
139
140 static long visorchipset_ioctl(struct file *file, unsigned int cmd,
141 unsigned long arg)
142 {
143 s64 adjustment;
144 s64 vrtc_offset;
145
146 switch (cmd) {
147 case VMCALL_QUERY_GUEST_VIRTUAL_TIME_OFFSET:
148 /* get the physical rtc offset */
149 vrtc_offset = issue_vmcall_query_guest_virtual_time_offset();
150 if (copy_to_user
151 ((void __user *)arg, &vrtc_offset, sizeof(vrtc_offset))) {
152 return -EFAULT;
153 }
154 return SUCCESS;
155 case VMCALL_UPDATE_PHYSICAL_TIME:
156 if (copy_from_user
157 (&adjustment, (void __user *)arg, sizeof(adjustment))) {
158 return -EFAULT;
159 }
160 return issue_vmcall_update_physical_time(adjustment);
161 default:
162 return -EFAULT;
163 }
164 }
This page took 0.063971 seconds and 4 git commands to generate.