linux/drivers/usb/host/ehci-sysfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2007 by Alan Stern
   4 */
   5
   6/* this file is part of ehci-hcd.c */
   7
   8
   9/* Display the ports dedicated to the companion controller */
  10static ssize_t companion_show(struct device *dev,
  11                              struct device_attribute *attr,
  12                              char *buf)
  13{
  14        struct ehci_hcd         *ehci;
  15        int                     nports, index, n;
  16        int                     count = PAGE_SIZE;
  17        char                    *ptr = buf;
  18
  19        ehci = hcd_to_ehci(dev_get_drvdata(dev));
  20        nports = HCS_N_PORTS(ehci->hcs_params);
  21
  22        for (index = 0; index < nports; ++index) {
  23                if (test_bit(index, &ehci->companion_ports)) {
  24                        n = scnprintf(ptr, count, "%d\n", index + 1);
  25                        ptr += n;
  26                        count -= n;
  27                }
  28        }
  29        return ptr - buf;
  30}
  31
  32/*
  33 * Dedicate or undedicate a port to the companion controller.
  34 * Syntax is "[-]portnum", where a leading '-' sign means
  35 * return control of the port to the EHCI controller.
  36 */
  37static ssize_t companion_store(struct device *dev,
  38                               struct device_attribute *attr,
  39                               const char *buf, size_t count)
  40{
  41        struct ehci_hcd         *ehci;
  42        int                     portnum, new_owner;
  43
  44        ehci = hcd_to_ehci(dev_get_drvdata(dev));
  45        new_owner = PORT_OWNER;         /* Owned by companion */
  46        if (sscanf(buf, "%d", &portnum) != 1)
  47                return -EINVAL;
  48        if (portnum < 0) {
  49                portnum = - portnum;
  50                new_owner = 0;          /* Owned by EHCI */
  51        }
  52        if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params))
  53                return -ENOENT;
  54        portnum--;
  55        if (new_owner)
  56                set_bit(portnum, &ehci->companion_ports);
  57        else
  58                clear_bit(portnum, &ehci->companion_ports);
  59        set_owner(ehci, portnum, new_owner);
  60        return count;
  61}
  62static DEVICE_ATTR_RW(companion);
  63
  64
  65/*
  66 * Display / Set uframe_periodic_max
  67 */
  68static ssize_t uframe_periodic_max_show(struct device *dev,
  69                                        struct device_attribute *attr,
  70                                        char *buf)
  71{
  72        struct ehci_hcd         *ehci;
  73        int                     n;
  74
  75        ehci = hcd_to_ehci(dev_get_drvdata(dev));
  76        n = scnprintf(buf, PAGE_SIZE, "%d\n", ehci->uframe_periodic_max);
  77        return n;
  78}
  79
  80
  81static ssize_t uframe_periodic_max_store(struct device *dev,
  82                                        struct device_attribute *attr,
  83                                        const char *buf, size_t count)
  84{
  85        struct ehci_hcd         *ehci;
  86        unsigned                uframe_periodic_max;
  87        unsigned                uframe;
  88        unsigned long           flags;
  89        ssize_t                 ret;
  90
  91        ehci = hcd_to_ehci(dev_get_drvdata(dev));
  92        if (kstrtouint(buf, 0, &uframe_periodic_max) < 0)
  93                return -EINVAL;
  94
  95        if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) {
  96                ehci_info(ehci, "rejecting invalid request for "
  97                                "uframe_periodic_max=%u\n", uframe_periodic_max);
  98                return -EINVAL;
  99        }
 100
 101        ret = -EINVAL;
 102
 103        /*
 104         * lock, so that our checking does not race with possible periodic
 105         * bandwidth allocation through submitting new urbs.
 106         */
 107        spin_lock_irqsave (&ehci->lock, flags);
 108
 109        /*
 110         * for request to decrease max periodic bandwidth, we have to check
 111         * to see whether the decrease is possible.
 112         */
 113        if (uframe_periodic_max < ehci->uframe_periodic_max) {
 114                u8              allocated_max = 0;
 115
 116                for (uframe = 0; uframe < EHCI_BANDWIDTH_SIZE; ++uframe)
 117                        allocated_max = max(allocated_max,
 118                                        ehci->bandwidth[uframe]);
 119
 120                if (allocated_max > uframe_periodic_max) {
 121                        ehci_info(ehci,
 122                                "cannot decrease uframe_periodic_max because "
 123                                "periodic bandwidth is already allocated "
 124                                "(%u > %u)\n",
 125                                allocated_max, uframe_periodic_max);
 126                        goto out_unlock;
 127                }
 128        }
 129
 130        /* increasing is always ok */
 131
 132        ehci_info(ehci, "setting max periodic bandwidth to %u%% "
 133                        "(== %u usec/uframe)\n",
 134                        100*uframe_periodic_max/125, uframe_periodic_max);
 135
 136        if (uframe_periodic_max != 100)
 137                ehci_warn(ehci, "max periodic bandwidth set is non-standard\n");
 138
 139        ehci->uframe_periodic_max = uframe_periodic_max;
 140        ret = count;
 141
 142out_unlock:
 143        spin_unlock_irqrestore (&ehci->lock, flags);
 144        return ret;
 145}
 146static DEVICE_ATTR_RW(uframe_periodic_max);
 147
 148
 149static inline int create_sysfs_files(struct ehci_hcd *ehci)
 150{
 151        struct device   *controller = ehci_to_hcd(ehci)->self.controller;
 152        int     i = 0;
 153
 154        /* with integrated TT there is no companion! */
 155        if (!ehci_is_TDI(ehci))
 156                i = device_create_file(controller, &dev_attr_companion);
 157        if (i)
 158                goto out;
 159
 160        i = device_create_file(controller, &dev_attr_uframe_periodic_max);
 161out:
 162        return i;
 163}
 164
 165static inline void remove_sysfs_files(struct ehci_hcd *ehci)
 166{
 167        struct device   *controller = ehci_to_hcd(ehci)->self.controller;
 168
 169        /* with integrated TT there is no companion! */
 170        if (!ehci_is_TDI(ehci))
 171                device_remove_file(controller, &dev_attr_companion);
 172
 173        device_remove_file(controller, &dev_attr_uframe_periodic_max);
 174}
 175