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

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

rename fibril_sync.[ch] to fibril_synch.[ch]

  • Property mode set to 100644
File size: 13.0 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 <fibril_synch.h>
45#include <adt/hash_table.h>
46#include <sys/stat.h>
47#include "devfs.h"
48#include "devfs_ops.h"
49
50#define PLB_GET_CHAR(pos) (devfs_reg.plb_ro[pos % PLB_SIZE])
51
52/** Opened devices structure */
53typedef struct {
54 dev_handle_t handle;
55 int phone;
56 size_t refcount;
57 link_t link;
58} device_t;
59
60/** Hash table of opened devices */
61static hash_table_t devices;
62
63/** Hash table mutex */
64static FIBRIL_MUTEX_INITIALIZE(devices_mutex);
65
66#define DEVICES_KEYS 1
67#define DEVICES_KEY_HANDLE 0
68#define DEVICES_BUCKETS 256
69
70/* Implementation of hash table interface for the nodes hash table. */
71static hash_index_t devices_hash(unsigned long key[])
72{
73 return key[DEVICES_KEY_HANDLE] % DEVICES_BUCKETS;
74}
75
76static int devices_compare(unsigned long key[], hash_count_t keys, link_t *item)
77{
78 device_t *dev = hash_table_get_instance(item, device_t, link);
79 return (dev->handle == (dev_handle_t) key[DEVICES_KEY_HANDLE]);
80}
81
82static void devices_remove_callback(link_t *item)
83{
84 free(hash_table_get_instance(item, device_t, link));
85}
86
87static hash_table_operations_t devices_ops = {
88 .hash = devices_hash,
89 .compare = devices_compare,
90 .remove_callback = devices_remove_callback
91};
92
93bool devfs_init(void)
94{
95 if (!hash_table_create(&devices, DEVICES_BUCKETS,
96 DEVICES_KEYS, &devices_ops))
97 return false;
98
99 if (devmap_get_phone(DEVMAP_CLIENT, IPC_FLAG_BLOCKING) < 0)
100 return false;
101
102 return true;
103}
104
105void devfs_mounted(ipc_callid_t rid, ipc_call_t *request)
106{
107 /* Accept the mount options */
108 ipc_callid_t callid;
109 size_t size;
110 if (!async_data_write_receive(&callid, &size)) {
111 ipc_answer_0(callid, EINVAL);
112 ipc_answer_0(rid, EINVAL);
113 return;
114 }
115
116 char *opts = malloc(size + 1);
117 if (!opts) {
118 ipc_answer_0(callid, ENOMEM);
119 ipc_answer_0(rid, ENOMEM);
120 return;
121 }
122
123 ipcarg_t retval = async_data_write_finalize(callid, opts, size);
124 if (retval != EOK) {
125 ipc_answer_0(rid, retval);
126 free(opts);
127 return;
128 }
129
130 free(opts);
131
132 ipc_answer_3(rid, EOK, 0, 0, 0);
133}
134
135void devfs_mount(ipc_callid_t rid, ipc_call_t *request)
136{
137 ipc_answer_0(rid, ENOTSUP);
138}
139
140void devfs_lookup(ipc_callid_t rid, ipc_call_t *request)
141{
142 ipcarg_t first = IPC_GET_ARG1(*request);
143 ipcarg_t last = IPC_GET_ARG2(*request);
144 dev_handle_t dev_handle = IPC_GET_ARG3(*request);
145 ipcarg_t lflag = IPC_GET_ARG4(*request);
146 fs_index_t index = IPC_GET_ARG5(*request);
147
148 /* Hierarchy is flat, no altroot is supported */
149 if (index != 0) {
150 ipc_answer_0(rid, ENOENT);
151 return;
152 }
153
154 if ((lflag & L_LINK) || (lflag & L_UNLINK)) {
155 ipc_answer_0(rid, ENOTSUP);
156 return;
157 }
158
159 /* Eat slash */
160 if (PLB_GET_CHAR(first) == '/') {
161 first++;
162 first %= PLB_SIZE;
163 }
164
165 if (first >= last) {
166 /* Root entry */
167 if (!(lflag & L_FILE))
168 ipc_answer_5(rid, EOK, devfs_reg.fs_handle, dev_handle, 0, 0, 0);
169 else
170 ipc_answer_0(rid, ENOENT);
171 } else {
172 if (!(lflag & L_DIRECTORY)) {
173 size_t len;
174 if (last >= first)
175 len = last - first + 1;
176 else
177 len = first + PLB_SIZE - last + 1;
178
179 char *name = (char *) malloc(len + 1);
180 if (name == NULL) {
181 ipc_answer_0(rid, ENOMEM);
182 return;
183 }
184
185 size_t i;
186 for (i = 0; i < len; i++)
187 name[i] = PLB_GET_CHAR(first + i);
188
189 name[len] = 0;
190
191 dev_handle_t handle;
192 if (devmap_device_get_handle(name, &handle, 0) != EOK) {
193 free(name);
194 ipc_answer_0(rid, ENOENT);
195 return;
196 }
197
198 if (lflag & L_OPEN) {
199 unsigned long key[] = {
200 [DEVICES_KEY_HANDLE] = (unsigned long) handle
201 };
202
203 fibril_mutex_lock(&devices_mutex);
204 link_t *lnk = hash_table_find(&devices, key);
205 if (lnk == NULL) {
206 int phone = devmap_device_connect(handle, 0);
207 if (phone < 0) {
208 fibril_mutex_unlock(&devices_mutex);
209 free(name);
210 ipc_answer_0(rid, ENOENT);
211 return;
212 }
213
214 device_t *dev = (device_t *) malloc(sizeof(device_t));
215 if (dev == NULL) {
216 fibril_mutex_unlock(&devices_mutex);
217 free(name);
218 ipc_answer_0(rid, ENOMEM);
219 return;
220 }
221
222 dev->handle = handle;
223 dev->phone = phone;
224 dev->refcount = 1;
225
226 hash_table_insert(&devices, key, &dev->link);
227 } else {
228 device_t *dev = hash_table_get_instance(lnk, device_t, link);
229 dev->refcount++;
230 }
231 fibril_mutex_unlock(&devices_mutex);
232 }
233
234 free(name);
235
236 ipc_answer_5(rid, EOK, devfs_reg.fs_handle, dev_handle, handle, 0, 1);
237 } else
238 ipc_answer_0(rid, ENOENT);
239 }
240}
241
242void devfs_open_node(ipc_callid_t rid, ipc_call_t *request)
243{
244 dev_handle_t handle = IPC_GET_ARG2(*request);
245
246 unsigned long key[] = {
247 [DEVICES_KEY_HANDLE] = (unsigned long) handle
248 };
249
250 fibril_mutex_lock(&devices_mutex);
251 link_t *lnk = hash_table_find(&devices, key);
252 if (lnk == NULL) {
253 int phone = devmap_device_connect(handle, 0);
254 if (phone < 0) {
255 fibril_mutex_unlock(&devices_mutex);
256 ipc_answer_0(rid, ENOENT);
257 return;
258 }
259
260 device_t *dev = (device_t *) malloc(sizeof(device_t));
261 if (dev == NULL) {
262 fibril_mutex_unlock(&devices_mutex);
263 ipc_answer_0(rid, ENOMEM);
264 return;
265 }
266
267 dev->handle = handle;
268 dev->phone = phone;
269 dev->refcount = 1;
270
271 hash_table_insert(&devices, key, &dev->link);
272 } else {
273 device_t *dev = hash_table_get_instance(lnk, device_t, link);
274 dev->refcount++;
275 }
276 fibril_mutex_unlock(&devices_mutex);
277
278 ipc_answer_3(rid, EOK, 0, 1, L_FILE);
279}
280
281void devfs_stat(ipc_callid_t rid, ipc_call_t *request)
282{
283 dev_handle_t dev_handle = (dev_handle_t) IPC_GET_ARG1(*request);
284 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
285
286 ipc_callid_t callid;
287 size_t size;
288 if (!async_data_read_receive(&callid, &size) ||
289 size != sizeof(struct stat)) {
290 ipc_answer_0(callid, EINVAL);
291 ipc_answer_0(rid, EINVAL);
292 return;
293 }
294
295 struct stat stat;
296 memset(&stat, 0, sizeof(struct stat));
297
298 stat.fs_handle = devfs_reg.fs_handle;
299 stat.dev_handle = dev_handle;
300 stat.index = index;
301 stat.lnkcnt = 1;
302 stat.is_file = (index != 0);
303 stat.size = 0;
304
305 if (index != 0) {
306 unsigned long key[] = {
307 [DEVICES_KEY_HANDLE] = (unsigned long) index
308 };
309
310 fibril_mutex_lock(&devices_mutex);
311 link_t *lnk = hash_table_find(&devices, key);
312 if (lnk != NULL)
313 stat.devfs_stat.device = (dev_handle_t)index;
314 fibril_mutex_unlock(&devices_mutex);
315 }
316
317 async_data_read_finalize(callid, &stat, sizeof(struct stat));
318 ipc_answer_0(rid, EOK);
319}
320
321void devfs_read(ipc_callid_t rid, ipc_call_t *request)
322{
323 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
324 off_t pos = (off_t) IPC_GET_ARG3(*request);
325
326 if (index != 0) {
327 unsigned long key[] = {
328 [DEVICES_KEY_HANDLE] = (unsigned long) index
329 };
330
331 fibril_mutex_lock(&devices_mutex);
332 link_t *lnk = hash_table_find(&devices, key);
333 if (lnk == NULL) {
334 fibril_mutex_unlock(&devices_mutex);
335 ipc_answer_0(rid, ENOENT);
336 return;
337 }
338
339 device_t *dev = hash_table_get_instance(lnk, device_t, link);
340
341 ipc_callid_t callid;
342 if (!async_data_read_receive(&callid, NULL)) {
343 fibril_mutex_unlock(&devices_mutex);
344 ipc_answer_0(callid, EINVAL);
345 ipc_answer_0(rid, EINVAL);
346 return;
347 }
348
349 /* Make a request at the driver */
350 ipc_call_t answer;
351 aid_t msg = async_send_3(dev->phone, IPC_GET_METHOD(*request),
352 IPC_GET_ARG1(*request), IPC_GET_ARG2(*request),
353 IPC_GET_ARG3(*request), &answer);
354
355 /* Forward the IPC_M_DATA_READ request to the driver */
356 ipc_forward_fast(callid, dev->phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
357 fibril_mutex_unlock(&devices_mutex);
358
359 /* Wait for reply from the driver. */
360 ipcarg_t rc;
361 async_wait_for(msg, &rc);
362 size_t bytes = IPC_GET_ARG1(answer);
363
364 /* Driver reply is the final result of the whole operation */
365 ipc_answer_1(rid, rc, bytes);
366 } else {
367 ipc_callid_t callid;
368 size_t size;
369 if (!async_data_read_receive(&callid, &size)) {
370 ipc_answer_0(callid, EINVAL);
371 ipc_answer_0(rid, EINVAL);
372 return;
373 }
374
375 size_t count = devmap_device_get_count();
376 dev_desc_t *desc = malloc(count * sizeof(dev_desc_t));
377 if (desc == NULL) {
378 ipc_answer_0(callid, ENOMEM);
379 ipc_answer_1(rid, ENOMEM, 0);
380 return;
381 }
382
383 size_t max = devmap_device_get_devices(count, desc);
384
385 if (pos < max) {
386 async_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1);
387 } else {
388 ipc_answer_0(callid, ENOENT);
389 ipc_answer_1(rid, ENOENT, 0);
390 return;
391 }
392
393 free(desc);
394
395 ipc_answer_1(rid, EOK, 1);
396 }
397}
398
399void devfs_write(ipc_callid_t rid, ipc_call_t *request)
400{
401 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
402 off_t pos = (off_t) IPC_GET_ARG3(*request);
403
404 if (index != 0) {
405 unsigned long key[] = {
406 [DEVICES_KEY_HANDLE] = (unsigned long) index
407 };
408
409 fibril_mutex_lock(&devices_mutex);
410 link_t *lnk = hash_table_find(&devices, key);
411 if (lnk == NULL) {
412 fibril_mutex_unlock(&devices_mutex);
413 ipc_answer_0(rid, ENOENT);
414 return;
415 }
416
417 device_t *dev = hash_table_get_instance(lnk, device_t, link);
418
419 ipc_callid_t callid;
420 if (!async_data_write_receive(&callid, NULL)) {
421 fibril_mutex_unlock(&devices_mutex);
422 ipc_answer_0(callid, EINVAL);
423 ipc_answer_0(rid, EINVAL);
424 return;
425 }
426
427 /* Make a request at the driver */
428 ipc_call_t answer;
429 aid_t msg = async_send_3(dev->phone, IPC_GET_METHOD(*request),
430 IPC_GET_ARG1(*request), IPC_GET_ARG2(*request),
431 IPC_GET_ARG3(*request), &answer);
432
433 /* Forward the IPC_M_DATA_WRITE request to the driver */
434 ipc_forward_fast(callid, dev->phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME);
435
436 fibril_mutex_unlock(&devices_mutex);
437
438 /* Wait for reply from the driver. */
439 ipcarg_t rc;
440 async_wait_for(msg, &rc);
441 size_t bytes = IPC_GET_ARG1(answer);
442
443 /* Driver reply is the final result of the whole operation */
444 ipc_answer_1(rid, rc, bytes);
445 } else {
446 /* Read-only filesystem */
447 ipc_answer_0(rid, ENOTSUP);
448 }
449}
450
451void devfs_truncate(ipc_callid_t rid, ipc_call_t *request)
452{
453 ipc_answer_0(rid, ENOTSUP);
454}
455
456void devfs_close(ipc_callid_t rid, ipc_call_t *request)
457{
458 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
459
460 if (index != 0) {
461 unsigned long key[] = {
462 [DEVICES_KEY_HANDLE] = (unsigned long) index
463 };
464
465 fibril_mutex_lock(&devices_mutex);
466 link_t *lnk = hash_table_find(&devices, key);
467 if (lnk == NULL) {
468 fibril_mutex_unlock(&devices_mutex);
469 ipc_answer_0(rid, ENOENT);
470 return;
471 }
472
473 device_t *dev = hash_table_get_instance(lnk, device_t, link);
474 dev->refcount--;
475
476 if (dev->refcount == 0) {
477 ipc_hangup(dev->phone);
478 hash_table_remove(&devices, key, DEVICES_KEYS);
479 }
480
481 fibril_mutex_unlock(&devices_mutex);
482
483 ipc_answer_0(rid, EOK);
484 } else
485 ipc_answer_0(rid, ENOTSUP);
486}
487
488void devfs_sync(ipc_callid_t rid, ipc_call_t *request)
489{
490 fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request);
491
492 if (index != 0) {
493 unsigned long key[] = {
494 [DEVICES_KEY_HANDLE] = (unsigned long) index
495 };
496
497 fibril_mutex_lock(&devices_mutex);
498 link_t *lnk = hash_table_find(&devices, key);
499 if (lnk == NULL) {
500 fibril_mutex_unlock(&devices_mutex);
501 ipc_answer_0(rid, ENOENT);
502 return;
503 }
504
505 device_t *dev = hash_table_get_instance(lnk, device_t, link);
506
507 /* Make a request at the driver */
508 ipc_call_t answer;
509 aid_t msg = async_send_2(dev->phone, IPC_GET_METHOD(*request),
510 IPC_GET_ARG1(*request), IPC_GET_ARG2(*request), &answer);
511
512 fibril_mutex_unlock(&devices_mutex);
513
514 /* Wait for reply from the driver */
515 ipcarg_t rc;
516 async_wait_for(msg, &rc);
517
518 /* Driver reply is the final result of the whole operation */
519 ipc_answer_0(rid, rc);
520 } else
521 ipc_answer_0(rid, ENOTSUP);
522}
523
524void devfs_destroy(ipc_callid_t rid, ipc_call_t *request)
525{
526 ipc_answer_0(rid, ENOTSUP);
527}
528
529/**
530 * @}
531 */
Note: See TracBrowser for help on using the repository browser.