1843e1988Sjohnlev /* 2843e1988Sjohnlev * CDDL HEADER START 3843e1988Sjohnlev * 4843e1988Sjohnlev * The contents of this file are subject to the terms of the 5843e1988Sjohnlev * Common Development and Distribution License (the "License"). 6843e1988Sjohnlev * You may not use this file except in compliance with the License. 7843e1988Sjohnlev * 8843e1988Sjohnlev * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9843e1988Sjohnlev * or http://www.opensolaris.org/os/licensing. 10843e1988Sjohnlev * See the License for the specific language governing permissions 11843e1988Sjohnlev * and limitations under the License. 12843e1988Sjohnlev * 13843e1988Sjohnlev * When distributing Covered Code, include this CDDL HEADER in each 14843e1988Sjohnlev * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15843e1988Sjohnlev * If applicable, add the following below this CDDL HEADER, with the 16843e1988Sjohnlev * fields enclosed by brackets "[]" replaced with your own identifying 17843e1988Sjohnlev * information: Portions Copyright [yyyy] [name of copyright owner] 18843e1988Sjohnlev * 19843e1988Sjohnlev * CDDL HEADER END 20843e1988Sjohnlev */ 21843e1988Sjohnlev 22843e1988Sjohnlev /* 23*0dc2366fSVenugopal Iyer * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24843e1988Sjohnlev * Use is subject to license terms. 25843e1988Sjohnlev */ 26843e1988Sjohnlev 27843e1988Sjohnlev /* 28843e1988Sjohnlev * Xen inter-domain backend - GLDv3 driver edition. 29843e1988Sjohnlev * 30843e1988Sjohnlev * A traditional GLDv3 driver used to communicate with a guest 31843e1988Sjohnlev * domain. This driver is typically plumbed underneath the IP stack 32843e1988Sjohnlev * or a software ethernet bridge. 33843e1988Sjohnlev */ 34843e1988Sjohnlev 35843e1988Sjohnlev #include "xnb.h" 36843e1988Sjohnlev 37843e1988Sjohnlev #include <sys/sunddi.h> 38843e1988Sjohnlev #include <sys/conf.h> 39843e1988Sjohnlev #include <sys/modctl.h> 40843e1988Sjohnlev #include <sys/strsubr.h> 41843e1988Sjohnlev #include <sys/dlpi.h> 42843e1988Sjohnlev #include <sys/pattr.h> 43da14cebeSEric Cheng #include <sys/mac_provider.h> 44843e1988Sjohnlev #include <sys/mac_ether.h> 45843e1988Sjohnlev #include <xen/sys/xendev.h> 4656567907SDavid Edmondson #include <sys/note.h> 47843e1988Sjohnlev 48843e1988Sjohnlev /* Required driver entry points for GLDv3 */ 49843e1988Sjohnlev static int xnbu_m_start(void *); 50843e1988Sjohnlev static void xnbu_m_stop(void *); 51843e1988Sjohnlev static int xnbu_m_set_mac_addr(void *, const uint8_t *); 52843e1988Sjohnlev static int xnbu_m_set_multicast(void *, boolean_t, const uint8_t *); 53843e1988Sjohnlev static int xnbu_m_set_promiscuous(void *, boolean_t); 54843e1988Sjohnlev static int xnbu_m_stat(void *, uint_t, uint64_t *); 55843e1988Sjohnlev static boolean_t xnbu_m_getcapab(void *, mac_capab_t, void *); 56843e1988Sjohnlev static mblk_t *xnbu_m_send(void *, mblk_t *); 57843e1988Sjohnlev 58843e1988Sjohnlev typedef struct xnbu { 59843e1988Sjohnlev mac_handle_t u_mh; 60843e1988Sjohnlev boolean_t u_need_sched; 61843e1988Sjohnlev } xnbu_t; 62843e1988Sjohnlev 6356567907SDavid Edmondson static mac_callbacks_t xnbu_callbacks = { 64da14cebeSEric Cheng MC_GETCAPAB, 65843e1988Sjohnlev xnbu_m_stat, 66843e1988Sjohnlev xnbu_m_start, 67843e1988Sjohnlev xnbu_m_stop, 68843e1988Sjohnlev xnbu_m_set_promiscuous, 69843e1988Sjohnlev xnbu_m_set_multicast, 70843e1988Sjohnlev xnbu_m_set_mac_addr, 71843e1988Sjohnlev xnbu_m_send, 72843e1988Sjohnlev NULL, 73*0dc2366fSVenugopal Iyer NULL, 74843e1988Sjohnlev xnbu_m_getcapab 75843e1988Sjohnlev }; 76843e1988Sjohnlev 77843e1988Sjohnlev static void 78843e1988Sjohnlev xnbu_to_host(xnb_t *xnbp, mblk_t *mp) 79843e1988Sjohnlev { 80551bc2a6Smrj xnbu_t *xnbup = xnbp->xnb_flavour_data; 81843e1988Sjohnlev boolean_t sched = B_FALSE; 82843e1988Sjohnlev 83843e1988Sjohnlev ASSERT(mp != NULL); 84843e1988Sjohnlev 85da14cebeSEric Cheng mac_rx(xnbup->u_mh, NULL, mp); 86843e1988Sjohnlev 87024c26efSMax zhen mutex_enter(&xnbp->xnb_rx_lock); 88843e1988Sjohnlev 89843e1988Sjohnlev /* 90843e1988Sjohnlev * If a transmit attempt failed because we ran out of ring 91843e1988Sjohnlev * space and there is now some space, re-enable the transmit 92843e1988Sjohnlev * path. 93843e1988Sjohnlev */ 94843e1988Sjohnlev if (xnbup->u_need_sched && 95551bc2a6Smrj RING_HAS_UNCONSUMED_REQUESTS(&xnbp->xnb_rx_ring)) { 96843e1988Sjohnlev sched = B_TRUE; 97843e1988Sjohnlev xnbup->u_need_sched = B_FALSE; 98843e1988Sjohnlev } 99843e1988Sjohnlev 100024c26efSMax zhen mutex_exit(&xnbp->xnb_rx_lock); 101843e1988Sjohnlev 102843e1988Sjohnlev if (sched) 103843e1988Sjohnlev mac_tx_update(xnbup->u_mh); 104843e1988Sjohnlev } 105843e1988Sjohnlev 106843e1988Sjohnlev static mblk_t * 107843e1988Sjohnlev xnbu_cksum_from_peer(xnb_t *xnbp, mblk_t *mp, uint16_t flags) 108843e1988Sjohnlev { 109843e1988Sjohnlev /* 110843e1988Sjohnlev * Take a conservative approach - if the checksum is blank 111843e1988Sjohnlev * then we fill it in. 112843e1988Sjohnlev * 113843e1988Sjohnlev * If the consumer of the packet is IP then we might actually 114843e1988Sjohnlev * only need fill it in if the data is not validated, but how 115843e1988Sjohnlev * do we know who might end up with the packet? 116843e1988Sjohnlev */ 117843e1988Sjohnlev 118843e1988Sjohnlev if ((flags & NETTXF_csum_blank) != 0) { 119843e1988Sjohnlev /* 120843e1988Sjohnlev * The checksum is blank. We must fill it in here. 121843e1988Sjohnlev */ 122843e1988Sjohnlev mp = xnb_process_cksum_flags(xnbp, mp, 0); 123843e1988Sjohnlev 124843e1988Sjohnlev /* 125843e1988Sjohnlev * Because we calculated the checksum ourselves we 126843e1988Sjohnlev * know that it must be good, so we assert this. 127843e1988Sjohnlev */ 128d4071fd3Sdme flags |= NETTXF_data_validated; 129843e1988Sjohnlev } 130843e1988Sjohnlev 131843e1988Sjohnlev if ((flags & NETTXF_data_validated) != 0) { 132843e1988Sjohnlev /* 133843e1988Sjohnlev * The checksum is asserted valid. 134843e1988Sjohnlev */ 135*0dc2366fSVenugopal Iyer mac_hcksum_set(mp, 0, 0, 0, 0, HCK_FULLCKSUM_OK); 136843e1988Sjohnlev } 137843e1988Sjohnlev 138843e1988Sjohnlev return (mp); 139843e1988Sjohnlev } 140843e1988Sjohnlev 141843e1988Sjohnlev static uint16_t 142843e1988Sjohnlev xnbu_cksum_to_peer(xnb_t *xnbp, mblk_t *mp) 143843e1988Sjohnlev { 14456567907SDavid Edmondson _NOTE(ARGUNUSED(xnbp)); 145843e1988Sjohnlev uint16_t r = 0; 146843e1988Sjohnlev uint32_t pflags; 147843e1988Sjohnlev 148*0dc2366fSVenugopal Iyer mac_hcksum_get(mp, NULL, NULL, NULL, NULL, &pflags); 149843e1988Sjohnlev 150843e1988Sjohnlev /* 151843e1988Sjohnlev * If the protocol stack has requested checksum 152843e1988Sjohnlev * offload, inform the peer that we have not 153843e1988Sjohnlev * calculated the checksum. 154843e1988Sjohnlev */ 155843e1988Sjohnlev if ((pflags & HCK_FULLCKSUM) != 0) 156843e1988Sjohnlev r |= NETRXF_csum_blank; 157843e1988Sjohnlev 158843e1988Sjohnlev return (r); 159843e1988Sjohnlev } 160843e1988Sjohnlev 16156567907SDavid Edmondson static boolean_t 16256567907SDavid Edmondson xnbu_start_connect(xnb_t *xnbp) 163843e1988Sjohnlev { 164551bc2a6Smrj xnbu_t *xnbup = xnbp->xnb_flavour_data; 165843e1988Sjohnlev 166843e1988Sjohnlev mac_link_update(xnbup->u_mh, LINK_STATE_UP); 167843e1988Sjohnlev /* 168843e1988Sjohnlev * We are able to send packets now - bring them on. 169843e1988Sjohnlev */ 170843e1988Sjohnlev mac_tx_update(xnbup->u_mh); 17156567907SDavid Edmondson 17256567907SDavid Edmondson return (B_TRUE); 17356567907SDavid Edmondson } 17456567907SDavid Edmondson 17556567907SDavid Edmondson static boolean_t 17656567907SDavid Edmondson xnbu_peer_connected(xnb_t *xnbp) 17756567907SDavid Edmondson { 17856567907SDavid Edmondson _NOTE(ARGUNUSED(xnbp)); 17956567907SDavid Edmondson 18056567907SDavid Edmondson return (B_TRUE); 181843e1988Sjohnlev } 182843e1988Sjohnlev 183843e1988Sjohnlev static void 18456567907SDavid Edmondson xnbu_peer_disconnected(xnb_t *xnbp) 185843e1988Sjohnlev { 186551bc2a6Smrj xnbu_t *xnbup = xnbp->xnb_flavour_data; 187843e1988Sjohnlev 188843e1988Sjohnlev mac_link_update(xnbup->u_mh, LINK_STATE_DOWN); 189843e1988Sjohnlev } 190843e1988Sjohnlev 191843e1988Sjohnlev /*ARGSUSED*/ 192843e1988Sjohnlev static boolean_t 19356567907SDavid Edmondson xnbu_hotplug_connected(xnb_t *xnbp) 194843e1988Sjohnlev { 195843e1988Sjohnlev return (B_TRUE); 196843e1988Sjohnlev } 197843e1988Sjohnlev 198843e1988Sjohnlev static mblk_t * 199843e1988Sjohnlev xnbu_m_send(void *arg, mblk_t *mp) 200843e1988Sjohnlev { 201843e1988Sjohnlev xnb_t *xnbp = arg; 202551bc2a6Smrj xnbu_t *xnbup = xnbp->xnb_flavour_data; 20356567907SDavid Edmondson boolean_t sched = B_FALSE; 204843e1988Sjohnlev 205551bc2a6Smrj mp = xnb_copy_to_peer(arg, mp); 206843e1988Sjohnlev 20756567907SDavid Edmondson mutex_enter(&xnbp->xnb_rx_lock); 208843e1988Sjohnlev /* 209843e1988Sjohnlev * If we consumed all of the mblk_t's offered, perhaps we need 210843e1988Sjohnlev * to indicate that we can accept more. Otherwise we are full 211843e1988Sjohnlev * and need to wait for space. 212843e1988Sjohnlev */ 213843e1988Sjohnlev if (mp == NULL) { 21456567907SDavid Edmondson sched = xnbup->u_need_sched; 21556567907SDavid Edmondson xnbup->u_need_sched = B_FALSE; 21656567907SDavid Edmondson } else { 21756567907SDavid Edmondson xnbup->u_need_sched = B_TRUE; 21856567907SDavid Edmondson } 21956567907SDavid Edmondson mutex_exit(&xnbp->xnb_rx_lock); 22056567907SDavid Edmondson 221843e1988Sjohnlev /* 222843e1988Sjohnlev * If a previous transmit attempt failed because the ring 223843e1988Sjohnlev * was full, try again now. 224843e1988Sjohnlev */ 22556567907SDavid Edmondson if (sched) 226843e1988Sjohnlev mac_tx_update(xnbup->u_mh); 227843e1988Sjohnlev 228843e1988Sjohnlev return (mp); 229843e1988Sjohnlev } 230843e1988Sjohnlev 231843e1988Sjohnlev /* 232843e1988Sjohnlev * xnbu_m_set_mac_addr() -- set the physical network address on the board 233843e1988Sjohnlev */ 234843e1988Sjohnlev /* ARGSUSED */ 235843e1988Sjohnlev static int 236843e1988Sjohnlev xnbu_m_set_mac_addr(void *arg, const uint8_t *macaddr) 237843e1988Sjohnlev { 238843e1988Sjohnlev xnb_t *xnbp = arg; 239551bc2a6Smrj xnbu_t *xnbup = xnbp->xnb_flavour_data; 240843e1988Sjohnlev 241551bc2a6Smrj bcopy(macaddr, xnbp->xnb_mac_addr, ETHERADDRL); 242551bc2a6Smrj mac_unicst_update(xnbup->u_mh, xnbp->xnb_mac_addr); 243843e1988Sjohnlev 244843e1988Sjohnlev return (0); 245843e1988Sjohnlev } 246843e1988Sjohnlev 247843e1988Sjohnlev /* 248843e1988Sjohnlev * xnbu_m_set_multicast() -- set (enable) or disable a multicast address 249843e1988Sjohnlev */ 250843e1988Sjohnlev /*ARGSUSED*/ 251843e1988Sjohnlev static int 252843e1988Sjohnlev xnbu_m_set_multicast(void *arg, boolean_t add, const uint8_t *mca) 253843e1988Sjohnlev { 254843e1988Sjohnlev /* 255843e1988Sjohnlev * We always accept all packets from the peer, so nothing to 256843e1988Sjohnlev * do for enable or disable. 257843e1988Sjohnlev */ 258843e1988Sjohnlev return (0); 259843e1988Sjohnlev } 260843e1988Sjohnlev 261843e1988Sjohnlev 262843e1988Sjohnlev /* 263843e1988Sjohnlev * xnbu_m_set_promiscuous() -- set or reset promiscuous mode on the board 264843e1988Sjohnlev */ 265843e1988Sjohnlev /* ARGSUSED */ 266843e1988Sjohnlev static int 267843e1988Sjohnlev xnbu_m_set_promiscuous(void *arg, boolean_t on) 268843e1988Sjohnlev { 269843e1988Sjohnlev /* 270843e1988Sjohnlev * We always accept all packets from the peer, so nothing to 271843e1988Sjohnlev * do for enable or disable. 272843e1988Sjohnlev */ 273843e1988Sjohnlev return (0); 274843e1988Sjohnlev } 275843e1988Sjohnlev 276843e1988Sjohnlev /* 277843e1988Sjohnlev * xnbu_m_start() -- start the board receiving and enable interrupts. 278843e1988Sjohnlev */ 279843e1988Sjohnlev /*ARGSUSED*/ 280843e1988Sjohnlev static int 281843e1988Sjohnlev xnbu_m_start(void *arg) 282843e1988Sjohnlev { 283843e1988Sjohnlev return (0); 284843e1988Sjohnlev } 285843e1988Sjohnlev 286843e1988Sjohnlev /* 287843e1988Sjohnlev * xnbu_m_stop() - disable hardware 288843e1988Sjohnlev */ 289843e1988Sjohnlev /*ARGSUSED*/ 290843e1988Sjohnlev static void 291843e1988Sjohnlev xnbu_m_stop(void *arg) 292843e1988Sjohnlev { 293843e1988Sjohnlev } 294843e1988Sjohnlev 295843e1988Sjohnlev static int 296843e1988Sjohnlev xnbu_m_stat(void *arg, uint_t stat, uint64_t *val) 297843e1988Sjohnlev { 298843e1988Sjohnlev xnb_t *xnbp = arg; 299843e1988Sjohnlev 300551bc2a6Smrj mutex_enter(&xnbp->xnb_tx_lock); 301551bc2a6Smrj mutex_enter(&xnbp->xnb_rx_lock); 302843e1988Sjohnlev 303843e1988Sjohnlev #define map_stat(q, r) \ 304843e1988Sjohnlev case (MAC_STAT_##q): \ 305551bc2a6Smrj *val = xnbp->xnb_stat_##r; \ 306843e1988Sjohnlev break 307843e1988Sjohnlev 308843e1988Sjohnlev switch (stat) { 309843e1988Sjohnlev 310024c26efSMax zhen map_stat(IPACKETS, opackets); 311024c26efSMax zhen map_stat(OPACKETS, ipackets); 312024c26efSMax zhen map_stat(RBYTES, obytes); 313024c26efSMax zhen map_stat(OBYTES, rbytes); 314843e1988Sjohnlev 315843e1988Sjohnlev default: 316551bc2a6Smrj mutex_exit(&xnbp->xnb_rx_lock); 317551bc2a6Smrj mutex_exit(&xnbp->xnb_tx_lock); 318843e1988Sjohnlev 319843e1988Sjohnlev return (ENOTSUP); 320843e1988Sjohnlev } 321843e1988Sjohnlev 322843e1988Sjohnlev #undef map_stat 323843e1988Sjohnlev 324551bc2a6Smrj mutex_exit(&xnbp->xnb_rx_lock); 325551bc2a6Smrj mutex_exit(&xnbp->xnb_tx_lock); 326843e1988Sjohnlev 327843e1988Sjohnlev return (0); 328843e1988Sjohnlev } 329843e1988Sjohnlev 330843e1988Sjohnlev static boolean_t 331843e1988Sjohnlev xnbu_m_getcapab(void *arg, mac_capab_t cap, void *cap_data) 332843e1988Sjohnlev { 33356567907SDavid Edmondson _NOTE(ARGUNUSED(arg)); 334843e1988Sjohnlev 335843e1988Sjohnlev switch (cap) { 336843e1988Sjohnlev case MAC_CAPAB_HCKSUM: { 337843e1988Sjohnlev uint32_t *capab = cap_data; 338843e1988Sjohnlev 339568a765bSdme *capab = HCKSUM_INET_PARTIAL; 340843e1988Sjohnlev break; 341843e1988Sjohnlev } 342843e1988Sjohnlev default: 343843e1988Sjohnlev return (B_FALSE); 344843e1988Sjohnlev } 345843e1988Sjohnlev 346843e1988Sjohnlev return (B_TRUE); 347843e1988Sjohnlev } 348843e1988Sjohnlev 34956567907SDavid Edmondson /* 35056567907SDavid Edmondson * All packets are passed to the peer, so adding and removing 35156567907SDavid Edmondson * multicast addresses is meaningless. 35256567907SDavid Edmondson */ 35356567907SDavid Edmondson static boolean_t 35456567907SDavid Edmondson xnbu_mcast_add(xnb_t *xnbp, ether_addr_t *addr) 35556567907SDavid Edmondson { 35656567907SDavid Edmondson _NOTE(ARGUNUSED(xnbp, addr)); 35756567907SDavid Edmondson 35856567907SDavid Edmondson return (B_TRUE); 35956567907SDavid Edmondson } 36056567907SDavid Edmondson 36156567907SDavid Edmondson static boolean_t 36256567907SDavid Edmondson xnbu_mcast_del(xnb_t *xnbp, ether_addr_t *addr) 36356567907SDavid Edmondson { 36456567907SDavid Edmondson _NOTE(ARGUNUSED(xnbp, addr)); 36556567907SDavid Edmondson 36656567907SDavid Edmondson return (B_TRUE); 36756567907SDavid Edmondson } 36856567907SDavid Edmondson 369843e1988Sjohnlev static int 370843e1988Sjohnlev xnbu_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 371843e1988Sjohnlev { 372843e1988Sjohnlev static xnb_flavour_t flavour = { 37356567907SDavid Edmondson xnbu_to_host, xnbu_peer_connected, xnbu_peer_disconnected, 37456567907SDavid Edmondson xnbu_hotplug_connected, xnbu_start_connect, 375843e1988Sjohnlev xnbu_cksum_from_peer, xnbu_cksum_to_peer, 37656567907SDavid Edmondson xnbu_mcast_add, xnbu_mcast_del, 377843e1988Sjohnlev }; 378843e1988Sjohnlev xnbu_t *xnbup; 379843e1988Sjohnlev xnb_t *xnbp; 380843e1988Sjohnlev mac_register_t *mr; 381843e1988Sjohnlev int err; 382843e1988Sjohnlev 383843e1988Sjohnlev switch (cmd) { 384843e1988Sjohnlev case DDI_ATTACH: 385843e1988Sjohnlev break; 386843e1988Sjohnlev case DDI_RESUME: 387843e1988Sjohnlev return (DDI_SUCCESS); 388843e1988Sjohnlev default: 389843e1988Sjohnlev return (DDI_FAILURE); 390843e1988Sjohnlev } 391843e1988Sjohnlev 392843e1988Sjohnlev xnbup = kmem_zalloc(sizeof (*xnbup), KM_SLEEP); 393843e1988Sjohnlev 394843e1988Sjohnlev if ((mr = mac_alloc(MAC_VERSION)) == NULL) { 395843e1988Sjohnlev kmem_free(xnbup, sizeof (*xnbup)); 396843e1988Sjohnlev return (DDI_FAILURE); 397843e1988Sjohnlev } 398843e1988Sjohnlev 399843e1988Sjohnlev if (xnb_attach(dip, &flavour, xnbup) != DDI_SUCCESS) { 400843e1988Sjohnlev mac_free(mr); 401843e1988Sjohnlev kmem_free(xnbup, sizeof (*xnbup)); 402843e1988Sjohnlev return (DDI_FAILURE); 403843e1988Sjohnlev } 404843e1988Sjohnlev 405843e1988Sjohnlev xnbp = ddi_get_driver_private(dip); 406843e1988Sjohnlev ASSERT(xnbp != NULL); 407843e1988Sjohnlev 408843e1988Sjohnlev mr->m_dip = dip; 409843e1988Sjohnlev mr->m_driver = xnbp; 410843e1988Sjohnlev 411843e1988Sjohnlev /* 412843e1988Sjohnlev * Initialize pointers to device specific functions which will be 413843e1988Sjohnlev * used by the generic layer. 414843e1988Sjohnlev */ 415843e1988Sjohnlev mr->m_type_ident = MAC_PLUGIN_IDENT_ETHER; 416551bc2a6Smrj mr->m_src_addr = xnbp->xnb_mac_addr; 41756567907SDavid Edmondson mr->m_callbacks = &xnbu_callbacks; 418843e1988Sjohnlev mr->m_min_sdu = 0; 419843e1988Sjohnlev mr->m_max_sdu = XNBMAXPKT; 420d62bc4baSyz147064 /* 421d62bc4baSyz147064 * xnbu is a virtual device, and it is not associated with any 422d62bc4baSyz147064 * physical device. Its margin size is determined by the maximum 423d62bc4baSyz147064 * packet size it can handle, which is PAGESIZE. 424d62bc4baSyz147064 */ 425d62bc4baSyz147064 mr->m_margin = PAGESIZE - XNBMAXPKT - sizeof (struct ether_header); 426843e1988Sjohnlev 427551bc2a6Smrj (void) memset(xnbp->xnb_mac_addr, 0xff, ETHERADDRL); 428551bc2a6Smrj xnbp->xnb_mac_addr[0] &= 0xfe; 429843e1988Sjohnlev xnbup->u_need_sched = B_FALSE; 430843e1988Sjohnlev 431843e1988Sjohnlev /* 432843e1988Sjohnlev * Register ourselves with the GLDv3 interface. 433843e1988Sjohnlev */ 434843e1988Sjohnlev err = mac_register(mr, &xnbup->u_mh); 435843e1988Sjohnlev mac_free(mr); 436843e1988Sjohnlev if (err != 0) { 437843e1988Sjohnlev xnb_detach(dip); 438843e1988Sjohnlev kmem_free(xnbup, sizeof (*xnbup)); 439843e1988Sjohnlev return (DDI_FAILURE); 440843e1988Sjohnlev } 441843e1988Sjohnlev 442843e1988Sjohnlev mac_link_update(xnbup->u_mh, LINK_STATE_DOWN); 443843e1988Sjohnlev 444843e1988Sjohnlev return (DDI_SUCCESS); 445843e1988Sjohnlev } 446843e1988Sjohnlev 447843e1988Sjohnlev /*ARGSUSED*/ 448843e1988Sjohnlev int 449843e1988Sjohnlev xnbu_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 450843e1988Sjohnlev { 451843e1988Sjohnlev xnb_t *xnbp = ddi_get_driver_private(dip); 452551bc2a6Smrj xnbu_t *xnbup = xnbp->xnb_flavour_data; 453843e1988Sjohnlev 454843e1988Sjohnlev switch (cmd) { 455843e1988Sjohnlev case DDI_DETACH: 456843e1988Sjohnlev break; 457843e1988Sjohnlev case DDI_SUSPEND: 458843e1988Sjohnlev return (DDI_SUCCESS); 459843e1988Sjohnlev default: 460843e1988Sjohnlev return (DDI_FAILURE); 461843e1988Sjohnlev } 462843e1988Sjohnlev 463843e1988Sjohnlev ASSERT(xnbp != NULL); 464843e1988Sjohnlev ASSERT(xnbup != NULL); 465843e1988Sjohnlev 466551bc2a6Smrj mutex_enter(&xnbp->xnb_tx_lock); 467551bc2a6Smrj mutex_enter(&xnbp->xnb_rx_lock); 468843e1988Sjohnlev 469551bc2a6Smrj if (!xnbp->xnb_detachable || xnbp->xnb_connected || 470024c26efSMax zhen (xnbp->xnb_tx_buf_count > 0)) { 471551bc2a6Smrj mutex_exit(&xnbp->xnb_rx_lock); 472551bc2a6Smrj mutex_exit(&xnbp->xnb_tx_lock); 473843e1988Sjohnlev 474843e1988Sjohnlev return (DDI_FAILURE); 475843e1988Sjohnlev } 476843e1988Sjohnlev 477551bc2a6Smrj mutex_exit(&xnbp->xnb_rx_lock); 478551bc2a6Smrj mutex_exit(&xnbp->xnb_tx_lock); 479843e1988Sjohnlev 480843e1988Sjohnlev /* 481843e1988Sjohnlev * Attempt to unregister the mac. 482843e1988Sjohnlev */ 483843e1988Sjohnlev if ((xnbup->u_mh != NULL) && (mac_unregister(xnbup->u_mh) != 0)) 484843e1988Sjohnlev return (DDI_FAILURE); 485843e1988Sjohnlev kmem_free(xnbup, sizeof (*xnbup)); 486843e1988Sjohnlev 487843e1988Sjohnlev xnb_detach(dip); 488843e1988Sjohnlev 489843e1988Sjohnlev return (DDI_SUCCESS); 490843e1988Sjohnlev } 491843e1988Sjohnlev 492843e1988Sjohnlev DDI_DEFINE_STREAM_OPS(ops, nulldev, nulldev, xnbu_attach, xnbu_detach, 49319397407SSherry Moore nodev, NULL, D_MP, NULL, ddi_quiesce_not_supported); 494843e1988Sjohnlev 495843e1988Sjohnlev static struct modldrv modldrv = { 496024c26efSMax zhen &mod_driverops, "xnbu driver", &ops 497843e1988Sjohnlev }; 498843e1988Sjohnlev 499843e1988Sjohnlev static struct modlinkage modlinkage = { 500843e1988Sjohnlev MODREV_1, &modldrv, NULL 501843e1988Sjohnlev }; 502843e1988Sjohnlev 503843e1988Sjohnlev int 504843e1988Sjohnlev _init(void) 505843e1988Sjohnlev { 506843e1988Sjohnlev int i; 507843e1988Sjohnlev 508843e1988Sjohnlev mac_init_ops(&ops, "xnbu"); 509843e1988Sjohnlev 510843e1988Sjohnlev i = mod_install(&modlinkage); 511843e1988Sjohnlev if (i != DDI_SUCCESS) 512843e1988Sjohnlev mac_fini_ops(&ops); 513843e1988Sjohnlev 514843e1988Sjohnlev return (i); 515843e1988Sjohnlev } 516843e1988Sjohnlev 517843e1988Sjohnlev int 518843e1988Sjohnlev _fini(void) 519843e1988Sjohnlev { 520843e1988Sjohnlev int i; 521843e1988Sjohnlev 522843e1988Sjohnlev i = mod_remove(&modlinkage); 523843e1988Sjohnlev if (i == DDI_SUCCESS) 524843e1988Sjohnlev mac_fini_ops(&ops); 525843e1988Sjohnlev 526843e1988Sjohnlev return (i); 527843e1988Sjohnlev } 528843e1988Sjohnlev 529843e1988Sjohnlev int 530843e1988Sjohnlev _info(struct modinfo *modinfop) 531843e1988Sjohnlev { 532843e1988Sjohnlev return (mod_info(&modlinkage, modinfop)); 533843e1988Sjohnlev } 534