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.

pxe_undi.c 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  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 <stdint.h>
  24. #include <stdio.h>
  25. #include <string.h>
  26. #include <byteswap.h>
  27. #include <basemem_packet.h>
  28. #include <gpxe/netdevice.h>
  29. #include <gpxe/iobuf.h>
  30. #include <gpxe/device.h>
  31. #include <gpxe/pci.h>
  32. #include <gpxe/if_ether.h>
  33. #include <gpxe/ip.h>
  34. #include <gpxe/arp.h>
  35. #include <gpxe/rarp.h>
  36. #include "pxe.h"
  37. /**
  38. * Count of outstanding transmitted packets
  39. *
  40. * This is incremented each time PXENV_UNDI_TRANSMIT is called, and
  41. * decremented each time that PXENV_UNDI_ISR is called with the TX
  42. * queue empty, stopping when the count reaches zero. This allows us
  43. * to provide a pessimistic approximation of TX completion events to
  44. * the PXE NBP simply by monitoring the netdev's TX queue.
  45. */
  46. static int undi_tx_count = 0;
  47. /**
  48. * Open PXE network device
  49. *
  50. * @ret rc Return status code
  51. */
  52. static int pxe_netdev_open ( void ) {
  53. return netdev_open ( pxe_netdev );
  54. }
  55. /**
  56. * Close PXE network device
  57. *
  58. */
  59. static void pxe_netdev_close ( void ) {
  60. netdev_close ( pxe_netdev );
  61. undi_tx_count = 0;
  62. }
  63. /* PXENV_UNDI_STARTUP
  64. *
  65. * Status: working
  66. */
  67. PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
  68. DBG ( "PXENV_UNDI_STARTUP" );
  69. undi_startup->Status = PXENV_STATUS_SUCCESS;
  70. return PXENV_EXIT_SUCCESS;
  71. }
  72. /* PXENV_UNDI_CLEANUP
  73. *
  74. * Status: working
  75. */
  76. PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
  77. DBG ( "PXENV_UNDI_CLEANUP" );
  78. pxe_netdev_close();
  79. undi_cleanup->Status = PXENV_STATUS_SUCCESS;
  80. return PXENV_EXIT_SUCCESS;
  81. }
  82. /* PXENV_UNDI_INITIALIZE
  83. *
  84. * Status: working
  85. */
  86. PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
  87. *undi_initialize ) {
  88. DBG ( "PXENV_UNDI_INITIALIZE" );
  89. undi_initialize->Status = PXENV_STATUS_SUCCESS;
  90. return PXENV_EXIT_SUCCESS;
  91. }
  92. /* PXENV_UNDI_RESET_ADAPTER
  93. *
  94. * Status: working
  95. */
  96. PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
  97. *undi_reset_adapter ) {
  98. int rc;
  99. DBG ( "PXENV_UNDI_RESET_ADAPTER" );
  100. pxe_netdev_close();
  101. if ( ( rc = pxe_netdev_open() ) != 0 ) {
  102. undi_reset_adapter->Status = PXENV_STATUS ( rc );
  103. return PXENV_EXIT_FAILURE;
  104. }
  105. undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
  106. return PXENV_EXIT_SUCCESS;
  107. }
  108. /* PXENV_UNDI_SHUTDOWN
  109. *
  110. * Status: working
  111. */
  112. PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
  113. *undi_shutdown ) {
  114. DBG ( "PXENV_UNDI_SHUTDOWN" );
  115. pxe_netdev_close();
  116. undi_shutdown->Status = PXENV_STATUS_SUCCESS;
  117. return PXENV_EXIT_SUCCESS;
  118. }
  119. /* PXENV_UNDI_OPEN
  120. *
  121. * Status: working
  122. */
  123. PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
  124. int rc;
  125. DBG ( "PXENV_UNDI_OPEN" );
  126. if ( ( rc = pxe_netdev_open() ) != 0 ) {
  127. undi_open->Status = PXENV_STATUS ( rc );
  128. return PXENV_EXIT_FAILURE;
  129. }
  130. undi_open->Status = PXENV_STATUS_SUCCESS;
  131. return PXENV_EXIT_SUCCESS;
  132. }
  133. /* PXENV_UNDI_CLOSE
  134. *
  135. * Status: working
  136. */
  137. PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
  138. DBG ( "PXENV_UNDI_CLOSE" );
  139. pxe_netdev_close();
  140. undi_close->Status = PXENV_STATUS_SUCCESS;
  141. return PXENV_EXIT_SUCCESS;
  142. }
  143. /* PXENV_UNDI_TRANSMIT
  144. *
  145. * Status: working
  146. */
  147. PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
  148. *undi_transmit ) {
  149. struct s_PXENV_UNDI_TBD tbd;
  150. struct DataBlk *datablk;
  151. struct io_buffer *iobuf;
  152. struct net_protocol *net_protocol;
  153. char destaddr[MAX_LL_ADDR_LEN];
  154. const void *ll_dest;
  155. size_t ll_hlen = pxe_netdev->ll_protocol->ll_header_len;
  156. size_t len;
  157. unsigned int i;
  158. int rc;
  159. DBG ( "PXENV_UNDI_TRANSMIT" );
  160. /* Identify network-layer protocol */
  161. switch ( undi_transmit->Protocol ) {
  162. case P_IP: net_protocol = &ipv4_protocol; break;
  163. case P_ARP: net_protocol = &arp_protocol; break;
  164. case P_RARP: net_protocol = &rarp_protocol; break;
  165. case P_UNKNOWN:
  166. net_protocol = NULL;
  167. ll_hlen = 0;
  168. break;
  169. default:
  170. undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
  171. return PXENV_EXIT_FAILURE;
  172. }
  173. DBG ( " %s", ( net_protocol ? net_protocol->name : "UNKNOWN" ) );
  174. /* Calculate total packet length */
  175. copy_from_real ( &tbd, undi_transmit->TBD.segment,
  176. undi_transmit->TBD.offset, sizeof ( tbd ) );
  177. len = tbd.ImmedLength;
  178. DBG ( " %zd", tbd.ImmedLength );
  179. for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
  180. datablk = &tbd.DataBlock[i];
  181. len += datablk->TDDataLen;
  182. DBG ( "+%zd", datablk->TDDataLen );
  183. }
  184. /* Allocate and fill I/O buffer */
  185. iobuf = alloc_iob ( ll_hlen + len );
  186. if ( ! iobuf ) {
  187. undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
  188. return PXENV_EXIT_FAILURE;
  189. }
  190. iob_reserve ( iobuf, ll_hlen );
  191. copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
  192. tbd.Xmit.offset, tbd.ImmedLength );
  193. for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
  194. datablk = &tbd.DataBlock[i];
  195. copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
  196. datablk->TDDataPtr.segment,
  197. datablk->TDDataPtr.offset,
  198. datablk->TDDataLen );
  199. }
  200. /* Transmit packet */
  201. if ( net_protocol == NULL ) {
  202. /* Link-layer header already present */
  203. rc = netdev_tx ( pxe_netdev, iobuf );
  204. } else {
  205. /* Calculate destination address */
  206. if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
  207. copy_from_real ( destaddr,
  208. undi_transmit->DestAddr.segment,
  209. undi_transmit->DestAddr.offset,
  210. pxe_netdev->ll_protocol->ll_addr_len );
  211. ll_dest = destaddr;
  212. } else {
  213. ll_dest = pxe_netdev->ll_protocol->ll_broadcast;
  214. }
  215. rc = net_tx ( iobuf, pxe_netdev, net_protocol, ll_dest );
  216. }
  217. /* Flag transmission as in-progress */
  218. undi_tx_count++;
  219. undi_transmit->Status = PXENV_STATUS ( rc );
  220. return ( ( rc == 0 ) ? PXENV_EXIT_SUCCESS : PXENV_EXIT_FAILURE );
  221. }
  222. /* PXENV_UNDI_SET_MCAST_ADDRESS
  223. *
  224. * Status: stub (no PXE multicast support)
  225. */
  226. PXENV_EXIT_t
  227. pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
  228. *undi_set_mcast_address ) {
  229. DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
  230. undi_set_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
  231. return PXENV_EXIT_FAILURE;
  232. }
  233. /* PXENV_UNDI_SET_STATION_ADDRESS
  234. *
  235. * Status: working
  236. */
  237. PXENV_EXIT_t
  238. pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
  239. *undi_set_station_address ) {
  240. DBG ( "PXENV_UNDI_SET_STATION_ADDRESS" );
  241. /* If adapter is open, the change will have no effect; return
  242. * an error
  243. */
  244. if ( pxe_netdev->state & NETDEV_OPEN ) {
  245. undi_set_station_address->Status =
  246. PXENV_STATUS_UNDI_INVALID_STATE;
  247. return PXENV_EXIT_FAILURE;
  248. }
  249. /* Update MAC address */
  250. memcpy ( pxe_netdev->ll_addr,
  251. &undi_set_station_address->StationAddress,
  252. pxe_netdev->ll_protocol->ll_addr_len );
  253. undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
  254. return PXENV_EXIT_SUCCESS;
  255. }
  256. /* PXENV_UNDI_SET_PACKET_FILTER
  257. *
  258. * Status: won't implement (would require driver API changes for no
  259. * real benefit)
  260. */
  261. PXENV_EXIT_t
  262. pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
  263. *undi_set_packet_filter ) {
  264. DBG ( "PXENV_UNDI_SET_PACKET_FILTER" );
  265. undi_set_packet_filter->Status = PXENV_STATUS_UNSUPPORTED;
  266. return PXENV_EXIT_FAILURE;
  267. }
  268. /* PXENV_UNDI_GET_INFORMATION
  269. *
  270. * Status: working
  271. */
  272. PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
  273. *undi_get_information ) {
  274. struct device *dev = pxe_netdev->dev;
  275. struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
  276. DBG ( "PXENV_UNDI_GET_INFORMATION" );
  277. undi_get_information->BaseIo = dev->desc.ioaddr;
  278. undi_get_information->IntNumber = dev->desc.irq;
  279. /* Cheat: assume all cards can cope with this */
  280. undi_get_information->MaxTranUnit = ETH_MAX_MTU;
  281. undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
  282. undi_get_information->HwAddrLen = ll_protocol->ll_addr_len;
  283. /* Cheat: assume card is always configured with its permanent
  284. * node address. This is a valid assumption within Etherboot
  285. * at the time of writing.
  286. */
  287. memcpy ( &undi_get_information->CurrentNodeAddress,
  288. pxe_netdev->ll_addr,
  289. sizeof ( undi_get_information->CurrentNodeAddress ) );
  290. memcpy ( &undi_get_information->PermNodeAddress,
  291. pxe_netdev->ll_addr,
  292. sizeof ( undi_get_information->PermNodeAddress ) );
  293. undi_get_information->ROMAddress = 0;
  294. /* nic.rom_info->rom_segment; */
  295. /* We only provide the ability to receive or transmit a single
  296. * packet at a time. This is a bootloader, not an OS.
  297. */
  298. undi_get_information->RxBufCt = 1;
  299. undi_get_information->TxBufCt = 1;
  300. undi_get_information->Status = PXENV_STATUS_SUCCESS;
  301. return PXENV_EXIT_SUCCESS;
  302. }
  303. /* PXENV_UNDI_GET_STATISTICS
  304. *
  305. * Status: working
  306. */
  307. PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
  308. *undi_get_statistics ) {
  309. DBG ( "PXENV_UNDI_GET_STATISTICS" );
  310. undi_get_statistics->XmtGoodFrames = pxe_netdev->stats.tx_count;
  311. undi_get_statistics->RcvGoodFrames = pxe_netdev->stats.rx_count;
  312. undi_get_statistics->RcvCRCErrors = 0;
  313. undi_get_statistics->RcvResourceErrors = 0;
  314. undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
  315. return PXENV_EXIT_SUCCESS;
  316. }
  317. /* PXENV_UNDI_CLEAR_STATISTICS
  318. *
  319. * Status: working
  320. */
  321. PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
  322. *undi_clear_statistics ) {
  323. DBG ( "PXENV_UNDI_CLEAR_STATISTICS" );
  324. memset ( &pxe_netdev->stats, 0, sizeof ( pxe_netdev->stats ) );
  325. undi_clear_statistics->Status = PXENV_STATUS_SUCCESS;
  326. return PXENV_EXIT_SUCCESS;
  327. }
  328. /* PXENV_UNDI_INITIATE_DIAGS
  329. *
  330. * Status: won't implement (would require driver API changes for no
  331. * real benefit)
  332. */
  333. PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
  334. *undi_initiate_diags ) {
  335. DBG ( "PXENV_UNDI_INITIATE_DIAGS" );
  336. undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
  337. return PXENV_EXIT_FAILURE;
  338. }
  339. /* PXENV_UNDI_FORCE_INTERRUPT
  340. *
  341. * Status: working
  342. */
  343. PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
  344. *undi_force_interrupt ) {
  345. DBG ( "PXENV_UNDI_FORCE_INTERRUPT" );
  346. #if 0
  347. eth_irq ( FORCE );
  348. #endif
  349. undi_force_interrupt->Status = PXENV_STATUS_SUCCESS;
  350. return PXENV_EXIT_SUCCESS;
  351. }
  352. /* PXENV_UNDI_GET_MCAST_ADDRESS
  353. *
  354. * Status: stub (no PXE multicast support)
  355. */
  356. PXENV_EXIT_t
  357. pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
  358. *undi_get_mcast_address ) {
  359. DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS" );
  360. undi_get_mcast_address->Status = PXENV_STATUS_UNSUPPORTED;
  361. return PXENV_EXIT_FAILURE;
  362. }
  363. /* PXENV_UNDI_GET_NIC_TYPE
  364. *
  365. * Status: working
  366. */
  367. PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
  368. *undi_get_nic_type ) {
  369. struct device *dev = pxe_netdev->dev;
  370. DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
  371. memset ( &undi_get_nic_type->info, 0,
  372. sizeof ( undi_get_nic_type->info ) );
  373. switch ( dev->desc.bus_type ) {
  374. case BUS_TYPE_PCI: {
  375. struct pci_nic_info *info = &undi_get_nic_type->info.pci;
  376. undi_get_nic_type->NicType = PCI_NIC;
  377. info->Vendor_ID = dev->desc.vendor;
  378. info->Dev_ID = dev->desc.device;
  379. info->Base_Class = PCI_BASE_CLASS ( dev->desc.class );
  380. info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class );
  381. info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class );
  382. info->BusDevFunc = dev->desc.location;
  383. /* Cheat: remaining fields are probably unnecessary,
  384. * and would require adding extra code to pci.c.
  385. */
  386. undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
  387. undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
  388. break; }
  389. case BUS_TYPE_ISAPNP: {
  390. struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
  391. undi_get_nic_type->NicType = PnP_NIC;
  392. info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) |
  393. dev->desc.device );
  394. info->CardSelNum = dev->desc.location;
  395. /* Cheat: remaining fields are probably unnecessary,
  396. * and would require adding extra code to isapnp.c.
  397. */
  398. break; }
  399. default:
  400. undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
  401. return PXENV_EXIT_FAILURE;
  402. }
  403. undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
  404. return PXENV_EXIT_SUCCESS;
  405. }
  406. /* PXENV_UNDI_GET_IFACE_INFO
  407. *
  408. * Status: working
  409. */
  410. PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
  411. *undi_get_iface_info ) {
  412. DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
  413. /* Just hand back some info, doesn't really matter what it is.
  414. * Most PXE stacks seem to take this approach.
  415. */
  416. snprintf ( ( char * ) undi_get_iface_info->IfaceType,
  417. sizeof ( undi_get_iface_info->IfaceType ), "Etherboot" );
  418. undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
  419. undi_get_iface_info->ServiceFlags = 0;
  420. memset ( undi_get_iface_info->Reserved, 0,
  421. sizeof(undi_get_iface_info->Reserved) );
  422. undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
  423. return PXENV_EXIT_SUCCESS;
  424. }
  425. /* PXENV_UNDI_GET_STATE
  426. *
  427. * Status: impossible
  428. */
  429. PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
  430. *undi_get_state ) {
  431. DBG ( "PXENV_UNDI_GET_STATE" );
  432. undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
  433. return PXENV_EXIT_FAILURE;
  434. };
  435. /* PXENV_UNDI_ISR
  436. *
  437. * Status: working
  438. */
  439. PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
  440. struct io_buffer *iobuf;
  441. size_t len;
  442. DBG ( "PXENV_UNDI_ISR" );
  443. /* Just in case some idiot actually looks at these fields when
  444. * we weren't meant to fill them in...
  445. */
  446. undi_isr->BufferLength = 0;
  447. undi_isr->FrameLength = 0;
  448. undi_isr->FrameHeaderLength = 0;
  449. undi_isr->ProtType = 0;
  450. undi_isr->PktType = 0;
  451. switch ( undi_isr->FuncFlag ) {
  452. case PXENV_UNDI_ISR_IN_START :
  453. DBG ( " START" );
  454. /* Call poll(). This should acknowledge the device
  455. * interrupt and queue up any received packet.
  456. */
  457. if ( netdev_poll ( pxe_netdev, -1U ) ) {
  458. /* Packet waiting in queue */
  459. DBG ( " OURS" );
  460. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
  461. } else {
  462. DBG ( " NOT_OURS" );
  463. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
  464. }
  465. break;
  466. case PXENV_UNDI_ISR_IN_PROCESS :
  467. case PXENV_UNDI_ISR_IN_GET_NEXT :
  468. DBG ( " PROCESS/GET_NEXT" );
  469. /* If we have not yet marked a TX as complete, and the
  470. * netdev TX queue is empty, report the TX completion.
  471. */
  472. if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) {
  473. undi_tx_count--;
  474. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT;
  475. break;
  476. }
  477. /* Remove first packet from netdev RX queue */
  478. iobuf = netdev_rx_dequeue ( pxe_netdev );
  479. if ( ! iobuf ) {
  480. /* No more packets remaining */
  481. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
  482. break;
  483. }
  484. /* Copy packet to base memory buffer */
  485. len = iob_len ( iobuf );
  486. DBG ( " RECEIVE %zd", len );
  487. if ( len > sizeof ( basemem_packet ) ) {
  488. /* Should never happen */
  489. len = sizeof ( basemem_packet );
  490. }
  491. memcpy ( basemem_packet, iobuf->data, len );
  492. /* Fill in UNDI_ISR structure */
  493. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
  494. undi_isr->BufferLength = len;
  495. undi_isr->FrameLength = len;
  496. undi_isr->FrameHeaderLength =
  497. pxe_netdev->ll_protocol->ll_header_len;
  498. undi_isr->Frame.segment = rm_ds;
  499. undi_isr->Frame.offset =
  500. ( ( unsigned ) & __from_data16 ( basemem_packet ) );
  501. /* Probably ought to fill in packet type */
  502. undi_isr->ProtType = P_UNKNOWN;
  503. undi_isr->PktType = XMT_DESTADDR;
  504. /* Free packet */
  505. free_iob ( iobuf );
  506. break;
  507. default :
  508. DBG ( " INVALID(%04x)", undi_isr->FuncFlag );
  509. /* Should never happen */
  510. undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
  511. undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
  512. return PXENV_EXIT_FAILURE;
  513. }
  514. undi_isr->Status = PXENV_STATUS_SUCCESS;
  515. return PXENV_EXIT_SUCCESS;
  516. }