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 frame, uframe; 101 unsigned short allocated_max; 102 unsigned long flags; 103 ssize_t ret; 104 105 ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev))); 106 if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) 107 return -EINVAL; 108 109 if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) { 110 ehci_info(ehci, "rejecting invalid request for " 111 "uframe_periodic_max=%u\n", uframe_periodic_max); 112 return -EINVAL; 113 } 114 115 ret = -EINVAL; 116 117 /* 118 * lock, so that our checking does not race with possible periodic 119 * bandwidth allocation through submitting new urbs. 120 */ 121 spin_lock_irqsave (&ehci->lock, flags); 122 123 /* 124 * for request to decrease max periodic bandwidth, we have to check 125 * every microframe in the schedule to see whether the decrease is 126 * possible. 127 */ 128 if (uframe_periodic_max < ehci->uframe_periodic_max) { 129 allocated_max = 0; 130 131 for (frame = 0; frame < ehci->periodic_size; ++frame) 132 for (uframe = 0; uframe < 7; ++uframe) 133 allocated_max = max(allocated_max, 134 periodic_usecs (ehci, frame, uframe)); 135 136 if (allocated_max > uframe_periodic_max) { 137 ehci_info(ehci, 138 "cannot decrease uframe_periodic_max becase " 139 "periodic bandwidth is already allocated " 140 "(%u > %u)\n", 141 allocated_max, uframe_periodic_max); 142 goto out_unlock; 143 } 144 } 145 146 /* increasing is always ok */ 147 148 ehci_info(ehci, "setting max periodic bandwidth to %u%% " 149 "(== %u usec/uframe)\n", 150 100*uframe_periodic_max/125, uframe_periodic_max); 151 152 if (uframe_periodic_max != 100) 153 ehci_warn(ehci, "max periodic bandwidth set is non-standard\n"); 154 155 ehci->uframe_periodic_max = uframe_periodic_max; 156 ret = count; 157 158 out_unlock: 159 spin_unlock_irqrestore (&ehci->lock, flags); 160 return ret; 161 } 162 static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, store_uframe_periodic_max); 163 164 165 static inline int create_sysfs_files(struct ehci_hcd *ehci) 166 { 167 struct device *controller = ehci_to_hcd(ehci)->self.controller; 168 int i = 0; 169 170 /* with integrated TT there is no companion! */ 171 if (!ehci_is_TDI(ehci)) 172 i = device_create_file(controller, &dev_attr_companion); 173 if (i) 174 goto out; 175 176 i = device_create_file(controller, &dev_attr_uframe_periodic_max); 177 out: 178 return i; 179 } 180 181 static inline void remove_sysfs_files(struct ehci_hcd *ehci) 182 { 183 struct device *controller = ehci_to_hcd(ehci)->self.controller; 184 185 /* with integrated TT there is no companion! */ 186 if (!ehci_is_TDI(ehci)) 187 device_remove_file(controller, &dev_attr_companion); 188 189 device_remove_file(controller, &dev_attr_uframe_periodic_max); 190 } 191