• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/usb/wusbcore/
1
2#include <linux/slab.h>
3#include "wusbhc.h"
4
5/*
6 * Reset a fake port
7 *
8 * Using a Reset Device IE is too heavyweight as it causes the device
9 * to enter the UnConnected state and leave the cluster, this can mean
10 * that when the device reconnects it is connected to a different fake
11 * port.
12 *
13 * Instead, reset authenticated devices with a SetAddress(0), followed
14 * by a SetAddresss(AuthAddr).
15 *
16 * For unauthenticated devices just pretend to reset but do nothing.
17 * If the device initialization continues to fail it will eventually
18 * time out after TrustTimeout and enter the UnConnected state.
19 *
20 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
21 *
22 * Supposedly we are the only thread accesing @wusbhc->port; in any
23 * case, maybe we should move the mutex locking from
24 * wusbhc_devconnect_auth() to here.
25 *
26 * @port_idx refers to the wusbhc's port index, not the USB port number
27 */
28static int wusbhc_rh_port_reset(struct wusbhc *wusbhc, u8 port_idx)
29{
30	int result = 0;
31	struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx);
32	struct wusb_dev *wusb_dev = port->wusb_dev;
33
34	if (wusb_dev == NULL)
35		return -ENOTCONN;
36
37	port->status |= USB_PORT_STAT_RESET;
38	port->change |= USB_PORT_STAT_C_RESET;
39
40	if (wusb_dev->addr & WUSB_DEV_ADDR_UNAUTH)
41		result = 0;
42	else
43		result = wusb_dev_update_address(wusbhc, wusb_dev);
44
45	port->status &= ~USB_PORT_STAT_RESET;
46	port->status |= USB_PORT_STAT_ENABLE;
47	port->change |= USB_PORT_STAT_C_RESET | USB_PORT_STAT_C_ENABLE;
48
49	return result;
50}
51
52/*
53 * Return the hub change status bitmap
54 *
55 * The bits in the change status bitmap are cleared when a
56 * ClearPortFeature request is issued (USB2.0[11.12.3,11.12.4].
57 *
58 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
59 *
60 * WARNING!! This gets called from atomic context; we cannot get the
61 *           mutex--the only race condition we can find is some bit
62 *           changing just after we copy it, which shouldn't be too
63 *           big of a problem [and we can't make it an spinlock
64 *           because other parts need to take it and sleep] .
65 *
66 *           @usb_hcd is refcounted, so it won't dissapear under us
67 *           and before killing a host, the polling of the root hub
68 *           would be stopped anyway.
69 */
70int wusbhc_rh_status_data(struct usb_hcd *usb_hcd, char *_buf)
71{
72	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
73	size_t cnt, size;
74	unsigned long *buf = (unsigned long *) _buf;
75
76	/* WE DON'T LOCK, see comment */
77	size = wusbhc->ports_max + 1 /* hub bit */;
78	size = (size + 8 - 1) / 8;	/* round to bytes */
79	for (cnt = 0; cnt < wusbhc->ports_max; cnt++)
80		if (wusb_port_by_idx(wusbhc, cnt)->change)
81			set_bit(cnt + 1, buf);
82		else
83			clear_bit(cnt + 1, buf);
84	return size;
85}
86EXPORT_SYMBOL_GPL(wusbhc_rh_status_data);
87
88/*
89 * Return the hub's desciptor
90 *
91 * NOTE: almost cut and paste from ehci-hub.c
92 *
93 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked
94 */
95static int wusbhc_rh_get_hub_descr(struct wusbhc *wusbhc, u16 wValue,
96				   u16 wIndex,
97				   struct usb_hub_descriptor *descr,
98				   u16 wLength)
99{
100	u16 temp = 1 + (wusbhc->ports_max / 8);
101	u8 length = 7 + 2 * temp;
102
103	if (wLength < length)
104		return -ENOSPC;
105	descr->bDescLength = 7 + 2 * temp;
106	descr->bDescriptorType = 0x29;	/* HUB type */
107	descr->bNbrPorts = wusbhc->ports_max;
108	descr->wHubCharacteristics = cpu_to_le16(
109		0x00			/* All ports power at once */
110		| 0x00			/* not part of compound device */
111		| 0x10			/* No overcurrent protection */
112		| 0x00
113		| 0x00);		/* No port indicators */
114	descr->bPwrOn2PwrGood = 0;
115	descr->bHubContrCurrent = 0;
116	/* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
117	memset(&descr->bitmap[0], 0, temp);
118	memset(&descr->bitmap[temp], 0xff, temp);
119	return 0;
120}
121
122/*
123 * Clear a hub feature
124 *
125 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
126 *
127 * Nothing to do, so no locking needed ;)
128 */
129static int wusbhc_rh_clear_hub_feat(struct wusbhc *wusbhc, u16 feature)
130{
131	int result;
132
133	switch (feature) {
134	case C_HUB_LOCAL_POWER:
135	case C_HUB_OVER_CURRENT:
136		result = 0;
137		break;
138	default:
139		result = -EPIPE;
140	}
141	return result;
142}
143
144/*
145 * Return hub status (it is always zero...)
146 *
147 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
148 *
149 * Nothing to do, so no locking needed ;)
150 */
151static int wusbhc_rh_get_hub_status(struct wusbhc *wusbhc, u32 *buf,
152				    u16 wLength)
153{
154	*buf = 0;
155	return 0;
156}
157
158/*
159 * Set a port feature
160 *
161 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
162 */
163static int wusbhc_rh_set_port_feat(struct wusbhc *wusbhc, u16 feature,
164				   u8 selector, u8 port_idx)
165{
166	struct device *dev = wusbhc->dev;
167
168	if (port_idx > wusbhc->ports_max)
169		return -EINVAL;
170
171	switch (feature) {
172		/* According to USB2.0[11.24.2.13]p2, these features
173		 * are not required to be implemented. */
174	case USB_PORT_FEAT_C_OVER_CURRENT:
175	case USB_PORT_FEAT_C_ENABLE:
176	case USB_PORT_FEAT_C_SUSPEND:
177	case USB_PORT_FEAT_C_CONNECTION:
178	case USB_PORT_FEAT_C_RESET:
179		return 0;
180	case USB_PORT_FEAT_POWER:
181		/* No such thing, but we fake it works */
182		mutex_lock(&wusbhc->mutex);
183		wusb_port_by_idx(wusbhc, port_idx)->status |= USB_PORT_STAT_POWER;
184		mutex_unlock(&wusbhc->mutex);
185		return 0;
186	case USB_PORT_FEAT_RESET:
187		return wusbhc_rh_port_reset(wusbhc, port_idx);
188	case USB_PORT_FEAT_ENABLE:
189	case USB_PORT_FEAT_SUSPEND:
190		dev_err(dev, "(port_idx %d) set feat %d/%d UNIMPLEMENTED\n",
191			port_idx, feature, selector);
192		return -ENOSYS;
193	default:
194		dev_err(dev, "(port_idx %d) set feat %d/%d UNKNOWN\n",
195			port_idx, feature, selector);
196		return -EPIPE;
197	}
198
199	return 0;
200}
201
202/*
203 * Clear a port feature...
204 *
205 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
206 */
207static int wusbhc_rh_clear_port_feat(struct wusbhc *wusbhc, u16 feature,
208				     u8 selector, u8 port_idx)
209{
210	int result = 0;
211	struct device *dev = wusbhc->dev;
212
213	if (port_idx > wusbhc->ports_max)
214		return -EINVAL;
215
216	mutex_lock(&wusbhc->mutex);
217	switch (feature) {
218	case USB_PORT_FEAT_POWER:	/* fake port always on */
219		/* According to USB2.0[11.24.2.7.1.4], no need to implement? */
220	case USB_PORT_FEAT_C_OVER_CURRENT:
221		break;
222	case USB_PORT_FEAT_C_RESET:
223		wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_RESET;
224		break;
225	case USB_PORT_FEAT_C_CONNECTION:
226		wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_CONNECTION;
227		break;
228	case USB_PORT_FEAT_ENABLE:
229		__wusbhc_dev_disable(wusbhc, port_idx);
230		break;
231	case USB_PORT_FEAT_C_ENABLE:
232		wusb_port_by_idx(wusbhc, port_idx)->change &= ~USB_PORT_STAT_C_ENABLE;
233		break;
234	case USB_PORT_FEAT_SUSPEND:
235	case USB_PORT_FEAT_C_SUSPEND:
236		dev_err(dev, "(port_idx %d) Clear feat %d/%d UNIMPLEMENTED\n",
237			port_idx, feature, selector);
238		result = -ENOSYS;
239		break;
240	default:
241		dev_err(dev, "(port_idx %d) Clear feat %d/%d UNKNOWN\n",
242			port_idx, feature, selector);
243		result = -EPIPE;
244		break;
245	}
246	mutex_unlock(&wusbhc->mutex);
247
248	return result;
249}
250
251/*
252 * Return the port's status
253 *
254 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
255 */
256static int wusbhc_rh_get_port_status(struct wusbhc *wusbhc, u16 port_idx,
257				     u32 *_buf, u16 wLength)
258{
259	__le16 *buf = (__le16 *)_buf;
260
261	if (port_idx > wusbhc->ports_max)
262		return -EINVAL;
263
264	mutex_lock(&wusbhc->mutex);
265	buf[0] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->status);
266	buf[1] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->change);
267	mutex_unlock(&wusbhc->mutex);
268
269	return 0;
270}
271
272/*
273 * Entry point for Root Hub operations
274 *
275 * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
276 */
277int wusbhc_rh_control(struct usb_hcd *usb_hcd, u16 reqntype, u16 wValue,
278		      u16 wIndex, char *buf, u16 wLength)
279{
280	int result = -ENOSYS;
281	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
282
283	switch (reqntype) {
284	case GetHubDescriptor:
285		result = wusbhc_rh_get_hub_descr(
286			wusbhc, wValue, wIndex,
287			(struct usb_hub_descriptor *) buf, wLength);
288		break;
289	case ClearHubFeature:
290		result = wusbhc_rh_clear_hub_feat(wusbhc, wValue);
291		break;
292	case GetHubStatus:
293		result = wusbhc_rh_get_hub_status(wusbhc, (u32 *)buf, wLength);
294		break;
295
296	case SetPortFeature:
297		result = wusbhc_rh_set_port_feat(wusbhc, wValue, wIndex >> 8,
298						 (wIndex & 0xff) - 1);
299		break;
300	case ClearPortFeature:
301		result = wusbhc_rh_clear_port_feat(wusbhc, wValue, wIndex >> 8,
302						   (wIndex & 0xff) - 1);
303		break;
304	case GetPortStatus:
305		result = wusbhc_rh_get_port_status(wusbhc, wIndex - 1,
306						   (u32 *)buf, wLength);
307		break;
308
309	case SetHubFeature:
310	default:
311		dev_err(wusbhc->dev, "%s (%p [%p], %x, %x, %x, %p, %x) "
312			"UNIMPLEMENTED\n", __func__, usb_hcd, wusbhc, reqntype,
313			wValue, wIndex, buf, wLength);
314		/* dump_stack(); */
315		result = -ENOSYS;
316	}
317	return result;
318}
319EXPORT_SYMBOL_GPL(wusbhc_rh_control);
320
321int wusbhc_rh_suspend(struct usb_hcd *usb_hcd)
322{
323	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
324	dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__,
325		usb_hcd, wusbhc);
326	/* dump_stack(); */
327	return -ENOSYS;
328}
329EXPORT_SYMBOL_GPL(wusbhc_rh_suspend);
330
331int wusbhc_rh_resume(struct usb_hcd *usb_hcd)
332{
333	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
334	dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__,
335		usb_hcd, wusbhc);
336	/* dump_stack(); */
337	return -ENOSYS;
338}
339EXPORT_SYMBOL_GPL(wusbhc_rh_resume);
340
341int wusbhc_rh_start_port_reset(struct usb_hcd *usb_hcd, unsigned port_idx)
342{
343	struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
344	dev_err(wusbhc->dev, "%s (%p [%p], port_idx %u) UNIMPLEMENTED\n",
345		__func__, usb_hcd, wusbhc, port_idx);
346	WARN_ON(1);
347	return -ENOSYS;
348}
349EXPORT_SYMBOL_GPL(wusbhc_rh_start_port_reset);
350
351static void wusb_port_init(struct wusb_port *port)
352{
353	port->status |= USB_PORT_STAT_HIGH_SPEED;
354}
355
356/*
357 * Alloc fake port specific fields and status.
358 */
359int wusbhc_rh_create(struct wusbhc *wusbhc)
360{
361	int result = -ENOMEM;
362	size_t port_size, itr;
363	port_size = wusbhc->ports_max * sizeof(wusbhc->port[0]);
364	wusbhc->port = kzalloc(port_size, GFP_KERNEL);
365	if (wusbhc->port == NULL)
366		goto error_port_alloc;
367	for (itr = 0; itr < wusbhc->ports_max; itr++)
368		wusb_port_init(&wusbhc->port[itr]);
369	result = 0;
370error_port_alloc:
371	return result;
372}
373
374void wusbhc_rh_destroy(struct wusbhc *wusbhc)
375{
376	kfree(wusbhc->port);
377}
378