Skip to content

Commit caf4916

Browse files
xiaoguangwulijinxia
authored andcommitted
DM USB: xHCI: Support control transfer for USB port mapper.
This patch implements the control transfer for port mapper. With this patch, USB2.0 device can be enumerated successfully in user OS. Change-Id: I567bd00ca310d68375acd94a5cc5bcd287665df1 Signed-off-by: Wu, Xiaoguang <xiaoguang.wu@intel.com> Reviewed-by: Shuo Liu <shuo.a.liu@intel.com> Reviewed-by: Yu Wang <yu1.wang@intel.com> Reviewed-by: Zhao Yakui <yakui.zhao@intel.com> Acked-by: Eddie Dong <eddie.dong@intel.com>
1 parent b12527f commit caf4916

File tree

4 files changed

+377
-3
lines changed

4 files changed

+377
-3
lines changed

devicemodel/hw/pci/xhci.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -427,10 +427,10 @@ pci_xhci_dev_create(struct pci_xhci_vdev *xdev, void *dev_data)
427427

428428
/* TODO: following function pointers will be populated in future */
429429
ue->ue_init = usb_dev_init;
430-
ue->ue_request = NULL;
430+
ue->ue_request = usb_dev_request;
431431
ue->ue_data = NULL;
432432
ue->ue_info = usb_dev_info;
433-
ue->ue_reset = NULL;
433+
ue->ue_reset = usb_dev_reset;
434434
ue->ue_remove = NULL;
435435
ue->ue_stop = NULL;
436436
ue->ue_deinit = usb_dev_deinit;

devicemodel/hw/platform/usb_pmapper.c

Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,129 @@ usb_dev_err_convert(int err)
5959
return USB_ERR_IOERROR;
6060
}
6161

62+
static inline struct usb_dev_ep *
63+
usb_dev_get_ep(struct usb_dev *udev, int pid, int ep)
64+
{
65+
assert(udev);
66+
67+
if (ep < 0 || ep >= USB_NUM_ENDPOINT) {
68+
UPRINTF(LWRN, "invalid ep %d\r\n", ep);
69+
return NULL;
70+
}
71+
72+
if (ep == 0)
73+
return &udev->epc;
74+
75+
if (pid == TOKEN_IN)
76+
return udev->epi + ep - 1;
77+
else
78+
return udev->epo + ep - 1;
79+
}
80+
81+
static inline void
82+
usb_dev_set_ep_type(struct usb_dev *udev, int pid, int epnum,
83+
uint8_t type)
84+
{
85+
struct usb_dev_ep *ep;
86+
87+
ep = usb_dev_get_ep(udev, pid, epnum);
88+
if (ep)
89+
ep->type = type;
90+
}
91+
92+
static inline uint8_t
93+
usb_dev_get_ep_type(struct usb_dev *udev, int pid, int epnum)
94+
{
95+
struct usb_dev_ep *ep;
96+
97+
ep = usb_dev_get_ep(udev, pid, epnum);
98+
if (!ep)
99+
return USB_EP_ERR_TYPE;
100+
else
101+
return ep->type;
102+
}
103+
104+
static void
105+
usb_dev_reset_ep(struct usb_dev *udev)
106+
{
107+
int ep;
108+
109+
udev->epc.type = USB_ENDPOINT_CONTROL;
110+
for (ep = 0; ep < USB_NUM_ENDPOINT; ep++) {
111+
udev->epi[ep].pid = TOKEN_IN;
112+
udev->epo[ep].pid = TOKEN_OUT;
113+
udev->epi[ep].type = USB_ENDPOINT_INVALID;
114+
udev->epo[ep].type = USB_ENDPOINT_INVALID;
115+
}
116+
}
117+
118+
static void
119+
usb_dev_update_ep(struct usb_dev *udev)
120+
{
121+
struct libusb_config_descriptor *cfg;
122+
const struct libusb_interface_descriptor *_if;
123+
const struct libusb_endpoint_descriptor *desc;
124+
int i, j;
125+
126+
assert(udev);
127+
if (libusb_get_active_config_descriptor(udev->ldev, &cfg))
128+
return;
129+
130+
for (i = 0; i < cfg->bNumInterfaces; i++) {
131+
_if = &cfg->interface[i].altsetting[udev->alts[i]];
132+
133+
for (j = 0; j < _if->bNumEndpoints; j++) {
134+
desc = &_if->endpoint[j];
135+
usb_dev_set_ep_type(udev,
136+
USB_EP_PID(desc),
137+
USB_EP_NR(desc),
138+
USB_EP_TYPE(desc));
139+
}
140+
}
141+
libusb_free_config_descriptor(cfg);
142+
}
143+
144+
static int
145+
usb_dev_native_toggle_if(struct usb_dev *udev, int claim)
146+
{
147+
struct libusb_config_descriptor *config;
148+
uint8_t b, p, c, i;
149+
int rc = 0, r;
150+
151+
assert(udev);
152+
assert(udev->handle);
153+
assert(udev->ldev);
154+
assert(claim == 1 || claim == 0);
155+
156+
b = udev->bus;
157+
p = udev->port;
158+
159+
r = libusb_get_active_config_descriptor(udev->ldev, &config);
160+
if (r) {
161+
UPRINTF(LWRN, "%d-%d: can't get config\r\n", b, p);
162+
return -1;
163+
}
164+
165+
c = config->bConfigurationValue;
166+
for (i = 0; i < config->bNumInterfaces; i++) {
167+
if (claim == 1)
168+
r = libusb_claim_interface(udev->handle, i);
169+
else
170+
r = libusb_release_interface(udev->handle, i);
171+
172+
if (r) {
173+
rc = -1;
174+
UPRINTF(LWRN, "%d-%d:%d.%d can't %s if\r\n", b, p, c, i,
175+
claim == 1 ? "claim" : "release");
176+
}
177+
}
178+
if (rc)
179+
UPRINTF(LWRN, "%d-%d fail to %s rc %d\r\n", b, p,
180+
claim == 1 ? "claim" : "release", rc);
181+
libusb_free_config_descriptor(config);
182+
return rc;
183+
}
184+
62185
static int
63186
usb_dev_native_toggle_if_drivers(struct usb_dev *udev, int attach)
64187
{
@@ -100,6 +223,220 @@ usb_dev_native_toggle_if_drivers(struct usb_dev *udev, int attach)
100223
return rc;
101224
}
102225

