18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/****************************************************************************** 38c2ecf20Sopenharmony_ci * xusbatm.c - dumb usbatm-based driver for modems initialized in userspace 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru) 68c2ecf20Sopenharmony_ci ******************************************************************************/ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> /* for eth_random_addr() */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "usbatm.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define XUSBATM_DRIVERS_MAX 8 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define XUSBATM_PARM(name, type, parmtype, desc) \ 178c2ecf20Sopenharmony_ci static type name[XUSBATM_DRIVERS_MAX]; \ 188c2ecf20Sopenharmony_ci static unsigned int num_##name; \ 198c2ecf20Sopenharmony_ci module_param_array(name, parmtype, &num_##name, 0444); \ 208c2ecf20Sopenharmony_ci MODULE_PARM_DESC(name, desc) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ciXUSBATM_PARM(vendor, unsigned short, ushort, "USB device vendor"); 238c2ecf20Sopenharmony_ciXUSBATM_PARM(product, unsigned short, ushort, "USB device product"); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciXUSBATM_PARM(rx_endpoint, unsigned char, byte, "rx endpoint number"); 268c2ecf20Sopenharmony_ciXUSBATM_PARM(tx_endpoint, unsigned char, byte, "tx endpoint number"); 278c2ecf20Sopenharmony_ciXUSBATM_PARM(rx_padding, unsigned char, byte, "rx padding (default 0)"); 288c2ecf20Sopenharmony_ciXUSBATM_PARM(tx_padding, unsigned char, byte, "tx padding (default 0)"); 298c2ecf20Sopenharmony_ciXUSBATM_PARM(rx_altsetting, unsigned char, byte, "rx altsetting (default 0)"); 308c2ecf20Sopenharmony_ciXUSBATM_PARM(tx_altsetting, unsigned char, byte, "rx altsetting (default 0)"); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic const char xusbatm_driver_name[] = "xusbatm"; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic struct usbatm_driver xusbatm_drivers[XUSBATM_DRIVERS_MAX]; 358c2ecf20Sopenharmony_cistatic struct usb_device_id xusbatm_usb_ids[XUSBATM_DRIVERS_MAX + 1]; 368c2ecf20Sopenharmony_cistatic struct usb_driver xusbatm_usb_driver; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic struct usb_interface *xusbatm_find_intf(struct usb_device *usb_dev, int altsetting, u8 ep) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct usb_host_interface *alt; 418c2ecf20Sopenharmony_ci struct usb_interface *intf; 428c2ecf20Sopenharmony_ci int i, j; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) 458c2ecf20Sopenharmony_ci if ((intf = usb_dev->actconfig->interface[i]) && (alt = usb_altnum_to_altsetting(intf, altsetting))) 468c2ecf20Sopenharmony_ci for (j = 0; j < alt->desc.bNumEndpoints; j++) 478c2ecf20Sopenharmony_ci if (alt->endpoint[j].desc.bEndpointAddress == ep) 488c2ecf20Sopenharmony_ci return intf; 498c2ecf20Sopenharmony_ci return NULL; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int xusbatm_capture_intf(struct usbatm_data *usbatm, struct usb_device *usb_dev, 538c2ecf20Sopenharmony_ci struct usb_interface *intf, int altsetting, int claim) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci int ifnum = intf->altsetting->desc.bInterfaceNumber; 568c2ecf20Sopenharmony_ci int ret; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (claim && (ret = usb_driver_claim_interface(&xusbatm_usb_driver, intf, usbatm))) { 598c2ecf20Sopenharmony_ci usb_err(usbatm, "%s: failed to claim interface %2d (%d)!\n", __func__, ifnum, ret); 608c2ecf20Sopenharmony_ci return ret; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci ret = usb_set_interface(usb_dev, ifnum, altsetting); 638c2ecf20Sopenharmony_ci if (ret) { 648c2ecf20Sopenharmony_ci usb_err(usbatm, "%s: altsetting %2d for interface %2d failed (%d)!\n", __func__, altsetting, ifnum, ret); 658c2ecf20Sopenharmony_ci return ret; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci return 0; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic void xusbatm_release_intf(struct usb_device *usb_dev, struct usb_interface *intf, int claimed) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci if (claimed) { 738c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 748c2ecf20Sopenharmony_ci usb_driver_release_interface(&xusbatm_usb_driver, intf); 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int xusbatm_bind(struct usbatm_data *usbatm, 798c2ecf20Sopenharmony_ci struct usb_interface *intf, const struct usb_device_id *id) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(intf); 828c2ecf20Sopenharmony_ci int drv_ix = id - xusbatm_usb_ids; 838c2ecf20Sopenharmony_ci int rx_alt = rx_altsetting[drv_ix]; 848c2ecf20Sopenharmony_ci int tx_alt = tx_altsetting[drv_ix]; 858c2ecf20Sopenharmony_ci struct usb_interface *rx_intf = xusbatm_find_intf(usb_dev, rx_alt, rx_endpoint[drv_ix]); 868c2ecf20Sopenharmony_ci struct usb_interface *tx_intf = xusbatm_find_intf(usb_dev, tx_alt, tx_endpoint[drv_ix]); 878c2ecf20Sopenharmony_ci int ret; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: binding driver %d: vendor %04x product %04x" 908c2ecf20Sopenharmony_ci " rx: ep %02x padd %d alt %2d tx: ep %02x padd %d alt %2d\n", 918c2ecf20Sopenharmony_ci __func__, drv_ix, vendor[drv_ix], product[drv_ix], 928c2ecf20Sopenharmony_ci rx_endpoint[drv_ix], rx_padding[drv_ix], rx_alt, 938c2ecf20Sopenharmony_ci tx_endpoint[drv_ix], tx_padding[drv_ix], tx_alt); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (!rx_intf || !tx_intf) { 968c2ecf20Sopenharmony_ci if (!rx_intf) 978c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: no interface contains endpoint %02x in altsetting %2d\n", 988c2ecf20Sopenharmony_ci __func__, rx_endpoint[drv_ix], rx_alt); 998c2ecf20Sopenharmony_ci if (!tx_intf) 1008c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: no interface contains endpoint %02x in altsetting %2d\n", 1018c2ecf20Sopenharmony_ci __func__, tx_endpoint[drv_ix], tx_alt); 1028c2ecf20Sopenharmony_ci return -ENODEV; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if ((rx_intf != intf) && (tx_intf != intf)) 1068c2ecf20Sopenharmony_ci return -ENODEV; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if ((rx_intf == tx_intf) && (rx_alt != tx_alt)) { 1098c2ecf20Sopenharmony_ci usb_err(usbatm, "%s: altsettings clash on interface %2d (%2d vs %2d)!\n", __func__, 1108c2ecf20Sopenharmony_ci rx_intf->altsetting->desc.bInterfaceNumber, rx_alt, tx_alt); 1118c2ecf20Sopenharmony_ci return -EINVAL; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s: rx If#=%2d; tx If#=%2d\n", __func__, 1158c2ecf20Sopenharmony_ci rx_intf->altsetting->desc.bInterfaceNumber, 1168c2ecf20Sopenharmony_ci tx_intf->altsetting->desc.bInterfaceNumber); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci ret = xusbatm_capture_intf(usbatm, usb_dev, rx_intf, rx_alt, rx_intf != intf); 1198c2ecf20Sopenharmony_ci if (ret) 1208c2ecf20Sopenharmony_ci return ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if ((tx_intf != rx_intf) && (ret = xusbatm_capture_intf(usbatm, usb_dev, tx_intf, tx_alt, tx_intf != intf))) { 1238c2ecf20Sopenharmony_ci xusbatm_release_intf(usb_dev, rx_intf, rx_intf != intf); 1248c2ecf20Sopenharmony_ci return ret; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void xusbatm_unbind(struct usbatm_data *usbatm, 1318c2ecf20Sopenharmony_ci struct usb_interface *intf) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct usb_device *usb_dev = interface_to_usbdev(intf); 1348c2ecf20Sopenharmony_ci int i; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci usb_dbg(usbatm, "%s entered\n", __func__); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci for (i = 0; i < usb_dev->actconfig->desc.bNumInterfaces; i++) { 1398c2ecf20Sopenharmony_ci struct usb_interface *cur_intf = usb_dev->actconfig->interface[i]; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (cur_intf && (usb_get_intfdata(cur_intf) == usbatm)) { 1428c2ecf20Sopenharmony_ci usb_set_intfdata(cur_intf, NULL); 1438c2ecf20Sopenharmony_ci usb_driver_release_interface(&xusbatm_usb_driver, cur_intf); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int xusbatm_atm_start(struct usbatm_data *usbatm, 1498c2ecf20Sopenharmony_ci struct atm_dev *atm_dev) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci atm_dbg(usbatm, "%s entered\n", __func__); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* use random MAC as we've no way to get it from the device */ 1548c2ecf20Sopenharmony_ci eth_random_addr(atm_dev->esi); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int xusbatm_usb_probe(struct usb_interface *intf, 1618c2ecf20Sopenharmony_ci const struct usb_device_id *id) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci return usbatm_usb_probe(intf, id, 1648c2ecf20Sopenharmony_ci xusbatm_drivers + (id - xusbatm_usb_ids)); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic struct usb_driver xusbatm_usb_driver = { 1688c2ecf20Sopenharmony_ci .name = xusbatm_driver_name, 1698c2ecf20Sopenharmony_ci .probe = xusbatm_usb_probe, 1708c2ecf20Sopenharmony_ci .disconnect = usbatm_usb_disconnect, 1718c2ecf20Sopenharmony_ci .id_table = xusbatm_usb_ids 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int __init xusbatm_init(void) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci int i; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (!num_vendor || 1798c2ecf20Sopenharmony_ci num_vendor != num_product || 1808c2ecf20Sopenharmony_ci num_vendor != num_rx_endpoint || 1818c2ecf20Sopenharmony_ci num_vendor != num_tx_endpoint) { 1828c2ecf20Sopenharmony_ci printk(KERN_WARNING "xusbatm: malformed module parameters\n"); 1838c2ecf20Sopenharmony_ci return -EINVAL; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci for (i = 0; i < num_vendor; i++) { 1878c2ecf20Sopenharmony_ci rx_endpoint[i] |= USB_DIR_IN; 1888c2ecf20Sopenharmony_ci tx_endpoint[i] &= USB_ENDPOINT_NUMBER_MASK; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci xusbatm_usb_ids[i].match_flags = USB_DEVICE_ID_MATCH_DEVICE; 1918c2ecf20Sopenharmony_ci xusbatm_usb_ids[i].idVendor = vendor[i]; 1928c2ecf20Sopenharmony_ci xusbatm_usb_ids[i].idProduct = product[i]; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci xusbatm_drivers[i].driver_name = xusbatm_driver_name; 1958c2ecf20Sopenharmony_ci xusbatm_drivers[i].bind = xusbatm_bind; 1968c2ecf20Sopenharmony_ci xusbatm_drivers[i].unbind = xusbatm_unbind; 1978c2ecf20Sopenharmony_ci xusbatm_drivers[i].atm_start = xusbatm_atm_start; 1988c2ecf20Sopenharmony_ci xusbatm_drivers[i].bulk_in = rx_endpoint[i]; 1998c2ecf20Sopenharmony_ci xusbatm_drivers[i].bulk_out = tx_endpoint[i]; 2008c2ecf20Sopenharmony_ci xusbatm_drivers[i].rx_padding = rx_padding[i]; 2018c2ecf20Sopenharmony_ci xusbatm_drivers[i].tx_padding = tx_padding[i]; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return usb_register(&xusbatm_usb_driver); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_cimodule_init(xusbatm_init); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic void __exit xusbatm_exit(void) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci usb_deregister(&xusbatm_usb_driver); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_cimodule_exit(xusbatm_exit); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ciMODULE_AUTHOR("Roman Kagan, Duncan Sands"); 2158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for USB ADSL modems initialized in userspace"); 2168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 217