From d19025e7ddf2dc137155e91b16d6c2cf6cececb2 Mon Sep 17 00:00:00 2001 From: Ravikumar Vembu Date: Wed, 18 Jan 2017 14:57:21 -0600 Subject: [PATCH] usb: mass_storage: Add support for SC_REBOOT Add support for a vendor specific SCSI pass through command to trigger reboot. SC_REBOOT (0xd7) - triggers reboot into fastboot mode SC_REBOOT_1 (0xd8) - triggers reboot into normal mode. Propagated from (CR) Change-Id: I4fc4839968135a63c594dca69b1dc93699e4a9bc Reviewed-on: https://gerrit.mot.com/1750946 SME-Granted: SME Approvals Granted SLTApproved: Slta Waiver Tested-by: Jira Key Reviewed-by: Zonghua Liu Reviewed-by: Yuecai Liu Reviewed-by: Jianqi Yang Submit-Approved: Jira Key Reviewed-on: https://gerrit.mot.com/1785335 Reviewed-by: Huosheng Liao --- drivers/usb/gadget/function/f_mass_storage.c | 67 ++++++++++++++++++++ include/scsi/scsi_proto.h | 2 + 2 files changed, 69 insertions(+) diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 9ad998e9db33..59fdd37674e6 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -209,6 +209,7 @@ #include #include #include +#include #include #include #include @@ -231,6 +232,13 @@ #define FSG_DRIVER_DESC "Mass Storage Function" #define FSG_DRIVER_VERSION "2009/09/11" +enum fsg_restart_type { + FSG_RESTART_NONE, + FSG_REBOOT, + FSG_REBOOT_BL, +}; + + static const char fsg_string_interface[] = "Mass Storage"; #include "storage_common.h" @@ -314,6 +322,9 @@ struct fsg_common { void *private_data; char inquiry_string[INQUIRY_STRING_LEN]; + + enum fsg_restart_type restart_type; + struct delayed_work restart_work; }; struct fsg_dev { @@ -2115,6 +2126,35 @@ static int do_scsi_command(struct fsg_common *common) reply = do_write(common); break; + case SC_REBOOT: + common->data_size_from_cmnd = 0; + reply = check_command(common, common->cmnd_size, + DATA_DIR_NONE, 0, 0, "REBOOT BL"); + if (reply == 0) { + common->curlun->sense_data = SS_INVALID_COMMAND; + reply = -EINVAL; + } else { + pr_err("Triggered Reboot from SCSI Command\n"); + common->restart_type = FSG_REBOOT; + schedule_delayed_work(&common->restart_work, + msecs_to_jiffies(1000)); + } + break; + case SC_REBOOT_2: + common->data_size_from_cmnd = 0; + reply = check_command(common, common->cmnd_size, + DATA_DIR_NONE, 0, 0, "REBOOT"); + if (reply == 0) { + common->curlun->sense_data = SS_INVALID_COMMAND; + reply = -EINVAL; + } else { + pr_err("Triggered Reboot BL from SCSI Command\n"); + common->restart_type = FSG_REBOOT_BL; + schedule_delayed_work(&common->restart_work, + msecs_to_jiffies(1000)); + } + break; + /* * Some mandatory commands that we recognize but don't implement. * They don't mean much in this setting. It's left as an exercise @@ -2661,6 +2701,32 @@ static void fsg_lun_release(struct device *dev) /* Nothing needs to be done */ } +static int disable_restarts; +module_param(disable_restarts, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(disable_restarts, "Disable SCSI Initiated Reboots"); +static void fsg_restart_work(struct work_struct *work) +{ + struct fsg_common *common = container_of(work, + struct fsg_common, + restart_work.work); + + if (disable_restarts) { + pr_err("SCSI Reboots Disabled\n"); + return; + } + + switch (common->restart_type) { + case FSG_REBOOT: + kernel_restart(""); + break; + case FSG_REBOOT_BL: + kernel_restart("bootloader"); + break; + default: + break; + } +} + static struct fsg_common *fsg_common_setup(struct fsg_common *common) { if (!common) { @@ -2676,6 +2742,7 @@ static struct fsg_common *fsg_common_setup(struct fsg_common *common) init_completion(&common->thread_notifier); init_waitqueue_head(&common->io_wait); init_waitqueue_head(&common->fsg_wait); + INIT_DELAYED_WORK(&common->restart_work, fsg_restart_work); common->state = FSG_STATE_TERMINATED; memset(common->luns, 0, sizeof(common->luns)); diff --git a/include/scsi/scsi_proto.h b/include/scsi/scsi_proto.h index c36860111932..fca5a614cd45 100644 --- a/include/scsi/scsi_proto.h +++ b/include/scsi/scsi_proto.h @@ -169,6 +169,8 @@ /* Vendor specific CDBs start here */ #define VENDOR_SPECIFIC_CDB 0xc0 +#define SC_REBOOT 0xd7 +#define SC_REBOOT_2 0xd8 /* * SCSI command lengths