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.

snpnet.c 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. /*
  2. * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License as
  6. * published by the Free Software Foundation; either version 2 of the
  7. * License, or any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  17. * 02110-1301, USA.
  18. */
  19. FILE_LICENCE ( GPL2_OR_LATER );
  20. #include <stdlib.h>
  21. #include <stdio.h>
  22. #include <string.h>
  23. #include <errno.h>
  24. #include <ipxe/iobuf.h>
  25. #include <ipxe/netdevice.h>
  26. #include <ipxe/ethernet.h>
  27. #include <ipxe/vsprintf.h>
  28. #include <ipxe/efi/efi.h>
  29. #include <ipxe/efi/Protocol/SimpleNetwork.h>
  30. #include <ipxe/efi/efi_driver.h>
  31. #include <ipxe/efi/efi_pci.h>
  32. #include <ipxe/efi/efi_utils.h>
  33. #include "snpnet.h"
  34. /** @file
  35. *
  36. * SNP NIC driver
  37. *
  38. */
  39. /** An SNP NIC */
  40. struct snp_nic {
  41. /** EFI device */
  42. struct efi_device *efidev;
  43. /** Simple network protocol */
  44. EFI_SIMPLE_NETWORK_PROTOCOL *snp;
  45. /** Generic device */
  46. struct device dev;
  47. /** Maximum packet size
  48. *
  49. * This is calculated as the sum of MediaHeaderSize and
  50. * MaxPacketSize, and may therefore be an overestimate.
  51. */
  52. size_t mtu;
  53. /** Current transmit buffer */
  54. struct io_buffer *txbuf;
  55. /** Current receive buffer */
  56. struct io_buffer *rxbuf;
  57. };
  58. /** Maximum number of received packets per poll */
  59. #define SNP_RX_QUOTA 4
  60. /**
  61. * Format SNP MAC address (for debugging)
  62. *
  63. * @v mac MAC address
  64. * @v len Length of MAC address
  65. * @ret text MAC address as text
  66. */
  67. static const char * snpnet_mac_text ( EFI_MAC_ADDRESS *mac, size_t len ) {
  68. static char buf[ sizeof ( *mac ) * 3 /* "xx:" or "xx\0" */ ];
  69. size_t used = 0;
  70. unsigned int i;
  71. for ( i = 0 ; i < len ; i++ ) {
  72. used += ssnprintf ( &buf[used], ( sizeof ( buf ) - used ),
  73. "%s%02x", ( used ? ":" : "" ),
  74. mac->Addr[i] );
  75. }
  76. return buf;
  77. }
  78. /**
  79. * Dump SNP mode information (for debugging)
  80. *
  81. * @v netdev Network device
  82. */
  83. static void snpnet_dump_mode ( struct net_device *netdev ) {
  84. struct snp_nic *snp = netdev_priv ( netdev );
  85. EFI_SIMPLE_NETWORK_MODE *mode = snp->snp->Mode;
  86. size_t mac_len = mode->HwAddressSize;
  87. unsigned int i;
  88. /* Do nothing unless debugging is enabled */
  89. if ( ! DBG_EXTRA )
  90. return;
  91. DBGC2 ( snp, "SNP %s st %d type %d hdr %d pkt %d rxflt %#x/%#x%s "
  92. "nvram %d acc %d mcast %d/%d\n", netdev->name, mode->State,
  93. mode->IfType, mode->MediaHeaderSize, mode->MaxPacketSize,
  94. mode->ReceiveFilterSetting, mode->ReceiveFilterMask,
  95. ( mode->MultipleTxSupported ? " multitx" : "" ),
  96. mode->NvRamSize, mode->NvRamAccessSize,
  97. mode->MCastFilterCount, mode->MaxMCastFilterCount );
  98. DBGC2 ( snp, "SNP %s hw %s", netdev->name,
  99. snpnet_mac_text ( &mode->PermanentAddress, mac_len ) );
  100. DBGC2 ( snp, " addr %s%s",
  101. snpnet_mac_text ( &mode->CurrentAddress, mac_len ),
  102. ( mode->MacAddressChangeable ? "" : "(f)" ) );
  103. DBGC2 ( snp, " bcast %s\n",
  104. snpnet_mac_text ( &mode->BroadcastAddress, mac_len ) );
  105. for ( i = 0 ; i < mode->MCastFilterCount ; i++ ) {
  106. DBGC2 ( snp, "SNP %s mcast %s\n", netdev->name,
  107. snpnet_mac_text ( &mode->MCastFilter[i], mac_len ) );
  108. }
  109. DBGC2 ( snp, "SNP %s media %s\n", netdev->name,
  110. ( mode->MediaPresentSupported ?
  111. ( mode->MediaPresent ? "present" : "not present" ) :
  112. "presence not supported" ) );
  113. }
  114. /**
  115. * Check link state
  116. *
  117. * @v netdev Network device
  118. */
  119. static void snpnet_check_link ( struct net_device *netdev ) {
  120. struct snp_nic *snp = netdev_priv ( netdev );
  121. EFI_SIMPLE_NETWORK_MODE *mode = snp->snp->Mode;
  122. /* Do nothing unless media presence detection is supported */
  123. if ( ! mode->MediaPresentSupported )
  124. return;
  125. /* Report any link status change */
  126. if ( mode->MediaPresent && ( ! netdev_link_ok ( netdev ) ) ) {
  127. netdev_link_up ( netdev );
  128. } else if ( ( ! mode->MediaPresent ) && netdev_link_ok ( netdev ) ) {
  129. netdev_link_down ( netdev );
  130. }
  131. }
  132. /**
  133. * Transmit packet
  134. *
  135. * @v netdev Network device
  136. * @v iobuf I/O buffer
  137. * @ret rc Return status code
  138. */
  139. static int snpnet_transmit ( struct net_device *netdev,
  140. struct io_buffer *iobuf ) {
  141. struct snp_nic *snp = netdev_priv ( netdev );
  142. EFI_STATUS efirc;
  143. int rc;
  144. /* Defer the packet if there is already a transmission in progress */
  145. if ( snp->txbuf ) {
  146. netdev_tx_defer ( netdev, iobuf );
  147. return 0;
  148. }
  149. /* Transmit packet */
  150. if ( ( efirc = snp->snp->Transmit ( snp->snp, 0, iob_len ( iobuf ),
  151. iobuf->data, NULL, NULL,
  152. NULL ) ) != 0 ) {
  153. rc = -EEFI ( efirc );
  154. DBGC ( snp, "SNP %s could not transmit: %s\n",
  155. netdev->name, strerror ( rc ) );
  156. return rc;
  157. }
  158. snp->txbuf = iobuf;
  159. return 0;
  160. }
  161. /**
  162. * Poll for completed packets
  163. *
  164. * @v netdev Network device
  165. */
  166. static void snpnet_poll_tx ( struct net_device *netdev ) {
  167. struct snp_nic *snp = netdev->priv;
  168. UINT32 irq;
  169. VOID *txbuf;
  170. EFI_STATUS efirc;
  171. int rc;
  172. /* Get status */
  173. if ( ( efirc = snp->snp->GetStatus ( snp->snp, &irq, &txbuf ) ) != 0 ) {
  174. rc = -EEFI ( efirc );
  175. DBGC ( snp, "SNP %s could not get status: %s\n",
  176. netdev->name, strerror ( rc ) );
  177. netdev_rx_err ( netdev, NULL, rc );
  178. return;
  179. }
  180. /* Do nothing unless we have a completion */
  181. if ( ! txbuf )
  182. return;
  183. /* Sanity check */
  184. if ( ! snp->txbuf ) {
  185. DBGC ( snp, "SNP %s reported spurious TX completion\n",
  186. netdev->name );
  187. netdev_tx_err ( netdev, NULL, -EPIPE );
  188. return;
  189. }
  190. /* Complete transmission */
  191. netdev_tx_complete ( netdev, snp->txbuf );
  192. snp->txbuf = NULL;
  193. }
  194. /**
  195. * Poll for received packets
  196. *
  197. * @v netdev Network device
  198. */
  199. static void snpnet_poll_rx ( struct net_device *netdev ) {
  200. struct snp_nic *snp = netdev->priv;
  201. UINTN len;
  202. unsigned int quota;
  203. EFI_STATUS efirc;
  204. int rc;
  205. /* Retrieve up to SNP_RX_QUOTA packets */
  206. for ( quota = SNP_RX_QUOTA ; quota ; quota-- ) {
  207. /* Allocate buffer, if required */
  208. if ( ! snp->rxbuf ) {
  209. snp->rxbuf = alloc_iob ( snp->mtu );
  210. if ( ! snp->rxbuf ) {
  211. /* Leave for next poll */
  212. break;
  213. }
  214. }
  215. /* Receive packet */
  216. len = iob_tailroom ( snp->rxbuf );
  217. if ( ( efirc = snp->snp->Receive ( snp->snp, NULL, &len,
  218. snp->rxbuf->data, NULL,
  219. NULL, NULL ) ) != 0 ) {
  220. /* EFI_NOT_READY is just the usual "no packet"
  221. * status indication; ignore it.
  222. */
  223. if ( efirc == EFI_NOT_READY )
  224. break;
  225. /* Anything else is an error */
  226. rc = -EEFI ( efirc );
  227. DBGC ( snp, "SNP %s could not receive: %s\n",
  228. netdev->name, strerror ( rc ) );
  229. netdev_rx_err ( netdev, NULL, rc );
  230. break;
  231. }
  232. /* Hand off to network stack */
  233. iob_put ( snp->rxbuf, len );
  234. netdev_rx ( netdev, snp->rxbuf );
  235. snp->rxbuf = NULL;
  236. }
  237. }
  238. /**
  239. * Poll for completed packets
  240. *
  241. * @v netdev Network device
  242. */
  243. static void snpnet_poll ( struct net_device *netdev ) {
  244. /* Process any TX completions */
  245. snpnet_poll_tx ( netdev );
  246. /* Process any RX completions */
  247. snpnet_poll_rx ( netdev );
  248. /* Check for link state changes */
  249. snpnet_check_link ( netdev );
  250. }
  251. /**
  252. * Set receive filters
  253. *
  254. * @v netdev Network device
  255. * @ret rc Return status code
  256. */
  257. static int snpnet_rx_filters ( struct net_device *netdev ) {
  258. struct snp_nic *snp = netdev->priv;
  259. UINT32 filters[] = {
  260. snp->snp->Mode->ReceiveFilterMask,
  261. ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
  262. EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST |
  263. EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST ),
  264. ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
  265. EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST ),
  266. ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST ),
  267. };
  268. unsigned int i;
  269. EFI_STATUS efirc;
  270. int rc;
  271. /* Try possible receive filters in turn */
  272. for ( i = 0; i < ( sizeof ( filters ) / sizeof ( filters[0] ) ); i++ ) {
  273. efirc = snp->snp->ReceiveFilters ( snp->snp, filters[i],
  274. 0, TRUE, 0, NULL );
  275. if ( efirc == 0 )
  276. return 0;
  277. rc = -EEFI ( efirc );
  278. DBGC ( snp, "SNP %s could not set receive filters %#02x (have "
  279. "%#02x): %s\n", netdev->name, filters[i],
  280. snp->snp->Mode->ReceiveFilterSetting, strerror ( rc ) );
  281. }
  282. return rc;
  283. }
  284. /**
  285. * Open network device
  286. *
  287. * @v netdev Network device
  288. * @ret rc Return status code
  289. */
  290. static int snpnet_open ( struct net_device *netdev ) {
  291. struct snp_nic *snp = netdev->priv;
  292. EFI_MAC_ADDRESS *mac = ( ( void * ) netdev->ll_addr );
  293. EFI_STATUS efirc;
  294. int rc;
  295. /* Try setting MAC address (before initialising) */
  296. if ( ( efirc = snp->snp->StationAddress ( snp->snp, FALSE, mac ) ) !=0){
  297. rc = -EEFI ( efirc );
  298. DBGC ( snp, "SNP %s could not set station address before "
  299. "initialising: %s\n", netdev->name, strerror ( rc ) );
  300. /* Ignore error */
  301. }
  302. /* Initialise NIC */
  303. if ( ( efirc = snp->snp->Initialize ( snp->snp, 0, 0 ) ) != 0 ) {
  304. rc = -EEFI ( efirc );
  305. snpnet_dump_mode ( netdev );
  306. DBGC ( snp, "SNP %s could not initialise: %s\n",
  307. netdev->name, strerror ( rc ) );
  308. return rc;
  309. }
  310. /* Try setting MAC address (after initialising) */
  311. if ( ( efirc = snp->snp->StationAddress ( snp->snp, FALSE, mac ) ) !=0){
  312. rc = -EEFI ( efirc );
  313. DBGC ( snp, "SNP %s could not set station address after "
  314. "initialising: %s\n", netdev->name, strerror ( rc ) );
  315. /* Ignore error */
  316. }
  317. /* Set receive filters */
  318. if ( ( rc = snpnet_rx_filters ( netdev ) ) != 0 ) {
  319. /* Ignore error */
  320. }
  321. /* Dump mode information (for debugging) */
  322. snpnet_dump_mode ( netdev );
  323. return 0;
  324. }
  325. /**
  326. * Close network device
  327. *
  328. * @v netdev Network device
  329. */
  330. static void snpnet_close ( struct net_device *netdev ) {
  331. struct snp_nic *snp = netdev->priv;
  332. EFI_STATUS efirc;
  333. int rc;
  334. /* Shut down NIC */
  335. if ( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) {
  336. rc = -EEFI ( efirc );
  337. DBGC ( snp, "SNP %s could not shut down: %s\n",
  338. netdev->name, strerror ( rc ) );
  339. /* Nothing we can do about this */
  340. }
  341. /* Discard transmit buffer, if applicable */
  342. if ( snp->txbuf ) {
  343. netdev_tx_complete_err ( netdev, snp->txbuf, -ECANCELED );
  344. snp->txbuf = NULL;
  345. }
  346. /* Discard receive buffer, if applicable */
  347. if ( snp->rxbuf ) {
  348. free_iob ( snp->rxbuf );
  349. snp->rxbuf = NULL;
  350. }
  351. }
  352. /** SNP network device operations */
  353. static struct net_device_operations snpnet_operations = {
  354. .open = snpnet_open,
  355. .close = snpnet_close,
  356. .transmit = snpnet_transmit,
  357. .poll = snpnet_poll,
  358. };
  359. /**
  360. * Get underlying PCI device information
  361. *
  362. * @v efidev EFI device
  363. * @v dev Generic device to fill in
  364. * @ret rc Return status code
  365. */
  366. static int snpnet_pci_info ( struct efi_device *efidev, struct device *dev ) {
  367. EFI_HANDLE device = efidev->device;
  368. EFI_HANDLE pci_device;
  369. struct pci_device pci;
  370. int rc;
  371. /* Find parent PCI device */
  372. if ( ( rc = efi_locate_device ( device, &efi_pci_io_protocol_guid,
  373. &pci_device ) ) != 0 ) {
  374. DBGC ( device, "SNP %p %s is not a PCI device: %s\n",
  375. device, efi_handle_name ( device ), strerror ( rc ) );
  376. return rc;
  377. }
  378. /* Get PCI device information */
  379. if ( ( rc = efipci_info ( pci_device, &pci ) ) != 0 ) {
  380. DBGC ( device, "SNP %p %s could not get PCI information: %s\n",
  381. device, efi_handle_name ( device ), strerror ( rc ) );
  382. return rc;
  383. }
  384. /* Populate SNP device information */
  385. memcpy ( &dev->desc, &pci.dev.desc, sizeof ( dev->desc ) );
  386. snprintf ( dev->name, sizeof ( dev->name ), "SNP-%s", pci.dev.name );
  387. return 0;
  388. }
  389. /**
  390. * Get underlying device information
  391. *
  392. * @v efidev EFI device
  393. * @v dev Generic device to fill in
  394. * @ret rc Return status code
  395. */
  396. static int snpnet_dev_info ( struct efi_device *efidev, struct device *dev ) {
  397. EFI_HANDLE device = efidev->device;
  398. int rc;
  399. /* Try getting underlying PCI device information */
  400. if ( ( rc = snpnet_pci_info ( efidev, dev ) ) == 0 )
  401. return 0;
  402. DBGC ( device, "SNP %p %s could not get underlying device "
  403. "information\n", device, efi_handle_name ( device ) );
  404. return -ENOTTY;
  405. }
  406. /**
  407. * Attach driver to device
  408. *
  409. * @v efidev EFI device
  410. * @ret rc Return status code
  411. */
  412. int snpnet_start ( struct efi_device *efidev ) {
  413. EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
  414. EFI_HANDLE device = efidev->device;
  415. EFI_SIMPLE_NETWORK_MODE *mode;
  416. struct net_device *netdev;
  417. struct snp_nic *snp;
  418. void *interface;
  419. EFI_STATUS efirc;
  420. int rc;
  421. /* Open SNP protocol */
  422. if ( ( efirc = bs->OpenProtocol ( device,
  423. &efi_simple_network_protocol_guid,
  424. &interface, efi_image_handle, device,
  425. ( EFI_OPEN_PROTOCOL_BY_DRIVER |
  426. EFI_OPEN_PROTOCOL_EXCLUSIVE )))!=0){
  427. rc = -EEFI ( efirc );
  428. DBGC ( device, "SNP %p %s cannot open SNP protocol: %s\n",
  429. device, efi_handle_name ( device ), strerror ( rc ) );
  430. DBGC_EFI_OPENERS ( device, device,
  431. &efi_simple_network_protocol_guid );
  432. goto err_open_protocol;
  433. }
  434. /* Allocate and initialise structure */
  435. netdev = alloc_etherdev ( sizeof ( *snp ) );
  436. if ( ! netdev ) {
  437. rc = -ENOMEM;
  438. goto err_alloc;
  439. }
  440. netdev_init ( netdev, &snpnet_operations );
  441. snp = netdev->priv;
  442. snp->efidev = efidev;
  443. snp->snp = interface;
  444. mode = snp->snp->Mode;
  445. efidev_set_drvdata ( efidev, netdev );
  446. /* Populate underlying device information */
  447. if ( ( rc = snpnet_dev_info ( efidev, &snp->dev ) ) != 0 )
  448. goto err_info;
  449. snp->dev.driver_name = "SNP";
  450. snp->dev.parent = &efidev->dev;
  451. list_add ( &snp->dev.siblings, &efidev->dev.children );
  452. INIT_LIST_HEAD ( &snp->dev.children );
  453. netdev->dev = &snp->dev;
  454. /* Bring to the Started state */
  455. if ( ( mode->State == EfiSimpleNetworkStopped ) &&
  456. ( ( efirc = snp->snp->Start ( snp->snp ) ) != 0 ) ) {
  457. rc = -EEFI ( efirc );
  458. DBGC ( device, "SNP %p %s could not start: %s\n", device,
  459. efi_handle_name ( device ), strerror ( rc ) );
  460. goto err_start;
  461. }
  462. if ( ( mode->State == EfiSimpleNetworkInitialized ) &&
  463. ( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) ) {
  464. rc = -EEFI ( efirc );
  465. DBGC ( device, "SNP %p %s could not shut down: %s\n", device,
  466. efi_handle_name ( device ), strerror ( rc ) );
  467. goto err_shutdown;
  468. }
  469. /* Populate network device parameters */
  470. if ( mode->HwAddressSize != netdev->ll_protocol->hw_addr_len ) {
  471. DBGC ( device, "SNP %p %s has invalid hardware address "
  472. "length %d\n", device, efi_handle_name ( device ),
  473. mode->HwAddressSize );
  474. rc = -ENOTSUP;
  475. goto err_hw_addr_len;
  476. }
  477. memcpy ( netdev->hw_addr, &mode->PermanentAddress,
  478. netdev->ll_protocol->hw_addr_len );
  479. if ( mode->HwAddressSize != netdev->ll_protocol->ll_addr_len ) {
  480. DBGC ( device, "SNP %p %s has invalid link-layer address "
  481. "length %d\n", device, efi_handle_name ( device ),
  482. mode->HwAddressSize );
  483. rc = -ENOTSUP;
  484. goto err_ll_addr_len;
  485. }
  486. memcpy ( netdev->ll_addr, &mode->CurrentAddress,
  487. netdev->ll_protocol->ll_addr_len );
  488. snp->mtu = ( snp->snp->Mode->MaxPacketSize +
  489. snp->snp->Mode->MediaHeaderSize );
  490. /* Register network device */
  491. if ( ( rc = register_netdev ( netdev ) ) != 0 )
  492. goto err_register_netdev;
  493. DBGC ( device, "SNP %p %s registered as %s\n",
  494. device, efi_handle_name ( device ), netdev->name );
  495. /* Set initial link state */
  496. if ( snp->snp->Mode->MediaPresentSupported ) {
  497. snpnet_check_link ( netdev );
  498. } else {
  499. netdev_link_up ( netdev );
  500. }
  501. return 0;
  502. unregister_netdev ( netdev );
  503. err_register_netdev:
  504. err_ll_addr_len:
  505. err_hw_addr_len:
  506. err_shutdown:
  507. err_start:
  508. list_del ( &snp->dev.siblings );
  509. err_info:
  510. netdev_nullify ( netdev );
  511. netdev_put ( netdev );
  512. err_alloc:
  513. bs->CloseProtocol ( device, &efi_simple_network_protocol_guid,
  514. efi_image_handle, device );
  515. err_open_protocol:
  516. return rc;
  517. }
  518. /**
  519. * Detach driver from device
  520. *
  521. * @v efidev EFI device
  522. */
  523. void snpnet_stop ( struct efi_device *efidev ) {
  524. EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
  525. struct net_device *netdev = efidev_get_drvdata ( efidev );
  526. struct snp_nic *snp = netdev->priv;
  527. EFI_HANDLE device = efidev->device;
  528. EFI_STATUS efirc;
  529. int rc;
  530. /* Unregister network device */
  531. unregister_netdev ( netdev );
  532. /* Stop SNP protocol */
  533. if ( ( efirc = snp->snp->Stop ( snp->snp ) ) != 0 ) {
  534. rc = -EEFI ( efirc );
  535. DBGC ( device, "SNP %p %s could not stop: %s\n", device,
  536. efi_handle_name ( device ), strerror ( rc ) );
  537. /* Nothing we can do about this */
  538. }
  539. /* Free network device */
  540. list_del ( &snp->dev.siblings );
  541. netdev_nullify ( netdev );
  542. netdev_put ( netdev );
  543. /* Close SNP protocol */
  544. bs->CloseProtocol ( device, &efi_simple_network_protocol_guid,
  545. efi_image_handle, device );
  546. }