summaryrefslogtreecommitdiffstats
path: root/package/system/utils/usb-modeswitch/files/modeswitch.hotplug
blob: eb71b3056a5801a15290ec85e2dfed7b1338a18b (plain)
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
#!/bin/sh

local uVid uPid uMa uPr uSe
local sVe sMo sRe

local modeswitch="/usr/bin/usb_modeswitch"


log() {
	logger -t "usb-modeswitch" "$@"
}

sanitize() {
	sed -e 's/[[:space:]]\+$//; s/[[:space:]]\+/_/g' "$@"
}

find_scsi_attrs() {
	[ -n "$DEVPATH" ] && [ -d /sys/$DEVPATH/host* ] && {
		log "$DEVICENAME is a SCSI device, waiting for it to settle..."
		local timeout=20
		while [ $((--timeout)) -ge 0 ]; do
			[ -d /sys/$DEVPATH/host*/target* ] && {
				local scsi_dir
				for scsi_dir in /sys/$DEVPATH/host*/target*/*; do
					[ -d "$scsi_dir" ] || break
					case "$scsi_dir" in
						*/host*/target*/*:*:*:*)
							sVe=$(sanitize "$scsi_dir/vendor")
							sMo=$(sanitize "$scsi_dir/model")
							sRe=$(sanitize "$scsi_dir/rev")

							log "$DEVICENAME: Vendor=${sVe:-?} Model=${sMo:-?} Revision=${sRe:-?}"
							return 0
						;;
					esac
				done
			} || {
				sleep 1
			}
		done
		log "$DEVICENAME: Failed to get SCSI attributes!"
	}

	return 1
}

find_usb_attrs() {
	local usb_dir="/sys/$DEVPATH"
	[ -f "$usb_dir/idVendor" ] || usb_dir="${usb_dir%/*}"

	uVid=$(cat "$usb_dir/idVendor")
	uPid=$(cat "$usb_dir/idProduct")
	uMa=$(sanitize "$usb_dir/manufacturer")
	uPr=$(sanitize "$usb_dir/product")
	uSe=$(sanitize "$usb_dir/serial")

	log "$DEVICENAME: Manufacturer=${uMa:-?} Product=${uPr:-?} Serial=${uSe:-?}"
}

match_config_tag() {
	local conf="$1"
	local tag="$2"

	case "${conf##*/}" in
		*:*$tag=*)
			local cmp; eval "cmp=\$$tag"
			local pat="${conf#*:$tag=}"; pat="${pat%%:*}"
			case "$cmp" in
				*$pat*) return 0 ;;
				*) return 1 ;;
			esac
		;;
	esac

	return 0
}

match_config() {
	local conf="$1"
	local tag

	for tag in uMa uPr uSe sVe sMo sRe; do
		match_config_tag "$conf" "$tag" || return 1
	done

	return 0
}



if [ "$ACTION" = add ]; then
	[ -d "/etc/usb_modeswitch.d" ] && [ -x "$modeswitch" ] && {
		case "$DEVICENAME" in
			*-*:*.*) : ;;
			*) exit 0 ;;
		esac

		find_usb_attrs

		local candidates=0
		local conf configs
		for conf in /etc/usb_modeswitch.d/$uVid:$uPid*; do
			[ -f "$conf" ] || break
			configs="${configs:+$configs }$conf"
			$((candidates++))
		done

		# Found more than one candidate, read SCSI attributes and find the best match
		[ $candidates -gt 1 ] && {
			find_scsi_attrs
			for conf in $configs; do
				match_config "$conf" && {
					configs="$conf"
					candidates=1
					break
				}
			done
		}

		# If a candidate is remaining, start usb-modeswitch
		[ -n "$configs" ] && {
			log "$DEVICENAME: Selecting ${configs%% *} for mode switching"
			# ugly workaround, but working for all hw we got for testing
			switching_done=0
			switching_tries=0
			local usb_dir="/sys/$DEVPATH"
			[ -f "$usb_dir/idVendor" ] || usb_dir="${usb_dir%/*}"
			while [ $switching_done -lt 1 -a $switching_tries -le 6 ]; do
				$modeswitch -v $uVid -p $uPid -I -D -n -s 30 -c "${configs%% *}"
				if [ $(sanitize "$usb_dir/idProduct") = $uPid ]; then
					log "$DEVICENAME: Switching seemingly failed"
					sleep 1
				else
					switching_done=1
				fi
				switching_tries=$(( $switching_tries + 1 ))
			done
		}
	}
fi