+/**
+ * sysfs_get_classdev_device: returns the sysfs_device corresponding to
+ * sysfs_class_device, if present
+ * @clsdev: class device whose sysfs_device is required
+ * Returns sysfs_device on success, NULL on error or if device is not
+ * implemented
+ */
+struct sysfs_device *sysfs_get_classdev_device
+ (struct sysfs_class_device *clsdev)
+{
+ struct sysfs_link *devlink = NULL;
+ unsigned char devpath[SYSFS_PATH_MAX];
+
+ if (clsdev == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ strcpy(devpath, clsdev->path);
+ strcat(devpath, "/device");
+ if ((sysfs_path_is_link(devpath)) != 0) {
+ if (clsdev->sysdevice != NULL) {
+ sysfs_close_device(clsdev->sysdevice);
+ clsdev->sysdevice = NULL;
+ }
+ return NULL;
+ }
+
+ if (clsdev->directory == NULL) {
+ clsdev->directory = sysfs_open_directory(clsdev->path);
+ if (clsdev->directory == NULL)
+ return NULL;
+ }
+ devlink = sysfs_get_directory_link(clsdev->directory, "device");
+ if (devlink == NULL) {
+ if (clsdev->sysdevice != NULL) {
+ dprintf("Device link no longer exists\n");
+ sysfs_close_device(clsdev->sysdevice);
+ clsdev->sysdevice = NULL;
+ }
+ return NULL;
+ }
+
+ if (clsdev->sysdevice != NULL) {
+ if (!strncmp(devlink->target, clsdev->sysdevice->path,
+ SYSFS_PATH_MAX))
+ /* sysdevice hasn't changed */
+ return (clsdev->sysdevice);
+ else
+ /* come here only if the device link for has changed */
+ sysfs_close_device(clsdev->sysdevice);
+ }
+
+ clsdev->sysdevice = sysfs_open_device_path(devlink->target);
+ if (clsdev->sysdevice == NULL)
+ return NULL;
+ if (clsdev->driver != NULL)
+ strcpy(clsdev->sysdevice->driver_name, clsdev->driver->name);
+
+ return (clsdev->sysdevice);
+}
+
+/**
+ * sysfs_get_classdev_driver: returns the sysfs_driver corresponding to
+ * sysfs_class_device, if present
+ * @clsdev: class device whose sysfs_device is required
+ * Returns sysfs_driver on success, NULL on error or if driver is not
+ * implemented
+ */
+struct sysfs_driver *sysfs_get_classdev_driver
+ (struct sysfs_class_device *clsdev)
+{
+ struct sysfs_link *drvlink = NULL;
+ unsigned char drvpath[SYSFS_PATH_MAX];
+
+ if (clsdev == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ strcpy(drvpath, clsdev->path);
+ strcat(drvpath, "/driver");
+ if ((sysfs_path_is_link(drvpath)) != 0) {
+ if (clsdev->driver != NULL) {
+ sysfs_close_driver(clsdev->driver);
+ clsdev->driver = NULL;
+ }
+ return NULL;
+ }
+
+ if (clsdev->directory == NULL) {
+ clsdev->directory = sysfs_open_directory(clsdev->path);
+ if (clsdev->directory == NULL)
+ return NULL;
+ }
+ drvlink = sysfs_get_directory_link(clsdev->directory, "driver");
+ if (drvlink == NULL) {
+ if (clsdev->driver != NULL) {
+ dprintf("Driver link no longer exists\n");
+ sysfs_close_driver(clsdev->driver);
+ clsdev->driver = NULL;
+ }
+ return NULL;
+ }
+ if (clsdev->driver != NULL) {
+ if (!strncmp(drvlink->target, clsdev->driver->path,
+ SYSFS_PATH_MAX))
+ /* driver hasn't changed */
+ return (clsdev->driver);
+ else
+ /* come here only if the device link for has changed */
+ sysfs_close_driver(clsdev->driver);
+ }
+
+ clsdev->driver = sysfs_open_driver_path(drvlink->target);
+ if (clsdev->driver == NULL)
+ return NULL;
+ if (clsdev->sysdevice != NULL)
+ strcpy(clsdev->sysdevice->driver_name, clsdev->driver->name);
+
+ return (clsdev->driver);
+}
+
+/**
+ * get_blockdev_parent: Get the parent class device for a "block" subsystem
+ * device if present
+ * @clsdev: block subsystem class device whose parent needs to be found
+ * Returns 0 on success and 1 on error
+ */
+static int get_blockdev_parent(struct sysfs_class_device *clsdev)
+{
+ unsigned char parent_path[SYSFS_PATH_MAX], *c = NULL;
+
+ strcpy(parent_path, clsdev->path);
+ c = strstr(parent_path, SYSFS_BLOCK_NAME);
+ if (c == NULL) {
+ dprintf("Class device %s does not belong to BLOCK subsystem\n",
+ clsdev->name);
+ return 1;
+ }
+
+ c += strlen(SYSFS_BLOCK_NAME);
+ if (*c == '/')
+ c++;
+ else
+ goto errout;
+
+ /* validate whether the given class device is a partition or not */
+ if ((strncmp(c, clsdev->name, strlen(clsdev->name))) == 0) {
+ dprintf("%s not a partition\n", clsdev->name);
+ return 1;
+ }
+
+ c = strchr(c, '/');
+ if (c == NULL)
+ goto errout;
+
+ *c = '\0';
+
+ clsdev->parent = sysfs_open_class_device_path(parent_path);
+ if (clsdev->parent == NULL) {
+ dprintf("Error opening the parent class device at %s\n",
+ parent_path);
+ return 1;
+ }
+ return 0;
+
+errout:
+ dprintf("Invalid path %s\n", clsdev->path);
+ return 1;
+}
+
+/**
+ * sysfs_get_classdev_parent: Retrieves the parent of a class device.
+ * eg., when working with hda1, this function can be used to retrieve the
+ * sysfs_class_device for hda
+ *
+ * @clsdev: class device whose parent details are required.
+ * Returns sysfs_class_device of the parent on success, NULL on failure
+ */
+struct sysfs_class_device *sysfs_get_classdev_parent
+ (struct sysfs_class_device *clsdev)
+{
+ if (clsdev == NULL) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (clsdev->parent != NULL)
+ return (clsdev->parent);
+
+ /*
+ * As of now, only block devices have a parent child heirarchy in sysfs
+ * We do not know, if, in the future, more classes will have a similar
+ * structure. Hence, we now call a specialized function for block and
+ * later we can add support functions for other subsystems as required.
+ */
+ if (!(strcmp(clsdev->classname, SYSFS_BLOCK_NAME))) {
+ if ((get_blockdev_parent(clsdev)) == 0)
+ return (clsdev->parent);
+ }
+ return NULL;
+}
+