source: mainline/uspace/srv/fs/devfs/devfs_ops.c@ cc27c8c5

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since cc27c8c5 was d9c8c81, checked in by Martin Decky <martin@…>, 16 years ago

rename libadt → adt

  • Property mode set to 100644
File size: 11.5 KB
Line 
1/*
2 * Copyright (c) 2009 Martin Decky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup fs
30 * @{
31 */
32
33/**
34 * @file devfs_ops.c
35 * @brief Implementation of VFS operations for the devfs file system server.
36 */
37
38#include <ipc/ipc.h>
39#include <bool.h>
40#include <errno.h>
41#include <malloc.h>
42#include <string.h>
43#include <libfs.h>
44#include <adt/hash_table.h>
45#include "devfs.h"
46#include "devfs_ops.h"
47
48#define PLB_GET_CHAR(pos) (devfs_reg.plb_ro[pos % PLB_SIZE])
49
50/** Opened devices structure */
51typedef struct {
52 dev_handle_t handle;
53 int phone;
54 size_t refcount;
55 link_t link;
56} device_t;
57
58/** Hash table of opened devices */
59static hash_table_t devices;
60
61#define DEVICES_KEYS 1
62#define DEVICES_KEY_HANDLE 0
63#define DEVICES_BUCKETS 256
64
65/* Implementation of hash table interface for the nodes hash table. */
66static hash_index_t devices_hash(unsigned long key[])
67{
68 return key[DEVICES_KEY_HANDLE] % DEVICES_BUCKETS;
69}
70
71static int devices_compare(unsigned long key[], hash_count_t keys, link_t *item)
72{
73 device_t *dev = hash_table_get_instance(item, device_t, link);
74 return (dev->handle == (dev_handle_t) key[DEVICES_KEY_HANDLE]);
75}
76
77static void devices_remove_callback(link_t *item)
78{
79 free(hash_table_get_instance(item, device_t, link));
80}
81
82static hash_table_operations_t devices_ops = {
83 .hash = devices_hash,
84 .compare = devices_compare,
85 .remove_callback = devices_remove_callback
86};
87
88bool devfs_init(void)
89{
90 if (!hash_table_create(&devices, DEVICES_BUCKETS,
91 DEVICES_KEYS, &devices_ops))
92 return false;
93
94 if (devmap_get_phone(DEVMAP_CLIENT, IPC_FLAG_BLOCKING) < 0)
95 return false;
96
97 return true;
98}
99
100void devfs_mounted(ipc_callid_t rid, ipc_call_t *request)
101{
102 /* Accept the mount options */
103 ipc_callid_t callid;
104 size_t size;
105 if (!ipc_data_write_receive(&callid, &size)) {
106 ipc_answer_0(callid, EINVAL);
107 ipc_answer_0(rid, EINVAL);
108 return;
109 }
110
111 char *opts = malloc(size + 1);
112 if (!opts) {
113 ipc_answer_0(callid, ENOMEM);
114 ipc_answer_0(rid, ENOMEM);
115 return;
116 }
117
118 ipcarg_t retval = ipc_data_write_finalize(callid, opts, size);
119 if (retval != EOK) {
120 ipc_answer_0(rid, retval);
121 free(opts);
122 return;
123 }
124
125 free(opts);
126
127 ipc_answer_3(rid, EOK, 0, 0, 0);
128}
129
130void devfs_mount(ipc_callid_t rid, ipc_call_t *request)
131{
132 ipc_answer_0(rid, ENOTSUP);
133}
134
135void devfs_lookup(ipc_callid_t rid, ipc_call_t *request)
136{
137 ipcarg_t first = IPC_GET_ARG1(*request);
138 ipcarg_t last = IPC_GET_ARG2(*request);
139 dev_handle_t dev_handle = IPC_GET_ARG3(*request);
140 ipcarg_t lflag = IPC_GET_ARG4(*request);
141 fs_index_t index = IPC_GET_ARG5(*request);
142
143 /* Hierarchy is flat, no altroot is supported */
144 if (index != 0) {
145 ipc_answer_0(rid, ENOENT);
146 return;
147 }
148
149 if ((lflag & L_LINK) || (lflag & L_UNLINK)) {
150 ipc_answer_0(rid, ENOTSUP);
151 return;
152 }
153
154 /* Eat slash */
155 if (PLB_GET_CHAR(first) == '/') {
156 first++;
157 first %= PLB_SIZE;
158 }
159
160 if (first >= last) {
161 /* Root entry */
162 if (lflag & L_DIRECTORY)
163 ipc_answer_5(rid, EOK, devfs_reg.fs_handle, dev_handle, 0, 0, 0);
164 else
165 ipc_answer_0(rid, ENOENT);
166 } else {
167 if (lflag & L_FILE) {
168 size_t len;
169 if (last >= first)
170 len = last - first + 1;
171 else
172 len = first + PLB_SIZE - last + 1;
173
174 char *name = (char *) malloc(len + 1);
175 if (name == NULL) {
176 ipc_answer_0(rid, ENOMEM);
177 return;
178 }
179
180 size_t i;
181 for (i = 0; i < len; i++)
182 name[i] = PLB_GET_CHAR(first + i);
183
184 name[len] = 0;
185
186 dev_handle_t handle;
187 if (devmap_device_get_handle(name, &handle, 0) != EOK) {
188 free(name);
189 ipc_answer_0(rid, ENOENT);
190 return;
191 }
192
193 if (lflag & L_OPEN) {
194 unsigned long key[] = {
195 [DEVICES_KEY_HANDLE] = (unsigned long) handle
196 };
197
198 link_t *lnk = hash_table_find(&devices, key);
199 if (lnk == NULL) {
200 int phone = devmap_device_connect(handle, 0);
201 if (phone < 0) {
202 free(name);
203 ipc_answer_0(rid, ENOENT);
204 return;
205 }
206
207 device_t *dev = (device_t *) malloc(sizeof(device_t));
208 if (dev == NULL) {
209 free(name);
210 ipc_answer_0(rid, ENOMEM);
211 return;
212 }
213
214 dev->handle = handle;
215 dev->phone = phone;
216 dev->refcount = 1;
217
218 hash_table_insert(&devices, key, &dev->link);
219 } else {
220 device_t *dev = hash_table_get_instance(lnk, device_t, link);
221 dev->refcount++;
222 }
223 }
224
225 free(name);
226
227 ipc_answer_5(rid, EOK, devfs_reg.fs_handle, dev_handle, handle, 0, 1);
228 } else
229 ipc_answer_0(rid, ENOENT);
230 }
231}
232
233void devfs_open_node(ipc_callid_t rid, ipc_call_t *request)
234{
235 dev_handle_t handle = IPC_GET_ARG2(*request);
236
237 unsigned long key[] = {
238 [DEVICES_KEY_HANDLE] = (unsigned long) handle
239 };
240
241 link_t *lnk = hash_table_find(&devices, key);
242 if (lnk == NULL) {
243 int phone = devmap_device_connect(handle, 0);
244 if (phone < 0) {
245 ipc_answer_0(rid, ENOENT);
246 return;
247 }
248
249 device_t *dev = (device_t *) malloc(sizeof(device_t));
250 if (dev == NULL) {
251 ipc_answer_0(rid, ENOMEM);
252 return;
253 }
254
255 dev->handle = handle;
256 dev->phone = phone;
257 dev->refcount = 1;
258
259 hash_table_insert(&devices, key, &dev->link);
260 } else {
261 device_t *dev = hash_table_get_instance(lnk, device_t, link);
262 dev->refcount++;
263 }
264
265 ipc_answer_3(rid, EOK, 0, 1, L_FILE);
266}
267
268void devfs_device(ipc_callid_t rid, ipc_call_t *request)
269{
270 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
271
272 if (index != 0) {
273 unsigned long key[] = {
274 [DEVICES_KEY_HANDLE] = (unsigned long) index
275 };
276
277 link_t *lnk = hash_table_find(&devices, key);
278 if (lnk == NULL) {
279 ipc_answer_0(rid, ENOENT);
280 return;
281 }
282
283 ipc_answer_1(rid, EOK, (ipcarg_t) index);
284 } else
285 ipc_answer_0(rid, ENOTSUP);
286}
287
288void devfs_read(ipc_callid_t rid, ipc_call_t *request)
289{
290 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
291 off_t pos = (off_t) IPC_GET_ARG3(*request);
292
293 if (index != 0) {
294 unsigned long key[] = {
295 [DEVICES_KEY_HANDLE] = (unsigned long) index
296 };
297
298 link_t *lnk = hash_table_find(&devices, key);
299 if (lnk == NULL) {
300 ipc_answer_0(rid, ENOENT);
301 return;
302 }
303
304 device_t *dev = hash_table_get_instance(lnk, device_t, link);
305
306 ipc_callid_t callid;
307 if (!ipc_data_read_receive(&callid, NULL)) {
308 ipc_answer_0(callid, EINVAL);
309 ipc_answer_0(rid, EINVAL);
310 return;
311 }
312
313 /* Make a request at the driver */
314 ipc_call_t answer;
315 aid_t msg = async_send_3(dev->phone, IPC_GET_METHOD(*request),
316 IPC_GET_ARG1(*request), IPC_GET_ARG2(*request),
317 IPC_GET_ARG3(*request), &answer);
318
319 /* Forward the IPC_M_DATA_READ request to the driver */
320 ipc_forward_fast(callid, dev->phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
321
322 /* Wait for reply from the driver. */
323 ipcarg_t rc;
324 async_wait_for(msg, &rc);
325 size_t bytes = IPC_GET_ARG1(answer);
326
327 /* Driver reply is the final result of the whole operation */
328 ipc_answer_1(rid, rc, bytes);
329 } else {
330 ipc_callid_t callid;
331 size_t size;
332 if (!ipc_data_read_receive(&callid, &size)) {
333 ipc_answer_0(callid, EINVAL);
334 ipc_answer_0(rid, EINVAL);
335 return;
336 }
337
338 size_t count = devmap_device_get_count();
339 dev_desc_t *desc = malloc(count * sizeof(dev_desc_t));
340 if (desc == NULL) {
341 ipc_answer_0(callid, ENOMEM);
342 ipc_answer_1(rid, ENOMEM, 0);
343 return;
344 }
345
346 size_t max = devmap_device_get_devices(count, desc);
347
348 if (pos < max) {
349 ipc_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
350 } else {
351 ipc_answer_0(callid, ENOENT);
352 ipc_answer_1(rid, ENOENT, 0);
353 return;
354 }
355
356 free(desc);
357
358 ipc_answer_1(rid, EOK, 1);
359 }
360}
361
362void devfs_write(ipc_callid_t rid, ipc_call_t *request)
363{
364 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
365 off_t pos = (off_t) IPC_GET_ARG3(*request);
366
367 if (index != 0) {
368 unsigned long key[] = {
369 [DEVICES_KEY_HANDLE] = (unsigned long) index
370 };
371
372 link_t *lnk = hash_table_find(&devices, key);
373 if (lnk == NULL) {
374 ipc_answer_0(rid, ENOENT);
375 return;
376 }
377
378 device_t *dev = hash_table_get_instance(lnk, device_t, link);
379
380 ipc_callid_t callid;
381 if (!ipc_data_write_receive(&callid, NULL)) {
382 ipc_answer_0(callid, EINVAL);
383 ipc_answer_0(rid, EINVAL);
384 return;
385 }
386
387 /* Make a request at the driver */
388 ipc_call_t answer;
389 aid_t msg = async_send_3(dev->phone, IPC_GET_METHOD(*request),
390 IPC_GET_ARG1(*request), IPC_GET_ARG2(*request),
391 IPC_GET_ARG3(*request), &answer);
392
393 /* Forward the IPC_M_DATA_WRITE request to the driver */
394 ipc_forward_fast(callid, dev->phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
395
396 /* Wait for reply from the driver. */
397 ipcarg_t rc;
398 async_wait_for(msg, &rc);
399 size_t bytes = IPC_GET_ARG1(answer);
400
401 /* Driver reply is the final result of the whole operation */
402 ipc_answer_1(rid, rc, bytes);
403 } else {
404 /* Read-only filesystem */
405 ipc_answer_0(rid, ENOTSUP);
406 }
407}
408
409void devfs_truncate(ipc_callid_t rid, ipc_call_t *request)
410{
411 ipc_answer_0(rid, ENOTSUP);
412}
413
414void devfs_close(ipc_callid_t rid, ipc_call_t *request)
415{
416 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
417
418 if (index != 0) {
419 unsigned long key[] = {
420 [DEVICES_KEY_HANDLE] = (unsigned long) index
421 };
422
423 link_t *lnk = hash_table_find(&devices, key);
424 if (lnk == NULL) {
425 ipc_answer_0(rid, ENOENT);
426 return;
427 }
428
429 device_t *dev = hash_table_get_instance(lnk, device_t, link);
430 dev->refcount--;
431
432 if (dev->refcount == 0) {
433 ipc_hangup(dev->phone);
434 hash_table_remove(&devices, key, DEVICES_KEYS);
435 }
436
437 ipc_answer_0(rid, EOK);
438 } else
439 ipc_answer_0(rid, ENOTSUP);
440}
441
442void devfs_sync(ipc_callid_t rid, ipc_call_t *request)
443{
444 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
445
446 if (index != 0) {
447 unsigned long key[] = {
448 [DEVICES_KEY_HANDLE] = (unsigned long) index
449 };
450
451 link_t *lnk = hash_table_find(&devices, key);
452 if (lnk == NULL) {
453 ipc_answer_0(rid, ENOENT);
454 return;
455 }
456
457 device_t *dev = hash_table_get_instance(lnk, device_t, link);
458
459 /* Make a request at the driver */
460 ipc_call_t answer;
461 aid_t msg = async_send_2(dev->phone, IPC_GET_METHOD(*request),
462 IPC_GET_ARG1(*request), IPC_GET_ARG2(*request), &answer);
463
464 /* Wait for reply from the driver */
465 ipcarg_t rc;
466 async_wait_for(msg, &rc);
467
468 /* Driver reply is the final result of the whole operation */
469 ipc_answer_0(rid, rc);
470 } else
471 ipc_answer_0(rid, ENOTSUP);
472}
473
474void devfs_destroy(ipc_callid_t rid, ipc_call_t *request)
475{
476 ipc_answer_0(rid, ENOTSUP);
477}
478
479/**
480 * @}
481 */
Note: See TracBrowser for help on using the repository browser.