From bbabbcb7a08165a6149748ba514df97db28e2c49 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/2053807 Reviewed-by: Xiaojun Ji --- drivers/moto_f_mass_storage/f_mass_storage.c | 67 ++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/drivers/moto_f_mass_storage/f_mass_storage.c b/drivers/moto_f_mass_storage/f_mass_storage.c index 65a76e572d42..d18543b88ac7 100644 --- a/drivers/moto_f_mass_storage/f_mass_storage.c +++ b/drivers/moto_f_mass_storage/f_mass_storage.c @@ -209,6 +209,7 @@ #include #include #include +#include #include #include #include @@ -235,6 +236,11 @@ static const char fsg_string_interface[] = "Mass Storage"; +enum fsg_restart_type { + FSG_RESTART_NONE, + FSG_REBOOT, + FSG_REBOOT_BL, +}; /* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ static struct usb_string fsg_strings[] = { @@ -352,6 +358,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 { @@ -1315,6 +1324,8 @@ static int check_command(struct fsg_common *common, int cmnd_size, return 0; } +#define SC_REBOOT 0xd7 +#define SC_REBOOT_2 0xd8 static int do_scsi_command(struct fsg_common *common) { struct fsg_buffhd *bh; @@ -1427,6 +1438,35 @@ static int do_scsi_command(struct fsg_common *common) "TEST UNIT READY"); 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 @@ -1959,6 +1999,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) { @@ -1974,6 +2040,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));