summaryrefslogtreecommitdiff
path: root/utils/scsi/rbscsi.c
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2020-01-12 23:12:18 +0100
committerAmaury Pouly <amaury.pouly@gmail.com>2020-01-12 23:12:18 +0100
commitee15e45224d94ff84471e5ac4c05eb189dbe5980 (patch)
tree63991bcff06889b92cc3b4433dc0cde0f1930d00 /utils/scsi/rbscsi.c
parent9e94a5eadcdbef406c22833b8afa9e380b698fdf (diff)
downloadrockbox-ee15e45224d94ff84471e5ac4c05eb189dbe5980.tar.gz
rockbox-ee15e45224d94ff84471e5ac4c05eb189dbe5980.zip
nwztool/scsitool: rewrite device detection on linux to make it simpler
Change-Id: I8f393a53ea64c82f05e437923bcba05aa8a7a75c
Diffstat (limited to 'utils/scsi/rbscsi.c')
-rw-r--r--utils/scsi/rbscsi.c130
1 files changed, 27 insertions, 103 deletions
diff --git a/utils/scsi/rbscsi.c b/utils/scsi/rbscsi.c
index 8e15f1fd6a..adfecd8d0a 100644
--- a/utils/scsi/rbscsi.c
+++ b/utils/scsi/rbscsi.c
@@ -157,52 +157,10 @@ void rb_scsi_close(rb_scsi_device_t dev)
157 free(dev); 157 free(dev);
158} 158}
159 159
160static int is_hctl(const char *name) 160/* find the first entry of a directory (which is expended to contain only one) and turn
161{ 161 * it into a /dev/ path */
162 char *end;
163 strtoul(name, &end, 0); /* h */
164 if(*end != ':')
165 return 0;
166 strtoul(end + 1, &end, 0); /* c */
167 if(*end != ':')
168 return 0;
169 strtoul(end + 1, &end, 0); /* t */
170 if(*end != ':')
171 return 0;
172 strtoul(end + 1, &end, 0); /* l */
173 return *end == 0;
174}
175
176static int _resolve_link_dev_path(char *path, size_t pathsz)
177{
178 /* make sure it is a directory */
179 struct stat st;
180 if(stat(path, &st) < 0)
181 return 0;
182 if(!S_ISDIR(st.st_mode))
183 return 0;
184 if(chdir(path) < 0)
185 return 0;
186 if(getcwd(path, pathsz) == NULL)
187 return 0;
188 return 1;
189}
190
191static int resolve_link_dev_path(char *path, size_t pathsz)
192{
193 /* change directory, ask the current path and resolve current directory */
194 char curdir[PATH_MAX];
195 if(getcwd(curdir, sizeof(curdir)) == NULL)
196 return 0;
197 int ret = _resolve_link_dev_path(path, pathsz);
198 chdir(curdir);
199 return ret;
200}
201
202static int scan_resolve_first_dev_path(char *path, size_t pathsz) 162static int scan_resolve_first_dev_path(char *path, size_t pathsz)
203{ 163{
204 size_t pathlen = strlen(path);
205 char *pathend = path + pathlen;
206 DIR *dir = opendir(path); 164 DIR *dir = opendir(path);
207 if(dir == NULL) 165 if(dir == NULL)
208 return 0; 166 return 0;
@@ -212,13 +170,8 @@ static int scan_resolve_first_dev_path(char *path, size_t pathsz)
212 { 170 {
213 if(!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) 171 if(!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
214 continue; 172 continue;
215 /* we found the entry, if it is a symlink, resolve it, otherwise it must be a directory */ 173 /* we expect a directory, but it could be a symlink, only the name matters */
216 if(d->d_type == DT_LNK) 174 if(d->d_type == DT_LNK || d->d_type == DT_DIR)
217 {
218 snprintf(pathend, pathsz - pathlen, "/%s", d->d_name);
219 ret = resolve_link_dev_path(path, pathsz);
220 }
221 else if(d->d_type == DT_DIR)
222 { 175 {
223 snprintf(path, pathsz, "/dev/%s", d->d_name); 176 snprintf(path, pathsz, "/dev/%s", d->d_name);
224 ret = 1; 177 ret = 1;
@@ -229,33 +182,6 @@ static int scan_resolve_first_dev_path(char *path, size_t pathsz)
229 return ret; 182 return ret;
230} 183}
231 184
232static int scan_resolve_dev_path(char *path, size_t pathsz, const char *match)
233{
234 size_t pathlen = strlen(path);
235 char *pathend = path + pathlen;
236 DIR *dir = opendir(path);
237 if(dir == NULL)
238 return 0;
239 struct dirent *d;
240 int ret = 0;
241 while((d = readdir(dir)))
242 {
243 if(strcmp(d->d_name, match))
244 continue;
245 /* we found the entry, there are two case:
246 * - directory: we need to scan it and find the first entry
247 * - symlink: we need to see where it goes and extract the basename */
248 snprintf(pathend, pathsz - pathlen, "/%s", d->d_name);
249 if(d->d_type == DT_DIR)
250 ret = scan_resolve_first_dev_path(path, pathsz);
251 else if(d->d_type == DT_LNK)
252 ret = resolve_link_dev_path(path, pathsz);
253 break;
254 }
255 closedir(dir);
256 return ret;
257}
258
259static char *read_file_or_null(const char *path) 185static char *read_file_or_null(const char *path)
260{ 186{
261 FILE *f = fopen(path, "r"); 187 FILE *f = fopen(path, "r");
@@ -277,9 +203,9 @@ static char *read_file_or_null(const char *path)
277 203
278struct rb_scsi_devent_t *rb_scsi_list(void) 204struct rb_scsi_devent_t *rb_scsi_list(void)
279{ 205{
280 /* list devices in /sys/bus/scsi/devices 206 /* list devices in /sys/class/scsi_generic
281 * we only keep entries of the form h:c:t:l */ 207 * we only keep entries of the form sgX */
282#define SYS_SCSI_DEV_PATH "/sys/bus/scsi/devices" 208#define SYS_SCSI_DEV_PATH "/sys/class/scsi_generic"
283 DIR *dir = opendir(SYS_SCSI_DEV_PATH); 209 DIR *dir = opendir(SYS_SCSI_DEV_PATH);
284 if(dir == NULL) 210 if(dir == NULL)
285 return NULL; 211 return NULL;
@@ -292,31 +218,29 @@ struct rb_scsi_devent_t *rb_scsi_list(void)
292 { 218 {
293 if(!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) 219 if(!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
294 continue; 220 continue;
295 /* make sure the name is of the form h:c:t:l, we do not want targets or hosts */ 221 /* make sure the name is of the form sgX, and extract X */
296 if(!is_hctl(d->d_name)) 222 int sg_idx;
223 if(sscanf(d->d_name, "sg%d", &sg_idx) != 1)
297 continue; 224 continue;
298 /* we now need to scan the directory to find the block and scsi generic device path:
299 * block: there should be a 'block' entry
300 * scsi: there should be a 'scsi_generic' entry
301 * Both entries can either be a symlink to the devide, or a directory with a single entry */
302 char scsi_path[PATH_MAX];
303 snprintf(scsi_path, sizeof(scsi_path), SYS_SCSI_DEV_PATH "/%s", d->d_name);
304 if(!scan_resolve_dev_path(scsi_path, sizeof(scsi_path), "scsi_generic"))
305 continue;
306 char block_path[PATH_MAX];
307 snprintf(block_path, sizeof(block_path), SYS_SCSI_DEV_PATH "/%s", d->d_name);
308 if(!scan_resolve_dev_path(block_path, sizeof(block_path), "block"))
309 block_path[0] = 0;
310 dev = realloc(dev, (2 + nr_dev) * sizeof(struct rb_scsi_devent_t)); 225 dev = realloc(dev, (2 + nr_dev) * sizeof(struct rb_scsi_devent_t));
311 dev[nr_dev].scsi_path = strdup(scsi_path); 226
312 dev[nr_dev].block_path = block_path[0] == 0 ? NULL : strdup(block_path); 227 char path[PATH_MAX];
228 snprintf(path, sizeof(path), "/dev/sg%d", sg_idx);
229 dev[nr_dev].scsi_path = strdup(path);
230 /* /sys/class/scsi_generic/sgX is a folder containing a subfolder 'device' that contains
231 * - a 'block' folder with a single entry: the block device
232 * - the vendor/model/rev files */
233 snprintf(path, sizeof(path), SYS_SCSI_DEV_PATH "/sg%d/device/block", sg_idx);
234 if(!scan_resolve_first_dev_path(path, sizeof(path)))
235 path[0] = 0;
236 dev[nr_dev].block_path = path[0] == 0 ? NULL : strdup(path);
313 /* fill vendor/model/rev */ 237 /* fill vendor/model/rev */
314 snprintf(scsi_path, sizeof(scsi_path), SYS_SCSI_DEV_PATH "/%s/vendor", d->d_name); 238 snprintf(path, sizeof(path), SYS_SCSI_DEV_PATH "/sg%d/device/vendor", sg_idx);
315 dev[nr_dev].vendor = read_file_or_null(scsi_path); 239 dev[nr_dev].vendor = read_file_or_null(path);
316 snprintf(scsi_path, sizeof(scsi_path), SYS_SCSI_DEV_PATH "/%s/model", d->d_name); 240 snprintf(path, sizeof(path), SYS_SCSI_DEV_PATH "/sg%d/device/model", sg_idx);
317 dev[nr_dev].model = read_file_or_null(scsi_path); 241 dev[nr_dev].model = read_file_or_null(path);
318 snprintf(scsi_path, sizeof(scsi_path), SYS_SCSI_DEV_PATH "/%s/rev", d->d_name); 242 snprintf(path, sizeof(path), SYS_SCSI_DEV_PATH "/sg%d/device/rev", sg_idx);
319 dev[nr_dev].rev = read_file_or_null(scsi_path); 243 dev[nr_dev].rev = read_file_or_null(path);
320 244
321 /* sentinel */ 245 /* sentinel */
322 dev[++nr_dev].scsi_path = NULL; 246 dev[++nr_dev].scsi_path = NULL;