/* * Copyright (c) 2009 Lukas Mejdrech * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup nildummy * @{ */ /** @file * Dummy network interface layer module implementation. * @see nildummy.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nildummy.h" /** The module name. */ #define NAME "nildummy" /** Default maximum transmission unit. */ #define NET_DEFAULT_MTU 1500 /** Network interface layer module global data. */ nildummy_globals_t nildummy_globals; DEVICE_MAP_IMPLEMENT(nildummy_devices, nildummy_device_t); int nil_device_state_msg_local(int nil_phone, device_id_t device_id, int state) { fibril_rwlock_read_lock(&nildummy_globals.protos_lock); if (nildummy_globals.proto.phone) il_device_state_msg(nildummy_globals.proto.phone, device_id, state, nildummy_globals.proto.service); fibril_rwlock_read_unlock(&nildummy_globals.protos_lock); return EOK; } int nil_initialize(int net_phone) { int rc; fibril_rwlock_initialize(&nildummy_globals.devices_lock); fibril_rwlock_initialize(&nildummy_globals.protos_lock); fibril_rwlock_write_lock(&nildummy_globals.devices_lock); fibril_rwlock_write_lock(&nildummy_globals.protos_lock); nildummy_globals.net_phone = net_phone; nildummy_globals.proto.phone = 0; rc = nildummy_devices_initialize(&nildummy_globals.devices); fibril_rwlock_write_unlock(&nildummy_globals.protos_lock); fibril_rwlock_write_unlock(&nildummy_globals.devices_lock); return rc; } /** Process IPC messages from the registered device driver modules in an * infinite loop. * * @param[in] iid The message identifier. * @param[in,out] icall The message parameters. */ static void nildummy_receiver(ipc_callid_t iid, ipc_call_t *icall) { packet_t *packet; int rc; while (true) { switch (IPC_GET_IMETHOD(*icall)) { case NET_NIL_DEVICE_STATE: rc = nil_device_state_msg_local(0, IPC_GET_DEVICE(icall), IPC_GET_STATE(icall)); ipc_answer_0(iid, (sysarg_t) rc); break; case NET_NIL_RECEIVED: rc = packet_translate_remote(nildummy_globals.net_phone, &packet, IPC_GET_PACKET(icall)); if (rc == EOK) { rc = nil_received_msg_local(0, IPC_GET_DEVICE(icall), packet, 0); } ipc_answer_0(iid, (sysarg_t) rc); break; default: ipc_answer_0(iid, (sysarg_t) ENOTSUP); } iid = async_get_call(icall); } } /** Register new device or updates the MTU of an existing one. * * Determine the device local hardware address. * * @param[in] device_id The new device identifier. * @param[in] service The device driver service. * @param[in] mtu The device maximum transmission unit. * @return EOK on success. * @return EEXIST if the device with the different service exists. * @return ENOMEM if there is not enough memory left. * @return Other error codes as defined for the * netif_bind_service() function. * @return Other error codes as defined for the * netif_get_addr_req() function. */ static int nildummy_device_message(device_id_t device_id, services_t service, size_t mtu) { nildummy_device_t *device; int index; int rc; fibril_rwlock_write_lock(&nildummy_globals.devices_lock); /* An existing device? */ device = nildummy_devices_find(&nildummy_globals.devices, device_id); if (device) { if (device->service != service) { printf("Device %d already exists\n", device->device_id); fibril_rwlock_write_unlock( &nildummy_globals.devices_lock); return EEXIST; } /* Update MTU */ if (mtu > 0) device->mtu = mtu; else device->mtu = NET_DEFAULT_MTU; printf("Device %d already exists:\tMTU\t= %zu\n", device->device_id, device->mtu); fibril_rwlock_write_unlock(&nildummy_globals.devices_lock); /* Notify the upper layer module */ fibril_rwlock_read_lock(&nildummy_globals.protos_lock); if (nildummy_globals.proto.phone) { il_mtu_changed_msg(nildummy_globals.proto.phone, device->device_id, device->mtu, nildummy_globals.proto.service); } fibril_rwlock_read_unlock(&nildummy_globals.protos_lock); return EOK; } /* Create a new device */ device = (nildummy_device_t *) malloc(sizeof(nildummy_device_t)); if (!device) return ENOMEM; device->device_id = device_id; device->service = service; if (mtu > 0) device->mtu = mtu; else device->mtu = NET_DEFAULT_MTU; /* Bind the device driver */ device->phone = netif_bind_service(device->service, device->device_id, SERVICE_ETHERNET, nildummy_receiver); if (device->phone < 0) { fibril_rwlock_write_unlock(&nildummy_globals.devices_lock); free(device); return device->phone; } /* Get hardware address */ rc = netif_get_addr_req(device->phone, device->device_id, &device->addr, &device->addr_data); if (rc != EOK) { fibril_rwlock_write_unlock(&nildummy_globals.devices_lock); free(device); return rc; } /* Add to the cache */ index = nildummy_devices_add(&nildummy_globals.devices, device->device_id, device); if (index < 0) { fibril_rwlock_write_unlock(&nildummy_globals.devices_lock); free(device->addr); free(device->addr_data); free(device); return index; } printf("%s: Device registered (id: %d, service: %d, mtu: %zu)\n", NAME, device->device_id, device->service, device->mtu); fibril_rwlock_write_unlock(&nildummy_globals.devices_lock); return EOK; } /** Return the device hardware address. * * @param[in] device_id The device identifier. * @param[out] address The device hardware address. * @return EOK on success. * @return EBADMEM if the address parameter is NULL. * @return ENOENT if there no such device. * */ static int nildummy_addr_message(device_id_t device_id, measured_string_t **address) { nildummy_device_t *device; if (!address) return EBADMEM; fibril_rwlock_read_lock(&nildummy_globals.devices_lock); device = nildummy_devices_find(&nildummy_globals.devices, device_id); if (!device) { fibril_rwlock_read_unlock(&nildummy_globals.devices_lock); return ENOENT; } *address = device->addr; fibril_rwlock_read_unlock(&nildummy_globals.devices_lock); return (*address) ? EOK : ENOENT; } /** Return the device packet dimensions for sending. * * @param[in] device_id The device identifier. * @param[out] addr_len The minimum reserved address length. * @param[out] prefix The minimum reserved prefix size. * @param[out] content The maximum content size. * @param[out] suffix The minimum reserved suffix size. * @return EOK on success. * @return EBADMEM if either one of the parameters is NULL. * @return ENOENT if there is no such device. * */ static int nildummy_packet_space_message(device_id_t device_id, size_t *addr_len, size_t *prefix, size_t *content, size_t *suffix) { nildummy_device_t *device; if (!addr_len || !prefix || !content || !suffix) return EBADMEM; fibril_rwlock_read_lock(&nildummy_globals.devices_lock); device = nildummy_devices_find(&nildummy_globals.devices, device_id); if (!device) { fibril_rwlock_read_unlock(&nildummy_globals.devices_lock); return ENOENT; } *content = device->mtu; fibril_rwlock_read_unlock(&nildummy_globals.devices_lock); *addr_len = 0; *prefix = 0; *suffix = 0; return EOK; } int nil_received_msg_local(int nil_phone, device_id_t device_id, packet_t *packet, services_t target) { packet_t *next; fibril_rwlock_read_lock(&nildummy_globals.protos_lock); if (nildummy_globals.proto.phone) { do { next = pq_detach(packet); il_received_msg(nildummy_globals.proto.phone, device_id, packet, nildummy_globals.proto.service); packet = next; } while(packet); } fibril_rwlock_read_unlock(&nildummy_globals.protos_lock); return EOK; } /** Register receiving module service. * * Pass received packets for this service. * * @param[in] service The module service. * @param[in] phone The service phone. * @return EOK on success. * @return ENOENT if the service is not known. * @return ENOMEM if there is not enough memory left. */ static int nildummy_register_message(services_t service, int phone) { fibril_rwlock_write_lock(&nildummy_globals.protos_lock); nildummy_globals.proto.service = service; nildummy_globals.proto.phone = phone; printf("%s: Protocol registered (service: %d, phone: %d)\n", NAME, nildummy_globals.proto.service, nildummy_globals.proto.phone); fibril_rwlock_write_unlock(&nildummy_globals.protos_lock); return EOK; } /** Send the packet queue. * * @param[in] device_id The device identifier. * @param[in] packet The packet queue. * @param[in] sender The sending module service. * @return EOK on success. * @return ENOENT if there no such device. * @return EINVAL if the service parameter is not known. */ static int nildummy_send_message(device_id_t device_id, packet_t *packet, services_t sender) { nildummy_device_t *device; fibril_rwlock_read_lock(&nildummy_globals.devices_lock); device = nildummy_devices_find(&nildummy_globals.devices, device_id); if (!device) { fibril_rwlock_read_unlock(&nildummy_globals.devices_lock); return ENOENT; } /* Send packet queue */ if (packet) netif_send_msg(device->phone, device_id, packet, SERVICE_NILDUMMY); fibril_rwlock_read_unlock(&nildummy_globals.devices_lock); return EOK; } int nil_message_standalone(const char *name, ipc_callid_t callid, ipc_call_t *call, ipc_call_t *answer, int *answer_count) { measured_string_t *address; packet_t *packet; size_t addrlen; size_t prefix; size_t suffix; size_t content; int rc; *answer_count = 0; switch (IPC_GET_IMETHOD(*call)) { case IPC_M_PHONE_HUNGUP: return EOK; case NET_NIL_DEVICE: return nildummy_device_message(IPC_GET_DEVICE(call), IPC_GET_SERVICE(call), IPC_GET_MTU(call)); case NET_NIL_SEND: rc = packet_translate_remote(nildummy_globals.net_phone, &packet, IPC_GET_PACKET(call)); if (rc != EOK) return rc; return nildummy_send_message(IPC_GET_DEVICE(call), packet, IPC_GET_SERVICE(call)); case NET_NIL_PACKET_SPACE: rc = nildummy_packet_space_message(IPC_GET_DEVICE(call), &addrlen, &prefix, &content, &suffix); if (rc != EOK) return rc; IPC_SET_ADDR(answer, addrlen); IPC_SET_PREFIX(answer, prefix); IPC_SET_CONTENT(answer, content); IPC_SET_SUFFIX(answer, suffix); *answer_count = 4; return EOK; case NET_NIL_ADDR: rc = nildummy_addr_message(IPC_GET_DEVICE(call), &address); if (rc != EOK) return rc; return measured_strings_reply(address, 1); case NET_NIL_BROADCAST_ADDR: rc = nildummy_addr_message(IPC_GET_DEVICE(call), &address); if (rc != EOK) return rc; return measured_strings_reply(address, 1); case IPC_M_CONNECT_TO_ME: return nildummy_register_message(NIL_GET_PROTO(call), IPC_GET_PHONE(call)); } return ENOTSUP; } /** Default thread for new connections. * * @param[in] iid The initial message identifier. * @param[in] icall The initial message call structure. */ static void nil_client_connection(ipc_callid_t iid, ipc_call_t *icall) { /* * Accept the connection * - Answer the first IPC_M_CONNECT_ME_TO call. */ ipc_answer_0(iid, EOK); while (true) { ipc_call_t answer; int answer_count; /* Clear the answer structure */ refresh_answer(&answer, &answer_count); /* Fetch the next message */ ipc_call_t call; ipc_callid_t callid = async_get_call(&call); /* Process the message */ int res = nil_module_message_standalone(NAME, callid, &call, &answer, &answer_count); /* * End if told to either by the message or the processing * result. */ if ((IPC_GET_IMETHOD(call) == IPC_M_PHONE_HUNGUP) || (res == EHANGUP)) return; /* Answer the message */ answer_call(callid, res, &answer, answer_count); } } int main(int argc, char *argv[]) { int rc; /* Start the module */ rc = nil_module_start_standalone(nil_client_connection); return rc; } /** @} */