source: mainline/uspace/drv/nic/ne2k/ne2k.c@ 3a4c6d9

Last change on this file since 3a4c6d9 was 3a4c6d9, checked in by Jiri Svoboda <jiri@…>, 2 months ago

Packet capture (thx Nataliia Korop)

  • Property mode set to 100644
File size: 12.1 KB
Line 
1/*
2 * Copyright (c) 2025 Jiri Svoboda
3 * Copyright (c) 2011 Martin Decky
4 * Copyright (c) 2011 Radim Vansa
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * @addtogroup drv_ne2k
33 * @{
34 */
35/**
36 * @file
37 * @brief Bridge between NICF, DDF and business logic for the NIC
38 */
39
40#include <stdio.h>
41#include <errno.h>
42#include <device/hw_res.h>
43#include <stdlib.h>
44#include <str_error.h>
45#include <async.h>
46#include <ddf/log.h>
47#include "dp8390.h"
48
49#define NAME "ne2k"
50
51/** Return the ISR from the interrupt call.
52 *
53 * @param[in] call The interrupt call.
54 *
55 */
56#define IRQ_GET_ISR(call) ((int) ipc_get_arg2(&call))
57
58/** Return the TSR from the interrupt call.
59 *
60 * @param[in] call The interrupt call.
61 *
62 */
63#define IRQ_GET_TSR(call) ((int) ipc_get_arg3(&call))
64
65#define DRIVER_DATA(dev) ((nic_t *) ddf_dev_data_get(dev))
66#define NE2K(device) ((ne2k_t *) nic_get_specific(DRIVER_DATA(device)))
67
68static irq_pio_range_t ne2k_ranges_prototype[] = {
69 {
70 .base = 0,
71 .size = NE2K_IO_SIZE,
72 }
73};
74
75/** NE2000 kernel interrupt command sequence.
76 *
77 */
78static irq_cmd_t ne2k_cmds_prototype[] = {
79 {
80 /* Read Interrupt Status Register */
81 .cmd = CMD_PIO_READ_8,
82 .addr = NULL,
83 .dstarg = 2
84 },
85 {
86 /* Mask supported interrupt causes */
87 .cmd = CMD_AND,
88 .value = (ISR_PRX | ISR_PTX | ISR_RXE | ISR_TXE | ISR_OVW |
89 ISR_CNT | ISR_RDC),
90 .srcarg = 2,
91 .dstarg = 3,
92 },
93 {
94 /* Predicate for accepting the interrupt */
95 .cmd = CMD_PREDICATE,
96 .value = 4,
97 .srcarg = 3
98 },
99 {
100 /*
101 * Mask future interrupts via
102 * Interrupt Mask Register
103 */
104 .cmd = CMD_PIO_WRITE_8,
105 .addr = NULL,
106 .value = 0
107 },
108 {
109 /* Acknowledge the current interrupt */
110 .cmd = CMD_PIO_WRITE_A_8,
111 .addr = NULL,
112 .srcarg = 3
113 },
114 {
115 /* Read Transmit Status Register */
116 .cmd = CMD_PIO_READ_8,
117 .addr = NULL,
118 .dstarg = 3
119 },
120 {
121 .cmd = CMD_ACCEPT
122 }
123};
124
125static void ne2k_interrupt_handler(ipc_call_t *, void *);
126
127static errno_t ne2k_register_interrupt(nic_t *nic_data,
128 cap_irq_handle_t *handle)
129{
130 ne2k_t *ne2k = (ne2k_t *) nic_get_specific(nic_data);
131
132 if (ne2k->code.cmdcount == 0) {
133 irq_pio_range_t *ne2k_ranges;
134 irq_cmd_t *ne2k_cmds;
135
136 ne2k_ranges = malloc(sizeof(ne2k_ranges_prototype));
137 if (!ne2k_ranges)
138 return ENOMEM;
139 memcpy(ne2k_ranges, ne2k_ranges_prototype,
140 sizeof(ne2k_ranges_prototype));
141 ne2k_ranges[0].base = (uintptr_t) ne2k->base_port;
142
143 ne2k_cmds = malloc(sizeof(ne2k_cmds_prototype));
144 if (!ne2k_cmds) {
145 free(ne2k_ranges);
146 return ENOMEM;
147 }
148 memcpy(ne2k_cmds, ne2k_cmds_prototype,
149 sizeof(ne2k_cmds_prototype));
150 ne2k_cmds[0].addr = ne2k->base_port + DP_ISR;
151 ne2k_cmds[3].addr = ne2k->base_port + DP_IMR;
152 ne2k_cmds[4].addr = ne2k_cmds[0].addr;
153 ne2k_cmds[5].addr = ne2k->base_port + DP_TSR;
154
155 ne2k->code.rangecount = sizeof(ne2k_ranges_prototype) /
156 sizeof(irq_pio_range_t);
157 ne2k->code.ranges = ne2k_ranges;
158
159 ne2k->code.cmdcount = sizeof(ne2k_cmds_prototype) /
160 sizeof(irq_cmd_t);
161 ne2k->code.cmds = ne2k_cmds;
162 }
163
164 return register_interrupt_handler(nic_get_ddf_dev(nic_data),
165 ne2k->irq, ne2k_interrupt_handler, (void *)nic_data, &ne2k->code,
166 handle);
167}
168
169static ddf_dev_ops_t ne2k_dev_ops;
170
171static void ne2k_dev_cleanup(ddf_dev_t *dev)
172{
173 if (ddf_dev_data_get(dev) != NULL) {
174 ne2k_t *ne2k = NE2K(dev);
175 if (ne2k) {
176 free(ne2k->code.ranges);
177 free(ne2k->code.cmds);
178 }
179 nic_unbind_and_destroy(dev);
180 }
181}
182
183static errno_t ne2k_dev_init(nic_t *nic_data)
184{
185 /* Get HW resources */
186 hw_res_list_parsed_t hw_res_parsed;
187 hw_res_list_parsed_init(&hw_res_parsed);
188
189 errno_t rc = nic_get_resources(nic_data, &hw_res_parsed);
190
191 if (rc != EOK)
192 goto failed;
193
194 if (hw_res_parsed.irqs.count == 0) {
195 rc = EINVAL;
196 goto failed;
197 }
198
199 if (hw_res_parsed.io_ranges.count == 0) {
200 rc = EINVAL;
201 goto failed;
202 }
203
204 if (hw_res_parsed.io_ranges.ranges[0].size < NE2K_IO_SIZE) {
205 rc = EINVAL;
206 goto failed;
207 }
208
209 ne2k_t *ne2k = (ne2k_t *) nic_get_specific(nic_data);
210 ne2k->irq = hw_res_parsed.irqs.irqs[0];
211
212 addr_range_t regs = hw_res_parsed.io_ranges.ranges[0];
213 ne2k->base_port = RNGABSPTR(regs);
214
215 hw_res_list_parsed_clean(&hw_res_parsed);
216
217 /* Enable programmed I/O */
218 if (pio_enable_range(&regs, &ne2k->port) != EOK)
219 return EADDRNOTAVAIL;
220
221 ne2k->data_port = ne2k->port + NE2K_DATA;
222 ne2k->receive_configuration = RCR_AB | RCR_AM;
223 ne2k->probed = false;
224 ne2k->up = false;
225
226 /* Find out whether the device is present. */
227 if (ne2k_probe(ne2k) != EOK)
228 return ENOENT;
229
230 ne2k->probed = true;
231
232 if (ne2k_register_interrupt(nic_data, NULL) != EOK)
233 return EINVAL;
234
235 return EOK;
236
237failed:
238 hw_res_list_parsed_clean(&hw_res_parsed);
239 return rc;
240}
241
242/** NE2K interrupt handler
243 *
244 * @param call IRQ event notification
245 * @param arg Argument (nic_t *)
246 */
247void ne2k_interrupt_handler(ipc_call_t *call, void *arg)
248{
249 nic_t *nic_data = (nic_t *)arg;
250 ne2k_interrupt(nic_data, IRQ_GET_ISR(*call), IRQ_GET_TSR(*call));
251}
252
253static errno_t ne2k_on_activating(nic_t *nic_data)
254{
255 ne2k_t *ne2k = (ne2k_t *) nic_get_specific(nic_data);
256
257 if (!ne2k->up) {
258 errno_t rc = ne2k_up(ne2k);
259 if (rc != EOK)
260 return rc;
261
262 rc = hw_res_enable_interrupt(ne2k->parent_sess, ne2k->irq);
263 if (rc != EOK) {
264 ne2k_down(ne2k);
265 return rc;
266 }
267 }
268 return EOK;
269}
270
271static errno_t ne2k_on_stopping(nic_t *nic_data)
272{
273 ne2k_t *ne2k = (ne2k_t *) nic_get_specific(nic_data);
274
275 (void) hw_res_disable_interrupt(ne2k->parent_sess, ne2k->irq);
276 ne2k->receive_configuration = RCR_AB | RCR_AM;
277 ne2k_down(ne2k);
278 return EOK;
279}
280
281static errno_t ne2k_set_address(ddf_fun_t *fun, const nic_address_t *address)
282{
283 nic_t *nic_data = DRIVER_DATA(ddf_fun_get_dev(fun));
284 errno_t rc = nic_report_address(nic_data, address);
285 if (rc != EOK) {
286 return EINVAL;
287 }
288 /*
289 * Note: some frame with previous physical address may slip to NIL here
290 * (for a moment the filtering is not exact), but ethernet should be OK with
291 * that. Some frames may also be lost, but this is not a problem.
292 */
293 ne2k_set_physical_address((ne2k_t *) nic_get_specific(nic_data), address);
294 return EOK;
295}
296
297static errno_t ne2k_get_device_info(ddf_fun_t *fun, nic_device_info_t *info)
298{
299 nic_t *nic_data = nic_get_from_ddf_fun(fun);
300 if (!nic_data)
301 return ENOENT;
302
303 str_cpy(info->vendor_name, sizeof(info->vendor_name), "Novell");
304 str_cpy(info->model_name, sizeof(info->model_name), "NE2000");
305
306 return EOK;
307}
308
309static errno_t ne2k_get_cable_state(ddf_fun_t *fun, nic_cable_state_t *state)
310{
311 *state = NIC_CS_PLUGGED;
312 return EOK;
313}
314
315static errno_t ne2k_get_operation_mode(ddf_fun_t *fun, int *speed,
316 nic_channel_mode_t *duplex, nic_role_t *role)
317{
318 *speed = 10;
319 *duplex = NIC_CM_HALF_DUPLEX; // XXX
320 *role = NIC_ROLE_UNKNOWN;
321 return EOK;
322}
323
324static errno_t ne2k_on_unicast_mode_change(nic_t *nic_data,
325 nic_unicast_mode_t new_mode,
326 const nic_address_t *address_list, size_t address_count)
327{
328 ne2k_t *ne2k = (ne2k_t *) nic_get_specific(nic_data);
329 switch (new_mode) {
330 case NIC_UNICAST_BLOCKED:
331 ne2k_set_promisc_phys(ne2k, false);
332 nic_report_hw_filtering(nic_data, 0, -1, -1);
333 return EOK;
334 case NIC_UNICAST_DEFAULT:
335 ne2k_set_promisc_phys(ne2k, false);
336 nic_report_hw_filtering(nic_data, 1, -1, -1);
337 return EOK;
338 case NIC_UNICAST_LIST:
339 ne2k_set_promisc_phys(ne2k, true);
340 nic_report_hw_filtering(nic_data, 0, -1, -1);
341 return EOK;
342 case NIC_UNICAST_PROMISC:
343 ne2k_set_promisc_phys(ne2k, true);
344 nic_report_hw_filtering(nic_data, 1, -1, -1);
345 return EOK;
346 default:
347 return ENOTSUP;
348 }
349}
350
351static errno_t ne2k_on_multicast_mode_change(nic_t *nic_data,
352 nic_multicast_mode_t new_mode,
353 const nic_address_t *address_list, size_t address_count)
354{
355 ne2k_t *ne2k = (ne2k_t *) nic_get_specific(nic_data);
356 switch (new_mode) {
357 case NIC_MULTICAST_BLOCKED:
358 ne2k_set_accept_mcast(ne2k, false);
359 nic_report_hw_filtering(nic_data, -1, 1, -1);
360 return EOK;
361 case NIC_MULTICAST_LIST:
362 ne2k_set_accept_mcast(ne2k, true);
363 ne2k_set_mcast_hash(ne2k,
364 nic_mcast_hash(address_list, address_count));
365 nic_report_hw_filtering(nic_data, -1, 0, -1);
366 return EOK;
367 case NIC_MULTICAST_PROMISC:
368 ne2k_set_accept_mcast(ne2k, true);
369 ne2k_set_mcast_hash(ne2k, 0xFFFFFFFFFFFFFFFFllu);
370 nic_report_hw_filtering(nic_data, -1, 1, -1);
371 return EOK;
372 default:
373 return ENOTSUP;
374 }
375}
376
377static errno_t ne2k_on_broadcast_mode_change(nic_t *nic_data,
378 nic_broadcast_mode_t new_mode)
379{
380 ne2k_t *ne2k = (ne2k_t *) nic_get_specific(nic_data);
381 switch (new_mode) {
382 case NIC_BROADCAST_BLOCKED:
383 ne2k_set_accept_bcast(ne2k, false);
384 return EOK;
385 case NIC_BROADCAST_ACCEPTED:
386 ne2k_set_accept_bcast(ne2k, true);
387 return EOK;
388 default:
389 return ENOTSUP;
390 }
391}
392
393static errno_t ne2k_dev_add(ddf_dev_t *dev)
394{
395 ddf_fun_t *fun;
396
397 /* Allocate driver data for the device. */
398 nic_t *nic_data = nic_create_and_bind(dev);
399 if (nic_data == NULL)
400 return ENOMEM;
401
402 nic_set_send_frame_handler(nic_data, ne2k_send);
403 nic_set_state_change_handlers(nic_data,
404 ne2k_on_activating, NULL, ne2k_on_stopping);
405 nic_set_filtering_change_handlers(nic_data,
406 ne2k_on_unicast_mode_change, ne2k_on_multicast_mode_change,
407 ne2k_on_broadcast_mode_change, NULL, NULL);
408
409 ne2k_t *ne2k = malloc(sizeof(ne2k_t));
410 if (NULL != ne2k) {
411 memset(ne2k, 0, sizeof(ne2k_t));
412 nic_set_specific(nic_data, ne2k);
413 } else {
414 nic_unbind_and_destroy(dev);
415 return ENOMEM;
416 }
417
418 ne2k->dev = dev;
419 ne2k->parent_sess = ddf_dev_parent_sess_get(dev);
420 if (ne2k->parent_sess == NULL) {
421 ne2k_dev_cleanup(dev);
422 return ENOMEM;
423 }
424
425 errno_t rc = ne2k_dev_init(nic_data);
426 if (rc != EOK) {
427 ne2k_dev_cleanup(dev);
428 return rc;
429 }
430
431 rc = nic_report_address(nic_data, &ne2k->mac);
432 if (rc != EOK) {
433 ne2k_dev_cleanup(dev);
434 return rc;
435 }
436
437 fun = ddf_fun_create(nic_get_ddf_dev(nic_data), fun_exposed, "port0");
438 if (fun == NULL) {
439 ne2k_dev_cleanup(dev);
440 return ENOMEM;
441 }
442
443 nic_set_ddf_fun(nic_data, fun);
444 ddf_fun_set_ops(fun, &ne2k_dev_ops);
445
446 rc = ddf_fun_bind(fun);
447 if (rc != EOK) {
448 ddf_fun_destroy(fun);
449 ne2k_dev_cleanup(dev);
450 return rc;
451 }
452
453 rc = nic_fun_add_to_cats(fun);
454 if (rc != EOK) {
455 ddf_msg(LVL_ERROR, "Failed adding function to categories");
456 ddf_fun_unbind(fun);
457 return rc;
458 }
459
460 return EOK;
461}
462
463static errno_t ne2k_dev_quiesce(ddf_dev_t *dev)
464{
465 nic_t *nic;
466 ne2k_t *ne2k;
467
468 nic = nic_get_from_ddf_dev(dev);
469
470 ne2k = (ne2k_t *)nic_get_specific(nic);
471 ne2k_quiesce(ne2k);
472
473 return EOK;
474}
475
476static nic_iface_t ne2k_nic_iface = {
477 .set_address = ne2k_set_address,
478 .get_device_info = ne2k_get_device_info,
479 .get_cable_state = ne2k_get_cable_state,
480 .get_operation_mode = ne2k_get_operation_mode,
481};
482
483static driver_ops_t ne2k_driver_ops = {
484 .dev_add = ne2k_dev_add,
485 .dev_quiesce = ne2k_dev_quiesce
486};
487
488static driver_t ne2k_driver = {
489 .name = NAME,
490 .driver_ops = &ne2k_driver_ops
491};
492
493int main(int argc, char *argv[])
494{
495 printf("%s: HelenOS NE 2000 network adapter driver\n", NAME);
496
497 nic_driver_init(NAME);
498 nic_driver_implement(&ne2k_driver_ops, &ne2k_dev_ops, &ne2k_nic_iface);
499
500 ddf_log_init(NAME);
501 return ddf_driver_main(&ne2k_driver);
502}
503
504/** @}
505 */
Note: See TracBrowser for help on using the repository browser.