]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - drivers/staging/usbip/userspace/src/usbip_bind.c
staging: usbip: convert usbip-host driver to usb_device_driver
[mirror_ubuntu-artful-kernel.git] / drivers / staging / usbip / userspace / src / usbip_bind.c
1 /*
2 * Copyright (C) 2011 matt mooney <mfm@muteddisk.com>
3 * 2005-2007 Takahiro Hirofuchi
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <sysfs/libsysfs.h>
20
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <getopt.h>
27
28 #include "usbip_common.h"
29 #include "utils.h"
30 #include "usbip.h"
31
32 enum unbind_status {
33 UNBIND_ST_OK,
34 UNBIND_ST_USBIP_HOST,
35 UNBIND_ST_FAILED
36 };
37
38 static const char usbip_bind_usage_string[] =
39 "usbip bind <args>\n"
40 " -b, --busid=<busid> Bind " USBIP_HOST_DRV_NAME ".ko to device "
41 "on <busid>\n";
42
43 void usbip_bind_usage(void)
44 {
45 printf("usage: %s", usbip_bind_usage_string);
46 }
47
48 /* call at unbound state */
49 static int bind_usbip(char *busid)
50 {
51 char bus_type[] = "usb";
52 char attr_name[] = "bind";
53 char sysfs_mntpath[SYSFS_PATH_MAX];
54 char bind_attr_path[SYSFS_PATH_MAX];
55 struct sysfs_attribute *bind_attr;
56 int failed = 0;
57 int rc, ret = -1;
58
59 rc = sysfs_get_mnt_path(sysfs_mntpath, SYSFS_PATH_MAX);
60 if (rc < 0) {
61 err("sysfs must be mounted: %s", strerror(errno));
62 return -1;
63 }
64
65 snprintf(bind_attr_path, sizeof(bind_attr_path), "%s/%s/%s/%s/%s/%s",
66 sysfs_mntpath, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME,
67 USBIP_HOST_DRV_NAME, attr_name);
68
69 bind_attr = sysfs_open_attribute(bind_attr_path);
70 if (!bind_attr) {
71 dbg("problem getting bind attribute: %s", strerror(errno));
72 return -1;
73 }
74
75 rc = sysfs_write_attribute(bind_attr, busid, SYSFS_BUS_ID_SIZE);
76 if (rc < 0) {
77 dbg("bind driver at %s failed", busid);
78 failed = 1;
79 }
80
81 if (!failed)
82 ret = 0;
83
84 sysfs_close_attribute(bind_attr);
85
86 return ret;
87 }
88
89 /* buggy driver may cause dead lock */
90 static int unbind_other(char *busid)
91 {
92 char bus_type[] = "usb";
93 struct sysfs_device *busid_dev;
94 struct sysfs_device *dev;
95 struct sysfs_driver *drv;
96 struct sysfs_attribute *unbind_attr;
97 struct sysfs_attribute *bDevClass;
98 int rc;
99 enum unbind_status status = UNBIND_ST_OK;
100
101 busid_dev = sysfs_open_device(bus_type, busid);
102 if (!busid_dev) {
103 dbg("sysfs_open_device %s failed: %s", busid, strerror(errno));
104 return -1;
105 }
106 dbg("busid path: %s", busid_dev->path);
107
108 bDevClass = sysfs_get_device_attr(busid_dev, "bDeviceClass");
109 if (!bDevClass) {
110 dbg("problem getting device attribute: %s",
111 strerror(errno));
112 goto err_close_busid_dev;
113 }
114
115 if (!strncmp(bDevClass->value, "09", bDevClass->len)) {
116 dbg("skip unbinding of hub");
117 goto err_close_busid_dev;
118 }
119
120 dev = sysfs_open_device(bus_type, busid);
121 if (!dev) {
122 dbg("could not open device: %s",
123 strerror(errno));
124 goto err_close_busid_dev;
125 }
126
127 dbg("%s -> %s", dev->name, dev->driver_name);
128
129 if (!strncmp("unknown", dev->driver_name, SYSFS_NAME_LEN)) {
130 /* unbound interface */
131 sysfs_close_device(dev);
132 goto out;
133 }
134
135 if (!strncmp(USBIP_HOST_DRV_NAME, dev->driver_name,
136 SYSFS_NAME_LEN)) {
137 /* already bound to usbip-host */
138 status = UNBIND_ST_USBIP_HOST;
139 sysfs_close_device(dev);
140 goto out;
141 }
142
143 /* unbinding */
144 drv = sysfs_open_driver(bus_type, dev->driver_name);
145 if (!drv) {
146 dbg("could not open device driver on %s: %s",
147 dev->name, strerror(errno));
148 goto err_close_intf_dev;
149 }
150 dbg("device driver: %s", drv->path);
151
152 unbind_attr = sysfs_get_driver_attr(drv, "unbind");
153 if (!unbind_attr) {
154 dbg("problem getting device driver attribute: %s",
155 strerror(errno));
156 goto err_close_intf_drv;
157 }
158
159 rc = sysfs_write_attribute(unbind_attr, dev->bus_id,
160 SYSFS_BUS_ID_SIZE);
161 if (rc < 0) {
162 /* NOTE: why keep unbinding other interfaces? */
163 dbg("unbind driver at %s failed", dev->bus_id);
164 status = UNBIND_ST_FAILED;
165 }
166
167 sysfs_close_driver(drv);
168 sysfs_close_device(dev);
169
170 goto out;
171
172 err_close_intf_drv:
173 sysfs_close_driver(drv);
174 err_close_intf_dev:
175 sysfs_close_device(dev);
176 err_close_busid_dev:
177 status = UNBIND_ST_FAILED;
178 out:
179 sysfs_close_device(busid_dev);
180
181 return status;
182 }
183
184 static int bind_device(char *busid)
185 {
186 int rc;
187
188 rc = unbind_other(busid);
189 if (rc == UNBIND_ST_FAILED) {
190 err("could not unbind driver from device on busid %s", busid);
191 return -1;
192 } else if (rc == UNBIND_ST_USBIP_HOST) {
193 err("device on busid %s is already bound to %s", busid,
194 USBIP_HOST_DRV_NAME);
195 return -1;
196 }
197
198 rc = modify_match_busid(busid, 1);
199 if (rc < 0) {
200 err("unable to bind device on %s", busid);
201 return -1;
202 }
203
204 rc = bind_usbip(busid);
205 if (rc < 0) {
206 err("could not bind device to %s", USBIP_HOST_DRV_NAME);
207 modify_match_busid(busid, 0);
208 return -1;
209 }
210
211 printf("bind device on busid %s: complete\n", busid);
212
213 return 0;
214 }
215
216 int usbip_bind(int argc, char *argv[])
217 {
218 static const struct option opts[] = {
219 { "busid", required_argument, NULL, 'b' },
220 { NULL, 0, NULL, 0 }
221 };
222
223 int opt;
224 int ret = -1;
225
226 for (;;) {
227 opt = getopt_long(argc, argv, "b:", opts, NULL);
228
229 if (opt == -1)
230 break;
231
232 switch (opt) {
233 case 'b':
234 ret = bind_device(optarg);
235 goto out;
236 default:
237 goto err_out;
238 }
239 }
240
241 err_out:
242 usbip_bind_usage();
243 out:
244 return ret;
245 }