source: mainline/uspace/srv/net/nil/eth/eth.c@ 9d58539

lfn serial ticket/834-toolchain-update topic/fix-logger-deadlock topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9d58539 was 9d58539, checked in by Prutkov Alex <prutkov.alex@…>, 14 years ago

Fixed unix permissions for all files

  • Property mode set to 100644
File size: 25.9 KB
RevLine 
[21580dd]1/*
2 * Copyright (c) 2009 Lukas Mejdrech
[609243f4]3 * Copyright (c) 2011 Radim Vansa
[21580dd]4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup eth
31 * @{
32 */
33
34/** @file
35 * Ethernet module implementation.
36 * @see eth.h
37 */
38
[609243f4]39#include <assert.h>
[21580dd]40#include <async.h>
41#include <malloc.h>
42#include <mem.h>
43#include <stdio.h>
[2687bdb]44#include <byteorder.h>
[19f857a]45#include <str.h>
[4ef32e0c]46#include <errno.h>
[fe8dfa6]47#include <ipc/nil.h>
[514ee46]48#include <ipc/net.h>
[21580dd]49#include <ipc/services.h>
[77a69ea]50#include <loc.h>
[c7a8442]51#include <net/modules.h>
[849ed54]52#include <net_checksum.h>
53#include <ethernet_lsap.h>
54#include <ethernet_protocols.h>
55#include <protocol_map.h>
[e526f08]56#include <net/device.h>
[849ed54]57#include <net_interface.h>
[797b704]58#include <il_remote.h>
[849ed54]59#include <adt/measured_strings.h>
[0a866eeb]60#include <packet_client.h>
[14f1db0]61#include <packet_remote.h>
[609243f4]62#include <device/nic.h>
[fe8dfa6]63#include <nil_skel.h>
[21580dd]64#include "eth.h"
65
[6067284]66/** The module name. */
[24ab58b3]67#define NAME "eth"
[849ed54]68
[6067284]69/** Reserved packet prefix length. */
70#define ETH_PREFIX \
71 (sizeof(eth_header_t) + sizeof(eth_header_lsap_t) + \
[fe8dfa6]72 sizeof(eth_header_snap_t))
[21580dd]73
[6067284]74/** Reserved packet suffix length. */
[fe8dfa6]75#define ETH_SUFFIX (sizeof(eth_fcs_t))
[21580dd]76
[6067284]77/** Maximum packet content length. */
[fe8dfa6]78#define ETH_MAX_CONTENT 1500u
[21580dd]79
[6067284]80/** Minimum packet content length. */
[fe8dfa6]81#define ETH_MIN_CONTENT 46u
[21580dd]82
[6067284]83/** Maximum tagged packet content length. */
84#define ETH_MAX_TAGGED_CONTENT(flags) \
85 (ETH_MAX_CONTENT - \
[fe8dfa6]86 ((IS_8023_2_LSAP(flags) || IS_8023_2_SNAP(flags)) ? \
87 sizeof(eth_header_lsap_t) : 0) - \
88 (IS_8023_2_SNAP(flags) ? sizeof(eth_header_snap_t) : 0))
[6067284]89
90/** Minimum tagged packet content length. */
91#define ETH_MIN_TAGGED_CONTENT(flags) \
92 (ETH_MIN_CONTENT - \
[fe8dfa6]93 ((IS_8023_2_LSAP(flags) || IS_8023_2_SNAP(flags)) ? \
94 sizeof(eth_header_lsap_t) : 0) - \
95 (IS_8023_2_SNAP(flags) ? sizeof(eth_header_snap_t) : 0))
[6067284]96
97/** Dummy flag shift value. */
[fe8dfa6]98#define ETH_DUMMY_SHIFT 0
[21580dd]99
[6067284]100/** Mode flag shift value. */
[fe8dfa6]101#define ETH_MODE_SHIFT 1
[21580dd]102
103/** Dummy device flag.
[6067284]104 * Preamble and FCS are mandatory part of the packets.
[21580dd]105 */
[fe8dfa6]106#define ETH_DUMMY (1 << ETH_DUMMY_SHIFT)
[21580dd]107
108/** Returns the dummy flag.
[6067284]109 * @see ETH_DUMMY
[21580dd]110 */
[fe8dfa6]111#define IS_DUMMY(flags) ((flags) & ETH_DUMMY)
[21580dd]112
113/** Device mode flags.
[6067284]114 * @see ETH_DIX
115 * @see ETH_8023_2_LSAP
116 * @see ETH_8023_2_SNAP
[21580dd]117 */
[fe8dfa6]118#define ETH_MODE_MASK (3 << ETH_MODE_SHIFT)
[21580dd]119
[6067284]120/** DIX Ethernet mode flag. */
[fe8dfa6]121#define ETH_DIX (1 << ETH_MODE_SHIFT)
[21580dd]122
[fe8dfa6]123/** Return whether the DIX Ethernet mode flag is set.
[6067284]124 *
[fe8dfa6]125 * @param[in] flags Ethernet flags.
[6067284]126 * @see ETH_DIX
[fe8dfa6]127 *
[21580dd]128 */
[fe8dfa6]129#define IS_DIX(flags) (((flags) & ETH_MODE_MASK) == ETH_DIX)
[21580dd]130
[6067284]131/** 802.3 + 802.2 + LSAP mode flag. */
[fe8dfa6]132#define ETH_8023_2_LSAP (2 << ETH_MODE_SHIFT)
[21580dd]133
[fe8dfa6]134/** Return whether the 802.3 + 802.2 + LSAP mode flag is set.
[6067284]135 *
[fe8dfa6]136 * @param[in] flags Ethernet flags.
[6067284]137 * @see ETH_8023_2_LSAP
[fe8dfa6]138 *
[21580dd]139 */
[fe8dfa6]140#define IS_8023_2_LSAP(flags) (((flags) & ETH_MODE_MASK) == ETH_8023_2_LSAP)
[21580dd]141
[6067284]142/** 802.3 + 802.2 + LSAP + SNAP mode flag. */
[fe8dfa6]143#define ETH_8023_2_SNAP (3 << ETH_MODE_SHIFT)
[21580dd]144
[fe8dfa6]145/** Return whether the 802.3 + 802.2 + LSAP + SNAP mode flag is set.
[6067284]146 *
[fe8dfa6]147 * @param[in] flags Ethernet flags.
[6067284]148 * @see ETH_8023_2_SNAP
[fe8dfa6]149 *
[21580dd]150 */
[fe8dfa6]151#define IS_8023_2_SNAP(flags) (((flags) & ETH_MODE_MASK) == ETH_8023_2_SNAP)
[21580dd]152
153/** Type definition of the ethernet address type.
[6067284]154 * @see eth_addr_type
[21580dd]155 */
[6067284]156typedef enum eth_addr_type eth_addr_type_t;
[21580dd]157
[6067284]158/** Ethernet address type. */
159enum eth_addr_type {
160 /** Local address. */
[21580dd]161 ETH_LOCAL_ADDR,
[6067284]162 /** Broadcast address. */
[21580dd]163 ETH_BROADCAST_ADDR
164};
165
[6067284]166/** Ethernet module global data. */
167eth_globals_t eth_globals;
[21580dd]168
[6067284]169DEVICE_MAP_IMPLEMENT(eth_devices, eth_device_t);
170INT_MAP_IMPLEMENT(eth_protos, eth_proto_t);
[21580dd]171
[8d7ec69d]172static void eth_nic_cb_connection(ipc_callid_t iid, ipc_call_t *icall,
173 void *arg);
174
[9cd8165]175static int eth_device_state(eth_device_t *device, sysarg_t state)
[6067284]176{
[aadf01e]177 int index;
[4e5c7ba]178 eth_proto_t *proto;
[21580dd]179
[aadf01e]180 fibril_rwlock_read_lock(&eth_globals.protos_lock);
[6067284]181 for (index = eth_protos_count(&eth_globals.protos) - 1; index >= 0;
182 index--) {
[aadf01e]183 proto = eth_protos_get_index(&eth_globals.protos, index);
[6b82009]184 if ((proto) && (proto->sess)) {
[9cd8165]185 il_device_state_msg(proto->sess, device->device_id,
186 state, proto->service);
[aadf01e]187 }
[21580dd]188 }
[aadf01e]189 fibril_rwlock_read_unlock(&eth_globals.protos_lock);
[6067284]190
[21580dd]191 return EOK;
192}
193
[6b82009]194int nil_initialize(async_sess_t *sess)
[6067284]195{
[4ef32e0c]196 int rc;
[21580dd]197
[aadf01e]198 fibril_rwlock_initialize(&eth_globals.devices_lock);
199 fibril_rwlock_initialize(&eth_globals.protos_lock);
[6067284]200
[aadf01e]201 fibril_rwlock_write_lock(&eth_globals.devices_lock);
202 fibril_rwlock_write_lock(&eth_globals.protos_lock);
[4ee7364]203
[6b82009]204 eth_globals.net_sess = sess;
[609243f4]205 memcpy(eth_globals.broadcast_addr, "\xFF\xFF\xFF\xFF\xFF\xFF",
206 ETH_ADDR);
[fb04cba8]207
[4ef32e0c]208 rc = eth_devices_initialize(&eth_globals.devices);
209 if (rc != EOK) {
[0a3fbc7]210 free(eth_globals.broadcast_addr);
[6067284]211 goto out;
[0a3fbc7]212 }
[fb04cba8]213
[4ef32e0c]214 rc = eth_protos_initialize(&eth_globals.protos);
215 if (rc != EOK) {
[0a3fbc7]216 free(eth_globals.broadcast_addr);
[5fe7692]217 eth_devices_destroy(&eth_globals.devices, free);
[0a3fbc7]218 }
[609243f4]219
[6067284]220out:
[aadf01e]221 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
222 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
[6067284]223
[4ef32e0c]224 return rc;
[21580dd]225}
226
[609243f4]227/** Register new device or updates the MTU of an existing one.
[6067284]228 *
[609243f4]229 * Determine the device local hardware address.
[6b82009]230 *
[609243f4]231 * @param[in] device_id New device identifier.
[77a69ea]232 * @param[in] sid NIC service ID.
[609243f4]233 * @param[in] mtu Device maximum transmission unit.
[6067284]234 *
[609243f4]235 * @return EOK on success.
236 * @return EEXIST if the device with the different service exists.
237 * @return ENOMEM if there is not enough memory left.
[6067284]238 *
239 */
[77a69ea]240static int eth_device_message(nic_device_id_t device_id, service_id_t sid,
[fb04cba8]241 size_t mtu)
[6067284]242{
[4e5c7ba]243 eth_device_t *device;
[aadf01e]244 int index;
[6067284]245 measured_string_t names[2] = {
246 {
[61bfc370]247 (uint8_t *) "ETH_MODE",
[6067284]248 8
249 },
250 {
[61bfc370]251 (uint8_t *) "ETH_DUMMY",
[6067284]252 9
253 }
254 };
[4eca056]255 measured_string_t *configuration;
[aadf01e]256 size_t count = sizeof(names) / sizeof(measured_string_t);
[61bfc370]257 uint8_t *data;
[4e5c7ba]258 eth_proto_t *proto;
[4ef32e0c]259 int rc;
[21580dd]260
[aadf01e]261 fibril_rwlock_write_lock(&eth_globals.devices_lock);
[fb04cba8]262 /* An existing device? */
[aadf01e]263 device = eth_devices_find(&eth_globals.devices, device_id);
[6067284]264 if (device) {
[77a69ea]265 if (device->sid != sid) {
[aadf01e]266 printf("Device %d already exists\n", device->device_id);
267 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
[21580dd]268 return EEXIST;
[aadf01e]269 }
[6067284]270
[fb04cba8]271 /* Update mtu */
[6067284]272 if ((mtu > 0) && (mtu <= ETH_MAX_TAGGED_CONTENT(device->flags)))
[21580dd]273 device->mtu = mtu;
[6067284]274 else
275 device->mtu = ETH_MAX_TAGGED_CONTENT(device->flags);
276
[7e752b2]277 printf("Device %d already exists:\tMTU\t= %zu\n",
[6067284]278 device->device_id, device->mtu);
279 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
280
[fb04cba8]281 /* Notify all upper layer modules */
[6067284]282 fibril_rwlock_read_lock(&eth_globals.protos_lock);
283 for (index = 0; index < eth_protos_count(&eth_globals.protos);
284 index++) {
285 proto = eth_protos_get_index(&eth_globals.protos,
286 index);
[6b82009]287 if (proto->sess) {
288 il_mtu_changed_msg(proto->sess,
[6067284]289 device->device_id, device->mtu,
290 proto->service);
[21580dd]291 }
292 }
[fb04cba8]293
[6067284]294 fibril_rwlock_read_unlock(&eth_globals.protos_lock);
295 return EOK;
296 }
297
[fb04cba8]298 /* Create a new device */
[4e5c7ba]299 device = (eth_device_t *) malloc(sizeof(eth_device_t));
[6067284]300 if (!device)
301 return ENOMEM;
302
303 device->device_id = device_id;
[77a69ea]304 device->sid = sid;
[6067284]305 device->flags = 0;
306 if ((mtu > 0) && (mtu <= ETH_MAX_TAGGED_CONTENT(device->flags)))
307 device->mtu = mtu;
308 else
309 device->mtu = ETH_MAX_TAGGED_CONTENT(device->flags);
310
311 configuration = &names[0];
[6b82009]312 rc = net_get_device_conf_req(eth_globals.net_sess, device->device_id,
[4ef32e0c]313 &configuration, count, &data);
314 if (rc != EOK) {
[6067284]315 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
316 free(device);
[4ef32e0c]317 return rc;
[6067284]318 }
[fb04cba8]319
[6067284]320 if (configuration) {
[61bfc370]321 if (!str_lcmp((char *) configuration[0].value, "DIX",
[6067284]322 configuration[0].length)) {
323 device->flags |= ETH_DIX;
[61bfc370]324 } else if(!str_lcmp((char *) configuration[0].value, "8023_2_LSAP",
[6067284]325 configuration[0].length)) {
326 device->flags |= ETH_8023_2_LSAP;
327 } else {
328 device->flags |= ETH_8023_2_SNAP;
[21580dd]329 }
[6067284]330
331 if (configuration[1].value &&
332 (configuration[1].value[0] == 'y')) {
333 device->flags |= ETH_DUMMY;
[21580dd]334 }
[6067284]335 net_free_settings(configuration, data);
336 } else {
337 device->flags |= ETH_8023_2_SNAP;
[21580dd]338 }
[6067284]339
[fb04cba8]340 /* Bind the device driver */
[77a69ea]341 device->sess = loc_service_connect(EXCHANGE_SERIALIZE, sid,
[609243f4]342 IPC_FLAG_BLOCKING);
[6b82009]343 if (device->sess == NULL) {
[6067284]344 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
345 free(device);
[6b82009]346 return ENOENT;
[6067284]347 }
348
[9cd8165]349 rc = nic_callback_create(device->sess, eth_nic_cb_connection, device);
[8d7ec69d]350 if (rc != EOK) {
351 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
352 async_hangup(device->sess);
353 free(device);
354 return EIO;
355 }
[609243f4]356
[fb04cba8]357 /* Get hardware address */
[609243f4]358 rc = nic_get_address(device->sess, &device->addr);
[4ef32e0c]359 if (rc != EOK) {
[6067284]360 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
361 free(device);
[4ef32e0c]362 return rc;
[6067284]363 }
364
[fb04cba8]365 /* Add to the cache */
[6067284]366 index = eth_devices_add(&eth_globals.devices, device->device_id,
367 device);
368 if (index < 0) {
369 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
370 free(device);
371 return index;
372 }
373
[77a69ea]374 printf("%s: Device registered (id: %d, sid: %zu: mtu: %zu, "
[609243f4]375 "mac: " PRIMAC ", flags: 0x%x)\n", NAME,
[77a69ea]376 device->device_id, device->sid, device->mtu,
[609243f4]377 ARGSMAC(device->addr.address), device->flags);
[6067284]378
[aadf01e]379 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
[21580dd]380 return EOK;
381}
382
[6067284]383/** Processes the received packet and chooses the target registered module.
384 *
385 * @param[in] flags The device flags.
386 * @param[in] packet The packet.
[1bfd3d3]387 * @return The target registered module.
388 * @return NULL if the packet is not long enough.
389 * @return NULL if the packet is too long.
390 * @return NULL if the raw ethernet protocol is used.
391 * @return NULL if the dummy device FCS checksum is invalid.
392 * @return NULL if the packet address length is not big enough.
[6067284]393 */
[46d4d9f]394static eth_proto_t *eth_process_packet(int flags, packet_t *packet)
[6067284]395{
[4e5c7ba]396 eth_header_snap_t *header;
[aadf01e]397 size_t length;
398 eth_type_t type;
399 size_t prefix;
400 size_t suffix;
[4e5c7ba]401 eth_fcs_t *fcs;
[4ef32e0c]402 uint8_t *data;
403 int rc;
[aadf01e]404
405 length = packet_get_data_length(packet);
[6067284]406
407 if (IS_DUMMY(flags))
[aadf01e]408 packet_trim(packet, sizeof(eth_preamble_t), 0);
[6067284]409 if (length < sizeof(eth_header_t) + ETH_MIN_CONTENT +
410 (IS_DUMMY(flags) ? ETH_SUFFIX : 0))
411 return NULL;
412
[aadf01e]413 data = packet_get_data(packet);
[4e5c7ba]414 header = (eth_header_snap_t *) data;
[aadf01e]415 type = ntohs(header->header.ethertype);
[6067284]416
417 if (type >= ETH_MIN_PROTO) {
[fb04cba8]418 /* DIX Ethernet */
[aadf01e]419 prefix = sizeof(eth_header_t);
[21580dd]420 suffix = 0;
[4e5c7ba]421 fcs = (eth_fcs_t *) data + length - sizeof(eth_fcs_t);
[aadf01e]422 length -= sizeof(eth_fcs_t);
[609243f4]423 } else if (type <= ETH_MAX_CONTENT) {
[fb04cba8]424 /* Translate "LSAP" values */
[6067284]425 if ((header->lsap.dsap == ETH_LSAP_GLSAP) &&
426 (header->lsap.ssap == ETH_LSAP_GLSAP)) {
[fb04cba8]427 /* Raw packet -- discard */
[21580dd]428 return NULL;
[609243f4]429 } else if ((header->lsap.dsap == ETH_LSAP_SNAP) &&
[6067284]430 (header->lsap.ssap == ETH_LSAP_SNAP)) {
[fb04cba8]431 /*
432 * IEEE 802.3 + 802.2 + LSAP + SNAP
433 * organization code not supported
434 */
[aadf01e]435 type = ntohs(header->snap.ethertype);
[609243f4]436 prefix = sizeof(eth_header_t) + sizeof(eth_header_lsap_t) +
[6067284]437 sizeof(eth_header_snap_t);
438 } else {
[fb04cba8]439 /* IEEE 802.3 + 802.2 LSAP */
[aadf01e]440 type = lsap_map(header->lsap.dsap);
[609243f4]441 prefix = sizeof(eth_header_t) + sizeof(eth_header_lsap_t);
[21580dd]442 }
[fb04cba8]443
[6067284]444 suffix = (type < ETH_MIN_CONTENT) ? ETH_MIN_CONTENT - type : 0U;
[4e5c7ba]445 fcs = (eth_fcs_t *) data + prefix + type + suffix;
[21580dd]446 suffix += length - prefix - type;
447 length = prefix + type + suffix;
[6067284]448 } else {
[fb04cba8]449 /* Invalid length/type, should not occur */
[21580dd]450 return NULL;
451 }
[6067284]452
453 if (IS_DUMMY(flags)) {
[4ef32e0c]454 if (~compute_crc32(~0U, data, length * 8) != ntohl(*fcs))
[21580dd]455 return NULL;
[aadf01e]456 suffix += sizeof(eth_fcs_t);
[21580dd]457 }
[6067284]458
[4ef32e0c]459 rc = packet_set_addr(packet, header->header.source_address,
460 header->header.destination_address, ETH_ADDR);
461 if (rc != EOK)
462 return NULL;
463
464 rc = packet_trim(packet, prefix, suffix);
465 if (rc != EOK)
[21580dd]466 return NULL;
[6067284]467
[aadf01e]468 return eth_protos_find(&eth_globals.protos, type);
[21580dd]469}
470
[609243f4]471int nil_received_msg_local(nic_device_id_t device_id, packet_t *packet)
[6067284]472{
[4e5c7ba]473 eth_proto_t *proto;
[46d4d9f]474 packet_t *next;
[4e5c7ba]475 eth_device_t *device;
[aadf01e]476 int flags;
[21580dd]477
[aadf01e]478 fibril_rwlock_read_lock(&eth_globals.devices_lock);
479 device = eth_devices_find(&eth_globals.devices, device_id);
[6067284]480 if (!device) {
[aadf01e]481 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
[21580dd]482 return ENOENT;
483 }
[fb04cba8]484
[21580dd]485 flags = device->flags;
[aadf01e]486 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
487 fibril_rwlock_read_lock(&eth_globals.protos_lock);
[609243f4]488
[6067284]489 do {
[aadf01e]490 next = pq_detach(packet);
491 proto = eth_process_packet(flags, packet);
[6067284]492 if (proto) {
[6b82009]493 il_received_msg(proto->sess, device_id, packet,
[6067284]494 proto->service);
495 } else {
[28a3e74]496 /* Drop invalid/unknown */
[6b82009]497 pq_release_remote(eth_globals.net_sess,
[6067284]498 packet_get_id(packet));
[21580dd]499 }
500 packet = next;
[609243f4]501 } while (packet);
[fb04cba8]502
[aadf01e]503 fibril_rwlock_read_unlock(&eth_globals.protos_lock);
[21580dd]504 return EOK;
505}
506
[6067284]507/** Returns the device packet dimensions for sending.
508 *
509 * @param[in] device_id The device identifier.
510 * @param[out] addr_len The minimum reserved address length.
511 * @param[out] prefix The minimum reserved prefix size.
512 * @param[out] content The maximum content size.
513 * @param[out] suffix The minimum reserved suffix size.
[1bfd3d3]514 * @return EOK on success.
515 * @return EBADMEM if either one of the parameters is NULL.
516 * @return ENOENT if there is no such device.
[6067284]517 */
[609243f4]518static int eth_packet_space_message(nic_device_id_t device_id, size_t *addr_len,
[6067284]519 size_t *prefix, size_t *content, size_t *suffix)
520{
[4e5c7ba]521 eth_device_t *device;
[21580dd]522
[6067284]523 if (!addr_len || !prefix || !content || !suffix)
[aadf01e]524 return EBADMEM;
[6067284]525
[aadf01e]526 fibril_rwlock_read_lock(&eth_globals.devices_lock);
527 device = eth_devices_find(&eth_globals.devices, device_id);
[6067284]528 if (!device) {
[aadf01e]529 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
[21580dd]530 return ENOENT;
531 }
[fb04cba8]532
[aadf01e]533 *content = device->mtu;
534 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
[6067284]535
[aadf01e]536 *addr_len = ETH_ADDR;
537 *prefix = ETH_PREFIX;
538 *suffix = ETH_MIN_CONTENT + ETH_SUFFIX;
[fb04cba8]539
[21580dd]540 return EOK;
541}
542
[609243f4]543/** Send the device hardware address.
[6067284]544 *
545 * @param[in] device_id The device identifier.
546 * @param[in] type Type of the desired address.
[1bfd3d3]547 * @return EOK on success.
548 * @return EBADMEM if the address parameter is NULL.
549 * @return ENOENT if there no such device.
[6067284]550 */
[609243f4]551static int eth_addr_message(nic_device_id_t device_id, eth_addr_type_t type)
[6067284]552{
[609243f4]553 eth_device_t *device = NULL;
554 uint8_t *address;
555 size_t max_len;
556 ipc_callid_t callid;
557
558 if (type == ETH_BROADCAST_ADDR)
559 address = eth_globals.broadcast_addr;
560 else {
[aadf01e]561 fibril_rwlock_read_lock(&eth_globals.devices_lock);
562 device = eth_devices_find(&eth_globals.devices, device_id);
[6067284]563 if (!device) {
[aadf01e]564 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
[21580dd]565 return ENOENT;
566 }
[609243f4]567
568 address = (uint8_t *) &device->addr.address;
569 }
570
571 int rc = EOK;
572 if (!async_data_read_receive(&callid, &max_len)) {
573 rc = EREFUSED;
574 goto end;
575 }
576
577 if (max_len < ETH_ADDR) {
578 async_data_read_finalize(callid, NULL, 0);
579 rc = ELIMIT;
580 goto end;
[21580dd]581 }
[6067284]582
[609243f4]583 rc = async_data_read_finalize(callid, address, ETH_ADDR);
584 if (rc != EOK)
585 goto end;
586
587end:
588
589 if (type == ETH_LOCAL_ADDR)
590 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
591
592 return rc;
[21580dd]593}
594
[6b82009]595/** Register receiving module service.
[6067284]596 *
[6b82009]597 * Pass received packets for this service.
598 *
599 * @param[in] service Module service.
600 * @param[in] sess Service session.
601 *
602 * @return EOK on success.
603 * @return ENOENT if the service is not known.
604 * @return ENOMEM if there is not enough memory left.
[6067284]605 *
606 */
[6b82009]607static int eth_register_message(services_t service, async_sess_t *sess)
[6067284]608{
[4e5c7ba]609 eth_proto_t *proto;
[aadf01e]610 int protocol;
611 int index;
[21580dd]612
[aadf01e]613 protocol = protocol_map(SERVICE_ETHERNET, service);
[6067284]614 if (!protocol)
[aadf01e]615 return ENOENT;
[6067284]616
[aadf01e]617 fibril_rwlock_write_lock(&eth_globals.protos_lock);
618 proto = eth_protos_find(&eth_globals.protos, protocol);
[6067284]619 if (proto) {
[6b82009]620 proto->sess = sess;
[aadf01e]621 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
[21580dd]622 return EOK;
[6067284]623 } else {
[4e5c7ba]624 proto = (eth_proto_t *) malloc(sizeof(eth_proto_t));
[6067284]625 if (!proto) {
[aadf01e]626 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
[21580dd]627 return ENOMEM;
628 }
[fb04cba8]629
[21580dd]630 proto->service = service;
631 proto->protocol = protocol;
[6b82009]632 proto->sess = sess;
[fb04cba8]633
[aadf01e]634 index = eth_protos_add(&eth_globals.protos, protocol, proto);
[6067284]635 if (index < 0) {
[aadf01e]636 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
637 free(proto);
[21580dd]638 return index;
639 }
640 }
[24ab58b3]641
[00d7e1b]642 printf("%s: Protocol registered (protocol: %d, service: %#x)\n",
[6b82009]643 NAME, proto->protocol, proto->service);
[24ab58b3]644
[aadf01e]645 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
[21580dd]646 return EOK;
647}
648
[6067284]649/** Prepares the packet for sending.
650 *
651 * @param[in] flags The device flags.
652 * @param[in] packet The packet.
653 * @param[in] src_addr The source hardware address.
654 * @param[in] ethertype The ethernet protocol type.
655 * @param[in] mtu The device maximum transmission unit.
[1bfd3d3]656 * @return EOK on success.
657 * @return EINVAL if the packet addresses length is not long
[6067284]658 * enough.
[1bfd3d3]659 * @return EINVAL if the packet is bigger than the device MTU.
660 * @return ENOMEM if there is not enough memory in the packet.
[6067284]661 */
662static int
[46d4d9f]663eth_prepare_packet(int flags, packet_t *packet, uint8_t *src_addr, int ethertype,
[6067284]664 size_t mtu)
665{
[4e5c7ba]666 eth_header_snap_t *header;
667 eth_header_lsap_t *header_lsap;
668 eth_header_t *header_dix;
669 eth_fcs_t *fcs;
[6067284]670 uint8_t *src;
671 uint8_t *dest;
[aadf01e]672 size_t length;
673 int i;
[6067284]674 void *padding;
[4e5c7ba]675 eth_preamble_t *preamble;
[aadf01e]676
677 i = packet_get_addr(packet, &src, &dest);
[6067284]678 if (i < 0)
[aadf01e]679 return i;
[609243f4]680
[6067284]681 if (i != ETH_ADDR)
[aadf01e]682 return EINVAL;
[609243f4]683
684 for (i = 0; i < ETH_ADDR; i++) {
685 if (src[i]) {
686 src_addr = src;
687 break;
688 }
689 }
[6067284]690
[aadf01e]691 length = packet_get_data_length(packet);
[6067284]692 if (length > mtu)
[aadf01e]693 return EINVAL;
[6067284]694
695 if (length < ETH_MIN_TAGGED_CONTENT(flags)) {
696 padding = packet_suffix(packet,
697 ETH_MIN_TAGGED_CONTENT(flags) - length);
698 if (!padding)
[aadf01e]699 return ENOMEM;
[fb04cba8]700
[aadf01e]701 bzero(padding, ETH_MIN_TAGGED_CONTENT(flags) - length);
[21580dd]702 }
[6067284]703
704 if (IS_DIX(flags)) {
[aadf01e]705 header_dix = PACKET_PREFIX(packet, eth_header_t);
[6067284]706 if (!header_dix)
[aadf01e]707 return ENOMEM;
[6067284]708
[aadf01e]709 header_dix->ethertype = (uint16_t) ethertype;
710 memcpy(header_dix->source_address, src_addr, ETH_ADDR);
711 memcpy(header_dix->destination_address, dest, ETH_ADDR);
712 src = &header_dix->destination_address[0];
[609243f4]713 } else if (IS_8023_2_LSAP(flags)) {
[aadf01e]714 header_lsap = PACKET_PREFIX(packet, eth_header_lsap_t);
[6067284]715 if (!header_lsap)
[aadf01e]716 return ENOMEM;
[6067284]717
718 header_lsap->header.ethertype = htons(length +
719 sizeof(eth_header_lsap_t));
[aadf01e]720 header_lsap->lsap.dsap = lsap_unmap(ntohs(ethertype));
[21580dd]721 header_lsap->lsap.ssap = header_lsap->lsap.dsap;
722 header_lsap->lsap.ctrl = IEEE_8023_2_UI;
[aadf01e]723 memcpy(header_lsap->header.source_address, src_addr, ETH_ADDR);
724 memcpy(header_lsap->header.destination_address, dest, ETH_ADDR);
725 src = &header_lsap->header.destination_address[0];
[609243f4]726 } else if (IS_8023_2_SNAP(flags)) {
[aadf01e]727 header = PACKET_PREFIX(packet, eth_header_snap_t);
[6067284]728 if (!header)
[aadf01e]729 return ENOMEM;
[6067284]730
731 header->header.ethertype = htons(length +
732 sizeof(eth_header_lsap_t) + sizeof(eth_header_snap_t));
[aadf01e]733 header->lsap.dsap = (uint16_t) ETH_LSAP_SNAP;
[21580dd]734 header->lsap.ssap = header->lsap.dsap;
735 header->lsap.ctrl = IEEE_8023_2_UI;
[6067284]736
[609243f4]737 for (i = 0; i < 3; i++)
[aadf01e]738 header->snap.protocol[i] = 0;
[6067284]739
[aadf01e]740 header->snap.ethertype = (uint16_t) ethertype;
741 memcpy(header->header.source_address, src_addr, ETH_ADDR);
742 memcpy(header->header.destination_address, dest, ETH_ADDR);
743 src = &header->header.destination_address[0];
[21580dd]744 }
[6067284]745
746 if (IS_DUMMY(flags)) {
[aadf01e]747 preamble = PACKET_PREFIX(packet, eth_preamble_t);
[6067284]748 if (!preamble)
[aadf01e]749 return ENOMEM;
[6067284]750
[609243f4]751 for (i = 0; i < 7; i++)
[aadf01e]752 preamble->preamble[i] = ETH_PREAMBLE;
[6067284]753
[80ce111]754 preamble->sfd = ETH_SFD;
[6067284]755
[aadf01e]756 fcs = PACKET_SUFFIX(packet, eth_fcs_t);
[6067284]757 if (!fcs)
[aadf01e]758 return ENOMEM;
[6067284]759
760 *fcs = htonl(~compute_crc32(~0U, src, length * 8));
[21580dd]761 }
[6067284]762
[21580dd]763 return EOK;
764}
765
[6067284]766/** Sends the packet queue.
767 *
768 * Sends only packet successfully processed by the eth_prepare_packet()
769 * function.
770 *
771 * @param[in] device_id The device identifier.
772 * @param[in] packet The packet queue.
773 * @param[in] sender The sending module service.
[1bfd3d3]774 * @return EOK on success.
775 * @return ENOENT if there no such device.
776 * @return EINVAL if the service parameter is not known.
[6067284]777 */
[609243f4]778static int eth_send_message(nic_device_id_t device_id, packet_t *packet,
[fb04cba8]779 services_t sender)
[6067284]780{
[4e5c7ba]781 eth_device_t *device;
[46d4d9f]782 packet_t *next;
783 packet_t *tmp;
[aadf01e]784 int ethertype;
[4ef32e0c]785 int rc;
[21580dd]786
[aadf01e]787 ethertype = htons(protocol_map(SERVICE_ETHERNET, sender));
[6067284]788 if (!ethertype) {
[6b82009]789 pq_release_remote(eth_globals.net_sess, packet_get_id(packet));
[21580dd]790 return EINVAL;
791 }
[6067284]792
[aadf01e]793 fibril_rwlock_read_lock(&eth_globals.devices_lock);
794 device = eth_devices_find(&eth_globals.devices, device_id);
[6067284]795 if (!device) {
[aadf01e]796 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
[21580dd]797 return ENOENT;
798 }
[6067284]799
[fb04cba8]800 /* Process packet queue */
[21580dd]801 next = packet;
[6067284]802 do {
[4ef32e0c]803 rc = eth_prepare_packet(device->flags, next,
[609243f4]804 (uint8_t *) &device->addr.address, ethertype, device->mtu);
[4ef32e0c]805 if (rc != EOK) {
[fb04cba8]806 /* Release invalid packet */
[aadf01e]807 tmp = pq_detach(next);
[6067284]808 if (next == packet)
[aadf01e]809 packet = tmp;
[6b82009]810 pq_release_remote(eth_globals.net_sess,
[6067284]811 packet_get_id(next));
[21580dd]812 next = tmp;
[6067284]813 } else {
[6d8455d]814 nic_send_frame(device->sess, packet_get_data(next),
815 packet_get_data_length(next));
[aadf01e]816 next = pq_next(next);
[21580dd]817 }
[609243f4]818 } while (next);
[6067284]819
[6d8455d]820 pq_release_remote(eth_globals.net_sess, packet_get_id(packet));
[609243f4]821
[aadf01e]822 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
[21580dd]823 return EOK;
824}
825
[9cd8165]826static int eth_received(eth_device_t *device)
[1bc35b5]827{
828 void *data;
829 size_t size;
830 int rc;
831
832 rc = async_data_write_accept(&data, false, 0, 0, 0, &size);
[8d7ec69d]833 if (rc != EOK) {
834 printf("%s: data_write_accept() failed\n", NAME);
[1bc35b5]835 return rc;
[8d7ec69d]836 }
[1bc35b5]837
838 packet_t *packet = packet_get_1_remote(eth_globals.net_sess, size);
839 if (packet == NULL)
840 return ENOMEM;
841
842 void *pdata = packet_suffix(packet, size);
843 memcpy(pdata, data, size);
844 free(data);
845
[9cd8165]846 return nil_received_msg_local(device->device_id, packet);
[1bc35b5]847}
848
[9cd8165]849static int eth_addr_changed(eth_device_t *device)
[609243f4]850{
851 nic_address_t address;
852 size_t length;
853 ipc_callid_t data_callid;
854 if (!async_data_write_receive(&data_callid, &length)) {
855 async_answer_0(data_callid, EINVAL);
856 return EINVAL;
857 }
858 if (length > sizeof (nic_address_t)) {
859 async_answer_0(data_callid, ELIMIT);
860 return ELIMIT;
861 }
862 if (async_data_write_finalize(data_callid, &address, length) != EOK) {
863 return EINVAL;
864 }
865
866 fibril_rwlock_write_lock(&eth_globals.devices_lock);
867
[9cd8165]868 printf("Device %d changing address from " PRIMAC " to " PRIMAC "\n",
869 device->device_id, ARGSMAC(device->addr.address),
870 ARGSMAC(address.address));
871 memcpy(&device->addr, &address, sizeof (nic_address_t));
872 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
[609243f4]873
[9cd8165]874 /* Notify all upper layer modules */
875 fibril_rwlock_read_lock(&eth_globals.protos_lock);
876 int index;
877 for (index = 0; index < eth_protos_count(&eth_globals.protos); index++) {
878 eth_proto_t *proto = eth_protos_get_index(&eth_globals.protos, index);
879 if (proto->sess != NULL) {
880 il_addr_changed_msg(proto->sess, device->device_id,
881 ETH_ADDR, address.address);
882 }
[609243f4]883 }
[9cd8165]884
885 fibril_rwlock_read_unlock(&eth_globals.protos_lock);
886 return EOK;
[609243f4]887}
888
[fe8dfa6]889int nil_module_message(ipc_callid_t callid, ipc_call_t *call,
890 ipc_call_t *answer, size_t *answer_count)
[24ab58b3]891{
[46d4d9f]892 packet_t *packet;
[e417b96]893 size_t addrlen;
894 size_t prefix;
895 size_t suffix;
896 size_t content;
[4ef32e0c]897 int rc;
[24ab58b3]898
[aadf01e]899 *answer_count = 0;
[79ae36dd]900
901 if (!IPC_GET_IMETHOD(*call))
[6067284]902 return EOK;
903
[6b82009]904 async_sess_t *callback =
905 async_callback_receive_start(EXCHANGE_SERIALIZE, call);
906 if (callback)
907 return eth_register_message(NIL_GET_PROTO(*call), callback);
908
[79ae36dd]909 switch (IPC_GET_IMETHOD(*call)) {
[6067284]910 case NET_NIL_DEVICE:
[774e6d1a]911 return eth_device_message(IPC_GET_DEVICE(*call),
[609243f4]912 IPC_GET_DEVICE_HANDLE(*call), IPC_GET_MTU(*call));
[6067284]913 case NET_NIL_SEND:
[6b82009]914 rc = packet_translate_remote(eth_globals.net_sess, &packet,
[774e6d1a]915 IPC_GET_PACKET(*call));
[4ef32e0c]916 if (rc != EOK)
917 return rc;
[609243f4]918
[774e6d1a]919 return eth_send_message(IPC_GET_DEVICE(*call), packet,
920 IPC_GET_SERVICE(*call));
[6067284]921 case NET_NIL_PACKET_SPACE:
[774e6d1a]922 rc = eth_packet_space_message(IPC_GET_DEVICE(*call), &addrlen,
[4ef32e0c]923 &prefix, &content, &suffix);
924 if (rc != EOK)
925 return rc;
[609243f4]926
[774e6d1a]927 IPC_SET_ADDR(*answer, addrlen);
928 IPC_SET_PREFIX(*answer, prefix);
929 IPC_SET_CONTENT(*answer, content);
930 IPC_SET_SUFFIX(*answer, suffix);
[6067284]931 *answer_count = 4;
[609243f4]932
[6067284]933 return EOK;
934 case NET_NIL_ADDR:
[609243f4]935 rc = eth_addr_message(IPC_GET_DEVICE(*call), ETH_LOCAL_ADDR);
[4ef32e0c]936 if (rc != EOK)
937 return rc;
[609243f4]938
939 IPC_SET_ADDR(*answer, ETH_ADDR);
940 *answer_count = 1;
941
942 return EOK;
[6067284]943 case NET_NIL_BROADCAST_ADDR:
[609243f4]944 rc = eth_addr_message(IPC_GET_DEVICE(*call), ETH_BROADCAST_ADDR);
[4ef32e0c]945 if (rc != EOK)
[609243f4]946 return rc;
947
948 IPC_SET_ADDR(*answer, ETH_ADDR);
949 *answer_count = 1;
950
951 return EOK;
[21580dd]952 }
[24ab58b3]953
[21580dd]954 return ENOTSUP;
955}
956
[8d7ec69d]957static void eth_nic_cb_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
958{
[9cd8165]959 eth_device_t *device = (eth_device_t *)arg;
[8d7ec69d]960 int rc;
961
962 async_answer_0(iid, EOK);
963
964 while (true) {
965 ipc_call_t call;
966 ipc_callid_t callid = async_get_call(&call);
967
968 if (!IPC_GET_IMETHOD(call))
969 break;
970
971 switch (IPC_GET_IMETHOD(call)) {
972 case NIC_EV_DEVICE_STATE:
[9cd8165]973 rc = eth_device_state(device, IPC_GET_ARG1(call));
[8d7ec69d]974 async_answer_0(callid, (sysarg_t) rc);
975 break;
976 case NIC_EV_RECEIVED:
[9cd8165]977 rc = eth_received(device);
[8d7ec69d]978 async_answer_0(callid, (sysarg_t) rc);
979 break;
980 case NIC_EV_ADDR_CHANGED:
[9cd8165]981 rc = eth_addr_changed(device);
[8d7ec69d]982 async_answer_0(callid, (sysarg_t) rc);
983 break;
984 default:
985 async_answer_0(callid, ENOTSUP);
986 }
987 }
988}
989
[849ed54]990int main(int argc, char *argv[])
991{
992 /* Start the module */
[fe8dfa6]993 return nil_module_start(SERVICE_ETHERNET);
[849ed54]994}
995
[21580dd]996/** @}
997 */
Note: See TracBrowser for help on using the repository browser.