1
/* $Id: ioqueue_symbian.cpp 4374 2013-02-27 07:15:57Z riza $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pj/ioqueue.h>
21
#include <pj/assert.h>
26
#include <pj/string.h>
28
#include "os_symbian.h"
30
class CIoqueueCallback;
41
/////////////////////////////////////////////////////////////////////////////
42
// Class to encapsulate asynchronous socket operation.
44
class CIoqueueCallback : public CActive
47
static CIoqueueCallback* NewL(pj_ioqueue_t *ioqueue,
48
pj_ioqueue_key_t *key,
50
const pj_ioqueue_callback *cb,
54
// Start asynchronous recv() operation
56
pj_status_t StartRead(pj_ioqueue_op_key_t *op_key,
57
void *buf, pj_ssize_t *size, unsigned flags,
58
pj_sockaddr_t *addr, int *addrlen);
61
// Start asynchronous accept() operation.
63
pj_status_t StartAccept(pj_ioqueue_op_key_t *op_key,
66
pj_sockaddr_t *remote,
70
// Completion callback.
75
// CActive's DoCancel()
80
// Cancel operation and call callback.
82
void CancelOperation(pj_ioqueue_op_key_t *op_key,
83
pj_ssize_t bytes_status);
88
void* get_user_data() const
92
void set_user_data(void *user_data)
94
user_data_ = user_data;
96
pj_ioqueue_op_key_t *get_op_key() const
98
return pending_data_.common_.op_key_;
100
CPjSocket* get_pj_socket()
106
// Type of pending operation.
114
pj_ioqueue_t *ioqueue_;
115
pj_ioqueue_key_t *key_;
117
pj_ioqueue_callback cb_;
131
pj_ioqueue_op_key_t *op_key_;
136
pj_ioqueue_op_key_t *op_key_;
137
pj_sockaddr_t *addr_;
141
struct Pending_Accept
143
pj_ioqueue_op_key_t *op_key_;
144
pj_sock_t *new_sock_;
145
pj_sockaddr_t *local_;
146
pj_sockaddr_t *remote_;
151
union Pending_Data pending_data_;
154
CIoqueueCallback(pj_ioqueue_t *ioqueue,
155
pj_ioqueue_key_t *key, pj_sock_t sock,
156
const pj_ioqueue_callback *cb, void *user_data)
157
: CActive(CActive::EPriorityStandard),
158
ioqueue_(ioqueue), key_(key), sock_((CPjSocket*)sock),
159
user_data_(user_data), aBufferPtr_(NULL, 0), type_(TYPE_NONE)
161
pj_memcpy(&cb_, cb, sizeof(*cb));
167
CActiveScheduler::Add(this);
170
void HandleReadCompletion();
171
CPjSocket *HandleAcceptCompletion();
175
CIoqueueCallback* CIoqueueCallback::NewL(pj_ioqueue_t *ioqueue,
176
pj_ioqueue_key_t *key,
178
const pj_ioqueue_callback *cb,
181
CIoqueueCallback *self = new CIoqueueCallback(ioqueue, key, sock,
183
CleanupStack::PushL(self);
185
CleanupStack::Pop(self);
192
// Start asynchronous recv() operation
194
pj_status_t CIoqueueCallback::StartRead(pj_ioqueue_op_key_t *op_key,
195
void *buf, pj_ssize_t *size,
197
pj_sockaddr_t *addr, int *addrlen)
199
PJ_ASSERT_RETURN(IsActive()==false, PJ_EBUSY);
200
PJ_ASSERT_RETURN(pending_data_.common_.op_key_==NULL, PJ_EBUSY);
202
flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC;
204
pending_data_.read_.op_key_ = op_key;
205
pending_data_.read_.addr_ = addr;
206
pending_data_.read_.addrlen_ = addrlen;
208
aBufferPtr_.Set((TUint8*)buf, 0, (TInt)*size);
211
if (addr && addrlen) {
212
sock_->Socket().RecvFrom(aBufferPtr_, aAddress_, flags, iStatus);
214
aAddress_.SetAddress(0);
215
aAddress_.SetPort(0);
217
if (sock_->IsDatagram()) {
218
sock_->Socket().Recv(aBufferPtr_, flags, iStatus);
220
// Using static like this is not pretty, but we don't need to use
221
// the value anyway, hence doing it like this is probably most
223
static TSockXfrLength len;
224
sock_->Socket().RecvOneOrMore(aBufferPtr_, flags, iStatus, len);
234
// Start asynchronous accept() operation.
236
pj_status_t CIoqueueCallback::StartAccept(pj_ioqueue_op_key_t *op_key,
238
pj_sockaddr_t *local,
239
pj_sockaddr_t *remote,
242
PJ_ASSERT_RETURN(IsActive()==false, PJ_EBUSY);
243
PJ_ASSERT_RETURN(pending_data_.common_.op_key_==NULL, PJ_EBUSY);
245
// addrlen must be specified if local or remote is specified
246
PJ_ASSERT_RETURN((!local && !remote) ||
247
(addrlen && *addrlen), PJ_EINVAL);
249
pending_data_.accept_.op_key_ = op_key;
250
pending_data_.accept_.new_sock_ = new_sock;
251
pending_data_.accept_.local_ = local;
252
pending_data_.accept_.remote_ = remote;
253
pending_data_.accept_.addrlen_ = addrlen;
255
// Create blank socket
256
blank_sock_.Open(PjSymbianOS::Instance()->SocketServ());
259
sock_->Socket().Accept(blank_sock_, iStatus);
267
// Handle asynchronous RecvFrom() completion
269
void CIoqueueCallback::HandleReadCompletion()
271
if (pending_data_.read_.addr_ && pending_data_.read_.addrlen_) {
272
PjSymbianOS::Addr2pj(aAddress_,
273
*(pj_sockaddr*)pending_data_.read_.addr_,
274
pending_data_.read_.addrlen_);
275
pending_data_.read_.addr_ = NULL;
276
pending_data_.read_.addrlen_ = NULL;
279
pending_data_.read_.op_key_ = NULL;
284
// Handle asynchronous Accept() completion.
286
CPjSocket *CIoqueueCallback::HandleAcceptCompletion()
288
CPjSocket *pjNewSock = new CPjSocket(get_pj_socket()->GetAf(),
289
get_pj_socket()->GetSockType(),
293
if (pending_data_.accept_.new_sock_) {
294
*pending_data_.accept_.new_sock_ = (pj_sock_t)pjNewSock;
295
pending_data_.accept_.new_sock_ = NULL;
298
if (pending_data_.accept_.local_) {
300
pj_sockaddr *ptr_sockaddr;
302
blank_sock_.LocalName(aAddr);
303
ptr_sockaddr = (pj_sockaddr*)pending_data_.accept_.local_;
304
addrlen = *pending_data_.accept_.addrlen_;
305
PjSymbianOS::Addr2pj(aAddr, *ptr_sockaddr, &addrlen);
306
pending_data_.accept_.local_ = NULL;
309
if (pending_data_.accept_.remote_) {
311
pj_sockaddr *ptr_sockaddr;
313
blank_sock_.RemoteName(aAddr);
314
ptr_sockaddr = (pj_sockaddr*)pending_data_.accept_.remote_;
315
addrlen = *pending_data_.accept_.addrlen_;
316
PjSymbianOS::Addr2pj(aAddr, *ptr_sockaddr, &addrlen);
317
pending_data_.accept_.remote_ = NULL;
320
if (pending_data_.accept_.addrlen_) {
322
if (pjNewSock->GetAf() == PJ_AF_INET)
323
addrlen = sizeof(pj_sockaddr_in);
324
else if (pjNewSock->GetAf() == PJ_AF_INET6)
325
addrlen = sizeof(pj_sockaddr_in6);
327
pj_assert(!"Unsupported address family");
330
*pending_data_.accept_.addrlen_ = addrlen;
331
pending_data_.accept_.addrlen_ = NULL;
339
// Completion callback.
341
void CIoqueueCallback::RunL()
343
pj_ioqueue_t *ioq = ioqueue_;
344
Type cur_type = type_;
348
if (cur_type == TYPE_READ) {
350
// Completion of asynchronous RecvFrom()
353
/* Clear op_key (save it to temp variable first!) */
354
pj_ioqueue_op_key_t *op_key = pending_data_.read_.op_key_;
355
pending_data_.read_.op_key_ = NULL;
357
// Handle failure condition
358
if (iStatus != KErrNone) {
359
if (cb_.on_read_complete) {
360
cb_.on_read_complete( key_, op_key,
361
-PJ_RETURN_OS_ERROR(iStatus.Int()));
366
HandleReadCompletion();
369
if (cb_.on_read_complete) {
370
cb_.on_read_complete(key_, op_key, aBufferPtr_.Length());
373
} else if (cur_type == TYPE_ACCEPT) {
375
// Completion of asynchronous Accept()
378
/* Clear op_key (save it to temp variable first!) */
379
pj_ioqueue_op_key_t *op_key = pending_data_.read_.op_key_;
380
pending_data_.read_.op_key_ = NULL;
382
// Handle failure condition
383
if (iStatus != KErrNone) {
384
if (pending_data_.accept_.new_sock_)
385
*pending_data_.accept_.new_sock_ = PJ_INVALID_SOCKET;
387
if (cb_.on_accept_complete) {
388
cb_.on_accept_complete( key_, op_key, PJ_INVALID_SOCKET,
389
-PJ_RETURN_OS_ERROR(iStatus.Int()));
394
CPjSocket *pjNewSock = HandleAcceptCompletion();
397
if (cb_.on_accept_complete) {
398
cb_.on_accept_complete( key_, op_key, (pj_sock_t)pjNewSock,
407
// CActive's DoCancel()
409
void CIoqueueCallback::DoCancel()
411
if (type_ == TYPE_READ)
412
sock_->Socket().CancelRecv();
413
else if (type_ == TYPE_ACCEPT)
414
sock_->Socket().CancelAccept();
417
pending_data_.common_.op_key_ = NULL;
421
// Cancel operation and call callback.
423
void CIoqueueCallback::CancelOperation(pj_ioqueue_op_key_t *op_key,
424
pj_ssize_t bytes_status)
426
Type cur_type = type_;
428
pj_assert(op_key == pending_data_.common_.op_key_);
432
if (cur_type == TYPE_READ) {
433
if (cb_.on_read_complete)
434
cb_.on_read_complete(key_, op_key, bytes_status);
435
} else if (cur_type == TYPE_ACCEPT)
440
/////////////////////////////////////////////////////////////////////////////
442
* IO Queue key structure.
444
struct pj_ioqueue_key_t
446
CIoqueueCallback *cbObj;
451
* Return the name of the ioqueue implementation.
453
PJ_DEF(const char*) pj_ioqueue_name(void)
455
return "ioqueue-symbian";
460
* Create a new I/O Queue framework.
462
PJ_DEF(pj_status_t) pj_ioqueue_create( pj_pool_t *pool,
464
pj_ioqueue_t **p_ioqueue)
468
PJ_UNUSED_ARG(max_fd);
470
ioq = PJ_POOL_ZALLOC_T(pool, pj_ioqueue_t);
477
* Destroy the I/O queue.
479
PJ_DEF(pj_status_t) pj_ioqueue_destroy( pj_ioqueue_t *ioq )
487
* Set the lock object to be used by the I/O Queue.
489
PJ_DEF(pj_status_t) pj_ioqueue_set_lock( pj_ioqueue_t *ioq,
491
pj_bool_t auto_delete )
493
/* Don't really need lock for now */
497
pj_lock_destroy(lock);
503
PJ_DEF(pj_status_t) pj_ioqueue_set_default_concurrency(pj_ioqueue_t *ioqueue,
506
/* Not supported, just return PJ_SUCCESS silently */
507
PJ_UNUSED_ARG(ioqueue);
508
PJ_UNUSED_ARG(allow);
513
* Register a socket to the I/O queue framework.
515
PJ_DEF(pj_status_t) pj_ioqueue_register_sock( pj_pool_t *pool,
519
const pj_ioqueue_callback *cb,
520
pj_ioqueue_key_t **p_key )
522
pj_ioqueue_key_t *key;
524
key = PJ_POOL_ZALLOC_T(pool, pj_ioqueue_key_t);
525
key->cbObj = CIoqueueCallback::NewL(ioq, key, sock, cb, user_data);
531
PJ_DEF(pj_status_t) pj_ioqueue_register_sock2(pj_pool_t *pool,
532
pj_ioqueue_t *ioqueue,
534
pj_grp_lock_t *grp_lock,
536
const pj_ioqueue_callback *cb,
537
pj_ioqueue_key_t **p_key)
539
PJ_UNUSED_ARG(grp_lock);
541
return pj_ioqueue_register_sock(pool, ioqueue, sock, user_data, cb, p_key);
545
* Unregister from the I/O Queue framework.
547
PJ_DEF(pj_status_t) pj_ioqueue_unregister( pj_ioqueue_key_t *key )
549
if (key == NULL || key->cbObj == NULL)
552
// Cancel pending async object
554
key->cbObj->Cancel();
558
key->cbObj->get_pj_socket()->Socket().Close();
559
delete key->cbObj->get_pj_socket();
561
// Delete async object.
572
* Get user data associated with an ioqueue key.
574
PJ_DEF(void*) pj_ioqueue_get_user_data( pj_ioqueue_key_t *key )
576
return key->cbObj->get_user_data();
581
* Set or change the user data to be associated with the file descriptor or
582
* handle or socket descriptor.
584
PJ_DEF(pj_status_t) pj_ioqueue_set_user_data( pj_ioqueue_key_t *key,
589
*old_data = key->cbObj->get_user_data();
590
key->cbObj->set_user_data(user_data);
597
* Initialize operation key.
599
PJ_DEF(void) pj_ioqueue_op_key_init( pj_ioqueue_op_key_t *op_key,
602
pj_bzero(op_key, size);
607
* Check if operation is pending on the specified operation key.
609
PJ_DEF(pj_bool_t) pj_ioqueue_is_pending( pj_ioqueue_key_t *key,
610
pj_ioqueue_op_key_t *op_key )
612
return key->cbObj->get_op_key()==op_key &&
613
key->cbObj->IsActive();
618
* Post completion status to the specified operation key and call the
619
* appropriate callback.
621
PJ_DEF(pj_status_t) pj_ioqueue_post_completion( pj_ioqueue_key_t *key,
622
pj_ioqueue_op_key_t *op_key,
623
pj_ssize_t bytes_status )
625
if (pj_ioqueue_is_pending(key, op_key)) {
626
key->cbObj->CancelOperation(op_key, bytes_status);
632
#if defined(PJ_HAS_TCP) && PJ_HAS_TCP != 0
634
* Instruct I/O Queue to accept incoming connection on the specified
637
PJ_DEF(pj_status_t) pj_ioqueue_accept( pj_ioqueue_key_t *key,
638
pj_ioqueue_op_key_t *op_key,
640
pj_sockaddr_t *local,
641
pj_sockaddr_t *remote,
645
return key->cbObj->StartAccept(op_key, new_sock, local, remote, addrlen);
650
* Initiate non-blocking socket connect.
652
PJ_DEF(pj_status_t) pj_ioqueue_connect( pj_ioqueue_key_t *key,
653
const pj_sockaddr_t *addr,
658
RSocket &rSock = key->cbObj->get_pj_socket()->Socket();
660
TRequestStatus reqStatus;
662
// Return failure if access point is marked as down by app.
663
PJ_SYMBIAN_CHECK_CONNECTION();
666
status = PjSymbianOS::pj2Addr(*(const pj_sockaddr*)addr, addrlen,
668
if (status != PJ_SUCCESS)
671
// We don't support async connect for now.
672
PJ_TODO(IOQUEUE_SUPPORT_ASYNC_CONNECT);
674
rSock.Connect(inetAddr, reqStatus);
675
User::WaitForRequest(reqStatus);
677
if (reqStatus == KErrNone)
680
return PJ_RETURN_OS_ERROR(reqStatus.Int());
684
#endif /* PJ_HAS_TCP */
687
* Poll the I/O Queue for completed events.
689
PJ_DEF(int) pj_ioqueue_poll( pj_ioqueue_t *ioq,
690
const pj_time_val *timeout)
692
/* Polling is not necessary on Symbian, since all async activities
693
* are registered to active scheduler.
696
PJ_UNUSED_ARG(timeout);
702
* Instruct the I/O Queue to read from the specified handle.
704
PJ_DEF(pj_status_t) pj_ioqueue_recv( pj_ioqueue_key_t *key,
705
pj_ioqueue_op_key_t *op_key,
710
// If socket has reader, delete it.
711
if (key->cbObj->get_pj_socket()->Reader())
712
key->cbObj->get_pj_socket()->DestroyReader();
715
flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC;
716
return key->cbObj->StartRead(op_key, buffer, length, flags, NULL, NULL);
721
* This function behaves similarly as #pj_ioqueue_recv(), except that it is
722
* normally called for socket, and the remote address will also be returned
723
* along with the data.
725
PJ_DEF(pj_status_t) pj_ioqueue_recvfrom( pj_ioqueue_key_t *key,
726
pj_ioqueue_op_key_t *op_key,
733
CPjSocket *sock = key->cbObj->get_pj_socket();
735
// If address is specified, check that the length match the
737
if (addr || addrlen) {
738
PJ_ASSERT_RETURN(addr && addrlen && *addrlen, PJ_EINVAL);
739
if (sock->GetAf() == PJ_AF_INET) {
740
PJ_ASSERT_RETURN(*addrlen>=(int)sizeof(pj_sockaddr_in), PJ_EINVAL);
741
} else if (sock->GetAf() == PJ_AF_INET6) {
742
PJ_ASSERT_RETURN(*addrlen>=(int)sizeof(pj_sockaddr_in6), PJ_EINVAL);
746
// If socket has reader, delete it.
748
sock->DestroyReader();
750
if (key->cbObj->IsActive())
754
flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC;
755
return key->cbObj->StartRead(op_key, buffer, length, flags, addr, addrlen);
760
* Instruct the I/O Queue to write to the handle.
762
PJ_DEF(pj_status_t) pj_ioqueue_send( pj_ioqueue_key_t *key,
763
pj_ioqueue_op_key_t *op_key,
768
TRequestStatus reqStatus;
769
TPtrC8 aBuffer((const TUint8*)data, (TInt)*length);
772
PJ_UNUSED_ARG(op_key);
774
// Forcing pending operation is not supported.
775
PJ_ASSERT_RETURN((flags & PJ_IOQUEUE_ALWAYS_ASYNC)==0, PJ_EINVAL);
777
// Return failure if access point is marked as down by app.
778
PJ_SYMBIAN_CHECK_CONNECTION();
781
flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC;
783
key->cbObj->get_pj_socket()->Socket().Send(aBuffer, flags, reqStatus, aLen);
784
User::WaitForRequest(reqStatus);
786
if (reqStatus.Int() != KErrNone)
787
return PJ_RETURN_OS_ERROR(reqStatus.Int());
789
//At least in UIQ Emulator, aLen.Length() reports incorrect length
790
//for UDP (some newlc.com users seem to have reported this too).
791
//*length = aLen.Length();
797
* Instruct the I/O Queue to write to the handle.
799
PJ_DEF(pj_status_t) pj_ioqueue_sendto( pj_ioqueue_key_t *key,
800
pj_ioqueue_op_key_t *op_key,
804
const pj_sockaddr_t *addr,
807
TRequestStatus reqStatus;
813
PJ_UNUSED_ARG(op_key);
815
// Forcing pending operation is not supported.
816
PJ_ASSERT_RETURN((flags & PJ_IOQUEUE_ALWAYS_ASYNC)==0, PJ_EINVAL);
818
// Return failure if access point is marked as down by app.
819
PJ_SYMBIAN_CHECK_CONNECTION();
822
status = PjSymbianOS::pj2Addr(*(const pj_sockaddr*)addr, addrlen,
824
if (status != PJ_SUCCESS)
828
flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC;
830
aBuffer.Set((const TUint8*)data, (TInt)*length);
831
CPjSocket *pjSock = key->cbObj->get_pj_socket();
833
pjSock->Socket().SendTo(aBuffer, inetAddr, flags, reqStatus, aLen);
834
User::WaitForRequest(reqStatus);
836
if (reqStatus.Int() != KErrNone)
837
return PJ_RETURN_OS_ERROR(reqStatus.Int());
839
//At least in UIQ Emulator, aLen.Length() reports incorrect length
840
//for UDP (some newlc.com users seem to have reported this too).
841
//*length = aLen.Length();
845
PJ_DEF(pj_status_t) pj_ioqueue_set_concurrency(pj_ioqueue_key_t *key,
848
/* Not supported, just return PJ_SUCCESS silently */
850
PJ_UNUSED_ARG(allow);
854
PJ_DEF(pj_status_t) pj_ioqueue_lock_key(pj_ioqueue_key_t *key)
856
/* Not supported, just return PJ_SUCCESS silently */
861
PJ_DEF(pj_status_t) pj_ioqueue_unlock_key(pj_ioqueue_key_t *key)
863
/* Not supported, just return PJ_SUCCESS silently */