41,13 → 41,56 |
#include <malloc.h> |
#include <string.h> |
#include <libfs.h> |
#include <adt/hash_table.h> |
#include "devfs.h" |
#include "devfs_ops.h" |
|
#define PLB_GET_CHAR(pos) (devfs_reg.plb_ro[pos % PLB_SIZE]) |
|
/** Opened devices structure */ |
typedef struct { |
dev_handle_t handle; |
int phone; |
size_t refcount; |
link_t link; |
} device_t; |
|
/** Hash table of opened devices */ |
static hash_table_t devices; |
|
#define DEVICES_KEYS 1 |
#define DEVICES_KEY_HANDLE 0 |
#define DEVICES_BUCKETS 256 |
|
/* Implementation of hash table interface for the nodes hash table. */ |
static hash_index_t devices_hash(unsigned long key[]) |
{ |
return key[DEVICES_KEY_HANDLE] % DEVICES_BUCKETS; |
} |
|
static int devices_compare(unsigned long key[], hash_count_t keys, link_t *item) |
{ |
device_t *dev = hash_table_get_instance(item, device_t, link); |
return (dev->handle == (dev_handle_t) key[DEVICES_KEY_HANDLE]); |
} |
|
static void devices_remove_callback(link_t *item) |
{ |
free(hash_table_get_instance(item, device_t, link)); |
} |
|
static hash_table_operations_t devices_ops = { |
.hash = devices_hash, |
.compare = devices_compare, |
.remove_callback = devices_remove_callback |
}; |
|
bool devfs_init(void) |
{ |
if (!hash_table_create(&devices, DEVICES_BUCKETS, |
DEVICES_KEYS, &devices_ops)) |
return false; |
|
if (devmap_get_phone(DEVMAP_CLIENT, IPC_FLAG_BLOCKING) < 0) |
return false; |
|
103,8 → 146,7 |
return; |
} |
|
/* This is a read-only filesystem */ |
if ((lflag & L_CREATE) || (lflag & L_LINK) || (lflag & L_UNLINK)) { |
if ((lflag & L_LINK) || (lflag & L_UNLINK)) { |
ipc_answer_0(rid, ENOTSUP); |
return; |
} |
123,7 → 165,7 |
ipc_answer_0(rid, ENOENT); |
} else { |
if (lflag & L_FILE) { |
count_t len; |
size_t len; |
if (last >= first) |
len = last - first + 1; |
else |
135,7 → 177,7 |
return; |
} |
|
count_t i; |
size_t i; |
for (i = 0; i < len; i++) |
name[i] = PLB_GET_CHAR(first + i); |
|
148,6 → 190,38 |
return; |
} |
|
if (lflag & L_OPEN) { |
unsigned long key[] = { |
[DEVICES_KEY_HANDLE] = (unsigned long) handle |
}; |
|
link_t *lnk = hash_table_find(&devices, key); |
if (lnk == NULL) { |
int phone = devmap_device_connect(handle, 0); |
if (phone < 0) { |
free(name); |
ipc_answer_0(rid, ENOENT); |
return; |
} |
|
device_t *dev = (device_t *) malloc(sizeof(device_t)); |
if (dev == NULL) { |
free(name); |
ipc_answer_0(rid, ENOMEM); |
return; |
} |
|
dev->handle = handle; |
dev->phone = phone; |
dev->refcount = 1; |
|
hash_table_insert(&devices, key, &dev->link); |
} else { |
device_t *dev = hash_table_get_instance(lnk, device_t, link); |
dev->refcount++; |
} |
} |
|
free(name); |
|
ipc_answer_5(rid, EOK, devfs_reg.fs_handle, dev_handle, handle, 0, 1); |
156,6 → 230,61 |
} |
} |
|
void devfs_open_node(ipc_callid_t rid, ipc_call_t *request) |
{ |
dev_handle_t handle = IPC_GET_ARG2(*request); |
|
unsigned long key[] = { |
[DEVICES_KEY_HANDLE] = (unsigned long) handle |
}; |
|
link_t *lnk = hash_table_find(&devices, key); |
if (lnk == NULL) { |
int phone = devmap_device_connect(handle, 0); |
if (phone < 0) { |
ipc_answer_0(rid, ENOENT); |
return; |
} |
|
device_t *dev = (device_t *) malloc(sizeof(device_t)); |
if (dev == NULL) { |
ipc_answer_0(rid, ENOMEM); |
return; |
} |
|
dev->handle = handle; |
dev->phone = phone; |
dev->refcount = 1; |
|
hash_table_insert(&devices, key, &dev->link); |
} else { |
device_t *dev = hash_table_get_instance(lnk, device_t, link); |
dev->refcount++; |
} |
|
ipc_answer_3(rid, EOK, 0, 1, L_FILE); |
} |
|
void devfs_device(ipc_callid_t rid, ipc_call_t *request) |
{ |
fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request); |
|
if (index != 0) { |
unsigned long key[] = { |
[DEVICES_KEY_HANDLE] = (unsigned long) index |
}; |
|
link_t *lnk = hash_table_find(&devices, key); |
if (lnk == NULL) { |
ipc_answer_0(rid, ENOENT); |
return; |
} |
|
ipc_answer_1(rid, EOK, (ipcarg_t) index); |
} else |
ipc_answer_0(rid, ENOTSUP); |
} |
|
void devfs_read(ipc_callid_t rid, ipc_call_t *request) |
{ |
fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request); |
162,34 → 291,59 |
off_t pos = (off_t) IPC_GET_ARG3(*request); |
|
if (index != 0) { |
ipc_answer_1(rid, ENOENT, 0); |
return; |
} |
|
/* |
* Receive the read request. |
*/ |
ipc_callid_t callid; |
size_t size; |
if (!ipc_data_read_receive(&callid, &size)) { |
ipc_answer_0(callid, EINVAL); |
ipc_answer_0(rid, EINVAL); |
return; |
} |
|
size_t bytes = 0; |
if (index != 0) { |
(void) ipc_data_read_finalize(callid, NULL, bytes); |
unsigned long key[] = { |
[DEVICES_KEY_HANDLE] = (unsigned long) index |
}; |
|
link_t *lnk = hash_table_find(&devices, key); |
if (lnk == NULL) { |
ipc_answer_0(rid, ENOENT); |
return; |
} |
|
device_t *dev = hash_table_get_instance(lnk, device_t, link); |
|
ipc_callid_t callid; |
if (!ipc_data_read_receive(&callid, NULL)) { |
ipc_answer_0(callid, EINVAL); |
ipc_answer_0(rid, EINVAL); |
return; |
} |
|
/* Make a request at the driver */ |
ipc_call_t answer; |
aid_t msg = async_send_3(dev->phone, IPC_GET_METHOD(*request), |
IPC_GET_ARG1(*request), IPC_GET_ARG2(*request), |
IPC_GET_ARG3(*request), &answer); |
|
/* Forward the IPC_M_DATA_READ request to the driver */ |
ipc_forward_fast(callid, dev->phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME); |
|
/* Wait for reply from the driver. */ |
ipcarg_t rc; |
async_wait_for(msg, &rc); |
size_t bytes = IPC_GET_ARG1(answer); |
|
/* Driver reply is the final result of the whole operation */ |
ipc_answer_1(rid, rc, bytes); |
} else { |
count_t count = devmap_device_get_count(); |
ipc_callid_t callid; |
size_t size; |
if (!ipc_data_read_receive(&callid, &size)) { |
ipc_answer_0(callid, EINVAL); |
ipc_answer_0(rid, EINVAL); |
return; |
} |
|
size_t count = devmap_device_get_count(); |
dev_desc_t *desc = malloc(count * sizeof(dev_desc_t)); |
if (desc == NULL) { |
ipc_answer_0(callid, ENOENT); |
ipc_answer_1(rid, ENOENT, 0); |
ipc_answer_0(callid, ENOMEM); |
ipc_answer_1(rid, ENOMEM, 0); |
return; |
} |
|
count_t max = devmap_device_get_devices(count, desc); |
size_t max = devmap_device_get_devices(count, desc); |
|
if (pos < max) { |
ipc_data_read_finalize(callid, desc[pos].name, str_size(desc[pos].name) + 1); |
200,10 → 354,9 |
} |
|
free(desc); |
bytes = 1; |
|
ipc_answer_1(rid, EOK, 1); |
} |
|
ipc_answer_1(rid, EOK, bytes); |
} |
|
void devfs_write(ipc_callid_t rid, ipc_call_t *request) |
211,20 → 364,46 |
fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request); |
off_t pos = (off_t) IPC_GET_ARG3(*request); |
|
/* |
* Receive the write request. |
*/ |
ipc_callid_t callid; |
size_t size; |
if (!ipc_data_write_receive(&callid, &size)) { |
ipc_answer_0(callid, EINVAL); |
ipc_answer_0(rid, EINVAL); |
return; |
if (index != 0) { |
unsigned long key[] = { |
[DEVICES_KEY_HANDLE] = (unsigned long) index |
}; |
|
link_t *lnk = hash_table_find(&devices, key); |
if (lnk == NULL) { |
ipc_answer_0(rid, ENOENT); |
return; |
} |
|
device_t *dev = hash_table_get_instance(lnk, device_t, link); |
|
ipc_callid_t callid; |
if (!ipc_data_write_receive(&callid, NULL)) { |
ipc_answer_0(callid, EINVAL); |
ipc_answer_0(rid, EINVAL); |
return; |
} |
|
/* Make a request at the driver */ |
ipc_call_t answer; |
aid_t msg = async_send_3(dev->phone, IPC_GET_METHOD(*request), |
IPC_GET_ARG1(*request), IPC_GET_ARG2(*request), |
IPC_GET_ARG3(*request), &answer); |
|
/* Forward the IPC_M_DATA_WRITE request to the driver */ |
ipc_forward_fast(callid, dev->phone, 0, 0, 0, IPC_FF_ROUTE_FROM_ME); |
|
/* Wait for reply from the driver. */ |
ipcarg_t rc; |
async_wait_for(msg, &rc); |
size_t bytes = IPC_GET_ARG1(answer); |
|
/* Driver reply is the final result of the whole operation */ |
ipc_answer_1(rid, rc, bytes); |
} else { |
/* Read-only filesystem */ |
ipc_answer_0(rid, ENOTSUP); |
} |
|
// TODO |
ipc_answer_0(callid, ENOENT); |
ipc_answer_2(rid, ENOENT, 0, 0); |
} |
|
void devfs_truncate(ipc_callid_t rid, ipc_call_t *request) |
232,6 → 411,66 |
ipc_answer_0(rid, ENOTSUP); |
} |
|
void devfs_close(ipc_callid_t rid, ipc_call_t *request) |
{ |
fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request); |
|
if (index != 0) { |
unsigned long key[] = { |
[DEVICES_KEY_HANDLE] = (unsigned long) index |
}; |
|
link_t *lnk = hash_table_find(&devices, key); |
if (lnk == NULL) { |
ipc_answer_0(rid, ENOENT); |
return; |
} |
|
device_t *dev = hash_table_get_instance(lnk, device_t, link); |
dev->refcount--; |
|
if (dev->refcount == 0) { |
ipc_hangup(dev->phone); |
hash_table_remove(&devices, key, DEVICES_KEYS); |
} |
|
ipc_answer_0(rid, EOK); |
} else |
ipc_answer_0(rid, ENOTSUP); |
} |
|
void devfs_sync(ipc_callid_t rid, ipc_call_t *request) |
{ |
fs_index_t index = (fs_index_t) IPC_GET_ARG2(*request); |
|
if (index != 0) { |
unsigned long key[] = { |
[DEVICES_KEY_HANDLE] = (unsigned long) index |
}; |
|
link_t *lnk = hash_table_find(&devices, key); |
if (lnk == NULL) { |
ipc_answer_0(rid, ENOENT); |
return; |
} |
|
device_t *dev = hash_table_get_instance(lnk, device_t, link); |
|
/* Make a request at the driver */ |
ipc_call_t answer; |
aid_t msg = async_send_2(dev->phone, IPC_GET_METHOD(*request), |
IPC_GET_ARG1(*request), IPC_GET_ARG2(*request), &answer); |
|
/* Wait for reply from the driver */ |
ipcarg_t rc; |
async_wait_for(msg, &rc); |
|
/* Driver reply is the final result of the whole operation */ |
ipc_answer_0(rid, rc); |
} else |
ipc_answer_0(rid, ENOTSUP); |
} |
|
void devfs_destroy(ipc_callid_t rid, ipc_call_t *request) |
{ |
ipc_answer_0(rid, ENOTSUP); |
239,4 → 478,4 |
|
/** |
* @} |
*/ |
*/ |