162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/firmware/edd.c 462306a36Sopenharmony_ci * Copyright (C) 2002, 2003, 2004 Dell Inc. 562306a36Sopenharmony_ci * by Matt Domsch <Matt_Domsch@dell.com> 662306a36Sopenharmony_ci * disk signature by Matt Domsch, Andrew Wilks, and Sandeep K. Shandilya 762306a36Sopenharmony_ci * legacy CHS by Patrick J. LoPresti <patl@users.sourceforge.net> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * BIOS Enhanced Disk Drive Services (EDD) 1062306a36Sopenharmony_ci * conformant to T13 Committee www.t13.org 1162306a36Sopenharmony_ci * projects 1572D, 1484D, 1386D, 1226DT 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * This code takes information provided by BIOS EDD calls 1462306a36Sopenharmony_ci * fn41 - Check Extensions Present and 1562306a36Sopenharmony_ci * fn48 - Get Device Parameters with EDD extensions 1662306a36Sopenharmony_ci * made in setup.S, copied to safe structures in setup.c, 1762306a36Sopenharmony_ci * and presents it in sysfs. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Please see http://linux.dell.com/edd/results.html for 2062306a36Sopenharmony_ci * the list of BIOSs which have been reported to implement EDD. 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci#include <linux/string.h> 2562306a36Sopenharmony_ci#include <linux/types.h> 2662306a36Sopenharmony_ci#include <linux/init.h> 2762306a36Sopenharmony_ci#include <linux/stat.h> 2862306a36Sopenharmony_ci#include <linux/err.h> 2962306a36Sopenharmony_ci#include <linux/ctype.h> 3062306a36Sopenharmony_ci#include <linux/slab.h> 3162306a36Sopenharmony_ci#include <linux/limits.h> 3262306a36Sopenharmony_ci#include <linux/device.h> 3362306a36Sopenharmony_ci#include <linux/pci.h> 3462306a36Sopenharmony_ci#include <linux/blkdev.h> 3562306a36Sopenharmony_ci#include <linux/edd.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define EDD_VERSION "0.16" 3862306a36Sopenharmony_ci#define EDD_DATE "2004-Jun-25" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ciMODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>"); 4162306a36Sopenharmony_ciMODULE_DESCRIPTION("sysfs interface to BIOS EDD information"); 4262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4362306a36Sopenharmony_ciMODULE_VERSION(EDD_VERSION); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define left (PAGE_SIZE - (p - buf) - 1) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct edd_device { 4862306a36Sopenharmony_ci unsigned int index; 4962306a36Sopenharmony_ci unsigned int mbr_signature; 5062306a36Sopenharmony_ci struct edd_info *info; 5162306a36Sopenharmony_ci struct kobject kobj; 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct edd_attribute { 5562306a36Sopenharmony_ci struct attribute attr; 5662306a36Sopenharmony_ci ssize_t(*show) (struct edd_device * edev, char *buf); 5762306a36Sopenharmony_ci int (*test) (struct edd_device * edev); 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/* forward declarations */ 6162306a36Sopenharmony_cistatic int edd_dev_is_type(struct edd_device *edev, const char *type); 6262306a36Sopenharmony_cistatic struct pci_dev *edd_get_pci_dev(struct edd_device *edev); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic struct edd_device *edd_devices[EDD_MBR_SIG_MAX]; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define EDD_DEVICE_ATTR(_name,_mode,_show,_test) \ 6762306a36Sopenharmony_cistruct edd_attribute edd_attr_##_name = { \ 6862306a36Sopenharmony_ci .attr = {.name = __stringify(_name), .mode = _mode }, \ 6962306a36Sopenharmony_ci .show = _show, \ 7062306a36Sopenharmony_ci .test = _test, \ 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic int 7462306a36Sopenharmony_ciedd_has_mbr_signature(struct edd_device *edev) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci return edev->index < min_t(unsigned char, edd.mbr_signature_nr, EDD_MBR_SIG_MAX); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int 8062306a36Sopenharmony_ciedd_has_edd_info(struct edd_device *edev) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci return edev->index < min_t(unsigned char, edd.edd_info_nr, EDDMAXNR); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic inline struct edd_info * 8662306a36Sopenharmony_ciedd_dev_get_info(struct edd_device *edev) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci return edev->info; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic inline void 9262306a36Sopenharmony_ciedd_dev_set_info(struct edd_device *edev, int i) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci edev->index = i; 9562306a36Sopenharmony_ci if (edd_has_mbr_signature(edev)) 9662306a36Sopenharmony_ci edev->mbr_signature = edd.mbr_signature[i]; 9762306a36Sopenharmony_ci if (edd_has_edd_info(edev)) 9862306a36Sopenharmony_ci edev->info = &edd.edd_info[i]; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#define to_edd_attr(_attr) container_of(_attr,struct edd_attribute,attr) 10262306a36Sopenharmony_ci#define to_edd_device(obj) container_of(obj,struct edd_device,kobj) 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic ssize_t 10562306a36Sopenharmony_ciedd_attr_show(struct kobject * kobj, struct attribute *attr, char *buf) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct edd_device *dev = to_edd_device(kobj); 10862306a36Sopenharmony_ci struct edd_attribute *edd_attr = to_edd_attr(attr); 10962306a36Sopenharmony_ci ssize_t ret = -EIO; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (edd_attr->show) 11262306a36Sopenharmony_ci ret = edd_attr->show(dev, buf); 11362306a36Sopenharmony_ci return ret; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic const struct sysfs_ops edd_attr_ops = { 11762306a36Sopenharmony_ci .show = edd_attr_show, 11862306a36Sopenharmony_ci}; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic ssize_t 12162306a36Sopenharmony_ciedd_show_host_bus(struct edd_device *edev, char *buf) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci struct edd_info *info; 12462306a36Sopenharmony_ci char *p = buf; 12562306a36Sopenharmony_ci int i; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (!edev) 12862306a36Sopenharmony_ci return -EINVAL; 12962306a36Sopenharmony_ci info = edd_dev_get_info(edev); 13062306a36Sopenharmony_ci if (!info || !buf) 13162306a36Sopenharmony_ci return -EINVAL; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 13462306a36Sopenharmony_ci if (isprint(info->params.host_bus_type[i])) { 13562306a36Sopenharmony_ci p += scnprintf(p, left, "%c", info->params.host_bus_type[i]); 13662306a36Sopenharmony_ci } else { 13762306a36Sopenharmony_ci p += scnprintf(p, left, " "); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (!strncmp(info->params.host_bus_type, "ISA", 3)) { 14262306a36Sopenharmony_ci p += scnprintf(p, left, "\tbase_address: %x\n", 14362306a36Sopenharmony_ci info->params.interface_path.isa.base_address); 14462306a36Sopenharmony_ci } else if (!strncmp(info->params.host_bus_type, "PCIX", 4) || 14562306a36Sopenharmony_ci !strncmp(info->params.host_bus_type, "PCI", 3) || 14662306a36Sopenharmony_ci !strncmp(info->params.host_bus_type, "XPRS", 4)) { 14762306a36Sopenharmony_ci p += scnprintf(p, left, 14862306a36Sopenharmony_ci "\t%02x:%02x.%d channel: %u\n", 14962306a36Sopenharmony_ci info->params.interface_path.pci.bus, 15062306a36Sopenharmony_ci info->params.interface_path.pci.slot, 15162306a36Sopenharmony_ci info->params.interface_path.pci.function, 15262306a36Sopenharmony_ci info->params.interface_path.pci.channel); 15362306a36Sopenharmony_ci } else if (!strncmp(info->params.host_bus_type, "IBND", 4) || 15462306a36Sopenharmony_ci !strncmp(info->params.host_bus_type, "HTPT", 4)) { 15562306a36Sopenharmony_ci p += scnprintf(p, left, 15662306a36Sopenharmony_ci "\tTBD: %llx\n", 15762306a36Sopenharmony_ci info->params.interface_path.ibnd.reserved); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci } else { 16062306a36Sopenharmony_ci p += scnprintf(p, left, "\tunknown: %llx\n", 16162306a36Sopenharmony_ci info->params.interface_path.unknown.reserved); 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci return (p - buf); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic ssize_t 16762306a36Sopenharmony_ciedd_show_interface(struct edd_device *edev, char *buf) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct edd_info *info; 17062306a36Sopenharmony_ci char *p = buf; 17162306a36Sopenharmony_ci int i; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (!edev) 17462306a36Sopenharmony_ci return -EINVAL; 17562306a36Sopenharmony_ci info = edd_dev_get_info(edev); 17662306a36Sopenharmony_ci if (!info || !buf) 17762306a36Sopenharmony_ci return -EINVAL; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 18062306a36Sopenharmony_ci if (isprint(info->params.interface_type[i])) { 18162306a36Sopenharmony_ci p += scnprintf(p, left, "%c", info->params.interface_type[i]); 18262306a36Sopenharmony_ci } else { 18362306a36Sopenharmony_ci p += scnprintf(p, left, " "); 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci if (!strncmp(info->params.interface_type, "ATAPI", 5)) { 18762306a36Sopenharmony_ci p += scnprintf(p, left, "\tdevice: %u lun: %u\n", 18862306a36Sopenharmony_ci info->params.device_path.atapi.device, 18962306a36Sopenharmony_ci info->params.device_path.atapi.lun); 19062306a36Sopenharmony_ci } else if (!strncmp(info->params.interface_type, "ATA", 3)) { 19162306a36Sopenharmony_ci p += scnprintf(p, left, "\tdevice: %u\n", 19262306a36Sopenharmony_ci info->params.device_path.ata.device); 19362306a36Sopenharmony_ci } else if (!strncmp(info->params.interface_type, "SCSI", 4)) { 19462306a36Sopenharmony_ci p += scnprintf(p, left, "\tid: %u lun: %llu\n", 19562306a36Sopenharmony_ci info->params.device_path.scsi.id, 19662306a36Sopenharmony_ci info->params.device_path.scsi.lun); 19762306a36Sopenharmony_ci } else if (!strncmp(info->params.interface_type, "USB", 3)) { 19862306a36Sopenharmony_ci p += scnprintf(p, left, "\tserial_number: %llx\n", 19962306a36Sopenharmony_ci info->params.device_path.usb.serial_number); 20062306a36Sopenharmony_ci } else if (!strncmp(info->params.interface_type, "1394", 4)) { 20162306a36Sopenharmony_ci p += scnprintf(p, left, "\teui: %llx\n", 20262306a36Sopenharmony_ci info->params.device_path.i1394.eui); 20362306a36Sopenharmony_ci } else if (!strncmp(info->params.interface_type, "FIBRE", 5)) { 20462306a36Sopenharmony_ci p += scnprintf(p, left, "\twwid: %llx lun: %llx\n", 20562306a36Sopenharmony_ci info->params.device_path.fibre.wwid, 20662306a36Sopenharmony_ci info->params.device_path.fibre.lun); 20762306a36Sopenharmony_ci } else if (!strncmp(info->params.interface_type, "I2O", 3)) { 20862306a36Sopenharmony_ci p += scnprintf(p, left, "\tidentity_tag: %llx\n", 20962306a36Sopenharmony_ci info->params.device_path.i2o.identity_tag); 21062306a36Sopenharmony_ci } else if (!strncmp(info->params.interface_type, "RAID", 4)) { 21162306a36Sopenharmony_ci p += scnprintf(p, left, "\tidentity_tag: %x\n", 21262306a36Sopenharmony_ci info->params.device_path.raid.array_number); 21362306a36Sopenharmony_ci } else if (!strncmp(info->params.interface_type, "SATA", 4)) { 21462306a36Sopenharmony_ci p += scnprintf(p, left, "\tdevice: %u\n", 21562306a36Sopenharmony_ci info->params.device_path.sata.device); 21662306a36Sopenharmony_ci } else { 21762306a36Sopenharmony_ci p += scnprintf(p, left, "\tunknown: %llx %llx\n", 21862306a36Sopenharmony_ci info->params.device_path.unknown.reserved1, 21962306a36Sopenharmony_ci info->params.device_path.unknown.reserved2); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return (p - buf); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/** 22662306a36Sopenharmony_ci * edd_show_raw_data() - copies raw data to buffer for userspace to parse 22762306a36Sopenharmony_ci * @edev: target edd_device 22862306a36Sopenharmony_ci * @buf: output buffer 22962306a36Sopenharmony_ci * 23062306a36Sopenharmony_ci * Returns: number of bytes written, or -EINVAL on failure 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_cistatic ssize_t 23362306a36Sopenharmony_ciedd_show_raw_data(struct edd_device *edev, char *buf) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct edd_info *info; 23662306a36Sopenharmony_ci ssize_t len = sizeof (info->params); 23762306a36Sopenharmony_ci if (!edev) 23862306a36Sopenharmony_ci return -EINVAL; 23962306a36Sopenharmony_ci info = edd_dev_get_info(edev); 24062306a36Sopenharmony_ci if (!info || !buf) 24162306a36Sopenharmony_ci return -EINVAL; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (!(info->params.key == 0xBEDD || info->params.key == 0xDDBE)) 24462306a36Sopenharmony_ci len = info->params.length; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* In case of buggy BIOSs */ 24762306a36Sopenharmony_ci if (len > (sizeof(info->params))) 24862306a36Sopenharmony_ci len = sizeof(info->params); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci memcpy(buf, &info->params, len); 25162306a36Sopenharmony_ci return len; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic ssize_t 25562306a36Sopenharmony_ciedd_show_version(struct edd_device *edev, char *buf) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct edd_info *info; 25862306a36Sopenharmony_ci char *p = buf; 25962306a36Sopenharmony_ci if (!edev) 26062306a36Sopenharmony_ci return -EINVAL; 26162306a36Sopenharmony_ci info = edd_dev_get_info(edev); 26262306a36Sopenharmony_ci if (!info || !buf) 26362306a36Sopenharmony_ci return -EINVAL; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci p += scnprintf(p, left, "0x%02x\n", info->version); 26662306a36Sopenharmony_ci return (p - buf); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic ssize_t 27062306a36Sopenharmony_ciedd_show_mbr_signature(struct edd_device *edev, char *buf) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci char *p = buf; 27362306a36Sopenharmony_ci p += scnprintf(p, left, "0x%08x\n", edev->mbr_signature); 27462306a36Sopenharmony_ci return (p - buf); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic ssize_t 27862306a36Sopenharmony_ciedd_show_extensions(struct edd_device *edev, char *buf) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct edd_info *info; 28162306a36Sopenharmony_ci char *p = buf; 28262306a36Sopenharmony_ci if (!edev) 28362306a36Sopenharmony_ci return -EINVAL; 28462306a36Sopenharmony_ci info = edd_dev_get_info(edev); 28562306a36Sopenharmony_ci if (!info || !buf) 28662306a36Sopenharmony_ci return -EINVAL; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (info->interface_support & EDD_EXT_FIXED_DISK_ACCESS) { 28962306a36Sopenharmony_ci p += scnprintf(p, left, "Fixed disk access\n"); 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci if (info->interface_support & EDD_EXT_DEVICE_LOCKING_AND_EJECTING) { 29262306a36Sopenharmony_ci p += scnprintf(p, left, "Device locking and ejecting\n"); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci if (info->interface_support & EDD_EXT_ENHANCED_DISK_DRIVE_SUPPORT) { 29562306a36Sopenharmony_ci p += scnprintf(p, left, "Enhanced Disk Drive support\n"); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci if (info->interface_support & EDD_EXT_64BIT_EXTENSIONS) { 29862306a36Sopenharmony_ci p += scnprintf(p, left, "64-bit extensions\n"); 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci return (p - buf); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic ssize_t 30462306a36Sopenharmony_ciedd_show_info_flags(struct edd_device *edev, char *buf) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct edd_info *info; 30762306a36Sopenharmony_ci char *p = buf; 30862306a36Sopenharmony_ci if (!edev) 30962306a36Sopenharmony_ci return -EINVAL; 31062306a36Sopenharmony_ci info = edd_dev_get_info(edev); 31162306a36Sopenharmony_ci if (!info || !buf) 31262306a36Sopenharmony_ci return -EINVAL; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (info->params.info_flags & EDD_INFO_DMA_BOUNDARY_ERROR_TRANSPARENT) 31562306a36Sopenharmony_ci p += scnprintf(p, left, "DMA boundary error transparent\n"); 31662306a36Sopenharmony_ci if (info->params.info_flags & EDD_INFO_GEOMETRY_VALID) 31762306a36Sopenharmony_ci p += scnprintf(p, left, "geometry valid\n"); 31862306a36Sopenharmony_ci if (info->params.info_flags & EDD_INFO_REMOVABLE) 31962306a36Sopenharmony_ci p += scnprintf(p, left, "removable\n"); 32062306a36Sopenharmony_ci if (info->params.info_flags & EDD_INFO_WRITE_VERIFY) 32162306a36Sopenharmony_ci p += scnprintf(p, left, "write verify\n"); 32262306a36Sopenharmony_ci if (info->params.info_flags & EDD_INFO_MEDIA_CHANGE_NOTIFICATION) 32362306a36Sopenharmony_ci p += scnprintf(p, left, "media change notification\n"); 32462306a36Sopenharmony_ci if (info->params.info_flags & EDD_INFO_LOCKABLE) 32562306a36Sopenharmony_ci p += scnprintf(p, left, "lockable\n"); 32662306a36Sopenharmony_ci if (info->params.info_flags & EDD_INFO_NO_MEDIA_PRESENT) 32762306a36Sopenharmony_ci p += scnprintf(p, left, "no media present\n"); 32862306a36Sopenharmony_ci if (info->params.info_flags & EDD_INFO_USE_INT13_FN50) 32962306a36Sopenharmony_ci p += scnprintf(p, left, "use int13 fn50\n"); 33062306a36Sopenharmony_ci return (p - buf); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic ssize_t 33462306a36Sopenharmony_ciedd_show_legacy_max_cylinder(struct edd_device *edev, char *buf) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct edd_info *info; 33762306a36Sopenharmony_ci char *p = buf; 33862306a36Sopenharmony_ci if (!edev) 33962306a36Sopenharmony_ci return -EINVAL; 34062306a36Sopenharmony_ci info = edd_dev_get_info(edev); 34162306a36Sopenharmony_ci if (!info || !buf) 34262306a36Sopenharmony_ci return -EINVAL; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci p += scnprintf(p, left, "%u\n", info->legacy_max_cylinder); 34562306a36Sopenharmony_ci return (p - buf); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic ssize_t 34962306a36Sopenharmony_ciedd_show_legacy_max_head(struct edd_device *edev, char *buf) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci struct edd_info *info; 35262306a36Sopenharmony_ci char *p = buf; 35362306a36Sopenharmony_ci if (!edev) 35462306a36Sopenharmony_ci return -EINVAL; 35562306a36Sopenharmony_ci info = edd_dev_get_info(edev); 35662306a36Sopenharmony_ci if (!info || !buf) 35762306a36Sopenharmony_ci return -EINVAL; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci p += scnprintf(p, left, "%u\n", info->legacy_max_head); 36062306a36Sopenharmony_ci return (p - buf); 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic ssize_t 36462306a36Sopenharmony_ciedd_show_legacy_sectors_per_track(struct edd_device *edev, char *buf) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct edd_info *info; 36762306a36Sopenharmony_ci char *p = buf; 36862306a36Sopenharmony_ci if (!edev) 36962306a36Sopenharmony_ci return -EINVAL; 37062306a36Sopenharmony_ci info = edd_dev_get_info(edev); 37162306a36Sopenharmony_ci if (!info || !buf) 37262306a36Sopenharmony_ci return -EINVAL; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci p += scnprintf(p, left, "%u\n", info->legacy_sectors_per_track); 37562306a36Sopenharmony_ci return (p - buf); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic ssize_t 37962306a36Sopenharmony_ciedd_show_default_cylinders(struct edd_device *edev, char *buf) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct edd_info *info; 38262306a36Sopenharmony_ci char *p = buf; 38362306a36Sopenharmony_ci if (!edev) 38462306a36Sopenharmony_ci return -EINVAL; 38562306a36Sopenharmony_ci info = edd_dev_get_info(edev); 38662306a36Sopenharmony_ci if (!info || !buf) 38762306a36Sopenharmony_ci return -EINVAL; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci p += scnprintf(p, left, "%u\n", info->params.num_default_cylinders); 39062306a36Sopenharmony_ci return (p - buf); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic ssize_t 39462306a36Sopenharmony_ciedd_show_default_heads(struct edd_device *edev, char *buf) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct edd_info *info; 39762306a36Sopenharmony_ci char *p = buf; 39862306a36Sopenharmony_ci if (!edev) 39962306a36Sopenharmony_ci return -EINVAL; 40062306a36Sopenharmony_ci info = edd_dev_get_info(edev); 40162306a36Sopenharmony_ci if (!info || !buf) 40262306a36Sopenharmony_ci return -EINVAL; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci p += scnprintf(p, left, "%u\n", info->params.num_default_heads); 40562306a36Sopenharmony_ci return (p - buf); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic ssize_t 40962306a36Sopenharmony_ciedd_show_default_sectors_per_track(struct edd_device *edev, char *buf) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct edd_info *info; 41262306a36Sopenharmony_ci char *p = buf; 41362306a36Sopenharmony_ci if (!edev) 41462306a36Sopenharmony_ci return -EINVAL; 41562306a36Sopenharmony_ci info = edd_dev_get_info(edev); 41662306a36Sopenharmony_ci if (!info || !buf) 41762306a36Sopenharmony_ci return -EINVAL; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci p += scnprintf(p, left, "%u\n", info->params.sectors_per_track); 42062306a36Sopenharmony_ci return (p - buf); 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic ssize_t 42462306a36Sopenharmony_ciedd_show_sectors(struct edd_device *edev, char *buf) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct edd_info *info; 42762306a36Sopenharmony_ci char *p = buf; 42862306a36Sopenharmony_ci if (!edev) 42962306a36Sopenharmony_ci return -EINVAL; 43062306a36Sopenharmony_ci info = edd_dev_get_info(edev); 43162306a36Sopenharmony_ci if (!info || !buf) 43262306a36Sopenharmony_ci return -EINVAL; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci p += scnprintf(p, left, "%llu\n", info->params.number_of_sectors); 43562306a36Sopenharmony_ci return (p - buf); 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/* 44062306a36Sopenharmony_ci * Some device instances may not have all the above attributes, 44162306a36Sopenharmony_ci * or the attribute values may be meaningless (i.e. if 44262306a36Sopenharmony_ci * the device is < EDD 3.0, it won't have host_bus and interface 44362306a36Sopenharmony_ci * information), so don't bother making files for them. Likewise 44462306a36Sopenharmony_ci * if the default_{cylinders,heads,sectors_per_track} values 44562306a36Sopenharmony_ci * are zero, the BIOS doesn't provide sane values, don't bother 44662306a36Sopenharmony_ci * creating files for them either. 44762306a36Sopenharmony_ci */ 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_cistatic int 45062306a36Sopenharmony_ciedd_has_legacy_max_cylinder(struct edd_device *edev) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct edd_info *info; 45362306a36Sopenharmony_ci if (!edev) 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci info = edd_dev_get_info(edev); 45662306a36Sopenharmony_ci if (!info) 45762306a36Sopenharmony_ci return 0; 45862306a36Sopenharmony_ci return info->legacy_max_cylinder > 0; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic int 46262306a36Sopenharmony_ciedd_has_legacy_max_head(struct edd_device *edev) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct edd_info *info; 46562306a36Sopenharmony_ci if (!edev) 46662306a36Sopenharmony_ci return 0; 46762306a36Sopenharmony_ci info = edd_dev_get_info(edev); 46862306a36Sopenharmony_ci if (!info) 46962306a36Sopenharmony_ci return 0; 47062306a36Sopenharmony_ci return info->legacy_max_head > 0; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic int 47462306a36Sopenharmony_ciedd_has_legacy_sectors_per_track(struct edd_device *edev) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct edd_info *info; 47762306a36Sopenharmony_ci if (!edev) 47862306a36Sopenharmony_ci return 0; 47962306a36Sopenharmony_ci info = edd_dev_get_info(edev); 48062306a36Sopenharmony_ci if (!info) 48162306a36Sopenharmony_ci return 0; 48262306a36Sopenharmony_ci return info->legacy_sectors_per_track > 0; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int 48662306a36Sopenharmony_ciedd_has_default_cylinders(struct edd_device *edev) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct edd_info *info; 48962306a36Sopenharmony_ci if (!edev) 49062306a36Sopenharmony_ci return 0; 49162306a36Sopenharmony_ci info = edd_dev_get_info(edev); 49262306a36Sopenharmony_ci if (!info) 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci return info->params.num_default_cylinders > 0; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic int 49862306a36Sopenharmony_ciedd_has_default_heads(struct edd_device *edev) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci struct edd_info *info; 50162306a36Sopenharmony_ci if (!edev) 50262306a36Sopenharmony_ci return 0; 50362306a36Sopenharmony_ci info = edd_dev_get_info(edev); 50462306a36Sopenharmony_ci if (!info) 50562306a36Sopenharmony_ci return 0; 50662306a36Sopenharmony_ci return info->params.num_default_heads > 0; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int 51062306a36Sopenharmony_ciedd_has_default_sectors_per_track(struct edd_device *edev) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct edd_info *info; 51362306a36Sopenharmony_ci if (!edev) 51462306a36Sopenharmony_ci return 0; 51562306a36Sopenharmony_ci info = edd_dev_get_info(edev); 51662306a36Sopenharmony_ci if (!info) 51762306a36Sopenharmony_ci return 0; 51862306a36Sopenharmony_ci return info->params.sectors_per_track > 0; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic int 52262306a36Sopenharmony_ciedd_has_edd30(struct edd_device *edev) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct edd_info *info; 52562306a36Sopenharmony_ci int i; 52662306a36Sopenharmony_ci u8 csum = 0; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (!edev) 52962306a36Sopenharmony_ci return 0; 53062306a36Sopenharmony_ci info = edd_dev_get_info(edev); 53162306a36Sopenharmony_ci if (!info) 53262306a36Sopenharmony_ci return 0; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (!(info->params.key == 0xBEDD || info->params.key == 0xDDBE)) { 53562306a36Sopenharmony_ci return 0; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* We support only T13 spec */ 54062306a36Sopenharmony_ci if (info->params.device_path_info_length != 44) 54162306a36Sopenharmony_ci return 0; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci for (i = 30; i < info->params.device_path_info_length + 30; i++) 54462306a36Sopenharmony_ci csum += *(((u8 *)&info->params) + i); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (csum) 54762306a36Sopenharmony_ci return 0; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return 1; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic EDD_DEVICE_ATTR(raw_data, 0444, edd_show_raw_data, edd_has_edd_info); 55462306a36Sopenharmony_cistatic EDD_DEVICE_ATTR(version, 0444, edd_show_version, edd_has_edd_info); 55562306a36Sopenharmony_cistatic EDD_DEVICE_ATTR(extensions, 0444, edd_show_extensions, edd_has_edd_info); 55662306a36Sopenharmony_cistatic EDD_DEVICE_ATTR(info_flags, 0444, edd_show_info_flags, edd_has_edd_info); 55762306a36Sopenharmony_cistatic EDD_DEVICE_ATTR(sectors, 0444, edd_show_sectors, edd_has_edd_info); 55862306a36Sopenharmony_cistatic EDD_DEVICE_ATTR(legacy_max_cylinder, 0444, 55962306a36Sopenharmony_ci edd_show_legacy_max_cylinder, 56062306a36Sopenharmony_ci edd_has_legacy_max_cylinder); 56162306a36Sopenharmony_cistatic EDD_DEVICE_ATTR(legacy_max_head, 0444, edd_show_legacy_max_head, 56262306a36Sopenharmony_ci edd_has_legacy_max_head); 56362306a36Sopenharmony_cistatic EDD_DEVICE_ATTR(legacy_sectors_per_track, 0444, 56462306a36Sopenharmony_ci edd_show_legacy_sectors_per_track, 56562306a36Sopenharmony_ci edd_has_legacy_sectors_per_track); 56662306a36Sopenharmony_cistatic EDD_DEVICE_ATTR(default_cylinders, 0444, edd_show_default_cylinders, 56762306a36Sopenharmony_ci edd_has_default_cylinders); 56862306a36Sopenharmony_cistatic EDD_DEVICE_ATTR(default_heads, 0444, edd_show_default_heads, 56962306a36Sopenharmony_ci edd_has_default_heads); 57062306a36Sopenharmony_cistatic EDD_DEVICE_ATTR(default_sectors_per_track, 0444, 57162306a36Sopenharmony_ci edd_show_default_sectors_per_track, 57262306a36Sopenharmony_ci edd_has_default_sectors_per_track); 57362306a36Sopenharmony_cistatic EDD_DEVICE_ATTR(interface, 0444, edd_show_interface, edd_has_edd30); 57462306a36Sopenharmony_cistatic EDD_DEVICE_ATTR(host_bus, 0444, edd_show_host_bus, edd_has_edd30); 57562306a36Sopenharmony_cistatic EDD_DEVICE_ATTR(mbr_signature, 0444, edd_show_mbr_signature, edd_has_mbr_signature); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci/* These attributes are conditional and only added for some devices. */ 57862306a36Sopenharmony_cistatic struct edd_attribute * edd_attrs[] = { 57962306a36Sopenharmony_ci &edd_attr_raw_data, 58062306a36Sopenharmony_ci &edd_attr_version, 58162306a36Sopenharmony_ci &edd_attr_extensions, 58262306a36Sopenharmony_ci &edd_attr_info_flags, 58362306a36Sopenharmony_ci &edd_attr_sectors, 58462306a36Sopenharmony_ci &edd_attr_legacy_max_cylinder, 58562306a36Sopenharmony_ci &edd_attr_legacy_max_head, 58662306a36Sopenharmony_ci &edd_attr_legacy_sectors_per_track, 58762306a36Sopenharmony_ci &edd_attr_default_cylinders, 58862306a36Sopenharmony_ci &edd_attr_default_heads, 58962306a36Sopenharmony_ci &edd_attr_default_sectors_per_track, 59062306a36Sopenharmony_ci &edd_attr_interface, 59162306a36Sopenharmony_ci &edd_attr_host_bus, 59262306a36Sopenharmony_ci &edd_attr_mbr_signature, 59362306a36Sopenharmony_ci NULL, 59462306a36Sopenharmony_ci}; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci/** 59762306a36Sopenharmony_ci * edd_release - free edd structure 59862306a36Sopenharmony_ci * @kobj: kobject of edd structure 59962306a36Sopenharmony_ci * 60062306a36Sopenharmony_ci * This is called when the refcount of the edd structure 60162306a36Sopenharmony_ci * reaches 0. This should happen right after we unregister, 60262306a36Sopenharmony_ci * but just in case, we use the release callback anyway. 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic void edd_release(struct kobject * kobj) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci struct edd_device * dev = to_edd_device(kobj); 60862306a36Sopenharmony_ci kfree(dev); 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic const struct kobj_type edd_ktype = { 61262306a36Sopenharmony_ci .release = edd_release, 61362306a36Sopenharmony_ci .sysfs_ops = &edd_attr_ops, 61462306a36Sopenharmony_ci}; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic struct kset *edd_kset; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci/** 62062306a36Sopenharmony_ci * edd_dev_is_type() - is this EDD device a 'type' device? 62162306a36Sopenharmony_ci * @edev: target edd_device 62262306a36Sopenharmony_ci * @type: a host bus or interface identifier string per the EDD spec 62362306a36Sopenharmony_ci * 62462306a36Sopenharmony_ci * Returns 1 (TRUE) if it is a 'type' device, 0 otherwise. 62562306a36Sopenharmony_ci */ 62662306a36Sopenharmony_cistatic int 62762306a36Sopenharmony_ciedd_dev_is_type(struct edd_device *edev, const char *type) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct edd_info *info; 63062306a36Sopenharmony_ci if (!edev) 63162306a36Sopenharmony_ci return 0; 63262306a36Sopenharmony_ci info = edd_dev_get_info(edev); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (type && info) { 63562306a36Sopenharmony_ci if (!strncmp(info->params.host_bus_type, type, strlen(type)) || 63662306a36Sopenharmony_ci !strncmp(info->params.interface_type, type, strlen(type))) 63762306a36Sopenharmony_ci return 1; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci return 0; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/** 64362306a36Sopenharmony_ci * edd_get_pci_dev() - finds pci_dev that matches edev 64462306a36Sopenharmony_ci * @edev: edd_device 64562306a36Sopenharmony_ci * 64662306a36Sopenharmony_ci * Returns pci_dev if found, or NULL 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_cistatic struct pci_dev * 64962306a36Sopenharmony_ciedd_get_pci_dev(struct edd_device *edev) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci struct edd_info *info = edd_dev_get_info(edev); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (edd_dev_is_type(edev, "PCI") || edd_dev_is_type(edev, "XPRS")) { 65462306a36Sopenharmony_ci return pci_get_domain_bus_and_slot(0, 65562306a36Sopenharmony_ci info->params.interface_path.pci.bus, 65662306a36Sopenharmony_ci PCI_DEVFN(info->params.interface_path.pci.slot, 65762306a36Sopenharmony_ci info->params.interface_path.pci.function)); 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci return NULL; 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic int 66362306a36Sopenharmony_ciedd_create_symlink_to_pcidev(struct edd_device *edev) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci struct pci_dev *pci_dev = edd_get_pci_dev(edev); 66762306a36Sopenharmony_ci int ret; 66862306a36Sopenharmony_ci if (!pci_dev) 66962306a36Sopenharmony_ci return 1; 67062306a36Sopenharmony_ci ret = sysfs_create_link(&edev->kobj,&pci_dev->dev.kobj,"pci_dev"); 67162306a36Sopenharmony_ci pci_dev_put(pci_dev); 67262306a36Sopenharmony_ci return ret; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic inline void 67662306a36Sopenharmony_ciedd_device_unregister(struct edd_device *edev) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci kobject_put(&edev->kobj); 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cistatic void edd_populate_dir(struct edd_device * edev) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct edd_attribute * attr; 68462306a36Sopenharmony_ci int error = 0; 68562306a36Sopenharmony_ci int i; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci for (i = 0; (attr = edd_attrs[i]) && !error; i++) { 68862306a36Sopenharmony_ci if (!attr->test || attr->test(edev)) 68962306a36Sopenharmony_ci error = sysfs_create_file(&edev->kobj,&attr->attr); 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (!error) { 69362306a36Sopenharmony_ci edd_create_symlink_to_pcidev(edev); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic int 69862306a36Sopenharmony_ciedd_device_register(struct edd_device *edev, int i) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci int error; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (!edev) 70362306a36Sopenharmony_ci return 1; 70462306a36Sopenharmony_ci edd_dev_set_info(edev, i); 70562306a36Sopenharmony_ci edev->kobj.kset = edd_kset; 70662306a36Sopenharmony_ci error = kobject_init_and_add(&edev->kobj, &edd_ktype, NULL, 70762306a36Sopenharmony_ci "int13_dev%02x", 0x80 + i); 70862306a36Sopenharmony_ci if (!error) { 70962306a36Sopenharmony_ci edd_populate_dir(edev); 71062306a36Sopenharmony_ci kobject_uevent(&edev->kobj, KOBJ_ADD); 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci return error; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_cistatic inline int edd_num_devices(void) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci return max_t(unsigned char, 71862306a36Sopenharmony_ci min_t(unsigned char, EDD_MBR_SIG_MAX, edd.mbr_signature_nr), 71962306a36Sopenharmony_ci min_t(unsigned char, EDDMAXNR, edd.edd_info_nr)); 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci/** 72362306a36Sopenharmony_ci * edd_init() - creates sysfs tree of EDD data 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_cistatic int __init 72662306a36Sopenharmony_ciedd_init(void) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci int i; 72962306a36Sopenharmony_ci int rc=0; 73062306a36Sopenharmony_ci struct edd_device *edev; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (!edd_num_devices()) 73362306a36Sopenharmony_ci return -ENODEV; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci printk(KERN_INFO "BIOS EDD facility v%s %s, %d devices found\n", 73662306a36Sopenharmony_ci EDD_VERSION, EDD_DATE, edd_num_devices()); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci edd_kset = kset_create_and_add("edd", NULL, firmware_kobj); 73962306a36Sopenharmony_ci if (!edd_kset) 74062306a36Sopenharmony_ci return -ENOMEM; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci for (i = 0; i < edd_num_devices(); i++) { 74362306a36Sopenharmony_ci edev = kzalloc(sizeof (*edev), GFP_KERNEL); 74462306a36Sopenharmony_ci if (!edev) { 74562306a36Sopenharmony_ci rc = -ENOMEM; 74662306a36Sopenharmony_ci goto out; 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci rc = edd_device_register(edev, i); 75062306a36Sopenharmony_ci if (rc) { 75162306a36Sopenharmony_ci kfree(edev); 75262306a36Sopenharmony_ci goto out; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci edd_devices[i] = edev; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci return 0; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ciout: 76062306a36Sopenharmony_ci while (--i >= 0) 76162306a36Sopenharmony_ci edd_device_unregister(edd_devices[i]); 76262306a36Sopenharmony_ci kset_unregister(edd_kset); 76362306a36Sopenharmony_ci return rc; 76462306a36Sopenharmony_ci} 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_cistatic void __exit 76762306a36Sopenharmony_ciedd_exit(void) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci int i; 77062306a36Sopenharmony_ci struct edd_device *edev; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci for (i = 0; i < edd_num_devices(); i++) { 77362306a36Sopenharmony_ci if ((edev = edd_devices[i])) 77462306a36Sopenharmony_ci edd_device_unregister(edev); 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci kset_unregister(edd_kset); 77762306a36Sopenharmony_ci} 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cilate_initcall(edd_init); 78062306a36Sopenharmony_cimodule_exit(edd_exit); 781