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
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(nic_device_id_t device_id, 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_id, state,
186 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, 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 }
357
358 /* Get hardware address */
359 rc = nic_get_address(device->sess, &device->addr);
360 if (rc != EOK) {
361 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
362 free(device);
363 return rc;
364 }
365
366 /* Add to the cache */
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
375 printf("%s: Device registered (id: %d, sid: %zu: mtu: %zu, "
376 "mac: " PRIMAC ", flags: 0x%x)\n", NAME,
377 device->device_id, device->sid, device->mtu,
378 ARGSMAC(device->addr.address), device->flags);
379
380 fibril_rwlock_write_unlock(&eth_globals.devices_lock);
381 return EOK;
382}
383
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.
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.
394 */
395static eth_proto_t *eth_process_packet(int flags, packet_t *packet)
396{
397 eth_header_snap_t *header;
398 size_t length;
399 eth_type_t type;
400 size_t prefix;
401 size_t suffix;
402 eth_fcs_t *fcs;
403 uint8_t *data;
404 int rc;
405
406 length = packet_get_data_length(packet);
407
408 if (IS_DUMMY(flags))
409 packet_trim(packet, sizeof(eth_preamble_t), 0);
410 if (length < sizeof(eth_header_t) + ETH_MIN_CONTENT +
411 (IS_DUMMY(flags) ? ETH_SUFFIX : 0))
412 return NULL;
413
414 data = packet_get_data(packet);
415 header = (eth_header_snap_t *) data;
416 type = ntohs(header->header.ethertype);
417
418 if (type >= ETH_MIN_PROTO) {
419 /* DIX Ethernet */
420 prefix = sizeof(eth_header_t);
421 suffix = 0;
422 fcs = (eth_fcs_t *) data + length - sizeof(eth_fcs_t);
423 length -= sizeof(eth_fcs_t);
424 } else if (type <= ETH_MAX_CONTENT) {
425 /* Translate "LSAP" values */
426 if ((header->lsap.dsap == ETH_LSAP_GLSAP) &&
427 (header->lsap.ssap == ETH_LSAP_GLSAP)) {
428 /* Raw packet -- discard */
429 return NULL;
430 } else if ((header->lsap.dsap == ETH_LSAP_SNAP) &&
431 (header->lsap.ssap == ETH_LSAP_SNAP)) {
432 /*
433 * IEEE 802.3 + 802.2 + LSAP + SNAP
434 * organization code not supported
435 */
436 type = ntohs(header->snap.ethertype);
437 prefix = sizeof(eth_header_t) + sizeof(eth_header_lsap_t) +
438 sizeof(eth_header_snap_t);
439 } else {
440 /* IEEE 802.3 + 802.2 LSAP */
441 type = lsap_map(header->lsap.dsap);
442 prefix = sizeof(eth_header_t) + sizeof(eth_header_lsap_t);
443 }
444
445 suffix = (type < ETH_MIN_CONTENT) ? ETH_MIN_CONTENT - type : 0U;
446 fcs = (eth_fcs_t *) data + prefix + type + suffix;
447 suffix += length - prefix - type;
448 length = prefix + type + suffix;
449 } else {
450 /* Invalid length/type, should not occur */
451 return NULL;
452 }
453
454 if (IS_DUMMY(flags)) {
455 if (~compute_crc32(~0U, data, length * 8) != ntohl(*fcs))
456 return NULL;
457 suffix += sizeof(eth_fcs_t);
458 }
459
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)
467 return NULL;
468
469 return eth_protos_find(&eth_globals.protos, type);
470}
471
472int nil_received_msg_local(nic_device_id_t device_id, packet_t *packet)
473{
474 eth_proto_t *proto;
475 packet_t *next;
476 eth_device_t *device;
477 int flags;
478
479 fibril_rwlock_read_lock(&eth_globals.devices_lock);
480 device = eth_devices_find(&eth_globals.devices, device_id);
481 if (!device) {
482 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
483 return ENOENT;
484 }
485
486 flags = device->flags;
487 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
488 fibril_rwlock_read_lock(&eth_globals.protos_lock);
489
490 do {
491 next = pq_detach(packet);
492 proto = eth_process_packet(flags, packet);
493 if (proto) {
494 il_received_msg(proto->sess, device_id, packet,
495 proto->service);
496 } else {
497 /* Drop invalid/unknown */
498 pq_release_remote(eth_globals.net_sess,
499 packet_get_id(packet));
500 }
501 packet = next;
502 } while (packet);
503
504 fibril_rwlock_read_unlock(&eth_globals.protos_lock);
505 return EOK;
506}
507
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.
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.
518 */
519static int eth_packet_space_message(nic_device_id_t device_id, size_t *addr_len,
520 size_t *prefix, size_t *content, size_t *suffix)
521{
522 eth_device_t *device;
523
524 if (!addr_len || !prefix || !content || !suffix)
525 return EBADMEM;
526
527 fibril_rwlock_read_lock(&eth_globals.devices_lock);
528 device = eth_devices_find(&eth_globals.devices, device_id);
529 if (!device) {
530 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
531 return ENOENT;
532 }
533
534 *content = device->mtu;
535 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
536
537 *addr_len = ETH_ADDR;
538 *prefix = ETH_PREFIX;
539 *suffix = ETH_MIN_CONTENT + ETH_SUFFIX;
540
541 return EOK;
542}
543
544/** Send the device hardware address.
545 *
546 * @param[in] device_id The device identifier.
547 * @param[in] type Type of the desired address.
548 * @return EOK on success.
549 * @return EBADMEM if the address parameter is NULL.
550 * @return ENOENT if there no such device.
551 */
552static int eth_addr_message(nic_device_id_t device_id, eth_addr_type_t type)
553{
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 {
562 fibril_rwlock_read_lock(&eth_globals.devices_lock);
563 device = eth_devices_find(&eth_globals.devices, device_id);
564 if (!device) {
565 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
566 return ENOENT;
567 }
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;
582 }
583
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;
594}
595
596/** Register receiving module service.
597 *
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.
606 *
607 */
608static int eth_register_message(services_t service, async_sess_t *sess)
609{
610 eth_proto_t *proto;
611 int protocol;
612 int index;
613
614 protocol = protocol_map(SERVICE_ETHERNET, service);
615 if (!protocol)
616 return ENOENT;
617
618 fibril_rwlock_write_lock(&eth_globals.protos_lock);
619 proto = eth_protos_find(&eth_globals.protos, protocol);
620 if (proto) {
621 proto->sess = sess;
622 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
623 return EOK;
624 } else {
625 proto = (eth_proto_t *) malloc(sizeof(eth_proto_t));
626 if (!proto) {
627 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
628 return ENOMEM;
629 }
630
631 proto->service = service;
632 proto->protocol = protocol;
633 proto->sess = sess;
634
635 index = eth_protos_add(&eth_globals.protos, protocol, proto);
636 if (index < 0) {
637 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
638 free(proto);
639 return index;
640 }
641 }
642
643 printf("%s: Protocol registered (protocol: %d, service: %#x)\n",
644 NAME, proto->protocol, proto->service);
645
646 fibril_rwlock_write_unlock(&eth_globals.protos_lock);
647 return EOK;
648}
649
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.
657 * @return EOK on success.
658 * @return EINVAL if the packet addresses length is not long
659 * enough.
660 * @return EINVAL if the packet is bigger than the device MTU.
661 * @return ENOMEM if there is not enough memory in the packet.
662 */
663static int
664eth_prepare_packet(int flags, packet_t *packet, uint8_t *src_addr, int ethertype,
665 size_t mtu)
666{
667 eth_header_snap_t *header;
668 eth_header_lsap_t *header_lsap;
669 eth_header_t *header_dix;
670 eth_fcs_t *fcs;
671 uint8_t *src;
672 uint8_t *dest;
673 size_t length;
674 int i;
675 void *padding;
676 eth_preamble_t *preamble;
677
678 i = packet_get_addr(packet, &src, &dest);
679 if (i < 0)
680 return i;
681
682 if (i != ETH_ADDR)
683 return EINVAL;
684
685 for (i = 0; i < ETH_ADDR; i++) {
686 if (src[i]) {
687 src_addr = src;
688 break;
689 }
690 }
691
692 length = packet_get_data_length(packet);
693 if (length > mtu)
694 return EINVAL;
695
696 if (length < ETH_MIN_TAGGED_CONTENT(flags)) {
697 padding = packet_suffix(packet,
698 ETH_MIN_TAGGED_CONTENT(flags) - length);
699 if (!padding)
700 return ENOMEM;
701
702 bzero(padding, ETH_MIN_TAGGED_CONTENT(flags) - length);
703 }
704
705 if (IS_DIX(flags)) {
706 header_dix = PACKET_PREFIX(packet, eth_header_t);
707 if (!header_dix)
708 return ENOMEM;
709
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];
714 } else if (IS_8023_2_LSAP(flags)) {
715 header_lsap = PACKET_PREFIX(packet, eth_header_lsap_t);
716 if (!header_lsap)
717 return ENOMEM;
718
719 header_lsap->header.ethertype = htons(length +
720 sizeof(eth_header_lsap_t));
721 header_lsap->lsap.dsap = lsap_unmap(ntohs(ethertype));
722 header_lsap->lsap.ssap = header_lsap->lsap.dsap;
723 header_lsap->lsap.ctrl = IEEE_8023_2_UI;
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];
727 } else if (IS_8023_2_SNAP(flags)) {
728 header = PACKET_PREFIX(packet, eth_header_snap_t);
729 if (!header)
730 return ENOMEM;
731
732 header->header.ethertype = htons(length +
733 sizeof(eth_header_lsap_t) + sizeof(eth_header_snap_t));
734 header->lsap.dsap = (uint16_t) ETH_LSAP_SNAP;
735 header->lsap.ssap = header->lsap.dsap;
736 header->lsap.ctrl = IEEE_8023_2_UI;
737
738 for (i = 0; i < 3; i++)
739 header->snap.protocol[i] = 0;
740
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];
745 }
746
747 if (IS_DUMMY(flags)) {
748 preamble = PACKET_PREFIX(packet, eth_preamble_t);
749 if (!preamble)
750 return ENOMEM;
751
752 for (i = 0; i < 7; i++)
753 preamble->preamble[i] = ETH_PREAMBLE;
754
755 preamble->sfd = ETH_SFD;
756
757 fcs = PACKET_SUFFIX(packet, eth_fcs_t);
758 if (!fcs)
759 return ENOMEM;
760
761 *fcs = htonl(~compute_crc32(~0U, src, length * 8));
762 }
763
764 return EOK;
765}
766
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.
775 * @return EOK on success.
776 * @return ENOENT if there no such device.
777 * @return EINVAL if the service parameter is not known.
778 */
779static int eth_send_message(nic_device_id_t device_id, packet_t *packet,
780 services_t sender)
781{
782 eth_device_t *device;
783 packet_t *next;
784 packet_t *tmp;
785 int ethertype;
786 int rc;
787
788 ethertype = htons(protocol_map(SERVICE_ETHERNET, sender));
789 if (!ethertype) {
790 pq_release_remote(eth_globals.net_sess, packet_get_id(packet));
791 return EINVAL;
792 }
793
794 fibril_rwlock_read_lock(&eth_globals.devices_lock);
795 device = eth_devices_find(&eth_globals.devices, device_id);
796 if (!device) {
797 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
798 return ENOENT;
799 }
800
801 /* Process packet queue */
802 next = packet;
803 do {
804 rc = eth_prepare_packet(device->flags, next,
805 (uint8_t *) &device->addr.address, ethertype, device->mtu);
806 if (rc != EOK) {
807 /* Release invalid packet */
808 tmp = pq_detach(next);
809 if (next == packet)
810 packet = tmp;
811 pq_release_remote(eth_globals.net_sess,
812 packet_get_id(next));
813 next = tmp;
814 } else {
815 nic_send_frame(device->sess, packet_get_data(next),
816 packet_get_data_length(next));
817 next = pq_next(next);
818 }
819 } while (next);
820
821 pq_release_remote(eth_globals.net_sess, packet_get_id(packet));
822
823 fibril_rwlock_read_unlock(&eth_globals.devices_lock);
824 return EOK;
825}
826
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);
834 if (rc != EOK) {
835 printf("%s: data_write_accept() failed\n", NAME);
836 return rc;
837 }
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
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
894int nil_module_message(ipc_callid_t callid, ipc_call_t *call,
895 ipc_call_t *answer, size_t *answer_count)
896{
897 packet_t *packet;
898 size_t addrlen;
899 size_t prefix;
900 size_t suffix;
901 size_t content;
902 int rc;
903
904 *answer_count = 0;
905
906 if (!IPC_GET_IMETHOD(*call))
907 return EOK;
908
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
914 switch (IPC_GET_IMETHOD(*call)) {
915 case NET_NIL_DEVICE:
916 return eth_device_message(IPC_GET_DEVICE(*call),
917 IPC_GET_DEVICE_HANDLE(*call), IPC_GET_MTU(*call));
918 case NET_NIL_SEND:
919 rc = packet_translate_remote(eth_globals.net_sess, &packet,
920 IPC_GET_PACKET(*call));
921 if (rc != EOK)
922 return rc;
923
924 return eth_send_message(IPC_GET_DEVICE(*call), packet,
925 IPC_GET_SERVICE(*call));
926 case NET_NIL_PACKET_SPACE:
927 rc = eth_packet_space_message(IPC_GET_DEVICE(*call), &addrlen,
928 &prefix, &content, &suffix);
929 if (rc != EOK)
930 return rc;
931
932 IPC_SET_ADDR(*answer, addrlen);
933 IPC_SET_PREFIX(*answer, prefix);
934 IPC_SET_CONTENT(*answer, content);
935 IPC_SET_SUFFIX(*answer, suffix);
936 *answer_count = 4;
937
938 return EOK;
939 case NET_NIL_ADDR:
940 rc = eth_addr_message(IPC_GET_DEVICE(*call), ETH_LOCAL_ADDR);
941 if (rc != EOK)
942 return rc;
943
944 IPC_SET_ADDR(*answer, ETH_ADDR);
945 *answer_count = 1;
946
947 return EOK;
948 case NET_NIL_BROADCAST_ADDR:
949 rc = eth_addr_message(IPC_GET_DEVICE(*call), ETH_BROADCAST_ADDR);
950 if (rc != EOK)
951 return rc;
952
953 IPC_SET_ADDR(*answer, ETH_ADDR);
954 *answer_count = 1;
955
956 return EOK;
957 }
958
959 return ENOTSUP;
960}
961
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
995int main(int argc, char *argv[])
996{
997 /* Start the module */
998 return nil_module_start(SERVICE_ETHERNET);
999}
1000
1001/** @}
1002 */
Note: See TracBrowser for help on using the repository browser.