18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/usb/core/file.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) Copyright Linus Torvalds 1999 68c2ecf20Sopenharmony_ci * (C) Copyright Johannes Erdfelt 1999-2001 78c2ecf20Sopenharmony_ci * (C) Copyright Andreas Gal 1999 88c2ecf20Sopenharmony_ci * (C) Copyright Gregory P. Smith 1999 98c2ecf20Sopenharmony_ci * (C) Copyright Deti Fliegl 1999 (new USB architecture) 108c2ecf20Sopenharmony_ci * (C) Copyright Randy Dunlap 2000 118c2ecf20Sopenharmony_ci * (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id, 128c2ecf20Sopenharmony_ci * more docs, etc) 138c2ecf20Sopenharmony_ci * (C) Copyright Yggdrasil Computing, Inc. 2000 148c2ecf20Sopenharmony_ci * (usb_device_id matching changes by Adam J. Richter) 158c2ecf20Sopenharmony_ci * (C) Copyright Greg Kroah-Hartman 2002-2003 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * Released under the GPLv2 only. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/errno.h> 228c2ecf20Sopenharmony_ci#include <linux/rwsem.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/string.h> 258c2ecf20Sopenharmony_ci#include <linux/usb.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "usb.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define MAX_USB_MINORS 256 308c2ecf20Sopenharmony_cistatic const struct file_operations *usb_minors[MAX_USB_MINORS]; 318c2ecf20Sopenharmony_cistatic DECLARE_RWSEM(minor_rwsem); 328c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(init_usb_class_mutex); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int usb_open(struct inode *inode, struct file *file) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci int err = -ENODEV; 378c2ecf20Sopenharmony_ci const struct file_operations *new_fops; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci down_read(&minor_rwsem); 408c2ecf20Sopenharmony_ci new_fops = fops_get(usb_minors[iminor(inode)]); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (!new_fops) 438c2ecf20Sopenharmony_ci goto done; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci replace_fops(file, new_fops); 468c2ecf20Sopenharmony_ci /* Curiouser and curiouser... NULL ->open() as "no device" ? */ 478c2ecf20Sopenharmony_ci if (file->f_op->open) 488c2ecf20Sopenharmony_ci err = file->f_op->open(inode, file); 498c2ecf20Sopenharmony_ci done: 508c2ecf20Sopenharmony_ci up_read(&minor_rwsem); 518c2ecf20Sopenharmony_ci return err; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic const struct file_operations usb_fops = { 558c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 568c2ecf20Sopenharmony_ci .open = usb_open, 578c2ecf20Sopenharmony_ci .llseek = noop_llseek, 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic struct usb_class { 618c2ecf20Sopenharmony_ci struct kref kref; 628c2ecf20Sopenharmony_ci struct class *class; 638c2ecf20Sopenharmony_ci} *usb_class; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic char *usb_devnode(struct device *dev, umode_t *mode) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct usb_class_driver *drv; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci drv = dev_get_drvdata(dev); 708c2ecf20Sopenharmony_ci if (!drv || !drv->devnode) 718c2ecf20Sopenharmony_ci return NULL; 728c2ecf20Sopenharmony_ci return drv->devnode(dev, mode); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int init_usb_class(void) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci int result = 0; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (usb_class != NULL) { 808c2ecf20Sopenharmony_ci kref_get(&usb_class->kref); 818c2ecf20Sopenharmony_ci goto exit; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci usb_class = kmalloc(sizeof(*usb_class), GFP_KERNEL); 858c2ecf20Sopenharmony_ci if (!usb_class) { 868c2ecf20Sopenharmony_ci result = -ENOMEM; 878c2ecf20Sopenharmony_ci goto exit; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci kref_init(&usb_class->kref); 918c2ecf20Sopenharmony_ci usb_class->class = class_create(THIS_MODULE, "usbmisc"); 928c2ecf20Sopenharmony_ci if (IS_ERR(usb_class->class)) { 938c2ecf20Sopenharmony_ci result = PTR_ERR(usb_class->class); 948c2ecf20Sopenharmony_ci printk(KERN_ERR "class_create failed for usb devices\n"); 958c2ecf20Sopenharmony_ci kfree(usb_class); 968c2ecf20Sopenharmony_ci usb_class = NULL; 978c2ecf20Sopenharmony_ci goto exit; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci usb_class->class->devnode = usb_devnode; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ciexit: 1028c2ecf20Sopenharmony_ci return result; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic void release_usb_class(struct kref *kref) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci /* Ok, we cheat as we know we only have one usb_class */ 1088c2ecf20Sopenharmony_ci class_destroy(usb_class->class); 1098c2ecf20Sopenharmony_ci kfree(usb_class); 1108c2ecf20Sopenharmony_ci usb_class = NULL; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic void destroy_usb_class(void) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci mutex_lock(&init_usb_class_mutex); 1168c2ecf20Sopenharmony_ci kref_put(&usb_class->kref, release_usb_class); 1178c2ecf20Sopenharmony_ci mutex_unlock(&init_usb_class_mutex); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ciint usb_major_init(void) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci int error; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci error = register_chrdev(USB_MAJOR, "usb", &usb_fops); 1258c2ecf20Sopenharmony_ci if (error) 1268c2ecf20Sopenharmony_ci printk(KERN_ERR "Unable to get major %d for usb devices\n", 1278c2ecf20Sopenharmony_ci USB_MAJOR); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return error; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_civoid usb_major_cleanup(void) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci unregister_chrdev(USB_MAJOR, "usb"); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/** 1388c2ecf20Sopenharmony_ci * usb_register_dev - register a USB device, and ask for a minor number 1398c2ecf20Sopenharmony_ci * @intf: pointer to the usb_interface that is being registered 1408c2ecf20Sopenharmony_ci * @class_driver: pointer to the usb_class_driver for this device 1418c2ecf20Sopenharmony_ci * 1428c2ecf20Sopenharmony_ci * This should be called by all USB drivers that use the USB major number. 1438c2ecf20Sopenharmony_ci * If CONFIG_USB_DYNAMIC_MINORS is enabled, the minor number will be 1448c2ecf20Sopenharmony_ci * dynamically allocated out of the list of available ones. If it is not 1458c2ecf20Sopenharmony_ci * enabled, the minor number will be based on the next available free minor, 1468c2ecf20Sopenharmony_ci * starting at the class_driver->minor_base. 1478c2ecf20Sopenharmony_ci * 1488c2ecf20Sopenharmony_ci * This function also creates a usb class device in the sysfs tree. 1498c2ecf20Sopenharmony_ci * 1508c2ecf20Sopenharmony_ci * usb_deregister_dev() must be called when the driver is done with 1518c2ecf20Sopenharmony_ci * the minor numbers given out by this function. 1528c2ecf20Sopenharmony_ci * 1538c2ecf20Sopenharmony_ci * Return: -EINVAL if something bad happens with trying to register a 1548c2ecf20Sopenharmony_ci * device, and 0 on success. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_ciint usb_register_dev(struct usb_interface *intf, 1578c2ecf20Sopenharmony_ci struct usb_class_driver *class_driver) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci int retval; 1608c2ecf20Sopenharmony_ci int minor_base = class_driver->minor_base; 1618c2ecf20Sopenharmony_ci int minor; 1628c2ecf20Sopenharmony_ci char name[20]; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci#ifdef CONFIG_USB_DYNAMIC_MINORS 1658c2ecf20Sopenharmony_ci /* 1668c2ecf20Sopenharmony_ci * We don't care what the device tries to start at, we want to start 1678c2ecf20Sopenharmony_ci * at zero to pack the devices into the smallest available space with 1688c2ecf20Sopenharmony_ci * no holes in the minor range. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ci minor_base = 0; 1718c2ecf20Sopenharmony_ci#endif 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (class_driver->fops == NULL) 1748c2ecf20Sopenharmony_ci return -EINVAL; 1758c2ecf20Sopenharmony_ci if (intf->minor >= 0) 1768c2ecf20Sopenharmony_ci return -EADDRINUSE; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci mutex_lock(&init_usb_class_mutex); 1798c2ecf20Sopenharmony_ci retval = init_usb_class(); 1808c2ecf20Sopenharmony_ci mutex_unlock(&init_usb_class_mutex); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (retval) 1838c2ecf20Sopenharmony_ci return retval; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci dev_dbg(&intf->dev, "looking for a minor, starting at %d\n", minor_base); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci down_write(&minor_rwsem); 1888c2ecf20Sopenharmony_ci for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) { 1898c2ecf20Sopenharmony_ci if (usb_minors[minor]) 1908c2ecf20Sopenharmony_ci continue; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci usb_minors[minor] = class_driver->fops; 1938c2ecf20Sopenharmony_ci intf->minor = minor; 1948c2ecf20Sopenharmony_ci break; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci if (intf->minor < 0) { 1978c2ecf20Sopenharmony_ci up_write(&minor_rwsem); 1988c2ecf20Sopenharmony_ci return -EXFULL; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* create a usb class device for this usb interface */ 2028c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), class_driver->name, minor - minor_base); 2038c2ecf20Sopenharmony_ci intf->usb_dev = device_create(usb_class->class, &intf->dev, 2048c2ecf20Sopenharmony_ci MKDEV(USB_MAJOR, minor), class_driver, 2058c2ecf20Sopenharmony_ci "%s", kbasename(name)); 2068c2ecf20Sopenharmony_ci if (IS_ERR(intf->usb_dev)) { 2078c2ecf20Sopenharmony_ci usb_minors[minor] = NULL; 2088c2ecf20Sopenharmony_ci intf->minor = -1; 2098c2ecf20Sopenharmony_ci retval = PTR_ERR(intf->usb_dev); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci up_write(&minor_rwsem); 2128c2ecf20Sopenharmony_ci return retval; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_register_dev); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/** 2178c2ecf20Sopenharmony_ci * usb_deregister_dev - deregister a USB device's dynamic minor. 2188c2ecf20Sopenharmony_ci * @intf: pointer to the usb_interface that is being deregistered 2198c2ecf20Sopenharmony_ci * @class_driver: pointer to the usb_class_driver for this device 2208c2ecf20Sopenharmony_ci * 2218c2ecf20Sopenharmony_ci * Used in conjunction with usb_register_dev(). This function is called 2228c2ecf20Sopenharmony_ci * when the USB driver is finished with the minor numbers gotten from a 2238c2ecf20Sopenharmony_ci * call to usb_register_dev() (usually when the device is disconnected 2248c2ecf20Sopenharmony_ci * from the system.) 2258c2ecf20Sopenharmony_ci * 2268c2ecf20Sopenharmony_ci * This function also removes the usb class device from the sysfs tree. 2278c2ecf20Sopenharmony_ci * 2288c2ecf20Sopenharmony_ci * This should be called by all drivers that use the USB major number. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_civoid usb_deregister_dev(struct usb_interface *intf, 2318c2ecf20Sopenharmony_ci struct usb_class_driver *class_driver) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci if (intf->minor == -1) 2348c2ecf20Sopenharmony_ci return; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci dev_dbg(&intf->dev, "removing %d minor\n", intf->minor); 2378c2ecf20Sopenharmony_ci device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor)); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci down_write(&minor_rwsem); 2408c2ecf20Sopenharmony_ci usb_minors[intf->minor] = NULL; 2418c2ecf20Sopenharmony_ci up_write(&minor_rwsem); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci intf->usb_dev = NULL; 2448c2ecf20Sopenharmony_ci intf->minor = -1; 2458c2ecf20Sopenharmony_ci destroy_usb_class(); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_deregister_dev); 248