199ca880aSopenharmony_ci/* 299ca880aSopenharmony_ci * compose persistent device path 399ca880aSopenharmony_ci * 499ca880aSopenharmony_ci * Copyright (C) 2009-2011 Kay Sievers <kay@vrfy.org> 599ca880aSopenharmony_ci * 699ca880aSopenharmony_ci * Logic based on Hannes Reinecke's shell script. 799ca880aSopenharmony_ci * 899ca880aSopenharmony_ci * This program is free software: you can redistribute it and/or modify 999ca880aSopenharmony_ci * it under the terms of the GNU General Public License as published by 1099ca880aSopenharmony_ci * the Free Software Foundation, either version 2 of the License, or 1199ca880aSopenharmony_ci * (at your option) any later version. 1299ca880aSopenharmony_ci * 1399ca880aSopenharmony_ci * This program is distributed in the hope that it will be useful, 1499ca880aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 1599ca880aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1699ca880aSopenharmony_ci * GNU General Public License for more details. 1799ca880aSopenharmony_ci * 1899ca880aSopenharmony_ci * You should have received a copy of the GNU General Public License 1999ca880aSopenharmony_ci * along with this program. If not, see <http://www.gnu.org/licenses/>. 2099ca880aSopenharmony_ci */ 2199ca880aSopenharmony_ci 2299ca880aSopenharmony_ci#include <stdio.h> 2399ca880aSopenharmony_ci#include <stdlib.h> 2499ca880aSopenharmony_ci#include <stdarg.h> 2599ca880aSopenharmony_ci#include <unistd.h> 2699ca880aSopenharmony_ci#include <string.h> 2799ca880aSopenharmony_ci#include <ctype.h> 2899ca880aSopenharmony_ci#include <fcntl.h> 2999ca880aSopenharmony_ci#include <errno.h> 3099ca880aSopenharmony_ci#include <dirent.h> 3199ca880aSopenharmony_ci#include <getopt.h> 3299ca880aSopenharmony_ci 3399ca880aSopenharmony_ci#include "udev.h" 3499ca880aSopenharmony_ci 3599ca880aSopenharmony_ci_printf_(2,3) 3699ca880aSopenharmony_cistatic int path_prepend(char **path, const char *fmt, ...) { 3799ca880aSopenharmony_ci va_list va; 3899ca880aSopenharmony_ci char *pre; 3999ca880aSopenharmony_ci int err = 0; 4099ca880aSopenharmony_ci 4199ca880aSopenharmony_ci va_start(va, fmt); 4299ca880aSopenharmony_ci err = vasprintf(&pre, fmt, va); 4399ca880aSopenharmony_ci va_end(va); 4499ca880aSopenharmony_ci if (err < 0) 4599ca880aSopenharmony_ci goto out; 4699ca880aSopenharmony_ci 4799ca880aSopenharmony_ci if (*path != NULL) { 4899ca880aSopenharmony_ci char *new; 4999ca880aSopenharmony_ci 5099ca880aSopenharmony_ci err = asprintf(&new, "%s-%s", pre, *path); 5199ca880aSopenharmony_ci free(pre); 5299ca880aSopenharmony_ci if (err < 0) 5399ca880aSopenharmony_ci goto out; 5499ca880aSopenharmony_ci free(*path); 5599ca880aSopenharmony_ci *path = new; 5699ca880aSopenharmony_ci } else { 5799ca880aSopenharmony_ci *path = pre; 5899ca880aSopenharmony_ci } 5999ca880aSopenharmony_ciout: 6099ca880aSopenharmony_ci return err; 6199ca880aSopenharmony_ci} 6299ca880aSopenharmony_ci 6399ca880aSopenharmony_ci/* 6499ca880aSopenharmony_ci** Linux only supports 32 bit luns. 6599ca880aSopenharmony_ci** See drivers/scsi/scsi_scan.c::scsilun_to_int() for more details. 6699ca880aSopenharmony_ci*/ 6799ca880aSopenharmony_cistatic int format_lun_number(struct udev_device *dev, char **path) { 6899ca880aSopenharmony_ci unsigned long lun = strtoul(udev_device_get_sysnum(dev), NULL, 10); 6999ca880aSopenharmony_ci 7099ca880aSopenharmony_ci /* address method 0, peripheral device addressing with bus id of zero */ 7199ca880aSopenharmony_ci if (lun < 256) 7299ca880aSopenharmony_ci return path_prepend(path, "lun-%lu", lun); 7399ca880aSopenharmony_ci /* handle all other lun addressing methods by using a variant of the original lun format */ 7499ca880aSopenharmony_ci return path_prepend(path, "lun-0x%04lx%04lx00000000", lun & 0xffff, (lun >> 16) & 0xffff); 7599ca880aSopenharmony_ci} 7699ca880aSopenharmony_ci 7799ca880aSopenharmony_cistatic struct udev_device *skip_subsystem(struct udev_device *dev, const char *subsys) { 7899ca880aSopenharmony_ci struct udev_device *parent = dev; 7999ca880aSopenharmony_ci 8099ca880aSopenharmony_ci while (parent != NULL) { 8199ca880aSopenharmony_ci const char *subsystem; 8299ca880aSopenharmony_ci 8399ca880aSopenharmony_ci subsystem = udev_device_get_subsystem(parent); 8499ca880aSopenharmony_ci if (subsystem == NULL || !streq(subsystem, subsys)) 8599ca880aSopenharmony_ci break; 8699ca880aSopenharmony_ci dev = parent; 8799ca880aSopenharmony_ci parent = udev_device_get_parent(parent); 8899ca880aSopenharmony_ci } 8999ca880aSopenharmony_ci return dev; 9099ca880aSopenharmony_ci} 9199ca880aSopenharmony_ci 9299ca880aSopenharmony_cistatic struct udev_device *handle_scsi_fibre_channel(struct udev_device *parent, char **path) { 9399ca880aSopenharmony_ci struct udev *udev = udev_device_get_udev(parent); 9499ca880aSopenharmony_ci struct udev_device *targetdev; 9599ca880aSopenharmony_ci struct udev_device *fcdev = NULL; 9699ca880aSopenharmony_ci const char *port; 9799ca880aSopenharmony_ci char *lun = NULL; 9899ca880aSopenharmony_ci 9999ca880aSopenharmony_ci targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); 10099ca880aSopenharmony_ci if (targetdev == NULL) 10199ca880aSopenharmony_ci return NULL; 10299ca880aSopenharmony_ci 10399ca880aSopenharmony_ci fcdev = udev_device_new_from_subsystem_sysname(udev, "fc_transport", udev_device_get_sysname(targetdev)); 10499ca880aSopenharmony_ci if (fcdev == NULL) 10599ca880aSopenharmony_ci return NULL; 10699ca880aSopenharmony_ci port = udev_device_get_sysattr_value(fcdev, "port_name"); 10799ca880aSopenharmony_ci if (port == NULL) { 10899ca880aSopenharmony_ci parent = NULL; 10999ca880aSopenharmony_ci goto out; 11099ca880aSopenharmony_ci } 11199ca880aSopenharmony_ci 11299ca880aSopenharmony_ci format_lun_number(parent, &lun); 11399ca880aSopenharmony_ci path_prepend(path, "fc-%s-%s", port, lun); 11499ca880aSopenharmony_ci if (lun) 11599ca880aSopenharmony_ci free(lun); 11699ca880aSopenharmony_ciout: 11799ca880aSopenharmony_ci udev_device_unref(fcdev); 11899ca880aSopenharmony_ci return parent; 11999ca880aSopenharmony_ci} 12099ca880aSopenharmony_ci 12199ca880aSopenharmony_cistatic struct udev_device *handle_scsi_sas_wide_port(struct udev_device *parent, char **path) { 12299ca880aSopenharmony_ci struct udev *udev = udev_device_get_udev(parent); 12399ca880aSopenharmony_ci struct udev_device *targetdev; 12499ca880aSopenharmony_ci struct udev_device *target_parent; 12599ca880aSopenharmony_ci struct udev_device *sasdev; 12699ca880aSopenharmony_ci const char *sas_address; 12799ca880aSopenharmony_ci char *lun = NULL; 12899ca880aSopenharmony_ci 12999ca880aSopenharmony_ci targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); 13099ca880aSopenharmony_ci if (targetdev == NULL) 13199ca880aSopenharmony_ci return NULL; 13299ca880aSopenharmony_ci 13399ca880aSopenharmony_ci target_parent = udev_device_get_parent(targetdev); 13499ca880aSopenharmony_ci if (target_parent == NULL) 13599ca880aSopenharmony_ci return NULL; 13699ca880aSopenharmony_ci 13799ca880aSopenharmony_ci sasdev = udev_device_new_from_subsystem_sysname(udev, "sas_device", 13899ca880aSopenharmony_ci udev_device_get_sysname(target_parent)); 13999ca880aSopenharmony_ci if (sasdev == NULL) 14099ca880aSopenharmony_ci return NULL; 14199ca880aSopenharmony_ci 14299ca880aSopenharmony_ci sas_address = udev_device_get_sysattr_value(sasdev, "sas_address"); 14399ca880aSopenharmony_ci if (sas_address == NULL) { 14499ca880aSopenharmony_ci parent = NULL; 14599ca880aSopenharmony_ci goto out; 14699ca880aSopenharmony_ci } 14799ca880aSopenharmony_ci 14899ca880aSopenharmony_ci format_lun_number(parent, &lun); 14999ca880aSopenharmony_ci path_prepend(path, "sas-%s-%s", sas_address, lun); 15099ca880aSopenharmony_ci if (lun) 15199ca880aSopenharmony_ci free(lun); 15299ca880aSopenharmony_ciout: 15399ca880aSopenharmony_ci udev_device_unref(sasdev); 15499ca880aSopenharmony_ci return parent; 15599ca880aSopenharmony_ci} 15699ca880aSopenharmony_ci 15799ca880aSopenharmony_cistatic struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path) 15899ca880aSopenharmony_ci{ 15999ca880aSopenharmony_ci struct udev *udev = udev_device_get_udev(parent); 16099ca880aSopenharmony_ci struct udev_device *targetdev; 16199ca880aSopenharmony_ci struct udev_device *target_parent; 16299ca880aSopenharmony_ci struct udev_device *port; 16399ca880aSopenharmony_ci struct udev_device *expander; 16499ca880aSopenharmony_ci struct udev_device *target_sasdev = NULL; 16599ca880aSopenharmony_ci struct udev_device *expander_sasdev = NULL; 16699ca880aSopenharmony_ci struct udev_device *port_sasdev = NULL; 16799ca880aSopenharmony_ci const char *sas_address = NULL; 16899ca880aSopenharmony_ci const char *phy_id; 16999ca880aSopenharmony_ci const char *phy_count; 17099ca880aSopenharmony_ci char *lun = NULL; 17199ca880aSopenharmony_ci 17299ca880aSopenharmony_ci targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); 17399ca880aSopenharmony_ci if (targetdev == NULL) 17499ca880aSopenharmony_ci return NULL; 17599ca880aSopenharmony_ci 17699ca880aSopenharmony_ci target_parent = udev_device_get_parent(targetdev); 17799ca880aSopenharmony_ci if (target_parent == NULL) 17899ca880aSopenharmony_ci return NULL; 17999ca880aSopenharmony_ci 18099ca880aSopenharmony_ci /* Get sas device */ 18199ca880aSopenharmony_ci target_sasdev = udev_device_new_from_subsystem_sysname(udev, 18299ca880aSopenharmony_ci "sas_device", udev_device_get_sysname(target_parent)); 18399ca880aSopenharmony_ci if (target_sasdev == NULL) 18499ca880aSopenharmony_ci return NULL; 18599ca880aSopenharmony_ci 18699ca880aSopenharmony_ci /* The next parent is sas port */ 18799ca880aSopenharmony_ci port = udev_device_get_parent(target_parent); 18899ca880aSopenharmony_ci if (port == NULL) { 18999ca880aSopenharmony_ci parent = NULL; 19099ca880aSopenharmony_ci goto out; 19199ca880aSopenharmony_ci } 19299ca880aSopenharmony_ci 19399ca880aSopenharmony_ci /* Get port device */ 19499ca880aSopenharmony_ci port_sasdev = udev_device_new_from_subsystem_sysname(udev, 19599ca880aSopenharmony_ci "sas_port", udev_device_get_sysname(port)); 19699ca880aSopenharmony_ci 19799ca880aSopenharmony_ci phy_count = udev_device_get_sysattr_value(port_sasdev, "num_phys"); 19899ca880aSopenharmony_ci if (phy_count == NULL) { 19999ca880aSopenharmony_ci parent = NULL; 20099ca880aSopenharmony_ci goto out; 20199ca880aSopenharmony_ci } 20299ca880aSopenharmony_ci 20399ca880aSopenharmony_ci /* Check if we are simple disk */ 20499ca880aSopenharmony_ci if (strncmp(phy_count, "1", 2) != 0) { 20599ca880aSopenharmony_ci parent = handle_scsi_sas_wide_port(parent, path); 20699ca880aSopenharmony_ci goto out; 20799ca880aSopenharmony_ci } 20899ca880aSopenharmony_ci 20999ca880aSopenharmony_ci /* Get connected phy */ 21099ca880aSopenharmony_ci phy_id = udev_device_get_sysattr_value(target_sasdev, "phy_identifier"); 21199ca880aSopenharmony_ci if (phy_id == NULL) { 21299ca880aSopenharmony_ci parent = NULL; 21399ca880aSopenharmony_ci goto out; 21499ca880aSopenharmony_ci } 21599ca880aSopenharmony_ci 21699ca880aSopenharmony_ci /* The port's parent is either hba or expander */ 21799ca880aSopenharmony_ci expander = udev_device_get_parent(port); 21899ca880aSopenharmony_ci if (expander == NULL) { 21999ca880aSopenharmony_ci parent = NULL; 22099ca880aSopenharmony_ci goto out; 22199ca880aSopenharmony_ci } 22299ca880aSopenharmony_ci 22399ca880aSopenharmony_ci /* Get expander device */ 22499ca880aSopenharmony_ci expander_sasdev = udev_device_new_from_subsystem_sysname(udev, 22599ca880aSopenharmony_ci "sas_device", udev_device_get_sysname(expander)); 22699ca880aSopenharmony_ci if (expander_sasdev != NULL) { 22799ca880aSopenharmony_ci /* Get expander's address */ 22899ca880aSopenharmony_ci sas_address = udev_device_get_sysattr_value(expander_sasdev, 22999ca880aSopenharmony_ci "sas_address"); 23099ca880aSopenharmony_ci if (sas_address == NULL) { 23199ca880aSopenharmony_ci parent = NULL; 23299ca880aSopenharmony_ci goto out; 23399ca880aSopenharmony_ci } 23499ca880aSopenharmony_ci } 23599ca880aSopenharmony_ci 23699ca880aSopenharmony_ci format_lun_number(parent, &lun); 23799ca880aSopenharmony_ci if (sas_address) 23899ca880aSopenharmony_ci path_prepend(path, "sas-exp%s-phy%s-%s", sas_address, phy_id, lun); 23999ca880aSopenharmony_ci else 24099ca880aSopenharmony_ci path_prepend(path, "sas-phy%s-%s", phy_id, lun); 24199ca880aSopenharmony_ci 24299ca880aSopenharmony_ci if (lun) 24399ca880aSopenharmony_ci free(lun); 24499ca880aSopenharmony_ciout: 24599ca880aSopenharmony_ci udev_device_unref(target_sasdev); 24699ca880aSopenharmony_ci udev_device_unref(expander_sasdev); 24799ca880aSopenharmony_ci udev_device_unref(port_sasdev); 24899ca880aSopenharmony_ci return parent; 24999ca880aSopenharmony_ci} 25099ca880aSopenharmony_ci 25199ca880aSopenharmony_cistatic struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path) { 25299ca880aSopenharmony_ci struct udev *udev = udev_device_get_udev(parent); 25399ca880aSopenharmony_ci struct udev_device *transportdev; 25499ca880aSopenharmony_ci struct udev_device *sessiondev = NULL; 25599ca880aSopenharmony_ci const char *target; 25699ca880aSopenharmony_ci char *connname; 25799ca880aSopenharmony_ci struct udev_device *conndev = NULL; 25899ca880aSopenharmony_ci const char *addr; 25999ca880aSopenharmony_ci const char *port; 26099ca880aSopenharmony_ci char *lun = NULL; 26199ca880aSopenharmony_ci 26299ca880aSopenharmony_ci /* find iscsi session */ 26399ca880aSopenharmony_ci transportdev = parent; 26499ca880aSopenharmony_ci for (;;) { 26599ca880aSopenharmony_ci transportdev = udev_device_get_parent(transportdev); 26699ca880aSopenharmony_ci if (transportdev == NULL) 26799ca880aSopenharmony_ci return NULL; 26899ca880aSopenharmony_ci if (startswith(udev_device_get_sysname(transportdev), "session")) 26999ca880aSopenharmony_ci break; 27099ca880aSopenharmony_ci } 27199ca880aSopenharmony_ci 27299ca880aSopenharmony_ci /* find iscsi session device */ 27399ca880aSopenharmony_ci sessiondev = udev_device_new_from_subsystem_sysname(udev, "iscsi_session", udev_device_get_sysname(transportdev)); 27499ca880aSopenharmony_ci if (sessiondev == NULL) 27599ca880aSopenharmony_ci return NULL; 27699ca880aSopenharmony_ci target = udev_device_get_sysattr_value(sessiondev, "targetname"); 27799ca880aSopenharmony_ci if (target == NULL) { 27899ca880aSopenharmony_ci parent = NULL; 27999ca880aSopenharmony_ci goto out; 28099ca880aSopenharmony_ci } 28199ca880aSopenharmony_ci 28299ca880aSopenharmony_ci if (asprintf(&connname, "connection%s:0", udev_device_get_sysnum(transportdev)) < 0) { 28399ca880aSopenharmony_ci parent = NULL; 28499ca880aSopenharmony_ci goto out; 28599ca880aSopenharmony_ci } 28699ca880aSopenharmony_ci conndev = udev_device_new_from_subsystem_sysname(udev, "iscsi_connection", connname); 28799ca880aSopenharmony_ci free(connname); 28899ca880aSopenharmony_ci if (conndev == NULL) { 28999ca880aSopenharmony_ci parent = NULL; 29099ca880aSopenharmony_ci goto out; 29199ca880aSopenharmony_ci } 29299ca880aSopenharmony_ci addr = udev_device_get_sysattr_value(conndev, "persistent_address"); 29399ca880aSopenharmony_ci port = udev_device_get_sysattr_value(conndev, "persistent_port"); 29499ca880aSopenharmony_ci if (addr == NULL || port == NULL) { 29599ca880aSopenharmony_ci parent = NULL; 29699ca880aSopenharmony_ci goto out; 29799ca880aSopenharmony_ci } 29899ca880aSopenharmony_ci 29999ca880aSopenharmony_ci format_lun_number(parent, &lun); 30099ca880aSopenharmony_ci path_prepend(path, "ip-%s:%s-iscsi-%s-%s", addr, port, target, lun); 30199ca880aSopenharmony_ci if (lun) 30299ca880aSopenharmony_ci free(lun); 30399ca880aSopenharmony_ciout: 30499ca880aSopenharmony_ci udev_device_unref(sessiondev); 30599ca880aSopenharmony_ci udev_device_unref(conndev); 30699ca880aSopenharmony_ci return parent; 30799ca880aSopenharmony_ci} 30899ca880aSopenharmony_ci 30999ca880aSopenharmony_cistatic struct udev_device *handle_scsi_default(struct udev_device *parent, char **path) { 31099ca880aSopenharmony_ci struct udev_device *hostdev; 31199ca880aSopenharmony_ci int host, bus, target, lun; 31299ca880aSopenharmony_ci const char *name; 31399ca880aSopenharmony_ci char *base; 31499ca880aSopenharmony_ci char *pos; 31599ca880aSopenharmony_ci DIR *dir; 31699ca880aSopenharmony_ci struct dirent *dent; 31799ca880aSopenharmony_ci int basenum; 31899ca880aSopenharmony_ci 31999ca880aSopenharmony_ci hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host"); 32099ca880aSopenharmony_ci if (hostdev == NULL) 32199ca880aSopenharmony_ci return NULL; 32299ca880aSopenharmony_ci 32399ca880aSopenharmony_ci name = udev_device_get_sysname(parent); 32499ca880aSopenharmony_ci if (sscanf(name, "%d:%d:%d:%d", &host, &bus, &target, &lun) != 4) 32599ca880aSopenharmony_ci return NULL; 32699ca880aSopenharmony_ci 32799ca880aSopenharmony_ci /* 32899ca880aSopenharmony_ci * Rebase host offset to get the local relative number 32999ca880aSopenharmony_ci * 33099ca880aSopenharmony_ci * Note: This is by definition racy, unreliable and too simple. 33199ca880aSopenharmony_ci * Please do not copy this model anywhere. It's just a left-over 33299ca880aSopenharmony_ci * from the time we had no idea how things should look like in 33399ca880aSopenharmony_ci * the end. 33499ca880aSopenharmony_ci * 33599ca880aSopenharmony_ci * Making assumptions about a global in-kernel counter and use 33699ca880aSopenharmony_ci * that to calculate a local offset is a very broken concept. It 33799ca880aSopenharmony_ci * can only work as long as things are in strict order. 33899ca880aSopenharmony_ci * 33999ca880aSopenharmony_ci * The kernel needs to export the instance/port number of a 34099ca880aSopenharmony_ci * controller directly, without the need for rebase magic like 34199ca880aSopenharmony_ci * this. Manual driver unbind/bind, parallel hotplug/unplug will 34299ca880aSopenharmony_ci * get into the way of this "I hope it works" logic. 34399ca880aSopenharmony_ci */ 34499ca880aSopenharmony_ci basenum = -1; 34599ca880aSopenharmony_ci base = strdup(udev_device_get_syspath(hostdev)); 34699ca880aSopenharmony_ci if (base == NULL) 34799ca880aSopenharmony_ci return NULL; 34899ca880aSopenharmony_ci pos = strrchr(base, '/'); 34999ca880aSopenharmony_ci if (pos == NULL) { 35099ca880aSopenharmony_ci parent = NULL; 35199ca880aSopenharmony_ci goto out; 35299ca880aSopenharmony_ci } 35399ca880aSopenharmony_ci pos[0] = '\0'; 35499ca880aSopenharmony_ci dir = opendir(base); 35599ca880aSopenharmony_ci if (dir == NULL) { 35699ca880aSopenharmony_ci parent = NULL; 35799ca880aSopenharmony_ci goto out; 35899ca880aSopenharmony_ci } 35999ca880aSopenharmony_ci for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { 36099ca880aSopenharmony_ci char *rest; 36199ca880aSopenharmony_ci int i; 36299ca880aSopenharmony_ci 36399ca880aSopenharmony_ci if (dent->d_name[0] == '.') 36499ca880aSopenharmony_ci continue; 36599ca880aSopenharmony_ci if (dent->d_type != DT_DIR && dent->d_type != DT_LNK) 36699ca880aSopenharmony_ci continue; 36799ca880aSopenharmony_ci if (!startswith(dent->d_name, "host")) 36899ca880aSopenharmony_ci continue; 36999ca880aSopenharmony_ci i = strtoul(&dent->d_name[4], &rest, 10); 37099ca880aSopenharmony_ci if (rest[0] != '\0') 37199ca880aSopenharmony_ci continue; 37299ca880aSopenharmony_ci /* 37399ca880aSopenharmony_ci * find the smallest number; the host really needs to export its 37499ca880aSopenharmony_ci * own instance number per parent device; relying on the global host 37599ca880aSopenharmony_ci * enumeration and plainly rebasing the numbers sounds unreliable 37699ca880aSopenharmony_ci */ 37799ca880aSopenharmony_ci if (basenum == -1 || i < basenum) 37899ca880aSopenharmony_ci basenum = i; 37999ca880aSopenharmony_ci } 38099ca880aSopenharmony_ci closedir(dir); 38199ca880aSopenharmony_ci if (basenum == -1) { 38299ca880aSopenharmony_ci parent = NULL; 38399ca880aSopenharmony_ci goto out; 38499ca880aSopenharmony_ci } 38599ca880aSopenharmony_ci host -= basenum; 38699ca880aSopenharmony_ci 38799ca880aSopenharmony_ci path_prepend(path, "scsi-%u:%u:%u:%u", host, bus, target, lun); 38899ca880aSopenharmony_ciout: 38999ca880aSopenharmony_ci free(base); 39099ca880aSopenharmony_ci return hostdev; 39199ca880aSopenharmony_ci} 39299ca880aSopenharmony_ci 39399ca880aSopenharmony_cistatic struct udev_device *handle_scsi_hyperv(struct udev_device *parent, char **path) { 39499ca880aSopenharmony_ci struct udev_device *hostdev; 39599ca880aSopenharmony_ci struct udev_device *vmbusdev; 39699ca880aSopenharmony_ci const char *guid_str; 39799ca880aSopenharmony_ci char *lun = NULL; 39899ca880aSopenharmony_ci char guid[38]; 39999ca880aSopenharmony_ci size_t i, k; 40099ca880aSopenharmony_ci 40199ca880aSopenharmony_ci hostdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_host"); 40299ca880aSopenharmony_ci if (!hostdev) 40399ca880aSopenharmony_ci return NULL; 40499ca880aSopenharmony_ci 40599ca880aSopenharmony_ci vmbusdev = udev_device_get_parent(hostdev); 40699ca880aSopenharmony_ci if (!vmbusdev) 40799ca880aSopenharmony_ci return NULL; 40899ca880aSopenharmony_ci 40999ca880aSopenharmony_ci guid_str = udev_device_get_sysattr_value(vmbusdev, "device_id"); 41099ca880aSopenharmony_ci if (!guid_str) 41199ca880aSopenharmony_ci return NULL; 41299ca880aSopenharmony_ci 41399ca880aSopenharmony_ci if (strlen(guid_str) < 37 || guid_str[0] != '{' || guid_str[36] != '}') 41499ca880aSopenharmony_ci return NULL; 41599ca880aSopenharmony_ci 41699ca880aSopenharmony_ci for (i = 1, k = 0; i < 36; i++) { 41799ca880aSopenharmony_ci if (guid_str[i] == '-') 41899ca880aSopenharmony_ci continue; 41999ca880aSopenharmony_ci guid[k++] = guid_str[i]; 42099ca880aSopenharmony_ci } 42199ca880aSopenharmony_ci guid[k] = '\0'; 42299ca880aSopenharmony_ci 42399ca880aSopenharmony_ci format_lun_number(parent, &lun); 42499ca880aSopenharmony_ci path_prepend(path, "vmbus-%s-%s", guid, lun); 42599ca880aSopenharmony_ci free(lun); 42699ca880aSopenharmony_ci return parent; 42799ca880aSopenharmony_ci} 42899ca880aSopenharmony_ci 42999ca880aSopenharmony_cistatic struct udev_device *handle_scsi(struct udev_device *parent, char **path, bool *supported_parent) { 43099ca880aSopenharmony_ci const char *devtype; 43199ca880aSopenharmony_ci const char *name; 43299ca880aSopenharmony_ci const char *id; 43399ca880aSopenharmony_ci 43499ca880aSopenharmony_ci devtype = udev_device_get_devtype(parent); 43599ca880aSopenharmony_ci if (devtype == NULL || !streq(devtype, "scsi_device")) 43699ca880aSopenharmony_ci return parent; 43799ca880aSopenharmony_ci 43899ca880aSopenharmony_ci /* firewire */ 43999ca880aSopenharmony_ci id = udev_device_get_sysattr_value(parent, "ieee1394_id"); 44099ca880aSopenharmony_ci if (id != NULL) { 44199ca880aSopenharmony_ci parent = skip_subsystem(parent, "scsi"); 44299ca880aSopenharmony_ci path_prepend(path, "ieee1394-0x%s", id); 44399ca880aSopenharmony_ci *supported_parent = true; 44499ca880aSopenharmony_ci goto out; 44599ca880aSopenharmony_ci } 44699ca880aSopenharmony_ci 44799ca880aSopenharmony_ci /* scsi sysfs does not have a "subsystem" for the transport */ 44899ca880aSopenharmony_ci name = udev_device_get_syspath(parent); 44999ca880aSopenharmony_ci 45099ca880aSopenharmony_ci if (strstr(name, "/rport-") != NULL) { 45199ca880aSopenharmony_ci parent = handle_scsi_fibre_channel(parent, path); 45299ca880aSopenharmony_ci *supported_parent = true; 45399ca880aSopenharmony_ci goto out; 45499ca880aSopenharmony_ci } 45599ca880aSopenharmony_ci 45699ca880aSopenharmony_ci if (strstr(name, "/end_device-") != NULL) { 45799ca880aSopenharmony_ci parent = handle_scsi_sas(parent, path); 45899ca880aSopenharmony_ci *supported_parent = true; 45999ca880aSopenharmony_ci goto out; 46099ca880aSopenharmony_ci } 46199ca880aSopenharmony_ci 46299ca880aSopenharmony_ci if (strstr(name, "/session") != NULL) { 46399ca880aSopenharmony_ci parent = handle_scsi_iscsi(parent, path); 46499ca880aSopenharmony_ci *supported_parent = true; 46599ca880aSopenharmony_ci goto out; 46699ca880aSopenharmony_ci } 46799ca880aSopenharmony_ci 46899ca880aSopenharmony_ci /* 46999ca880aSopenharmony_ci * We do not support the ATA transport class, it uses global counters 47099ca880aSopenharmony_ci * to name the ata devices which numbers spread across multiple 47199ca880aSopenharmony_ci * controllers. 47299ca880aSopenharmony_ci * 47399ca880aSopenharmony_ci * The real link numbers are not exported. Also, possible chains of ports 47499ca880aSopenharmony_ci * behind port multipliers cannot be composed that way. 47599ca880aSopenharmony_ci * 47699ca880aSopenharmony_ci * Until all that is solved at the kernel level, there are no by-path/ 47799ca880aSopenharmony_ci * links for ATA devices. 47899ca880aSopenharmony_ci */ 47999ca880aSopenharmony_ci if (strstr(name, "/ata") != NULL) { 48099ca880aSopenharmony_ci parent = NULL; 48199ca880aSopenharmony_ci goto out; 48299ca880aSopenharmony_ci } 48399ca880aSopenharmony_ci 48499ca880aSopenharmony_ci if (strstr(name, "/vmbus_") != NULL) { 48599ca880aSopenharmony_ci parent = handle_scsi_hyperv(parent, path); 48699ca880aSopenharmony_ci goto out; 48799ca880aSopenharmony_ci } 48899ca880aSopenharmony_ci 48999ca880aSopenharmony_ci parent = handle_scsi_default(parent, path); 49099ca880aSopenharmony_ciout: 49199ca880aSopenharmony_ci return parent; 49299ca880aSopenharmony_ci} 49399ca880aSopenharmony_ci 49499ca880aSopenharmony_cistatic struct udev_device *handle_cciss(struct udev_device *parent, char **path) { 49599ca880aSopenharmony_ci const char *str; 49699ca880aSopenharmony_ci unsigned int controller, disk; 49799ca880aSopenharmony_ci 49899ca880aSopenharmony_ci str = udev_device_get_sysname(parent); 49999ca880aSopenharmony_ci if (sscanf(str, "c%ud%u%*s", &controller, &disk) != 2) 50099ca880aSopenharmony_ci return NULL; 50199ca880aSopenharmony_ci 50299ca880aSopenharmony_ci path_prepend(path, "cciss-disk%u", disk); 50399ca880aSopenharmony_ci parent = skip_subsystem(parent, "cciss"); 50499ca880aSopenharmony_ci return parent; 50599ca880aSopenharmony_ci} 50699ca880aSopenharmony_ci 50799ca880aSopenharmony_cistatic void handle_scsi_tape(struct udev_device *dev, char **path) { 50899ca880aSopenharmony_ci const char *name; 50999ca880aSopenharmony_ci 51099ca880aSopenharmony_ci /* must be the last device in the syspath */ 51199ca880aSopenharmony_ci if (*path != NULL) 51299ca880aSopenharmony_ci return; 51399ca880aSopenharmony_ci 51499ca880aSopenharmony_ci name = udev_device_get_sysname(dev); 51599ca880aSopenharmony_ci if (startswith(name, "nst") && strchr("lma", name[3]) != NULL) 51699ca880aSopenharmony_ci path_prepend(path, "nst%c", name[3]); 51799ca880aSopenharmony_ci else if (startswith(name, "st") && strchr("lma", name[2]) != NULL) 51899ca880aSopenharmony_ci path_prepend(path, "st%c", name[2]); 51999ca880aSopenharmony_ci} 52099ca880aSopenharmony_ci 52199ca880aSopenharmony_cistatic struct udev_device *handle_usb(struct udev_device *parent, char **path) { 52299ca880aSopenharmony_ci const char *devtype; 52399ca880aSopenharmony_ci const char *str; 52499ca880aSopenharmony_ci const char *port; 52599ca880aSopenharmony_ci 52699ca880aSopenharmony_ci devtype = udev_device_get_devtype(parent); 52799ca880aSopenharmony_ci if (devtype == NULL) 52899ca880aSopenharmony_ci return parent; 52999ca880aSopenharmony_ci if (!streq(devtype, "usb_interface") && !streq(devtype, "usb_device")) 53099ca880aSopenharmony_ci return parent; 53199ca880aSopenharmony_ci 53299ca880aSopenharmony_ci str = udev_device_get_sysname(parent); 53399ca880aSopenharmony_ci port = strchr(str, '-'); 53499ca880aSopenharmony_ci if (port == NULL) 53599ca880aSopenharmony_ci return parent; 53699ca880aSopenharmony_ci port++; 53799ca880aSopenharmony_ci 53899ca880aSopenharmony_ci parent = skip_subsystem(parent, "usb"); 53999ca880aSopenharmony_ci path_prepend(path, "usb-0:%s", port); 54099ca880aSopenharmony_ci return parent; 54199ca880aSopenharmony_ci} 54299ca880aSopenharmony_ci 54399ca880aSopenharmony_cistatic struct udev_device *handle_bcma(struct udev_device *parent, char **path) { 54499ca880aSopenharmony_ci const char *sysname; 54599ca880aSopenharmony_ci unsigned int core; 54699ca880aSopenharmony_ci 54799ca880aSopenharmony_ci sysname = udev_device_get_sysname(parent); 54899ca880aSopenharmony_ci if (sscanf(sysname, "bcma%*u:%u", &core) != 1) 54999ca880aSopenharmony_ci return NULL; 55099ca880aSopenharmony_ci 55199ca880aSopenharmony_ci path_prepend(path, "bcma-%u", core); 55299ca880aSopenharmony_ci return parent; 55399ca880aSopenharmony_ci} 55499ca880aSopenharmony_ci 55599ca880aSopenharmony_cistatic struct udev_device *handle_ccw(struct udev_device *parent, struct udev_device *dev, char **path) { 55699ca880aSopenharmony_ci struct udev_device *scsi_dev; 55799ca880aSopenharmony_ci 55899ca880aSopenharmony_ci scsi_dev = udev_device_get_parent_with_subsystem_devtype(dev, "scsi", "scsi_device"); 55999ca880aSopenharmony_ci if (scsi_dev != NULL) { 56099ca880aSopenharmony_ci const char *wwpn; 56199ca880aSopenharmony_ci const char *lun; 56299ca880aSopenharmony_ci const char *hba_id; 56399ca880aSopenharmony_ci 56499ca880aSopenharmony_ci hba_id = udev_device_get_sysattr_value(scsi_dev, "hba_id"); 56599ca880aSopenharmony_ci wwpn = udev_device_get_sysattr_value(scsi_dev, "wwpn"); 56699ca880aSopenharmony_ci lun = udev_device_get_sysattr_value(scsi_dev, "fcp_lun"); 56799ca880aSopenharmony_ci if (hba_id != NULL && lun != NULL && wwpn != NULL) { 56899ca880aSopenharmony_ci path_prepend(path, "ccw-%s-zfcp-%s:%s", hba_id, wwpn, lun); 56999ca880aSopenharmony_ci goto out; 57099ca880aSopenharmony_ci } 57199ca880aSopenharmony_ci } 57299ca880aSopenharmony_ci 57399ca880aSopenharmony_ci path_prepend(path, "ccw-%s", udev_device_get_sysname(parent)); 57499ca880aSopenharmony_ciout: 57599ca880aSopenharmony_ci parent = skip_subsystem(parent, "ccw"); 57699ca880aSopenharmony_ci return parent; 57799ca880aSopenharmony_ci} 57899ca880aSopenharmony_ci 57999ca880aSopenharmony_cistatic int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool test) { 58099ca880aSopenharmony_ci struct udev_device *parent; 58199ca880aSopenharmony_ci char *path = NULL; 58299ca880aSopenharmony_ci bool supported_transport = false; 58399ca880aSopenharmony_ci bool supported_parent = false; 58499ca880aSopenharmony_ci 58599ca880aSopenharmony_ci /* S390 ccw bus */ 58699ca880aSopenharmony_ci parent = udev_device_get_parent_with_subsystem_devtype(dev, "ccw", NULL); 58799ca880aSopenharmony_ci if (parent != NULL) { 58899ca880aSopenharmony_ci handle_ccw(parent, dev, &path); 58999ca880aSopenharmony_ci goto out; 59099ca880aSopenharmony_ci } 59199ca880aSopenharmony_ci 59299ca880aSopenharmony_ci /* walk up the chain of devices and compose path */ 59399ca880aSopenharmony_ci parent = dev; 59499ca880aSopenharmony_ci while (parent != NULL) { 59599ca880aSopenharmony_ci const char *subsys; 59699ca880aSopenharmony_ci 59799ca880aSopenharmony_ci subsys = udev_device_get_subsystem(parent); 59899ca880aSopenharmony_ci if (subsys == NULL) { 59999ca880aSopenharmony_ci ; 60099ca880aSopenharmony_ci } else if (streq(subsys, "scsi_tape")) { 60199ca880aSopenharmony_ci handle_scsi_tape(parent, &path); 60299ca880aSopenharmony_ci } else if (streq(subsys, "scsi")) { 60399ca880aSopenharmony_ci parent = handle_scsi(parent, &path, &supported_parent); 60499ca880aSopenharmony_ci supported_transport = true; 60599ca880aSopenharmony_ci } else if (streq(subsys, "cciss")) { 60699ca880aSopenharmony_ci parent = handle_cciss(parent, &path); 60799ca880aSopenharmony_ci supported_transport = true; 60899ca880aSopenharmony_ci } else if (streq(subsys, "usb")) { 60999ca880aSopenharmony_ci parent = handle_usb(parent, &path); 61099ca880aSopenharmony_ci supported_transport = true; 61199ca880aSopenharmony_ci } else if (streq(subsys, "bcma")) { 61299ca880aSopenharmony_ci parent = handle_bcma(parent, &path); 61399ca880aSopenharmony_ci supported_transport = true; 61499ca880aSopenharmony_ci } else if (streq(subsys, "serio")) { 61599ca880aSopenharmony_ci path_prepend(&path, "serio-%s", udev_device_get_sysnum(parent)); 61699ca880aSopenharmony_ci parent = skip_subsystem(parent, "serio"); 61799ca880aSopenharmony_ci } else if (streq(subsys, "pci")) { 61899ca880aSopenharmony_ci path_prepend(&path, "pci-%s", udev_device_get_sysname(parent)); 61999ca880aSopenharmony_ci parent = skip_subsystem(parent, "pci"); 62099ca880aSopenharmony_ci supported_parent = true; 62199ca880aSopenharmony_ci } else if (streq(subsys, "platform")) { 62299ca880aSopenharmony_ci path_prepend(&path, "platform-%s", udev_device_get_sysname(parent)); 62399ca880aSopenharmony_ci parent = skip_subsystem(parent, "platform"); 62499ca880aSopenharmony_ci supported_transport = true; 62599ca880aSopenharmony_ci supported_parent = true; 62699ca880aSopenharmony_ci } else if (streq(subsys, "acpi")) { 62799ca880aSopenharmony_ci path_prepend(&path, "acpi-%s", udev_device_get_sysname(parent)); 62899ca880aSopenharmony_ci parent = skip_subsystem(parent, "acpi"); 62999ca880aSopenharmony_ci supported_parent = true; 63099ca880aSopenharmony_ci } else if (streq(subsys, "xen")) { 63199ca880aSopenharmony_ci path_prepend(&path, "xen-%s", udev_device_get_sysname(parent)); 63299ca880aSopenharmony_ci parent = skip_subsystem(parent, "xen"); 63399ca880aSopenharmony_ci supported_parent = true; 63499ca880aSopenharmony_ci } else if (streq(subsys, "scm")) { 63599ca880aSopenharmony_ci path_prepend(&path, "scm-%s", udev_device_get_sysname(parent)); 63699ca880aSopenharmony_ci parent = skip_subsystem(parent, "scm"); 63799ca880aSopenharmony_ci supported_transport = true; 63899ca880aSopenharmony_ci supported_parent = true; 63999ca880aSopenharmony_ci } 64099ca880aSopenharmony_ci 64199ca880aSopenharmony_ci parent = udev_device_get_parent(parent); 64299ca880aSopenharmony_ci } 64399ca880aSopenharmony_ci 64499ca880aSopenharmony_ci /* 64599ca880aSopenharmony_ci * Do not return devices with an unknown parent device type. They 64699ca880aSopenharmony_ci * might produce conflicting IDs if the parent does not provide a 64799ca880aSopenharmony_ci * unique and predictable name. 64899ca880aSopenharmony_ci */ 64999ca880aSopenharmony_ci if (!supported_parent) { 65099ca880aSopenharmony_ci free(path); 65199ca880aSopenharmony_ci path = NULL; 65299ca880aSopenharmony_ci } 65399ca880aSopenharmony_ci 65499ca880aSopenharmony_ci /* 65599ca880aSopenharmony_ci * Do not return block devices without a well-known transport. Some 65699ca880aSopenharmony_ci * devices do not expose their buses and do not provide a unique 65799ca880aSopenharmony_ci * and predictable name that way. 65899ca880aSopenharmony_ci */ 65999ca880aSopenharmony_ci if (streq(udev_device_get_subsystem(dev), "block") && !supported_transport) { 66099ca880aSopenharmony_ci free(path); 66199ca880aSopenharmony_ci path = NULL; 66299ca880aSopenharmony_ci } 66399ca880aSopenharmony_ci 66499ca880aSopenharmony_ciout: 66599ca880aSopenharmony_ci if (path != NULL) { 66699ca880aSopenharmony_ci char tag[UTIL_NAME_SIZE]; 66799ca880aSopenharmony_ci size_t i; 66899ca880aSopenharmony_ci const char *p; 66999ca880aSopenharmony_ci 67099ca880aSopenharmony_ci /* compose valid udev tag name */ 67199ca880aSopenharmony_ci for (p = path, i = 0; *p; p++) { 67299ca880aSopenharmony_ci if ((*p >= '0' && *p <= '9') || 67399ca880aSopenharmony_ci (*p >= 'A' && *p <= 'Z') || 67499ca880aSopenharmony_ci (*p >= 'a' && *p <= 'z') || 67599ca880aSopenharmony_ci *p == '-') { 67699ca880aSopenharmony_ci tag[i++] = *p; 67799ca880aSopenharmony_ci continue; 67899ca880aSopenharmony_ci } 67999ca880aSopenharmony_ci 68099ca880aSopenharmony_ci /* skip all leading '_' */ 68199ca880aSopenharmony_ci if (i == 0) 68299ca880aSopenharmony_ci continue; 68399ca880aSopenharmony_ci 68499ca880aSopenharmony_ci /* avoid second '_' */ 68599ca880aSopenharmony_ci if (tag[i-1] == '_') 68699ca880aSopenharmony_ci continue; 68799ca880aSopenharmony_ci 68899ca880aSopenharmony_ci tag[i++] = '_'; 68999ca880aSopenharmony_ci } 69099ca880aSopenharmony_ci /* strip trailing '_' */ 69199ca880aSopenharmony_ci while (i > 0 && tag[i-1] == '_') 69299ca880aSopenharmony_ci i--; 69399ca880aSopenharmony_ci tag[i] = '\0'; 69499ca880aSopenharmony_ci 69599ca880aSopenharmony_ci udev_builtin_add_property(dev, test, "ID_PATH", path); 69699ca880aSopenharmony_ci udev_builtin_add_property(dev, test, "ID_PATH_TAG", tag); 69799ca880aSopenharmony_ci free(path); 69899ca880aSopenharmony_ci return EXIT_SUCCESS; 69999ca880aSopenharmony_ci } 70099ca880aSopenharmony_ci return EXIT_FAILURE; 70199ca880aSopenharmony_ci} 70299ca880aSopenharmony_ci 70399ca880aSopenharmony_ciconst struct udev_builtin udev_builtin_path_id = { 70499ca880aSopenharmony_ci .name = "path_id", 70599ca880aSopenharmony_ci .cmd = builtin_path_id, 70699ca880aSopenharmony_ci .help = "Compose persistent device path", 70799ca880aSopenharmony_ci .run_once = true, 70899ca880aSopenharmony_ci}; 709