1 /* 2 * Copyright (C) 2007 by Alan Stern 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License as published by the 6 * Free Software Foundation; either version 2 of the License, or (at your 7 * option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software Foundation, 16 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 */ 18 19 /* this file is part of ehci-hcd.c */ 20 21 22 /* Display the ports dedicated to the companion controller */ 23 static ssize_t show_companion(struct device *dev, 24 struct device_attribute *attr, 25 char *buf) 26 { 27 struct ehci_hcd *ehci; 28 int nports, index, n; 29 int count = PAGE_SIZE; 30 char *ptr = buf; 31 32 ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); 33 nports = HCS_N_PORTS(ehci->hcs_params); 34 35 for (index = 0; index < nports; ++index) { 36 if (test_bit(index, &ehci->companion_ports)) { 37 n = scnprintf(ptr, count, "%d\n", index + 1); 38 ptr += n; 39 count -= n; 40 } 41 } 42 return ptr - buf; 43 } 44 45 /* 46 * Dedicate or undedicate a port to the companion controller. 47 * Syntax is "[-]portnum", where a leading '-' sign means 48 * return control of the port to the EHCI controller. 49 */ 50 static ssize_t store_companion(struct device *dev, 51 struct device_attribute *attr, 52 const char *buf, size_t count) 53 { 54 struct ehci_hcd *ehci; 55 int portnum, new_owner; 56 57 ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); 58 new_owner = PORT_OWNER; /* Owned by companion */ 59 if (sscanf(buf, "%d", &portnum) != 1) 60 return -EINVAL; 61 if (portnum < 0) { 62 portnum = - portnum; 63 new_owner = 0; /* Owned by EHCI */ 64 } 65 if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params)) 66 return -ENOENT; 67 portnum--; 68 if (new_owner) 69 set_bit(portnum, &ehci->companion_ports); 70 else 71 clear_bit(portnum, &ehci->companion_ports); 72 set_owner(ehci, portnum, new_owner); 73 return count; 74 } 75 static DEVICE_ATTR(companion, 0644, show_companion, store_companion); 76 77 78 /* 79 * Display / Set uframe_periodic_max 80 */ 81 static ssize_t show_uframe_periodic_max(struct device *dev, 82 struct device_attribute *attr, 83 char *buf) 84 { 85 struct ehci_hcd *ehci; 86 int n; 87 88 ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); 89 n = scnprintf(buf, PAGE_SIZE, "%d\n", ehci->uframe_periodic_max); 90 return n; 91 } 92 93 94 static ssize_t store_uframe_periodic_max(struct device *dev, 95 struct device_attribute *attr, 96 const char *buf, size_t count) 97 { 98 struct ehci_hcd *ehci; 99 unsigned uframe_periodic_max; 100 unsigned uframe; 101 unsigned long flags; 102 ssize_t ret; 103 104 ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); 105 if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) 106 return -EINVAL; 107 108 if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) { 109 ehci_info(ehci, "rejecting invalid request for " 110 "uframe_periodic_max=%u\n", uframe_periodic_max); 111 return -EINVAL; 112 } 113 114 ret = -EINVAL; 115 116 /* 117 * lock, so that our checking does not race with possible periodic 118 * bandwidth allocation through submitting new urbs. 119 */ 120 spin_lock_irqsave (&ehci->lock, flags); 121 122 /* 123 * for request to decrease max periodic bandwidth, we have to check 124 * to see whether the decrease is possible. 125 */ 126 if (uframe_periodic_max < ehci->uframe_periodic_max) { 127 u8 allocated_max = 0; 128 129 for (uframe = 0; uframe < EHCI_BANDWIDTH_SIZE; ++uframe) 130 allocated_max = max(allocated_max, 131 ehci->bandwidth[uframe]); 132 133 if (allocated_max > uframe_periodic_max) { 134 ehci_info(ehci, 135 "cannot decrease uframe_periodic_max because " 136 "periodic bandwidth is already allocated " 137 "(%u > %u)\n", 138 allocated_max, uframe_periodic_max); 139 goto out_unlock; 140 } 141 } 142 143 /* increasing is always ok */ 144 145 ehci_info(ehci, "setting max periodic bandwidth to %u%% " 146 "(== %u usec/uframe)\n", 147 100*uframe_periodic_max/125, uframe_periodic_max); 148 149 if (uframe_periodic_max != 100) 150 ehci_warn(ehci, "max periodic bandwidth set is non-standard\n"); 151 152 ehci->uframe_periodic_max = uframe_periodic_max; 153 ret = count; 154 155 out_unlock: 156 spin_unlock_irqrestore (&ehci->lock, flags); 157 return ret; 158 } 159 static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, store_uframe_periodic_max); 160 161 162 static inline int create_sysfs_files(struct ehci_hcd *ehci) 163 { 164 struct device *controller = ehci_to_hcd(ehci)->self.controller; 165 int i = 0; 166 167 /* with integrated TT there is no companion! */ 168 if (!ehci_is_TDI(ehci)) 169 i = device_create_file(controller, &dev_attr_companion); 170 if (i) 171 goto out; 172 173 i = device_create_file(controller, &dev_attr_uframe_periodic_max); 174 out: 175 return i; 176 } 177 178 static inline void remove_sysfs_files(struct ehci_hcd *ehci) 179 { 180 struct device *controller = ehci_to_hcd(ehci)->self.controller; 181 182 /* with integrated TT there is no companion! */ 183 if (!ehci_is_TDI(ehci)) 184 device_remove_file(controller, &dev_attr_companion); 185 186 device_remove_file(controller, &dev_attr_uframe_periodic_max); 187 } 188