You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. /** @file
  2. *
  3. * PXE UNDI API
  4. *
  5. */
  6. /*
  7. * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License as
  11. * published by the Free Software Foundation; either version 2 of the
  12. * License, or any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful, but
  15. * WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to the Free Software
  21. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22. */
  23. #include "pxe.h"
  24. typedef struct {
  25. char dest[ETH_ALEN];
  26. char source[ETH_ALEN];
  27. uint16_t nstype;
  28. } media_header_t;
  29. static const char broadcast_mac[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
  30. /* PXENV_UNDI_STARTUP
  31. *
  32. * Status: working
  33. */
  34. PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
  35. DBG ( "PXENV_UNDI_STARTUP" );
  36. ENSURE_MIDWAY(undi_startup);
  37. undi_startup->Status = PXENV_STATUS_SUCCESS;
  38. return PXENV_EXIT_SUCCESS;
  39. }
  40. /* PXENV_UNDI_CLEANUP
  41. *
  42. * Status: working
  43. */
  44. PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
  45. DBG ( "PXENV_UNDI_CLEANUP" );
  46. ENSURE_CAN_UNLOAD ( undi_cleanup );
  47. undi_cleanup->Status = PXENV_STATUS_SUCCESS;
  48. return PXENV_EXIT_SUCCESS;
  49. }
  50. /* PXENV_UNDI_INITIALIZE
  51. *
  52. * Status: working
  53. */
  54. PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
  55. *undi_initialize ) {
  56. DBG ( "PXENV_UNDI_INITIALIZE" );
  57. ENSURE_MIDWAY ( undi_initialize );
  58. undi_initialize->Status = PXENV_STATUS_SUCCESS;
  59. return PXENV_EXIT_SUCCESS;
  60. }
  61. /* PXENV_UNDI_RESET_ADAPTER
  62. *
  63. * Status: working
  64. */
  65. PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
  66. *undi_reset_adapter ) {
  67. DBG ( "PXENV_UNDI_RESET_ADAPTER" );
  68. ENSURE_MIDWAY ( undi_reset_adapter );
  69. ENSURE_READY ( undi_reset_adapter );
  70. undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
  71. return PXENV_EXIT_SUCCESS;
  72. }
  73. /* PXENV_UNDI_SHUTDOWN
  74. *
  75. * Status: working
  76. */
  77. PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
  78. *undi_shutdown ) {
  79. DBG ( "PXENV_UNDI_SHUTDOWN" );
  80. ENSURE_MIDWAY ( undi_shutdown );
  81. undi_shutdown->Status = PXENV_STATUS_SUCCESS;
  82. return PXENV_EXIT_SUCCESS;
  83. }
  84. /* PXENV_UNDI_OPEN
  85. *
  86. * Status: working
  87. */
  88. PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
  89. DBG ( "PXENV_UNDI_OPEN" );
  90. ENSURE_READY ( undi_open );
  91. /* PXESPEC: This is where we choose to enable interrupts.
  92. * Can't actually find where we're meant to in the PXE spec,
  93. * but this should work.
  94. */
  95. eth_irq ( ENABLE );
  96. undi_open->Status = PXENV_STATUS_SUCCESS;
  97. return PXENV_EXIT_SUCCESS;
  98. }
  99. /* PXENV_UNDI_CLOSE
  100. *
  101. * Status: working
  102. */
  103. PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
  104. DBG ( "PXENV_UNDI_CLOSE" );
  105. ENSURE_MIDWAY ( undi_close );
  106. undi_close->Status = PXENV_STATUS_SUCCESS;
  107. return PXENV_EXIT_SUCCESS;
  108. }
  109. /* PXENV_UNDI_TRANSMIT
  110. *
  111. * Status: working
  112. */
  113. PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
  114. *undi_transmit ) {
  115. struct s_PXENV_UNDI_TBD *tbd;
  116. const char *dest;
  117. unsigned int type;
  118. unsigned int length;
  119. const char *data;
  120. media_header_t *media_header;
  121. DBG ( "PXENV_UNDI_TRANSMIT" );
  122. ENSURE_READY ( undi_transmit );
  123. /* We support only the "immediate" portion of the TBD. Who
  124. * knows what Intel's "engineers" were smoking when they came
  125. * up with the array of transmit data blocks...
  126. */
  127. tbd = SEGOFF16_TO_PTR ( undi_transmit->TBD );
  128. if ( tbd->DataBlkCount > 0 ) {
  129. undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
  130. return PXENV_EXIT_FAILURE;
  131. }
  132. data = SEGOFF16_TO_PTR ( tbd->Xmit );
  133. length = tbd->ImmedLength;
  134. /* If destination is broadcast, we need to supply the MAC address */
  135. if ( undi_transmit->XmitFlag == XMT_BROADCAST ) {
  136. dest = broadcast_mac;
  137. } else {
  138. dest = SEGOFF16_TO_PTR ( undi_transmit->DestAddr );
  139. }
  140. /* We can't properly support P_UNKNOWN without rewriting all
  141. * the driver transmit() methods, so we cheat: if P_UNKNOWN is
  142. * specified we rip the destination address and type out of
  143. * the pre-assembled packet, then skip over the header.
  144. */
  145. switch ( undi_transmit->Protocol ) {
  146. case P_IP: type = ETH_P_IP; break;
  147. case P_ARP: type = ETH_P_ARP; break;
  148. case P_RARP: type = ETH_P_RARP; break;
  149. case P_UNKNOWN:
  150. media_header = (media_header_t*)data;
  151. dest = media_header->dest;
  152. type = ntohs ( media_header->nstype );
  153. data += ETH_HLEN;
  154. length -= ETH_HLEN;
  155. break;
  156. default:
  157. undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
  158. return PXENV_EXIT_FAILURE;
  159. }
  160. /* Send the packet */
  161. eth_transmit ( dest, type, length, data );
  162. undi_transmit->Status = PXENV_STATUS_SUCCESS;
  163. return PXENV_EXIT_SUCCESS;
  164. }
  165. /* PXENV_UNDI_SET_MCAST_ADDRESS
  166. *
  167. * Status: stub (no PXE multicast support)
  168. */
  169. PXENV_EXIT_t
  170. pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
  171. *undi_set_mcast_address ) {
  172. DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
  173. /* ENSURE_READY ( undi_set_mcast_address ); */
  174. undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
  175. return PXENV_EXIT_FAILURE;
  176. }
  177. /* PXENV_UNDI_SET_STATION_ADDRESS
  178. *
  179. * Status: working (deliberately incomplete)
  180. */
  181. PXENV_EXIT_t
  182. pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
  183. *undi_set_station_address ) {
  184. DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
  185. ENSURE_READY ( undi_set_station_address );
  186. /* We don't offer a facility to set the MAC address; this
  187. * would require adding extra code to all the Etherboot
  188. * drivers, for very little benefit. If we're setting it to
  189. * the current value anyway then return success, otherwise
  190. * return UNSUPPORTED.
  191. */
  192. if ( memcmp ( nic.node_addr,
  193. &undi_set_station_address->StationAddress,
  194. ETH_ALEN ) == 0 ) {
  195. undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
  196. return PXENV_EXIT_SUCCESS;
  197. }
  198. undi_set_station_address->Status = PXENV_STATUS_UNSUPPORTED;
  199. return PXENV_EXIT_FAILURE;
  200. }
  201. /* PXENV_UNDI_SET_PACKET_FILTER
  202. *
  203. * Status: won't implement (would require driver API changes for no
  204. * real benefit)
  205. */
  206. PXENV_EXIT_t
  207. pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
  208. *undi_set_packet_filter ) {
  209. DBG ( "PXENV_UNDI_SET_PACKET_FILTER" );
  210. /* ENSURE_READY ( undi_set_packet_filter ); */
  211. undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
  212. return PXENV_EXIT_FAILURE;
  213. }
  214. /* PXENV_UNDI_GET_INFORMATION
  215. *
  216. * Status: working
  217. */
  218. PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
  219. *undi_get_information ) {
  220. DBG ( "PXENV_UNDI_GET_INFORMATION" );
  221. ENSURE_READY ( undi_get_information );
  222. undi_get_information->BaseIo = nic.ioaddr;
  223. undi_get_information->IntNumber = nic.irqno;
  224. /* Cheat: assume all cards can cope with this */
  225. undi_get_information->MaxTranUnit = ETH_MAX_MTU;
  226. /* Cheat: we only ever have Ethernet cards */
  227. undi_get_information->HwType = ETHER_TYPE;
  228. undi_get_information->HwAddrLen = ETH_ALEN;
  229. /* Cheat: assume card is always configured with its permanent
  230. * node address. This is a valid assumption within Etherboot
  231. * at the time of writing.
  232. */
  233. memcpy ( &undi_get_information->CurrentNodeAddress, nic.node_addr,
  234. ETH_ALEN );
  235. memcpy ( &undi_get_information->PermNodeAddress, nic.node_addr,
  236. ETH_ALEN );
  237. undi_get_information->ROMAddress = 0;
  238. /* nic.rom_info->rom_segment; */
  239. /* We only provide the ability to receive or transmit a single
  240. * packet at a time. This is a bootloader, not an OS.
  241. */
  242. undi_get_information->RxBufCt = 1;
  243. undi_get_information->TxBufCt = 1;
  244. undi_get_information->Status = PXENV_STATUS_SUCCESS;
  245. return PXENV_EXIT_SUCCESS;
  246. }
  247. /* PXENV_UNDI_GET_STATISTICS
  248. *
  249. * Status: won't implement (would require driver API changes for no
  250. * real benefit)
  251. */
  252. PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
  253. *undi_get_statistics ) {
  254. DBG ( "PXENV_UNDI_GET_STATISTICS" );
  255. /* ENSURE_READY ( undi_get_statistics ); */
  256. undi_get_statistics->Status = PXENV_STATUS_UNSUPPORTED;
  257. return PXENV_EXIT_FAILURE;
  258. }
  259. /* PXENV_UNDI_CLEAR_STATISTICS
  260. *
  261. * Status: won't implement (would require driver API changes for no
  262. * real benefit)
  263. */
  264. PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
  265. *undi_clear_statistics ) {
  266. DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
  267. /* ENSURE_READY ( undi_clear_statistics ); */
  268. undi_clear_statistics->Status = PXENV_STATUS_UNSUPPORTED;
  269. return PXENV_EXIT_FAILURE;
  270. }
  271. /* PXENV_UNDI_INITIATE_DIAGS
  272. *
  273. * Status: won't implement (would require driver API changes for no
  274. * real benefit)
  275. */
  276. PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
  277. *undi_initiate_diags ) {
  278. DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
  279. /* ENSURE_READY ( undi_initiate_diags ); */
  280. undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
  281. return PXENV_EXIT_FAILURE;
  282. }
  283. /* PXENV_UNDI_FORCE_INTERRUPT
  284. *
  285. * Status: working
  286. */
  287. PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
  288. *undi_force_interrupt ) {
  289. DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
  290. ENSURE_READY ( undi_force_interrupt );
  291. eth_irq ( FORCE );
  292. undi_force_interrupt->Status = PXENV_STATUS_SUCCESS;
  293. return PXENV_EXIT_SUCCESS;
  294. }
  295. /* PXENV_UNDI_GET_MCAST_ADDRESS
  296. *
  297. * Status: stub (no PXE multicast support)
  298. */
  299. PXENV_EXIT_t
  300. pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
  301. *undi_get_mcast_address ) {
  302. DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
  303. /* ENSURE_READY ( undi_get_mcast_address ); */
  304. undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
  305. return PXENV_EXIT_FAILURE;
  306. }
  307. /* PXENV_UNDI_GET_NIC_TYPE
  308. *
  309. * Status: working
  310. */
  311. PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
  312. *undi_get_nic_type ) {
  313. #warning "device probing mechanism has changed completely"
  314. #if 0
  315. struct dev *dev = &dev;
  316. DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
  317. ENSURE_READY ( undi_get_nic_type );
  318. if ( dev->to_probe == PROBE_PCI ) {
  319. struct pci_device *pci = &dev->state.pci.dev;
  320. undi_get_nic_type->NicType = PCI_NIC;
  321. undi_get_nic_type->info.pci.Vendor_ID = pci->vendor;
  322. undi_get_nic_type->info.pci.Dev_ID = pci->dev_id;
  323. undi_get_nic_type->info.pci.Base_Class = pci->class >> 8;
  324. undi_get_nic_type->info.pci.Sub_Class = pci->class & 0xff;
  325. undi_get_nic_type->info.pci.BusDevFunc =
  326. ( pci->bus << 8 ) | pci->devfn;
  327. /* Cheat: these fields are probably unnecessary, and
  328. * would require adding extra code to pci.c.
  329. */
  330. undi_get_nic_type->info.pci.Prog_Intf = 0;
  331. undi_get_nic_type->info.pci.Rev = 0;
  332. undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
  333. undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
  334. } else if ( dev->to_probe == PROBE_ISA ) {
  335. /* const struct isa_driver *isa = dev->state.isa.driver; */
  336. undi_get_nic_type->NicType = PnP_NIC;
  337. /* Don't think anything fills these fields in, and
  338. * probably no-one will ever be interested in them.
  339. */
  340. undi_get_nic_type->info.pnp.EISA_Dev_ID = 0;
  341. undi_get_nic_type->info.pnp.Base_Class = 0;
  342. undi_get_nic_type->info.pnp.Sub_Class = 0;
  343. undi_get_nic_type->info.pnp.Prog_Intf = 0;
  344. undi_get_nic_type->info.pnp.CardSelNum = 0;
  345. } else {
  346. /* PXESPEC: There doesn't seem to be an "unknown type"
  347. * defined.
  348. */
  349. undi_get_nic_type->NicType = 0;
  350. }
  351. undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
  352. return PXENV_EXIT_SUCCESS;
  353. #endif
  354. }
  355. /* PXENV_UNDI_GET_IFACE_INFO
  356. *
  357. * Status: working
  358. */
  359. PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
  360. *undi_get_iface_info ) {
  361. DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
  362. ENSURE_READY ( undi_get_iface_info );
  363. /* Just hand back some info, doesn't really matter what it is.
  364. * Most PXE stacks seem to take this approach.
  365. */
  366. sprintf ( undi_get_iface_info->IfaceType, "Etherboot" );
  367. undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
  368. undi_get_iface_info->ServiceFlags = 0;
  369. memset ( undi_get_iface_info->Reserved, 0,
  370. sizeof(undi_get_iface_info->Reserved) );
  371. undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
  372. return PXENV_EXIT_SUCCESS;
  373. }
  374. /* PXENV_UNDI_GET_STATE
  375. *
  376. * Status: impossible
  377. */
  378. PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
  379. *undi_get_state ) {
  380. undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
  381. return PXENV_EXIT_FAILURE;
  382. };
  383. /* PXENV_UNDI_ISR
  384. *
  385. * Status: working
  386. */
  387. PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
  388. media_header_t *media_header = (media_header_t*)nic.packet;
  389. DBG ( "PXENV_UNDI_ISR" );
  390. /* We can't call ENSURE_READY, because this could be being
  391. * called as part of an interrupt service routine. Instead,
  392. * we should simply die if we're not READY.
  393. */
  394. if ( ( pxe_stack == NULL ) || ( pxe_stack->state < READY ) ) {
  395. undi_isr->Status = PXENV_STATUS_UNDI_INVALID_STATE;
  396. return PXENV_EXIT_FAILURE;
  397. }
  398. /* Just in case some idiot actually looks at these fields when
  399. * we weren't meant to fill them in...
  400. */
  401. undi_isr->BufferLength = 0;
  402. undi_isr->FrameLength = 0;
  403. undi_isr->FrameHeaderLength = 0;
  404. undi_isr->ProtType = 0;
  405. undi_isr->PktType = 0;
  406. switch ( undi_isr->FuncFlag ) {
  407. case PXENV_UNDI_ISR_IN_START :
  408. /* Is there a packet waiting? If so, disable
  409. * interrupts on the NIC and return "it's ours". Do
  410. * *not* necessarily acknowledge the interrupt; this
  411. * can happen later when eth_poll(1) is called. As
  412. * long as the interrupt is masked off so that it
  413. * doesn't immediately retrigger the 8259A then all
  414. * should be well.
  415. */
  416. DBG ( " START" );
  417. if ( eth_poll ( 0 ) ) {
  418. DBG ( " OURS" );
  419. eth_irq ( DISABLE );
  420. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
  421. } else {
  422. DBG ( " NOT_OURS" );
  423. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
  424. }
  425. break;
  426. case PXENV_UNDI_ISR_IN_PROCESS :
  427. /* Call poll(), return packet. If no packet, return "done".
  428. */
  429. DBG ( " PROCESS" );
  430. if ( eth_poll ( 1 ) ) {
  431. DBG ( " RECEIVE %d", nic.packetlen );
  432. if ( nic.packetlen > sizeof(pxe_stack->packet) ) {
  433. /* Should never happen */
  434. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
  435. undi_isr->Status =
  436. PXENV_STATUS_OUT_OF_RESOURCES;
  437. return PXENV_EXIT_FAILURE;
  438. }
  439. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
  440. undi_isr->BufferLength = nic.packetlen;
  441. undi_isr->FrameLength = nic.packetlen;
  442. undi_isr->FrameHeaderLength = ETH_HLEN;
  443. memcpy ( pxe_stack->packet, nic.packet, nic.packetlen);
  444. PTR_TO_SEGOFF16 ( pxe_stack->packet, undi_isr->Frame );
  445. switch ( ntohs(media_header->nstype) ) {
  446. case ETH_P_IP: undi_isr->ProtType = P_IP; break;
  447. case ETH_P_ARP: undi_isr->ProtType = P_ARP; break;
  448. case ETH_P_RARP: undi_isr->ProtType = P_RARP; break;
  449. default : undi_isr->ProtType = P_UNKNOWN;
  450. }
  451. if ( memcmp ( media_header->dest, broadcast_mac,
  452. sizeof(broadcast_mac) ) ) {
  453. undi_isr->PktType = XMT_BROADCAST;
  454. } else {
  455. undi_isr->PktType = XMT_DESTADDR;
  456. }
  457. break;
  458. } else {
  459. /* No break - fall through to IN_GET_NEXT */
  460. }
  461. case PXENV_UNDI_ISR_IN_GET_NEXT :
  462. /* We only ever return one frame at a time */
  463. DBG ( " GET_NEXT DONE" );
  464. /* Re-enable interrupts */
  465. eth_irq ( ENABLE );
  466. /* Force an interrupt if there's a packet still
  467. * waiting, since we only handle one packet per
  468. * interrupt.
  469. */
  470. if ( eth_poll ( 0 ) ) {
  471. DBG ( " (RETRIGGER)" );
  472. eth_irq ( FORCE );
  473. }
  474. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
  475. break;
  476. default :
  477. /* Should never happen */
  478. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
  479. undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
  480. return PXENV_EXIT_FAILURE;
  481. }
  482. undi_isr->Status = PXENV_STATUS_SUCCESS;
  483. return PXENV_EXIT_SUCCESS;
  484. }