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 32KB

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