162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Interfaces to retrieve and set PDC Stable options (firmware) 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2005-2006 Thibaut VARENE <varenet@parisc-linux.org> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * DEV NOTE: the PDC Procedures reference states that: 862306a36Sopenharmony_ci * "A minimum of 96 bytes of Stable Storage is required. Providing more than 962306a36Sopenharmony_ci * 96 bytes of Stable Storage is optional [...]. Failure to provide the 1062306a36Sopenharmony_ci * optional locations from 96 to 192 results in the loss of certain 1162306a36Sopenharmony_ci * functionality during boot." 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Since locations between 96 and 192 are the various paths, most (if not 1462306a36Sopenharmony_ci * all) PA-RISC machines should have them. Anyway, for safety reasons, the 1562306a36Sopenharmony_ci * following code can deal with just 96 bytes of Stable Storage, and all 1662306a36Sopenharmony_ci * sizes between 96 and 192 bytes (provided they are multiple of struct 1762306a36Sopenharmony_ci * pdc_module_path size, eg: 128, 160 and 192) to provide full information. 1862306a36Sopenharmony_ci * One last word: there's one path we can always count on: the primary path. 1962306a36Sopenharmony_ci * Anything above 224 bytes is used for 'osdep2' OS-dependent storage area. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * The first OS-dependent area should always be available. Obviously, this is 2262306a36Sopenharmony_ci * not true for the other one. Also bear in mind that reading/writing from/to 2362306a36Sopenharmony_ci * osdep2 is much more expensive than from/to osdep1. 2462306a36Sopenharmony_ci * NOTE: We do not handle the 2 bytes OS-dep area at 0x5D, nor the first 2562306a36Sopenharmony_ci * 2 bytes of storage available right after OSID. That's a total of 4 bytes 2662306a36Sopenharmony_ci * sacrificed: -ETOOLAZY :P 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * The current policy wrt file permissions is: 2962306a36Sopenharmony_ci * - write: root only 3062306a36Sopenharmony_ci * - read: (reading triggers PDC calls) ? root only : everyone 3162306a36Sopenharmony_ci * The rationale is that PDC calls could hog (DoS) the machine. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * TODO: 3462306a36Sopenharmony_ci * - timer/fastsize write calls 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#undef PDCS_DEBUG 3862306a36Sopenharmony_ci#ifdef PDCS_DEBUG 3962306a36Sopenharmony_ci#define DPRINTK(fmt, args...) printk(KERN_DEBUG fmt, ## args) 4062306a36Sopenharmony_ci#else 4162306a36Sopenharmony_ci#define DPRINTK(fmt, args...) 4262306a36Sopenharmony_ci#endif 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#include <linux/module.h> 4562306a36Sopenharmony_ci#include <linux/init.h> 4662306a36Sopenharmony_ci#include <linux/kernel.h> 4762306a36Sopenharmony_ci#include <linux/string.h> 4862306a36Sopenharmony_ci#include <linux/capability.h> 4962306a36Sopenharmony_ci#include <linux/ctype.h> 5062306a36Sopenharmony_ci#include <linux/sysfs.h> 5162306a36Sopenharmony_ci#include <linux/kobject.h> 5262306a36Sopenharmony_ci#include <linux/device.h> 5362306a36Sopenharmony_ci#include <linux/errno.h> 5462306a36Sopenharmony_ci#include <linux/spinlock.h> 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#include <asm/pdc.h> 5762306a36Sopenharmony_ci#include <asm/page.h> 5862306a36Sopenharmony_ci#include <linux/uaccess.h> 5962306a36Sopenharmony_ci#include <asm/hardware.h> 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define PDCS_VERSION "0.30" 6262306a36Sopenharmony_ci#define PDCS_PREFIX "PDC Stable Storage" 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define PDCS_ADDR_PPRI 0x00 6562306a36Sopenharmony_ci#define PDCS_ADDR_OSID 0x40 6662306a36Sopenharmony_ci#define PDCS_ADDR_OSD1 0x48 6762306a36Sopenharmony_ci#define PDCS_ADDR_DIAG 0x58 6862306a36Sopenharmony_ci#define PDCS_ADDR_FSIZ 0x5C 6962306a36Sopenharmony_ci#define PDCS_ADDR_PCON 0x60 7062306a36Sopenharmony_ci#define PDCS_ADDR_PALT 0x80 7162306a36Sopenharmony_ci#define PDCS_ADDR_PKBD 0xA0 7262306a36Sopenharmony_ci#define PDCS_ADDR_OSD2 0xE0 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ciMODULE_AUTHOR("Thibaut VARENE <varenet@parisc-linux.org>"); 7562306a36Sopenharmony_ciMODULE_DESCRIPTION("sysfs interface to HP PDC Stable Storage data"); 7662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 7762306a36Sopenharmony_ciMODULE_VERSION(PDCS_VERSION); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* holds Stable Storage size. Initialized once and for all, no lock needed */ 8062306a36Sopenharmony_cistatic unsigned long pdcs_size __read_mostly; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* holds OS ID. Initialized once and for all, hopefully to 0x0006 */ 8362306a36Sopenharmony_cistatic u16 pdcs_osid __read_mostly; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* This struct defines what we need to deal with a parisc pdc path entry */ 8662306a36Sopenharmony_cistruct pdcspath_entry { 8762306a36Sopenharmony_ci rwlock_t rw_lock; /* to protect path entry access */ 8862306a36Sopenharmony_ci short ready; /* entry record is valid if != 0 */ 8962306a36Sopenharmony_ci unsigned long addr; /* entry address in stable storage */ 9062306a36Sopenharmony_ci char *name; /* entry name */ 9162306a36Sopenharmony_ci struct pdc_module_path devpath; /* device path in parisc representation */ 9262306a36Sopenharmony_ci struct device *dev; /* corresponding device */ 9362306a36Sopenharmony_ci struct kobject kobj; 9462306a36Sopenharmony_ci}; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistruct pdcspath_attribute { 9762306a36Sopenharmony_ci struct attribute attr; 9862306a36Sopenharmony_ci ssize_t (*show)(struct pdcspath_entry *entry, char *buf); 9962306a36Sopenharmony_ci ssize_t (*store)(struct pdcspath_entry *entry, const char *buf, size_t count); 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#define PDCSPATH_ENTRY(_addr, _name) \ 10362306a36Sopenharmony_cistruct pdcspath_entry pdcspath_entry_##_name = { \ 10462306a36Sopenharmony_ci .ready = 0, \ 10562306a36Sopenharmony_ci .addr = _addr, \ 10662306a36Sopenharmony_ci .name = __stringify(_name), \ 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define PDCS_ATTR(_name, _mode, _show, _store) \ 11062306a36Sopenharmony_cistruct kobj_attribute pdcs_attr_##_name = { \ 11162306a36Sopenharmony_ci .attr = {.name = __stringify(_name), .mode = _mode}, \ 11262306a36Sopenharmony_ci .show = _show, \ 11362306a36Sopenharmony_ci .store = _store, \ 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define PATHS_ATTR(_name, _mode, _show, _store) \ 11762306a36Sopenharmony_cistruct pdcspath_attribute paths_attr_##_name = { \ 11862306a36Sopenharmony_ci .attr = {.name = __stringify(_name), .mode = _mode}, \ 11962306a36Sopenharmony_ci .show = _show, \ 12062306a36Sopenharmony_ci .store = _store, \ 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci#define to_pdcspath_attribute(_attr) container_of(_attr, struct pdcspath_attribute, attr) 12462306a36Sopenharmony_ci#define to_pdcspath_entry(obj) container_of(obj, struct pdcspath_entry, kobj) 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/** 12762306a36Sopenharmony_ci * pdcspath_fetch - This function populates the path entry structs. 12862306a36Sopenharmony_ci * @entry: A pointer to an allocated pdcspath_entry. 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * The general idea is that you don't read from the Stable Storage every time 13162306a36Sopenharmony_ci * you access the files provided by the facilities. We store a copy of the 13262306a36Sopenharmony_ci * content of the stable storage WRT various paths in these structs. We read 13362306a36Sopenharmony_ci * these structs when reading the files, and we will write to these structs when 13462306a36Sopenharmony_ci * writing to the files, and only then write them back to the Stable Storage. 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * This function expects to be called with @entry->rw_lock write-hold. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_cistatic int 13962306a36Sopenharmony_cipdcspath_fetch(struct pdcspath_entry *entry) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct pdc_module_path *devpath; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (!entry) 14462306a36Sopenharmony_ci return -EINVAL; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci devpath = &entry->devpath; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci DPRINTK("%s: fetch: 0x%p, 0x%p, addr: 0x%lx\n", __func__, 14962306a36Sopenharmony_ci entry, devpath, entry->addr); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* addr, devpath and count must be word aligned */ 15262306a36Sopenharmony_ci if (pdc_stable_read(entry->addr, devpath, sizeof(*devpath)) != PDC_OK) 15362306a36Sopenharmony_ci return -EIO; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Find the matching device. 15662306a36Sopenharmony_ci NOTE: hardware_path overlays with pdc_module_path, so the nice cast can 15762306a36Sopenharmony_ci be used */ 15862306a36Sopenharmony_ci entry->dev = hwpath_to_device((struct hardware_path *)devpath); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci entry->ready = 1; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci DPRINTK("%s: device: 0x%p\n", __func__, entry->dev); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/** 16862306a36Sopenharmony_ci * pdcspath_store - This function writes a path to stable storage. 16962306a36Sopenharmony_ci * @entry: A pointer to an allocated pdcspath_entry. 17062306a36Sopenharmony_ci * 17162306a36Sopenharmony_ci * It can be used in two ways: either by passing it a preset devpath struct 17262306a36Sopenharmony_ci * containing an already computed hardware path, or by passing it a device 17362306a36Sopenharmony_ci * pointer, from which it'll find out the corresponding hardware path. 17462306a36Sopenharmony_ci * For now we do not handle the case where there's an error in writing to the 17562306a36Sopenharmony_ci * Stable Storage area, so you'd better not mess up the data :P 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * This function expects to be called with @entry->rw_lock write-hold. 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_cistatic void 18062306a36Sopenharmony_cipdcspath_store(struct pdcspath_entry *entry) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct pdc_module_path *devpath; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci BUG_ON(!entry); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci devpath = &entry->devpath; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* We expect the caller to set the ready flag to 0 if the hardware 18962306a36Sopenharmony_ci path struct provided is invalid, so that we know we have to fill it. 19062306a36Sopenharmony_ci First case, we don't have a preset hwpath... */ 19162306a36Sopenharmony_ci if (!entry->ready) { 19262306a36Sopenharmony_ci /* ...but we have a device, map it */ 19362306a36Sopenharmony_ci BUG_ON(!entry->dev); 19462306a36Sopenharmony_ci device_to_hwpath(entry->dev, (struct hardware_path *)devpath); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci /* else, we expect the provided hwpath to be valid. */ 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci DPRINTK("%s: store: 0x%p, 0x%p, addr: 0x%lx\n", __func__, 19962306a36Sopenharmony_ci entry, devpath, entry->addr); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* addr, devpath and count must be word aligned */ 20262306a36Sopenharmony_ci if (pdc_stable_write(entry->addr, devpath, sizeof(*devpath)) != PDC_OK) 20362306a36Sopenharmony_ci WARN(1, KERN_ERR "%s: an error occurred when writing to PDC.\n" 20462306a36Sopenharmony_ci "It is likely that the Stable Storage data has been corrupted.\n" 20562306a36Sopenharmony_ci "Please check it carefully upon next reboot.\n", __func__); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* kobject is already registered */ 20862306a36Sopenharmony_ci entry->ready = 2; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci DPRINTK("%s: device: 0x%p\n", __func__, entry->dev); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/** 21462306a36Sopenharmony_ci * pdcspath_hwpath_read - This function handles hardware path pretty printing. 21562306a36Sopenharmony_ci * @entry: An allocated and populated pdscpath_entry struct. 21662306a36Sopenharmony_ci * @buf: The output buffer to write to. 21762306a36Sopenharmony_ci * 21862306a36Sopenharmony_ci * We will call this function to format the output of the hwpath attribute file. 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_cistatic ssize_t 22162306a36Sopenharmony_cipdcspath_hwpath_read(struct pdcspath_entry *entry, char *buf) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci char *out = buf; 22462306a36Sopenharmony_ci struct pdc_module_path *devpath; 22562306a36Sopenharmony_ci short i; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (!entry || !buf) 22862306a36Sopenharmony_ci return -EINVAL; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci read_lock(&entry->rw_lock); 23162306a36Sopenharmony_ci devpath = &entry->devpath; 23262306a36Sopenharmony_ci i = entry->ready; 23362306a36Sopenharmony_ci read_unlock(&entry->rw_lock); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (!i) /* entry is not ready */ 23662306a36Sopenharmony_ci return -ENODATA; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci for (i = 0; i < 6; i++) { 23962306a36Sopenharmony_ci if (devpath->path.bc[i] < 0) 24062306a36Sopenharmony_ci continue; 24162306a36Sopenharmony_ci out += sprintf(out, "%d/", devpath->path.bc[i]); 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci out += sprintf(out, "%u\n", (unsigned char)devpath->path.mod); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci return out - buf; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci/** 24962306a36Sopenharmony_ci * pdcspath_hwpath_write - This function handles hardware path modifying. 25062306a36Sopenharmony_ci * @entry: An allocated and populated pdscpath_entry struct. 25162306a36Sopenharmony_ci * @buf: The input buffer to read from. 25262306a36Sopenharmony_ci * @count: The number of bytes to be read. 25362306a36Sopenharmony_ci * 25462306a36Sopenharmony_ci * We will call this function to change the current hardware path. 25562306a36Sopenharmony_ci * Hardware paths are to be given '/'-delimited, without brackets. 25662306a36Sopenharmony_ci * We make sure that the provided path actually maps to an existing 25762306a36Sopenharmony_ci * device, BUT nothing would prevent some foolish user to set the path to some 25862306a36Sopenharmony_ci * PCI bridge or even a CPU... 25962306a36Sopenharmony_ci * A better work around would be to make sure we are at the end of a device tree 26062306a36Sopenharmony_ci * for instance, but it would be IMHO beyond the simple scope of that driver. 26162306a36Sopenharmony_ci * The aim is to provide a facility. Data correctness is left to userland. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_cistatic ssize_t 26462306a36Sopenharmony_cipdcspath_hwpath_write(struct pdcspath_entry *entry, const char *buf, size_t count) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct hardware_path hwpath; 26762306a36Sopenharmony_ci unsigned short i; 26862306a36Sopenharmony_ci char in[64], *temp; 26962306a36Sopenharmony_ci struct device *dev; 27062306a36Sopenharmony_ci int ret; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (!entry || !buf || !count) 27362306a36Sopenharmony_ci return -EINVAL; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* We'll use a local copy of buf */ 27662306a36Sopenharmony_ci count = min_t(size_t, count, sizeof(in)-1); 27762306a36Sopenharmony_ci strscpy(in, buf, count + 1); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* Let's clean up the target. 0xff is a blank pattern */ 28062306a36Sopenharmony_ci memset(&hwpath, 0xff, sizeof(hwpath)); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* First, pick the mod field (the last one of the input string) */ 28362306a36Sopenharmony_ci if (!(temp = strrchr(in, '/'))) 28462306a36Sopenharmony_ci return -EINVAL; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci hwpath.mod = simple_strtoul(temp+1, NULL, 10); 28762306a36Sopenharmony_ci in[temp-in] = '\0'; /* truncate the remaining string. just precaution */ 28862306a36Sopenharmony_ci DPRINTK("%s: mod: %d\n", __func__, hwpath.mod); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* Then, loop for each delimiter, making sure we don't have too many. 29162306a36Sopenharmony_ci we write the bc fields in a down-top way. No matter what, we stop 29262306a36Sopenharmony_ci before writing the last field. If there are too many fields anyway, 29362306a36Sopenharmony_ci then the user is a moron and it'll be caught up later when we'll 29462306a36Sopenharmony_ci check the consistency of the given hwpath. */ 29562306a36Sopenharmony_ci for (i=5; ((temp = strrchr(in, '/'))) && (temp-in > 0) && (likely(i)); i--) { 29662306a36Sopenharmony_ci hwpath.bc[i] = simple_strtoul(temp+1, NULL, 10); 29762306a36Sopenharmony_ci in[temp-in] = '\0'; 29862306a36Sopenharmony_ci DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.path.bc[i]); 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* Store the final field */ 30262306a36Sopenharmony_ci hwpath.bc[i] = simple_strtoul(in, NULL, 10); 30362306a36Sopenharmony_ci DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.path.bc[i]); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* Now we check that the user isn't trying to lure us */ 30662306a36Sopenharmony_ci if (!(dev = hwpath_to_device((struct hardware_path *)&hwpath))) { 30762306a36Sopenharmony_ci printk(KERN_WARNING "%s: attempt to set invalid \"%s\" " 30862306a36Sopenharmony_ci "hardware path: %s\n", __func__, entry->name, buf); 30962306a36Sopenharmony_ci return -EINVAL; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* So far so good, let's get in deep */ 31362306a36Sopenharmony_ci write_lock(&entry->rw_lock); 31462306a36Sopenharmony_ci entry->ready = 0; 31562306a36Sopenharmony_ci entry->dev = dev; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* Now, dive in. Write back to the hardware */ 31862306a36Sopenharmony_ci pdcspath_store(entry); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* Update the symlink to the real device */ 32162306a36Sopenharmony_ci sysfs_remove_link(&entry->kobj, "device"); 32262306a36Sopenharmony_ci write_unlock(&entry->rw_lock); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci ret = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device"); 32562306a36Sopenharmony_ci WARN_ON(ret); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" path to \"%s\"\n", 32862306a36Sopenharmony_ci entry->name, buf); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return count; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/** 33462306a36Sopenharmony_ci * pdcspath_layer_read - Extended layer (eg. SCSI ids) pretty printing. 33562306a36Sopenharmony_ci * @entry: An allocated and populated pdscpath_entry struct. 33662306a36Sopenharmony_ci * @buf: The output buffer to write to. 33762306a36Sopenharmony_ci * 33862306a36Sopenharmony_ci * We will call this function to format the output of the layer attribute file. 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_cistatic ssize_t 34162306a36Sopenharmony_cipdcspath_layer_read(struct pdcspath_entry *entry, char *buf) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci char *out = buf; 34462306a36Sopenharmony_ci struct pdc_module_path *devpath; 34562306a36Sopenharmony_ci short i; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (!entry || !buf) 34862306a36Sopenharmony_ci return -EINVAL; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci read_lock(&entry->rw_lock); 35162306a36Sopenharmony_ci devpath = &entry->devpath; 35262306a36Sopenharmony_ci i = entry->ready; 35362306a36Sopenharmony_ci read_unlock(&entry->rw_lock); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci if (!i) /* entry is not ready */ 35662306a36Sopenharmony_ci return -ENODATA; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci for (i = 0; i < 6 && devpath->layers[i]; i++) 35962306a36Sopenharmony_ci out += sprintf(out, "%u ", devpath->layers[i]); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci out += sprintf(out, "\n"); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci return out - buf; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci/** 36762306a36Sopenharmony_ci * pdcspath_layer_write - This function handles extended layer modifying. 36862306a36Sopenharmony_ci * @entry: An allocated and populated pdscpath_entry struct. 36962306a36Sopenharmony_ci * @buf: The input buffer to read from. 37062306a36Sopenharmony_ci * @count: The number of bytes to be read. 37162306a36Sopenharmony_ci * 37262306a36Sopenharmony_ci * We will call this function to change the current layer value. 37362306a36Sopenharmony_ci * Layers are to be given '.'-delimited, without brackets. 37462306a36Sopenharmony_ci * XXX beware we are far less checky WRT input data provided than for hwpath. 37562306a36Sopenharmony_ci * Potential harm can be done, since there's no way to check the validity of 37662306a36Sopenharmony_ci * the layer fields. 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_cistatic ssize_t 37962306a36Sopenharmony_cipdcspath_layer_write(struct pdcspath_entry *entry, const char *buf, size_t count) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci unsigned int layers[6]; /* device-specific info (ctlr#, unit#, ...) */ 38262306a36Sopenharmony_ci unsigned short i; 38362306a36Sopenharmony_ci char in[64], *temp; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (!entry || !buf || !count) 38662306a36Sopenharmony_ci return -EINVAL; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* We'll use a local copy of buf */ 38962306a36Sopenharmony_ci count = min_t(size_t, count, sizeof(in)-1); 39062306a36Sopenharmony_ci strscpy(in, buf, count + 1); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* Let's clean up the target. 0 is a blank pattern */ 39362306a36Sopenharmony_ci memset(&layers, 0, sizeof(layers)); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* First, pick the first layer */ 39662306a36Sopenharmony_ci if (unlikely(!isdigit(*in))) 39762306a36Sopenharmony_ci return -EINVAL; 39862306a36Sopenharmony_ci layers[0] = simple_strtoul(in, NULL, 10); 39962306a36Sopenharmony_ci DPRINTK("%s: layer[0]: %d\n", __func__, layers[0]); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci temp = in; 40262306a36Sopenharmony_ci for (i=1; ((temp = strchr(temp, '.'))) && (likely(i<6)); i++) { 40362306a36Sopenharmony_ci if (unlikely(!isdigit(*(++temp)))) 40462306a36Sopenharmony_ci return -EINVAL; 40562306a36Sopenharmony_ci layers[i] = simple_strtoul(temp, NULL, 10); 40662306a36Sopenharmony_ci DPRINTK("%s: layer[%d]: %d\n", __func__, i, layers[i]); 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* So far so good, let's get in deep */ 41062306a36Sopenharmony_ci write_lock(&entry->rw_lock); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* First, overwrite the current layers with the new ones, not touching 41362306a36Sopenharmony_ci the hardware path. */ 41462306a36Sopenharmony_ci memcpy(&entry->devpath.layers, &layers, sizeof(layers)); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* Now, dive in. Write back to the hardware */ 41762306a36Sopenharmony_ci pdcspath_store(entry); 41862306a36Sopenharmony_ci write_unlock(&entry->rw_lock); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" layers to \"%s\"\n", 42162306a36Sopenharmony_ci entry->name, buf); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return count; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci/** 42762306a36Sopenharmony_ci * pdcspath_attr_show - Generic read function call wrapper. 42862306a36Sopenharmony_ci * @kobj: The kobject to get info from. 42962306a36Sopenharmony_ci * @attr: The attribute looked upon. 43062306a36Sopenharmony_ci * @buf: The output buffer. 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_cistatic ssize_t 43362306a36Sopenharmony_cipdcspath_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) 43462306a36Sopenharmony_ci{ 43562306a36Sopenharmony_ci struct pdcspath_entry *entry = to_pdcspath_entry(kobj); 43662306a36Sopenharmony_ci struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr); 43762306a36Sopenharmony_ci ssize_t ret = 0; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (pdcs_attr->show) 44062306a36Sopenharmony_ci ret = pdcs_attr->show(entry, buf); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return ret; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci/** 44662306a36Sopenharmony_ci * pdcspath_attr_store - Generic write function call wrapper. 44762306a36Sopenharmony_ci * @kobj: The kobject to write info to. 44862306a36Sopenharmony_ci * @attr: The attribute to be modified. 44962306a36Sopenharmony_ci * @buf: The input buffer. 45062306a36Sopenharmony_ci * @count: The size of the buffer. 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_cistatic ssize_t 45362306a36Sopenharmony_cipdcspath_attr_store(struct kobject *kobj, struct attribute *attr, 45462306a36Sopenharmony_ci const char *buf, size_t count) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct pdcspath_entry *entry = to_pdcspath_entry(kobj); 45762306a36Sopenharmony_ci struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr); 45862306a36Sopenharmony_ci ssize_t ret = 0; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 46162306a36Sopenharmony_ci return -EACCES; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (pdcs_attr->store) 46462306a36Sopenharmony_ci ret = pdcs_attr->store(entry, buf, count); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci return ret; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic const struct sysfs_ops pdcspath_attr_ops = { 47062306a36Sopenharmony_ci .show = pdcspath_attr_show, 47162306a36Sopenharmony_ci .store = pdcspath_attr_store, 47262306a36Sopenharmony_ci}; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci/* These are the two attributes of any PDC path. */ 47562306a36Sopenharmony_cistatic PATHS_ATTR(hwpath, 0644, pdcspath_hwpath_read, pdcspath_hwpath_write); 47662306a36Sopenharmony_cistatic PATHS_ATTR(layer, 0644, pdcspath_layer_read, pdcspath_layer_write); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic struct attribute *paths_subsys_attrs[] = { 47962306a36Sopenharmony_ci &paths_attr_hwpath.attr, 48062306a36Sopenharmony_ci &paths_attr_layer.attr, 48162306a36Sopenharmony_ci NULL, 48262306a36Sopenharmony_ci}; 48362306a36Sopenharmony_ciATTRIBUTE_GROUPS(paths_subsys); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci/* Specific kobject type for our PDC paths */ 48662306a36Sopenharmony_cistatic struct kobj_type ktype_pdcspath = { 48762306a36Sopenharmony_ci .sysfs_ops = &pdcspath_attr_ops, 48862306a36Sopenharmony_ci .default_groups = paths_subsys_groups, 48962306a36Sopenharmony_ci}; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci/* We hard define the 4 types of path we expect to find */ 49262306a36Sopenharmony_cistatic PDCSPATH_ENTRY(PDCS_ADDR_PPRI, primary); 49362306a36Sopenharmony_cistatic PDCSPATH_ENTRY(PDCS_ADDR_PCON, console); 49462306a36Sopenharmony_cistatic PDCSPATH_ENTRY(PDCS_ADDR_PALT, alternative); 49562306a36Sopenharmony_cistatic PDCSPATH_ENTRY(PDCS_ADDR_PKBD, keyboard); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci/* An array containing all PDC paths we will deal with */ 49862306a36Sopenharmony_cistatic struct pdcspath_entry *pdcspath_entries[] = { 49962306a36Sopenharmony_ci &pdcspath_entry_primary, 50062306a36Sopenharmony_ci &pdcspath_entry_alternative, 50162306a36Sopenharmony_ci &pdcspath_entry_console, 50262306a36Sopenharmony_ci &pdcspath_entry_keyboard, 50362306a36Sopenharmony_ci NULL, 50462306a36Sopenharmony_ci}; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci/* For more insight of what's going on here, refer to PDC Procedures doc, 50862306a36Sopenharmony_ci * Section PDC_STABLE */ 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci/** 51162306a36Sopenharmony_ci * pdcs_size_read - Stable Storage size output. 51262306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace. 51362306a36Sopenharmony_ci * @attr: The kobject attributes. 51462306a36Sopenharmony_ci * @buf: The output buffer to write to. 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_cistatic ssize_t pdcs_size_read(struct kobject *kobj, 51762306a36Sopenharmony_ci struct kobj_attribute *attr, 51862306a36Sopenharmony_ci char *buf) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci char *out = buf; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (!buf) 52362306a36Sopenharmony_ci return -EINVAL; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* show the size of the stable storage */ 52662306a36Sopenharmony_ci out += sprintf(out, "%ld\n", pdcs_size); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci return out - buf; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci/** 53262306a36Sopenharmony_ci * pdcs_auto_read - Stable Storage autoboot/search flag output. 53362306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace. 53462306a36Sopenharmony_ci * @attr: The kobject attributes. 53562306a36Sopenharmony_ci * @buf: The output buffer to write to. 53662306a36Sopenharmony_ci * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_cistatic ssize_t pdcs_auto_read(struct kobject *kobj, 53962306a36Sopenharmony_ci struct kobj_attribute *attr, 54062306a36Sopenharmony_ci char *buf, int knob) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci char *out = buf; 54362306a36Sopenharmony_ci struct pdcspath_entry *pathentry; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci if (!buf) 54662306a36Sopenharmony_ci return -EINVAL; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* Current flags are stored in primary boot path entry */ 54962306a36Sopenharmony_ci pathentry = &pdcspath_entry_primary; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci read_lock(&pathentry->rw_lock); 55262306a36Sopenharmony_ci out += sprintf(out, "%s\n", (pathentry->devpath.path.flags & knob) ? 55362306a36Sopenharmony_ci "On" : "Off"); 55462306a36Sopenharmony_ci read_unlock(&pathentry->rw_lock); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci return out - buf; 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci/** 56062306a36Sopenharmony_ci * pdcs_autoboot_read - Stable Storage autoboot flag output. 56162306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace. 56262306a36Sopenharmony_ci * @attr: The kobject attributes. 56362306a36Sopenharmony_ci * @buf: The output buffer to write to. 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_cistatic ssize_t pdcs_autoboot_read(struct kobject *kobj, 56662306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci return pdcs_auto_read(kobj, attr, buf, PF_AUTOBOOT); 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci/** 57262306a36Sopenharmony_ci * pdcs_autosearch_read - Stable Storage autoboot flag output. 57362306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace. 57462306a36Sopenharmony_ci * @attr: The kobject attributes. 57562306a36Sopenharmony_ci * @buf: The output buffer to write to. 57662306a36Sopenharmony_ci */ 57762306a36Sopenharmony_cistatic ssize_t pdcs_autosearch_read(struct kobject *kobj, 57862306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci return pdcs_auto_read(kobj, attr, buf, PF_AUTOSEARCH); 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci/** 58462306a36Sopenharmony_ci * pdcs_timer_read - Stable Storage timer count output (in seconds). 58562306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace. 58662306a36Sopenharmony_ci * @attr: The kobject attributes. 58762306a36Sopenharmony_ci * @buf: The output buffer to write to. 58862306a36Sopenharmony_ci * 58962306a36Sopenharmony_ci * The value of the timer field correponds to a number of seconds in powers of 2. 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_cistatic ssize_t pdcs_timer_read(struct kobject *kobj, 59262306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci char *out = buf; 59562306a36Sopenharmony_ci struct pdcspath_entry *pathentry; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (!buf) 59862306a36Sopenharmony_ci return -EINVAL; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* Current flags are stored in primary boot path entry */ 60162306a36Sopenharmony_ci pathentry = &pdcspath_entry_primary; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* print the timer value in seconds */ 60462306a36Sopenharmony_ci read_lock(&pathentry->rw_lock); 60562306a36Sopenharmony_ci out += sprintf(out, "%u\n", (pathentry->devpath.path.flags & PF_TIMER) ? 60662306a36Sopenharmony_ci (1 << (pathentry->devpath.path.flags & PF_TIMER)) : 0); 60762306a36Sopenharmony_ci read_unlock(&pathentry->rw_lock); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci return out - buf; 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci/** 61362306a36Sopenharmony_ci * pdcs_osid_read - Stable Storage OS ID register output. 61462306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace. 61562306a36Sopenharmony_ci * @attr: The kobject attributes. 61662306a36Sopenharmony_ci * @buf: The output buffer to write to. 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_cistatic ssize_t pdcs_osid_read(struct kobject *kobj, 61962306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci char *out = buf; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (!buf) 62462306a36Sopenharmony_ci return -EINVAL; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci out += sprintf(out, "%s dependent data (0x%.4x)\n", 62762306a36Sopenharmony_ci os_id_to_string(pdcs_osid), pdcs_osid); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci return out - buf; 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci/** 63362306a36Sopenharmony_ci * pdcs_osdep1_read - Stable Storage OS-Dependent data area 1 output. 63462306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace. 63562306a36Sopenharmony_ci * @attr: The kobject attributes. 63662306a36Sopenharmony_ci * @buf: The output buffer to write to. 63762306a36Sopenharmony_ci * 63862306a36Sopenharmony_ci * This can hold 16 bytes of OS-Dependent data. 63962306a36Sopenharmony_ci */ 64062306a36Sopenharmony_cistatic ssize_t pdcs_osdep1_read(struct kobject *kobj, 64162306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci char *out = buf; 64462306a36Sopenharmony_ci u32 result[4]; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (!buf) 64762306a36Sopenharmony_ci return -EINVAL; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci if (pdc_stable_read(PDCS_ADDR_OSD1, &result, sizeof(result)) != PDC_OK) 65062306a36Sopenharmony_ci return -EIO; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci out += sprintf(out, "0x%.8x\n", result[0]); 65362306a36Sopenharmony_ci out += sprintf(out, "0x%.8x\n", result[1]); 65462306a36Sopenharmony_ci out += sprintf(out, "0x%.8x\n", result[2]); 65562306a36Sopenharmony_ci out += sprintf(out, "0x%.8x\n", result[3]); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return out - buf; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci/** 66162306a36Sopenharmony_ci * pdcs_diagnostic_read - Stable Storage Diagnostic register output. 66262306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace. 66362306a36Sopenharmony_ci * @attr: The kobject attributes. 66462306a36Sopenharmony_ci * @buf: The output buffer to write to. 66562306a36Sopenharmony_ci * 66662306a36Sopenharmony_ci * I have NFC how to interpret the content of that register ;-). 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_cistatic ssize_t pdcs_diagnostic_read(struct kobject *kobj, 66962306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci char *out = buf; 67262306a36Sopenharmony_ci u32 result; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci if (!buf) 67562306a36Sopenharmony_ci return -EINVAL; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci /* get diagnostic */ 67862306a36Sopenharmony_ci if (pdc_stable_read(PDCS_ADDR_DIAG, &result, sizeof(result)) != PDC_OK) 67962306a36Sopenharmony_ci return -EIO; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci out += sprintf(out, "0x%.4x\n", (result >> 16)); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci return out - buf; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci/** 68762306a36Sopenharmony_ci * pdcs_fastsize_read - Stable Storage FastSize register output. 68862306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace. 68962306a36Sopenharmony_ci * @attr: The kobject attributes. 69062306a36Sopenharmony_ci * @buf: The output buffer to write to. 69162306a36Sopenharmony_ci * 69262306a36Sopenharmony_ci * This register holds the amount of system RAM to be tested during boot sequence. 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_cistatic ssize_t pdcs_fastsize_read(struct kobject *kobj, 69562306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci char *out = buf; 69862306a36Sopenharmony_ci u32 result; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (!buf) 70162306a36Sopenharmony_ci return -EINVAL; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci /* get fast-size */ 70462306a36Sopenharmony_ci if (pdc_stable_read(PDCS_ADDR_FSIZ, &result, sizeof(result)) != PDC_OK) 70562306a36Sopenharmony_ci return -EIO; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if ((result & 0x0F) < 0x0E) 70862306a36Sopenharmony_ci out += sprintf(out, "%d kB", (1<<(result & 0x0F))*256); 70962306a36Sopenharmony_ci else 71062306a36Sopenharmony_ci out += sprintf(out, "All"); 71162306a36Sopenharmony_ci out += sprintf(out, "\n"); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci return out - buf; 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci/** 71762306a36Sopenharmony_ci * pdcs_osdep2_read - Stable Storage OS-Dependent data area 2 output. 71862306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace. 71962306a36Sopenharmony_ci * @attr: The kobject attributes. 72062306a36Sopenharmony_ci * @buf: The output buffer to write to. 72162306a36Sopenharmony_ci * 72262306a36Sopenharmony_ci * This can hold pdcs_size - 224 bytes of OS-Dependent data, when available. 72362306a36Sopenharmony_ci */ 72462306a36Sopenharmony_cistatic ssize_t pdcs_osdep2_read(struct kobject *kobj, 72562306a36Sopenharmony_ci struct kobj_attribute *attr, char *buf) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci char *out = buf; 72862306a36Sopenharmony_ci unsigned long size; 72962306a36Sopenharmony_ci unsigned short i; 73062306a36Sopenharmony_ci u32 result; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (unlikely(pdcs_size <= 224)) 73362306a36Sopenharmony_ci return -ENODATA; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci size = pdcs_size - 224; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci if (!buf) 73862306a36Sopenharmony_ci return -EINVAL; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci for (i=0; i<size; i+=4) { 74162306a36Sopenharmony_ci if (unlikely(pdc_stable_read(PDCS_ADDR_OSD2 + i, &result, 74262306a36Sopenharmony_ci sizeof(result)) != PDC_OK)) 74362306a36Sopenharmony_ci return -EIO; 74462306a36Sopenharmony_ci out += sprintf(out, "0x%.8x\n", result); 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci return out - buf; 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci/** 75162306a36Sopenharmony_ci * pdcs_auto_write - This function handles autoboot/search flag modifying. 75262306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace. 75362306a36Sopenharmony_ci * @attr: The kobject attributes. 75462306a36Sopenharmony_ci * @buf: The input buffer to read from. 75562306a36Sopenharmony_ci * @count: The number of bytes to be read. 75662306a36Sopenharmony_ci * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag 75762306a36Sopenharmony_ci * 75862306a36Sopenharmony_ci * We will call this function to change the current autoboot flag. 75962306a36Sopenharmony_ci * We expect a precise syntax: 76062306a36Sopenharmony_ci * \"n\" (n == 0 or 1) to toggle AutoBoot Off or On 76162306a36Sopenharmony_ci */ 76262306a36Sopenharmony_cistatic ssize_t pdcs_auto_write(struct kobject *kobj, 76362306a36Sopenharmony_ci struct kobj_attribute *attr, const char *buf, 76462306a36Sopenharmony_ci size_t count, int knob) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci struct pdcspath_entry *pathentry; 76762306a36Sopenharmony_ci unsigned char flags; 76862306a36Sopenharmony_ci char in[8], *temp; 76962306a36Sopenharmony_ci char c; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 77262306a36Sopenharmony_ci return -EACCES; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (!buf || !count) 77562306a36Sopenharmony_ci return -EINVAL; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* We'll use a local copy of buf */ 77862306a36Sopenharmony_ci count = min_t(size_t, count, sizeof(in)-1); 77962306a36Sopenharmony_ci strscpy(in, buf, count + 1); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* Current flags are stored in primary boot path entry */ 78262306a36Sopenharmony_ci pathentry = &pdcspath_entry_primary; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* Be nice to the existing flag record */ 78562306a36Sopenharmony_ci read_lock(&pathentry->rw_lock); 78662306a36Sopenharmony_ci flags = pathentry->devpath.path.flags; 78762306a36Sopenharmony_ci read_unlock(&pathentry->rw_lock); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci DPRINTK("%s: flags before: 0x%X\n", __func__, flags); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci temp = skip_spaces(in); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci c = *temp++ - '0'; 79462306a36Sopenharmony_ci if ((c != 0) && (c != 1)) 79562306a36Sopenharmony_ci goto parse_error; 79662306a36Sopenharmony_ci if (c == 0) 79762306a36Sopenharmony_ci flags &= ~knob; 79862306a36Sopenharmony_ci else 79962306a36Sopenharmony_ci flags |= knob; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci DPRINTK("%s: flags after: 0x%X\n", __func__, flags); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci /* So far so good, let's get in deep */ 80462306a36Sopenharmony_ci write_lock(&pathentry->rw_lock); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci /* Change the path entry flags first */ 80762306a36Sopenharmony_ci pathentry->devpath.path.flags = flags; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* Now, dive in. Write back to the hardware */ 81062306a36Sopenharmony_ci pdcspath_store(pathentry); 81162306a36Sopenharmony_ci write_unlock(&pathentry->rw_lock); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" to \"%s\"\n", 81462306a36Sopenharmony_ci (knob & PF_AUTOBOOT) ? "autoboot" : "autosearch", 81562306a36Sopenharmony_ci (flags & knob) ? "On" : "Off"); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci return count; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ciparse_error: 82062306a36Sopenharmony_ci printk(KERN_WARNING "%s: Parse error: expect \"n\" (n == 0 or 1)\n", __func__); 82162306a36Sopenharmony_ci return -EINVAL; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci/** 82562306a36Sopenharmony_ci * pdcs_autoboot_write - This function handles autoboot flag modifying. 82662306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace. 82762306a36Sopenharmony_ci * @attr: The kobject attributes. 82862306a36Sopenharmony_ci * @buf: The input buffer to read from. 82962306a36Sopenharmony_ci * @count: The number of bytes to be read. 83062306a36Sopenharmony_ci * 83162306a36Sopenharmony_ci * We will call this function to change the current boot flags. 83262306a36Sopenharmony_ci * We expect a precise syntax: 83362306a36Sopenharmony_ci * \"n\" (n == 0 or 1) to toggle AutoSearch Off or On 83462306a36Sopenharmony_ci */ 83562306a36Sopenharmony_cistatic ssize_t pdcs_autoboot_write(struct kobject *kobj, 83662306a36Sopenharmony_ci struct kobj_attribute *attr, 83762306a36Sopenharmony_ci const char *buf, size_t count) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci return pdcs_auto_write(kobj, attr, buf, count, PF_AUTOBOOT); 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci/** 84362306a36Sopenharmony_ci * pdcs_autosearch_write - This function handles autosearch flag modifying. 84462306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace. 84562306a36Sopenharmony_ci * @attr: The kobject attributes. 84662306a36Sopenharmony_ci * @buf: The input buffer to read from. 84762306a36Sopenharmony_ci * @count: The number of bytes to be read. 84862306a36Sopenharmony_ci * 84962306a36Sopenharmony_ci * We will call this function to change the current boot flags. 85062306a36Sopenharmony_ci * We expect a precise syntax: 85162306a36Sopenharmony_ci * \"n\" (n == 0 or 1) to toggle AutoSearch Off or On 85262306a36Sopenharmony_ci */ 85362306a36Sopenharmony_cistatic ssize_t pdcs_autosearch_write(struct kobject *kobj, 85462306a36Sopenharmony_ci struct kobj_attribute *attr, 85562306a36Sopenharmony_ci const char *buf, size_t count) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci return pdcs_auto_write(kobj, attr, buf, count, PF_AUTOSEARCH); 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci/** 86162306a36Sopenharmony_ci * pdcs_osdep1_write - Stable Storage OS-Dependent data area 1 input. 86262306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace. 86362306a36Sopenharmony_ci * @attr: The kobject attributes. 86462306a36Sopenharmony_ci * @buf: The input buffer to read from. 86562306a36Sopenharmony_ci * @count: The number of bytes to be read. 86662306a36Sopenharmony_ci * 86762306a36Sopenharmony_ci * This can store 16 bytes of OS-Dependent data. We use a byte-by-byte 86862306a36Sopenharmony_ci * write approach. It's up to userspace to deal with it when constructing 86962306a36Sopenharmony_ci * its input buffer. 87062306a36Sopenharmony_ci */ 87162306a36Sopenharmony_cistatic ssize_t pdcs_osdep1_write(struct kobject *kobj, 87262306a36Sopenharmony_ci struct kobj_attribute *attr, 87362306a36Sopenharmony_ci const char *buf, size_t count) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci u8 in[16]; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 87862306a36Sopenharmony_ci return -EACCES; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (!buf || !count) 88162306a36Sopenharmony_ci return -EINVAL; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci if (unlikely(pdcs_osid != OS_ID_LINUX)) 88462306a36Sopenharmony_ci return -EPERM; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci if (count > 16) 88762306a36Sopenharmony_ci return -EMSGSIZE; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* We'll use a local copy of buf */ 89062306a36Sopenharmony_ci memset(in, 0, 16); 89162306a36Sopenharmony_ci memcpy(in, buf, count); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci if (pdc_stable_write(PDCS_ADDR_OSD1, &in, sizeof(in)) != PDC_OK) 89462306a36Sopenharmony_ci return -EIO; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci return count; 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci/** 90062306a36Sopenharmony_ci * pdcs_osdep2_write - Stable Storage OS-Dependent data area 2 input. 90162306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace. 90262306a36Sopenharmony_ci * @attr: The kobject attributes. 90362306a36Sopenharmony_ci * @buf: The input buffer to read from. 90462306a36Sopenharmony_ci * @count: The number of bytes to be read. 90562306a36Sopenharmony_ci * 90662306a36Sopenharmony_ci * This can store pdcs_size - 224 bytes of OS-Dependent data. We use a 90762306a36Sopenharmony_ci * byte-by-byte write approach. It's up to userspace to deal with it when 90862306a36Sopenharmony_ci * constructing its input buffer. 90962306a36Sopenharmony_ci */ 91062306a36Sopenharmony_cistatic ssize_t pdcs_osdep2_write(struct kobject *kobj, 91162306a36Sopenharmony_ci struct kobj_attribute *attr, 91262306a36Sopenharmony_ci const char *buf, size_t count) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci unsigned long size; 91562306a36Sopenharmony_ci unsigned short i; 91662306a36Sopenharmony_ci u8 in[4]; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 91962306a36Sopenharmony_ci return -EACCES; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci if (!buf || !count) 92262306a36Sopenharmony_ci return -EINVAL; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci if (unlikely(pdcs_size <= 224)) 92562306a36Sopenharmony_ci return -ENOSYS; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci if (unlikely(pdcs_osid != OS_ID_LINUX)) 92862306a36Sopenharmony_ci return -EPERM; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci size = pdcs_size - 224; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (count > size) 93362306a36Sopenharmony_ci return -EMSGSIZE; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* We'll use a local copy of buf */ 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci for (i=0; i<count; i+=4) { 93862306a36Sopenharmony_ci memset(in, 0, 4); 93962306a36Sopenharmony_ci memcpy(in, buf+i, (count-i < 4) ? count-i : 4); 94062306a36Sopenharmony_ci if (unlikely(pdc_stable_write(PDCS_ADDR_OSD2 + i, &in, 94162306a36Sopenharmony_ci sizeof(in)) != PDC_OK)) 94262306a36Sopenharmony_ci return -EIO; 94362306a36Sopenharmony_ci } 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci return count; 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci/* The remaining attributes. */ 94962306a36Sopenharmony_cistatic PDCS_ATTR(size, 0444, pdcs_size_read, NULL); 95062306a36Sopenharmony_cistatic PDCS_ATTR(autoboot, 0644, pdcs_autoboot_read, pdcs_autoboot_write); 95162306a36Sopenharmony_cistatic PDCS_ATTR(autosearch, 0644, pdcs_autosearch_read, pdcs_autosearch_write); 95262306a36Sopenharmony_cistatic PDCS_ATTR(timer, 0444, pdcs_timer_read, NULL); 95362306a36Sopenharmony_cistatic PDCS_ATTR(osid, 0444, pdcs_osid_read, NULL); 95462306a36Sopenharmony_cistatic PDCS_ATTR(osdep1, 0600, pdcs_osdep1_read, pdcs_osdep1_write); 95562306a36Sopenharmony_cistatic PDCS_ATTR(diagnostic, 0400, pdcs_diagnostic_read, NULL); 95662306a36Sopenharmony_cistatic PDCS_ATTR(fastsize, 0400, pdcs_fastsize_read, NULL); 95762306a36Sopenharmony_cistatic PDCS_ATTR(osdep2, 0600, pdcs_osdep2_read, pdcs_osdep2_write); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic struct attribute *pdcs_subsys_attrs[] = { 96062306a36Sopenharmony_ci &pdcs_attr_size.attr, 96162306a36Sopenharmony_ci &pdcs_attr_autoboot.attr, 96262306a36Sopenharmony_ci &pdcs_attr_autosearch.attr, 96362306a36Sopenharmony_ci &pdcs_attr_timer.attr, 96462306a36Sopenharmony_ci &pdcs_attr_osid.attr, 96562306a36Sopenharmony_ci &pdcs_attr_osdep1.attr, 96662306a36Sopenharmony_ci &pdcs_attr_diagnostic.attr, 96762306a36Sopenharmony_ci &pdcs_attr_fastsize.attr, 96862306a36Sopenharmony_ci &pdcs_attr_osdep2.attr, 96962306a36Sopenharmony_ci NULL, 97062306a36Sopenharmony_ci}; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic const struct attribute_group pdcs_attr_group = { 97362306a36Sopenharmony_ci .attrs = pdcs_subsys_attrs, 97462306a36Sopenharmony_ci}; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic struct kobject *stable_kobj; 97762306a36Sopenharmony_cistatic struct kset *paths_kset; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci/** 98062306a36Sopenharmony_ci * pdcs_register_pathentries - Prepares path entries kobjects for sysfs usage. 98162306a36Sopenharmony_ci * 98262306a36Sopenharmony_ci * It creates kobjects corresponding to each path entry with nice sysfs 98362306a36Sopenharmony_ci * links to the real device. This is where the magic takes place: when 98462306a36Sopenharmony_ci * registering the subsystem attributes during module init, each kobject hereby 98562306a36Sopenharmony_ci * created will show in the sysfs tree as a folder containing files as defined 98662306a36Sopenharmony_ci * by path_subsys_attr[]. 98762306a36Sopenharmony_ci */ 98862306a36Sopenharmony_cistatic inline int __init 98962306a36Sopenharmony_cipdcs_register_pathentries(void) 99062306a36Sopenharmony_ci{ 99162306a36Sopenharmony_ci unsigned short i; 99262306a36Sopenharmony_ci struct pdcspath_entry *entry; 99362306a36Sopenharmony_ci int err; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci /* Initialize the entries rw_lock before anything else */ 99662306a36Sopenharmony_ci for (i = 0; (entry = pdcspath_entries[i]); i++) 99762306a36Sopenharmony_ci rwlock_init(&entry->rw_lock); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci for (i = 0; (entry = pdcspath_entries[i]); i++) { 100062306a36Sopenharmony_ci write_lock(&entry->rw_lock); 100162306a36Sopenharmony_ci err = pdcspath_fetch(entry); 100262306a36Sopenharmony_ci write_unlock(&entry->rw_lock); 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci if (err < 0) 100562306a36Sopenharmony_ci continue; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci entry->kobj.kset = paths_kset; 100862306a36Sopenharmony_ci err = kobject_init_and_add(&entry->kobj, &ktype_pdcspath, NULL, 100962306a36Sopenharmony_ci "%s", entry->name); 101062306a36Sopenharmony_ci if (err) { 101162306a36Sopenharmony_ci kobject_put(&entry->kobj); 101262306a36Sopenharmony_ci return err; 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci /* kobject is now registered */ 101662306a36Sopenharmony_ci write_lock(&entry->rw_lock); 101762306a36Sopenharmony_ci entry->ready = 2; 101862306a36Sopenharmony_ci write_unlock(&entry->rw_lock); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci /* Add a nice symlink to the real device */ 102162306a36Sopenharmony_ci if (entry->dev) { 102262306a36Sopenharmony_ci err = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device"); 102362306a36Sopenharmony_ci WARN_ON(err); 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci kobject_uevent(&entry->kobj, KOBJ_ADD); 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci return 0; 103062306a36Sopenharmony_ci} 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci/** 103362306a36Sopenharmony_ci * pdcs_unregister_pathentries - Routine called when unregistering the module. 103462306a36Sopenharmony_ci */ 103562306a36Sopenharmony_cistatic inline void 103662306a36Sopenharmony_cipdcs_unregister_pathentries(void) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci unsigned short i; 103962306a36Sopenharmony_ci struct pdcspath_entry *entry; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci for (i = 0; (entry = pdcspath_entries[i]); i++) { 104262306a36Sopenharmony_ci read_lock(&entry->rw_lock); 104362306a36Sopenharmony_ci if (entry->ready >= 2) 104462306a36Sopenharmony_ci kobject_put(&entry->kobj); 104562306a36Sopenharmony_ci read_unlock(&entry->rw_lock); 104662306a36Sopenharmony_ci } 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci/* 105062306a36Sopenharmony_ci * For now we register the stable subsystem with the firmware subsystem 105162306a36Sopenharmony_ci * and the paths subsystem with the stable subsystem 105262306a36Sopenharmony_ci */ 105362306a36Sopenharmony_cistatic int __init 105462306a36Sopenharmony_cipdc_stable_init(void) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci int rc = 0, error; 105762306a36Sopenharmony_ci u32 result; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci /* find the size of the stable storage */ 106062306a36Sopenharmony_ci if (pdc_stable_get_size(&pdcs_size) != PDC_OK) 106162306a36Sopenharmony_ci return -ENODEV; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci /* make sure we have enough data */ 106462306a36Sopenharmony_ci if (pdcs_size < 96) 106562306a36Sopenharmony_ci return -ENODATA; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci printk(KERN_INFO PDCS_PREFIX " facility v%s\n", PDCS_VERSION); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* get OSID */ 107062306a36Sopenharmony_ci if (pdc_stable_read(PDCS_ADDR_OSID, &result, sizeof(result)) != PDC_OK) 107162306a36Sopenharmony_ci return -EIO; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci /* the actual result is 16 bits away */ 107462306a36Sopenharmony_ci pdcs_osid = (u16)(result >> 16); 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci /* For now we'll register the directory at /sys/firmware/stable */ 107762306a36Sopenharmony_ci stable_kobj = kobject_create_and_add("stable", firmware_kobj); 107862306a36Sopenharmony_ci if (!stable_kobj) { 107962306a36Sopenharmony_ci rc = -ENOMEM; 108062306a36Sopenharmony_ci goto fail_firmreg; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci /* Don't forget the root entries */ 108462306a36Sopenharmony_ci error = sysfs_create_group(stable_kobj, &pdcs_attr_group); 108562306a36Sopenharmony_ci if (error) { 108662306a36Sopenharmony_ci rc = -ENOMEM; 108762306a36Sopenharmony_ci goto fail_ksetreg; 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci /* register the paths kset as a child of the stable kset */ 109162306a36Sopenharmony_ci paths_kset = kset_create_and_add("paths", NULL, stable_kobj); 109262306a36Sopenharmony_ci if (!paths_kset) { 109362306a36Sopenharmony_ci rc = -ENOMEM; 109462306a36Sopenharmony_ci goto fail_ksetreg; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci /* now we create all "files" for the paths kset */ 109862306a36Sopenharmony_ci if ((rc = pdcs_register_pathentries())) 109962306a36Sopenharmony_ci goto fail_pdcsreg; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci return rc; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_cifail_pdcsreg: 110462306a36Sopenharmony_ci pdcs_unregister_pathentries(); 110562306a36Sopenharmony_ci kset_unregister(paths_kset); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_cifail_ksetreg: 110862306a36Sopenharmony_ci kobject_put(stable_kobj); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_cifail_firmreg: 111162306a36Sopenharmony_ci printk(KERN_INFO PDCS_PREFIX " bailing out\n"); 111262306a36Sopenharmony_ci return rc; 111362306a36Sopenharmony_ci} 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_cistatic void __exit 111662306a36Sopenharmony_cipdc_stable_exit(void) 111762306a36Sopenharmony_ci{ 111862306a36Sopenharmony_ci pdcs_unregister_pathentries(); 111962306a36Sopenharmony_ci kset_unregister(paths_kset); 112062306a36Sopenharmony_ci kobject_put(stable_kobj); 112162306a36Sopenharmony_ci} 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_cimodule_init(pdc_stable_init); 112562306a36Sopenharmony_cimodule_exit(pdc_stable_exit); 1126