Commit 00d61244 authored by Matt Domsch's avatar Matt Domsch

Merge dell.com:/home/mdomsch/bk/linux-2.5

into dell.com:/home/mdomsch/bk/edd/linux-2.5-edd
parents 9552d6bc 3c01a46c
/* /*
* linux/arch/i386/kernel/edd.c * linux/arch/i386/kernel/edd.c
* Copyright (C) 2002 Dell Computer Corporation * Copyright (C) 2002, 2003 Dell Computer Corporation
* by Matt Domsch <Matt_Domsch@dell.com> * by Matt Domsch <Matt_Domsch@dell.com>
* *
* BIOS Enhanced Disk Drive Services (EDD) * BIOS Enhanced Disk Drive Services (EDD)
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* fn41 - Check Extensions Present and * fn41 - Check Extensions Present and
* fn48 - Get Device Parametes with EDD extensions * fn48 - Get Device Parametes with EDD extensions
* made in setup.S, copied to safe structures in setup.c, * made in setup.S, copied to safe structures in setup.c,
* and presents it in driverfs. * and presents it in sysfs.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License v2.0 as published by * it under the terms of the GNU General Public License v2.0 as published by
...@@ -30,8 +30,6 @@ ...@@ -30,8 +30,6 @@
* *
* TODO: * TODO:
* - Add IDE and USB disk device support * - Add IDE and USB disk device support
* - Get symlink creator helper functions exported from
* drivers/base instead of duplicating them here.
* - move edd.[ch] to better locations if/when one is decided * - move edd.[ch] to better locations if/when one is decided
*/ */
...@@ -46,18 +44,18 @@ ...@@ -46,18 +44,18 @@
#include <linux/limits.h> #include <linux/limits.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <asm/edd.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/blkdev.h> #include <linux/blkdev.h>
#include <asm/edd.h>
/* FIXME - this really belongs in include/scsi/scsi.h */ /* FIXME - this really belongs in include/scsi/scsi.h */
#include <../drivers/scsi/scsi.h> #include <../drivers/scsi/scsi.h>
#include <../drivers/scsi/hosts.h> #include <../drivers/scsi/hosts.h>
MODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>"); MODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>");
MODULE_DESCRIPTION("driverfs interface to BIOS EDD information"); MODULE_DESCRIPTION("sysfs interface to BIOS EDD information");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
#define EDD_VERSION "0.07 2002-Oct-24" #define EDD_VERSION "0.08 2003-Jan-07"
#define EDD_DEVICE_NAME_SIZE 16 #define EDD_DEVICE_NAME_SIZE 16
#define REPORT_URL "http://domsch.com/linux/edd30/results.html" #define REPORT_URL "http://domsch.com/linux/edd30/results.html"
...@@ -93,6 +91,7 @@ edd_dev_get_info(struct edd_device *edev) ...@@ -93,6 +91,7 @@ edd_dev_get_info(struct edd_device *edev)
{ {
return edev->info; return edev->info;
} }
static inline void static inline void
edd_dev_set_info(struct edd_device *edev, struct edd_info *info) edd_dev_set_info(struct edd_device *edev, struct edd_info *info)
{ {
...@@ -263,8 +262,8 @@ static ssize_t ...@@ -263,8 +262,8 @@ static ssize_t
edd_show_raw_data(struct edd_device *edev, char *buf) edd_show_raw_data(struct edd_device *edev, char *buf)
{ {
struct edd_info *info = edd_dev_get_info(edev); struct edd_info *info = edd_dev_get_info(edev);
int i, rc, warn_padding = 0, email = 0, nonzero_path = 0, int i, warn_padding = 0, nonzero_path = 0,
len = sizeof (*edd) - 4, found_pci=0; len = sizeof (*info) - 4, found_pci=0;
uint8_t checksum = 0, c = 0; uint8_t checksum = 0, c = 0;
char *p = buf; char *p = buf;
struct pci_dev *pci_dev=NULL; struct pci_dev *pci_dev=NULL;
...@@ -277,7 +276,7 @@ edd_show_raw_data(struct edd_device *edev, char *buf) ...@@ -277,7 +276,7 @@ edd_show_raw_data(struct edd_device *edev, char *buf)
len = info->params.length; len = info->params.length;
p += snprintf(p, left, "int13 fn48 returned data:\n\n"); p += snprintf(p, left, "int13 fn48 returned data:\n\n");
p += edd_dump_raw_data(p, left, ((char *) edd) + 4, len); p += edd_dump_raw_data(p, left, ((char *) info) + 4, len);
/* Spec violation. Adaptec AIC7899 returns 0xDDBE /* Spec violation. Adaptec AIC7899 returns 0xDDBE
here, when it should be 0xBEDD. here, when it should be 0xBEDD.
...@@ -286,7 +285,6 @@ edd_show_raw_data(struct edd_device *edev, char *buf) ...@@ -286,7 +285,6 @@ edd_show_raw_data(struct edd_device *edev, char *buf)
if (info->params.key == 0xDDBE) { if (info->params.key == 0xDDBE) {
p += snprintf(p, left, p += snprintf(p, left,
"Warning: Spec violation. Key should be 0xBEDD, is 0xDDBE\n"); "Warning: Spec violation. Key should be 0xBEDD, is 0xDDBE\n");
email++;
} }
if (!(info->params.key == 0xBEDD || info->params.key == 0xDDBE)) { if (!(info->params.key == 0xBEDD || info->params.key == 0xDDBE)) {
...@@ -294,7 +292,7 @@ edd_show_raw_data(struct edd_device *edev, char *buf) ...@@ -294,7 +292,7 @@ edd_show_raw_data(struct edd_device *edev, char *buf)
} }
for (i = 30; i <= 73; i++) { for (i = 30; i <= 73; i++) {
c = *(((uint8_t *) edd) + i + 4); c = *(((uint8_t *) info) + i + 4);
if (c) if (c)
nonzero_path++; nonzero_path++;
checksum += c; checksum += c;
...@@ -303,12 +301,10 @@ edd_show_raw_data(struct edd_device *edev, char *buf) ...@@ -303,12 +301,10 @@ edd_show_raw_data(struct edd_device *edev, char *buf)
if (checksum) { if (checksum) {
p += snprintf(p, left, p += snprintf(p, left,
"Warning: Spec violation. Device Path checksum invalid.\n"); "Warning: Spec violation. Device Path checksum invalid.\n");
email++;
} }
if (!nonzero_path) { if (!nonzero_path) {
p += snprintf(p, left, "Error: Spec violation. Empty device path.\n"); p += snprintf(p, left, "Error: Spec violation. Empty device path.\n");
email++;
goto out; goto out;
} }
...@@ -326,45 +322,35 @@ edd_show_raw_data(struct edd_device *edev, char *buf) ...@@ -326,45 +322,35 @@ edd_show_raw_data(struct edd_device *edev, char *buf)
if (warn_padding) { if (warn_padding) {
p += snprintf(p, left, p += snprintf(p, left,
"Warning: Spec violation. Padding should be 0x20.\n"); "Warning: Spec violation. Padding should be 0x20.\n");
email++;
} }
rc = edd_dev_is_type(edev, "PCI"); if (edd_dev_is_type(edev, "PCI")) {
if (!rc) { pci_dev = edd_get_pci_dev(edev);
pci_dev = pci_find_slot(info->params.interface_path.pci.bus,
PCI_DEVFN(info->params.interface_path.
pci.slot,
info->params.interface_path.
pci.function));
if (!pci_dev) { if (!pci_dev) {
p += snprintf(p, left, "Error: BIOS says this is a PCI device, but the OS doesn't know\n"); p += snprintf(p, left, "Error: BIOS says this is a PCI device, but the OS doesn't know\n");
p += snprintf(p, left, " about a PCI device at %02x:%02x.%d\n", p += snprintf(p, left, " about a PCI device at %02x:%02x.%d\n",
info->params.interface_path.pci.bus, info->params.interface_path.pci.bus,
info->params.interface_path.pci.slot, info->params.interface_path.pci.slot,
info->params.interface_path.pci.function); info->params.interface_path.pci.function);
email++;
} }
else { else {
found_pci++; found_pci++;
} }
} }
if (found_pci && !edd_dev_is_type(edev, "SCSI")) { if (found_pci && edd_dev_is_type(edev, "SCSI")) {
sd = edd_find_matching_scsi_device(edev); sd = edd_find_matching_scsi_device(edev);
if (!sd) { if (!sd) {
p += snprintf(p, left, "Error: BIOS says this is a SCSI device, but\n"); p += snprintf(p, left, "Error: BIOS says this is a SCSI device, but\n");
p += snprintf(p, left, " the OS doesn't know about this SCSI device.\n"); p += snprintf(p, left, " the OS doesn't know about this SCSI device.\n");
p += snprintf(p, left, " Do you have it's driver module loaded?\n"); p += snprintf(p, left, " Do you have it's driver module loaded?\n");
email++;
} }
} }
out: out:
if (email) {
p += snprintf(p, left, "\nPlease check %s\n", REPORT_URL); p += snprintf(p, left, "\nPlease check %s\n", REPORT_URL);
p += snprintf(p, left, "to see if this has been reported. If not,\n"); p += snprintf(p, left, "to see if this device has been reported. If not,\n");
p += snprintf(p, left, "please send the information requested there.\n"); p += snprintf(p, left, "please send the information requested there.\n");
}
return (p - buf); return (p - buf);
} }
...@@ -502,8 +488,8 @@ edd_has_default_cylinders(struct edd_device *edev) ...@@ -502,8 +488,8 @@ edd_has_default_cylinders(struct edd_device *edev)
{ {
struct edd_info *info = edd_dev_get_info(edev); struct edd_info *info = edd_dev_get_info(edev);
if (!edev || !info) if (!edev || !info)
return 1; return 0;
return !info->params.num_default_cylinders; return info->params.num_default_cylinders > 0;
} }
static int static int
...@@ -511,8 +497,8 @@ edd_has_default_heads(struct edd_device *edev) ...@@ -511,8 +497,8 @@ edd_has_default_heads(struct edd_device *edev)
{ {
struct edd_info *info = edd_dev_get_info(edev); struct edd_info *info = edd_dev_get_info(edev);
if (!edev || !info) if (!edev || !info)
return 1; return 0;
return !info->params.num_default_heads; return info->params.num_default_heads > 0;
} }
static int static int
...@@ -520,8 +506,8 @@ edd_has_default_sectors_per_track(struct edd_device *edev) ...@@ -520,8 +506,8 @@ edd_has_default_sectors_per_track(struct edd_device *edev)
{ {
struct edd_info *info = edd_dev_get_info(edev); struct edd_info *info = edd_dev_get_info(edev);
if (!edev || !info) if (!edev || !info)
return 1; return 0;
return !info->params.sectors_per_track; return info->params.sectors_per_track > 0;
} }
static int static int
...@@ -532,24 +518,24 @@ edd_has_edd30(struct edd_device *edev) ...@@ -532,24 +518,24 @@ edd_has_edd30(struct edd_device *edev)
char c; char c;
if (!edev || !info) if (!edev || !info)
return 1; return 0;
if (!(info->params.key == 0xBEDD || info->params.key == 0xDDBE)) { if (!(info->params.key == 0xBEDD || info->params.key == 0xDDBE)) {
return 1; return 0;
} }
for (i = 30; i <= 73; i++) { for (i = 30; i <= 73; i++) {
c = *(((uint8_t *) edd) + i + 4); c = *(((uint8_t *) info) + i + 4);
if (c) { if (c) {
nonzero_path++; nonzero_path++;
break; break;
} }
} }
if (!nonzero_path) { if (!nonzero_path) {
return 1; return 0;
} }
return 0; return 1;
} }
static EDD_DEVICE_ATTR(raw_data, 0444, edd_show_raw_data, NULL); static EDD_DEVICE_ATTR(raw_data, 0444, edd_show_raw_data, NULL);
...@@ -590,7 +576,23 @@ static struct edd_attribute * edd_attrs[] = { ...@@ -590,7 +576,23 @@ static struct edd_attribute * edd_attrs[] = {
NULL, NULL,
}; };
/**
* edd_release - free edd structure
* @kobj: kobject of edd structure
*
* This is called when the refcount of the edd structure
* reaches 0. This should happen right after we unregister,
* but just in case, we use the release callback anyway.
*/
static void edd_release(struct kobject * kobj)
{
struct edd_device * dev = to_edd_device(kobj);
kfree(dev);
}
static struct kobj_type ktype_edd = { static struct kobj_type ktype_edd = {
.release = edd_release,
.sysfs_ops = &edd_attr_ops, .sysfs_ops = &edd_attr_ops,
.default_attrs = def_attrs, .default_attrs = def_attrs,
}; };
...@@ -598,27 +600,24 @@ static struct kobj_type ktype_edd = { ...@@ -598,27 +600,24 @@ static struct kobj_type ktype_edd = {
static decl_subsys(edd,&ktype_edd); static decl_subsys(edd,&ktype_edd);
/** /**
* edd_dev_is_type() - is this EDD device a 'type' device? * edd_dev_is_type() - is this EDD device a 'type' device?
* @edev * @edev
* @type - a host bus or interface identifier string per the EDD spec * @type - a host bus or interface identifier string per the EDD spec
* *
* Returns 0 if it is a 'type' device, nonzero otherwise. * Returns 1 (TRUE) if it is a 'type' device, 0 otherwise.
*/ */
static int static int
edd_dev_is_type(struct edd_device *edev, const char *type) edd_dev_is_type(struct edd_device *edev, const char *type)
{ {
int rc;
struct edd_info *info = edd_dev_get_info(edev); struct edd_info *info = edd_dev_get_info(edev);
if (!edev || !info)
return 1;
rc = strncmp(info->params.host_bus_type, type, strlen(type)); if (edev && type && info) {
if (!rc) if (!strncmp(info->params.host_bus_type, type, strlen(type)) ||
!strncmp(info->params.interface_type, type, strlen(type)))
return 1;
}
return 0; return 0;
return strncmp(info->params.interface_type, type, strlen(type));
} }
/** /**
...@@ -631,16 +630,14 @@ static struct pci_dev * ...@@ -631,16 +630,14 @@ static struct pci_dev *
edd_get_pci_dev(struct edd_device *edev) edd_get_pci_dev(struct edd_device *edev)
{ {
struct edd_info *info = edd_dev_get_info(edev); struct edd_info *info = edd_dev_get_info(edev);
int rc;
rc = edd_dev_is_type(edev, "PCI");
if (rc)
return NULL;
if (edd_dev_is_type(edev, "PCI")) {
return pci_find_slot(info->params.interface_path.pci.bus, return pci_find_slot(info->params.interface_path.pci.bus,
PCI_DEVFN(info->params.interface_path.pci.slot, PCI_DEVFN(info->params.interface_path.pci.slot,
info->params.interface_path.pci. info->params.interface_path.pci.
function)); function));
}
return NULL;
} }
static int static int
...@@ -653,105 +650,98 @@ edd_create_symlink_to_pcidev(struct edd_device *edev) ...@@ -653,105 +650,98 @@ edd_create_symlink_to_pcidev(struct edd_device *edev)
return sysfs_create_link(&edev->kobj,&pci_dev->dev.kobj,"pci_dev"); return sysfs_create_link(&edev->kobj,&pci_dev->dev.kobj,"pci_dev");
} }
#if defined(CONFIG_SCSI) || defined(CONFIG_SCSI_MODULE)
struct edd_match_data {
struct edd_device * edev;
struct scsi_device * sd;
};
/** /**
* edd_match_scsidev() * edd_match_scsidev()
* @edev - EDD device is a known SCSI device * @edev - EDD device is a known SCSI device
* @sd - scsi_device with host who's parent is a PCI controller * @sd - scsi_device with host who's parent is a PCI controller
* *
* returns 0 on success, 1 on failure * returns 1 if a match is found, 0 if not.
*/ */
static int static int edd_match_scsidev(struct device * dev, void * d)
edd_match_scsidev(struct edd_device *edev, struct scsi_device *sd)
{ {
struct edd_info *info = edd_dev_get_info(edev); struct edd_match_data * data = (struct edd_match_data *)d;
struct edd_info *info = edd_dev_get_info(data->edev);
if (!edev || !sd || !info) struct scsi_device * sd = to_scsi_device(dev);
return 1;
if (info) {
if ((sd->channel == info->params.interface_path.pci.channel) && if ((sd->channel == info->params.interface_path.pci.channel) &&
(sd->id == info->params.device_path.scsi.id) && (sd->id == info->params.device_path.scsi.id) &&
(sd->lun == info->params.device_path.scsi.lun)) { (sd->lun == info->params.device_path.scsi.lun)) {
return 0; data->sd = sd;
}
return 1; return 1;
}
}
return 0;
} }
/** /**
* edd_find_matching_device() * edd_find_matching_device()
* @edev - edd_device to match * @edev - edd_device to match
* *
* Returns struct scsi_device * on success, * Search the SCSI devices for a drive that matches the EDD
* or NULL on failure. * device descriptor we have. If we find a match, return it,
* This assumes that all children of the PCI controller * otherwise, return NULL.
* are scsi_hosts, and that all children of scsi_hosts
* are scsi_devices.
* The reference counting probably isn't the best it could be.
*/ */
#define children_to_dev(n) container_of(n,struct device,node)
static struct scsi_device * static struct scsi_device *
edd_find_matching_scsi_device(struct edd_device *edev) edd_find_matching_scsi_device(struct edd_device *edev)
{ {
struct list_head *sdev_node; struct edd_match_data data;
int rc = 1; struct bus_type * scsi_bus = find_bus("scsi");
struct scsi_device *sd = NULL;
struct pci_dev *pci_dev;
rc = edd_dev_is_type(edev, "SCSI"); if (!scsi_bus) {
if (rc)
return NULL;
pci_dev = edd_get_pci_dev(edev);
if (!pci_dev)
return NULL; return NULL;
}
get_device(&pci_dev->dev); data.edev = edev;
list_for_each(sdev_node, &pci_dev->dev.children) { if (edd_dev_is_type(edev, "SCSI")) {
struct device *sdev_dev = children_to_dev(sdev_node); if (bus_for_each_dev(scsi_bus,NULL,&data,edd_match_scsidev))
get_device(sdev_dev); return data.sd;
sd = to_scsi_device(sdev_dev);
rc = edd_match_scsidev(edev, sd);
put_device(sdev_dev);
if (!rc)
break;
} }
return NULL;
put_device(&pci_dev->dev);
return rc ? NULL : sd;
} }
static int static int
edd_create_symlink_to_scsidev(struct edd_device *edev) edd_create_symlink_to_scsidev(struct edd_device *edev)
{ {
struct scsi_device *sdev;
struct pci_dev *pci_dev; struct pci_dev *pci_dev;
struct edd_info *info = edd_dev_get_info(edev); int rc = -EINVAL;
int rc;
rc = edd_dev_is_type(edev, "PCI"); pci_dev = edd_get_pci_dev(edev);
if (rc) if (pci_dev) {
struct scsi_device * sdev = edd_find_matching_scsi_device(edev);
if (sdev && get_device(&sdev->sdev_driverfs_dev)) {
rc = sysfs_create_link(&edev->kobj,
&sdev->sdev_driverfs_dev.kobj,
"disc");
put_device(&sdev->sdev_driverfs_dev);
}
}
return rc; return rc;
}
pci_dev = pci_find_slot(info->params.interface_path.pci.bus,
PCI_DEVFN(info->params.interface_path.pci.slot,
info->params.interface_path.pci.
function));
if (!pci_dev)
return 1;
sdev = edd_find_matching_scsi_device(edev);
if (!sdev)
return 1;
get_device(&sdev->sdev_driverfs_dev); #else
rc = sysfs_create_link(&edev->kobj,&sdev->sdev_driverfs_dev.kobj, "disc"); static struct scsi_device *
put_device(&sdev->sdev_driverfs_dev); edd_find_matching_scsi_device(struct edd_device *edev)
{
return rc; return NULL;
} }
static int
edd_create_symlink_to_scsidev(struct edd_device *edev)
{
return -ENOSYS;
}
#endif
static inline void static inline void
edd_device_unregister(struct edd_device *edev) edd_device_unregister(struct edd_device *edev)
...@@ -759,7 +749,7 @@ edd_device_unregister(struct edd_device *edev) ...@@ -759,7 +749,7 @@ edd_device_unregister(struct edd_device *edev)
kobject_unregister(&edev->kobj); kobject_unregister(&edev->kobj);
} }
static void populate_dir(struct edd_device * edev) static void edd_populate_dir(struct edd_device * edev)
{ {
struct edd_attribute * attr; struct edd_attribute * attr;
int error = 0; int error = 0;
...@@ -767,7 +757,7 @@ static void populate_dir(struct edd_device * edev) ...@@ -767,7 +757,7 @@ static void populate_dir(struct edd_device * edev)
for (i = 0; (attr = edd_attrs[i]) && !error; i++) { for (i = 0; (attr = edd_attrs[i]) && !error; i++) {
if (!attr->test || if (!attr->test ||
(attr->test && !attr->test(edev))) (attr->test && attr->test(edev)))
error = sysfs_create_file(&edev->kobj,&attr->attr); error = sysfs_create_file(&edev->kobj,&attr->attr);
} }
...@@ -791,12 +781,12 @@ edd_device_register(struct edd_device *edev, int i) ...@@ -791,12 +781,12 @@ edd_device_register(struct edd_device *edev, int i)
kobj_set_kset_s(edev,edd_subsys); kobj_set_kset_s(edev,edd_subsys);
error = kobject_register(&edev->kobj); error = kobject_register(&edev->kobj);
if (!error) if (!error)
populate_dir(edev); edd_populate_dir(edev);
return error; return error;
} }
/** /**
* edd_init() - creates driverfs tree of EDD data * edd_init() - creates sysfs tree of EDD data
* *
* This assumes that eddnr and edd were * This assumes that eddnr and edd were
* assigned in setup.c already. * assigned in setup.c already.
...@@ -845,10 +835,8 @@ edd_exit(void) ...@@ -845,10 +835,8 @@ edd_exit(void)
struct edd_device *edev; struct edd_device *edev;
for (i = 0; i < eddnr && i < EDDMAXNR; i++) { for (i = 0; i < eddnr && i < EDDMAXNR; i++) {
if ((edev = edd_devices[i])) { if ((edev = edd_devices[i]))
edd_device_unregister(edev); edd_device_unregister(edev);
kfree(edev);
}
} }
firmware_unregister(&edd_subsys); firmware_unregister(&edd_subsys);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment