1 /** @file 2 * 3 * PXE UNDI API 4 * 5 */ 6 7 /* 8 * Copyright (C) 2004 Michael Brown <mbrown (at) fensystems.co.uk>. 9 * 10 * This program is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU General Public License as 12 * published by the Free Software Foundation; either version 2 of the 13 * License, or any later version. 14 * 15 * This program is distributed in the hope that it will be useful, but 16 * WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 */ 24 25 FILE_LICENCE ( GPL2_OR_LATER ); 26 27 #include <stdint.h> 28 #include <stdio.h> 29 #include <string.h> 30 #include <byteswap.h> 31 #include <basemem_packet.h> 32 #include <gpxe/netdevice.h> 33 #include <gpxe/iobuf.h> 34 #include <gpxe/device.h> 35 #include <gpxe/pci.h> 36 #include <gpxe/if_ether.h> 37 #include <gpxe/ip.h> 38 #include <gpxe/arp.h> 39 #include <gpxe/rarp.h> 40 #include "pxe.h" 41 42 /** 43 * Count of outstanding transmitted packets 44 * 45 * This is incremented each time PXENV_UNDI_TRANSMIT is called, and 46 * decremented each time that PXENV_UNDI_ISR is called with the TX 47 * queue empty, stopping when the count reaches zero. This allows us 48 * to provide a pessimistic approximation of TX completion events to 49 * the PXE NBP simply by monitoring the netdev's TX queue. 50 */ 51 static int undi_tx_count = 0; 52 53 struct net_device *pxe_netdev = NULL; 54 55 /** 56 * Set network device as current PXE network device 57 * 58 * @v netdev Network device, or NULL 59 */ 60 void pxe_set_netdev ( struct net_device *netdev ) { 61 if ( pxe_netdev ) 62 netdev_put ( pxe_netdev ); 63 pxe_netdev = NULL; 64 if ( netdev ) 65 pxe_netdev = netdev_get ( netdev ); 66 } 67 68 /** 69 * Open PXE network device 70 * 71 * @ret rc Return status code 72 */ 73 static int pxe_netdev_open ( void ) { 74 int rc; 75 76 if ( ( rc = netdev_open ( pxe_netdev ) ) != 0 ) 77 return rc; 78 79 netdev_irq ( pxe_netdev, 1 ); 80 return 0; 81 } 82 83 /** 84 * Close PXE network device 85 * 86 */ 87 static void pxe_netdev_close ( void ) { 88 netdev_irq ( pxe_netdev, 0 ); 89 netdev_close ( pxe_netdev ); 90 undi_tx_count = 0; 91 } 92 93 /** 94 * Dump multicast address list 95 * 96 * @v mcast PXE multicast address list 97 */ 98 static void pxe_dump_mcast_list ( struct s_PXENV_UNDI_MCAST_ADDRESS *mcast ) { 99 struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol; 100 unsigned int i; 101 102 for ( i = 0 ; i < mcast->MCastAddrCount ; i++ ) { 103 DBG ( " %s", ll_protocol->ntoa ( mcast->McastAddr[i] ) ); 104 } 105 } 106 107 /* PXENV_UNDI_STARTUP 108 * 109 * Status: working 110 */ 111 PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) { 112 DBG ( "PXENV_UNDI_STARTUP\n" ); 113 114 undi_startup->Status = PXENV_STATUS_SUCCESS; 115 return PXENV_EXIT_SUCCESS; 116 } 117 118 /* PXENV_UNDI_CLEANUP 119 * 120 * Status: working 121 */ 122 PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) { 123 DBG ( "PXENV_UNDI_CLEANUP\n" ); 124 125 pxe_netdev_close(); 126 127 undi_cleanup->Status = PXENV_STATUS_SUCCESS; 128 return PXENV_EXIT_SUCCESS; 129 } 130 131 /* PXENV_UNDI_INITIALIZE 132 * 133 * Status: working 134 */ 135 PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE 136 *undi_initialize ) { 137 DBG ( "PXENV_UNDI_INITIALIZE protocolini %08x\n", 138 undi_initialize->ProtocolIni ); 139 140 undi_initialize->Status = PXENV_STATUS_SUCCESS; 141 return PXENV_EXIT_SUCCESS; 142 } 143 144 /* PXENV_UNDI_RESET_ADAPTER 145 * 146 * Status: working 147 */ 148 PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET 149 *undi_reset_adapter ) { 150 int rc; 151 152 DBG ( "PXENV_UNDI_RESET_ADAPTER" ); 153 pxe_dump_mcast_list ( &undi_reset_adapter->R_Mcast_Buf ); 154 DBG ( "\n" ); 155 156 pxe_netdev_close(); 157 if ( ( rc = pxe_netdev_open() ) != 0 ) { 158 DBG ( "PXENV_UNDI_RESET_ADAPTER could not reopen %s: %s\n", 159 pxe_netdev->name, strerror ( rc ) ); 160 undi_reset_adapter->Status = PXENV_STATUS ( rc ); 161 return PXENV_EXIT_FAILURE; 162 } 163 164 undi_reset_adapter->Status = PXENV_STATUS_SUCCESS; 165 return PXENV_EXIT_SUCCESS; 166 } 167 168 /* PXENV_UNDI_SHUTDOWN 169 * 170 * Status: working 171 */ 172 PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN 173 *undi_shutdown ) { 174 DBG ( "PXENV_UNDI_SHUTDOWN\n" ); 175 176 pxe_netdev_close(); 177 178 undi_shutdown->Status = PXENV_STATUS_SUCCESS; 179 return PXENV_EXIT_SUCCESS; 180 } 181 182 /* PXENV_UNDI_OPEN 183 * 184 * Status: working 185 */ 186 PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) { 187 int rc; 188 189 DBG ( "PXENV_UNDI_OPEN flag %04x filter %04x", 190 undi_open->OpenFlag, undi_open->PktFilter ); 191 pxe_dump_mcast_list ( &undi_open->R_Mcast_Buf ); 192 DBG ( "\n" ); 193 194 if ( ( rc = pxe_netdev_open() ) != 0 ) { 195 DBG ( "PXENV_UNDI_OPEN could not open %s: %s\n", 196 pxe_netdev->name, strerror ( rc ) ); 197 undi_open->Status = PXENV_STATUS ( rc ); 198 return PXENV_EXIT_FAILURE; 199 } 200 201 undi_open->Status = PXENV_STATUS_SUCCESS; 202 return PXENV_EXIT_SUCCESS; 203 } 204 205 /* PXENV_UNDI_CLOSE 206 * 207 * Status: working 208 */ 209 PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) { 210 DBG ( "PXENV_UNDI_CLOSE\n" ); 211 212 pxe_netdev_close(); 213 214 undi_close->Status = PXENV_STATUS_SUCCESS; 215 return PXENV_EXIT_SUCCESS; 216 } 217 218 /* PXENV_UNDI_TRANSMIT 219 * 220 * Status: working 221 */ 222 PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT 223 *undi_transmit ) { 224 struct s_PXENV_UNDI_TBD tbd; 225 struct DataBlk *datablk; 226 struct io_buffer *iobuf; 227 struct net_protocol *net_protocol; 228 struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol; 229 char destaddr[MAX_LL_ADDR_LEN]; 230 const void *ll_dest; 231 size_t ll_hlen = ll_protocol->ll_header_len; 232 size_t len; 233 unsigned int i; 234 int rc; 235 236 DBG2 ( "PXENV_UNDI_TRANSMIT" ); 237 238 /* Forcibly enable interrupts at this point, to work around 239 * callers that never call PXENV_UNDI_OPEN before attempting 240 * to use the UNDI API. 241 */ 242 netdev_irq ( pxe_netdev, 1 ); 243 244 /* Identify network-layer protocol */ 245 switch ( undi_transmit->Protocol ) { 246 case P_IP: net_protocol = &ipv4_protocol; break; 247 case P_ARP: net_protocol = &arp_protocol; break; 248 case P_RARP: net_protocol = &rarp_protocol; break; 249 case P_UNKNOWN: 250 net_protocol = NULL; 251 ll_hlen = 0; 252 break; 253 default: 254 DBG2 ( " %02x invalid protocol\n", undi_transmit->Protocol ); 255 undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER; 256 return PXENV_EXIT_FAILURE; 257 } 258 DBG2 ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) ); 259 260 /* Calculate total packet length */ 261 copy_from_real ( &tbd, undi_transmit->TBD.segment, 262 undi_transmit->TBD.offset, sizeof ( tbd ) ); 263 len = tbd.ImmedLength; 264 DBG2 ( " %04x:%04x+%x", tbd.Xmit.segment, tbd.Xmit.offset, 265 tbd.ImmedLength ); 266 for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) { 267 datablk = &tbd.DataBlock[i]; 268 len += datablk->TDDataLen; 269 DBG2 ( " %04x:%04x+%x", datablk->TDDataPtr.segment, 270 datablk->TDDataPtr.offset, datablk->TDDataLen ); 271 } 272 273 /* Allocate and fill I/O buffer */ 274 iobuf = alloc_iob ( ll_hlen + len ); 275 if ( ! iobuf ) { 276 DBG2 ( " could not allocate iobuf\n" ); 277 undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES; 278 return PXENV_EXIT_FAILURE; 279 } 280 iob_reserve ( iobuf, ll_hlen ); 281 copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment, 282 tbd.Xmit.offset, tbd.ImmedLength ); 283 for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) { 284 datablk = &tbd.DataBlock[i]; 285 copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ), 286 datablk->TDDataPtr.segment, 287 datablk->TDDataPtr.offset, 288 datablk->TDDataLen ); 289 } 290 291 /* Add link-layer header, if required to do so */ 292 if ( net_protocol != NULL ) { 293 294 /* Calculate destination address */ 295 if ( undi_transmit->XmitFlag == XMT_DESTADDR ) { 296 copy_from_real ( destaddr, 297 undi_transmit->DestAddr.segment, 298 undi_transmit->DestAddr.offset, 299 ll_protocol->ll_addr_len ); 300 ll_dest = destaddr; 301 DBG2 ( " DEST %s", ll_protocol->ntoa ( ll_dest ) ); 302 } else { 303 ll_dest = pxe_netdev->ll_broadcast; 304 DBG2 ( " BCAST" ); 305 } 306 307 /* Add link-layer header */ 308 if ( ( rc = ll_protocol->push ( pxe_netdev, iobuf, ll_dest, 309 pxe_netdev->ll_addr, 310 net_protocol->net_proto ))!=0){ 311 DBG2 ( " could not add link-layer header: %s\n", 312 strerror ( rc ) ); 313 free_iob ( iobuf ); 314 undi_transmit->Status = PXENV_STATUS ( rc ); 315 return PXENV_EXIT_FAILURE; 316 } 317 } 318 319 /* Flag transmission as in-progress. Do this before starting 320 * to transmit the packet, because the ISR may trigger before 321 * we return from netdev_tx(). 322 */ 323 undi_tx_count++; 324 325 /* Transmit packet */ 326 DBG2 ( "\n" ); 327 if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) { 328 DBG2 ( "PXENV_UNDI_TRANSMIT could not transmit: %s\n", 329 strerror ( rc ) ); 330 undi_tx_count--; 331 undi_transmit->Status = PXENV_STATUS ( rc ); 332 return PXENV_EXIT_FAILURE; 333 } 334 335 undi_transmit->Status = PXENV_STATUS_SUCCESS; 336 return PXENV_EXIT_SUCCESS; 337 } 338 339 /* PXENV_UNDI_SET_MCAST_ADDRESS 340 * 341 * Status: working (for NICs that support receive-all-multicast) 342 */ 343 PXENV_EXIT_t 344 pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS 345 *undi_set_mcast_address ) { 346 DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" ); 347 pxe_dump_mcast_list ( &undi_set_mcast_address->R_Mcast_Buf ); 348 DBG ( "\n" ); 349 350 undi_set_mcast_address->Status = PXENV_STATUS_SUCCESS; 351 return PXENV_EXIT_SUCCESS; 352 } 353 354 /* PXENV_UNDI_SET_STATION_ADDRESS 355 * 356 * Status: working 357 */ 358 PXENV_EXIT_t 359 pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS 360 *undi_set_station_address ) { 361 struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol; 362 363 DBG ( "PXENV_UNDI_SET_STATION_ADDRESS %s", 364 ll_protocol->ntoa ( undi_set_station_address->StationAddress ) ); 365 366 /* If adapter is open, the change will have no effect; return 367 * an error 368 */ 369 if ( pxe_netdev->state & NETDEV_OPEN ) { 370 DBG ( " failed: netdev is open\n" ); 371 undi_set_station_address->Status = 372 PXENV_STATUS_UNDI_INVALID_STATE; 373 return PXENV_EXIT_FAILURE; 374 } 375 376 /* Update MAC address */ 377 memcpy ( pxe_netdev->ll_addr, 378 &undi_set_station_address->StationAddress, 379 ll_protocol->ll_addr_len ); 380 381 DBG ( "\n" ); 382 undi_set_station_address->Status = PXENV_STATUS_SUCCESS; 383 return PXENV_EXIT_SUCCESS; 384 } 385 386 /* PXENV_UNDI_SET_PACKET_FILTER 387 * 388 * Status: won't implement (would require driver API changes for no 389 * real benefit) 390 */ 391 PXENV_EXIT_t 392 pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER 393 *undi_set_packet_filter ) { 394 395 DBG ( "PXENV_UNDI_SET_PACKET_FILTER %02x\n", 396 undi_set_packet_filter->filter ); 397 398 /* Pretend that we succeeded, otherwise the 3Com DOS UNDI 399 * driver refuses to load. (We ignore the filter value in the 400 * PXENV_UNDI_OPEN call anyway.) 401 */ 402 undi_set_packet_filter->Status = PXENV_STATUS_SUCCESS; 403 404 return PXENV_EXIT_SUCCESS; 405 } 406 407 /* PXENV_UNDI_GET_INFORMATION 408 * 409 * Status: working 410 */ 411 PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION 412 *undi_get_information ) { 413 struct device *dev = pxe_netdev->dev; 414 struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol; 415 size_t ll_addr_len = ll_protocol->ll_addr_len; 416 417 DBG ( "PXENV_UNDI_GET_INFORMATION" ); 418 419 undi_get_information->BaseIo = dev->desc.ioaddr; 420 undi_get_information->IntNumber = dev->desc.irq; 421 /* Cheat: assume all cards can cope with this */ 422 undi_get_information->MaxTranUnit = ETH_MAX_MTU; 423 undi_get_information->HwType = ntohs ( ll_protocol->ll_proto ); 424 undi_get_information->HwAddrLen = ll_addr_len; 425 assert ( ll_addr_len <= 426 sizeof ( undi_get_information->CurrentNodeAddress ) ); 427 memcpy ( &undi_get_information->CurrentNodeAddress, 428 pxe_netdev->ll_addr, 429 sizeof ( undi_get_information->CurrentNodeAddress ) ); 430 ll_protocol->init_addr ( pxe_netdev->hw_addr, 431 &undi_get_information->PermNodeAddress ); 432 undi_get_information->ROMAddress = 0; 433 /* nic.rom_info->rom_segment; */ 434 /* We only provide the ability to receive or transmit a single 435 * packet at a time. This is a bootloader, not an OS. 436 */ 437 undi_get_information->RxBufCt = 1; 438 undi_get_information->TxBufCt = 1; 439 440 DBG ( " io %04x irq %d mtu %d %s %s\n", 441 undi_get_information->BaseIo, undi_get_information->IntNumber, 442 undi_get_information->MaxTranUnit, ll_protocol->name, 443 ll_protocol->ntoa ( &undi_get_information->CurrentNodeAddress )); 444 undi_get_information->Status = PXENV_STATUS_SUCCESS; 445 return PXENV_EXIT_SUCCESS; 446 } 447 448 /* PXENV_UNDI_GET_STATISTICS 449 * 450 * Status: working 451 */ 452 PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS 453 *undi_get_statistics ) { 454 DBG ( "PXENV_UNDI_GET_STATISTICS" ); 455 456 undi_get_statistics->XmtGoodFrames = pxe_netdev->tx_stats.good; 457 undi_get_statistics->RcvGoodFrames = pxe_netdev->rx_stats.good; 458 undi_get_statistics->RcvCRCErrors = pxe_netdev->rx_stats.bad; 459 undi_get_statistics->RcvResourceErrors = pxe_netdev->rx_stats.bad; 460 461 DBG ( " txok %d rxok %d rxcrc %d rxrsrc %d\n", 462 undi_get_statistics->XmtGoodFrames, 463 undi_get_statistics->RcvGoodFrames, 464 undi_get_statistics->RcvCRCErrors, 465 undi_get_statistics->RcvResourceErrors ); 466 undi_get_statistics->Status = PXENV_STATUS_SUCCESS; 467 return PXENV_EXIT_SUCCESS; 468 } 469 470 /* PXENV_UNDI_CLEAR_STATISTICS 471 * 472 * Status: working 473 */ 474 PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS 475 *undi_clear_statistics ) { 476 DBG ( "PXENV_UNDI_CLEAR_STATISTICS\n" ); 477 478 memset ( &pxe_netdev->tx_stats, 0, sizeof ( pxe_netdev->tx_stats ) ); 479 memset ( &pxe_netdev->rx_stats, 0, sizeof ( pxe_netdev->rx_stats ) ); 480 481 undi_clear_statistics->Status = PXENV_STATUS_SUCCESS; 482 return PXENV_EXIT_SUCCESS; 483 } 484 485 /* PXENV_UNDI_INITIATE_DIAGS 486 * 487 * Status: won't implement (would require driver API changes for no 488 * real benefit) 489 */ 490 PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS 491 *undi_initiate_diags ) { 492 DBG ( "PXENV_UNDI_INITIATE_DIAGS failed: unsupported\n" ); 493 494 undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED; 495 return PXENV_EXIT_FAILURE; 496 } 497 498 /* PXENV_UNDI_FORCE_INTERRUPT 499 * 500 * Status: won't implement (would require driver API changes for no 501 * perceptible benefit) 502 */ 503 PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT 504 *undi_force_interrupt ) { 505 DBG ( "PXENV_UNDI_FORCE_INTERRUPT failed: unsupported\n" ); 506 507 undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED; 508 return PXENV_EXIT_FAILURE; 509 } 510 511 /* PXENV_UNDI_GET_MCAST_ADDRESS 512 * 513 * Status: working 514 */ 515 PXENV_EXIT_t 516 pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS 517 *undi_get_mcast_address ) { 518 struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol; 519 struct in_addr ip = { .s_addr = undi_get_mcast_address->InetAddr }; 520 int rc; 521 522 DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS %s", inet_ntoa ( ip ) ); 523 524 if ( ( rc = ll_protocol->mc_hash ( AF_INET, &ip, 525 undi_get_mcast_address->MediaAddr ))!=0){ 526 DBG ( " failed: %s\n", strerror ( rc ) ); 527 undi_get_mcast_address->Status = PXENV_STATUS ( rc ); 528 return PXENV_EXIT_FAILURE; 529 } 530 DBG ( "=>%s\n", 531 ll_protocol->ntoa ( undi_get_mcast_address->MediaAddr ) ); 532 533 undi_get_mcast_address->Status = PXENV_STATUS_SUCCESS; 534 return PXENV_EXIT_SUCCESS; 535 } 536 537 /* PXENV_UNDI_GET_NIC_TYPE 538 * 539 * Status: working 540 */ 541 PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE 542 *undi_get_nic_type ) { 543 struct device *dev = pxe_netdev->dev; 544 545 DBG ( "PXENV_UNDI_GET_NIC_TYPE" ); 546 547 memset ( &undi_get_nic_type->info, 0, 548 sizeof ( undi_get_nic_type->info ) ); 549 550 switch ( dev->desc.bus_type ) { 551 case BUS_TYPE_PCI: { 552 struct pci_nic_info *info = &undi_get_nic_type->info.pci; 553 554 undi_get_nic_type->NicType = PCI_NIC; 555 info->Vendor_ID = dev->desc.vendor; 556 info->Dev_ID = dev->desc.device; 557 info->Base_Class = PCI_BASE_CLASS ( dev->desc.class ); 558 info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class ); 559 info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class ); 560 info->BusDevFunc = dev->desc.location; 561 /* Cheat: remaining fields are probably unnecessary, 562 * and would require adding extra code to pci.c. 563 */ 564 undi_get_nic_type->info.pci.SubVendor_ID = 0xffff; 565 undi_get_nic_type->info.pci.SubDevice_ID = 0xffff; 566 DBG ( " PCI %02x:%02x.%x %04x:%04x (%04x:%04x) %02x%02x%02x " 567 "rev %02x\n", PCI_BUS ( info->BusDevFunc ), 568 PCI_SLOT ( info->BusDevFunc ), 569 PCI_FUNC ( info->BusDevFunc ), info->Vendor_ID, 570 info->Dev_ID, info->SubVendor_ID, info->SubDevice_ID, 571 info->Base_Class, info->Sub_Class, info->Prog_Intf, 572 info->Rev ); 573 break; } 574 case BUS_TYPE_ISAPNP: { 575 struct pnp_nic_info *info = &undi_get_nic_type->info.pnp; 576 577 undi_get_nic_type->NicType = PnP_NIC; 578 info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) | 579 dev->desc.device ); 580 info->CardSelNum = dev->desc.location; 581 /* Cheat: remaining fields are probably unnecessary, 582 * and would require adding extra code to isapnp.c. 583 */ 584 DBG ( " ISAPnP CSN %04x %08x %02x%02x%02x\n", 585 info->CardSelNum, info->EISA_Dev_ID, 586 info->Base_Class, info->Sub_Class, info->Prog_Intf ); 587 break; } 588 default: 589 DBG ( " failed: unknown bus type\n" ); 590 undi_get_nic_type->Status = PXENV_STATUS_FAILURE; 591 return PXENV_EXIT_FAILURE; 592 } 593 594 undi_get_nic_type->Status = PXENV_STATUS_SUCCESS; 595 return PXENV_EXIT_SUCCESS; 596 } 597 598 /* PXENV_UNDI_GET_IFACE_INFO 599 * 600 * Status: working 601 */ 602 PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO 603 *undi_get_iface_info ) { 604 DBG ( "PXENV_UNDI_GET_IFACE_INFO" ); 605 606 /* Just hand back some info, doesn't really matter what it is. 607 * Most PXE stacks seem to take this approach. 608 */ 609 snprintf ( ( char * ) undi_get_iface_info->IfaceType, 610 sizeof ( undi_get_iface_info->IfaceType ), "DIX+802.3" ); 611 undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */ 612 undi_get_iface_info->ServiceFlags = 613 ( SUPPORTED_BROADCAST | SUPPORTED_MULTICAST | 614 SUPPORTED_SET_STATION_ADDRESS | SUPPORTED_RESET | 615 SUPPORTED_OPEN_CLOSE | SUPPORTED_IRQ ); 616 memset ( undi_get_iface_info->Reserved, 0, 617 sizeof(undi_get_iface_info->Reserved) ); 618 619 DBG ( " %s %dbps flags %08x\n", undi_get_iface_info->IfaceType, 620 undi_get_iface_info->LinkSpeed, 621 undi_get_iface_info->ServiceFlags ); 622 undi_get_iface_info->Status = PXENV_STATUS_SUCCESS; 623 return PXENV_EXIT_SUCCESS; 624 } 625 626 /* PXENV_UNDI_GET_STATE 627 * 628 * Status: impossible 629 */ 630 PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE 631 *undi_get_state ) { 632 DBG ( "PXENV_UNDI_GET_STATE failed: unsupported\n" ); 633 634 undi_get_state->Status = PXENV_STATUS_UNSUPPORTED; 635 return PXENV_EXIT_FAILURE; 636 }; 637 638 /* PXENV_UNDI_ISR 639 * 640 * Status: working 641 */ 642 PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) { 643 struct io_buffer *iobuf; 644 size_t len; 645 struct ll_protocol *ll_protocol; 646 const void *ll_dest; 647 const void *ll_source; 648 uint16_t net_proto; 649 size_t ll_hlen; 650 struct net_protocol *net_protocol; 651 unsigned int prottype; 652 int rc; 653 654 /* Use coloured debug, since UNDI ISR messages are likely to 655 * be interspersed amongst other UNDI messages. 656 */ 657 DBGC2 ( &pxenv_undi_isr, "PXENV_UNDI_ISR" ); 658 659 /* Just in case some idiot actually looks at these fields when 660 * we weren't meant to fill them in... 661 */ 662 undi_isr->BufferLength = 0; 663 undi_isr->FrameLength = 0; 664 undi_isr->FrameHeaderLength = 0; 665 undi_isr->ProtType = 0; 666 undi_isr->PktType = 0; 667 668 switch ( undi_isr->FuncFlag ) { 669 case PXENV_UNDI_ISR_IN_START : 670 DBGC2 ( &pxenv_undi_isr, " START" ); 671 672 /* Call poll(). This should acknowledge the device 673 * interrupt and queue up any received packet. 674 */ 675 netdev_poll ( pxe_netdev ); 676 677 /* Disable interrupts to avoid interrupt storm */ 678 netdev_irq ( pxe_netdev, 0 ); 679 680 /* Always say it was ours for the sake of simplicity */ 681 DBGC2 ( &pxenv_undi_isr, " OURS" ); 682 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS; 683 break; 684 case PXENV_UNDI_ISR_IN_PROCESS : 685 case PXENV_UNDI_ISR_IN_GET_NEXT : 686 DBGC2 ( &pxenv_undi_isr, " %s", 687 ( ( undi_isr->FuncFlag == PXENV_UNDI_ISR_IN_PROCESS ) ? 688 "PROCESS" : "GET_NEXT" ) ); 689 690 /* Some dumb NBPs (e.g. emBoot's winBoot/i) never call 691 * PXENV_UNDI_ISR with FuncFlag=PXENV_UNDI_ISR_START; 692 * they just sit in a tight polling loop merrily 693 * violating the PXE spec with repeated calls to 694 * PXENV_UNDI_ISR_IN_PROCESS. Force extra polls to 695 * cope with these out-of-spec clients. 696 */ 697 netdev_poll ( pxe_netdev ); 698 699 /* If we have not yet marked a TX as complete, and the 700 * netdev TX queue is empty, report the TX completion. 701 */ 702 if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) { 703 DBGC2 ( &pxenv_undi_isr, " TXC" ); 704 undi_tx_count--; 705 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT; 706 break; 707 } 708 709 /* Remove first packet from netdev RX queue */ 710 iobuf = netdev_rx_dequeue ( pxe_netdev ); 711 if ( ! iobuf ) { 712 DBGC2 ( &pxenv_undi_isr, " DONE" ); 713 /* No more packets remaining */ 714 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE; 715 /* Re-enable interrupts */ 716 netdev_irq ( pxe_netdev, 1 ); 717 break; 718 } 719 720 /* Copy packet to base memory buffer */ 721 len = iob_len ( iobuf ); 722 DBGC2 ( &pxenv_undi_isr, " RX" ); 723 if ( len > sizeof ( basemem_packet ) ) { 724 /* Should never happen */ 725 DBGC2 ( &pxenv_undi_isr, " overlength (%zx)", len ); 726 len = sizeof ( basemem_packet ); 727 } 728 memcpy ( basemem_packet, iobuf->data, len ); 729 730 /* Strip link-layer header */ 731 ll_protocol = pxe_netdev->ll_protocol; 732 if ( ( rc = ll_protocol->pull ( pxe_netdev, iobuf, &ll_dest, 733 &ll_source, &net_proto )) !=0){ 734 /* Assume unknown net_proto and no ll_source */ 735 net_proto = 0; 736 ll_source = NULL; 737 } 738 ll_hlen = ( len - iob_len ( iobuf ) ); 739 740 /* Determine network-layer protocol */ 741 switch ( net_proto ) { 742 case htons ( ETH_P_IP ): 743 net_protocol = &ipv4_protocol; 744 prottype = P_IP; 745 break; 746 case htons ( ETH_P_ARP ): 747 net_protocol = &arp_protocol; 748 prottype = P_ARP; 749 break; 750 case htons ( ETH_P_RARP ): 751 net_protocol = &rarp_protocol; 752 prottype = P_RARP; 753 break; 754 default: 755 net_protocol = NULL; 756 prottype = P_UNKNOWN; 757 break; 758 } 759 760 /* Fill in UNDI_ISR structure */ 761 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE; 762 undi_isr->BufferLength = len; 763 undi_isr->FrameLength = len; 764 undi_isr->FrameHeaderLength = ll_hlen; 765 undi_isr->Frame.segment = rm_ds; 766 undi_isr->Frame.offset = __from_data16 ( basemem_packet ); 767 undi_isr->ProtType = prottype; 768 undi_isr->PktType = XMT_DESTADDR; 769 DBGC2 ( &pxenv_undi_isr, " %04x:%04x+%x(%x) %s hlen %d", 770 undi_isr->Frame.segment, undi_isr->Frame.offset, 771 undi_isr->BufferLength, undi_isr->FrameLength, 772 ( net_protocol ? net_protocol->name : "RAW" ), 773 undi_isr->FrameHeaderLength ); 774 775 /* Free packet */ 776 free_iob ( iobuf ); 777 break; 778 default : 779 DBGC2 ( &pxenv_undi_isr, " INVALID(%04x)\n", 780 undi_isr->FuncFlag ); 781 782 /* Should never happen */ 783 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE; 784 undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER; 785 return PXENV_EXIT_FAILURE; 786 } 787 788 DBGC2 ( &pxenv_undi_isr, "\n" ); 789 undi_isr->Status = PXENV_STATUS_SUCCESS; 790 return PXENV_EXIT_SUCCESS; 791 } 792