source: mainline/uspace/srv/net/nil/eth/eth.c@ 711e7fe5

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

Added printf module

  • Property mode set to 100755
File size: 25.9 KB
Line 
1/*
2 * Copyright (c) 2009 Lukas Mejdrech
3 * Copyright (c) 2011 Radim Vansa
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
39#include <assert.h>
40#include <async.h>
41#include <malloc.h>
42#include <mem.h>
43#include <stdio.h>
44#include <byteorder.h>
45#include <str.h>
46#include <errno.h>
47#include <ipc/nil.h>
48#include <ipc/net.h>
49#include <ipc/services.h>
50#include <loc.h>
51#include <net/modules.h>
52#include <net_checksum.h>
53#include <ethernet_lsap.h>
54#include <ethernet_protocols.h>
55#include <protocol_map.h>
56#include <net/device.h>
57#include <net_interface.h>
58#include <il_remote.h>
59#include <adt/measured_strings.h>
60#include <packet_client.h>
61#include <packet_remote.h>
62#include <device/nic.h>
63#include <nil_skel.h>
64#include "eth.h"
65
66/** The module name. */
67#define NAME "eth"
68
69/** Reserved packet prefix length. */
70#define ETH_PREFIX \
71 (sizeof(eth_header_t) + sizeof(eth_header_lsap_t) + \
72 sizeof(eth_header_snap_t))
73
74/** Reserved packet suffix length. */
75#define ETH_SUFFIX (sizeof(eth_fcs_t))
76
77/** Maximum packet content length. */
78#define ETH_MAX_CONTENT 1500u
79
80/** Minimum packet content length. */
81#define ETH_MIN_CONTENT 46u
82
83/** Maximum tagged packet content length. */
84#define ETH_MAX_TAGGED_CONTENT(flags) \
85 (ETH_MAX_CONTENT - \
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))
89
90/** Minimum tagged packet content length. */
91#define ETH_MIN_TAGGED_CONTENT(flags) \
92 (ETH_MIN_CONTENT - \
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))
96
97/** Dummy flag shift value. */
98#define ETH_DUMMY_SHIFT 0
99
100/** Mode flag shift value. */
101#define ETH_MODE_SHIFT 1
102
103/** Dummy device flag.
104 * Preamble and FCS are mandatory part of the packets.
105 */
106#define ETH_DUMMY (1 << ETH_DUMMY_SHIFT)
107
108/** Returns the dummy flag.
109 * @see ETH_DUMMY
110 */
111#define IS_DUMMY(flags) ((flags) & ETH_DUMMY)
112
113/** Device mode flags.
114 * @see ETH_DIX
115 * @see ETH_8023_2_LSAP
116 * @see ETH_8023_2_SNAP
117 */
118#define ETH_MODE_MASK (3 << ETH_MODE_SHIFT)
119
120/** DIX Ethernet mode flag. */
121#define ETH_DIX (1 << ETH_MODE_SHIFT)
122
123/** Return whether the DIX Ethernet mode flag is set.
124 *
125 * @param[in] flags Ethernet flags.
126 * @see ETH_DIX
127 *
128 */
129#define IS_DIX(flags) (((flags) & ETH_MODE_MASK) == ETH_DIX)
130
131/** 802.3 + 802.2 + LSAP mode flag. */
132#define ETH_8023_2_LSAP (2 << ETH_MODE_SHIFT)
133
134/** Return whether the 802.3 + 802.2 + LSAP mode flag is set.
135 *
136 * @param[in] flags Ethernet flags.
137 * @see ETH_8023_2_LSAP
138 *
139 */
140#define IS_8023_2_LSAP(flags) (((flags) & ETH_MODE_MASK) == ETH_8023_2_LSAP)
141
142/** 802.3 + 802.2 + LSAP + SNAP mode flag. */
143#define ETH_8023_2_SNAP (3 << ETH_MODE_SHIFT)
144
145/** Return whether the 802.3 + 802.2 + LSAP + SNAP mode flag is set.
146 *
147 * @param[in] flags Ethernet flags.
148 * @see ETH_8023_2_SNAP
149 *
150 */
151#define IS_8023_2_SNAP(flags) (((flags) & ETH_MODE_MASK) == ETH_8023_2_SNAP)
152
153/** Type definition of the ethernet address type.
154 * @see eth_addr_type
155 */
156typedef enum eth_addr_type eth_addr_type_t;
157
158/** Ethernet address type. */
159enum eth_addr_type {
160 /** Local address. */
161 ETH_LOCAL_ADDR,
162 /** Broadcast address. */
163 ETH_BROADCAST_ADDR
164};
165
166/** Ethernet module global data. */
167eth_globals_t eth_globals;
168
169DEVICE_MAP_IMPLEMENT(eth_devices, eth_device_t);
170INT_MAP_IMPLEMENT(eth_protos, eth_proto_t);
171
172static void eth_nic_cb_connection(ipc_callid_t iid, ipc_call_t *icall,
173 void *arg);
174
175static int eth_device_state(eth_device_t *device, sysarg_t state)
176{
177 int index;
178 eth_proto_t *proto;
179
180 fibril_rwlock_read_lock(&eth_globals.protos_lock);
181 for (index = eth_protos_count(&eth_globals.protos) - 1; index >= 0;
182 index--) {
183 proto = eth_protos_get_index(&eth_globals.protos, index);
184 if ((proto) && (proto->sess)) {
185 il_device_state_msg(proto->sess, device->device_id,
186 state, proto->service);
187 }
188 }
189 fibril_rwlock_read_unlock(&eth_globals.protos_lock);
190
191 return EOK;
192}
193
194int nil_initialize(async_sess_t *sess)
195{
196 int rc;
197
198 fibril_rwlock_initialize(&eth_globals.devices_lock);
199 fibril_rwlock_initialize(&eth_globals.protos_lock);
200
201 fibril_rwlock_write_lock(&eth_globals.devices_lock);
202 fibril_rwlock_write_lock(&eth_globals.protos_lock);
203
204 eth_globals.net_sess = sess;
205 memcpy(eth_globals.broadcast_addr, "\xFF\xFF\xFF\xFF\xFF\xFF",
206 ETH_ADDR);
207
208 rc = eth_devices_initialize(&eth_globals.devices);
209 if (rc != EOK) {
210 free(eth_globals.broadcast_addr);
211 goto out;
212 }
213
214 rc = eth_protos_initialize(&eth_globals.protos);
215 if (rc != EOK) {
216 free(eth_globals.broadcast_addr);
217 eth_devices_destroy(&eth_globals.devices, free);
218 }
219
220out:
221 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
222 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
223
224 return rc;
225}
226
227/** Register new device or updates the MTU of an existing one.
228 *
229 * Determine the device local hardware address.
230 *
231 * @param[in] device_id New device identifier.
232 * @param[in] sid NIC service ID.
233 * @param[in] mtu Device maximum transmission unit.
234 *
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.
238 *
239 */
240static int eth_device_message(nic_device_id_t device_id, service_id_t sid,
241 size_t mtu)
242{
243 eth_device_t *device;
244 int index;
245 measured_string_t names[2] = {
246 {
247 (uint8_t *) "ETH_MODE",
248 8
249 },
250 {
251 (uint8_t *) "ETH_DUMMY",
252 9
253 }
254 };
255 measured_string_t *configuration;
256 size_t count = sizeof(names) / sizeof(measured_string_t);
257 uint8_t *data;
258 eth_proto_t *proto;
259 int rc;
260
261 fibril_rwlock_write_lock(&eth_globals.devices_lock);
262 /* An existing device? */
263 device = eth_devices_find(&eth_globals.devices, device_id);
264 if (device) {
265 if (device->sid != sid) {
266 printf("Device %d already exists\n", device->device_id);
267 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
268 return EEXIST;
269 }
270
271 /* Update mtu */
272 if ((mtu > 0) && (mtu <= ETH_MAX_TAGGED_CONTENT(device->flags)))
273 device->mtu = mtu;
274 else
275 device->mtu = ETH_MAX_TAGGED_CONTENT(device->flags);
276
277 printf("Device %d already exists:\tMTU\t= %zu\n",
278 device->device_id, device->mtu);
279 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
280
281 /* Notify all upper layer modules */
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);
287 if (proto->sess) {
288 il_mtu_changed_msg(proto->sess,
289 device->device_id, device->mtu,
290 proto->service);
291 }
292 }
293
294 fibril_rwlock_read_unlock(&eth_globals.protos_lock);
295 return EOK;
296 }
297
298 /* Create a new device */
299 device = (eth_device_t *) malloc(sizeof(eth_device_t));
300 if (!device)
301 return ENOMEM;
302
303 device->device_id = device_id;
304 device->sid = sid;
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];
312 rc = net_get_device_conf_req(eth_globals.net_sess, device->device_id,
313 &configuration, count, &data);
314 if (rc != EOK) {
315 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
316 free(device);
317 return rc;
318 }
319
320 if (configuration) {
321 if (!str_lcmp((char *) configuration[0].value, "DIX",
322 configuration[0].length)) {
323 device->flags |= ETH_DIX;
324 } else if(!str_lcmp((char *) configuration[0].value, "8023_2_LSAP",
325 configuration[0].length)) {
326 device->flags |= ETH_8023_2_LSAP;
327 } else {
328 device->flags |= ETH_8023_2_SNAP;
329 }
330
331 if (configuration[1].value &&
332 (configuration[1].value[0] == 'y')) {
333 device->flags |= ETH_DUMMY;
334 }
335 net_free_settings(configuration, data);
336 } else {
337 device->flags |= ETH_8023_2_SNAP;
338 }
339
340 /* Bind the device driver */
341 device->sess = loc_service_connect(EXCHANGE_SERIALIZE, sid,
342 IPC_FLAG_BLOCKING);
343 if (device->sess == NULL) {
344 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
345 free(device);
346 return ENOENT;
347 }
348
349 rc = nic_callback_create(device->sess, eth_nic_cb_connection, device);
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 }
356
357 /* Get hardware address */
358 rc = nic_get_address(device->sess, &device->addr);
359 if (rc != EOK) {
360 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
361 free(device);
362 return rc;
363 }
364
365 /* Add to the cache */
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
374 printf("%s: Device registered (id: %d, sid: %zu: mtu: %zu, "
375 "mac: " PRIMAC ", flags: 0x%x)\n", NAME,
376 device->device_id, device->sid, device->mtu,
377 ARGSMAC(device->addr.address), device->flags);
378
379 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
380 return EOK;
381}
382
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.
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.
393 */
394static eth_proto_t *eth_process_packet(int flags, packet_t *packet)
395{
396 eth_header_snap_t *header;
397 size_t length;
398 eth_type_t type;
399 size_t prefix;
400 size_t suffix;
401 eth_fcs_t *fcs;
402 uint8_t *data;
403 int rc;
404
405 length = packet_get_data_length(packet);
406
407 if (IS_DUMMY(flags))
408 packet_trim(packet, sizeof(eth_preamble_t), 0);
409 if (length < sizeof(eth_header_t) + ETH_MIN_CONTENT +
410 (IS_DUMMY(flags) ? ETH_SUFFIX : 0))
411 return NULL;
412
413 data = packet_get_data(packet);
414 header = (eth_header_snap_t *) data;
415 type = ntohs(header->header.ethertype);
416
417 if (type >= ETH_MIN_PROTO) {
418 /* DIX Ethernet */
419 prefix = sizeof(eth_header_t);
420 suffix = 0;
421 fcs = (eth_fcs_t *) data + length - sizeof(eth_fcs_t);
422 length -= sizeof(eth_fcs_t);
423 } else if (type <= ETH_MAX_CONTENT) {
424 /* Translate "LSAP" values */
425 if ((header->lsap.dsap == ETH_LSAP_GLSAP) &&
426 (header->lsap.ssap == ETH_LSAP_GLSAP)) {
427 /* Raw packet -- discard */
428 return NULL;
429 } else if ((header->lsap.dsap == ETH_LSAP_SNAP) &&
430 (header->lsap.ssap == ETH_LSAP_SNAP)) {
431 /*
432 * IEEE 802.3 + 802.2 + LSAP + SNAP
433 * organization code not supported
434 */
435 type = ntohs(header->snap.ethertype);
436 prefix = sizeof(eth_header_t) + sizeof(eth_header_lsap_t) +
437 sizeof(eth_header_snap_t);
438 } else {
439 /* IEEE 802.3 + 802.2 LSAP */
440 type = lsap_map(header->lsap.dsap);
441 prefix = sizeof(eth_header_t) + sizeof(eth_header_lsap_t);
442 }
443
444 suffix = (type < ETH_MIN_CONTENT) ? ETH_MIN_CONTENT - type : 0U;
445 fcs = (eth_fcs_t *) data + prefix + type + suffix;
446 suffix += length - prefix - type;
447 length = prefix + type + suffix;
448 } else {
449 /* Invalid length/type, should not occur */
450 return NULL;
451 }
452
453 if (IS_DUMMY(flags)) {
454 if (~compute_crc32(~0U, data, length * 8) != ntohl(*fcs))
455 return NULL;
456 suffix += sizeof(eth_fcs_t);
457 }
458
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)
466 return NULL;
467
468 return eth_protos_find(&eth_globals.protos, type);
469}
470
471int nil_received_msg_local(nic_device_id_t device_id, packet_t *packet)
472{
473 eth_proto_t *proto;
474 packet_t *next;
475 eth_device_t *device;
476 int flags;
477
478 fibril_rwlock_read_lock(&eth_globals.devices_lock);
479 device = eth_devices_find(&eth_globals.devices, device_id);
480 if (!device) {
481 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
482 return ENOENT;
483 }
484
485 flags = device->flags;
486 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
487 fibril_rwlock_read_lock(&eth_globals.protos_lock);
488
489 do {
490 next = pq_detach(packet);
491 proto = eth_process_packet(flags, packet);
492 if (proto) {
493 il_received_msg(proto->sess, device_id, packet,
494 proto->service);
495 } else {
496 /* Drop invalid/unknown */
497 pq_release_remote(eth_globals.net_sess,
498 packet_get_id(packet));
499 }
500 packet = next;
501 } while (packet);
502
503 fibril_rwlock_read_unlock(&eth_globals.protos_lock);
504 return EOK;
505}
506
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.
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.
517 */
518static int eth_packet_space_message(nic_device_id_t device_id, size_t *addr_len,
519 size_t *prefix, size_t *content, size_t *suffix)
520{
521 eth_device_t *device;
522
523 if (!addr_len || !prefix || !content || !suffix)
524 return EBADMEM;
525
526 fibril_rwlock_read_lock(&eth_globals.devices_lock);
527 device = eth_devices_find(&eth_globals.devices, device_id);
528 if (!device) {
529 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
530 return ENOENT;
531 }
532
533 *content = device->mtu;
534 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
535
536 *addr_len = ETH_ADDR;
537 *prefix = ETH_PREFIX;
538 *suffix = ETH_MIN_CONTENT + ETH_SUFFIX;
539
540 return EOK;
541}
542
543/** Send the device hardware address.
544 *
545 * @param[in] device_id The device identifier.
546 * @param[in] type Type of the desired address.
547 * @return EOK on success.
548 * @return EBADMEM if the address parameter is NULL.
549 * @return ENOENT if there no such device.
550 */
551static int eth_addr_message(nic_device_id_t device_id, eth_addr_type_t type)
552{
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 {
561 fibril_rwlock_read_lock(&eth_globals.devices_lock);
562 device = eth_devices_find(&eth_globals.devices, device_id);
563 if (!device) {
564 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
565 return ENOENT;
566 }
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;
581 }
582
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;
593}
594
595/** Register receiving module service.
596 *
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.
605 *
606 */
607static int eth_register_message(services_t service, async_sess_t *sess)
608{
609 eth_proto_t *proto;
610 int protocol;
611 int index;
612
613 protocol = protocol_map(SERVICE_ETHERNET, service);
614 if (!protocol)
615 return ENOENT;
616
617 fibril_rwlock_write_lock(&eth_globals.protos_lock);
618 proto = eth_protos_find(&eth_globals.protos, protocol);
619 if (proto) {
620 proto->sess = sess;
621 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
622 return EOK;
623 } else {
624 proto = (eth_proto_t *) malloc(sizeof(eth_proto_t));
625 if (!proto) {
626 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
627 return ENOMEM;
628 }
629
630 proto->service = service;
631 proto->protocol = protocol;
632 proto->sess = sess;
633
634 index = eth_protos_add(&eth_globals.protos, protocol, proto);
635 if (index < 0) {
636 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
637 free(proto);
638 return index;
639 }
640 }
641
642 printf("%s: Protocol registered (protocol: %d, service: %#x)\n",
643 NAME, proto->protocol, proto->service);
644
645 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
646 return EOK;
647}
648
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.
656 * @return EOK on success.
657 * @return EINVAL if the packet addresses length is not long
658 * enough.
659 * @return EINVAL if the packet is bigger than the device MTU.
660 * @return ENOMEM if there is not enough memory in the packet.
661 */
662static int
663eth_prepare_packet(int flags, packet_t *packet, uint8_t *src_addr, int ethertype,
664 size_t mtu)
665{
666 eth_header_snap_t *header;
667 eth_header_lsap_t *header_lsap;
668 eth_header_t *header_dix;
669 eth_fcs_t *fcs;
670 uint8_t *src;
671 uint8_t *dest;
672 size_t length;
673 int i;
674 void *padding;
675 eth_preamble_t *preamble;
676
677 i = packet_get_addr(packet, &src, &dest);
678 if (i < 0)
679 return i;
680
681 if (i != ETH_ADDR)
682 return EINVAL;
683
684 for (i = 0; i < ETH_ADDR; i++) {
685 if (src[i]) {
686 src_addr = src;
687 break;
688 }
689 }
690
691 length = packet_get_data_length(packet);
692 if (length > mtu)
693 return EINVAL;
694
695 if (length < ETH_MIN_TAGGED_CONTENT(flags)) {
696 padding = packet_suffix(packet,
697 ETH_MIN_TAGGED_CONTENT(flags) - length);
698 if (!padding)
699 return ENOMEM;
700
701 bzero(padding, ETH_MIN_TAGGED_CONTENT(flags) - length);
702 }
703
704 if (IS_DIX(flags)) {
705 header_dix = PACKET_PREFIX(packet, eth_header_t);
706 if (!header_dix)
707 return ENOMEM;
708
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];
713 } else if (IS_8023_2_LSAP(flags)) {
714 header_lsap = PACKET_PREFIX(packet, eth_header_lsap_t);
715 if (!header_lsap)
716 return ENOMEM;
717
718 header_lsap->header.ethertype = htons(length +
719 sizeof(eth_header_lsap_t));
720 header_lsap->lsap.dsap = lsap_unmap(ntohs(ethertype));
721 header_lsap->lsap.ssap = header_lsap->lsap.dsap;
722 header_lsap->lsap.ctrl = IEEE_8023_2_UI;
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];
726 } else if (IS_8023_2_SNAP(flags)) {
727 header = PACKET_PREFIX(packet, eth_header_snap_t);
728 if (!header)
729 return ENOMEM;
730
731 header->header.ethertype = htons(length +
732 sizeof(eth_header_lsap_t) + sizeof(eth_header_snap_t));
733 header->lsap.dsap = (uint16_t) ETH_LSAP_SNAP;
734 header->lsap.ssap = header->lsap.dsap;
735 header->lsap.ctrl = IEEE_8023_2_UI;
736
737 for (i = 0; i < 3; i++)
738 header->snap.protocol[i] = 0;
739
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];
744 }
745
746 if (IS_DUMMY(flags)) {
747 preamble = PACKET_PREFIX(packet, eth_preamble_t);
748 if (!preamble)
749 return ENOMEM;
750
751 for (i = 0; i < 7; i++)
752 preamble->preamble[i] = ETH_PREAMBLE;
753
754 preamble->sfd = ETH_SFD;
755
756 fcs = PACKET_SUFFIX(packet, eth_fcs_t);
757 if (!fcs)
758 return ENOMEM;
759
760 *fcs = htonl(~compute_crc32(~0U, src, length * 8));
761 }
762
763 return EOK;
764}
765
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.
774 * @return EOK on success.
775 * @return ENOENT if there no such device.
776 * @return EINVAL if the service parameter is not known.
777 */
778static int eth_send_message(nic_device_id_t device_id, packet_t *packet,
779 services_t sender)
780{
781 eth_device_t *device;
782 packet_t *next;
783 packet_t *tmp;
784 int ethertype;
785 int rc;
786
787 ethertype = htons(protocol_map(SERVICE_ETHERNET, sender));
788 if (!ethertype) {
789 pq_release_remote(eth_globals.net_sess, packet_get_id(packet));
790 return EINVAL;
791 }
792
793 fibril_rwlock_read_lock(&eth_globals.devices_lock);
794 device = eth_devices_find(&eth_globals.devices, device_id);
795 if (!device) {
796 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
797 return ENOENT;
798 }
799
800 /* Process packet queue */
801 next = packet;
802 do {
803 rc = eth_prepare_packet(device->flags, next,
804 (uint8_t *) &device->addr.address, ethertype, device->mtu);
805 if (rc != EOK) {
806 /* Release invalid packet */
807 tmp = pq_detach(next);
808 if (next == packet)
809 packet = tmp;
810 pq_release_remote(eth_globals.net_sess,
811 packet_get_id(next));
812 next = tmp;
813 } else {
814 nic_send_frame(device->sess, packet_get_data(next),
815 packet_get_data_length(next));
816 next = pq_next(next);
817 }
818 } while (next);
819
820 pq_release_remote(eth_globals.net_sess, packet_get_id(packet));
821
822 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
823 return EOK;
824}
825
826static int eth_received(eth_device_t *device)
827{
828 void *data;
829 size_t size;
830 int rc;
831
832 rc = async_data_write_accept(&data, false, 0, 0, 0, &size);
833 if (rc != EOK) {
834 printf("%s: data_write_accept() failed\n", NAME);
835 return rc;
836 }
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
846 return nil_received_msg_local(device->device_id, packet);
847}
848
849static int eth_addr_changed(eth_device_t *device)
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
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);
873
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 }
883 }
884
885 fibril_rwlock_read_unlock(&eth_globals.protos_lock);
886 return EOK;
887}
888
889int nil_module_message(ipc_callid_t callid, ipc_call_t *call,
890 ipc_call_t *answer, size_t *answer_count)
891{
892 packet_t *packet;
893 size_t addrlen;
894 size_t prefix;
895 size_t suffix;
896 size_t content;
897 int rc;
898
899 *answer_count = 0;
900
901 if (!IPC_GET_IMETHOD(*call))
902 return EOK;
903
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
909 switch (IPC_GET_IMETHOD(*call)) {
910 case NET_NIL_DEVICE:
911 return eth_device_message(IPC_GET_DEVICE(*call),
912 IPC_GET_DEVICE_HANDLE(*call), IPC_GET_MTU(*call));
913 case NET_NIL_SEND:
914 rc = packet_translate_remote(eth_globals.net_sess, &packet,
915 IPC_GET_PACKET(*call));
916 if (rc != EOK)
917 return rc;
918
919 return eth_send_message(IPC_GET_DEVICE(*call), packet,
920 IPC_GET_SERVICE(*call));
921 case NET_NIL_PACKET_SPACE:
922 rc = eth_packet_space_message(IPC_GET_DEVICE(*call), &addrlen,
923 &prefix, &content, &suffix);
924 if (rc != EOK)
925 return rc;
926
927 IPC_SET_ADDR(*answer, addrlen);
928 IPC_SET_PREFIX(*answer, prefix);
929 IPC_SET_CONTENT(*answer, content);
930 IPC_SET_SUFFIX(*answer, suffix);
931 *answer_count = 4;
932
933 return EOK;
934 case NET_NIL_ADDR:
935 rc = eth_addr_message(IPC_GET_DEVICE(*call), ETH_LOCAL_ADDR);
936 if (rc != EOK)
937 return rc;
938
939 IPC_SET_ADDR(*answer, ETH_ADDR);
940 *answer_count = 1;
941
942 return EOK;
943 case NET_NIL_BROADCAST_ADDR:
944 rc = eth_addr_message(IPC_GET_DEVICE(*call), ETH_BROADCAST_ADDR);
945 if (rc != EOK)
946 return rc;
947
948 IPC_SET_ADDR(*answer, ETH_ADDR);
949 *answer_count = 1;
950
951 return EOK;
952 }
953
954 return ENOTSUP;
955}
956
957static void eth_nic_cb_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
958{
959 eth_device_t *device = (eth_device_t *)arg;
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:
973 rc = eth_device_state(device, IPC_GET_ARG1(call));
974 async_answer_0(callid, (sysarg_t) rc);
975 break;
976 case NIC_EV_RECEIVED:
977 rc = eth_received(device);
978 async_answer_0(callid, (sysarg_t) rc);
979 break;
980 case NIC_EV_ADDR_CHANGED:
981 rc = eth_addr_changed(device);
982 async_answer_0(callid, (sysarg_t) rc);
983 break;
984 default:
985 async_answer_0(callid, ENOTSUP);
986 }
987 }
988}
989
990int main(int argc, char *argv[])
991{
992 /* Start the module */
993 return nil_module_start(SERVICE_ETHERNET);
994}
995
996/** @}
997 */
Note: See TracBrowser for help on using the repository browser.