/kernel/trunk/generic/include/errno.h |
---|
37,5 → 37,8 |
#define EREFUSED -4 /* Connection refused */ |
#define EFORWARD -5 /* Forward error */ |
#define EPERM -6 /* Permission denied */ |
#define EHANGUP -7 /* Answerbox closed cionnection, call sys_ipc_hangup |
* to close the connection. Used by answerbox |
* to close the connection. */ |
#endif |
/kernel/trunk/generic/include/ipc/ipc.h |
---|
42,6 → 42,7 |
#define IPC_CALL_DISPATCHED (1<<2) /**< Call is in dispatch queue */ |
#define IPC_CALL_DISCARD_ANSWER (1<<3) /**< Answer will not be passed to |
* userspace, will be discarded */ |
#define IPC_CALL_FORWARDED (1<<4) /* Call was forwarded */ |
/* Flags for ipc_wait_for_call */ |
#define IPC_WAIT_NONBLOCKING 1 |
155,11 → 156,17 |
link_t answers; /**< Answered calls */ |
}; |
typedef enum { |
IPC_BUSY_FREE = 0, |
IPC_BUSY_CONNECTING, |
IPC_BUSY_CONNECTED |
} ipc_busy_t; |
struct phone_s { |
SPINLOCK_DECLARE(lock); |
link_t list; |
answerbox_t *callee; |
int busy; |
ipc_busy_t busy; |
atomic_t active_calls; |
}; |
181,7 → 188,7 |
extern void ipc_init(void); |
extern call_t * ipc_wait_for_call(answerbox_t *box, int flags); |
extern void ipc_answer(answerbox_t *box, call_t *request); |
extern void ipc_call(phone_t *phone, call_t *request); |
extern int ipc_call(phone_t *phone, call_t *call); |
extern void ipc_call_sync(phone_t *phone, call_t *request); |
extern void ipc_phone_init(phone_t *phone); |
extern void ipc_phone_connect(phone_t *phone, answerbox_t *box); |
190,7 → 197,7 |
extern void ipc_answerbox_init(answerbox_t *box); |
extern void ipc_call_static_init(call_t *call); |
extern void task_print_list(void); |
extern void ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox); |
extern int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox); |
extern answerbox_t *ipc_phone_0; |
extern void ipc_cleanup(task_t *task); |
/kernel/trunk/generic/src/ipc/ipcrsc.c |
---|
70,23 → 70,37 |
* IPC_M_PHONE_HUNGUP call from the phone that hung up. When all async |
* calls are answered, the phone is deallocated. |
* |
* *** The answerbox hangs up (ipc_answer(ESLAM)) |
* - The phone is disconnected. IPC_M_ANSWERBOX_HUNGUP notification |
* is sent to source task, the calling process is expected to |
* *** The answerbox hangs up (ipc_answer(EHANGUP)) |
* - The phone is disconnected. EHANGUP response code is sent |
* to the calling process. All new calls through this phone |
* get a EHUNGUP error code, the task is expected to |
* send an sys_ipc_hangup after cleaning up it's internal structures. |
* |
* Call forwarding |
* |
* The call can be forwarded, so that the answer to call is passed directly |
* to the original sender. However, this poses special problems regarding |
* routing of hangup messages. |
* |
* sys_ipc_hangup -> IPC_M_PHONE_HUNGUP |
* - this message CANNOT be forwarded |
* |
* EHANGUP during forward |
* - The *forwarding* phone will be closed, EFORWARD is sent to receiver. |
* |
* EHANGUP, ENOENT during forward |
* - EFORWARD is sent to the receiver, ipc_forward returns error code EFORWARD |
* |
* Cleanup strategy |
* |
* 1) Disconnect all our phones ('sys_ipc_hangup') |
* 1) Disconnect all our phones ('ipc_phone_hangup'). |
* |
* 2) Disconnect all phones connected to answerbox. |
* * Send message 'PHONE_DISCONNECTED' to the target application |
* - Once all phones are disconnected, no further calls can arrive |
* |
* 3) Answer all messages in 'calls' and 'dispatched_calls' queues with |
* appropriate error code. |
* appropriate error code (EHANGUP, EFORWARD). |
* |
* 4) Wait for all async answers to arrive |
* 4) Wait for all async answers to arrive. |
* |
*/ |
116,8 → 130,8 |
spinlock_lock(&TASK->lock); |
for (i=0; i < IPC_MAX_PHONES; i++) { |
if (!TASK->phones[i].busy && !atomic_get(&TASK->phones[i].active_calls)) { |
TASK->phones[i].busy = 1; |
if (TASK->phones[i].busy==IPC_BUSY_FREE && !atomic_get(&TASK->phones[i].active_calls)) { |
TASK->phones[i].busy = IPC_BUSY_CONNECTING; |
break; |
} |
} |
136,10 → 150,10 |
{ |
spinlock_lock(&TASK->lock); |
ASSERT(TASK->phones[phoneid].busy); |
ASSERT(TASK->phones[phoneid].busy == IPC_BUSY_CONNECTING); |
ASSERT(! TASK->phones[phoneid].callee); |
TASK->phones[phoneid].busy = 0; |
TASK->phones[phoneid].busy = IPC_BUSY_FREE; |
spinlock_unlock(&TASK->lock); |
} |
155,6 → 169,6 |
{ |
phone_t *phone = &TASK->phones[phoneid]; |
ASSERT(phone->busy); |
ASSERT(phone->busy == IPC_BUSY_CONNECTING); |
ipc_phone_connect(phone, box); |
} |
/kernel/trunk/generic/src/ipc/sysipc.c |
---|
75,9 → 75,9 |
*/ |
/** Return true if the caller (ipc_answer) should save |
* the old call contents and call answer_preprocess |
* the old call contents for answer_preprocess |
*/ |
static inline int answer_will_preprocess(call_t *call) |
static inline int answer_need_old(call_t *call) |
{ |
if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_TO_ME) |
return 1; |
91,6 → 91,14 |
{ |
int phoneid; |
if (IPC_GET_RETVAL(answer->data) == EHANGUP) { |
/* Atomic operation */ |
answer->data.phone->callee = NULL; |
} |
if (!olddata) |
return; |
if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_TO_ME) { |
phoneid = IPC_GET_ARG3(*olddata); |
if (IPC_GET_RETVAL(answer->data)) { |
117,6 → 125,9 |
/** Do basic kernel processing of received call answer */ |
static int process_answer(answerbox_t *box,call_t *call) |
{ |
if (IPC_GET_RETVAL(call->data) == EHANGUP && \ |
call->flags & IPC_CALL_FORWARDED) |
IPC_SET_RETVAL(call->data, EFORWARD); |
return 0; |
} |
275,6 → 286,8 |
if (!call) |
return ENOENT; |
call->flags |= IPC_CALL_FORWARDED; |
GET_CHECK_PHONE(phone, phoneid, { |
IPC_SET_RETVAL(call->data, EFORWARD); |
ipc_answer(&TASK->answerbox, call); |
298,9 → 311,7 |
IPC_SET_ARG1(call->data, arg1); |
} |
ipc_forward(call, phone, &TASK->answerbox); |
return 0; |
return ipc_forward(call, phone, &TASK->answerbox); |
} |
/** Send IPC answer */ |
309,24 → 320,22 |
{ |
call_t *call; |
ipc_data_t saved_data; |
int preprocess = 0; |
int saveddata = 0; |
call = get_call(callid); |
if (!call) |
return ENOENT; |
if (answer_will_preprocess(call)) { |
if (answer_need_old(call)) { |
memcpy(&saved_data, &call->data, sizeof(call->data)); |
preprocess = 1; |
saveddata = 1; |
} |
IPC_SET_RETVAL(call->data, retval); |
IPC_SET_ARG1(call->data, arg1); |
IPC_SET_ARG2(call->data, arg2); |
answer_preprocess(call, saveddata ? &saved_data : NULL); |
if (preprocess) |
answer_preprocess(call, &saved_data); |
ipc_answer(&TASK->answerbox, call); |
return 0; |
} |
336,21 → 345,20 |
{ |
call_t *call; |
ipc_data_t saved_data; |
int preprocess = 0; |
int saveddata = 0; |
call = get_call(callid); |
if (!call) |
return ENOENT; |
if (answer_will_preprocess(call)) { |
if (answer_need_old(call)) { |
memcpy(&saved_data, &call->data, sizeof(call->data)); |
preprocess = 1; |
saveddata = 1; |
} |
copy_from_uspace(&call->data.args, &data->args, |
sizeof(call->data.args)); |
if (preprocess) |
answer_preprocess(call, &saved_data); |
answer_preprocess(call, saveddata ? &saved_data : NULL); |
ipc_answer(&TASK->answerbox, call); |
448,6 → 456,8 |
restart: |
call = ipc_wait_for_call(&TASK->answerbox, flags); |
if (!call) |
return 0; |
if (call->flags & IPC_CALL_ANSWERED) { |
if (process_answer(&TASK->answerbox, call)) |
/kernel/trunk/generic/src/ipc/ipc.c |
---|
104,7 → 104,7 |
spinlock_lock(&phone->lock); |
ASSERT(!phone->callee); |
phone->busy = 1; |
phone->busy = IPC_BUSY_CONNECTED; |
phone->callee = box; |
spinlock_lock(&box->lock); |
120,7 → 120,8 |
{ |
spinlock_initialize(&phone->lock, "phone_lock"); |
phone->callee = NULL; |
phone->busy = 0; |
phone->busy = IPC_BUSY_FREE; |
atomic_set(&phone->active_calls, 0); |
} |
/** Helper function to facilitate synchronous calls */ |
171,8 → 172,10 |
/* Unsafe unchecking ipc_call */ |
static void _ipc_call(phone_t *phone, answerbox_t *box, call_t *call) |
{ |
if (! (call->flags & IPC_CALL_FORWARDED)) { |
atomic_inc(&phone->active_calls); |
call->data.phone = phone; |
} |
spinlock_lock(&box->lock); |
list_append(&call->list, &box->calls); |
185,7 → 188,7 |
* @param phone Phone connected to answerbox |
* @param request Request to be sent |
*/ |
void ipc_call(phone_t *phone, call_t *call) |
int ipc_call(phone_t *phone, call_t *call) |
{ |
answerbox_t *box; |
195,15 → 198,24 |
if (!box) { |
/* Trying to send over disconnected phone */ |
spinlock_unlock(&phone->lock); |
if (call->flags & IPC_CALL_FORWARDED) { |
IPC_SET_RETVAL(call->data, EFORWARD); |
} else { /* Simulate sending a message */ |
call->data.phone = phone; |
atomic_inc(&phone->active_calls); |
if (phone->busy == IPC_BUSY_CONNECTED) |
IPC_SET_RETVAL(call->data, EHANGUP); |
else |
IPC_SET_RETVAL(call->data, ENOENT); |
} |
_ipc_answer_free_call(call); |
return; |
return ENOENT; |
} |
_ipc_call(phone, box, call); |
spinlock_unlock(&phone->lock); |
return 0; |
} |
/** Disconnect phone from answerbox |
220,9 → 232,15 |
spinlock_lock(&phone->lock); |
box = phone->callee; |
if (!box) { |
if (phone->busy == IPC_BUSY_CONNECTING) { |
spinlock_unlock(&phone->lock); |
return -1; |
} |
/* Already disconnected phone */ |
phone->busy = IPC_BUSY_FREE; |
spinlock_unlock(&phone->lock); |
return 0; |
} |
spinlock_lock(&box->lock); |
list_remove(&phone->list); |
234,7 → 252,7 |
call->flags |= IPC_CALL_DISCARD_ANSWER; |
_ipc_call(phone, box, call); |
phone->busy = 0; |
phone->busy = IPC_BUSY_FREE; |
spinlock_unlock(&phone->lock); |
246,15 → 264,18 |
* @param request Request to be forwarded |
* @param newbox Target answerbox |
* @param oldbox Old answerbox |
* @return 0 on forward ok, error code, if there was error |
* |
* - the return value serves only as an information for the forwarder, |
* the original caller is notified automatically with EFORWARD |
*/ |
void ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox) |
int ipc_forward(call_t *call, phone_t *newphone, answerbox_t *oldbox) |
{ |
spinlock_lock(&oldbox->lock); |
atomic_dec(&call->data.phone->active_calls); |
list_remove(&call->list); |
spinlock_unlock(&oldbox->lock); |
ipc_call(newphone, call); |
return ipc_call(newphone, call); |
} |
279,8 → 300,6 |
/* Handle asynchronous answers */ |
request = list_get_instance(box->answers.next, call_t, list); |
list_remove(&request->list); |
printf("%d %P\n", IPC_GET_METHOD(request->data), |
request->data.phone); |
atomic_dec(&request->data.phone->active_calls); |
} else if (!list_empty(&box->calls)) { |
/* Handle requests */ |
313,6 → 332,15 |
*/ |
void ipc_cleanup(task_t *task) |
{ |
/* Cancel all calls in my dispatch queue */ |
int i; |
/* Disconnect all our phones ('ipc_phone_hangup') */ |
for (i=0;i < IPC_MAX_PHONES; i++) |
ipc_phone_hangup(&task->phones[i]); |
/* Disconnect all phones connected to answerbox */ |
/* Answer all messages in 'calls' and 'dispatched_calls' queues */ |
/* Wait for all async answers to arrive */ |
} |