source: mainline/uspace/lib/nic/src/nic_driver.c@ 01380b1

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

libnic no longer needs net_session.

  • Property mode set to 100644
File size: 32.9 KB
Line 
1/*
2 * Copyright (c) 2011 Radim Vansa
3 * Copyright (c) 2011 Jiri Michalec
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/**
31 * @addtogroup libnic
32 * @{
33 */
34/**
35 * @file
36 * @brief Internal implementation of general NIC operations
37 */
38
39#include <assert.h>
40#include <fibril_synch.h>
41#include <ns.h>
42#include <stdio.h>
43#include <str_error.h>
44#include <ipc/services.h>
45#include <ipc/ns.h>
46#include <ipc/irc.h>
47#include <sysinfo.h>
48#include <as.h>
49#include <devman.h>
50#include <ddf/interrupt.h>
51#include <ops/nic.h>
52#include <errno.h>
53
54#include "nic_driver.h"
55#include "nic_impl.h"
56
57#define NIC_GLOBALS_MAX_CACHE_SIZE 16
58
59nic_globals_t nic_globals;
60
61/**
62 * Initializes libraries required for NIC framework - logger
63 *
64 * @param name Name of the device/driver (used in logging)
65 */
66int nic_driver_init(const char *name)
67{
68 list_initialize(&nic_globals.frame_list_cache);
69 nic_globals.frame_list_cache_size = 0;
70 list_initialize(&nic_globals.frame_cache);
71 nic_globals.frame_cache_size = 0;
72 fibril_mutex_initialize(&nic_globals.lock);
73
74 char buffer[256];
75 snprintf(buffer, 256, "drv/" DEVICE_CATEGORY_NIC "/%s", name);
76
77 return EOK;
78}
79
80/**
81 * Fill in the default implementations for device options and NIC interface.
82 *
83 * @param driver_ops
84 * @param dev_ops
85 * @param iface
86 */
87void nic_driver_implement(driver_ops_t *driver_ops, ddf_dev_ops_t *dev_ops,
88 nic_iface_t *iface)
89{
90 if (dev_ops) {
91 if (!dev_ops->open)
92 dev_ops->open = nic_open_impl;
93 if (!dev_ops->close)
94 dev_ops->close = nic_close_impl;
95 if (!dev_ops->interfaces[NIC_DEV_IFACE])
96 dev_ops->interfaces[NIC_DEV_IFACE] = iface;
97 if (!dev_ops->default_handler)
98 dev_ops->default_handler = nic_default_handler_impl;
99 }
100
101 if (iface) {
102 if (!iface->get_state)
103 iface->get_state = nic_get_state_impl;
104 if (!iface->set_state)
105 iface->set_state = nic_set_state_impl;
106 if (!iface->send_frame)
107 iface->send_frame = nic_send_frame_impl;
108 if (!iface->connect_to_nil)
109 iface->connect_to_nil = nic_connect_to_nil_impl;
110 if (!iface->get_address)
111 iface->get_address = nic_get_address_impl;
112 if (!iface->get_stats)
113 iface->get_stats = nic_get_stats_impl;
114 if (!iface->unicast_get_mode)
115 iface->unicast_get_mode = nic_unicast_get_mode_impl;
116 if (!iface->unicast_set_mode)
117 iface->unicast_set_mode = nic_unicast_set_mode_impl;
118 if (!iface->multicast_get_mode)
119 iface->multicast_get_mode = nic_multicast_get_mode_impl;
120 if (!iface->multicast_set_mode)
121 iface->multicast_set_mode = nic_multicast_set_mode_impl;
122 if (!iface->broadcast_get_mode)
123 iface->broadcast_get_mode = nic_broadcast_get_mode_impl;
124 if (!iface->broadcast_set_mode)
125 iface->broadcast_set_mode = nic_broadcast_set_mode_impl;
126 if (!iface->blocked_sources_get)
127 iface->blocked_sources_get = nic_blocked_sources_get_impl;
128 if (!iface->blocked_sources_set)
129 iface->blocked_sources_set = nic_blocked_sources_set_impl;
130 if (!iface->vlan_get_mask)
131 iface->vlan_get_mask = nic_vlan_get_mask_impl;
132 if (!iface->vlan_set_mask)
133 iface->vlan_set_mask = nic_vlan_set_mask_impl;
134 if (!iface->wol_virtue_add)
135 iface->wol_virtue_add = nic_wol_virtue_add_impl;
136 if (!iface->wol_virtue_remove)
137 iface->wol_virtue_remove = nic_wol_virtue_remove_impl;
138 if (!iface->wol_virtue_probe)
139 iface->wol_virtue_probe = nic_wol_virtue_probe_impl;
140 if (!iface->wol_virtue_list)
141 iface->wol_virtue_list = nic_wol_virtue_list_impl;
142 if (!iface->wol_virtue_get_caps)
143 iface->wol_virtue_get_caps = nic_wol_virtue_get_caps_impl;
144 if (!iface->poll_get_mode)
145 iface->poll_get_mode = nic_poll_get_mode_impl;
146 if (!iface->poll_set_mode)
147 iface->poll_set_mode = nic_poll_set_mode_impl;
148 if (!iface->poll_now)
149 iface->poll_now = nic_poll_now_impl;
150 }
151}
152
153/**
154 * Setup send frame handler. This MUST be called in the add_device handler
155 * if the nic_send_message_impl function is used for sending messages (filled
156 * as send_message member of the nic_iface_t structure). The function must not
157 * be called anywhere else.
158 *
159 * @param nic_data
160 * @param sffunc Function handling the send_frame request
161 */
162void nic_set_send_frame_handler(nic_t *nic_data, send_frame_handler sffunc)
163{
164 nic_data->send_frame = sffunc;
165}
166
167/**
168 * Setup event handlers for transitions between driver states.
169 * This function can be called only in the add_device handler.
170 *
171 * @param on_activating Called when device is going to the ACTIVE state.
172 * @param on_going_down Called when device is going to the DOWN state.
173 * @param on_stopping Called when device is going to the STOP state.
174 */
175void nic_set_state_change_handlers(nic_t *nic_data,
176 state_change_handler on_activating, state_change_handler on_going_down,
177 state_change_handler on_stopping)
178{
179 nic_data->on_activating = on_activating;
180 nic_data->on_going_down = on_going_down;
181 nic_data->on_stopping = on_stopping;
182}
183
184/**
185 * Setup event handlers for changing the filtering modes.
186 * This function can be called only in the add_device handler.
187 *
188 * @param nic_data
189 * @param on_unicast_mode_change
190 * @param on_multicast_mode_change
191 * @param on_broadcast_mode_change
192 * @param on_blocked_sources_change
193 * @param on_vlan_mask_change
194 */
195void nic_set_filtering_change_handlers(nic_t *nic_data,
196 unicast_mode_change_handler on_unicast_mode_change,
197 multicast_mode_change_handler on_multicast_mode_change,
198 broadcast_mode_change_handler on_broadcast_mode_change,
199 blocked_sources_change_handler on_blocked_sources_change,
200 vlan_mask_change_handler on_vlan_mask_change)
201{
202 nic_data->on_unicast_mode_change = on_unicast_mode_change;
203 nic_data->on_multicast_mode_change = on_multicast_mode_change;
204 nic_data->on_broadcast_mode_change = on_broadcast_mode_change;
205 nic_data->on_blocked_sources_change = on_blocked_sources_change;
206 nic_data->on_vlan_mask_change = on_vlan_mask_change;
207}
208
209/**
210 * Setup filters for WOL virtues add and removal.
211 * This function can be called only in the add_device handler. Both handlers
212 * must be set or none of them.
213 *
214 * @param on_wv_add Called when a virtue is added
215 * @param on_wv_remove Called when a virtue is removed
216 */
217void nic_set_wol_virtue_change_handlers(nic_t *nic_data,
218 wol_virtue_add_handler on_wv_add, wol_virtue_remove_handler on_wv_remove)
219{
220 assert(on_wv_add != NULL && on_wv_remove != NULL);
221 nic_data->on_wol_virtue_add = on_wv_add;
222 nic_data->on_wol_virtue_remove = on_wv_remove;
223}
224
225/**
226 * Setup poll handlers.
227 * This function can be called only in the add_device handler.
228 *
229 * @param on_poll_mode_change Called when the mode is about to be changed
230 * @param on_poll_request Called when poll request is triggered
231 */
232void nic_set_poll_handlers(nic_t *nic_data,
233 poll_mode_change_handler on_poll_mode_change, poll_request_handler on_poll_req)
234{
235 nic_data->on_poll_mode_change = on_poll_mode_change;
236 nic_data->on_poll_request = on_poll_req;
237}
238
239/**
240 * Connect to the parent's driver and get HW resources list in parsed format.
241 * Note: this function should be called only from add_device handler, therefore
242 * we don't need to use locks.
243 *
244 * @param nic_data
245 * @param[out] resources Parsed lists of resources.
246 *
247 * @return EOK or negative error code
248 */
249int nic_get_resources(nic_t *nic_data, hw_res_list_parsed_t *resources)
250{
251 ddf_dev_t *dev = nic_data->dev;
252
253 /* Connect to the parent's driver. */
254 dev->parent_sess = devman_parent_device_connect(EXCHANGE_SERIALIZE,
255 dev->handle, IPC_FLAG_BLOCKING);
256 if (dev->parent_sess == NULL)
257 return EPARTY;
258
259 return hw_res_get_list_parsed(nic_data->dev->parent_sess, resources, 0);
260}
261
262/** Allocate frame
263 *
264 * @param nic_data The NIC driver data
265 * @param size Frame size in bytes
266 * @return pointer to allocated frame if success, NULL otherwise
267 */
268nic_frame_t *nic_alloc_frame(nic_t *nic_data, size_t size)
269{
270 nic_frame_t *frame;
271 fibril_mutex_lock(&nic_globals.lock);
272 if (nic_globals.frame_cache_size > 0) {
273 link_t *first = list_first(&nic_globals.frame_cache);
274 list_remove(first);
275 nic_globals.frame_cache_size--;
276 frame = list_get_instance(first, nic_frame_t, link);
277 fibril_mutex_unlock(&nic_globals.lock);
278 } else {
279 fibril_mutex_unlock(&nic_globals.lock);
280 frame = malloc(sizeof(nic_frame_t));
281 if (!frame)
282 return NULL;
283
284 link_initialize(&frame->link);
285 }
286
287 frame->data = malloc(size);
288 if (frame->data == NULL) {
289 free(frame);
290 return NULL;
291 }
292
293 frame->size = size;
294 return frame;
295}
296
297/** Release frame
298 *
299 * @param nic_data The driver data
300 * @param frame The frame to release
301 */
302void nic_release_frame(nic_t *nic_data, nic_frame_t *frame)
303{
304 if (!frame)
305 return;
306
307 if (frame->data != NULL) {
308 free(frame->data);
309 frame->data = NULL;
310 frame->size = 0;
311 }
312
313 fibril_mutex_lock(&nic_globals.lock);
314 if (nic_globals.frame_cache_size >= NIC_GLOBALS_MAX_CACHE_SIZE) {
315 fibril_mutex_unlock(&nic_globals.lock);
316 free(frame);
317 } else {
318 list_prepend(&frame->link, &nic_globals.frame_cache);
319 nic_globals.frame_cache_size++;
320 fibril_mutex_unlock(&nic_globals.lock);
321 }
322}
323
324/**
325 * Allocate a new frame list
326 *
327 * @return New frame list or NULL on error.
328 */
329nic_frame_list_t *nic_alloc_frame_list(void)
330{
331 nic_frame_list_t *frames;
332 fibril_mutex_lock(&nic_globals.lock);
333
334 if (nic_globals.frame_list_cache_size > 0) {
335 frames =
336 list_get_instance(list_first(&nic_globals.frame_list_cache),
337 nic_frame_list_t, head);
338 list_remove(&frames->head);
339 list_initialize(frames);
340 nic_globals.frame_list_cache_size--;
341 fibril_mutex_unlock(&nic_globals.lock);
342 } else {
343 fibril_mutex_unlock(&nic_globals.lock);
344
345 frames = malloc(sizeof (nic_frame_list_t));
346 if (frames != NULL)
347 list_initialize(frames);
348 }
349
350 return frames;
351}
352
353static void nic_driver_release_frame_list(nic_frame_list_t *frames)
354{
355 if (!frames)
356 return;
357 fibril_mutex_lock(&nic_globals.lock);
358 if (nic_globals.frame_list_cache_size >= NIC_GLOBALS_MAX_CACHE_SIZE) {
359 fibril_mutex_unlock(&nic_globals.lock);
360 free(frames);
361 } else {
362 list_prepend(&frames->head, &nic_globals.frame_list_cache);
363 nic_globals.frame_list_cache_size++;
364 fibril_mutex_unlock(&nic_globals.lock);
365 }
366}
367
368/**
369 * Append a frame to the frame list
370 *
371 * @param frames Frame list
372 * @param frame Appended frame
373 */
374void nic_frame_list_append(nic_frame_list_t *frames,
375 nic_frame_t *frame)
376{
377 assert(frame != NULL && frames != NULL);
378 list_append(&frame->link, frames);
379}
380
381
382/**
383 * Enable interrupts for this driver.
384 *
385 * @param nic_data
386 * @param irq The IRQ number for this device
387 */
388void nic_enable_interrupt(nic_t *nic_data, int irq)
389{
390 async_exch_t *exch = async_exchange_begin(nic_data->irc_session);
391 async_msg_1(exch, IRC_ENABLE_INTERRUPT, irq);
392 async_exchange_end(exch);
393}
394
395/**
396 * Disable interrupts for this driver.
397 *
398 * @param nic_data
399 * @param irq The IRQ number for this device
400 */
401void nic_disable_interrupt(nic_t *nic_data, int irq)
402{
403 async_exch_t *exch = async_exchange_begin(nic_data->irc_session);
404 async_msg_1(exch, IRC_CLEAR_INTERRUPT, irq);
405 async_exchange_end(exch);
406}
407
408/** Get the polling mode information from the device
409 *
410 * The main lock should be locked, otherwise the inconsistency between
411 * mode and period can occure.
412 *
413 * @param nic_data The controller data
414 * @param period [out] The the period. Valid only if mode == NIC_POLL_PERIODIC
415 * @return Current polling mode of the controller
416 */
417nic_poll_mode_t nic_query_poll_mode(nic_t *nic_data, struct timeval *period)
418{
419 if (period)
420 *period = nic_data->poll_period;
421 return nic_data->poll_mode;
422};
423
424/**
425 * Connect to IRC service. This function should be called only from
426 * the add_device handler, thus no locking is required.
427 *
428 * @param nic_data
429 *
430 * @return EOK If connection was successful.
431 * @return EINVAL If the IRC service cannot be determined.
432 * @return EREFUSED If IRC service cannot be connected.
433 */
434int nic_connect_to_services(nic_t *nic_data)
435{
436 /* IRC service */
437 sysarg_t apic;
438 sysarg_t i8259;
439 services_t irc_service = -1;
440 if (((sysinfo_get_value("apic", &apic) == EOK) && (apic)) ||
441 ((sysinfo_get_value("i8259", &i8259) == EOK) && (i8259)))
442 irc_service = SERVICE_IRC;
443 else
444 return EINVAL;
445
446 nic_data->irc_session = service_connect_blocking(EXCHANGE_SERIALIZE,
447 irc_service, 0, 0);
448 if (nic_data->irc_session == NULL)
449 return errno;
450
451 return EOK;
452}
453
454/** Inform the NICF about poll mode
455 *
456 * @param nic_data The controller data
457 * @param mode
458 * @param period [out] The the period. Valid only if mode == NIC_POLL_PERIODIC
459 * @return EOK
460 * @return EINVAL
461 */
462int nic_report_poll_mode(nic_t *nic_data, nic_poll_mode_t mode,
463 struct timeval *period)
464{
465 int rc = EOK;
466 fibril_rwlock_write_lock(&nic_data->main_lock);
467 nic_data->poll_mode = mode;
468 nic_data->default_poll_mode = mode;
469 if (mode == NIC_POLL_PERIODIC) {
470 if (period) {
471 memcpy(&nic_data->default_poll_period, period, sizeof (struct timeval));
472 memcpy(&nic_data->poll_period, period, sizeof (struct timeval));
473 } else {
474 rc = EINVAL;
475 }
476 }
477 fibril_rwlock_write_unlock(&nic_data->main_lock);
478 return rc;
479}
480
481/** Inform the NICF about device's MAC adress.
482 *
483 * @return EOK On success
484 *
485 */
486int nic_report_address(nic_t *nic_data, const nic_address_t *address)
487{
488 assert(nic_data);
489
490 if (address->address[0] & 1)
491 return EINVAL;
492
493 fibril_rwlock_write_lock(&nic_data->main_lock);
494
495 /* Notify NIL layer (and uppper) if bound - not in add_device */
496 if (nic_data->nil_session != NULL) {
497 int rc = nil_addr_changed_msg(nic_data->nil_session,
498 nic_data->device_id, address);
499 if (rc != EOK) {
500 fibril_rwlock_write_unlock(&nic_data->main_lock);
501 return rc;
502 }
503 }
504
505 fibril_rwlock_write_lock(&nic_data->rxc_lock);
506
507 /*
508 * The initial address (all zeroes) shouldn't be
509 * there and we will ignore that error -- in next
510 * calls this should not happen.
511 */
512 int rc = nic_rxc_set_addr(&nic_data->rx_control,
513 &nic_data->mac, address);
514
515 /* For the first time also record the default MAC */
516 if (MAC_IS_ZERO(nic_data->default_mac.address)) {
517 assert(MAC_IS_ZERO(nic_data->mac.address));
518 memcpy(&nic_data->default_mac, address, sizeof(nic_address_t));
519 }
520
521 fibril_rwlock_write_unlock(&nic_data->rxc_lock);
522
523 if ((rc != EOK) && (rc != ENOENT)) {
524 fibril_rwlock_write_unlock(&nic_data->main_lock);
525 return rc;
526 }
527
528 memcpy(&nic_data->mac, address, sizeof(nic_address_t));
529
530 fibril_rwlock_write_unlock(&nic_data->main_lock);
531
532 return EOK;
533}
534
535/**
536 * Used to obtain devices MAC address.
537 *
538 * The main lock should be locked, otherwise the inconsistent address
539 * can be returend.
540 *
541 * @param nic_data The controller data
542 * @param address The output for address.
543 */
544void nic_query_address(nic_t *nic_data, nic_address_t *addr) {
545 if (!addr)
546 return;
547 if (!nic_data)
548 memset(addr, 0, sizeof(nic_address_t));
549
550 memcpy(addr, &nic_data->mac, sizeof(nic_address_t));
551};
552
553/**
554 * The busy flag can be set to 1 only in the send_frame handler, to 0 it can
555 * be set anywhere.
556 *
557 * @param nic_data
558 * @param busy
559 */
560void nic_set_tx_busy(nic_t *nic_data, int busy)
561{
562 /*
563 * When the function is called in send_frame handler the main lock is
564 * locked so no race can happen.
565 * Otherwise, when it is unexpectedly set to 0 (even with main lock held
566 * by other fibril) it cannot crash anything.
567 */
568 nic_data->tx_busy = busy;
569}
570
571/**
572 * This is the function that the driver should call when it receives a frame.
573 * The frame is checked by filters and then sent up to the NIL layer or
574 * discarded. The frame is released.
575 *
576 * @param nic_data
577 * @param frame The received frame
578 */
579void nic_received_frame(nic_t *nic_data, nic_frame_t *frame)
580{
581 /* Note: this function must not lock main lock, because loopback driver
582 * calls it inside send_frame handler (with locked main lock) */
583 fibril_rwlock_read_lock(&nic_data->rxc_lock);
584 nic_frame_type_t frame_type;
585 int check = nic_rxc_check(&nic_data->rx_control, frame->data,
586 frame->size, &frame_type);
587 fibril_rwlock_read_unlock(&nic_data->rxc_lock);
588 /* Update statistics */
589 fibril_rwlock_write_lock(&nic_data->stats_lock);
590
591 if (nic_data->state == NIC_STATE_ACTIVE && check) {
592 nic_data->stats.receive_packets++;
593 nic_data->stats.receive_bytes += frame->size;
594 switch (frame_type) {
595 case NIC_FRAME_MULTICAST:
596 nic_data->stats.receive_multicast++;
597 break;
598 case NIC_FRAME_BROADCAST:
599 nic_data->stats.receive_broadcast++;
600 break;
601 default:
602 break;
603 }
604 fibril_rwlock_write_unlock(&nic_data->stats_lock);
605 nil_received_msg(nic_data->nil_session, nic_data->device_id,
606 frame->data, frame->size);
607 } else {
608 switch (frame_type) {
609 case NIC_FRAME_UNICAST:
610 nic_data->stats.receive_filtered_unicast++;
611 break;
612 case NIC_FRAME_MULTICAST:
613 nic_data->stats.receive_filtered_multicast++;
614 break;
615 case NIC_FRAME_BROADCAST:
616 nic_data->stats.receive_filtered_broadcast++;
617 break;
618 }
619 fibril_rwlock_write_unlock(&nic_data->stats_lock);
620 }
621 nic_release_frame(nic_data, frame);
622}
623
624/**
625 * This function is to be used only in the loopback driver. It's workaround
626 * for the situation when the frame does not contain ethernet address.
627 * The filtering is therefore not applied here.
628 *
629 * @param nic_data
630 * @param data Frame data
631 * @param size Frame size in bytes
632 */
633void nic_received_noneth_frame(nic_t *nic_data, void *data, size_t size)
634{
635 fibril_rwlock_write_lock(&nic_data->stats_lock);
636 nic_data->stats.receive_packets++;
637 nic_data->stats.receive_bytes += size;
638 fibril_rwlock_write_unlock(&nic_data->stats_lock);
639
640 nil_received_msg(nic_data->nil_session, nic_data->device_id,
641 data, size);
642}
643
644/**
645 * Some NICs can receive multiple frames during single interrupt. These can
646 * send them in whole list of frames (actually nic_frame_t structures), then
647 * the list is deallocated and each frame is passed to the
648 * nic_received_packet function.
649 *
650 * @param nic_data
651 * @param frames List of received frames
652 */
653void nic_received_frame_list(nic_t *nic_data, nic_frame_list_t *frames)
654{
655 if (frames == NULL)
656 return;
657 while (!list_empty(frames)) {
658 nic_frame_t *frame =
659 list_get_instance(list_first(frames), nic_frame_t, link);
660
661 list_remove(&frame->link);
662 nic_received_frame(nic_data, frame);
663 }
664 nic_driver_release_frame_list(frames);
665}
666
667/** Allocate and initialize the driver data.
668 *
669 * @return Allocated structure or NULL.
670 *
671 */
672static nic_t *nic_create(void)
673{
674 nic_t *nic_data = malloc(sizeof(nic_t));
675 if (nic_data == NULL)
676 return NULL;
677
678 /* Force zero to all uninitialized fields (e.g. added in future) */
679 bzero(nic_data, sizeof(nic_t));
680 if (nic_rxc_init(&nic_data->rx_control) != EOK) {
681 free(nic_data);
682 return NULL;
683 }
684
685 if (nic_wol_virtues_init(&nic_data->wol_virtues) != EOK) {
686 free(nic_data);
687 return NULL;
688 }
689
690 nic_data->dev = NULL;
691 nic_data->fun = NULL;
692 nic_data->device_id = NIC_DEVICE_INVALID_ID;
693 nic_data->state = NIC_STATE_STOPPED;
694 nic_data->nil_session = NULL;
695 nic_data->irc_session = NULL;
696 nic_data->poll_mode = NIC_POLL_IMMEDIATE;
697 nic_data->default_poll_mode = NIC_POLL_IMMEDIATE;
698 nic_data->send_frame = NULL;
699 nic_data->on_activating = NULL;
700 nic_data->on_going_down = NULL;
701 nic_data->on_stopping = NULL;
702 nic_data->specific = NULL;
703
704 fibril_rwlock_initialize(&nic_data->main_lock);
705 fibril_rwlock_initialize(&nic_data->stats_lock);
706 fibril_rwlock_initialize(&nic_data->rxc_lock);
707 fibril_rwlock_initialize(&nic_data->wv_lock);
708
709 bzero(&nic_data->mac, sizeof(nic_address_t));
710 bzero(&nic_data->default_mac, sizeof(nic_address_t));
711 bzero(&nic_data->stats, sizeof(nic_device_stats_t));
712
713 return nic_data;
714}
715
716/** Create NIC structure for the device and bind it to dev_fun_t
717 *
718 * The pointer to the created and initialized NIC structure will
719 * be stored in device->nic_data.
720 *
721 * @param device The NIC device structure
722 *
723 * @return Pointer to created nic_t structure or NULL
724 *
725 */
726nic_t *nic_create_and_bind(ddf_dev_t *device)
727{
728 assert(device);
729 assert(!device->driver_data);
730
731 nic_t *nic_data = nic_create();
732 if (!nic_data)
733 return NULL;
734
735 nic_data->dev = device;
736 device->driver_data = nic_data;
737
738 return nic_data;
739}
740
741/**
742 * Hangs up the phones in the structure, deallocates specific data and then
743 * the structure itself.
744 *
745 * @param data
746 */
747static void nic_destroy(nic_t *nic_data) {
748 if (nic_data->nil_session != NULL) {
749 async_hangup(nic_data->nil_session);
750 }
751
752 free(nic_data->specific);
753 free(nic_data);
754}
755
756/**
757 * Unbind and destroy nic_t stored in ddf_dev_t.nic_data.
758 * The ddf_dev_t.nic_data will be set to NULL, specific driver data will be
759 * destroyed.
760 *
761 * @param device The NIC device structure
762 */
763void nic_unbind_and_destroy(ddf_dev_t *device){
764 if (!device)
765 return;
766 if (!device->driver_data)
767 return;
768
769 nic_destroy((nic_t *) device->driver_data);
770 device->driver_data = NULL;
771 return;
772}
773
774/**
775 * Set information about current HW filtering.
776 * 1 ... Only those frames we want to receive are passed through HW
777 * 0 ... The HW filtering is imperfect
778 * -1 ... Don't change the setting
779 * Can be called only from the on_*_change handler.
780 *
781 * @param nic_data
782 * @param unicast_exact Unicast frames
783 * @param mcast_exact Multicast frames
784 * @param vlan_exact VLAN tags
785 */
786void nic_report_hw_filtering(nic_t *nic_data,
787 int unicast_exact, int multicast_exact, int vlan_exact)
788{
789 nic_rxc_hw_filtering(&nic_data->rx_control,
790 unicast_exact, multicast_exact, vlan_exact);
791}
792
793/**
794 * Computes hash for the address list based on standard multicast address
795 * hashing.
796 *
797 * @param address_list
798 * @param count
799 *
800 * @return Multicast hash
801 *
802 * @see multicast_hash
803 */
804uint64_t nic_mcast_hash(const nic_address_t *list, size_t count)
805{
806 return nic_rxc_mcast_hash(list, count);
807}
808
809/**
810 * Computes hash for multicast addresses currently set up in the RX multicast
811 * filtering. For promiscuous mode returns all ones, for blocking all zeroes.
812 * Can be called only from the state change handlers (on_activating,
813 * on_going_down and on_stopping).
814 *
815 * @param nic_data
816 *
817 * @return Multicast hash
818 *
819 * @see multicast_hash
820 */
821uint64_t nic_query_mcast_hash(nic_t *nic_data)
822{
823 fibril_rwlock_read_lock(&nic_data->rxc_lock);
824 uint64_t hash = nic_rxc_multicast_get_hash(&nic_data->rx_control);
825 fibril_rwlock_read_unlock(&nic_data->rxc_lock);
826 return hash;
827}
828
829/**
830 * Queries the current mode of unicast frames receiving.
831 * Can be called only from the on_*_change handler.
832 *
833 * @param nic_data
834 * @param mode The new unicast mode
835 * @param max_count Max number of addresses that can be written into the
836 * address_list.
837 * @param address_list List of MAC addresses or NULL.
838 * @param address_count Number of addresses in the list
839 */
840void nic_query_unicast(const nic_t *nic_data,
841 nic_unicast_mode_t *mode,
842 size_t max_count, nic_address_t *address_list, size_t *address_count)
843{
844 assert(mode != NULL);
845 nic_rxc_unicast_get_mode(&nic_data->rx_control, mode,
846 max_count, address_list, address_count);
847}
848
849/**
850 * Queries the current mode of multicast frames receiving.
851 * Can be called only from the on_*_change handler.
852 *
853 * @param nic_data
854 * @param mode The current multicast mode
855 * @param max_count Max number of addresses that can be written into the
856 * address_list.
857 * @param address_list List of MAC addresses or NULL.
858 * @param address_count Number of addresses in the list
859 */
860void nic_query_multicast(const nic_t *nic_data,
861 nic_multicast_mode_t *mode,
862 size_t max_count, nic_address_t *address_list, size_t *address_count)
863{
864 assert(mode != NULL);
865 nic_rxc_multicast_get_mode(&nic_data->rx_control, mode,
866 max_count, address_list, address_count);
867}
868
869/**
870 * Queries the current mode of broadcast frames receiving.
871 * Can be called only from the on_*_change handler.
872 *
873 * @param nic_data
874 * @param mode The new broadcast mode
875 */
876void nic_query_broadcast(const nic_t *nic_data,
877 nic_broadcast_mode_t *mode)
878{
879 assert(mode != NULL);
880 nic_rxc_broadcast_get_mode(&nic_data->rx_control, mode);
881}
882
883/**
884 * Queries the current blocked source addresses.
885 * Can be called only from the on_*_change handler.
886 *
887 * @param nic_data
888 * @param max_count Max number of addresses that can be written into the
889 * address_list.
890 * @param address_list List of MAC addresses or NULL.
891 * @param address_count Number of addresses in the list
892 */
893void nic_query_blocked_sources(const nic_t *nic_data,
894 size_t max_count, nic_address_t *address_list, size_t *address_count)
895{
896 nic_rxc_blocked_sources_get(&nic_data->rx_control,
897 max_count, address_list, address_count);
898}
899
900/**
901 * Query mask used for filtering according to the VLAN tags.
902 * Can be called only from the on_*_change handler.
903 *
904 * @param nic_data
905 * @param mask Must be 512 bytes long
906 *
907 * @return EOK
908 * @return ENOENT
909 */
910int nic_query_vlan_mask(const nic_t *nic_data, nic_vlan_mask_t *mask)
911{
912 assert(mask);
913 return nic_rxc_vlan_get_mask(&nic_data->rx_control, mask);
914}
915
916/**
917 * Query maximum number of WOL virtues of specified type allowed on the device.
918 * Can be called only from add_device and on_wol_virtue_* handlers.
919 *
920 * @param nic_data
921 * @param type The type of the WOL virtues
922 *
923 * @return Maximal number of allowed virtues of this type. -1 means this type
924 * is not supported at all.
925 */
926int nic_query_wol_max_caps(const nic_t *nic_data, nic_wv_type_t type)
927{
928 return nic_data->wol_virtues.caps_max[type];
929}
930
931/**
932 * Sets maximum number of WOL virtues of specified type allowed on the device.
933 * Can be called only from add_device and on_wol_virtue_* handlers.
934 *
935 * @param nic_data
936 * @param type The type of the WOL virtues
937 * @param count Maximal number of allowed virtues of this type. -1 means
938 * this type is not supported at all.
939 */
940void nic_set_wol_max_caps(nic_t *nic_data, nic_wv_type_t type, int count)
941{
942 nic_data->wol_virtues.caps_max[type] = count;
943}
944
945/**
946 * @param nic_data
947 * @return The driver-specific structure for this NIC.
948 */
949void *nic_get_specific(nic_t *nic_data)
950{
951 return nic_data->specific;
952}
953
954/**
955 * @param nic_data
956 * @param specific The driver-specific structure for this NIC.
957 */
958void nic_set_specific(nic_t *nic_data, void *specific)
959{
960 nic_data->specific = specific;
961}
962
963/**
964 * You can call the function only from one of the state change handlers.
965 * @param nic_data
966 * @return Current state of the NIC, prior to the actually executed change
967 */
968nic_device_state_t nic_query_state(nic_t *nic_data)
969{
970 return nic_data->state;
971}
972
973/**
974 * @param nic_data
975 * @return DDF device associated with this NIC.
976 */
977ddf_dev_t *nic_get_ddf_dev(nic_t *nic_data)
978{
979 return nic_data->dev;
980}
981
982/**
983 * @param nic_data
984 * @return DDF function associated with this NIC.
985 */
986ddf_fun_t *nic_get_ddf_fun(nic_t *nic_data)
987{
988 return nic_data->fun;
989}
990
991/**
992 * @param nic_data
993 * @param fun
994 */
995void nic_set_ddf_fun(nic_t *nic_data, ddf_fun_t *fun)
996{
997 nic_data->fun = fun;
998}
999
1000/**
1001 * @param dev DDF device associated with NIC
1002 * @return The associated NIC structure
1003 */
1004nic_t *nic_get_from_ddf_dev(ddf_dev_t *dev)
1005{
1006 return (nic_t *) dev->driver_data;
1007};
1008
1009/**
1010 * @param dev DDF function associated with NIC
1011 * @return The associated NIC structure
1012 */
1013nic_t *nic_get_from_ddf_fun(ddf_fun_t *fun)
1014{
1015 return (nic_t *) fun->driver_data;
1016};
1017
1018/**
1019 * Raises the send_packets and send_bytes in device statistics.
1020 *
1021 * @param nic_data
1022 * @param packets Number of received packets
1023 * @param bytes Number of received bytes
1024 */
1025void nic_report_send_ok(nic_t *nic_data, size_t packets, size_t bytes)
1026{
1027 fibril_rwlock_write_lock(&nic_data->stats_lock);
1028 nic_data->stats.send_packets += packets;
1029 nic_data->stats.send_bytes += bytes;
1030 fibril_rwlock_write_unlock(&nic_data->stats_lock);
1031}
1032
1033/**
1034 * Raises total error counter (send_errors) and the concrete send error counter
1035 * determined by the cause argument.
1036 *
1037 * @param nic_data
1038 * @param cause The concrete error cause.
1039 */
1040void nic_report_send_error(nic_t *nic_data, nic_send_error_cause_t cause,
1041 unsigned count)
1042{
1043 if (count == 0)
1044 return;
1045
1046 fibril_rwlock_write_lock(&nic_data->stats_lock);
1047 nic_data->stats.send_errors += count;
1048 switch (cause) {
1049 case NIC_SEC_BUFFER_FULL:
1050 nic_data->stats.send_dropped += count;
1051 break;
1052 case NIC_SEC_ABORTED:
1053 nic_data->stats.send_aborted_errors += count;
1054 break;
1055 case NIC_SEC_CARRIER_LOST:
1056 nic_data->stats.send_carrier_errors += count;
1057 break;
1058 case NIC_SEC_FIFO_OVERRUN:
1059 nic_data->stats.send_fifo_errors += count;
1060 break;
1061 case NIC_SEC_HEARTBEAT:
1062 nic_data->stats.send_heartbeat_errors += count;
1063 break;
1064 case NIC_SEC_WINDOW_ERROR:
1065 nic_data->stats.send_window_errors += count;
1066 break;
1067 case NIC_SEC_OTHER:
1068 break;
1069 }
1070 fibril_rwlock_write_unlock(&nic_data->stats_lock);
1071}
1072
1073/**
1074 * Raises total error counter (receive_errors) and the concrete receive error
1075 * counter determined by the cause argument.
1076 *
1077 * @param nic_data
1078 * @param cause The concrete error cause
1079 */
1080void nic_report_receive_error(nic_t *nic_data,
1081 nic_receive_error_cause_t cause, unsigned count)
1082{
1083 fibril_rwlock_write_lock(&nic_data->stats_lock);
1084 nic_data->stats.receive_errors += count;
1085 switch (cause) {
1086 case NIC_REC_BUFFER_FULL:
1087 nic_data->stats.receive_dropped += count;
1088 break;
1089 case NIC_REC_LENGTH:
1090 nic_data->stats.receive_length_errors += count;
1091 break;
1092 case NIC_REC_BUFFER_OVERFLOW:
1093 nic_data->stats.receive_dropped += count;
1094 break;
1095 case NIC_REC_CRC:
1096 nic_data->stats.receive_crc_errors += count;
1097 break;
1098 case NIC_REC_FRAME_ALIGNMENT:
1099 nic_data->stats.receive_frame_errors += count;
1100 break;
1101 case NIC_REC_FIFO_OVERRUN:
1102 nic_data->stats.receive_fifo_errors += count;
1103 break;
1104 case NIC_REC_MISSED:
1105 nic_data->stats.receive_missed_errors += count;
1106 break;
1107 case NIC_REC_OTHER:
1108 break;
1109 }
1110 fibril_rwlock_write_unlock(&nic_data->stats_lock);
1111}
1112
1113/**
1114 * Raises the collisions counter in device statistics.
1115 */
1116void nic_report_collisions(nic_t *nic_data, unsigned count)
1117{
1118 fibril_rwlock_write_lock(&nic_data->stats_lock);
1119 nic_data->stats.collisions += count;
1120 fibril_rwlock_write_unlock(&nic_data->stats_lock);
1121}
1122
1123/** Just wrapper for checking nonzero time interval
1124 *
1125 * @oaram t The interval to check
1126 * @returns Zero if the t is nonzero interval
1127 * @returns Nonzero if t is zero interval
1128 */
1129static int timeval_nonpositive(struct timeval t) {
1130 return (t.tv_sec <= 0) && (t.tv_usec <= 0);
1131}
1132
1133/** Main function of software period fibrill
1134 *
1135 * Just calls poll() in the nic->poll_period period
1136 *
1137 * @param data The NIC structure pointer
1138 *
1139 * @return 0, never reached
1140 */
1141static int period_fibril_fun(void *data)
1142{
1143 nic_t *nic = data;
1144 struct sw_poll_info *info = &nic->sw_poll_info;
1145 while (true) {
1146 fibril_rwlock_read_lock(&nic->main_lock);
1147 int run = info->run;
1148 int running = info->running;
1149 struct timeval remaining = nic->poll_period;
1150 fibril_rwlock_read_unlock(&nic->main_lock);
1151
1152 if (!running) {
1153 remaining.tv_sec = 5;
1154 remaining.tv_usec = 0;
1155 }
1156
1157 /* Wait the period (keep attention to overflows) */
1158 while (!timeval_nonpositive(remaining)) {
1159 suseconds_t wait = 0;
1160 if (remaining.tv_sec > 0) {
1161 time_t wait_sec = remaining.tv_sec;
1162 /* wait maximaly 5 seconds to get reasonable reaction time
1163 * when period is reset
1164 */
1165 if (wait_sec > 5)
1166 wait_sec = 5;
1167
1168 wait = (suseconds_t) wait_sec * 1000000;
1169
1170 remaining.tv_sec -= wait_sec;
1171 } else {
1172 wait = remaining.tv_usec;
1173
1174 if (wait > 5 * 1000000) {
1175 wait = 5 * 1000000;
1176 }
1177
1178 remaining.tv_usec -= wait;
1179 }
1180 async_usleep(wait);
1181
1182 /* Check if the period was not reset */
1183 if (info->run != run)
1184 break;
1185 }
1186
1187 /* Provide polling if the period finished */
1188 fibril_rwlock_read_lock(&nic->main_lock);
1189 if (info->running && info->run == run) {
1190 nic->on_poll_request(nic);
1191 }
1192 fibril_rwlock_read_unlock(&nic->main_lock);
1193 }
1194 return 0;
1195}
1196
1197/** Starts software periodic polling
1198 *
1199 * Reset to new period if the original period was running
1200 *
1201 * @param nic_data Nic data structure
1202 */
1203void nic_sw_period_start(nic_t *nic_data)
1204{
1205 /* Create the fibril if it is not crated */
1206 if (nic_data->sw_poll_info.fibril == 0) {
1207 nic_data->sw_poll_info.fibril = fibril_create(period_fibril_fun,
1208 nic_data);
1209 nic_data->sw_poll_info.running = 0;
1210 nic_data->sw_poll_info.run = 0;
1211
1212 /* Start fibril */
1213 fibril_add_ready(nic_data->sw_poll_info.fibril);
1214 }
1215
1216 /* Inform fibril about running with new period */
1217 nic_data->sw_poll_info.run = (nic_data->sw_poll_info.run + 1) % 100;
1218 nic_data->sw_poll_info.running = 1;
1219}
1220
1221/** Stops software periodic polling
1222 *
1223 * @param nic_data Nic data structure
1224 */
1225void nic_sw_period_stop(nic_t *nic_data)
1226{
1227 nic_data->sw_poll_info.running = 0;
1228}
1229
1230/** @}
1231 */
Note: See TracBrowser for help on using the repository browser.