Commit | Line | Data |
---|---|---|
a04d0423 BS |
1 | /* |
2 | * Copyright 2014 Red Hat Inc. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
20 | * OTHER DEALINGS IN THE SOFTWARE. | |
21 | * | |
22 | * Authors: Ben Skeggs <bskeggs@redhat.com> | |
23 | */ | |
24 | ||
c39f472e BS |
25 | #include <nvif/object.h> |
26 | #include <nvif/client.h> | |
27 | #include <nvif/driver.h> | |
28 | #include <nvif/ioctl.h> | |
a04d0423 BS |
29 | |
30 | int | |
31 | nvif_object_ioctl(struct nvif_object *object, void *data, u32 size, void **hack) | |
32 | { | |
a01ca78c | 33 | struct nvif_client *client = object->client; |
a04d0423 BS |
34 | union { |
35 | struct nvif_ioctl_v0 v0; | |
36 | } *args = data; | |
37 | ||
38 | if (size >= sizeof(*args) && args->v0.version == 0) { | |
bf81df9b BS |
39 | if (object != &client->object) |
40 | args->v0.object = nvif_handle(object); | |
41 | else | |
42 | args->v0.object = 0; | |
a04d0423 | 43 | args->v0.owner = NVIF_IOCTL_V0_OWNER_ANY; |
a04d0423 BS |
44 | } else |
45 | return -ENOSYS; | |
46 | ||
a01ca78c BS |
47 | return client->driver->ioctl(client->object.priv, client->super, |
48 | data, size, hack); | |
a04d0423 BS |
49 | } |
50 | ||
41a63406 BS |
51 | void |
52 | nvif_object_sclass_put(struct nvif_sclass **psclass) | |
53 | { | |
54 | kfree(*psclass); | |
55 | *psclass = NULL; | |
56 | } | |
57 | ||
a04d0423 | 58 | int |
41a63406 | 59 | nvif_object_sclass_get(struct nvif_object *object, struct nvif_sclass **psclass) |
a04d0423 BS |
60 | { |
61 | struct { | |
62 | struct nvif_ioctl_v0 ioctl; | |
63 | struct nvif_ioctl_sclass_v0 sclass; | |
41a63406 BS |
64 | } *args = NULL; |
65 | int ret, cnt = 0, i; | |
66 | u32 size; | |
a04d0423 | 67 | |
41a63406 BS |
68 | while (1) { |
69 | size = sizeof(*args) + cnt * sizeof(args->sclass.oclass[0]); | |
70 | if (!(args = kmalloc(size, GFP_KERNEL))) | |
71 | return -ENOMEM; | |
72 | args->ioctl.version = 0; | |
73 | args->ioctl.type = NVIF_IOCTL_V0_SCLASS; | |
74 | args->sclass.version = 0; | |
75 | args->sclass.count = cnt; | |
76 | ||
77 | ret = nvif_object_ioctl(object, args, size, NULL); | |
78 | if (ret == 0 && args->sclass.count <= cnt) | |
79 | break; | |
80 | cnt = args->sclass.count; | |
81 | kfree(args); | |
82 | if (ret != 0) | |
83 | return ret; | |
84 | } | |
85 | ||
6396bb22 | 86 | *psclass = kcalloc(args->sclass.count, sizeof(**psclass), GFP_KERNEL); |
41a63406 BS |
87 | if (*psclass) { |
88 | for (i = 0; i < args->sclass.count; i++) { | |
89 | (*psclass)[i].oclass = args->sclass.oclass[i].oclass; | |
90 | (*psclass)[i].minver = args->sclass.oclass[i].minver; | |
91 | (*psclass)[i].maxver = args->sclass.oclass[i].maxver; | |
92 | } | |
93 | ret = args->sclass.count; | |
94 | } else { | |
95 | ret = -ENOMEM; | |
96 | } | |
a04d0423 | 97 | |
a04d0423 BS |
98 | kfree(args); |
99 | return ret; | |
100 | } | |
101 | ||
102 | u32 | |
103 | nvif_object_rd(struct nvif_object *object, int size, u64 addr) | |
104 | { | |
105 | struct { | |
106 | struct nvif_ioctl_v0 ioctl; | |
107 | struct nvif_ioctl_rd_v0 rd; | |
108 | } args = { | |
109 | .ioctl.type = NVIF_IOCTL_V0_RD, | |
110 | .rd.size = size, | |
111 | .rd.addr = addr, | |
112 | }; | |
113 | int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL); | |
114 | if (ret) { | |
115 | /*XXX: warn? */ | |
116 | return 0; | |
117 | } | |
118 | return args.rd.data; | |
119 | } | |
120 | ||
121 | void | |
122 | nvif_object_wr(struct nvif_object *object, int size, u64 addr, u32 data) | |
123 | { | |
124 | struct { | |
125 | struct nvif_ioctl_v0 ioctl; | |
126 | struct nvif_ioctl_wr_v0 wr; | |
127 | } args = { | |
128 | .ioctl.type = NVIF_IOCTL_V0_WR, | |
129 | .wr.size = size, | |
130 | .wr.addr = addr, | |
131 | .wr.data = data, | |
132 | }; | |
133 | int ret = nvif_object_ioctl(object, &args, sizeof(args), NULL); | |
134 | if (ret) { | |
135 | /*XXX: warn? */ | |
136 | } | |
137 | } | |
138 | ||
139 | int | |
140 | nvif_object_mthd(struct nvif_object *object, u32 mthd, void *data, u32 size) | |
141 | { | |
142 | struct { | |
143 | struct nvif_ioctl_v0 ioctl; | |
144 | struct nvif_ioctl_mthd_v0 mthd; | |
145 | } *args; | |
146 | u8 stack[128]; | |
147 | int ret; | |
148 | ||
149 | if (sizeof(*args) + size > sizeof(stack)) { | |
150 | if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL))) | |
151 | return -ENOMEM; | |
152 | } else { | |
153 | args = (void *)stack; | |
154 | } | |
155 | args->ioctl.version = 0; | |
156 | args->ioctl.type = NVIF_IOCTL_V0_MTHD; | |
157 | args->mthd.version = 0; | |
158 | args->mthd.method = mthd; | |
159 | ||
160 | memcpy(args->mthd.data, data, size); | |
161 | ret = nvif_object_ioctl(object, args, sizeof(*args) + size, NULL); | |
162 | memcpy(data, args->mthd.data, size); | |
163 | if (args != (void *)stack) | |
164 | kfree(args); | |
165 | return ret; | |
166 | } | |
167 | ||
01326050 BS |
168 | void |
169 | nvif_object_unmap_handle(struct nvif_object *object) | |
170 | { | |
171 | struct { | |
172 | struct nvif_ioctl_v0 ioctl; | |
173 | struct nvif_ioctl_unmap unmap; | |
174 | } args = { | |
175 | .ioctl.type = NVIF_IOCTL_V0_UNMAP, | |
176 | }; | |
177 | ||
178 | nvif_object_ioctl(object, &args, sizeof(args), NULL); | |
179 | } | |
180 | ||
181 | int | |
182 | nvif_object_map_handle(struct nvif_object *object, void *argv, u32 argc, | |
183 | u64 *handle, u64 *length) | |
184 | { | |
185 | struct { | |
186 | struct nvif_ioctl_v0 ioctl; | |
187 | struct nvif_ioctl_map_v0 map; | |
188 | } *args; | |
189 | u32 argn = sizeof(*args) + argc; | |
190 | int ret, maptype; | |
191 | ||
192 | if (!(args = kzalloc(argn, GFP_KERNEL))) | |
193 | return -ENOMEM; | |
194 | args->ioctl.type = NVIF_IOCTL_V0_MAP; | |
195 | memcpy(args->map.data, argv, argc); | |
196 | ||
197 | ret = nvif_object_ioctl(object, args, argn, NULL); | |
198 | *handle = args->map.handle; | |
199 | *length = args->map.length; | |
200 | maptype = args->map.type; | |
201 | kfree(args); | |
202 | return ret ? ret : (maptype == NVIF_IOCTL_MAP_V0_IO); | |
203 | } | |
204 | ||
a04d0423 BS |
205 | void |
206 | nvif_object_unmap(struct nvif_object *object) | |
207 | { | |
01326050 BS |
208 | struct nvif_client *client = object->client; |
209 | if (object->map.ptr) { | |
210 | if (object->map.size) { | |
a04d0423 BS |
211 | client->driver->unmap(client, object->map.ptr, |
212 | object->map.size); | |
01326050 | 213 | object->map.size = 0; |
a04d0423 | 214 | } |
01326050 BS |
215 | object->map.ptr = NULL; |
216 | nvif_object_unmap_handle(object); | |
a04d0423 BS |
217 | } |
218 | } | |
219 | ||
220 | int | |
01326050 | 221 | nvif_object_map(struct nvif_object *object, void *argv, u32 argc) |
a04d0423 | 222 | { |
a01ca78c | 223 | struct nvif_client *client = object->client; |
01326050 BS |
224 | u64 handle, length; |
225 | int ret = nvif_object_map_handle(object, argv, argc, &handle, &length); | |
226 | if (ret >= 0) { | |
227 | if (ret) { | |
228 | object->map.ptr = client->driver->map(client, | |
229 | handle, | |
230 | length); | |
231 | if (ret = -ENOMEM, object->map.ptr) { | |
232 | object->map.size = length; | |
233 | return 0; | |
234 | } | |
235 | } else { | |
236 | object->map.ptr = (void *)(unsigned long)handle; | |
a04d0423 | 237 | return 0; |
01326050 BS |
238 | } |
239 | nvif_object_unmap_handle(object); | |
a04d0423 BS |
240 | } |
241 | return ret; | |
242 | } | |
243 | ||
a04d0423 BS |
244 | void |
245 | nvif_object_fini(struct nvif_object *object) | |
246 | { | |
a01ca78c BS |
247 | struct { |
248 | struct nvif_ioctl_v0 ioctl; | |
249 | struct nvif_ioctl_del del; | |
250 | } args = { | |
251 | .ioctl.type = NVIF_IOCTL_V0_DEL, | |
252 | }; | |
a04d0423 | 253 | |
a01ca78c BS |
254 | if (!object->client) |
255 | return; | |
256 | ||
257 | nvif_object_unmap(object); | |
258 | nvif_object_ioctl(object, &args, sizeof(args), NULL); | |
259 | object->client = NULL; | |
a04d0423 BS |
260 | } |
261 | ||
262 | int | |
315a8b2e | 263 | nvif_object_init(struct nvif_object *parent, u32 handle, s32 oclass, |
a01ca78c | 264 | void *data, u32 size, struct nvif_object *object) |
a04d0423 | 265 | { |
a01ca78c BS |
266 | struct { |
267 | struct nvif_ioctl_v0 ioctl; | |
268 | struct nvif_ioctl_new_v0 new; | |
269 | } *args; | |
a04d0423 BS |
270 | int ret = 0; |
271 | ||
a01ca78c | 272 | object->client = NULL; |
a04d0423 BS |
273 | object->handle = handle; |
274 | object->oclass = oclass; | |
a04d0423 BS |
275 | object->map.ptr = NULL; |
276 | object->map.size = 0; | |
277 | ||
bf81df9b | 278 | if (parent) { |
a01ca78c | 279 | if (!(args = kmalloc(sizeof(*args) + size, GFP_KERNEL))) { |
a04d0423 BS |
280 | nvif_object_fini(object); |
281 | return -ENOMEM; | |
282 | } | |
a04d0423 | 283 | |
a01ca78c BS |
284 | args->ioctl.version = 0; |
285 | args->ioctl.type = NVIF_IOCTL_V0_NEW; | |
286 | args->new.version = 0; | |
287 | args->new.route = parent->client->route; | |
bf81df9b BS |
288 | args->new.token = nvif_handle(object); |
289 | args->new.object = nvif_handle(object); | |
a01ca78c BS |
290 | args->new.handle = handle; |
291 | args->new.oclass = oclass; | |
292 | ||
293 | memcpy(args->new.data, data, size); | |
294 | ret = nvif_object_ioctl(parent, args, sizeof(*args) + size, | |
295 | &object->priv); | |
296 | memcpy(data, args->new.data, size); | |
297 | kfree(args); | |
298 | if (ret == 0) | |
299 | object->client = parent->client; | |
a04d0423 BS |
300 | } |
301 | ||
302 | if (ret) | |
303 | nvif_object_fini(object); | |
304 | return ret; | |
305 | } |