/* * This file implements handling of socket-related requests from VFS */ #include #include #include #include #include #include #include #include #include #include "inet_config.h" #include "proto.h" #include "socket.h" #if 0 #define debug_sock_print(str, ...) printf("LWIP %s:%d : " str "\n", \ __func__, __LINE__, ##__VA_ARGS__) #else #define debug_sock_print(...) debug_print(__VA_ARGS__) #endif struct socket socket[MAX_SOCKETS]; static int notified; #define recv_q_alloc() debug_malloc(sizeof(struct recv_q)) #define recv_q_free debug_free struct mq { message m; struct mq * prev; struct mq * next; }; #define mq_alloc() debug_malloc(sizeof(struct mq)) #define mq_free debug_free static struct mq * mq_head, *mq_tail; static int mq_enqueue(message * m) { struct mq * mq; debug_sock_print("sock %d op %d", m->DEVICE, m->m_type); mq = mq_alloc(); if (mq == NULL) return -1; mq->next = NULL; mq->m = *m; if (mq_head) { mq->prev = mq_tail; mq_tail->next = mq; mq_tail = mq; } else { mq->prev = NULL; mq_head = mq_tail = mq; } return 0; } __unused static struct mq * mq_dequeue_head(void) { struct mq * ret; if (!mq_head) return NULL; ret = mq_head; if (mq_head != mq_tail) { mq_head = mq_head->next; mq_head->prev = NULL; } else mq_head = mq_tail = NULL; debug_sock_print("socket %d\n", ret->m.DEVICE); return ret; } static void mq_dequeue(struct mq * mq) { if (mq_head == mq_tail) mq_head = mq_tail = NULL; else { if (mq->prev == NULL) { mq_head = mq->next; mq_head->prev = NULL; } else mq->prev->next = mq->next; if (mq->next == NULL) { mq_tail = mq->prev; mq_tail->next = NULL; } else mq->next->prev = mq->prev; } } static int mq_cancel(message * m) { struct mq * mq; for (mq = mq_tail; mq; mq = mq->prev) { if (m->DEVICE == mq->m.DEVICE && m->USER_ENDPT == mq->m.USER_ENDPT && m->IO_GRANT == mq->m.IO_GRANT) { debug_sock_print("socket %d\n", mq->m.DEVICE); break; } } mq_dequeue(mq); mq_free(mq); return 1; } int sock_enqueue_data(struct socket * sock, void * data, unsigned size) { struct recv_q * r; if (!(r = recv_q_alloc())) return ENOMEM; r->data = data; r->next = NULL; if (sock->recv_head) { sock->recv_tail->next = r; sock->recv_tail = r; } else { sock->recv_head = sock->recv_tail = r; } assert(size > 0); sock->recv_data_size += size; return OK; } void * sock_dequeue_data(struct socket * sock) { void * data; struct recv_q * r; if ((r = sock->recv_head)) { data = r->data; if (!(sock->recv_head = r->next)) sock->recv_tail = NULL; recv_q_free(r); return data; } return NULL; } void sock_dequeue_data_all(struct socket * sock, recv_data_free_fn data_free) { void * data; while ((data = sock_dequeue_data(sock))) data_free(data); sock->recv_data_size = 0; } static void set_reply_msg(message * m, int status) { int proc, ref; proc= m->USER_ENDPT; ref= (int)m->IO_GRANT; m->REP_ENDPT= proc; m->REP_STATUS= status; m->REP_IO_GRANT= ref; } void send_reply(message * m, int status) { int result; debug_sock_print("status %d", status); set_reply_msg(m, status); m->m_type = TASK_REPLY; result = send(m->m_source, m); if (result != OK) panic("LWIP : unable to send (err %d)", result); } void sock_revive(struct socket * sock, int status) { int result; assert(!(sock->flags & SOCK_FLG_OP_REVIVING)); assert(sock->flags & (SOCK_FLG_OP_PENDING | SOCK_FLG_OP_SUSPENDED)); if (notified) { debug_sock_print("already notified"); return; } else { assert(sock->mess.m_type != DEV_REVIVE); notified = 1; } debug_sock_print("socket num %ld, status %d", get_sock_num(sock), status); sock->mess.m_type = DEV_REVIVE; set_reply_msg(&sock->mess, status); result = notify(sock->mess.m_source); if (result != OK) panic("LWIP : unable to notify (err %d)", result); sock->flags |= SOCK_FLG_OP_REVIVING; } void sock_select_notify(struct socket * sock) { int result; debug_sock_print("socket num %ld", get_sock_num(sock)); assert(sock->select_ep != NONE); sock->flags |= SOCK_FLG_SEL_CHECK; if (notified) { debug_sock_print("already notified"); return; } else notified = 1; result = notify(sock->select_ep); if (result != OK) panic("LWIP : unable to notify (err %d)", result); } void sock_reply(struct socket * sock, int status) { debug_sock_print("socket num %ld status %d type %d", get_sock_num(sock), status, sock->mess.m_type); /* * If the status is SUSPEND send the * message only if this operation wasn't * suspended already, e.g. by enqueing the * message when the socket was busy * because of another pending message * * If there is a pending operation or we a reprocessing a suspended * operation, revive. * * Otherwise send a message straightaway */ if (status == SUSPEND) { if (sock->flags & SOCK_FLG_OP_SUSPENDED) { debug_sock_print("suspended before"); sock->flags &= ~SOCK_FLG_OP_SUSPENDED; return; } message m = sock->mess; debug_sock_print("SUSPEND"); send_reply(&m, status); } else if (sock->flags & (SOCK_FLG_OP_PENDING | SOCK_FLG_OP_SUSPENDED)) { sock_revive(sock, status); /* * From now on, we process suspended calls as any other. The * status is set and will be collected */ sock->flags &= ~SOCK_FLG_OP_SUSPENDED; } else send_reply(&sock->mess, status); } struct socket * get_unused_sock(void) { int i; for (i = SOCK_TYPES + MAX_DEVS; i < MAX_SOCKETS; i++) { if (socket[i].ops == NULL) { /* clear it all */ memset(&socket[i], 0, sizeof(struct socket)); return &socket[i]; } } return NULL; } struct socket * get_nic_sock(unsigned dev) { if (dev < MAX_DEVS) return &socket[dev + SOCK_TYPES]; else return NULL; } static void socket_open(message * m) { struct sock_ops * ops; struct socket * sock; int ret = OK; switch (m->DEVICE) { case SOCK_TYPE_TCP: ops = &sock_tcp_ops; break; case SOCK_TYPE_UDP: ops = &sock_udp_ops; break; case SOCK_TYPE_IP: ops = &sock_raw_ip_ops; break; default: if (m->DEVICE - SOCK_TYPES < MAX_DEVS) { m->DEVICE -= SOCK_TYPES; nic_open(m); return; } printf("LWIP unknown socket type %d\n", m->DEVICE); send_reply(m, EINVAL); return; } sock = get_unused_sock(); if (!sock) { printf("LWIP : no free socket\n"); send_reply(m, EAGAIN); return; } sock->ops = ops; sock->select_ep = NONE; sock->recv_data_size = 0; if (sock->ops && sock->ops->open) ret = sock->ops->open(sock, m); if (ret == OK) { debug_sock_print("new socket %ld", get_sock_num(sock)); send_reply(m, get_sock_num(sock)); } else { debug_sock_print("failed %d", ret); send_reply(m, ret); } } static void do_status(message * m) { int i; debug_sock_print("called"); notified = 0; for (i = 0; i < MAX_SOCKETS; i++) { struct socket * sock = &socket[i]; if (!sock->ops) { continue; } if (sock->flags & (SOCK_FLG_OP_REVIVING)) { /* * We send the reply and we are done with this request */ debug_sock_print("status %d ep %d sent sock %ld type %d", sock->mess.REP_STATUS, sock->mess.REP_ENDPT, get_sock_num(sock), sock->mess.m_type); send(m->m_source, &sock->mess); /* * Remove only the reviving flag, i.e. the status has * been consumed. SOCK_FLG_OP_PENDING may stay set. For * instance in case of a TCP write, the application is * already notified while the process of sending is * still going on */ sock->flags &= ~SOCK_FLG_OP_REVIVING; return; } /* * We check select AFTER possible reviving an operation, * otherwise the select will fail as the socket is still * blocking */ if (sock_select_check_set(sock)) { if (sock->ops && sock->ops->select_reply) { message msg; msg.m_type = DEV_IO_READY; msg.DEV_MINOR = get_sock_num(sock); msg.DEV_SEL_OPS = 0; sock->ops->select_reply(sock, &msg); if (msg.DEV_SEL_OPS) { int result; debug_sock_print("socket num %d select " "result 0x%x sent", msg.DEV_MINOR, msg.DEV_SEL_OPS); result = send(sock->select_ep, &msg); if (result != OK) panic("LWIP : unable to send " "(err %d)", result); sock_clear_select(sock); sock->select_ep = NONE; return; } } } } debug_sock_print("no status"); m->m_type = DEV_NO_STATUS; send(m->m_source, m); } static void socket_request_socket(struct socket * sock, message * m) { switch (m->m_type) { case DEV_READ_S: if (sock && sock->ops && sock->ops->read) sock->ops->read(sock, m); else send_reply(m, EINVAL); return; case DEV_WRITE_S: if (sock && sock->ops && sock->ops->write) sock->ops->write(sock, m); else send_reply(m, EINVAL); return; case DEV_IOCTL_S: if (sock && sock->ops && sock->ops->ioctl) sock->ops->ioctl(sock, m); else send_reply(m, EINVAL); return; default: panic("LWIP : cannot happen!"); } } void socket_request(message * m) { struct socket * sock; switch (m->m_type) { case DEV_OPEN: socket_open(m); return; case DEV_CLOSE: sock = get_sock(m->DEVICE); if (sock && sock->ops && sock->ops->close) { sock->flags &= ~SOCK_FLG_OP_PENDING; sock->mess = *m; sock->ops->close(sock, m); } else send_reply(m, EINVAL); return; case DEV_READ_S: case DEV_WRITE_S: case DEV_IOCTL_S: sock = get_sock(m->DEVICE); if (!sock) { send_reply(m, EINVAL); return; } /* * If an operation is pending (blocking operation) or writing is * still going and we want to read, suspend the new operation */ if ((sock->flags & (SOCK_FLG_OP_PENDING | SOCK_FLG_OP_REVIVING)) | (m->m_type == DEV_READ_S && sock->flags & SOCK_FLG_OP_WRITING)) { char * o = "\0"; if (sock->flags & SOCK_FLG_OP_READING) o = "READ"; else if (sock->flags & SOCK_FLG_OP_WRITING) o = "WRITE"; else o = "non R/W op"; debug_sock_print("socket %ld is busy by %s\n", get_sock_num(sock), o); if (mq_enqueue(m) == 0) { send_reply(m, SUSPEND); } else { debug_sock_print("Enqueuing suspended " "call failed"); send_reply(m, ENOMEM); } return; } sock->mess = *m; socket_request_socket(sock, m); return; case CANCEL: sock = get_sock(m->DEVICE); debug_sock_print("socket num %ld", get_sock_num(sock)); /* Cancel the last operation in the queue */ if (mq_cancel(m)) { send_reply(m, EINTR); return; /* ... or a blocked read */ } else if (sock->flags & SOCK_FLG_OP_PENDING && sock->flags & SOCK_FLG_OP_READING) { sock->flags &= ~SOCK_FLG_OP_PENDING; send_reply(m, EINTR); return; /* * .. or return the status of the operation which was finished * before canceled */ } else if (sock->flags & SOCK_FLG_OP_REVIVING) { sock->flags &= ~SOCK_FLG_OP_REVIVING; send_reply(m, sock->mess.REP_STATUS); } else panic("LWIP : no operation to cancel"); return; case DEV_SELECT: /* * Select is always executed immediately and is never suspended. * Although, it sets actions which must be monitored */ sock = get_sock(m->DEVICE); assert(sock->select_ep == NONE || sock->select_ep == m->m_source); if (sock && sock->ops && sock->ops->select) { sock->ops->select(sock, m); if (sock_select_set(sock)) sock->select_ep = m->m_source; } else send_reply(m, EINVAL); return; case DEV_STATUS: do_status(m); return; default: printf("LWIP : unknown message from VFS, type %d\n", m->m_type); } send_reply(m, EGENERIC); } void mq_process(void) { struct mq * mq; struct socket * sock; mq = mq_head; while(mq) { struct mq * next = mq->next; sock = get_sock(mq->m.DEVICE); if (!(sock->flags & (SOCK_FLG_OP_PENDING | SOCK_FLG_OP_REVIVING)) && !(mq->m.m_type == DEV_READ_S && sock->flags & SOCK_FLG_OP_WRITING)) { sock->flags = SOCK_FLG_OP_SUSPENDED; debug_sock_print("resuming op on sock %ld\n", get_sock_num(sock)); sock->mess = mq->m; socket_request_socket(sock, &sock->mess); mq_dequeue(mq); mq_free(mq); return; } mq = next; } } void generic_op_select(struct socket * sock, message * m) { int retsel = 0, sel; debug_print("socket num %ld 0x%x", get_sock_num(sock), m->USER_ENDPT); sel = m->USER_ENDPT; /* in this case any operation would block, no error */ if (sock->flags & SOCK_FLG_OP_PENDING) { if (sel & SEL_NOTIFY) { if (sel & SEL_RD) sock->flags |= SOCK_FLG_SEL_READ; if (sel & SEL_WR) sock->flags |= SOCK_FLG_SEL_WRITE; /* FIXME we do not monitor error */ } send_reply(m, 0); return; } if (sel & SEL_RD) { if (sock->recv_head) retsel |= SEL_RD; else if (sel & SEL_NOTIFY) sock->flags |= SOCK_FLG_SEL_READ; } /* FIXME generic packet socket never blocks on write */ if (sel & SEL_WR) retsel |= SEL_WR; /* FIXME SEL_ERR is ignored, we do not generate exceptions */ send_reply(m, retsel); } void generic_op_select_reply(struct socket * sock, __unused message * m) { assert(sock->select_ep != NONE); debug_print("socket num %ld", get_sock_num(sock)); /* unused for generic packet socket, see generic_op_select() */ assert((sock->flags & (SOCK_FLG_SEL_WRITE | SOCK_FLG_SEL_ERROR)) == 0); if (sock->flags & (SOCK_FLG_OP_PENDING | SOCK_FLG_OP_REVIVING)) { debug_print("WARNING socket still blocking!"); return; } if (sock->flags & SOCK_FLG_SEL_READ && sock->recv_head) m->DEV_SEL_OPS |= SEL_RD; if (m->DEV_SEL_OPS) sock->flags &= ~(SOCK_FLG_SEL_WRITE | SOCK_FLG_SEL_READ | SOCK_FLG_SEL_ERROR); }