diff options
Diffstat (limited to 'drivers/usb/gadget/dummy_hcd.c')
-rw-r--r-- | drivers/usb/gadget/dummy_hcd.c | 170 |
1 files changed, 122 insertions, 48 deletions
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 0f7541be28f3..95d584dbed13 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c | |||
@@ -63,16 +63,20 @@ MODULE_LICENSE("GPL"); | |||
63 | struct dummy_hcd_module_parameters { | 63 | struct dummy_hcd_module_parameters { |
64 | bool is_super_speed; | 64 | bool is_super_speed; |
65 | bool is_high_speed; | 65 | bool is_high_speed; |
66 | unsigned int num; | ||
66 | }; | 67 | }; |
67 | 68 | ||
68 | static struct dummy_hcd_module_parameters mod_data = { | 69 | static struct dummy_hcd_module_parameters mod_data = { |
69 | .is_super_speed = false, | 70 | .is_super_speed = false, |
70 | .is_high_speed = true, | 71 | .is_high_speed = true, |
72 | .num = 1, | ||
71 | }; | 73 | }; |
72 | module_param_named(is_super_speed, mod_data.is_super_speed, bool, S_IRUGO); | 74 | module_param_named(is_super_speed, mod_data.is_super_speed, bool, S_IRUGO); |
73 | MODULE_PARM_DESC(is_super_speed, "true to simulate SuperSpeed connection"); | 75 | MODULE_PARM_DESC(is_super_speed, "true to simulate SuperSpeed connection"); |
74 | module_param_named(is_high_speed, mod_data.is_high_speed, bool, S_IRUGO); | 76 | module_param_named(is_high_speed, mod_data.is_high_speed, bool, S_IRUGO); |
75 | MODULE_PARM_DESC(is_high_speed, "true to simulate HighSpeed connection"); | 77 | MODULE_PARM_DESC(is_high_speed, "true to simulate HighSpeed connection"); |
78 | module_param_named(num, mod_data.num, uint, S_IRUGO); | ||
79 | MODULE_PARM_DESC(num, "number of emulated controllers"); | ||
76 | /*-------------------------------------------------------------------------*/ | 80 | /*-------------------------------------------------------------------------*/ |
77 | 81 | ||
78 | /* gadget side driver data structres */ | 82 | /* gadget side driver data structres */ |
@@ -238,8 +242,6 @@ static inline struct dummy *gadget_dev_to_dummy(struct device *dev) | |||
238 | return container_of(dev, struct dummy, gadget.dev); | 242 | return container_of(dev, struct dummy, gadget.dev); |
239 | } | 243 | } |
240 | 244 | ||
241 | static struct dummy the_controller; | ||
242 | |||
243 | /*-------------------------------------------------------------------------*/ | 245 | /*-------------------------------------------------------------------------*/ |
244 | 246 | ||
245 | /* SLAVE/GADGET SIDE UTILITY ROUTINES */ | 247 | /* SLAVE/GADGET SIDE UTILITY ROUTINES */ |
@@ -973,9 +975,10 @@ static void init_dummy_udc_hw(struct dummy *dum) | |||
973 | 975 | ||
974 | static int dummy_udc_probe(struct platform_device *pdev) | 976 | static int dummy_udc_probe(struct platform_device *pdev) |
975 | { | 977 | { |
976 | struct dummy *dum = &the_controller; | 978 | struct dummy *dum; |
977 | int rc; | 979 | int rc; |
978 | 980 | ||
981 | dum = *((void **)dev_get_platdata(&pdev->dev)); | ||
979 | dum->gadget.name = gadget_name; | 982 | dum->gadget.name = gadget_name; |
980 | dum->gadget.ops = &dummy_ops; | 983 | dum->gadget.ops = &dummy_ops; |
981 | dum->gadget.max_speed = USB_SPEED_SUPER; | 984 | dum->gadget.max_speed = USB_SPEED_SUPER; |
@@ -2398,10 +2401,13 @@ static int dummy_h_get_frame(struct usb_hcd *hcd) | |||
2398 | 2401 | ||
2399 | static int dummy_setup(struct usb_hcd *hcd) | 2402 | static int dummy_setup(struct usb_hcd *hcd) |
2400 | { | 2403 | { |
2404 | struct dummy *dum; | ||
2405 | |||
2406 | dum = *((void **)dev_get_platdata(hcd->self.controller)); | ||
2401 | hcd->self.sg_tablesize = ~0; | 2407 | hcd->self.sg_tablesize = ~0; |
2402 | if (usb_hcd_is_primary_hcd(hcd)) { | 2408 | if (usb_hcd_is_primary_hcd(hcd)) { |
2403 | the_controller.hs_hcd = hcd_to_dummy_hcd(hcd); | 2409 | dum->hs_hcd = hcd_to_dummy_hcd(hcd); |
2404 | the_controller.hs_hcd->dum = &the_controller; | 2410 | dum->hs_hcd->dum = dum; |
2405 | /* | 2411 | /* |
2406 | * Mark the first roothub as being USB 2.0. | 2412 | * Mark the first roothub as being USB 2.0. |
2407 | * The USB 3.0 roothub will be registered later by | 2413 | * The USB 3.0 roothub will be registered later by |
@@ -2410,8 +2416,8 @@ static int dummy_setup(struct usb_hcd *hcd) | |||
2410 | hcd->speed = HCD_USB2; | 2416 | hcd->speed = HCD_USB2; |
2411 | hcd->self.root_hub->speed = USB_SPEED_HIGH; | 2417 | hcd->self.root_hub->speed = USB_SPEED_HIGH; |
2412 | } else { | 2418 | } else { |
2413 | the_controller.ss_hcd = hcd_to_dummy_hcd(hcd); | 2419 | dum->ss_hcd = hcd_to_dummy_hcd(hcd); |
2414 | the_controller.ss_hcd->dum = &the_controller; | 2420 | dum->ss_hcd->dum = dum; |
2415 | hcd->speed = HCD_USB3; | 2421 | hcd->speed = HCD_USB3; |
2416 | hcd->self.root_hub->speed = USB_SPEED_SUPER; | 2422 | hcd->self.root_hub->speed = USB_SPEED_SUPER; |
2417 | } | 2423 | } |
@@ -2524,11 +2530,13 @@ static struct hc_driver dummy_hcd = { | |||
2524 | 2530 | ||
2525 | static int dummy_hcd_probe(struct platform_device *pdev) | 2531 | static int dummy_hcd_probe(struct platform_device *pdev) |
2526 | { | 2532 | { |
2533 | struct dummy *dum; | ||
2527 | struct usb_hcd *hs_hcd; | 2534 | struct usb_hcd *hs_hcd; |
2528 | struct usb_hcd *ss_hcd; | 2535 | struct usb_hcd *ss_hcd; |
2529 | int retval; | 2536 | int retval; |
2530 | 2537 | ||
2531 | dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc); | 2538 | dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc); |
2539 | dum = *((void **)dev_get_platdata(&pdev->dev)); | ||
2532 | 2540 | ||
2533 | if (!mod_data.is_super_speed) | 2541 | if (!mod_data.is_super_speed) |
2534 | dummy_hcd.flags = HCD_USB2; | 2542 | dummy_hcd.flags = HCD_USB2; |
@@ -2561,7 +2569,7 @@ dealloc_usb2_hcd: | |||
2561 | usb_remove_hcd(hs_hcd); | 2569 | usb_remove_hcd(hs_hcd); |
2562 | put_usb2_hcd: | 2570 | put_usb2_hcd: |
2563 | usb_put_hcd(hs_hcd); | 2571 | usb_put_hcd(hs_hcd); |
2564 | the_controller.hs_hcd = the_controller.ss_hcd = NULL; | 2572 | dum->hs_hcd = dum->ss_hcd = NULL; |
2565 | return retval; | 2573 | return retval; |
2566 | } | 2574 | } |
2567 | 2575 | ||
@@ -2579,8 +2587,8 @@ static int dummy_hcd_remove(struct platform_device *pdev) | |||
2579 | usb_remove_hcd(dummy_hcd_to_hcd(dum->hs_hcd)); | 2587 | usb_remove_hcd(dummy_hcd_to_hcd(dum->hs_hcd)); |
2580 | usb_put_hcd(dummy_hcd_to_hcd(dum->hs_hcd)); | 2588 | usb_put_hcd(dummy_hcd_to_hcd(dum->hs_hcd)); |
2581 | 2589 | ||
2582 | the_controller.hs_hcd = NULL; | 2590 | dum->hs_hcd = NULL; |
2583 | the_controller.ss_hcd = NULL; | 2591 | dum->ss_hcd = NULL; |
2584 | 2592 | ||
2585 | return 0; | 2593 | return 0; |
2586 | } | 2594 | } |
@@ -2627,13 +2635,15 @@ static struct platform_driver dummy_hcd_driver = { | |||
2627 | }; | 2635 | }; |
2628 | 2636 | ||
2629 | /*-------------------------------------------------------------------------*/ | 2637 | /*-------------------------------------------------------------------------*/ |
2630 | 2638 | #define MAX_NUM_UDC 2 | |
2631 | static struct platform_device *the_udc_pdev; | 2639 | static struct platform_device *the_udc_pdev[MAX_NUM_UDC]; |
2632 | static struct platform_device *the_hcd_pdev; | 2640 | static struct platform_device *the_hcd_pdev[MAX_NUM_UDC]; |
2633 | 2641 | ||
2634 | static int __init init(void) | 2642 | static int __init init(void) |
2635 | { | 2643 | { |
2636 | int retval = -ENOMEM; | 2644 | int retval = -ENOMEM; |
2645 | int i; | ||
2646 | struct dummy *dum[MAX_NUM_UDC]; | ||
2637 | 2647 | ||
2638 | if (usb_disabled()) | 2648 | if (usb_disabled()) |
2639 | return -ENODEV; | 2649 | return -ENODEV; |
@@ -2641,65 +2651,129 @@ static int __init init(void) | |||
2641 | if (!mod_data.is_high_speed && mod_data.is_super_speed) | 2651 | if (!mod_data.is_high_speed && mod_data.is_super_speed) |
2642 | return -EINVAL; | 2652 | return -EINVAL; |
2643 | 2653 | ||
2644 | the_hcd_pdev = platform_device_alloc(driver_name, -1); | 2654 | if (mod_data.num < 1 || mod_data.num > MAX_NUM_UDC) { |
2645 | if (!the_hcd_pdev) | 2655 | pr_err("Number of emulated UDC must be in range of 1…%d\n", |
2646 | return retval; | 2656 | MAX_NUM_UDC); |
2647 | the_udc_pdev = platform_device_alloc(gadget_name, -1); | 2657 | return -EINVAL; |
2648 | if (!the_udc_pdev) | 2658 | } |
2649 | goto err_alloc_udc; | 2659 | |
2660 | for (i = 0; i < mod_data.num; i++) { | ||
2661 | the_hcd_pdev[i] = platform_device_alloc(driver_name, i); | ||
2662 | if (!the_hcd_pdev[i]) { | ||
2663 | i--; | ||
2664 | while (i >= 0) | ||
2665 | platform_device_put(the_hcd_pdev[i--]); | ||
2666 | return retval; | ||
2667 | } | ||
2668 | } | ||
2669 | for (i = 0; i < mod_data.num; i++) { | ||
2670 | the_udc_pdev[i] = platform_device_alloc(gadget_name, i); | ||
2671 | if (!the_udc_pdev[i]) { | ||
2672 | i--; | ||
2673 | while (i >= 0) | ||
2674 | platform_device_put(the_udc_pdev[i--]); | ||
2675 | goto err_alloc_udc; | ||
2676 | } | ||
2677 | } | ||
2678 | for (i = 0; i < mod_data.num; i++) { | ||
2679 | dum[i] = kzalloc(sizeof(struct dummy), GFP_KERNEL); | ||
2680 | if (!dum[i]) | ||
2681 | goto err_add_pdata; | ||
2682 | retval = platform_device_add_data(the_hcd_pdev[i], &dum[i], | ||
2683 | sizeof(void *)); | ||
2684 | if (retval) | ||
2685 | goto err_add_pdata; | ||
2686 | retval = platform_device_add_data(the_udc_pdev[i], &dum[i], | ||
2687 | sizeof(void *)); | ||
2688 | if (retval) | ||
2689 | goto err_add_pdata; | ||
2690 | } | ||
2650 | 2691 | ||
2651 | retval = platform_driver_register(&dummy_hcd_driver); | 2692 | retval = platform_driver_register(&dummy_hcd_driver); |
2652 | if (retval < 0) | 2693 | if (retval < 0) |
2653 | goto err_register_hcd_driver; | 2694 | goto err_add_pdata; |
2654 | retval = platform_driver_register(&dummy_udc_driver); | 2695 | retval = platform_driver_register(&dummy_udc_driver); |
2655 | if (retval < 0) | 2696 | if (retval < 0) |
2656 | goto err_register_udc_driver; | 2697 | goto err_register_udc_driver; |
2657 | 2698 | ||
2658 | retval = platform_device_add(the_hcd_pdev); | 2699 | for (i = 0; i < mod_data.num; i++) { |
2659 | if (retval < 0) | 2700 | retval = platform_device_add(the_hcd_pdev[i]); |
2660 | goto err_add_hcd; | 2701 | if (retval < 0) { |
2661 | if (!the_controller.hs_hcd || | 2702 | i--; |
2662 | (!the_controller.ss_hcd && mod_data.is_super_speed)) { | 2703 | while (i >= 0) |
2663 | /* | 2704 | platform_device_del(the_hcd_pdev[i--]); |
2664 | * The hcd was added successfully but its probe function failed | 2705 | goto err_add_hcd; |
2665 | * for some reason. | 2706 | } |
2666 | */ | ||
2667 | retval = -EINVAL; | ||
2668 | goto err_add_udc; | ||
2669 | } | 2707 | } |
2670 | retval = platform_device_add(the_udc_pdev); | 2708 | for (i = 0; i < mod_data.num; i++) { |
2671 | if (retval < 0) | 2709 | if (!dum[i]->hs_hcd || |
2672 | goto err_add_udc; | 2710 | (!dum[i]->ss_hcd && mod_data.is_super_speed)) { |
2673 | if (!platform_get_drvdata(the_udc_pdev)) { | 2711 | /* |
2674 | /* | 2712 | * The hcd was added successfully but its probe |
2675 | * The udc was added successfully but its probe function failed | 2713 | * function failed for some reason. |
2676 | * for some reason. | 2714 | */ |
2677 | */ | 2715 | retval = -EINVAL; |
2678 | retval = -EINVAL; | 2716 | goto err_add_udc; |
2679 | goto err_probe_udc; | 2717 | } |
2718 | } | ||
2719 | |||
2720 | for (i = 0; i < mod_data.num; i++) { | ||
2721 | retval = platform_device_add(the_udc_pdev[i]); | ||
2722 | if (retval < 0) { | ||
2723 | i--; | ||
2724 | while (i >= 0) | ||
2725 | platform_device_del(the_udc_pdev[i]); | ||
2726 | goto err_add_udc; | ||
2727 | } | ||
2728 | } | ||
2729 | |||
2730 | for (i = 0; i < mod_data.num; i++) { | ||
2731 | if (!platform_get_drvdata(the_udc_pdev[i])) { | ||
2732 | /* | ||
2733 | * The udc was added successfully but its probe | ||
2734 | * function failed for some reason. | ||
2735 | */ | ||
2736 | retval = -EINVAL; | ||
2737 | goto err_probe_udc; | ||
2738 | } | ||
2680 | } | 2739 | } |
2681 | return retval; | 2740 | return retval; |
2682 | 2741 | ||
2683 | err_probe_udc: | 2742 | err_probe_udc: |
2684 | platform_device_del(the_udc_pdev); | 2743 | for (i = 0; i < mod_data.num; i++) |
2744 | platform_device_del(the_udc_pdev[i]); | ||
2685 | err_add_udc: | 2745 | err_add_udc: |
2686 | platform_device_del(the_hcd_pdev); | 2746 | for (i = 0; i < mod_data.num; i++) |
2747 | platform_device_del(the_hcd_pdev[i]); | ||
2687 | err_add_hcd: | 2748 | err_add_hcd: |
2688 | platform_driver_unregister(&dummy_udc_driver); | 2749 | platform_driver_unregister(&dummy_udc_driver); |
2689 | err_register_udc_driver: | 2750 | err_register_udc_driver: |
2690 | platform_driver_unregister(&dummy_hcd_driver); | 2751 | platform_driver_unregister(&dummy_hcd_driver); |
2691 | err_register_hcd_driver: | 2752 | err_add_pdata: |
2692 | platform_device_put(the_udc_pdev); | 2753 | for (i = 0; i < mod_data.num; i++) |
2754 | kfree(dum[i]); | ||
2755 | for (i = 0; i < mod_data.num; i++) | ||
2756 | platform_device_put(the_udc_pdev[i]); | ||
2693 | err_alloc_udc: | 2757 | err_alloc_udc: |
2694 | platform_device_put(the_hcd_pdev); | 2758 | for (i = 0; i < mod_data.num; i++) |
2759 | platform_device_put(the_hcd_pdev[i]); | ||
2695 | return retval; | 2760 | return retval; |
2696 | } | 2761 | } |
2697 | module_init(init); | 2762 | module_init(init); |
2698 | 2763 | ||
2699 | static void __exit cleanup(void) | 2764 | static void __exit cleanup(void) |
2700 | { | 2765 | { |
2701 | platform_device_unregister(the_udc_pdev); | 2766 | int i; |
2702 | platform_device_unregister(the_hcd_pdev); | 2767 | |
2768 | for (i = 0; i < mod_data.num; i++) { | ||
2769 | struct dummy *dum; | ||
2770 | |||
2771 | dum = *((void **)dev_get_platdata(&the_udc_pdev[i]->dev)); | ||
2772 | |||
2773 | platform_device_unregister(the_udc_pdev[i]); | ||
2774 | platform_device_unregister(the_hcd_pdev[i]); | ||
2775 | kfree(dum); | ||
2776 | } | ||
2703 | platform_driver_unregister(&dummy_udc_driver); | 2777 | platform_driver_unregister(&dummy_udc_driver); |
2704 | platform_driver_unregister(&dummy_hcd_driver); | 2778 | platform_driver_unregister(&dummy_hcd_driver); |
2705 | } | 2779 | } |