1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72#include <linux/slab.h>
73#include <linux/export.h>
74#include "wusbhc.h"
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99static int wusbhc_rh_port_reset(struct wusbhc *wusbhc, u8 port_idx)
100{
101 int result = 0;
102 struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx);
103 struct wusb_dev *wusb_dev = port->wusb_dev;
104
105 if (wusb_dev == NULL)
106 return -ENOTCONN;
107
108 port->status |= USB_PORT_STAT_RESET;
109 port->change |= USB_PORT_STAT_C_RESET;
110
111 if (wusb_dev->addr & WUSB_DEV_ADDR_UNAUTH)
112 result = 0;
113 else
114 result = wusb_dev_update_address(wusbhc, wusb_dev);
115
116 port->status &= ~USB_PORT_STAT_RESET;
117 port->status |= USB_PORT_STAT_ENABLE;
118 port->change |= USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_ENABLE;
119
120 return result;
121}
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141int wusbhc_rh_status_data(struct usb_hcd *usb_hcd, char *_buf)
142{
143 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
144 size_t cnt, size, bits_set = 0;
145
146
147
148 size = DIV_ROUND_UP(wusbhc->ports_max + 1, 8);
149
150
151 memset(_buf, 0, size);
152
153 for (cnt = 0; cnt < wusbhc->ports_max; cnt++) {
154
155 if (wusb_port_by_idx(wusbhc, cnt)->change) {
156 const int bitpos = cnt+1;
157
158 _buf[bitpos/8] |= (1 << (bitpos % 8));
159 bits_set++;
160 }
161 }
162
163 return bits_set ? size : 0;
164}
165EXPORT_SYMBOL_GPL(wusbhc_rh_status_data);
166
167
168
169
170
171
172
173
174static int wusbhc_rh_get_hub_descr(struct wusbhc *wusbhc, u16 wValue,
175 u16 wIndex,
176 struct usb_hub_descriptor *descr,
177 u16 wLength)
178{
179 u16 temp = 1 + (wusbhc->ports_max / 8);
180 u8 length = 7 + 2 * temp;
181
182 if (wLength < length)
183 return -ENOSPC;
184 descr->bDescLength = 7 + 2 * temp;
185 descr->bDescriptorType = USB_DT_HUB;
186 descr->bNbrPorts = wusbhc->ports_max;
187 descr->wHubCharacteristics = cpu_to_le16(
188 HUB_CHAR_COMMON_LPSM
189 | 0x00
190 | HUB_CHAR_NO_OCPM
191 | 0x00
192 | 0x00);
193 descr->bPwrOn2PwrGood = 0;
194 descr->bHubContrCurrent = 0;
195
196 memset(&descr->u.hs.DeviceRemovable[0], 0, temp);
197 memset(&descr->u.hs.DeviceRemovable[temp], 0xff, temp);
198 return 0;
199}
200
201
202
203
204
205
206
207
208static int wusbhc_rh_clear_hub_feat(struct wusbhc *wusbhc, u16 feature)
209{
210 int result;
211
212 switch (feature) {
213 case C_HUB_LOCAL_POWER:
214
215
216
217 case C_HUB_OVER_CURRENT:
218 result = 0;
219 break;
220 default:
221 result = -EPIPE;
222 }
223 return result;
224}
225
226
227
228
229
230
231
232
233static int wusbhc_rh_get_hub_status(struct wusbhc *wusbhc, u32 *buf,
234 u16 wLength)
235{
236
237 *buf = 0;
238 return 0;
239}
240
241
242
243
244
245
246static int wusbhc_rh_set_port_feat(struct wusbhc *wusbhc, u16 feature,
247 u8 selector, u8 port_idx)
248{
249 struct device *dev = wusbhc->dev;
250
251 if (port_idx > wusbhc->ports_max)
252 return -EINVAL;
253
254 switch (feature) {
255
256
257 case USB_PORT_FEAT_C_OVER_CURRENT:
258 case USB_PORT_FEAT_C_ENABLE:
259 case USB_PORT_FEAT_C_SUSPEND:
260 case USB_PORT_FEAT_C_CONNECTION:
261 case USB_PORT_FEAT_C_RESET:
262 return 0;
263 case USB_PORT_FEAT_POWER:
264
265 mutex_lock(&wusbhc->mutex);
266 wusb_port_by_idx(wusbhc, port_idx)->status |= USB_PORT_STAT_POWER;
267 mutex_unlock(&wusbhc->mutex);
268 return 0;
269 case USB_PORT_FEAT_RESET:
270 return wusbhc_rh_port_reset(wusbhc, port_idx);
271 case USB_PORT_FEAT_ENABLE:
272 case USB_PORT_FEAT_SUSPEND:
273 dev_err(dev, "(port_idx %d) set feat %d/%d UNIMPLEMENTED\n",
274 port_idx, feature, selector);
275 return -ENOSYS;
276 default:
277 dev_err(dev, "(port_idx %d) set feat %d/%d UNKNOWN\n",
278 port_idx, feature, selector);
279 return -EPIPE;
280 }
281
282 return 0;
283}
284
285
286
287
288
289
290static int wusbhc_rh_clear_port_feat(struct wusbhc *wusbhc, u16 feature,
291 u8 selector, u8 port_idx)
292{
293 int result = 0;
294 struct device *dev = wusbhc->dev;
295
296 if (port_idx > wusbhc->ports_max)
297 return -EINVAL;
298
299 mutex_lock(&wusbhc->mutex);
300 switch (feature) {
301 case USB_PORT_FEAT_POWER:
302
303 case USB_PORT_FEAT_C_OVER_CURRENT:
304 break;
305 case USB_PORT_FEAT_C_RESET:
306 wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_RESET;
307 break;
308 case USB_PORT_FEAT_C_CONNECTION:
309 wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_CONNECTION;
310 break;
311 case USB_PORT_FEAT_ENABLE:
312 __wusbhc_dev_disable(wusbhc, port_idx);
313 break;
314 case USB_PORT_FEAT_C_ENABLE:
315 wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_ENABLE;
316 break;
317 case USB_PORT_FEAT_SUSPEND:
318 case USB_PORT_FEAT_C_SUSPEND:
319 dev_err(dev, "(port_idx %d) Clear feat %d/%d UNIMPLEMENTED\n",
320 port_idx, feature, selector);
321 result = -ENOSYS;
322 break;
323 default:
324 dev_err(dev, "(port_idx %d) Clear feat %d/%d UNKNOWN\n",
325 port_idx, feature, selector);
326 result = -EPIPE;
327 break;
328 }
329 mutex_unlock(&wusbhc->mutex);
330
331 return result;
332}
333
334
335
336
337
338
339static int wusbhc_rh_get_port_status(struct wusbhc *wusbhc, u16 port_idx,
340 u32 *_buf, u16 wLength)
341{
342 __le16 *buf = (__le16 *)_buf;
343
344 if (port_idx > wusbhc->ports_max)
345 return -EINVAL;
346
347 mutex_lock(&wusbhc->mutex);
348 buf[0] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->status);
349 buf[1] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->change);
350 mutex_unlock(&wusbhc->mutex);
351
352 return 0;
353}
354
355
356
357
358
359
360int wusbhc_rh_control(struct usb_hcd *usb_hcd, u16 reqntype, u16 wValue,
361 u16 wIndex, char *buf, u16 wLength)
362{
363 int result = -ENOSYS;
364 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
365
366 switch (reqntype) {
367 case GetHubDescriptor:
368 result = wusbhc_rh_get_hub_descr(
369 wusbhc, wValue, wIndex,
370 (struct usb_hub_descriptor *) buf, wLength);
371 break;
372 case ClearHubFeature:
373 result = wusbhc_rh_clear_hub_feat(wusbhc, wValue);
374 break;
375 case GetHubStatus:
376 result = wusbhc_rh_get_hub_status(wusbhc, (u32 *)buf, wLength);
377 break;
378
379 case SetPortFeature:
380 result = wusbhc_rh_set_port_feat(wusbhc, wValue, wIndex >> 8,
381 (wIndex & 0xff) - 1);
382 break;
383 case ClearPortFeature:
384 result = wusbhc_rh_clear_port_feat(wusbhc, wValue, wIndex >> 8,
385 (wIndex & 0xff) - 1);
386 break;
387 case GetPortStatus:
388 result = wusbhc_rh_get_port_status(wusbhc, wIndex - 1,
389 (u32 *)buf, wLength);
390 break;
391
392 case SetHubFeature:
393 default:
394 dev_err(wusbhc->dev, "%s (%p [%p], %x, %x, %x, %p, %x) "
395 "UNIMPLEMENTED\n", __func__, usb_hcd, wusbhc, reqntype,
396 wValue, wIndex, buf, wLength);
397
398 result = -ENOSYS;
399 }
400 return result;
401}
402EXPORT_SYMBOL_GPL(wusbhc_rh_control);
403
404int wusbhc_rh_start_port_reset(struct usb_hcd *usb_hcd, unsigned port_idx)
405{
406 struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
407 dev_err(wusbhc->dev, "%s (%p [%p], port_idx %u) UNIMPLEMENTED\n",
408 __func__, usb_hcd, wusbhc, port_idx);
409 WARN_ON(1);
410 return -ENOSYS;
411}
412EXPORT_SYMBOL_GPL(wusbhc_rh_start_port_reset);
413
414static void wusb_port_init(struct wusb_port *port)
415{
416 port->status |= USB_PORT_STAT_HIGH_SPEED;
417}
418
419
420
421
422int wusbhc_rh_create(struct wusbhc *wusbhc)
423{
424 int result = -ENOMEM;
425 size_t port_size, itr;
426 port_size = wusbhc->ports_max * sizeof(wusbhc->port[0]);
427 wusbhc->port = kzalloc(port_size, GFP_KERNEL);
428 if (wusbhc->port == NULL)
429 goto error_port_alloc;
430 for (itr = 0; itr < wusbhc->ports_max; itr++)
431 wusb_port_init(&wusbhc->port[itr]);
432 result = 0;
433error_port_alloc:
434 return result;
435}
436
437void wusbhc_rh_destroy(struct wusbhc *wusbhc)
438{
439 kfree(wusbhc->port);
440}
441