1*0791c032SJeremy Kerr // SPDX-License-Identifier: GPL-2.0 2*0791c032SJeremy Kerr /* 3*0791c032SJeremy Kerr * mctp-usb.c - MCTP-over-USB (DMTF DSP0283) transport binding driver. 4*0791c032SJeremy Kerr * 5*0791c032SJeremy Kerr * DSP0283 is available at: 6*0791c032SJeremy Kerr * https://www.dmtf.org/sites/default/files/standards/documents/DSP0283_1.0.1.pdf 7*0791c032SJeremy Kerr * 8*0791c032SJeremy Kerr * Copyright (C) 2024-2025 Code Construct Pty Ltd 9*0791c032SJeremy Kerr */ 10*0791c032SJeremy Kerr 11*0791c032SJeremy Kerr #include <linux/module.h> 12*0791c032SJeremy Kerr #include <linux/netdevice.h> 13*0791c032SJeremy Kerr #include <linux/usb.h> 14*0791c032SJeremy Kerr #include <linux/usb/mctp-usb.h> 15*0791c032SJeremy Kerr 16*0791c032SJeremy Kerr #include <net/mctp.h> 17*0791c032SJeremy Kerr #include <net/mctpdevice.h> 18*0791c032SJeremy Kerr #include <net/pkt_sched.h> 19*0791c032SJeremy Kerr 20*0791c032SJeremy Kerr #include <uapi/linux/if_arp.h> 21*0791c032SJeremy Kerr 22*0791c032SJeremy Kerr struct mctp_usb { 23*0791c032SJeremy Kerr struct usb_device *usbdev; 24*0791c032SJeremy Kerr struct usb_interface *intf; 25*0791c032SJeremy Kerr bool stopped; 26*0791c032SJeremy Kerr 27*0791c032SJeremy Kerr struct net_device *netdev; 28*0791c032SJeremy Kerr 29*0791c032SJeremy Kerr u8 ep_in; 30*0791c032SJeremy Kerr u8 ep_out; 31*0791c032SJeremy Kerr 32*0791c032SJeremy Kerr struct urb *tx_urb; 33*0791c032SJeremy Kerr struct urb *rx_urb; 34*0791c032SJeremy Kerr 35*0791c032SJeremy Kerr struct delayed_work rx_retry_work; 36*0791c032SJeremy Kerr }; 37*0791c032SJeremy Kerr 38*0791c032SJeremy Kerr static void mctp_usb_out_complete(struct urb *urb) 39*0791c032SJeremy Kerr { 40*0791c032SJeremy Kerr struct sk_buff *skb = urb->context; 41*0791c032SJeremy Kerr struct net_device *netdev = skb->dev; 42*0791c032SJeremy Kerr int status; 43*0791c032SJeremy Kerr 44*0791c032SJeremy Kerr status = urb->status; 45*0791c032SJeremy Kerr 46*0791c032SJeremy Kerr switch (status) { 47*0791c032SJeremy Kerr case -ENOENT: 48*0791c032SJeremy Kerr case -ECONNRESET: 49*0791c032SJeremy Kerr case -ESHUTDOWN: 50*0791c032SJeremy Kerr case -EPROTO: 51*0791c032SJeremy Kerr dev_dstats_tx_dropped(netdev); 52*0791c032SJeremy Kerr break; 53*0791c032SJeremy Kerr case 0: 54*0791c032SJeremy Kerr dev_dstats_tx_add(netdev, skb->len); 55*0791c032SJeremy Kerr netif_wake_queue(netdev); 56*0791c032SJeremy Kerr consume_skb(skb); 57*0791c032SJeremy Kerr return; 58*0791c032SJeremy Kerr default: 59*0791c032SJeremy Kerr netdev_dbg(netdev, "unexpected tx urb status: %d\n", status); 60*0791c032SJeremy Kerr dev_dstats_tx_dropped(netdev); 61*0791c032SJeremy Kerr } 62*0791c032SJeremy Kerr 63*0791c032SJeremy Kerr kfree_skb(skb); 64*0791c032SJeremy Kerr } 65*0791c032SJeremy Kerr 66*0791c032SJeremy Kerr static netdev_tx_t mctp_usb_start_xmit(struct sk_buff *skb, 67*0791c032SJeremy Kerr struct net_device *dev) 68*0791c032SJeremy Kerr { 69*0791c032SJeremy Kerr struct mctp_usb *mctp_usb = netdev_priv(dev); 70*0791c032SJeremy Kerr struct mctp_usb_hdr *hdr; 71*0791c032SJeremy Kerr unsigned int plen; 72*0791c032SJeremy Kerr struct urb *urb; 73*0791c032SJeremy Kerr int rc; 74*0791c032SJeremy Kerr 75*0791c032SJeremy Kerr plen = skb->len; 76*0791c032SJeremy Kerr 77*0791c032SJeremy Kerr if (plen + sizeof(*hdr) > MCTP_USB_XFER_SIZE) 78*0791c032SJeremy Kerr goto err_drop; 79*0791c032SJeremy Kerr 80*0791c032SJeremy Kerr rc = skb_cow_head(skb, sizeof(*hdr)); 81*0791c032SJeremy Kerr if (rc) 82*0791c032SJeremy Kerr goto err_drop; 83*0791c032SJeremy Kerr 84*0791c032SJeremy Kerr hdr = skb_push(skb, sizeof(*hdr)); 85*0791c032SJeremy Kerr if (!hdr) 86*0791c032SJeremy Kerr goto err_drop; 87*0791c032SJeremy Kerr 88*0791c032SJeremy Kerr hdr->id = cpu_to_be16(MCTP_USB_DMTF_ID); 89*0791c032SJeremy Kerr hdr->rsvd = 0; 90*0791c032SJeremy Kerr hdr->len = plen + sizeof(*hdr); 91*0791c032SJeremy Kerr 92*0791c032SJeremy Kerr urb = mctp_usb->tx_urb; 93*0791c032SJeremy Kerr 94*0791c032SJeremy Kerr usb_fill_bulk_urb(urb, mctp_usb->usbdev, 95*0791c032SJeremy Kerr usb_sndbulkpipe(mctp_usb->usbdev, mctp_usb->ep_out), 96*0791c032SJeremy Kerr skb->data, skb->len, 97*0791c032SJeremy Kerr mctp_usb_out_complete, skb); 98*0791c032SJeremy Kerr 99*0791c032SJeremy Kerr rc = usb_submit_urb(urb, GFP_ATOMIC); 100*0791c032SJeremy Kerr if (rc) 101*0791c032SJeremy Kerr goto err_drop; 102*0791c032SJeremy Kerr else 103*0791c032SJeremy Kerr netif_stop_queue(dev); 104*0791c032SJeremy Kerr 105*0791c032SJeremy Kerr return NETDEV_TX_OK; 106*0791c032SJeremy Kerr 107*0791c032SJeremy Kerr err_drop: 108*0791c032SJeremy Kerr dev_dstats_tx_dropped(dev); 109*0791c032SJeremy Kerr kfree_skb(skb); 110*0791c032SJeremy Kerr return NETDEV_TX_OK; 111*0791c032SJeremy Kerr } 112*0791c032SJeremy Kerr 113*0791c032SJeremy Kerr static void mctp_usb_in_complete(struct urb *urb); 114*0791c032SJeremy Kerr 115*0791c032SJeremy Kerr /* If we fail to queue an in urb atomically (either due to skb allocation or 116*0791c032SJeremy Kerr * urb submission), we will schedule a rx queue in nonatomic context 117*0791c032SJeremy Kerr * after a delay, specified in jiffies 118*0791c032SJeremy Kerr */ 119*0791c032SJeremy Kerr static const unsigned long RX_RETRY_DELAY = HZ / 4; 120*0791c032SJeremy Kerr 121*0791c032SJeremy Kerr static int mctp_usb_rx_queue(struct mctp_usb *mctp_usb, gfp_t gfp) 122*0791c032SJeremy Kerr { 123*0791c032SJeremy Kerr struct sk_buff *skb; 124*0791c032SJeremy Kerr int rc; 125*0791c032SJeremy Kerr 126*0791c032SJeremy Kerr skb = __netdev_alloc_skb(mctp_usb->netdev, MCTP_USB_XFER_SIZE, gfp); 127*0791c032SJeremy Kerr if (!skb) { 128*0791c032SJeremy Kerr rc = -ENOMEM; 129*0791c032SJeremy Kerr goto err_retry; 130*0791c032SJeremy Kerr } 131*0791c032SJeremy Kerr 132*0791c032SJeremy Kerr usb_fill_bulk_urb(mctp_usb->rx_urb, mctp_usb->usbdev, 133*0791c032SJeremy Kerr usb_rcvbulkpipe(mctp_usb->usbdev, mctp_usb->ep_in), 134*0791c032SJeremy Kerr skb->data, MCTP_USB_XFER_SIZE, 135*0791c032SJeremy Kerr mctp_usb_in_complete, skb); 136*0791c032SJeremy Kerr 137*0791c032SJeremy Kerr rc = usb_submit_urb(mctp_usb->rx_urb, gfp); 138*0791c032SJeremy Kerr if (rc) { 139*0791c032SJeremy Kerr netdev_dbg(mctp_usb->netdev, "rx urb submit failure: %d\n", rc); 140*0791c032SJeremy Kerr kfree_skb(skb); 141*0791c032SJeremy Kerr if (rc == -ENOMEM) 142*0791c032SJeremy Kerr goto err_retry; 143*0791c032SJeremy Kerr } 144*0791c032SJeremy Kerr 145*0791c032SJeremy Kerr return rc; 146*0791c032SJeremy Kerr 147*0791c032SJeremy Kerr err_retry: 148*0791c032SJeremy Kerr schedule_delayed_work(&mctp_usb->rx_retry_work, RX_RETRY_DELAY); 149*0791c032SJeremy Kerr return rc; 150*0791c032SJeremy Kerr } 151*0791c032SJeremy Kerr 152*0791c032SJeremy Kerr static void mctp_usb_in_complete(struct urb *urb) 153*0791c032SJeremy Kerr { 154*0791c032SJeremy Kerr struct sk_buff *skb = urb->context; 155*0791c032SJeremy Kerr struct net_device *netdev = skb->dev; 156*0791c032SJeremy Kerr struct mctp_usb *mctp_usb = netdev_priv(netdev); 157*0791c032SJeremy Kerr struct mctp_skb_cb *cb; 158*0791c032SJeremy Kerr unsigned int len; 159*0791c032SJeremy Kerr int status; 160*0791c032SJeremy Kerr 161*0791c032SJeremy Kerr status = urb->status; 162*0791c032SJeremy Kerr 163*0791c032SJeremy Kerr switch (status) { 164*0791c032SJeremy Kerr case -ENOENT: 165*0791c032SJeremy Kerr case -ECONNRESET: 166*0791c032SJeremy Kerr case -ESHUTDOWN: 167*0791c032SJeremy Kerr case -EPROTO: 168*0791c032SJeremy Kerr kfree_skb(skb); 169*0791c032SJeremy Kerr return; 170*0791c032SJeremy Kerr case 0: 171*0791c032SJeremy Kerr break; 172*0791c032SJeremy Kerr default: 173*0791c032SJeremy Kerr netdev_dbg(netdev, "unexpected rx urb status: %d\n", status); 174*0791c032SJeremy Kerr kfree_skb(skb); 175*0791c032SJeremy Kerr return; 176*0791c032SJeremy Kerr } 177*0791c032SJeremy Kerr 178*0791c032SJeremy Kerr len = urb->actual_length; 179*0791c032SJeremy Kerr __skb_put(skb, len); 180*0791c032SJeremy Kerr 181*0791c032SJeremy Kerr while (skb) { 182*0791c032SJeremy Kerr struct sk_buff *skb2 = NULL; 183*0791c032SJeremy Kerr struct mctp_usb_hdr *hdr; 184*0791c032SJeremy Kerr u8 pkt_len; /* length of MCTP packet, no USB header */ 185*0791c032SJeremy Kerr 186*0791c032SJeremy Kerr hdr = skb_pull_data(skb, sizeof(*hdr)); 187*0791c032SJeremy Kerr if (!hdr) 188*0791c032SJeremy Kerr break; 189*0791c032SJeremy Kerr 190*0791c032SJeremy Kerr if (be16_to_cpu(hdr->id) != MCTP_USB_DMTF_ID) { 191*0791c032SJeremy Kerr netdev_dbg(netdev, "rx: invalid id %04x\n", 192*0791c032SJeremy Kerr be16_to_cpu(hdr->id)); 193*0791c032SJeremy Kerr break; 194*0791c032SJeremy Kerr } 195*0791c032SJeremy Kerr 196*0791c032SJeremy Kerr if (hdr->len < 197*0791c032SJeremy Kerr sizeof(struct mctp_hdr) + sizeof(struct mctp_usb_hdr)) { 198*0791c032SJeremy Kerr netdev_dbg(netdev, "rx: short packet (hdr) %d\n", 199*0791c032SJeremy Kerr hdr->len); 200*0791c032SJeremy Kerr break; 201*0791c032SJeremy Kerr } 202*0791c032SJeremy Kerr 203*0791c032SJeremy Kerr /* we know we have at least sizeof(struct mctp_usb_hdr) here */ 204*0791c032SJeremy Kerr pkt_len = hdr->len - sizeof(struct mctp_usb_hdr); 205*0791c032SJeremy Kerr if (pkt_len > skb->len) { 206*0791c032SJeremy Kerr netdev_dbg(netdev, 207*0791c032SJeremy Kerr "rx: short packet (xfer) %d, actual %d\n", 208*0791c032SJeremy Kerr hdr->len, skb->len); 209*0791c032SJeremy Kerr break; 210*0791c032SJeremy Kerr } 211*0791c032SJeremy Kerr 212*0791c032SJeremy Kerr if (pkt_len < skb->len) { 213*0791c032SJeremy Kerr /* more packets may follow - clone to a new 214*0791c032SJeremy Kerr * skb to use on the next iteration 215*0791c032SJeremy Kerr */ 216*0791c032SJeremy Kerr skb2 = skb_clone(skb, GFP_ATOMIC); 217*0791c032SJeremy Kerr if (skb2) { 218*0791c032SJeremy Kerr if (!skb_pull(skb2, pkt_len)) { 219*0791c032SJeremy Kerr kfree_skb(skb2); 220*0791c032SJeremy Kerr skb2 = NULL; 221*0791c032SJeremy Kerr } 222*0791c032SJeremy Kerr } 223*0791c032SJeremy Kerr skb_trim(skb, pkt_len); 224*0791c032SJeremy Kerr } 225*0791c032SJeremy Kerr 226*0791c032SJeremy Kerr dev_dstats_rx_add(netdev, skb->len); 227*0791c032SJeremy Kerr 228*0791c032SJeremy Kerr skb->protocol = htons(ETH_P_MCTP); 229*0791c032SJeremy Kerr skb_reset_network_header(skb); 230*0791c032SJeremy Kerr cb = __mctp_cb(skb); 231*0791c032SJeremy Kerr cb->halen = 0; 232*0791c032SJeremy Kerr netif_rx(skb); 233*0791c032SJeremy Kerr 234*0791c032SJeremy Kerr skb = skb2; 235*0791c032SJeremy Kerr } 236*0791c032SJeremy Kerr 237*0791c032SJeremy Kerr if (skb) 238*0791c032SJeremy Kerr kfree_skb(skb); 239*0791c032SJeremy Kerr 240*0791c032SJeremy Kerr mctp_usb_rx_queue(mctp_usb, GFP_ATOMIC); 241*0791c032SJeremy Kerr } 242*0791c032SJeremy Kerr 243*0791c032SJeremy Kerr static void mctp_usb_rx_retry_work(struct work_struct *work) 244*0791c032SJeremy Kerr { 245*0791c032SJeremy Kerr struct mctp_usb *mctp_usb = container_of(work, struct mctp_usb, 246*0791c032SJeremy Kerr rx_retry_work.work); 247*0791c032SJeremy Kerr 248*0791c032SJeremy Kerr if (READ_ONCE(mctp_usb->stopped)) 249*0791c032SJeremy Kerr return; 250*0791c032SJeremy Kerr 251*0791c032SJeremy Kerr mctp_usb_rx_queue(mctp_usb, GFP_KERNEL); 252*0791c032SJeremy Kerr } 253*0791c032SJeremy Kerr 254*0791c032SJeremy Kerr static int mctp_usb_open(struct net_device *dev) 255*0791c032SJeremy Kerr { 256*0791c032SJeremy Kerr struct mctp_usb *mctp_usb = netdev_priv(dev); 257*0791c032SJeremy Kerr 258*0791c032SJeremy Kerr WRITE_ONCE(mctp_usb->stopped, false); 259*0791c032SJeremy Kerr 260*0791c032SJeremy Kerr return mctp_usb_rx_queue(mctp_usb, GFP_KERNEL); 261*0791c032SJeremy Kerr } 262*0791c032SJeremy Kerr 263*0791c032SJeremy Kerr static int mctp_usb_stop(struct net_device *dev) 264*0791c032SJeremy Kerr { 265*0791c032SJeremy Kerr struct mctp_usb *mctp_usb = netdev_priv(dev); 266*0791c032SJeremy Kerr 267*0791c032SJeremy Kerr netif_stop_queue(dev); 268*0791c032SJeremy Kerr 269*0791c032SJeremy Kerr /* prevent RX submission retry */ 270*0791c032SJeremy Kerr WRITE_ONCE(mctp_usb->stopped, true); 271*0791c032SJeremy Kerr 272*0791c032SJeremy Kerr usb_kill_urb(mctp_usb->rx_urb); 273*0791c032SJeremy Kerr usb_kill_urb(mctp_usb->tx_urb); 274*0791c032SJeremy Kerr 275*0791c032SJeremy Kerr cancel_delayed_work_sync(&mctp_usb->rx_retry_work); 276*0791c032SJeremy Kerr 277*0791c032SJeremy Kerr return 0; 278*0791c032SJeremy Kerr } 279*0791c032SJeremy Kerr 280*0791c032SJeremy Kerr static const struct net_device_ops mctp_usb_netdev_ops = { 281*0791c032SJeremy Kerr .ndo_start_xmit = mctp_usb_start_xmit, 282*0791c032SJeremy Kerr .ndo_open = mctp_usb_open, 283*0791c032SJeremy Kerr .ndo_stop = mctp_usb_stop, 284*0791c032SJeremy Kerr }; 285*0791c032SJeremy Kerr 286*0791c032SJeremy Kerr static void mctp_usb_netdev_setup(struct net_device *dev) 287*0791c032SJeremy Kerr { 288*0791c032SJeremy Kerr dev->type = ARPHRD_MCTP; 289*0791c032SJeremy Kerr 290*0791c032SJeremy Kerr dev->mtu = MCTP_USB_MTU_MIN; 291*0791c032SJeremy Kerr dev->min_mtu = MCTP_USB_MTU_MIN; 292*0791c032SJeremy Kerr dev->max_mtu = MCTP_USB_MTU_MAX; 293*0791c032SJeremy Kerr 294*0791c032SJeremy Kerr dev->hard_header_len = sizeof(struct mctp_usb_hdr); 295*0791c032SJeremy Kerr dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; 296*0791c032SJeremy Kerr dev->flags = IFF_NOARP; 297*0791c032SJeremy Kerr dev->netdev_ops = &mctp_usb_netdev_ops; 298*0791c032SJeremy Kerr dev->pcpu_stat_type = NETDEV_PCPU_STAT_DSTATS; 299*0791c032SJeremy Kerr } 300*0791c032SJeremy Kerr 301*0791c032SJeremy Kerr static int mctp_usb_probe(struct usb_interface *intf, 302*0791c032SJeremy Kerr const struct usb_device_id *id) 303*0791c032SJeremy Kerr { 304*0791c032SJeremy Kerr struct usb_endpoint_descriptor *ep_in, *ep_out; 305*0791c032SJeremy Kerr struct usb_host_interface *iface_desc; 306*0791c032SJeremy Kerr struct net_device *netdev; 307*0791c032SJeremy Kerr struct mctp_usb *dev; 308*0791c032SJeremy Kerr int rc; 309*0791c032SJeremy Kerr 310*0791c032SJeremy Kerr /* only one alternate */ 311*0791c032SJeremy Kerr iface_desc = intf->cur_altsetting; 312*0791c032SJeremy Kerr 313*0791c032SJeremy Kerr rc = usb_find_common_endpoints(iface_desc, &ep_in, &ep_out, NULL, NULL); 314*0791c032SJeremy Kerr if (rc) { 315*0791c032SJeremy Kerr dev_err(&intf->dev, "invalid endpoints on device?\n"); 316*0791c032SJeremy Kerr return rc; 317*0791c032SJeremy Kerr } 318*0791c032SJeremy Kerr 319*0791c032SJeremy Kerr netdev = alloc_netdev(sizeof(*dev), "mctpusb%d", NET_NAME_ENUM, 320*0791c032SJeremy Kerr mctp_usb_netdev_setup); 321*0791c032SJeremy Kerr if (!netdev) 322*0791c032SJeremy Kerr return -ENOMEM; 323*0791c032SJeremy Kerr 324*0791c032SJeremy Kerr SET_NETDEV_DEV(netdev, &intf->dev); 325*0791c032SJeremy Kerr dev = netdev_priv(netdev); 326*0791c032SJeremy Kerr dev->netdev = netdev; 327*0791c032SJeremy Kerr dev->usbdev = usb_get_dev(interface_to_usbdev(intf)); 328*0791c032SJeremy Kerr dev->intf = intf; 329*0791c032SJeremy Kerr usb_set_intfdata(intf, dev); 330*0791c032SJeremy Kerr 331*0791c032SJeremy Kerr dev->ep_in = ep_in->bEndpointAddress; 332*0791c032SJeremy Kerr dev->ep_out = ep_out->bEndpointAddress; 333*0791c032SJeremy Kerr 334*0791c032SJeremy Kerr dev->tx_urb = usb_alloc_urb(0, GFP_KERNEL); 335*0791c032SJeremy Kerr dev->rx_urb = usb_alloc_urb(0, GFP_KERNEL); 336*0791c032SJeremy Kerr if (!dev->tx_urb || !dev->rx_urb) { 337*0791c032SJeremy Kerr rc = -ENOMEM; 338*0791c032SJeremy Kerr goto err_free_urbs; 339*0791c032SJeremy Kerr } 340*0791c032SJeremy Kerr 341*0791c032SJeremy Kerr INIT_DELAYED_WORK(&dev->rx_retry_work, mctp_usb_rx_retry_work); 342*0791c032SJeremy Kerr 343*0791c032SJeremy Kerr rc = mctp_register_netdev(netdev, NULL, MCTP_PHYS_BINDING_USB); 344*0791c032SJeremy Kerr if (rc) 345*0791c032SJeremy Kerr goto err_free_urbs; 346*0791c032SJeremy Kerr 347*0791c032SJeremy Kerr return 0; 348*0791c032SJeremy Kerr 349*0791c032SJeremy Kerr err_free_urbs: 350*0791c032SJeremy Kerr usb_free_urb(dev->tx_urb); 351*0791c032SJeremy Kerr usb_free_urb(dev->rx_urb); 352*0791c032SJeremy Kerr free_netdev(netdev); 353*0791c032SJeremy Kerr return rc; 354*0791c032SJeremy Kerr } 355*0791c032SJeremy Kerr 356*0791c032SJeremy Kerr static void mctp_usb_disconnect(struct usb_interface *intf) 357*0791c032SJeremy Kerr { 358*0791c032SJeremy Kerr struct mctp_usb *dev = usb_get_intfdata(intf); 359*0791c032SJeremy Kerr 360*0791c032SJeremy Kerr mctp_unregister_netdev(dev->netdev); 361*0791c032SJeremy Kerr usb_free_urb(dev->tx_urb); 362*0791c032SJeremy Kerr usb_free_urb(dev->rx_urb); 363*0791c032SJeremy Kerr usb_put_dev(dev->usbdev); 364*0791c032SJeremy Kerr free_netdev(dev->netdev); 365*0791c032SJeremy Kerr } 366*0791c032SJeremy Kerr 367*0791c032SJeremy Kerr static const struct usb_device_id mctp_usb_devices[] = { 368*0791c032SJeremy Kerr { USB_INTERFACE_INFO(USB_CLASS_MCTP, 0x0, 0x1) }, 369*0791c032SJeremy Kerr { 0 }, 370*0791c032SJeremy Kerr }; 371*0791c032SJeremy Kerr 372*0791c032SJeremy Kerr MODULE_DEVICE_TABLE(usb, mctp_usb_devices); 373*0791c032SJeremy Kerr 374*0791c032SJeremy Kerr static struct usb_driver mctp_usb_driver = { 375*0791c032SJeremy Kerr .name = "mctp-usb", 376*0791c032SJeremy Kerr .id_table = mctp_usb_devices, 377*0791c032SJeremy Kerr .probe = mctp_usb_probe, 378*0791c032SJeremy Kerr .disconnect = mctp_usb_disconnect, 379*0791c032SJeremy Kerr }; 380*0791c032SJeremy Kerr 381*0791c032SJeremy Kerr module_usb_driver(mctp_usb_driver) 382*0791c032SJeremy Kerr 383*0791c032SJeremy Kerr MODULE_LICENSE("GPL"); 384*0791c032SJeremy Kerr MODULE_AUTHOR("Jeremy Kerr <jk@codeconstruct.com.au>"); 385*0791c032SJeremy Kerr MODULE_DESCRIPTION("MCTP USB transport"); 386