diff options
Diffstat (limited to 'drivers/usb/gadget/f_mass_storage.c')
-rw-r--r-- | drivers/usb/gadget/f_mass_storage.c | 138 |
1 files changed, 104 insertions, 34 deletions
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index f4911c09022e..7d05a0be5c60 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c | |||
@@ -163,6 +163,10 @@ | |||
163 | * ro setting are not allowed when the medium is loaded or if CD-ROM | 163 | * ro setting are not allowed when the medium is loaded or if CD-ROM |
164 | * emulation is being used. | 164 | * emulation is being used. |
165 | * | 165 | * |
166 | * When a LUN receive an "eject" SCSI request (Start/Stop Unit), | ||
167 | * if the LUN is removable, the backing file is released to simulate | ||
168 | * ejection. | ||
169 | * | ||
166 | * | 170 | * |
167 | * This function is heavily based on "File-backed Storage Gadget" by | 171 | * This function is heavily based on "File-backed Storage Gadget" by |
168 | * Alan Stern which in turn is heavily based on "Gadget Zero" by David | 172 | * Alan Stern which in turn is heavily based on "Gadget Zero" by David |
@@ -302,7 +306,6 @@ static const char fsg_string_interface[] = "Mass Storage"; | |||
302 | 306 | ||
303 | 307 | ||
304 | #define FSG_NO_INTR_EP 1 | 308 | #define FSG_NO_INTR_EP 1 |
305 | #define FSG_BUFFHD_STATIC_BUFFER 1 | ||
306 | #define FSG_NO_DEVICE_STRINGS 1 | 309 | #define FSG_NO_DEVICE_STRINGS 1 |
307 | #define FSG_NO_OTG 1 | 310 | #define FSG_NO_OTG 1 |
308 | #define FSG_NO_INTR_EP 1 | 311 | #define FSG_NO_INTR_EP 1 |
@@ -1385,12 +1388,50 @@ static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh) | |||
1385 | 1388 | ||
1386 | static int do_start_stop(struct fsg_common *common) | 1389 | static int do_start_stop(struct fsg_common *common) |
1387 | { | 1390 | { |
1388 | if (!common->curlun) { | 1391 | struct fsg_lun *curlun = common->curlun; |
1392 | int loej, start; | ||
1393 | |||
1394 | if (!curlun) { | ||
1389 | return -EINVAL; | 1395 | return -EINVAL; |
1390 | } else if (!common->curlun->removable) { | 1396 | } else if (!curlun->removable) { |
1391 | common->curlun->sense_data = SS_INVALID_COMMAND; | 1397 | curlun->sense_data = SS_INVALID_COMMAND; |
1392 | return -EINVAL; | 1398 | return -EINVAL; |
1393 | } | 1399 | } |
1400 | |||
1401 | loej = common->cmnd[4] & 0x02; | ||
1402 | start = common->cmnd[4] & 0x01; | ||
1403 | |||
1404 | /* eject code from file_storage.c:do_start_stop() */ | ||
1405 | |||
1406 | if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */ | ||
1407 | (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */ | ||
1408 | curlun->sense_data = SS_INVALID_FIELD_IN_CDB; | ||
1409 | return -EINVAL; | ||
1410 | } | ||
1411 | |||
1412 | if (!start) { | ||
1413 | /* Are we allowed to unload the media? */ | ||
1414 | if (curlun->prevent_medium_removal) { | ||
1415 | LDBG(curlun, "unload attempt prevented\n"); | ||
1416 | curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED; | ||
1417 | return -EINVAL; | ||
1418 | } | ||
1419 | if (loej) { /* Simulate an unload/eject */ | ||
1420 | up_read(&common->filesem); | ||
1421 | down_write(&common->filesem); | ||
1422 | fsg_lun_close(curlun); | ||
1423 | up_write(&common->filesem); | ||
1424 | down_read(&common->filesem); | ||
1425 | } | ||
1426 | } else { | ||
1427 | |||
1428 | /* Our emulation doesn't support mounting; the medium is | ||
1429 | * available for use as soon as it is loaded. */ | ||
1430 | if (!fsg_lun_is_open(curlun)) { | ||
1431 | curlun->sense_data = SS_MEDIUM_NOT_PRESENT; | ||
1432 | return -EINVAL; | ||
1433 | } | ||
1434 | } | ||
1394 | return 0; | 1435 | return 0; |
1395 | } | 1436 | } |
1396 | 1437 | ||
@@ -2701,10 +2742,8 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, | |||
2701 | /* Maybe allocate device-global string IDs, and patch descriptors */ | 2742 | /* Maybe allocate device-global string IDs, and patch descriptors */ |
2702 | if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { | 2743 | if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { |
2703 | rc = usb_string_id(cdev); | 2744 | rc = usb_string_id(cdev); |
2704 | if (rc < 0) { | 2745 | if (unlikely(rc < 0)) |
2705 | kfree(common); | 2746 | goto error_release; |
2706 | return ERR_PTR(rc); | ||
2707 | } | ||
2708 | fsg_strings[FSG_STRING_INTERFACE].id = rc; | 2747 | fsg_strings[FSG_STRING_INTERFACE].id = rc; |
2709 | fsg_intf_desc.iInterface = rc; | 2748 | fsg_intf_desc.iInterface = rc; |
2710 | } | 2749 | } |
@@ -2712,9 +2751,9 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, | |||
2712 | /* Create the LUNs, open their backing files, and register the | 2751 | /* Create the LUNs, open their backing files, and register the |
2713 | * LUN devices in sysfs. */ | 2752 | * LUN devices in sysfs. */ |
2714 | curlun = kzalloc(nluns * sizeof *curlun, GFP_KERNEL); | 2753 | curlun = kzalloc(nluns * sizeof *curlun, GFP_KERNEL); |
2715 | if (!curlun) { | 2754 | if (unlikely(!curlun)) { |
2716 | kfree(common); | 2755 | rc = -ENOMEM; |
2717 | return ERR_PTR(-ENOMEM); | 2756 | goto error_release; |
2718 | } | 2757 | } |
2719 | common->luns = curlun; | 2758 | common->luns = curlun; |
2720 | 2759 | ||
@@ -2762,13 +2801,19 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, | |||
2762 | 2801 | ||
2763 | 2802 | ||
2764 | /* Data buffers cyclic list */ | 2803 | /* Data buffers cyclic list */ |
2765 | /* Buffers in buffhds are static -- no need for additional | ||
2766 | * allocation. */ | ||
2767 | bh = common->buffhds; | 2804 | bh = common->buffhds; |
2768 | i = FSG_NUM_BUFFERS - 1; | 2805 | i = FSG_NUM_BUFFERS; |
2806 | goto buffhds_first_it; | ||
2769 | do { | 2807 | do { |
2770 | bh->next = bh + 1; | 2808 | bh->next = bh + 1; |
2771 | } while (++bh, --i); | 2809 | ++bh; |
2810 | buffhds_first_it: | ||
2811 | bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL); | ||
2812 | if (unlikely(!bh->buf)) { | ||
2813 | rc = -ENOMEM; | ||
2814 | goto error_release; | ||
2815 | } | ||
2816 | } while (--i); | ||
2772 | bh->next = common->buffhds; | 2817 | bh->next = common->buffhds; |
2773 | 2818 | ||
2774 | 2819 | ||
@@ -2867,10 +2912,7 @@ error_release: | |||
2867 | 2912 | ||
2868 | static void fsg_common_release(struct kref *ref) | 2913 | static void fsg_common_release(struct kref *ref) |
2869 | { | 2914 | { |
2870 | struct fsg_common *common = | 2915 | struct fsg_common *common = container_of(ref, struct fsg_common, ref); |
2871 | container_of(ref, struct fsg_common, ref); | ||
2872 | unsigned i = common->nluns; | ||
2873 | struct fsg_lun *lun = common->luns; | ||
2874 | 2916 | ||
2875 | /* If the thread isn't already dead, tell it to exit now */ | 2917 | /* If the thread isn't already dead, tell it to exit now */ |
2876 | if (common->state != FSG_STATE_TERMINATED) { | 2918 | if (common->state != FSG_STATE_TERMINATED) { |
@@ -2881,17 +2923,29 @@ static void fsg_common_release(struct kref *ref) | |||
2881 | complete(&common->thread_notifier); | 2923 | complete(&common->thread_notifier); |
2882 | } | 2924 | } |
2883 | 2925 | ||
2884 | /* Beware tempting for -> do-while optimization: when in error | 2926 | if (likely(common->luns)) { |
2885 | * recovery nluns may be zero. */ | 2927 | struct fsg_lun *lun = common->luns; |
2928 | unsigned i = common->nluns; | ||
2929 | |||
2930 | /* In error recovery common->nluns may be zero. */ | ||
2931 | for (; i; --i, ++lun) { | ||
2932 | device_remove_file(&lun->dev, &dev_attr_ro); | ||
2933 | device_remove_file(&lun->dev, &dev_attr_file); | ||
2934 | fsg_lun_close(lun); | ||
2935 | device_unregister(&lun->dev); | ||
2936 | } | ||
2937 | |||
2938 | kfree(common->luns); | ||
2939 | } | ||
2886 | 2940 | ||
2887 | for (; i; --i, ++lun) { | 2941 | { |
2888 | device_remove_file(&lun->dev, &dev_attr_ro); | 2942 | struct fsg_buffhd *bh = common->buffhds; |
2889 | device_remove_file(&lun->dev, &dev_attr_file); | 2943 | unsigned i = FSG_NUM_BUFFERS; |
2890 | fsg_lun_close(lun); | 2944 | do { |
2891 | device_unregister(&lun->dev); | 2945 | kfree(bh->buf); |
2946 | } while (++bh, --i); | ||
2892 | } | 2947 | } |
2893 | 2948 | ||
2894 | kfree(common->luns); | ||
2895 | if (common->free_storage_on_release) | 2949 | if (common->free_storage_on_release) |
2896 | kfree(common); | 2950 | kfree(common); |
2897 | } | 2951 | } |
@@ -2906,11 +2960,13 @@ static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) | |||
2906 | 2960 | ||
2907 | DBG(fsg, "unbind\n"); | 2961 | DBG(fsg, "unbind\n"); |
2908 | fsg_common_put(fsg->common); | 2962 | fsg_common_put(fsg->common); |
2963 | usb_free_descriptors(fsg->function.descriptors); | ||
2964 | usb_free_descriptors(fsg->function.hs_descriptors); | ||
2909 | kfree(fsg); | 2965 | kfree(fsg); |
2910 | } | 2966 | } |
2911 | 2967 | ||
2912 | 2968 | ||
2913 | static int __init fsg_bind(struct usb_configuration *c, struct usb_function *f) | 2969 | static int fsg_bind(struct usb_configuration *c, struct usb_function *f) |
2914 | { | 2970 | { |
2915 | struct fsg_dev *fsg = fsg_from_func(f); | 2971 | struct fsg_dev *fsg = fsg_from_func(f); |
2916 | struct usb_gadget *gadget = c->cdev->gadget; | 2972 | struct usb_gadget *gadget = c->cdev->gadget; |
@@ -2946,7 +3002,9 @@ static int __init fsg_bind(struct usb_configuration *c, struct usb_function *f) | |||
2946 | fsg_fs_bulk_in_desc.bEndpointAddress; | 3002 | fsg_fs_bulk_in_desc.bEndpointAddress; |
2947 | fsg_hs_bulk_out_desc.bEndpointAddress = | 3003 | fsg_hs_bulk_out_desc.bEndpointAddress = |
2948 | fsg_fs_bulk_out_desc.bEndpointAddress; | 3004 | fsg_fs_bulk_out_desc.bEndpointAddress; |
2949 | f->hs_descriptors = fsg_hs_function; | 3005 | f->hs_descriptors = usb_copy_descriptors(fsg_hs_function); |
3006 | if (unlikely(!f->hs_descriptors)) | ||
3007 | return -ENOMEM; | ||
2950 | } | 3008 | } |
2951 | 3009 | ||
2952 | return 0; | 3010 | return 0; |
@@ -2978,7 +3036,11 @@ static int fsg_add(struct usb_composite_dev *cdev, | |||
2978 | 3036 | ||
2979 | fsg->function.name = FSG_DRIVER_DESC; | 3037 | fsg->function.name = FSG_DRIVER_DESC; |
2980 | fsg->function.strings = fsg_strings_array; | 3038 | fsg->function.strings = fsg_strings_array; |
2981 | fsg->function.descriptors = fsg_fs_function; | 3039 | fsg->function.descriptors = usb_copy_descriptors(fsg_fs_function); |
3040 | if (unlikely(!fsg->function.descriptors)) { | ||
3041 | rc = -ENOMEM; | ||
3042 | goto error_free_fsg; | ||
3043 | } | ||
2982 | fsg->function.bind = fsg_bind; | 3044 | fsg->function.bind = fsg_bind; |
2983 | fsg->function.unbind = fsg_unbind; | 3045 | fsg->function.unbind = fsg_unbind; |
2984 | fsg->function.setup = fsg_setup; | 3046 | fsg->function.setup = fsg_setup; |
@@ -2993,11 +3055,19 @@ static int fsg_add(struct usb_composite_dev *cdev, | |||
2993 | * call to usb_add_function() was successful. */ | 3055 | * call to usb_add_function() was successful. */ |
2994 | 3056 | ||
2995 | rc = usb_add_function(c, &fsg->function); | 3057 | rc = usb_add_function(c, &fsg->function); |
3058 | if (unlikely(rc)) | ||
3059 | goto error_free_all; | ||
2996 | 3060 | ||
2997 | if (likely(rc == 0)) | 3061 | fsg_common_get(fsg->common); |
2998 | fsg_common_get(fsg->common); | 3062 | return 0; |
2999 | else | 3063 | |
3000 | kfree(fsg); | 3064 | error_free_all: |
3065 | usb_free_descriptors(fsg->function.descriptors); | ||
3066 | /* fsg_bind() might have copied those; or maybe not? who cares | ||
3067 | * -- free it just in case. */ | ||
3068 | usb_free_descriptors(fsg->function.hs_descriptors); | ||
3069 | error_free_fsg: | ||
3070 | kfree(fsg); | ||
3001 | 3071 | ||
3002 | return rc; | 3072 | return rc; |
3003 | } | 3073 | } |