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 <a17671@motorola.com>
Reviewed-by: Yuecai Liu <liuyc20@motorola.com>
Reviewed-by: Jianqi Yang <yangj@motorola.com>
Submit-Approved: Jira Key
Reviewed-on: https://gerrit.mot.com/1785335
Reviewed-by: Huosheng Liao <liaohs@motorola.com>
This commit is contained in:
Ravikumar Vembu 2017-01-18 14:57:21 -06:00 • committed by Xiaojun Ji
commit d19025e7dd
2 changed files with 69 additions and 0 deletions

View file

@ -209,6 +209,7 @@
#include <linux/kthread.h>
#include <linux/sched/signal.h>
#include <linux/limits.h>
#include <linux/reboot.h>
#include <linux/rwsem.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@ -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));

View file

@ -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