diff options
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r-- | drivers/usb/core/hub.c | 27 |
1 files changed, 21 insertions, 6 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 9dca59ef18b3..b8bb20d7acdb 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -362,7 +362,8 @@ static void usb_set_lpm_parameters(struct usb_device *udev) | |||
362 | } | 362 | } |
363 | 363 | ||
364 | /* USB 2.0 spec Section 11.24.4.5 */ | 364 | /* USB 2.0 spec Section 11.24.4.5 */ |
365 | static int get_hub_descriptor(struct usb_device *hdev, void *data) | 365 | static int get_hub_descriptor(struct usb_device *hdev, |
366 | struct usb_hub_descriptor *desc) | ||
366 | { | 367 | { |
367 | int i, ret, size; | 368 | int i, ret, size; |
368 | unsigned dtype; | 369 | unsigned dtype; |
@@ -378,10 +379,18 @@ static int get_hub_descriptor(struct usb_device *hdev, void *data) | |||
378 | for (i = 0; i < 3; i++) { | 379 | for (i = 0; i < 3; i++) { |
379 | ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), | 380 | ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), |
380 | USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, | 381 | USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, |
381 | dtype << 8, 0, data, size, | 382 | dtype << 8, 0, desc, size, |
382 | USB_CTRL_GET_TIMEOUT); | 383 | USB_CTRL_GET_TIMEOUT); |
383 | if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2)) | 384 | if (hub_is_superspeed(hdev)) { |
385 | if (ret == size) | ||
386 | return ret; | ||
387 | } else if (ret >= USB_DT_HUB_NONVAR_SIZE + 2) { | ||
388 | /* Make sure we have the DeviceRemovable field. */ | ||
389 | size = USB_DT_HUB_NONVAR_SIZE + desc->bNbrPorts / 8 + 1; | ||
390 | if (ret < size) | ||
391 | return -EMSGSIZE; | ||
384 | return ret; | 392 | return ret; |
393 | } | ||
385 | } | 394 | } |
386 | return -EINVAL; | 395 | return -EINVAL; |
387 | } | 396 | } |
@@ -1313,7 +1322,7 @@ static int hub_configure(struct usb_hub *hub, | |||
1313 | } | 1322 | } |
1314 | mutex_init(&hub->status_mutex); | 1323 | mutex_init(&hub->status_mutex); |
1315 | 1324 | ||
1316 | hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL); | 1325 | hub->descriptor = kzalloc(sizeof(*hub->descriptor), GFP_KERNEL); |
1317 | if (!hub->descriptor) { | 1326 | if (!hub->descriptor) { |
1318 | ret = -ENOMEM; | 1327 | ret = -ENOMEM; |
1319 | goto fail; | 1328 | goto fail; |
@@ -1321,13 +1330,19 @@ static int hub_configure(struct usb_hub *hub, | |||
1321 | 1330 | ||
1322 | /* Request the entire hub descriptor. | 1331 | /* Request the entire hub descriptor. |
1323 | * hub->descriptor can handle USB_MAXCHILDREN ports, | 1332 | * hub->descriptor can handle USB_MAXCHILDREN ports, |
1324 | * but the hub can/will return fewer bytes here. | 1333 | * but a (non-SS) hub can/will return fewer bytes here. |
1325 | */ | 1334 | */ |
1326 | ret = get_hub_descriptor(hdev, hub->descriptor); | 1335 | ret = get_hub_descriptor(hdev, hub->descriptor); |
1327 | if (ret < 0) { | 1336 | if (ret < 0) { |
1328 | message = "can't read hub descriptor"; | 1337 | message = "can't read hub descriptor"; |
1329 | goto fail; | 1338 | goto fail; |
1330 | } else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) { | 1339 | } |
1340 | |||
1341 | maxchild = USB_MAXCHILDREN; | ||
1342 | if (hub_is_superspeed(hdev)) | ||
1343 | maxchild = min_t(unsigned, maxchild, USB_SS_MAXPORTS); | ||
1344 | |||
1345 | if (hub->descriptor->bNbrPorts > maxchild) { | ||
1331 | message = "hub has too many ports!"; | 1346 | message = "hub has too many ports!"; |
1332 | ret = -ENODEV; | 1347 | ret = -ENODEV; |
1333 | goto fail; | 1348 | goto fail; |