18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * phonet.c -- USB CDC Phonet host driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2008-2009 Nokia Corporation. All rights reserved. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Rémi Denis-Courmont 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/mm.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/gfp.h> 148c2ecf20Sopenharmony_ci#include <linux/usb.h> 158c2ecf20Sopenharmony_ci#include <linux/usb/cdc.h> 168c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 178c2ecf20Sopenharmony_ci#include <linux/if_arp.h> 188c2ecf20Sopenharmony_ci#include <linux/if_phonet.h> 198c2ecf20Sopenharmony_ci#include <linux/phonet.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define PN_MEDIA_USB 0x1B 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic const unsigned rxq_size = 17; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct usbpn_dev { 268c2ecf20Sopenharmony_ci struct net_device *dev; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci struct usb_interface *intf, *data_intf; 298c2ecf20Sopenharmony_ci struct usb_device *usb; 308c2ecf20Sopenharmony_ci unsigned int tx_pipe, rx_pipe; 318c2ecf20Sopenharmony_ci u8 active_setting; 328c2ecf20Sopenharmony_ci u8 disconnected; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci unsigned tx_queue; 358c2ecf20Sopenharmony_ci spinlock_t tx_lock; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci spinlock_t rx_lock; 388c2ecf20Sopenharmony_ci struct sk_buff *rx_skb; 398c2ecf20Sopenharmony_ci struct urb *urbs[]; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void tx_complete(struct urb *req); 438c2ecf20Sopenharmony_cistatic void rx_complete(struct urb *req); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* 468c2ecf20Sopenharmony_ci * Network device callbacks 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_cistatic netdev_tx_t usbpn_xmit(struct sk_buff *skb, struct net_device *dev) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct usbpn_dev *pnd = netdev_priv(dev); 518c2ecf20Sopenharmony_ci struct urb *req = NULL; 528c2ecf20Sopenharmony_ci unsigned long flags; 538c2ecf20Sopenharmony_ci int err; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (skb->protocol != htons(ETH_P_PHONET)) 568c2ecf20Sopenharmony_ci goto drop; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci req = usb_alloc_urb(0, GFP_ATOMIC); 598c2ecf20Sopenharmony_ci if (!req) 608c2ecf20Sopenharmony_ci goto drop; 618c2ecf20Sopenharmony_ci usb_fill_bulk_urb(req, pnd->usb, pnd->tx_pipe, skb->data, skb->len, 628c2ecf20Sopenharmony_ci tx_complete, skb); 638c2ecf20Sopenharmony_ci req->transfer_flags = URB_ZERO_PACKET; 648c2ecf20Sopenharmony_ci err = usb_submit_urb(req, GFP_ATOMIC); 658c2ecf20Sopenharmony_ci if (err) { 668c2ecf20Sopenharmony_ci usb_free_urb(req); 678c2ecf20Sopenharmony_ci goto drop; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci spin_lock_irqsave(&pnd->tx_lock, flags); 718c2ecf20Sopenharmony_ci pnd->tx_queue++; 728c2ecf20Sopenharmony_ci if (pnd->tx_queue >= dev->tx_queue_len) 738c2ecf20Sopenharmony_ci netif_stop_queue(dev); 748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pnd->tx_lock, flags); 758c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cidrop: 788c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 798c2ecf20Sopenharmony_ci dev->stats.tx_dropped++; 808c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void tx_complete(struct urb *req) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct sk_buff *skb = req->context; 868c2ecf20Sopenharmony_ci struct net_device *dev = skb->dev; 878c2ecf20Sopenharmony_ci struct usbpn_dev *pnd = netdev_priv(dev); 888c2ecf20Sopenharmony_ci int status = req->status; 898c2ecf20Sopenharmony_ci unsigned long flags; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci switch (status) { 928c2ecf20Sopenharmony_ci case 0: 938c2ecf20Sopenharmony_ci dev->stats.tx_bytes += skb->len; 948c2ecf20Sopenharmony_ci break; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci case -ENOENT: 978c2ecf20Sopenharmony_ci case -ECONNRESET: 988c2ecf20Sopenharmony_ci case -ESHUTDOWN: 998c2ecf20Sopenharmony_ci dev->stats.tx_aborted_errors++; 1008c2ecf20Sopenharmony_ci fallthrough; 1018c2ecf20Sopenharmony_ci default: 1028c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 1038c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "TX error (%d)\n", status); 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci spin_lock_irqsave(&pnd->tx_lock, flags); 1088c2ecf20Sopenharmony_ci pnd->tx_queue--; 1098c2ecf20Sopenharmony_ci netif_wake_queue(dev); 1108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pnd->tx_lock, flags); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 1138c2ecf20Sopenharmony_ci usb_free_urb(req); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct net_device *dev = pnd->dev; 1198c2ecf20Sopenharmony_ci struct page *page; 1208c2ecf20Sopenharmony_ci int err; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci page = __dev_alloc_page(gfp_flags | __GFP_NOMEMALLOC); 1238c2ecf20Sopenharmony_ci if (!page) 1248c2ecf20Sopenharmony_ci return -ENOMEM; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci usb_fill_bulk_urb(req, pnd->usb, pnd->rx_pipe, page_address(page), 1278c2ecf20Sopenharmony_ci PAGE_SIZE, rx_complete, dev); 1288c2ecf20Sopenharmony_ci req->transfer_flags = 0; 1298c2ecf20Sopenharmony_ci err = usb_submit_urb(req, gfp_flags); 1308c2ecf20Sopenharmony_ci if (unlikely(err)) { 1318c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "RX submit error (%d)\n", err); 1328c2ecf20Sopenharmony_ci put_page(page); 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci return err; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void rx_complete(struct urb *req) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct net_device *dev = req->context; 1408c2ecf20Sopenharmony_ci struct usbpn_dev *pnd = netdev_priv(dev); 1418c2ecf20Sopenharmony_ci struct page *page = virt_to_page(req->transfer_buffer); 1428c2ecf20Sopenharmony_ci struct sk_buff *skb; 1438c2ecf20Sopenharmony_ci unsigned long flags; 1448c2ecf20Sopenharmony_ci int status = req->status; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci switch (status) { 1478c2ecf20Sopenharmony_ci case 0: 1488c2ecf20Sopenharmony_ci spin_lock_irqsave(&pnd->rx_lock, flags); 1498c2ecf20Sopenharmony_ci skb = pnd->rx_skb; 1508c2ecf20Sopenharmony_ci if (!skb) { 1518c2ecf20Sopenharmony_ci skb = pnd->rx_skb = netdev_alloc_skb(dev, 12); 1528c2ecf20Sopenharmony_ci if (likely(skb)) { 1538c2ecf20Sopenharmony_ci /* Can't use pskb_pull() on page in IRQ */ 1548c2ecf20Sopenharmony_ci skb_put_data(skb, page_address(page), 1); 1558c2ecf20Sopenharmony_ci skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, 1568c2ecf20Sopenharmony_ci page, 1, req->actual_length, 1578c2ecf20Sopenharmony_ci PAGE_SIZE); 1588c2ecf20Sopenharmony_ci page = NULL; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci } else { 1618c2ecf20Sopenharmony_ci skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, 1628c2ecf20Sopenharmony_ci page, 0, req->actual_length, 1638c2ecf20Sopenharmony_ci PAGE_SIZE); 1648c2ecf20Sopenharmony_ci page = NULL; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci if (req->actual_length < PAGE_SIZE) 1678c2ecf20Sopenharmony_ci pnd->rx_skb = NULL; /* Last fragment */ 1688c2ecf20Sopenharmony_ci else 1698c2ecf20Sopenharmony_ci skb = NULL; 1708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pnd->rx_lock, flags); 1718c2ecf20Sopenharmony_ci if (skb) { 1728c2ecf20Sopenharmony_ci skb->protocol = htons(ETH_P_PHONET); 1738c2ecf20Sopenharmony_ci skb_reset_mac_header(skb); 1748c2ecf20Sopenharmony_ci __skb_pull(skb, 1); 1758c2ecf20Sopenharmony_ci skb->dev = dev; 1768c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 1778c2ecf20Sopenharmony_ci dev->stats.rx_bytes += skb->len; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci netif_rx(skb); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci goto resubmit; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci case -ENOENT: 1848c2ecf20Sopenharmony_ci case -ECONNRESET: 1858c2ecf20Sopenharmony_ci case -ESHUTDOWN: 1868c2ecf20Sopenharmony_ci req = NULL; 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci case -EOVERFLOW: 1908c2ecf20Sopenharmony_ci dev->stats.rx_over_errors++; 1918c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "RX overflow\n"); 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci case -EILSEQ: 1958c2ecf20Sopenharmony_ci dev->stats.rx_crc_errors++; 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci dev->stats.rx_errors++; 2008c2ecf20Sopenharmony_ciresubmit: 2018c2ecf20Sopenharmony_ci if (page) 2028c2ecf20Sopenharmony_ci put_page(page); 2038c2ecf20Sopenharmony_ci if (req) 2048c2ecf20Sopenharmony_ci rx_submit(pnd, req, GFP_ATOMIC); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int usbpn_close(struct net_device *dev); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int usbpn_open(struct net_device *dev) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct usbpn_dev *pnd = netdev_priv(dev); 2128c2ecf20Sopenharmony_ci int err; 2138c2ecf20Sopenharmony_ci unsigned i; 2148c2ecf20Sopenharmony_ci unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci err = usb_set_interface(pnd->usb, num, pnd->active_setting); 2178c2ecf20Sopenharmony_ci if (err) 2188c2ecf20Sopenharmony_ci return err; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci for (i = 0; i < rxq_size; i++) { 2218c2ecf20Sopenharmony_ci struct urb *req = usb_alloc_urb(0, GFP_KERNEL); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (!req || rx_submit(pnd, req, GFP_KERNEL)) { 2248c2ecf20Sopenharmony_ci usb_free_urb(req); 2258c2ecf20Sopenharmony_ci usbpn_close(dev); 2268c2ecf20Sopenharmony_ci return -ENOMEM; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci pnd->urbs[i] = req; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci netif_wake_queue(dev); 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int usbpn_close(struct net_device *dev) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct usbpn_dev *pnd = netdev_priv(dev); 2388c2ecf20Sopenharmony_ci unsigned i; 2398c2ecf20Sopenharmony_ci unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci netif_stop_queue(dev); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci for (i = 0; i < rxq_size; i++) { 2448c2ecf20Sopenharmony_ci struct urb *req = pnd->urbs[i]; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (!req) 2478c2ecf20Sopenharmony_ci continue; 2488c2ecf20Sopenharmony_ci usb_kill_urb(req); 2498c2ecf20Sopenharmony_ci usb_free_urb(req); 2508c2ecf20Sopenharmony_ci pnd->urbs[i] = NULL; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return usb_set_interface(pnd->usb, num, !pnd->active_setting); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int usbpn_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct if_phonet_req *req = (struct if_phonet_req *)ifr; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci switch (cmd) { 2618c2ecf20Sopenharmony_ci case SIOCPNGAUTOCONF: 2628c2ecf20Sopenharmony_ci req->ifr_phonet_autoconf.device = PN_DEV_PC; 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic const struct net_device_ops usbpn_ops = { 2698c2ecf20Sopenharmony_ci .ndo_open = usbpn_open, 2708c2ecf20Sopenharmony_ci .ndo_stop = usbpn_close, 2718c2ecf20Sopenharmony_ci .ndo_start_xmit = usbpn_xmit, 2728c2ecf20Sopenharmony_ci .ndo_do_ioctl = usbpn_ioctl, 2738c2ecf20Sopenharmony_ci}; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic void usbpn_setup(struct net_device *dev) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci dev->features = 0; 2788c2ecf20Sopenharmony_ci dev->netdev_ops = &usbpn_ops, 2798c2ecf20Sopenharmony_ci dev->header_ops = &phonet_header_ops; 2808c2ecf20Sopenharmony_ci dev->type = ARPHRD_PHONET; 2818c2ecf20Sopenharmony_ci dev->flags = IFF_POINTOPOINT | IFF_NOARP; 2828c2ecf20Sopenharmony_ci dev->mtu = PHONET_MAX_MTU; 2838c2ecf20Sopenharmony_ci dev->min_mtu = PHONET_MIN_MTU; 2848c2ecf20Sopenharmony_ci dev->max_mtu = PHONET_MAX_MTU; 2858c2ecf20Sopenharmony_ci dev->hard_header_len = 1; 2868c2ecf20Sopenharmony_ci dev->dev_addr[0] = PN_MEDIA_USB; 2878c2ecf20Sopenharmony_ci dev->addr_len = 1; 2888c2ecf20Sopenharmony_ci dev->tx_queue_len = 3; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci dev->needs_free_netdev = true; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/* 2948c2ecf20Sopenharmony_ci * USB driver callbacks 2958c2ecf20Sopenharmony_ci */ 2968c2ecf20Sopenharmony_cistatic const struct usb_device_id usbpn_ids[] = { 2978c2ecf20Sopenharmony_ci { 2988c2ecf20Sopenharmony_ci .match_flags = USB_DEVICE_ID_MATCH_VENDOR 2998c2ecf20Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_CLASS 3008c2ecf20Sopenharmony_ci | USB_DEVICE_ID_MATCH_INT_SUBCLASS, 3018c2ecf20Sopenharmony_ci .idVendor = 0x0421, /* Nokia */ 3028c2ecf20Sopenharmony_ci .bInterfaceClass = USB_CLASS_COMM, 3038c2ecf20Sopenharmony_ci .bInterfaceSubClass = 0xFE, 3048c2ecf20Sopenharmony_ci }, 3058c2ecf20Sopenharmony_ci { }, 3068c2ecf20Sopenharmony_ci}; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, usbpn_ids); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic struct usb_driver usbpn_driver; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci static const char ifname[] = "usbpn%d"; 3158c2ecf20Sopenharmony_ci const struct usb_cdc_union_desc *union_header = NULL; 3168c2ecf20Sopenharmony_ci const struct usb_host_interface *data_desc; 3178c2ecf20Sopenharmony_ci struct usb_interface *data_intf; 3188c2ecf20Sopenharmony_ci struct usb_device *usbdev = interface_to_usbdev(intf); 3198c2ecf20Sopenharmony_ci struct net_device *dev; 3208c2ecf20Sopenharmony_ci struct usbpn_dev *pnd; 3218c2ecf20Sopenharmony_ci u8 *data; 3228c2ecf20Sopenharmony_ci int phonet = 0; 3238c2ecf20Sopenharmony_ci int len, err; 3248c2ecf20Sopenharmony_ci struct usb_cdc_parsed_header hdr; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci data = intf->altsetting->extra; 3278c2ecf20Sopenharmony_ci len = intf->altsetting->extralen; 3288c2ecf20Sopenharmony_ci cdc_parse_cdc_header(&hdr, intf, data, len); 3298c2ecf20Sopenharmony_ci union_header = hdr.usb_cdc_union_desc; 3308c2ecf20Sopenharmony_ci phonet = hdr.phonet_magic_present; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (!union_header || !phonet) 3338c2ecf20Sopenharmony_ci return -EINVAL; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci data_intf = usb_ifnum_to_if(usbdev, union_header->bSlaveInterface0); 3368c2ecf20Sopenharmony_ci if (data_intf == NULL) 3378c2ecf20Sopenharmony_ci return -ENODEV; 3388c2ecf20Sopenharmony_ci /* Data interface has one inactive and one active setting */ 3398c2ecf20Sopenharmony_ci if (data_intf->num_altsetting != 2) 3408c2ecf20Sopenharmony_ci return -EINVAL; 3418c2ecf20Sopenharmony_ci if (data_intf->altsetting[0].desc.bNumEndpoints == 0 && 3428c2ecf20Sopenharmony_ci data_intf->altsetting[1].desc.bNumEndpoints == 2) 3438c2ecf20Sopenharmony_ci data_desc = data_intf->altsetting + 1; 3448c2ecf20Sopenharmony_ci else 3458c2ecf20Sopenharmony_ci if (data_intf->altsetting[0].desc.bNumEndpoints == 2 && 3468c2ecf20Sopenharmony_ci data_intf->altsetting[1].desc.bNumEndpoints == 0) 3478c2ecf20Sopenharmony_ci data_desc = data_intf->altsetting; 3488c2ecf20Sopenharmony_ci else 3498c2ecf20Sopenharmony_ci return -EINVAL; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci dev = alloc_netdev(struct_size(pnd, urbs, rxq_size), ifname, 3528c2ecf20Sopenharmony_ci NET_NAME_UNKNOWN, usbpn_setup); 3538c2ecf20Sopenharmony_ci if (!dev) 3548c2ecf20Sopenharmony_ci return -ENOMEM; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci pnd = netdev_priv(dev); 3578c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &intf->dev); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci pnd->dev = dev; 3608c2ecf20Sopenharmony_ci pnd->usb = usbdev; 3618c2ecf20Sopenharmony_ci pnd->intf = intf; 3628c2ecf20Sopenharmony_ci pnd->data_intf = data_intf; 3638c2ecf20Sopenharmony_ci spin_lock_init(&pnd->tx_lock); 3648c2ecf20Sopenharmony_ci spin_lock_init(&pnd->rx_lock); 3658c2ecf20Sopenharmony_ci /* Endpoints */ 3668c2ecf20Sopenharmony_ci if (usb_pipein(data_desc->endpoint[0].desc.bEndpointAddress)) { 3678c2ecf20Sopenharmony_ci pnd->rx_pipe = usb_rcvbulkpipe(usbdev, 3688c2ecf20Sopenharmony_ci data_desc->endpoint[0].desc.bEndpointAddress); 3698c2ecf20Sopenharmony_ci pnd->tx_pipe = usb_sndbulkpipe(usbdev, 3708c2ecf20Sopenharmony_ci data_desc->endpoint[1].desc.bEndpointAddress); 3718c2ecf20Sopenharmony_ci } else { 3728c2ecf20Sopenharmony_ci pnd->rx_pipe = usb_rcvbulkpipe(usbdev, 3738c2ecf20Sopenharmony_ci data_desc->endpoint[1].desc.bEndpointAddress); 3748c2ecf20Sopenharmony_ci pnd->tx_pipe = usb_sndbulkpipe(usbdev, 3758c2ecf20Sopenharmony_ci data_desc->endpoint[0].desc.bEndpointAddress); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci pnd->active_setting = data_desc - data_intf->altsetting; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci err = usb_driver_claim_interface(&usbpn_driver, data_intf, pnd); 3808c2ecf20Sopenharmony_ci if (err) 3818c2ecf20Sopenharmony_ci goto out; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* Force inactive mode until the network device is brought UP */ 3848c2ecf20Sopenharmony_ci usb_set_interface(usbdev, union_header->bSlaveInterface0, 3858c2ecf20Sopenharmony_ci !pnd->active_setting); 3868c2ecf20Sopenharmony_ci usb_set_intfdata(intf, pnd); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci err = register_netdev(dev); 3898c2ecf20Sopenharmony_ci if (err) { 3908c2ecf20Sopenharmony_ci /* Set disconnected flag so that disconnect() returns early. */ 3918c2ecf20Sopenharmony_ci pnd->disconnected = 1; 3928c2ecf20Sopenharmony_ci usb_driver_release_interface(&usbpn_driver, data_intf); 3938c2ecf20Sopenharmony_ci goto out; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "USB CDC Phonet device found\n"); 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ciout: 4008c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 4018c2ecf20Sopenharmony_ci free_netdev(dev); 4028c2ecf20Sopenharmony_ci return err; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic void usbpn_disconnect(struct usb_interface *intf) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci struct usbpn_dev *pnd = usb_get_intfdata(intf); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (pnd->disconnected) 4108c2ecf20Sopenharmony_ci return; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci pnd->disconnected = 1; 4138c2ecf20Sopenharmony_ci usb_driver_release_interface(&usbpn_driver, 4148c2ecf20Sopenharmony_ci (pnd->intf == intf) ? pnd->data_intf : pnd->intf); 4158c2ecf20Sopenharmony_ci unregister_netdev(pnd->dev); 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic struct usb_driver usbpn_driver = { 4198c2ecf20Sopenharmony_ci .name = "cdc_phonet", 4208c2ecf20Sopenharmony_ci .probe = usbpn_probe, 4218c2ecf20Sopenharmony_ci .disconnect = usbpn_disconnect, 4228c2ecf20Sopenharmony_ci .id_table = usbpn_ids, 4238c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 4248c2ecf20Sopenharmony_ci}; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cimodule_usb_driver(usbpn_driver); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Remi Denis-Courmont"); 4298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("USB CDC Phonet host interface"); 4308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 431