-
Notifications
You must be signed in to change notification settings - Fork 6
/
suspend-usb-device
executable file
·181 lines (152 loc) · 5.38 KB
/
suspend-usb-device
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#!/bin/bash
#
# suspend-usb-device: an easy-to-use script to properly put an USB
# device into suspend mode that can then be unplugged safely
#
# Copyright (C) 2009, Yan Li <elliot.li.tech@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# To reach the auther, please write an email to the address as stated
# above.
# ACKNOWLEDGEMENTS:
# Christian Schmitt <chris@ilovelinux.de> for firewire supporting
# David <d.tonhofer@m-plify.com> for improving parent device
# search and verbose output message
usage()
{
cat<<EOF
suspend-usb-device Copyright (C) 2009 Yan Li <elliot.li.tech@gmail.com>
This script is designed to properly put an USB device into suspend
mode that can then be unplugged safely. It sends a SYNCHRONIZE CACHE
command followed by a START-STOP command (if the device supports it),
unbinds the device from the driver and then suspends the USB
port. After that you can disconnect your USB device safely.
usage:
$0 [options] dev
sample:
$0 /dev/sde
options:
-l show the device and USB bus ID only
-h print this usage
-v verbose
This program comes with ABSOLUTELY NO WARRANTY. This is free
software, and you are welcome to redistribute it under certain
conditions; for details please read the licese at the beginning of the
source code file.
EOF
}
set -e -u
SHOW_DEVICE_ONLY=0
VERBOSE=0
while getopts "vlh" opt; do
case "$opt" in
h)
usage
exit 2
;;
l)
SHOW_DEVICE_ONLY=1
;;
v)
VERBOSE=1
;;
?)
echo
usage
exit 2
;;
esac
done
DEV_NAME=${!OPTIND:-}
if [ -z ${DEV_NAME} ]; then
usage
exit 2
fi
# mount checking
if mount | grep "^${DEV_NAME}[[:digit:]]* "; then
1>&2 echo
1>&2 echo "the above disk or partition is still mounted, can't suspend device"
1>&2 echo "unmount it first using umount"
exit 1
fi
# looking for the parent of the device with type "usb-storage:usb", it
# is the grand-parent device of the SCSI host, and it's devpath is
# like
# /devices/pci0000:00/0000:00:1d.7/usb5/5-8 (or /fw5/fw5-8 for firewire devices)
# without an USB hub, the device path looks like:
# /devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.0/host5/target5:0:0/5:0:0:0
# here the grand-parent of host5 is 2-1
# when there's a USB HUB, the device path is like:
# /devices/pci0000:00/0000:00:1d.0/usb5/5-2/5-2.2/5-2.2:1.0/host4/target4:0:0/4:0:0:0
# and the grand-parent of host4 is 5-2.2
DEVICE=$(udevadm info --query=path --name=${DEV_NAME} --attribute-walk | \
egrep "looking at parent device" | head -1 | \
sed -e "s/.*looking at parent device '\(\/devices\/.*\)\/.*\/host.*/\1/g")
if [ -z $DEVICE ]; then
1>&2 echo "cannot find appropriate parent USB/Firewire device, "
1>&2 echo "perhaps ${DEV_NAME} is not an USB/Firewire device?"
exit 1
fi
# the trailing basename of ${DEVICE} is DEV_BUS_ID ("5-8" in the
# sample above)
DEV_BUS_ID=${DEVICE##*/}
[[ $VERBOSE == 1 ]] && echo "Found device $DEVICE associated to $DEV_NAME; USB bus id is $DEV_BUS_ID"
if [ ${SHOW_DEVICE_ONLY} -eq 1 ]; then
echo Device: ${DEVICE}
echo Bus ID: ${DEV_BUS_ID}
exit 0
fi
# flush all buffers
sync
# root check
if [ `id -u` -ne 0 ]; then
1>&2 echo error, must be run as root, exiting...
exit 1
fi
# send SCSI sync command, some devices don't support this so we just
# ignore errors with "|| true"
[[ $VERBOSE == 1 ]] && echo "Syncing device $DEV_NAME"
sdparm --command=sync "$DEV_NAME" >/dev/null || true
# send SCSI stop command
[[ $VERBOSE == 1 ]] && echo "Stopping device $DEV_NAME"
sdparm --command=stop "$DEV_NAME" >/dev/null
# unbind it; if this yields "no such device", we are trying to unbind the wrong device
[[ $VERBOSE == 1 ]] && echo "Unbinding device $DEV_BUS_ID"
if [[ "${DEV_BUS_ID}" == fw* ]]
then
echo -n "${DEV_BUS_ID}" > /sys/bus/firewire/drivers/sbp2/unbind
else
echo -n "${DEV_BUS_ID}" > /sys/bus/usb/drivers/usb/unbind
# suspend it if it's an USB device (we have no way to suspend a
# firewire device yet)
# check if CONFIG_USB_SUSPEND is enabled
[[ $VERBOSE == 1 ]] && echo "Checking whether $DEVICE can be suspended"
POWER_LEVEL_FILE=/sys${DEVICE}/power/level
if [ ! -f "$POWER_LEVEL_FILE" ]; then
1>&2 cat<<EOF
It's safe to remove the USB device now but better can be done. The
power level control file $POWER_LEVEL_FILE
doesn't exist on the system so I have no way to put the USB device
into suspend mode, perhaps you don't have CONFIG_USB_SUSPEND enabled
in your running kernel.
Read
http://elliotli.blogspot.com/2009/01/safely-remove-usb-hard-drive-in-linux.html
for an detailed explanation.
EOF
exit 3
fi
[[ $VERBOSE == 1 ]] && echo "Suspending $DEVICE by writing to $POWER_LEVEL_FILE"
echo 'suspend' > "$POWER_LEVEL_FILE"
fi