162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * phonet.c -- USB CDC Phonet host driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008-2009 Nokia Corporation. All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Rémi Denis-Courmont 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/mm.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/gfp.h> 1462306a36Sopenharmony_ci#include <linux/usb.h> 1562306a36Sopenharmony_ci#include <linux/usb/cdc.h> 1662306a36Sopenharmony_ci#include <linux/netdevice.h> 1762306a36Sopenharmony_ci#include <linux/if_arp.h> 1862306a36Sopenharmony_ci#include <linux/if_phonet.h> 1962306a36Sopenharmony_ci#include <linux/phonet.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define PN_MEDIA_USB 0x1B 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic const unsigned rxq_size = 17; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct usbpn_dev { 2662306a36Sopenharmony_ci struct net_device *dev; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci struct usb_interface *intf, *data_intf; 2962306a36Sopenharmony_ci struct usb_device *usb; 3062306a36Sopenharmony_ci unsigned int tx_pipe, rx_pipe; 3162306a36Sopenharmony_ci u8 active_setting; 3262306a36Sopenharmony_ci u8 disconnected; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci unsigned tx_queue; 3562306a36Sopenharmony_ci spinlock_t tx_lock; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci spinlock_t rx_lock; 3862306a36Sopenharmony_ci struct sk_buff *rx_skb; 3962306a36Sopenharmony_ci struct urb *urbs[]; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic void tx_complete(struct urb *req); 4362306a36Sopenharmony_cistatic void rx_complete(struct urb *req); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * Network device callbacks 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_cistatic netdev_tx_t usbpn_xmit(struct sk_buff *skb, struct net_device *dev) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct usbpn_dev *pnd = netdev_priv(dev); 5162306a36Sopenharmony_ci struct urb *req = NULL; 5262306a36Sopenharmony_ci unsigned long flags; 5362306a36Sopenharmony_ci int err; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (skb->protocol != htons(ETH_P_PHONET)) 5662306a36Sopenharmony_ci goto drop; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci req = usb_alloc_urb(0, GFP_ATOMIC); 5962306a36Sopenharmony_ci if (!req) 6062306a36Sopenharmony_ci goto drop; 6162306a36Sopenharmony_ci usb_fill_bulk_urb(req, pnd->usb, pnd->tx_pipe, skb->data, skb->len, 6262306a36Sopenharmony_ci tx_complete, skb); 6362306a36Sopenharmony_ci req->transfer_flags = URB_ZERO_PACKET; 6462306a36Sopenharmony_ci err = usb_submit_urb(req, GFP_ATOMIC); 6562306a36Sopenharmony_ci if (err) { 6662306a36Sopenharmony_ci usb_free_urb(req); 6762306a36Sopenharmony_ci goto drop; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci spin_lock_irqsave(&pnd->tx_lock, flags); 7162306a36Sopenharmony_ci pnd->tx_queue++; 7262306a36Sopenharmony_ci if (pnd->tx_queue >= dev->tx_queue_len) 7362306a36Sopenharmony_ci netif_stop_queue(dev); 7462306a36Sopenharmony_ci spin_unlock_irqrestore(&pnd->tx_lock, flags); 7562306a36Sopenharmony_ci return NETDEV_TX_OK; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cidrop: 7862306a36Sopenharmony_ci dev_kfree_skb(skb); 7962306a36Sopenharmony_ci dev->stats.tx_dropped++; 8062306a36Sopenharmony_ci return NETDEV_TX_OK; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void tx_complete(struct urb *req) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct sk_buff *skb = req->context; 8662306a36Sopenharmony_ci struct net_device *dev = skb->dev; 8762306a36Sopenharmony_ci struct usbpn_dev *pnd = netdev_priv(dev); 8862306a36Sopenharmony_ci int status = req->status; 8962306a36Sopenharmony_ci unsigned long flags; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci switch (status) { 9262306a36Sopenharmony_ci case 0: 9362306a36Sopenharmony_ci dev->stats.tx_bytes += skb->len; 9462306a36Sopenharmony_ci break; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci case -ENOENT: 9762306a36Sopenharmony_ci case -ECONNRESET: 9862306a36Sopenharmony_ci case -ESHUTDOWN: 9962306a36Sopenharmony_ci dev->stats.tx_aborted_errors++; 10062306a36Sopenharmony_ci fallthrough; 10162306a36Sopenharmony_ci default: 10262306a36Sopenharmony_ci dev->stats.tx_errors++; 10362306a36Sopenharmony_ci dev_dbg(&dev->dev, "TX error (%d)\n", status); 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci dev->stats.tx_packets++; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci spin_lock_irqsave(&pnd->tx_lock, flags); 10862306a36Sopenharmony_ci pnd->tx_queue--; 10962306a36Sopenharmony_ci netif_wake_queue(dev); 11062306a36Sopenharmony_ci spin_unlock_irqrestore(&pnd->tx_lock, flags); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci dev_kfree_skb_any(skb); 11362306a36Sopenharmony_ci usb_free_urb(req); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct net_device *dev = pnd->dev; 11962306a36Sopenharmony_ci struct page *page; 12062306a36Sopenharmony_ci int err; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci page = __dev_alloc_page(gfp_flags | __GFP_NOMEMALLOC); 12362306a36Sopenharmony_ci if (!page) 12462306a36Sopenharmony_ci return -ENOMEM; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci usb_fill_bulk_urb(req, pnd->usb, pnd->rx_pipe, page_address(page), 12762306a36Sopenharmony_ci PAGE_SIZE, rx_complete, dev); 12862306a36Sopenharmony_ci req->transfer_flags = 0; 12962306a36Sopenharmony_ci err = usb_submit_urb(req, gfp_flags); 13062306a36Sopenharmony_ci if (unlikely(err)) { 13162306a36Sopenharmony_ci dev_dbg(&dev->dev, "RX submit error (%d)\n", err); 13262306a36Sopenharmony_ci put_page(page); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci return err; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic void rx_complete(struct urb *req) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct net_device *dev = req->context; 14062306a36Sopenharmony_ci struct usbpn_dev *pnd = netdev_priv(dev); 14162306a36Sopenharmony_ci struct page *page = virt_to_page(req->transfer_buffer); 14262306a36Sopenharmony_ci struct sk_buff *skb; 14362306a36Sopenharmony_ci unsigned long flags; 14462306a36Sopenharmony_ci int status = req->status; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci switch (status) { 14762306a36Sopenharmony_ci case 0: 14862306a36Sopenharmony_ci spin_lock_irqsave(&pnd->rx_lock, flags); 14962306a36Sopenharmony_ci skb = pnd->rx_skb; 15062306a36Sopenharmony_ci if (!skb) { 15162306a36Sopenharmony_ci skb = pnd->rx_skb = netdev_alloc_skb(dev, 12); 15262306a36Sopenharmony_ci if (likely(skb)) { 15362306a36Sopenharmony_ci /* Can't use pskb_pull() on page in IRQ */ 15462306a36Sopenharmony_ci skb_put_data(skb, page_address(page), 1); 15562306a36Sopenharmony_ci skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, 15662306a36Sopenharmony_ci page, 1, req->actual_length, 15762306a36Sopenharmony_ci PAGE_SIZE); 15862306a36Sopenharmony_ci page = NULL; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci } else { 16162306a36Sopenharmony_ci skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, 16262306a36Sopenharmony_ci page, 0, req->actual_length, 16362306a36Sopenharmony_ci PAGE_SIZE); 16462306a36Sopenharmony_ci page = NULL; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci if (req->actual_length < PAGE_SIZE) 16762306a36Sopenharmony_ci pnd->rx_skb = NULL; /* Last fragment */ 16862306a36Sopenharmony_ci else 16962306a36Sopenharmony_ci skb = NULL; 17062306a36Sopenharmony_ci spin_unlock_irqrestore(&pnd->rx_lock, flags); 17162306a36Sopenharmony_ci if (skb) { 17262306a36Sopenharmony_ci skb->protocol = htons(ETH_P_PHONET); 17362306a36Sopenharmony_ci skb_reset_mac_header(skb); 17462306a36Sopenharmony_ci __skb_pull(skb, 1); 17562306a36Sopenharmony_ci skb->dev = dev; 17662306a36Sopenharmony_ci dev->stats.rx_packets++; 17762306a36Sopenharmony_ci dev->stats.rx_bytes += skb->len; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci netif_rx(skb); 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci goto resubmit; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci case -ENOENT: 18462306a36Sopenharmony_ci case -ECONNRESET: 18562306a36Sopenharmony_ci case -ESHUTDOWN: 18662306a36Sopenharmony_ci req = NULL; 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci case -EOVERFLOW: 19062306a36Sopenharmony_ci dev->stats.rx_over_errors++; 19162306a36Sopenharmony_ci dev_dbg(&dev->dev, "RX overflow\n"); 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci case -EILSEQ: 19562306a36Sopenharmony_ci dev->stats.rx_crc_errors++; 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci dev->stats.rx_errors++; 20062306a36Sopenharmony_ciresubmit: 20162306a36Sopenharmony_ci if (page) 20262306a36Sopenharmony_ci put_page(page); 20362306a36Sopenharmony_ci if (req) 20462306a36Sopenharmony_ci rx_submit(pnd, req, GFP_ATOMIC); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int usbpn_close(struct net_device *dev); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int usbpn_open(struct net_device *dev) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct usbpn_dev *pnd = netdev_priv(dev); 21262306a36Sopenharmony_ci int err; 21362306a36Sopenharmony_ci unsigned i; 21462306a36Sopenharmony_ci unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci err = usb_set_interface(pnd->usb, num, pnd->active_setting); 21762306a36Sopenharmony_ci if (err) 21862306a36Sopenharmony_ci return err; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci for (i = 0; i < rxq_size; i++) { 22162306a36Sopenharmony_ci struct urb *req = usb_alloc_urb(0, GFP_KERNEL); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (!req || rx_submit(pnd, req, GFP_KERNEL)) { 22462306a36Sopenharmony_ci usb_free_urb(req); 22562306a36Sopenharmony_ci usbpn_close(dev); 22662306a36Sopenharmony_ci return -ENOMEM; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci pnd->urbs[i] = req; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci netif_wake_queue(dev); 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic int usbpn_close(struct net_device *dev) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct usbpn_dev *pnd = netdev_priv(dev); 23862306a36Sopenharmony_ci unsigned i; 23962306a36Sopenharmony_ci unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci netif_stop_queue(dev); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci for (i = 0; i < rxq_size; i++) { 24462306a36Sopenharmony_ci struct urb *req = pnd->urbs[i]; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (!req) 24762306a36Sopenharmony_ci continue; 24862306a36Sopenharmony_ci usb_kill_urb(req); 24962306a36Sopenharmony_ci usb_free_urb(req); 25062306a36Sopenharmony_ci pnd->urbs[i] = NULL; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return usb_set_interface(pnd->usb, num, !pnd->active_setting); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int usbpn_siocdevprivate(struct net_device *dev, struct ifreq *ifr, 25762306a36Sopenharmony_ci void __user *data, int cmd) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct if_phonet_req *req = (struct if_phonet_req *)ifr; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci switch (cmd) { 26262306a36Sopenharmony_ci case SIOCPNGAUTOCONF: 26362306a36Sopenharmony_ci req->ifr_phonet_autoconf.device = PN_DEV_PC; 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci return -ENOIOCTLCMD; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic const struct net_device_ops usbpn_ops = { 27062306a36Sopenharmony_ci .ndo_open = usbpn_open, 27162306a36Sopenharmony_ci .ndo_stop = usbpn_close, 27262306a36Sopenharmony_ci .ndo_start_xmit = usbpn_xmit, 27362306a36Sopenharmony_ci .ndo_siocdevprivate = usbpn_siocdevprivate, 27462306a36Sopenharmony_ci}; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic void usbpn_setup(struct net_device *dev) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci const u8 addr = PN_MEDIA_USB; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci dev->features = 0; 28162306a36Sopenharmony_ci dev->netdev_ops = &usbpn_ops; 28262306a36Sopenharmony_ci dev->header_ops = &phonet_header_ops; 28362306a36Sopenharmony_ci dev->type = ARPHRD_PHONET; 28462306a36Sopenharmony_ci dev->flags = IFF_POINTOPOINT | IFF_NOARP; 28562306a36Sopenharmony_ci dev->mtu = PHONET_MAX_MTU; 28662306a36Sopenharmony_ci dev->min_mtu = PHONET_MIN_MTU; 28762306a36Sopenharmony_ci dev->max_mtu = PHONET_MAX_MTU; 28862306a36Sopenharmony_ci dev->hard_header_len = 1; 28962306a36Sopenharmony_ci dev->addr_len = 1; 29062306a36Sopenharmony_ci dev_addr_set(dev, &addr); 29162306a36Sopenharmony_ci dev->tx_queue_len = 3; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci dev->needs_free_netdev = true; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci/* 29762306a36Sopenharmony_ci * USB driver callbacks 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_cistatic const struct usb_device_id usbpn_ids[] = { 30062306a36Sopenharmony_ci { 30162306a36Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_VENDOR 30262306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_CLASS 30362306a36Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_SUBCLASS, 30462306a36Sopenharmony_ci .idVendor = 0x0421, /* Nokia */ 30562306a36Sopenharmony_ci .bInterfaceClass = USB_CLASS_COMM, 30662306a36Sopenharmony_ci .bInterfaceSubClass = 0xFE, 30762306a36Sopenharmony_ci }, 30862306a36Sopenharmony_ci { }, 30962306a36Sopenharmony_ci}; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, usbpn_ids); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic struct usb_driver usbpn_driver; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci static const char ifname[] = "usbpn%d"; 31862306a36Sopenharmony_ci const struct usb_cdc_union_desc *union_header = NULL; 31962306a36Sopenharmony_ci const struct usb_host_interface *data_desc; 32062306a36Sopenharmony_ci struct usb_interface *data_intf; 32162306a36Sopenharmony_ci struct usb_device *usbdev = interface_to_usbdev(intf); 32262306a36Sopenharmony_ci struct net_device *dev; 32362306a36Sopenharmony_ci struct usbpn_dev *pnd; 32462306a36Sopenharmony_ci u8 *data; 32562306a36Sopenharmony_ci int phonet = 0; 32662306a36Sopenharmony_ci int len, err; 32762306a36Sopenharmony_ci struct usb_cdc_parsed_header hdr; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci data = intf->altsetting->extra; 33062306a36Sopenharmony_ci len = intf->altsetting->extralen; 33162306a36Sopenharmony_ci cdc_parse_cdc_header(&hdr, intf, data, len); 33262306a36Sopenharmony_ci union_header = hdr.usb_cdc_union_desc; 33362306a36Sopenharmony_ci phonet = hdr.phonet_magic_present; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (!union_header || !phonet) 33662306a36Sopenharmony_ci return -EINVAL; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci data_intf = usb_ifnum_to_if(usbdev, union_header->bSlaveInterface0); 33962306a36Sopenharmony_ci if (data_intf == NULL) 34062306a36Sopenharmony_ci return -ENODEV; 34162306a36Sopenharmony_ci /* Data interface has one inactive and one active setting */ 34262306a36Sopenharmony_ci if (data_intf->num_altsetting != 2) 34362306a36Sopenharmony_ci return -EINVAL; 34462306a36Sopenharmony_ci if (data_intf->altsetting[0].desc.bNumEndpoints == 0 && 34562306a36Sopenharmony_ci data_intf->altsetting[1].desc.bNumEndpoints == 2) 34662306a36Sopenharmony_ci data_desc = data_intf->altsetting + 1; 34762306a36Sopenharmony_ci else 34862306a36Sopenharmony_ci if (data_intf->altsetting[0].desc.bNumEndpoints == 2 && 34962306a36Sopenharmony_ci data_intf->altsetting[1].desc.bNumEndpoints == 0) 35062306a36Sopenharmony_ci data_desc = data_intf->altsetting; 35162306a36Sopenharmony_ci else 35262306a36Sopenharmony_ci return -EINVAL; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci dev = alloc_netdev(struct_size(pnd, urbs, rxq_size), ifname, 35562306a36Sopenharmony_ci NET_NAME_UNKNOWN, usbpn_setup); 35662306a36Sopenharmony_ci if (!dev) 35762306a36Sopenharmony_ci return -ENOMEM; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci pnd = netdev_priv(dev); 36062306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &intf->dev); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci pnd->dev = dev; 36362306a36Sopenharmony_ci pnd->usb = usbdev; 36462306a36Sopenharmony_ci pnd->intf = intf; 36562306a36Sopenharmony_ci pnd->data_intf = data_intf; 36662306a36Sopenharmony_ci spin_lock_init(&pnd->tx_lock); 36762306a36Sopenharmony_ci spin_lock_init(&pnd->rx_lock); 36862306a36Sopenharmony_ci /* Endpoints */ 36962306a36Sopenharmony_ci if (usb_pipein(data_desc->endpoint[0].desc.bEndpointAddress)) { 37062306a36Sopenharmony_ci pnd->rx_pipe = usb_rcvbulkpipe(usbdev, 37162306a36Sopenharmony_ci data_desc->endpoint[0].desc.bEndpointAddress); 37262306a36Sopenharmony_ci pnd->tx_pipe = usb_sndbulkpipe(usbdev, 37362306a36Sopenharmony_ci data_desc->endpoint[1].desc.bEndpointAddress); 37462306a36Sopenharmony_ci } else { 37562306a36Sopenharmony_ci pnd->rx_pipe = usb_rcvbulkpipe(usbdev, 37662306a36Sopenharmony_ci data_desc->endpoint[1].desc.bEndpointAddress); 37762306a36Sopenharmony_ci pnd->tx_pipe = usb_sndbulkpipe(usbdev, 37862306a36Sopenharmony_ci data_desc->endpoint[0].desc.bEndpointAddress); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci pnd->active_setting = data_desc - data_intf->altsetting; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci err = usb_driver_claim_interface(&usbpn_driver, data_intf, pnd); 38362306a36Sopenharmony_ci if (err) 38462306a36Sopenharmony_ci goto out; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci /* Force inactive mode until the network device is brought UP */ 38762306a36Sopenharmony_ci usb_set_interface(usbdev, union_header->bSlaveInterface0, 38862306a36Sopenharmony_ci !pnd->active_setting); 38962306a36Sopenharmony_ci usb_set_intfdata(intf, pnd); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci err = register_netdev(dev); 39262306a36Sopenharmony_ci if (err) { 39362306a36Sopenharmony_ci /* Set disconnected flag so that disconnect() returns early. */ 39462306a36Sopenharmony_ci pnd->disconnected = 1; 39562306a36Sopenharmony_ci usb_driver_release_interface(&usbpn_driver, data_intf); 39662306a36Sopenharmony_ci goto out; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci dev_dbg(&dev->dev, "USB CDC Phonet device found\n"); 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ciout: 40362306a36Sopenharmony_ci usb_set_intfdata(intf, NULL); 40462306a36Sopenharmony_ci free_netdev(dev); 40562306a36Sopenharmony_ci return err; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic void usbpn_disconnect(struct usb_interface *intf) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct usbpn_dev *pnd = usb_get_intfdata(intf); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (pnd->disconnected) 41362306a36Sopenharmony_ci return; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci pnd->disconnected = 1; 41662306a36Sopenharmony_ci usb_driver_release_interface(&usbpn_driver, 41762306a36Sopenharmony_ci (pnd->intf == intf) ? pnd->data_intf : pnd->intf); 41862306a36Sopenharmony_ci unregister_netdev(pnd->dev); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic struct usb_driver usbpn_driver = { 42262306a36Sopenharmony_ci .name = "cdc_phonet", 42362306a36Sopenharmony_ci .probe = usbpn_probe, 42462306a36Sopenharmony_ci .disconnect = usbpn_disconnect, 42562306a36Sopenharmony_ci .id_table = usbpn_ids, 42662306a36Sopenharmony_ci .disable_hub_initiated_lpm = 1, 42762306a36Sopenharmony_ci}; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cimodule_usb_driver(usbpn_driver); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ciMODULE_AUTHOR("Remi Denis-Courmont"); 43262306a36Sopenharmony_ciMODULE_DESCRIPTION("USB CDC Phonet host interface"); 43362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 434