00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 FILE_LICENCE ( GPL2_OR_LATER );
00020
00021 #include <string.h>
00022 #include <pxe.h>
00023 #include <realmode.h>
00024 #include <pic8259.h>
00025 #include <biosint.h>
00026 #include <pnpbios.h>
00027 #include <basemem_packet.h>
00028 #include <gpxe/io.h>
00029 #include <gpxe/iobuf.h>
00030 #include <gpxe/netdevice.h>
00031 #include <gpxe/if_ether.h>
00032 #include <gpxe/ethernet.h>
00033 #include <undi.h>
00034 #include <undinet.h>
00035 #include <pxeparent.h>
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045 struct undi_nic {
00046
00047 unsigned int irq;
00048
00049 int isr_processing;
00050
00051 int hacks;
00052 };
00053
00054
00055
00056
00057
00058
00059
00060 #define UNDI_HACK_EB54 0x0001
00061
00062
00063
00064 static void undinet_close ( struct net_device *netdev );
00065
00066
00067 static SEGOFF16_t undinet_entry;
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081 extern void undiisr ( void );
00082
00083
00084 uint8_t __data16 ( undiisr_irq );
00085 #define undiisr_irq __use_data16 ( undiisr_irq )
00086
00087
00088 struct segoff __data16 ( undiisr_next_handler );
00089 #define undiisr_next_handler __use_data16 ( undiisr_next_handler )
00090
00091
00092 volatile uint8_t __data16 ( undiisr_trigger_count ) = 0;
00093 #define undiisr_trigger_count __use_data16 ( undiisr_trigger_count )
00094
00095
00096 static unsigned int last_trigger_count = 0;
00097
00098
00099
00100
00101
00102
00103 static void undinet_hook_isr ( unsigned int irq ) {
00104
00105 assert ( irq <= IRQ_MAX );
00106 assert ( undiisr_irq == 0 );
00107
00108 undiisr_irq = irq;
00109 hook_bios_interrupt ( IRQ_INT ( irq ),
00110 ( ( unsigned int ) undiisr ),
00111 &undiisr_next_handler );
00112 }
00113
00114
00115
00116
00117
00118
00119 static void undinet_unhook_isr ( unsigned int irq ) {
00120
00121 assert ( irq <= IRQ_MAX );
00122
00123 unhook_bios_interrupt ( IRQ_INT ( irq ),
00124 ( ( unsigned int ) undiisr ),
00125 &undiisr_next_handler );
00126 undiisr_irq = 0;
00127 }
00128
00129
00130
00131
00132
00133
00134 static int undinet_isr_triggered ( void ) {
00135 unsigned int this_trigger_count;
00136
00137
00138 this_trigger_count = undiisr_trigger_count;
00139
00140 if ( this_trigger_count == last_trigger_count ) {
00141
00142 return 0;
00143 } else {
00144
00145 last_trigger_count = this_trigger_count;
00146 return 1;
00147 }
00148 }
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158 static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd );
00159 #define undinet_tbd __use_data16 ( undinet_tbd )
00160
00161
00162
00163
00164
00165
00166
00167
00168 static int undinet_transmit ( struct net_device *netdev,
00169 struct io_buffer *iobuf ) {
00170 struct s_PXENV_UNDI_TRANSMIT undi_transmit;
00171 size_t len = iob_len ( iobuf );
00172 int rc;
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185 if ( len > sizeof ( basemem_packet ) )
00186 len = sizeof ( basemem_packet );
00187 memcpy ( &basemem_packet, iobuf->data, len );
00188
00189
00190 memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
00191 undi_transmit.DestAddr.segment = rm_ds;
00192 undi_transmit.DestAddr.offset = __from_data16 ( &undinet_tbd );
00193 undi_transmit.TBD.segment = rm_ds;
00194 undi_transmit.TBD.offset = __from_data16 ( &undinet_tbd );
00195
00196
00197 undinet_tbd.ImmedLength = len;
00198 undinet_tbd.Xmit.segment = rm_ds;
00199 undinet_tbd.Xmit.offset = __from_data16 ( basemem_packet );
00200
00201
00202 if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_TRANSMIT,
00203 &undi_transmit,
00204 sizeof ( undi_transmit ) ) ) != 0 )
00205 goto done;
00206
00207
00208 netdev_tx_complete ( netdev, iobuf );
00209
00210 done:
00211 return rc;
00212 }
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244 static void undinet_poll ( struct net_device *netdev ) {
00245 struct undi_nic *undinic = netdev->priv;
00246 struct s_PXENV_UNDI_ISR undi_isr;
00247 struct io_buffer *iobuf = NULL;
00248 size_t len;
00249 size_t frag_len;
00250 size_t max_frag_len;
00251 int rc;
00252
00253 if ( ! undinic->isr_processing ) {
00254
00255 if ( ! undinet_isr_triggered() ) {
00256
00257 __asm__ __volatile__ ( REAL_CODE ( "sti\n\t"
00258 "nop\n\t"
00259 "nop\n\t"
00260 "cli\n\t" ) : : );
00261 return;
00262 }
00263
00264
00265 undinic->isr_processing = 1;
00266 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
00267 } else {
00268
00269 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
00270 }
00271
00272
00273 while ( 1 ) {
00274 if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR,
00275 &undi_isr,
00276 sizeof ( undi_isr ) ) ) != 0 )
00277 break;
00278 switch ( undi_isr.FuncFlag ) {
00279 case PXENV_UNDI_ISR_OUT_TRANSMIT:
00280
00281 break;
00282 case PXENV_UNDI_ISR_OUT_RECEIVE:
00283
00284 len = undi_isr.FrameLength;
00285 frag_len = undi_isr.BufferLength;
00286 if ( ( len == 0 ) || ( len < frag_len ) ) {
00287
00288 DBGC ( undinic, "UNDINIC %p reported insane "
00289 "fragment (%zd of %zd bytes)\n",
00290 undinic, frag_len, len );
00291 netdev_rx_err ( netdev, NULL, -EINVAL );
00292 break;
00293 }
00294 if ( ! iobuf )
00295 iobuf = alloc_iob ( len );
00296 if ( ! iobuf ) {
00297 DBGC ( undinic, "UNDINIC %p could not "
00298 "allocate %zd bytes for RX buffer\n",
00299 undinic, len );
00300
00301 netdev_rx_err ( netdev, NULL, -ENOMEM );
00302 goto done;
00303 }
00304 max_frag_len = iob_tailroom ( iobuf );
00305 if ( frag_len > max_frag_len ) {
00306 DBGC ( undinic, "UNDINIC %p fragment too big "
00307 "(%zd+%zd does not fit into %zd)\n",
00308 undinic, iob_len ( iobuf ), frag_len,
00309 ( iob_len ( iobuf ) + max_frag_len ) );
00310 frag_len = max_frag_len;
00311 }
00312 copy_from_real ( iob_put ( iobuf, frag_len ),
00313 undi_isr.Frame.segment,
00314 undi_isr.Frame.offset, frag_len );
00315 if ( iob_len ( iobuf ) == len ) {
00316
00317 netdev_rx ( netdev, iob_disown ( iobuf ) );
00318
00319
00320
00321 if ( undinic->hacks & UNDI_HACK_EB54 )
00322 --last_trigger_count;
00323 }
00324 break;
00325 case PXENV_UNDI_ISR_OUT_DONE:
00326
00327 undinic->isr_processing = 0;
00328 goto done;
00329 default:
00330
00331 DBGC ( undinic, "UNDINIC %p ISR returned invalid "
00332 "FuncFlag %04x\n", undinic, undi_isr.FuncFlag );
00333 undinic->isr_processing = 0;
00334 goto done;
00335 }
00336 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
00337 }
00338
00339 done:
00340 if ( iobuf ) {
00341 DBGC ( undinic, "UNDINIC %p returned incomplete packet "
00342 "(%zd of %zd)\n", undinic, iob_len ( iobuf ),
00343 ( iob_len ( iobuf ) + iob_tailroom ( iobuf ) ) );
00344 netdev_rx_err ( netdev, iobuf, -EINVAL );
00345 }
00346 }
00347
00348
00349
00350
00351
00352
00353
00354 static int undinet_open ( struct net_device *netdev ) {
00355 struct undi_nic *undinic = netdev->priv;
00356 struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address;
00357 struct s_PXENV_UNDI_OPEN undi_open;
00358 int rc;
00359
00360
00361 undinet_hook_isr ( undinic->irq );
00362 enable_irq ( undinic->irq );
00363 send_eoi ( undinic->irq );
00364
00365
00366
00367
00368
00369
00370 memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
00371 sizeof ( undi_set_address.StationAddress ) );
00372 pxeparent_call ( undinet_entry, PXENV_UNDI_SET_STATION_ADDRESS,
00373 &undi_set_address, sizeof ( undi_set_address ) );
00374
00375
00376
00377
00378
00379
00380 memset ( &undi_open, 0, sizeof ( undi_open ) );
00381 undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS );
00382 if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_OPEN,
00383 &undi_open, sizeof ( undi_open ) ) ) != 0 )
00384 goto err;
00385
00386 DBGC ( undinic, "UNDINIC %p opened\n", undinic );
00387 return 0;
00388
00389 err:
00390 undinet_close ( netdev );
00391 return rc;
00392 }
00393
00394
00395
00396
00397
00398
00399 static void undinet_close ( struct net_device *netdev ) {
00400 struct undi_nic *undinic = netdev->priv;
00401 struct s_PXENV_UNDI_ISR undi_isr;
00402 struct s_PXENV_UNDI_CLOSE undi_close;
00403 int rc;
00404
00405
00406 while ( undinic->isr_processing ) {
00407 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
00408 if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR,
00409 &undi_isr,
00410 sizeof ( undi_isr ) ) ) != 0 )
00411 break;
00412 switch ( undi_isr.FuncFlag ) {
00413 case PXENV_UNDI_ISR_OUT_TRANSMIT:
00414 case PXENV_UNDI_ISR_OUT_RECEIVE:
00415
00416 break;
00417 default:
00418
00419 undinic->isr_processing = 0;
00420 break;
00421 }
00422 }
00423
00424
00425 pxeparent_call ( undinet_entry, PXENV_UNDI_CLOSE,
00426 &undi_close, sizeof ( undi_close ) );
00427
00428
00429 disable_irq ( undinic->irq );
00430 undinet_unhook_isr ( undinic->irq );
00431
00432 DBGC ( undinic, "UNDINIC %p closed\n", undinic );
00433 }
00434
00435
00436
00437
00438
00439
00440
00441 static void undinet_irq ( struct net_device *netdev, int enable ) {
00442 struct undi_nic *undinic = netdev->priv;
00443
00444
00445 DBGC ( undinic, "UNDINIC %p cannot %s interrupts\n",
00446 undinic, ( enable ? "enable" : "disable" ) );
00447 }
00448
00449
00450 static struct net_device_operations undinet_operations = {
00451 .open = undinet_open,
00452 .close = undinet_close,
00453 .transmit = undinet_transmit,
00454 .poll = undinet_poll,
00455 .irq = undinet_irq,
00456 };
00457
00458
00459
00460
00461
00462
00463
00464 int undinet_probe ( struct undi_device *undi ) {
00465 struct net_device *netdev;
00466 struct undi_nic *undinic;
00467 struct s_PXENV_START_UNDI start_undi;
00468 struct s_PXENV_UNDI_STARTUP undi_startup;
00469 struct s_PXENV_UNDI_INITIALIZE undi_initialize;
00470 struct s_PXENV_UNDI_GET_INFORMATION undi_info;
00471 struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface;
00472 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
00473 struct s_PXENV_UNDI_CLEANUP undi_cleanup;
00474 struct s_PXENV_STOP_UNDI stop_undi;
00475 int rc;
00476
00477
00478 netdev = alloc_etherdev ( sizeof ( *undinic ) );
00479 if ( ! netdev )
00480 return -ENOMEM;
00481 netdev_init ( netdev, &undinet_operations );
00482 undinic = netdev->priv;
00483 undi_set_drvdata ( undi, netdev );
00484 netdev->dev = &undi->dev;
00485 memset ( undinic, 0, sizeof ( *undinic ) );
00486 undinet_entry = undi->entry;
00487 DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi );
00488
00489
00490 if ( ! ( undi->flags & UNDI_FL_STARTED ) ) {
00491 memset ( &start_undi, 0, sizeof ( start_undi ) );
00492 start_undi.AX = undi->pci_busdevfn;
00493 start_undi.BX = undi->isapnp_csn;
00494 start_undi.DX = undi->isapnp_read_port;
00495 start_undi.ES = BIOS_SEG;
00496 start_undi.DI = find_pnp_bios();
00497 if ( ( rc = pxeparent_call ( undinet_entry, PXENV_START_UNDI,
00498 &start_undi,
00499 sizeof ( start_undi ) ) ) != 0 )
00500 goto err_start_undi;
00501 }
00502 undi->flags |= UNDI_FL_STARTED;
00503
00504
00505 if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) {
00506 memset ( &undi_startup, 0, sizeof ( undi_startup ) );
00507 if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_STARTUP,
00508 &undi_startup,
00509 sizeof ( undi_startup ) ) ) != 0 )
00510 goto err_undi_startup;
00511 memset ( &undi_initialize, 0, sizeof ( undi_initialize ) );
00512 if ( ( rc = pxeparent_call ( undinet_entry,
00513 PXENV_UNDI_INITIALIZE,
00514 &undi_initialize,
00515 sizeof ( undi_initialize ))) != 0 )
00516 goto err_undi_initialize;
00517 }
00518 undi->flags |= UNDI_FL_INITIALIZED;
00519
00520
00521 memset ( &undi_info, 0, sizeof ( undi_info ) );
00522 if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_GET_INFORMATION,
00523 &undi_info, sizeof ( undi_info ) ) ) != 0 )
00524 goto err_undi_get_information;
00525 memcpy ( netdev->hw_addr, undi_info.PermNodeAddress, ETH_ALEN );
00526 undinic->irq = undi_info.IntNumber;
00527 if ( undinic->irq > IRQ_MAX ) {
00528 DBGC ( undinic, "UNDINIC %p invalid IRQ %d\n",
00529 undinic, undinic->irq );
00530 goto err_bad_irq;
00531 }
00532 DBGC ( undinic, "UNDINIC %p is %s on IRQ %d\n",
00533 undinic, eth_ntoa ( netdev->hw_addr ), undinic->irq );
00534
00535
00536 memset ( &undi_iface, 0, sizeof ( undi_iface ) );
00537 if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_GET_IFACE_INFO,
00538 &undi_iface,
00539 sizeof ( undi_iface ) ) ) != 0 )
00540 goto err_undi_get_iface_info;
00541 DBGC ( undinic, "UNDINIC %p has type %s, speed %d, flags %08x\n",
00542 undinic, undi_iface.IfaceType, undi_iface.LinkSpeed,
00543 undi_iface.ServiceFlags );
00544 if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
00545 sizeof ( undi_iface.IfaceType ) ) == 0 ) {
00546 DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
00547 undinic );
00548 undinic->hacks |= UNDI_HACK_EB54;
00549 }
00550
00551
00552 netdev_link_up ( netdev );
00553
00554
00555 if ( ( rc = register_netdev ( netdev ) ) != 0 )
00556 goto err_register;
00557
00558 DBGC ( undinic, "UNDINIC %p added\n", undinic );
00559 return 0;
00560
00561 err_register:
00562 err_undi_get_iface_info:
00563 err_bad_irq:
00564 err_undi_get_information:
00565 err_undi_initialize:
00566
00567 memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
00568 pxeparent_call ( undinet_entry, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
00569 sizeof ( undi_shutdown ) );
00570 memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
00571 pxeparent_call ( undinet_entry, PXENV_UNDI_CLEANUP, &undi_cleanup,
00572 sizeof ( undi_cleanup ) );
00573 undi->flags &= ~UNDI_FL_INITIALIZED;
00574 err_undi_startup:
00575
00576 memset ( &stop_undi, 0, sizeof ( stop_undi ) );
00577 pxeparent_call ( undinet_entry, PXENV_STOP_UNDI, &stop_undi,
00578 sizeof ( stop_undi ) );
00579 undi->flags &= ~UNDI_FL_STARTED;
00580 err_start_undi:
00581 netdev_nullify ( netdev );
00582 netdev_put ( netdev );
00583 undi_set_drvdata ( undi, NULL );
00584 return rc;
00585 }
00586
00587
00588
00589
00590
00591
00592 void undinet_remove ( struct undi_device *undi ) {
00593 struct net_device *netdev = undi_get_drvdata ( undi );
00594 struct undi_nic *undinic = netdev->priv;
00595 struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
00596 struct s_PXENV_UNDI_CLEANUP undi_cleanup;
00597 struct s_PXENV_STOP_UNDI stop_undi;
00598
00599
00600 unregister_netdev ( netdev );
00601
00602
00603
00604
00605 if ( ! ( undi->flags & UNDI_FL_KEEP_ALL ) ) {
00606
00607
00608 memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
00609 pxeparent_call ( undinet_entry, PXENV_UNDI_SHUTDOWN,
00610 &undi_shutdown, sizeof ( undi_shutdown ) );
00611 memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
00612 pxeparent_call ( undinet_entry, PXENV_UNDI_CLEANUP,
00613 &undi_cleanup, sizeof ( undi_cleanup ) );
00614 undi->flags &= ~UNDI_FL_INITIALIZED;
00615
00616
00617 memset ( &stop_undi, 0, sizeof ( stop_undi ) );
00618 pxeparent_call ( undinet_entry, PXENV_STOP_UNDI, &stop_undi,
00619 sizeof ( stop_undi ) );
00620 undi->flags &= ~UNDI_FL_STARTED;
00621 }
00622
00623
00624 memset ( &undinet_entry, 0, sizeof ( undinet_entry ) );
00625
00626
00627 netdev_nullify ( netdev );
00628 netdev_put ( netdev );
00629
00630 DBGC ( undinic, "UNDINIC %p removed\n", undinic );
00631 }