226+
static void
227+
usb_dev_set_config(struct usb_dev *udev, struct usb_data_xfer *xfer, int config)
228+
{
229+
int rc = 0;
230+
struct libusb_config_descriptor *cfg;
231+
232+
assert(udev);
233+
assert(udev->ldev);
234+
assert(udev->handle);
235+
236+
/*
237+
* set configuration
238+
* according to the libusb doc, the detach and release work
239+
* should be done before set configuration.
240+
*/
241+
usb_dev_native_toggle_if_drivers(udev, 0);
242+
usb_dev_native_toggle_if(udev, 0);
243+
244+
rc = libusb_set_configuration(udev->handle, config);
245+
if (rc) {
246+
UPRINTF(LWRN, "fail to set config rc %d\r\n", rc);
247+
goto err2;
248+
}
249+
250+
/* claim all the interfaces of this configuration */
251+
rc = libusb_get_active_config_descriptor(udev->ldev, &cfg);
252+
if (rc) {
253+
UPRINTF(LWRN, "fail to get config rc %d\r\n", rc);
254+
goto err2;
255+
}
256+
257+
rc = usb_dev_native_toggle_if(udev, 1);
258+
if (rc) {
259+
UPRINTF(LWRN, "fail to claim if, rc %d\r\n", rc);
260+
goto err1;
261+
}
262+
263+
udev->if_num = cfg->bNumInterfaces;
264+
udev->configuration = config;
265+
266+
usb_dev_reset_ep(udev);
267+
usb_dev_update_ep(udev);
268+
libusb_free_config_descriptor(cfg);
269+
return;
270+
271+
err1:
272+
usb_dev_native_toggle_if(udev, 0);
273+
libusb_free_config_descriptor(cfg);
274+
err2:
275+
UPRINTF(LWRN, "%d-%d: fail to set config\r\n", udev->bus, udev->port);
276+
xfer->status = USB_ERR_STALLED;
277+
}
278+
279+
static void
280+
usb_dev_set_if(struct usb_dev *udev, int iface, int alt, struct usb_data_xfer
281+
*xfer)
282+
{
283+
assert(udev);
284+
assert(xfer);
285+
assert(udev->handle);
286+
287+
if (iface >= USB_NUM_INTERFACE)
288+
goto errout;
289+
290+
UPRINTF(LDBG, "%d-%d set if, iface %d alt %d\r\n", udev->bus,
291+
udev->port, iface, alt);
292+
293+
if (libusb_set_interface_alt_setting(udev->handle, iface, alt))
294+
goto errout;
295+
296+
udev->alts[iface] = alt;
297+
/*
298+
* FIXME: Only support single interface USB device first. Need fix in
299+
* future to support composite USB device.
300+
*/
301+
usb_dev_reset_ep(udev);
302+
usb_dev_update_ep(udev);
303+
return;
304+
305+
errout:
306+
xfer->status = USB_ERR_STALLED;
307+
UPRINTF(LDBG, "%d-%d fail to set if, iface %d alt %d\r\n",
308+
udev->bus, udev->port, iface, alt);
309+
}
310+
311+
static struct usb_data_xfer_block *
312+
usb_dev_prepare_ctrl_xfer(struct usb_data_xfer *xfer)
313+
{
314+
int i, idx;
315+
struct usb_data_xfer_block *ret = NULL;
316+
struct usb_data_xfer_block *blk = NULL;
317+
318+
idx = xfer->head;
319+
for (i = 0; i < xfer->ndata; i++) {
320+
/*
321+
* find out the data block and set every
322+
* block to be processed
323+
*/
324+
blk = &xfer->data[idx];
325+
if (blk->blen > 0 && !ret)
326+
ret = blk;
327+
328+
blk->processed = 1;
329+
idx = (idx + 1) % USB_MAX_XFER_BLOCKS;
330+
}
331+
return ret;
332+
}
333+
334+
int
335+
usb_dev_reset(void *pdata)
336+
{
337+
struct usb_dev *udev;
338+
339+
udev = pdata;
340+
assert(udev);
341+
342+
UPRINTF(LDBG, "reset endpoints\n");
343+
libusb_reset_device(udev->handle);
344+
usb_dev_reset_ep(udev);
345+
usb_dev_update_ep(udev);
346+
return 0;
347+
}
348+
349+
int
350+
usb_dev_request(void *pdata, struct usb_data_xfer *xfer)
351+
{
352+
struct usb_dev *udev;
353+
uint8_t request_type;
354+
uint8_t request;
355+
uint16_t value;
356+
uint16_t index;
357+
uint16_t len;
358+
struct usb_data_xfer_block *blk;
359+
uint8_t *data;
360+
int rc;
361+
362+
udev = pdata;
363+
364+
assert(xfer);
365+
assert(udev);
366+
367+
xfer->status = USB_ERR_NORMAL_COMPLETION;
368+
if (!udev->ldev || !xfer->ureq) {
369+
UPRINTF(LWRN, "invalid request\r\n");
370+
xfer->status = USB_ERR_IOERROR;
371+
goto out;
372+
}
373+
374+
request_type = xfer->ureq->bmRequestType;
375+
request = xfer->ureq->bRequest;
376+
value = xfer->ureq->wValue;
377+
index = xfer->ureq->wIndex;
378+
len = xfer->ureq->wLength;
379+
380+
blk = usb_dev_prepare_ctrl_xfer(xfer);
381+
data = blk ? blk->buf : NULL;
382+
383+
UPRINTF(LDBG,
384+
"urb: type 0x%x req 0x%x val 0x%x idx %d len %d data %d\n",
385+
request_type, request, value, index, len,
386+
blk ? blk->blen : 0);
387+
388+
/*
389+
* according to usb spec, control transfer may have no
390+
* DATA STAGE, so the valid situations are:
391+
* a. with DATA STAGE: blk != NULL && len > 0
392+
* b. without DATA STAGE: blk == NULL && len == 0
393+
* any other situations, just skip process
394+
*/
395+
if ((!blk && len > 0) || (blk && len <= 0))
396+
goto out;
397+
398+
switch (UREQ(request, request_type)) {
399+
case UREQ(UR_SET_ADDRESS, UT_READ_DEVICE):
400+
UPRINTF(LDBG, "UR_SET_ADDRESS\n");
401+
udev->addr = value;
402+
goto out;
403+
case UREQ(UR_SET_CONFIG, UT_READ_DEVICE):
404+
UPRINTF(LDBG, "UR_SET_CONFIG\n");
405+
usb_dev_set_config(udev, xfer, value & 0xff);
406+
goto out;
407+
case UREQ(UR_SET_INTERFACE, UT_READ_INTERFACE):
408+
UPRINTF(LDBG, "UR_SET_INTERFACE\n");
409+
usb_dev_set_if(udev, index, value, xfer);
410+
goto out;
411+
case UREQ(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
412+
if (value == 0) {
413+
UPRINTF(LDBG, "UR_CLEAR_HALT\n");
414+
libusb_clear_halt(udev->handle, index);
415+
goto out;
416+
}
417+
}
418+
419+
/* send it to physical device */
420+
/* TODO: should this be async operation? */
421+
rc = libusb_control_transfer(udev->handle, request_type, request,
422+
value, index, data, len, 100);
423+
424+
if (rc >= 0 && blk) {
425+
blk->blen = len - rc;
426+
blk->bdone += rc;
427+
xfer->status = blk->blen > 0 ? USB_ERR_SHORT_XFER :
428+
USB_ERR_NORMAL_COMPLETION;
429+
} else if (rc >= 0)
430+
xfer->status = USB_ERR_NORMAL_COMPLETION;
431+
else
432+
xfer->status = usb_dev_err_convert(rc);
433+
434+
UPRINTF(LDBG, "usb rc %d, blk %p, blen %u bdon %u\n", rc, blk,
435+
blk ? blk->blen : 0, blk ? blk->bdone : 0);
436+
out:
437+
return xfer->status;
438+
}
439+
103440
void *
104441
usb_dev_init(void *pdata, char *opt)
105442
{

0 commit comments

Comments
 (0)