source: mainline/uspace/srv/net/nil/eth/eth.c@ 8d7ec69d

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8d7ec69d was 8d7ec69d, checked in by Jiri Svoboda <jiri@…>, 13 years ago

NIC should talk to its client via a callback connection with NIC-defined
protocol (was using nil, was connecting via NS).

  • Property mode set to 100644
File size: 26.0 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
175static int eth_device_state(nic_device_id_t device_id, 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)) {
185 il_device_state_msg(proto->sess, device_id, state,
[6067284]186 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
[8d7ec69d]349 rc = nic_callback_create(device->sess, device_id,
350 eth_nic_cb_connection, NULL);
351 if (rc != EOK) {
352 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
353 async_hangup(device->sess);
354 free(device);
355 return EIO;
356 }
[609243f4]357
[fb04cba8]358 /* Get hardware address */
[609243f4]359 rc = nic_get_address(device->sess, &device->addr);
[4ef32e0c]360 if (rc != EOK) {
[6067284]361 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
362 free(device);
[4ef32e0c]363 return rc;
[6067284]364 }
365
[fb04cba8]366 /* Add to the cache */
[6067284]367 index = eth_devices_add(&eth_globals.devices, device->device_id,
368 device);
369 if (index < 0) {
370 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
371 free(device);
372 return index;
373 }
374
[77a69ea]375 printf("%s: Device registered (id: %d, sid: %zu: mtu: %zu, "
[609243f4]376 "mac: " PRIMAC ", flags: 0x%x)\n", NAME,
[77a69ea]377 device->device_id, device->sid, device->mtu,
[609243f4]378 ARGSMAC(device->addr.address), device->flags);
[6067284]379
[aadf01e]380 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
[21580dd]381 return EOK;
382}
383
[6067284]384/** Processes the received packet and chooses the target registered module.
385 *
386 * @param[in] flags The device flags.
387 * @param[in] packet The packet.
[1bfd3d3]388 * @return The target registered module.
389 * @return NULL if the packet is not long enough.
390 * @return NULL if the packet is too long.
391 * @return NULL if the raw ethernet protocol is used.
392 * @return NULL if the dummy device FCS checksum is invalid.
393 * @return NULL if the packet address length is not big enough.
[6067284]394 */
[46d4d9f]395static eth_proto_t *eth_process_packet(int flags, packet_t *packet)
[6067284]396{
[4e5c7ba]397 eth_header_snap_t *header;
[aadf01e]398 size_t length;
399 eth_type_t type;
400 size_t prefix;
401 size_t suffix;
[4e5c7ba]402 eth_fcs_t *fcs;
[4ef32e0c]403 uint8_t *data;
404 int rc;
[aadf01e]405
406 length = packet_get_data_length(packet);
[6067284]407
408 if (IS_DUMMY(flags))
[aadf01e]409 packet_trim(packet, sizeof(eth_preamble_t), 0);
[6067284]410 if (length < sizeof(eth_header_t) + ETH_MIN_CONTENT +
411 (IS_DUMMY(flags) ? ETH_SUFFIX : 0))
412 return NULL;
413
[aadf01e]414 data = packet_get_data(packet);
[4e5c7ba]415 header = (eth_header_snap_t *) data;
[aadf01e]416 type = ntohs(header->header.ethertype);
[6067284]417
418 if (type >= ETH_MIN_PROTO) {
[fb04cba8]419 /* DIX Ethernet */
[aadf01e]420 prefix = sizeof(eth_header_t);
[21580dd]421 suffix = 0;
[4e5c7ba]422 fcs = (eth_fcs_t *) data + length - sizeof(eth_fcs_t);
[aadf01e]423 length -= sizeof(eth_fcs_t);
[609243f4]424 } else if (type <= ETH_MAX_CONTENT) {
[fb04cba8]425 /* Translate "LSAP" values */
[6067284]426 if ((header->lsap.dsap == ETH_LSAP_GLSAP) &&
427 (header->lsap.ssap == ETH_LSAP_GLSAP)) {
[fb04cba8]428 /* Raw packet -- discard */
[21580dd]429 return NULL;
[609243f4]430 } else if ((header->lsap.dsap == ETH_LSAP_SNAP) &&
[6067284]431 (header->lsap.ssap == ETH_LSAP_SNAP)) {
[fb04cba8]432 /*
433 * IEEE 802.3 + 802.2 + LSAP + SNAP
434 * organization code not supported
435 */
[aadf01e]436 type = ntohs(header->snap.ethertype);
[609243f4]437 prefix = sizeof(eth_header_t) + sizeof(eth_header_lsap_t) +
[6067284]438 sizeof(eth_header_snap_t);
439 } else {
[fb04cba8]440 /* IEEE 802.3 + 802.2 LSAP */
[aadf01e]441 type = lsap_map(header->lsap.dsap);
[609243f4]442 prefix = sizeof(eth_header_t) + sizeof(eth_header_lsap_t);
[21580dd]443 }
[fb04cba8]444
[6067284]445 suffix = (type < ETH_MIN_CONTENT) ? ETH_MIN_CONTENT - type : 0U;
[4e5c7ba]446 fcs = (eth_fcs_t *) data + prefix + type + suffix;
[21580dd]447 suffix += length - prefix - type;
448 length = prefix + type + suffix;
[6067284]449 } else {
[fb04cba8]450 /* Invalid length/type, should not occur */
[21580dd]451 return NULL;
452 }
[6067284]453
454 if (IS_DUMMY(flags)) {
[4ef32e0c]455 if (~compute_crc32(~0U, data, length * 8) != ntohl(*fcs))
[21580dd]456 return NULL;
[aadf01e]457 suffix += sizeof(eth_fcs_t);
[21580dd]458 }
[6067284]459
[4ef32e0c]460 rc = packet_set_addr(packet, header->header.source_address,
461 header->header.destination_address, ETH_ADDR);
462 if (rc != EOK)
463 return NULL;
464
465 rc = packet_trim(packet, prefix, suffix);
466 if (rc != EOK)
[21580dd]467 return NULL;
[6067284]468
[aadf01e]469 return eth_protos_find(&eth_globals.protos, type);
[21580dd]470}
471
[609243f4]472int nil_received_msg_local(nic_device_id_t device_id, packet_t *packet)
[6067284]473{
[4e5c7ba]474 eth_proto_t *proto;
[46d4d9f]475 packet_t *next;
[4e5c7ba]476 eth_device_t *device;
[aadf01e]477 int flags;
[21580dd]478
[aadf01e]479 fibril_rwlock_read_lock(&eth_globals.devices_lock);
480 device = eth_devices_find(&eth_globals.devices, device_id);
[6067284]481 if (!device) {
[aadf01e]482 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
[21580dd]483 return ENOENT;
484 }
[fb04cba8]485
[21580dd]486 flags = device->flags;
[aadf01e]487 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
488 fibril_rwlock_read_lock(&eth_globals.protos_lock);
[609243f4]489
[6067284]490 do {
[aadf01e]491 next = pq_detach(packet);
492 proto = eth_process_packet(flags, packet);
[6067284]493 if (proto) {
[6b82009]494 il_received_msg(proto->sess, device_id, packet,
[6067284]495 proto->service);
496 } else {
[28a3e74]497 /* Drop invalid/unknown */
[6b82009]498 pq_release_remote(eth_globals.net_sess,
[6067284]499 packet_get_id(packet));
[21580dd]500 }
501 packet = next;
[609243f4]502 } while (packet);
[fb04cba8]503
[aadf01e]504 fibril_rwlock_read_unlock(&eth_globals.protos_lock);
[21580dd]505 return EOK;
506}
507
[6067284]508/** Returns the device packet dimensions for sending.
509 *
510 * @param[in] device_id The device identifier.
511 * @param[out] addr_len The minimum reserved address length.
512 * @param[out] prefix The minimum reserved prefix size.
513 * @param[out] content The maximum content size.
514 * @param[out] suffix The minimum reserved suffix size.
[1bfd3d3]515 * @return EOK on success.
516 * @return EBADMEM if either one of the parameters is NULL.
517 * @return ENOENT if there is no such device.
[6067284]518 */
[609243f4]519static int eth_packet_space_message(nic_device_id_t device_id, size_t *addr_len,
[6067284]520 size_t *prefix, size_t *content, size_t *suffix)
521{
[4e5c7ba]522 eth_device_t *device;
[21580dd]523
[6067284]524 if (!addr_len || !prefix || !content || !suffix)
[aadf01e]525 return EBADMEM;
[6067284]526
[aadf01e]527 fibril_rwlock_read_lock(&eth_globals.devices_lock);
528 device = eth_devices_find(&eth_globals.devices, device_id);
[6067284]529 if (!device) {
[aadf01e]530 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
[21580dd]531 return ENOENT;
532 }
[fb04cba8]533
[aadf01e]534 *content = device->mtu;
535 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
[6067284]536
[aadf01e]537 *addr_len = ETH_ADDR;
538 *prefix = ETH_PREFIX;
539 *suffix = ETH_MIN_CONTENT + ETH_SUFFIX;
[fb04cba8]540
[21580dd]541 return EOK;
542}
543
[609243f4]544/** Send the device hardware address.
[6067284]545 *
546 * @param[in] device_id The device identifier.
547 * @param[in] type Type of the desired address.
[1bfd3d3]548 * @return EOK on success.
549 * @return EBADMEM if the address parameter is NULL.
550 * @return ENOENT if there no such device.
[6067284]551 */
[609243f4]552static int eth_addr_message(nic_device_id_t device_id, eth_addr_type_t type)
[6067284]553{
[609243f4]554 eth_device_t *device = NULL;
555 uint8_t *address;
556 size_t max_len;
557 ipc_callid_t callid;
558
559 if (type == ETH_BROADCAST_ADDR)
560 address = eth_globals.broadcast_addr;
561 else {
[aadf01e]562 fibril_rwlock_read_lock(&eth_globals.devices_lock);
563 device = eth_devices_find(&eth_globals.devices, device_id);
[6067284]564 if (!device) {
[aadf01e]565 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
[21580dd]566 return ENOENT;
567 }
[609243f4]568
569 address = (uint8_t *) &device->addr.address;
570 }
571
572 int rc = EOK;
573 if (!async_data_read_receive(&callid, &max_len)) {
574 rc = EREFUSED;
575 goto end;
576 }
577
578 if (max_len < ETH_ADDR) {
579 async_data_read_finalize(callid, NULL, 0);
580 rc = ELIMIT;
581 goto end;
[21580dd]582 }
[6067284]583
[609243f4]584 rc = async_data_read_finalize(callid, address, ETH_ADDR);
585 if (rc != EOK)
586 goto end;
587
588end:
589
590 if (type == ETH_LOCAL_ADDR)
591 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
592
593 return rc;
[21580dd]594}
595
[6b82009]596/** Register receiving module service.
[6067284]597 *
[6b82009]598 * Pass received packets for this service.
599 *
600 * @param[in] service Module service.
601 * @param[in] sess Service session.
602 *
603 * @return EOK on success.
604 * @return ENOENT if the service is not known.
605 * @return ENOMEM if there is not enough memory left.
[6067284]606 *
607 */
[6b82009]608static int eth_register_message(services_t service, async_sess_t *sess)
[6067284]609{
[4e5c7ba]610 eth_proto_t *proto;
[aadf01e]611 int protocol;
612 int index;
[21580dd]613
[aadf01e]614 protocol = protocol_map(SERVICE_ETHERNET, service);
[6067284]615 if (!protocol)
[aadf01e]616 return ENOENT;
[6067284]617
[aadf01e]618 fibril_rwlock_write_lock(&eth_globals.protos_lock);
619 proto = eth_protos_find(&eth_globals.protos, protocol);
[6067284]620 if (proto) {
[6b82009]621 proto->sess = sess;
[aadf01e]622 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
[21580dd]623 return EOK;
[6067284]624 } else {
[4e5c7ba]625 proto = (eth_proto_t *) malloc(sizeof(eth_proto_t));
[6067284]626 if (!proto) {
[aadf01e]627 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
[21580dd]628 return ENOMEM;
629 }
[fb04cba8]630
[21580dd]631 proto->service = service;
632 proto->protocol = protocol;
[6b82009]633 proto->sess = sess;
[fb04cba8]634
[aadf01e]635 index = eth_protos_add(&eth_globals.protos, protocol, proto);
[6067284]636 if (index < 0) {
[aadf01e]637 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
638 free(proto);
[21580dd]639 return index;
640 }
641 }
[24ab58b3]642
[00d7e1b]643 printf("%s: Protocol registered (protocol: %d, service: %#x)\n",
[6b82009]644 NAME, proto->protocol, proto->service);
[24ab58b3]645
[aadf01e]646 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
[21580dd]647 return EOK;
648}
649
[6067284]650/** Prepares the packet for sending.
651 *
652 * @param[in] flags The device flags.
653 * @param[in] packet The packet.
654 * @param[in] src_addr The source hardware address.
655 * @param[in] ethertype The ethernet protocol type.
656 * @param[in] mtu The device maximum transmission unit.
[1bfd3d3]657 * @return EOK on success.
658 * @return EINVAL if the packet addresses length is not long
[6067284]659 * enough.
[1bfd3d3]660 * @return EINVAL if the packet is bigger than the device MTU.
661 * @return ENOMEM if there is not enough memory in the packet.
[6067284]662 */
663static int
[46d4d9f]664eth_prepare_packet(int flags, packet_t *packet, uint8_t *src_addr, int ethertype,
[6067284]665 size_t mtu)
666{
[4e5c7ba]667 eth_header_snap_t *header;
668 eth_header_lsap_t *header_lsap;
669 eth_header_t *header_dix;
670 eth_fcs_t *fcs;
[6067284]671 uint8_t *src;
672 uint8_t *dest;
[aadf01e]673 size_t length;
674 int i;
[6067284]675 void *padding;
[4e5c7ba]676 eth_preamble_t *preamble;
[aadf01e]677
678 i = packet_get_addr(packet, &src, &dest);
[6067284]679 if (i < 0)
[aadf01e]680 return i;
[609243f4]681
[6067284]682 if (i != ETH_ADDR)
[aadf01e]683 return EINVAL;
[609243f4]684
685 for (i = 0; i < ETH_ADDR; i++) {
686 if (src[i]) {
687 src_addr = src;
688 break;
689 }
690 }
[6067284]691
[aadf01e]692 length = packet_get_data_length(packet);
[6067284]693 if (length > mtu)
[aadf01e]694 return EINVAL;
[6067284]695
696 if (length < ETH_MIN_TAGGED_CONTENT(flags)) {
697 padding = packet_suffix(packet,
698 ETH_MIN_TAGGED_CONTENT(flags) - length);
699 if (!padding)
[aadf01e]700 return ENOMEM;
[fb04cba8]701
[aadf01e]702 bzero(padding, ETH_MIN_TAGGED_CONTENT(flags) - length);
[21580dd]703 }
[6067284]704
705 if (IS_DIX(flags)) {
[aadf01e]706 header_dix = PACKET_PREFIX(packet, eth_header_t);
[6067284]707 if (!header_dix)
[aadf01e]708 return ENOMEM;
[6067284]709
[aadf01e]710 header_dix->ethertype = (uint16_t) ethertype;
711 memcpy(header_dix->source_address, src_addr, ETH_ADDR);
712 memcpy(header_dix->destination_address, dest, ETH_ADDR);
713 src = &header_dix->destination_address[0];
[609243f4]714 } else if (IS_8023_2_LSAP(flags)) {
[aadf01e]715 header_lsap = PACKET_PREFIX(packet, eth_header_lsap_t);
[6067284]716 if (!header_lsap)
[aadf01e]717 return ENOMEM;
[6067284]718
719 header_lsap->header.ethertype = htons(length +
720 sizeof(eth_header_lsap_t));
[aadf01e]721 header_lsap->lsap.dsap = lsap_unmap(ntohs(ethertype));
[21580dd]722 header_lsap->lsap.ssap = header_lsap->lsap.dsap;
723 header_lsap->lsap.ctrl = IEEE_8023_2_UI;
[aadf01e]724 memcpy(header_lsap->header.source_address, src_addr, ETH_ADDR);
725 memcpy(header_lsap->header.destination_address, dest, ETH_ADDR);
726 src = &header_lsap->header.destination_address[0];
[609243f4]727 } else if (IS_8023_2_SNAP(flags)) {
[aadf01e]728 header = PACKET_PREFIX(packet, eth_header_snap_t);
[6067284]729 if (!header)
[aadf01e]730 return ENOMEM;
[6067284]731
732 header->header.ethertype = htons(length +
733 sizeof(eth_header_lsap_t) + sizeof(eth_header_snap_t));
[aadf01e]734 header->lsap.dsap = (uint16_t) ETH_LSAP_SNAP;
[21580dd]735 header->lsap.ssap = header->lsap.dsap;
736 header->lsap.ctrl = IEEE_8023_2_UI;
[6067284]737
[609243f4]738 for (i = 0; i < 3; i++)
[aadf01e]739 header->snap.protocol[i] = 0;
[6067284]740
[aadf01e]741 header->snap.ethertype = (uint16_t) ethertype;
742 memcpy(header->header.source_address, src_addr, ETH_ADDR);
743 memcpy(header->header.destination_address, dest, ETH_ADDR);
744 src = &header->header.destination_address[0];
[21580dd]745 }
[6067284]746
747 if (IS_DUMMY(flags)) {
[aadf01e]748 preamble = PACKET_PREFIX(packet, eth_preamble_t);
[6067284]749 if (!preamble)
[aadf01e]750 return ENOMEM;
[6067284]751
[609243f4]752 for (i = 0; i < 7; i++)
[aadf01e]753 preamble->preamble[i] = ETH_PREAMBLE;
[6067284]754
[80ce111]755 preamble->sfd = ETH_SFD;
[6067284]756
[aadf01e]757 fcs = PACKET_SUFFIX(packet, eth_fcs_t);
[6067284]758 if (!fcs)
[aadf01e]759 return ENOMEM;
[6067284]760
761 *fcs = htonl(~compute_crc32(~0U, src, length * 8));
[21580dd]762 }
[6067284]763
[21580dd]764 return EOK;
765}
766
[6067284]767/** Sends the packet queue.
768 *
769 * Sends only packet successfully processed by the eth_prepare_packet()
770 * function.
771 *
772 * @param[in] device_id The device identifier.
773 * @param[in] packet The packet queue.
774 * @param[in] sender The sending module service.
[1bfd3d3]775 * @return EOK on success.
776 * @return ENOENT if there no such device.
777 * @return EINVAL if the service parameter is not known.
[6067284]778 */
[609243f4]779static int eth_send_message(nic_device_id_t device_id, packet_t *packet,
[fb04cba8]780 services_t sender)
[6067284]781{
[4e5c7ba]782 eth_device_t *device;
[46d4d9f]783 packet_t *next;
784 packet_t *tmp;
[aadf01e]785 int ethertype;
[4ef32e0c]786 int rc;
[21580dd]787
[aadf01e]788 ethertype = htons(protocol_map(SERVICE_ETHERNET, sender));
[6067284]789 if (!ethertype) {
[6b82009]790 pq_release_remote(eth_globals.net_sess, packet_get_id(packet));
[21580dd]791 return EINVAL;
792 }
[6067284]793
[aadf01e]794 fibril_rwlock_read_lock(&eth_globals.devices_lock);
795 device = eth_devices_find(&eth_globals.devices, device_id);
[6067284]796 if (!device) {
[aadf01e]797 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
[21580dd]798 return ENOENT;
799 }
[6067284]800
[fb04cba8]801 /* Process packet queue */
[21580dd]802 next = packet;
[6067284]803 do {
[4ef32e0c]804 rc = eth_prepare_packet(device->flags, next,
[609243f4]805 (uint8_t *) &device->addr.address, ethertype, device->mtu);
[4ef32e0c]806 if (rc != EOK) {
[fb04cba8]807 /* Release invalid packet */
[aadf01e]808 tmp = pq_detach(next);
[6067284]809 if (next == packet)
[aadf01e]810 packet = tmp;
[6b82009]811 pq_release_remote(eth_globals.net_sess,
[6067284]812 packet_get_id(next));
[21580dd]813 next = tmp;
[6067284]814 } else {
[6d8455d]815 nic_send_frame(device->sess, packet_get_data(next),
816 packet_get_data_length(next));
[aadf01e]817 next = pq_next(next);
[21580dd]818 }
[609243f4]819 } while (next);
[6067284]820
[6d8455d]821 pq_release_remote(eth_globals.net_sess, packet_get_id(packet));
[609243f4]822
[aadf01e]823 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
[21580dd]824 return EOK;
825}
826
[1bc35b5]827static int eth_received(nic_device_id_t device_id)
828{
829 void *data;
830 size_t size;
831 int rc;
832
833 rc = async_data_write_accept(&data, false, 0, 0, 0, &size);
[8d7ec69d]834 if (rc != EOK) {
835 printf("%s: data_write_accept() failed\n", NAME);
[1bc35b5]836 return rc;
[8d7ec69d]837 }
[1bc35b5]838
839 packet_t *packet = packet_get_1_remote(eth_globals.net_sess, size);
840 if (packet == NULL)
841 return ENOMEM;
842
843 void *pdata = packet_suffix(packet, size);
844 memcpy(pdata, data, size);
845 free(data);
846
847 return nil_received_msg_local(device_id, packet);
848}
849
[609243f4]850static int eth_addr_changed(nic_device_id_t device_id)
851{
852 nic_address_t address;
853 size_t length;
854 ipc_callid_t data_callid;
855 if (!async_data_write_receive(&data_callid, &length)) {
856 async_answer_0(data_callid, EINVAL);
857 return EINVAL;
858 }
859 if (length > sizeof (nic_address_t)) {
860 async_answer_0(data_callid, ELIMIT);
861 return ELIMIT;
862 }
863 if (async_data_write_finalize(data_callid, &address, length) != EOK) {
864 return EINVAL;
865 }
866
867 fibril_rwlock_write_lock(&eth_globals.devices_lock);
868 /* An existing device? */
869 eth_device_t *device = eth_devices_find(&eth_globals.devices, device_id);
870 if (device) {
871 printf("Device %d changing address from " PRIMAC " to " PRIMAC "\n",
872 device_id, ARGSMAC(device->addr.address), ARGSMAC(address.address));
873 memcpy(&device->addr, &address, sizeof (nic_address_t));
874 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
875
876 /* Notify all upper layer modules */
877 fibril_rwlock_read_lock(&eth_globals.protos_lock);
878 int index;
879 for (index = 0; index < eth_protos_count(&eth_globals.protos); index++) {
880 eth_proto_t *proto = eth_protos_get_index(&eth_globals.protos, index);
881 if (proto->sess != NULL) {
882 il_addr_changed_msg(proto->sess, device->device_id,
883 ETH_ADDR, address.address);
884 }
885 }
886
887 fibril_rwlock_read_unlock(&eth_globals.protos_lock);
888 return EOK;
889 } else {
890 return ENOENT;
891 }
892}
893
[fe8dfa6]894int nil_module_message(ipc_callid_t callid, ipc_call_t *call,
895 ipc_call_t *answer, size_t *answer_count)
[24ab58b3]896{
[46d4d9f]897 packet_t *packet;
[e417b96]898 size_t addrlen;
899 size_t prefix;
900 size_t suffix;
901 size_t content;
[4ef32e0c]902 int rc;
[24ab58b3]903
[aadf01e]904 *answer_count = 0;
[79ae36dd]905
906 if (!IPC_GET_IMETHOD(*call))
[6067284]907 return EOK;
908
[6b82009]909 async_sess_t *callback =
910 async_callback_receive_start(EXCHANGE_SERIALIZE, call);
911 if (callback)
912 return eth_register_message(NIL_GET_PROTO(*call), callback);
913
[79ae36dd]914 switch (IPC_GET_IMETHOD(*call)) {
[6067284]915 case NET_NIL_DEVICE:
[774e6d1a]916 return eth_device_message(IPC_GET_DEVICE(*call),
[609243f4]917 IPC_GET_DEVICE_HANDLE(*call), IPC_GET_MTU(*call));
[6067284]918 case NET_NIL_SEND:
[6b82009]919 rc = packet_translate_remote(eth_globals.net_sess, &packet,
[774e6d1a]920 IPC_GET_PACKET(*call));
[4ef32e0c]921 if (rc != EOK)
922 return rc;
[609243f4]923
[774e6d1a]924 return eth_send_message(IPC_GET_DEVICE(*call), packet,
925 IPC_GET_SERVICE(*call));
[6067284]926 case NET_NIL_PACKET_SPACE:
[774e6d1a]927 rc = eth_packet_space_message(IPC_GET_DEVICE(*call), &addrlen,
[4ef32e0c]928 &prefix, &content, &suffix);
929 if (rc != EOK)
930 return rc;
[609243f4]931
[774e6d1a]932 IPC_SET_ADDR(*answer, addrlen);
933 IPC_SET_PREFIX(*answer, prefix);
934 IPC_SET_CONTENT(*answer, content);
935 IPC_SET_SUFFIX(*answer, suffix);
[6067284]936 *answer_count = 4;
[609243f4]937
[6067284]938 return EOK;
939 case NET_NIL_ADDR:
[609243f4]940 rc = eth_addr_message(IPC_GET_DEVICE(*call), ETH_LOCAL_ADDR);
[4ef32e0c]941 if (rc != EOK)
942 return rc;
[609243f4]943
944 IPC_SET_ADDR(*answer, ETH_ADDR);
945 *answer_count = 1;
946
947 return EOK;
[6067284]948 case NET_NIL_BROADCAST_ADDR:
[609243f4]949 rc = eth_addr_message(IPC_GET_DEVICE(*call), ETH_BROADCAST_ADDR);
[4ef32e0c]950 if (rc != EOK)
[609243f4]951 return rc;
952
953 IPC_SET_ADDR(*answer, ETH_ADDR);
954 *answer_count = 1;
955
956 return EOK;
[21580dd]957 }
[24ab58b3]958
[21580dd]959 return ENOTSUP;
960}
961
[8d7ec69d]962static void eth_nic_cb_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
963{
964 int rc;
965
966 async_answer_0(iid, EOK);
967
968 while (true) {
969 ipc_call_t call;
970 ipc_callid_t callid = async_get_call(&call);
971
972 if (!IPC_GET_IMETHOD(call))
973 break;
974
975 switch (IPC_GET_IMETHOD(call)) {
976 case NIC_EV_DEVICE_STATE:
977 rc = eth_device_state(IPC_GET_ARG1(call),
978 IPC_GET_ARG2(call));
979 async_answer_0(callid, (sysarg_t) rc);
980 break;
981 case NIC_EV_RECEIVED:
982 rc = eth_received(IPC_GET_ARG1(call));
983 async_answer_0(callid, (sysarg_t) rc);
984 break;
985 case NIC_EV_ADDR_CHANGED:
986 rc = eth_addr_changed(IPC_GET_ARG1(call));
987 async_answer_0(callid, (sysarg_t) rc);
988 break;
989 default:
990 async_answer_0(callid, ENOTSUP);
991 }
992 }
993}
994
[849ed54]995int main(int argc, char *argv[])
996{
997 /* Start the module */
[fe8dfa6]998 return nil_module_start(SERVICE_ETHERNET);
[849ed54]999}
1000
[21580dd]1001/** @}
1002 */
Note: See TracBrowser for help on using the repository browser.