* [GIT PATCH] Driver Core and sysfs patches for 2.6.17-rc1
@ 2006-04-14 20:00 Greg KH
2006-04-14 20:09 ` [PATCH 1/7] sysfs: Allow sysfs attribute files to be pollable Greg KH
0 siblings, 1 reply; 8+ messages in thread
From: Greg KH @ 2006-04-14 20:00 UTC (permalink / raw)
To: Linus Torvalds, Andrew Morton; +Cc: linux-kernel
Here are some driver core and sysfs patches for 2.6.17-rc1. They contain
the following changes:
- allow sysfs files to be polled (this missed the initial
2.6.16-git merge due to the patch being reworked by Neil. It
has been included in the -mm tree for a number of months now
with no reported issues. Sorry for missing this one the first
time around this cycle)
- fix bug in manual binding of devices to drivers.
- report the offending driver when suspend fails
- fix partition scanning and reporting to userspace (was being
reported before the scanning was finished, which isn't very
nice.)
- few other minor bugfixes and build fixes.
All of these patches have been in the -mm tree for a number of weeks, if
not months (in the case of the sysfs poll patch).
Please pull from:
rsync://rsync.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-2.6.git/
or if master.kernel.org hasn't synced up yet:
master.kernel.org:/pub/scm/linux/kernel/git/gregkh/driver-2.6.git/
Patches will be sent as a follow-on to this message to lkml for people
to see.
thanks,
greg k-h
arch/i386/kernel/Makefile | 2 -
arch/ia64/kernel/Makefile | 3 -
arch/x86_64/kernel/Makefile | 4 --
drivers/base/bus.c | 5 ++
drivers/base/class.c | 13 +++----
drivers/base/dd.c | 2 -
drivers/base/power/suspend.c | 12 ++++++
drivers/firmware/Makefile | 3 +
drivers/firmware/dmi_scan.c | 12 +++---
drivers/md/md.c | 1
drivers/pci/pci-driver.c | 6 ++-
drivers/pci/pci.c | 6 ++-
drivers/usb/core/hcd-pci.c | 7 +--
fs/partitions/check.c | 38 ++++++++++++++++-----
fs/sysfs/dir.c | 1
fs/sysfs/file.c | 76 +++++++++++++++++++++++++++++++++++++++++++
fs/sysfs/sysfs.h | 1
include/linux/genhd.h | 1
include/linux/kobject.h | 2 +
include/linux/pm.h | 8 ++++
include/linux/sysfs.h | 6 +++
lib/kobject.c | 1
22 files changed, 173 insertions(+), 37 deletions(-)
---------------
Alan Stern:
driver core: safely unbind drivers for devices not on a bus
Andrew Morton:
pm: print name of failed suspend function
Bjorn Helgaas:
DMI: move dmi_scan.c from arch/i386 to drivers/firmware/
Jayachandran C:
driver core: fix unnecessary NULL check in drivers/base/class.c
Kay Sievers:
BLOCK: delay all uevents until partition table is scanned
NeilBrown:
sysfs: Allow sysfs attribute files to be pollable
Ryan Wilson:
driver core: driver_bind attribute returns incorrect value
^ permalink raw reply [flat|nested] 8+ messages in thread* [PATCH 1/7] sysfs: Allow sysfs attribute files to be pollable 2006-04-14 20:00 [GIT PATCH] Driver Core and sysfs patches for 2.6.17-rc1 Greg KH @ 2006-04-14 20:09 ` Greg KH 2006-04-14 20:09 ` [PATCH 2/7] driver core: safely unbind drivers for devices not on a bus Greg KH 0 siblings, 1 reply; 8+ messages in thread From: Greg KH @ 2006-04-14 20:09 UTC (permalink / raw) To: linux-kernel; +Cc: NeilBrown, Greg Kroah-Hartman It works like this: Open the file Read all the contents. Call poll requesting POLLERR or POLLPRI (so select/exceptfds works) When poll returns, close the file and go to top of loop. or lseek to start of file and go back to the 'read'. Events are signaled by an object manager calling sysfs_notify(kobj, dir, attr); If the dir is non-NULL, it is used to find a subdirectory which contains the attribute (presumably created by sysfs_create_group). This has a cost of one int per attribute, one wait_queuehead per kobject, one int per open file. The name "sysfs_notify" may be confused with the inotify functionality. Maybe it would be nice to support inotify for sysfs attributes as well? This patch also uses sysfs_notify to allow /sys/block/md*/md/sync_action to be pollable Signed-off-by: Neil Brown <neilb@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- drivers/md/md.c | 1 + fs/sysfs/dir.c | 1 + fs/sysfs/file.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++ fs/sysfs/sysfs.h | 1 + include/linux/kobject.h | 2 + include/linux/sysfs.h | 6 ++++ lib/kobject.c | 1 + 7 files changed, 88 insertions(+), 0 deletions(-) 4508a7a734b111b8b7e39986237d84acb1168dd0 diff --git a/drivers/md/md.c b/drivers/md/md.c index 1ed5152..434ca39 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -163,6 +163,7 @@ void md_new_event(mddev_t *mddev) { atomic_inc(&md_event_count); wake_up(&md_event_waiters); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); } EXPORT_SYMBOL_GPL(md_new_event); diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 6cfdc9a..610b5bd 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -43,6 +43,7 @@ static struct sysfs_dirent * sysfs_new_d memset(sd, 0, sizeof(*sd)); atomic_set(&sd->s_count, 1); + atomic_set(&sd->s_event, 0); INIT_LIST_HEAD(&sd->s_children); list_add(&sd->s_sibling, &parent_sd->s_children); sd->s_element = element; diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index f1cb1dd..cf37866 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -6,6 +6,7 @@ #include <linux/fsnotify.h> #include <linux/kobject.h> #include <linux/namei.h> +#include <linux/poll.h> #include <asm/uaccess.h> #include <asm/semaphore.h> @@ -57,6 +58,7 @@ struct sysfs_buffer { struct sysfs_ops * ops; struct semaphore sem; int needs_read_fill; + int event; }; @@ -72,6 +74,7 @@ struct sysfs_buffer { */ static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) { + struct sysfs_dirent * sd = dentry->d_fsdata; struct attribute * attr = to_attr(dentry); struct kobject * kobj = to_kobj(dentry->d_parent); struct sysfs_ops * ops = buffer->ops; @@ -83,6 +86,7 @@ static int fill_read_buffer(struct dentr if (!buffer->page) return -ENOMEM; + buffer->event = atomic_read(&sd->s_event); count = ops->show(kobj,attr,buffer->page); buffer->needs_read_fill = 0; BUG_ON(count > (ssize_t)PAGE_SIZE); @@ -348,12 +352,84 @@ static int sysfs_release(struct inode * return 0; } +/* Sysfs attribute files are pollable. The idea is that you read + * the content and then you use 'poll' or 'select' to wait for + * the content to change. When the content changes (assuming the + * manager for the kobject supports notification), poll will + * return POLLERR|POLLPRI, and select will return the fd whether + * it is waiting for read, write, or exceptions. + * Once poll/select indicates that the value has changed, you + * need to close and re-open the file, as simply seeking and reading + * again will not get new data, or reset the state of 'poll'. + * Reminder: this only works for attributes which actively support + * it, and it is not possible to test an attribute from userspace + * to see if it supports poll (Nether 'poll' or 'select' return + * an appropriate error code). When in doubt, set a suitable timeout value. + */ +static unsigned int sysfs_poll(struct file *filp, poll_table *wait) +{ + struct sysfs_buffer * buffer = filp->private_data; + struct kobject * kobj = to_kobj(filp->f_dentry->d_parent); + struct sysfs_dirent * sd = filp->f_dentry->d_fsdata; + int res = 0; + + poll_wait(filp, &kobj->poll, wait); + + if (buffer->event != atomic_read(&sd->s_event)) { + res = POLLERR|POLLPRI; + buffer->needs_read_fill = 1; + } + + return res; +} + + +static struct dentry *step_down(struct dentry *dir, const char * name) +{ + struct dentry * de; + + if (dir == NULL || dir->d_inode == NULL) + return NULL; + + mutex_lock(&dir->d_inode->i_mutex); + de = lookup_one_len(name, dir, strlen(name)); + mutex_unlock(&dir->d_inode->i_mutex); + dput(dir); + if (IS_ERR(de)) + return NULL; + if (de->d_inode == NULL) { + dput(de); + return NULL; + } + return de; +} + +void sysfs_notify(struct kobject * k, char *dir, char *attr) +{ + struct dentry *de = k->dentry; + if (de) + dget(de); + if (de && dir) + de = step_down(de, dir); + if (de && attr) + de = step_down(de, attr); + if (de) { + struct sysfs_dirent * sd = de->d_fsdata; + if (sd) + atomic_inc(&sd->s_event); + wake_up_interruptible(&k->poll); + dput(de); + } +} +EXPORT_SYMBOL_GPL(sysfs_notify); + const struct file_operations sysfs_file_operations = { .read = sysfs_read_file, .write = sysfs_write_file, .llseek = generic_file_llseek, .open = sysfs_open_file, .release = sysfs_release, + .poll = sysfs_poll, }; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 32958a7..3651ffb 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -11,6 +11,7 @@ extern int sysfs_make_dirent(struct sysf extern int sysfs_add_file(struct dentry *, const struct attribute *, int); extern void sysfs_hash_and_remove(struct dentry * dir, const char * name); +extern struct sysfs_dirent *sysfs_find(struct sysfs_dirent *dir, const char * name); extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); extern void sysfs_remove_subdir(struct dentry *); diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 4cb1214..dcd0623 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -24,6 +24,7 @@ #include <linux/rwsem.h> #include <linux/kref.h> #include <linux/kernel.h> +#include <linux/wait.h> #include <asm/atomic.h> #define KOBJ_NAME_LEN 20 @@ -56,6 +57,7 @@ struct kobject { struct kset * kset; struct kobj_type * ktype; struct dentry * dentry; + wait_queue_head_t poll; }; extern int kobject_set_name(struct kobject *, const char *, ...) diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 392da5a..1ea5d3c 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -74,6 +74,7 @@ struct sysfs_dirent { umode_t s_mode; struct dentry * s_dentry; struct iattr * s_iattr; + atomic_t s_event; }; #define SYSFS_ROOT 0x0001 @@ -117,6 +118,7 @@ int sysfs_remove_bin_file(struct kobject int sysfs_create_group(struct kobject *, const struct attribute_group *); void sysfs_remove_group(struct kobject *, const struct attribute_group *); +void sysfs_notify(struct kobject * k, char *dir, char *attr); #else /* CONFIG_SYSFS */ @@ -185,6 +187,10 @@ static inline void sysfs_remove_group(st ; } +static inline void sysfs_notify(struct kobject * k, char *dir, char *attr) +{ +} + #endif /* CONFIG_SYSFS */ #endif /* _SYSFS_H_ */ diff --git a/lib/kobject.c b/lib/kobject.c index 25204a4..01d9575 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -128,6 +128,7 @@ void kobject_init(struct kobject * kobj) { kref_init(&kobj->kref); INIT_LIST_HEAD(&kobj->entry); + init_waitqueue_head(&kobj->poll); kobj->kset = kset_get(kobj->kset); } -- 1.2.6 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 2/7] driver core: safely unbind drivers for devices not on a bus 2006-04-14 20:09 ` [PATCH 1/7] sysfs: Allow sysfs attribute files to be pollable Greg KH @ 2006-04-14 20:09 ` Greg KH 2006-04-14 20:09 ` [PATCH 3/7] BLOCK: delay all uevents until partition table is scanned Greg KH 0 siblings, 1 reply; 8+ messages in thread From: Greg KH @ 2006-04-14 20:09 UTC (permalink / raw) To: linux-kernel; +Cc: Alan Stern, Greg Kroah-Hartman This patch (as667) changes the __device_release_driver() routine to prevent it from crashing when it runs across a device not on any bus. This seems logical, inasmuch as the corresponding bus_add_device() routine has an explicit check allowing it to accept such devices. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- drivers/base/dd.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) 0f836ca4c122f4ef096110d652a6326fe34e6961 diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 730a9ce..889c711 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -209,7 +209,7 @@ static void __device_release_driver(stru sysfs_remove_link(&dev->kobj, "driver"); klist_remove(&dev->knode_driver); - if (dev->bus->remove) + if (dev->bus && dev->bus->remove) dev->bus->remove(dev); else if (drv->remove) drv->remove(dev); -- 1.2.6 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 3/7] BLOCK: delay all uevents until partition table is scanned 2006-04-14 20:09 ` [PATCH 2/7] driver core: safely unbind drivers for devices not on a bus Greg KH @ 2006-04-14 20:09 ` Greg KH 2006-04-14 20:09 ` [PATCH 4/7] driver core: fix unnecessary NULL check in drivers/base/class.c Greg KH 0 siblings, 1 reply; 8+ messages in thread From: Greg KH @ 2006-04-14 20:09 UTC (permalink / raw) To: linux-kernel; +Cc: Kay Sievers, Greg Kroah-Hartman [BLOCK] delay all uevents until partition table is scanned Here we delay the annoucement of all block device events until the disk's partition table is scanned and all partition devices are already created and sysfs is populated. We have a bunch of old bugs for removable storage handling where we probe successfully for a filesystem on the raw disk, but at the same time the kernel recognizes a partition table and creates partition devices. Currently there is no sane way to tell if partitions will show up or not at the time the disk device is announced to userspace. With the delayed events we can simply skip any probe for a filesystem on the raw disk when we find already present partitions. Signed-off-by: Kay Sievers <kay.sievers@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- fs/partitions/check.c | 38 ++++++++++++++++++++++++++++++-------- include/linux/genhd.h | 1 + 2 files changed, 31 insertions(+), 8 deletions(-) d4d7e5dffc4844ef51fe11f497bd774c04413a00 diff --git a/fs/partitions/check.c b/fs/partitions/check.c index af0cb4b..f3b6af0 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -331,7 +331,9 @@ void delete_partition(struct gendisk *di devfs_remove("%s/part%d", disk->devfs_name, part); if (p->holder_dir) kobject_unregister(p->holder_dir); - kobject_unregister(&p->kobj); + kobject_uevent(&p->kobj, KOBJ_REMOVE); + kobject_del(&p->kobj); + kobject_put(&p->kobj); } void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len) @@ -357,7 +359,10 @@ void add_partition(struct gendisk *disk, snprintf(p->kobj.name,KOBJ_NAME_LEN,"%s%d",disk->kobj.name,part); p->kobj.parent = &disk->kobj; p->kobj.ktype = &ktype_part; - kobject_register(&p->kobj); + kobject_init(&p->kobj); + kobject_add(&p->kobj); + if (!disk->part_uevent_suppress) + kobject_uevent(&p->kobj, KOBJ_ADD); partition_sysfs_add_subdir(p); disk->part[part-1] = p; } @@ -395,6 +400,8 @@ void register_disk(struct gendisk *disk) { struct block_device *bdev; char *s; + int i; + struct hd_struct *p; int err; strlcpy(disk->kobj.name,disk->disk_name,KOBJ_NAME_LEN); @@ -406,13 +413,12 @@ void register_disk(struct gendisk *disk) return; disk_sysfs_symlinks(disk); disk_sysfs_add_subdirs(disk); - kobject_uevent(&disk->kobj, KOBJ_ADD); /* No minors to use for partitions */ if (disk->minors == 1) { if (disk->devfs_name[0] != '\0') devfs_add_disk(disk); - return; + goto exit; } /* always add handle for the whole disk */ @@ -420,16 +426,32 @@ void register_disk(struct gendisk *disk) /* No such device (e.g., media were just removed) */ if (!get_capacity(disk)) - return; + goto exit; bdev = bdget_disk(disk, 0); if (!bdev) - return; + goto exit; + /* scan partition table, but suppress uevents */ bdev->bd_invalidated = 1; - if (blkdev_get(bdev, FMODE_READ, 0) < 0) - return; + disk->part_uevent_suppress = 1; + err = blkdev_get(bdev, FMODE_READ, 0); + disk->part_uevent_suppress = 0; + if (err < 0) + goto exit; blkdev_put(bdev); + +exit: + /* announce disk after possible partitions are already created */ + kobject_uevent(&disk->kobj, KOBJ_ADD); + + /* announce possible partitions */ + for (i = 1; i < disk->minors; i++) { + p = disk->part[i-1]; + if (!p || !p->nr_sects) + continue; + kobject_uevent(&p->kobj, KOBJ_ADD); + } } int rescan_partitions(struct gendisk *disk, struct block_device *bdev) diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 10a27f2..2ef845b 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -105,6 +105,7 @@ struct gendisk { * disks that can't be partitioned. */ char disk_name[32]; /* name of major driver */ struct hd_struct **part; /* [indexed by minor] */ + int part_uevent_suppress; struct block_device_operations *fops; struct request_queue *queue; void *private_data; -- 1.2.6 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 4/7] driver core: fix unnecessary NULL check in drivers/base/class.c 2006-04-14 20:09 ` [PATCH 3/7] BLOCK: delay all uevents until partition table is scanned Greg KH @ 2006-04-14 20:09 ` Greg KH 2006-04-14 20:09 ` [PATCH 5/7] driver core: driver_bind attribute returns incorrect value Greg KH 0 siblings, 1 reply; 8+ messages in thread From: Greg KH @ 2006-04-14 20:09 UTC (permalink / raw) To: linux-kernel; +Cc: Jayachandran C, Jayachandran C., Greg Kroah-Hartman This patch tries to fix an issue in drivers/base/class.c, please review and apply if correct. Patch Description: "parent_class" is checked for NULL already, so removed the unnecessary check. Signed-off-by: Jayachandran C. <c.jayachandran@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- drivers/base/class.c | 13 ++++++------- 1 files changed, 6 insertions(+), 7 deletions(-) a14388904ca67197c9a531dba2358d8131697865 diff --git a/drivers/base/class.c b/drivers/base/class.c index df7fdab..0e71dff 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -562,14 +562,13 @@ int class_device_add(struct class_device kobject_uevent(&class_dev->kobj, KOBJ_ADD); /* notify any interfaces this device is now here */ - if (parent_class) { - down(&parent_class->sem); - list_add_tail(&class_dev->node, &parent_class->children); - list_for_each_entry(class_intf, &parent_class->interfaces, node) - if (class_intf->add) - class_intf->add(class_dev, class_intf); - up(&parent_class->sem); + down(&parent_class->sem); + list_add_tail(&class_dev->node, &parent_class->children); + list_for_each_entry(class_intf, &parent_class->interfaces, node) { + if (class_intf->add) + class_intf->add(class_dev, class_intf); } + up(&parent_class->sem); register_done: if (error) { -- 1.2.6 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 5/7] driver core: driver_bind attribute returns incorrect value 2006-04-14 20:09 ` [PATCH 4/7] driver core: fix unnecessary NULL check in drivers/base/class.c Greg KH @ 2006-04-14 20:09 ` Greg KH 2006-04-14 20:09 ` [PATCH 6/7] pm: print name of failed suspend function Greg KH 0 siblings, 1 reply; 8+ messages in thread From: Greg KH @ 2006-04-14 20:09 UTC (permalink / raw) To: linux-kernel; +Cc: Ryan Wilson, Greg Kroah-Hartman The manual driver <-> device binding attribute in sysfs doesn't return the correct value on failure or success of driver_probe_device. driver_probe_device returns 1 on success (the driver accepted the device) or 0 on probe failure (when the driver didn't accept the device but no real error occured). However, the attribute can't just return 0 or 1, it must return the number of bytes consumed from buf or an error value. Returning 0 indicates to userspace that nothing was written (even though the kernel has tried to do the bind/probe and failed). Returning 1 indicates that only one character was accepted in which case userspace will re-try the write with a partial string. A more correct version of driver_bind would return count (to indicate the entire string was consumed) when driver_probe_device returns 1 and -ENODEV when driver_probe_device returns 0. This patch makes that change. Signed-off-by: Ryan Wilson <hap9@epoch.ncsc.mil> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- drivers/base/bus.c | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) 372254018eb1b65ee69210d11686bfc65c8d84db diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 48718b7..76656ac 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -188,6 +188,11 @@ static ssize_t driver_bind(struct device up(&dev->sem); if (dev->parent) up(&dev->parent->sem); + + if (err > 0) /* success */ + err = count; + else if (err == 0) /* driver didn't accept device */ + err = -ENODEV; } put_device(dev); put_bus(bus); -- 1.2.6 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 6/7] pm: print name of failed suspend function 2006-04-14 20:09 ` [PATCH 5/7] driver core: driver_bind attribute returns incorrect value Greg KH @ 2006-04-14 20:09 ` Greg KH 2006-04-14 20:09 ` [PATCH 7/7] DMI: move dmi_scan.c from arch/i386 to drivers/firmware/ Greg KH 0 siblings, 1 reply; 8+ messages in thread From: Greg KH @ 2006-04-14 20:09 UTC (permalink / raw) To: linux-kernel; +Cc: Andrew Morton, Greg Kroah-Hartman Print more diagnostic info to help identify the source of power management suspend failures. Example: usb_hcd_pci_suspend(): pci_set_power_state+0x0/0x1af() returns -22 pci_device_suspend(): usb_hcd_pci_suspend+0x0/0x11b() returns -22 suspend_device(): pci_device_suspend+0x0/0x34() returns -22 Work-in-progress. It needs lots more suspend_report_result() calls sprinkled everywhere. Cc: Patrick Mochel <mochel@digitalimplant.org> Cc: Pavel Machek <pavel@ucw.cz> Cc: Nigel Cunningham <nigel@suspend2.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- drivers/base/power/suspend.c | 12 ++++++++++++ drivers/pci/pci-driver.c | 6 ++++-- drivers/pci/pci.c | 6 ++++-- drivers/usb/core/hcd-pci.c | 7 +++---- include/linux/pm.h | 8 ++++++++ 5 files changed, 31 insertions(+), 8 deletions(-) 026694920579590c73b5c56705d543568ed5ad41 diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index bdb6066..662209d 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -10,6 +10,8 @@ #include <linux/vt_kern.h> #include <linux/device.h> +#include <linux/kallsyms.h> +#include <linux/pm.h> #include "../base.h" #include "power.h" @@ -58,6 +60,7 @@ int suspend_device(struct device * dev, if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) { dev_dbg(dev, "suspending\n"); error = dev->bus->suspend(dev, state); + suspend_report_result(dev->bus->suspend, error); } up(&dev->sem); return error; @@ -169,3 +172,12 @@ int device_power_down(pm_message_t state EXPORT_SYMBOL_GPL(device_power_down); +void __suspend_report_result(const char *function, void *fn, int ret) +{ + if (ret) { + printk(KERN_ERR "%s(): ", function); + print_fn_descriptor_symbol("%s() returns ", (unsigned long)fn); + printk("%d\n", ret); + } +} +EXPORT_SYMBOL_GPL(__suspend_report_result); diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index f22f69a..1456759 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -271,10 +271,12 @@ static int pci_device_suspend(struct dev struct pci_driver * drv = pci_dev->driver; int i = 0; - if (drv && drv->suspend) + if (drv && drv->suspend) { i = drv->suspend(pci_dev, state); - else + suspend_report_result(drv->suspend, i); + } else { pci_save_state(pci_dev); + } return i; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index bea1ad1..042fa52 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -307,9 +307,11 @@ pci_set_power_state(struct pci_dev *dev, * Can enter D0 from any state, but if we can only go deeper * to sleep if we're already in a low power state */ - if (state != PCI_D0 && dev->current_state > state) + if (state != PCI_D0 && dev->current_state > state) { + printk(KERN_ERR "%s(): %s: state=%d, current state=%d\n", + __FUNCTION__, pci_name(dev), state, dev->current_state); return -EINVAL; - else if (dev->current_state == state) + } else if (dev->current_state == state) return 0; /* we're already there */ /* find PCI PM capability in list */ diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 0d2193b..66b7840 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -213,11 +213,9 @@ int usb_hcd_pci_suspend (struct pci_dev if (hcd->driver->suspend) { retval = hcd->driver->suspend(hcd, message); - if (retval) { - dev_dbg (&dev->dev, "PCI pre-suspend fail, %d\n", - retval); + suspend_report_result(hcd->driver->suspend, retval); + if (retval) goto done; - } } synchronize_irq(dev->irq); @@ -263,6 +261,7 @@ int usb_hcd_pci_suspend (struct pci_dev * some device state (e.g. as part of clock reinit). */ retval = pci_set_power_state (dev, PCI_D3hot); + suspend_report_result(pci_set_power_state, retval); if (retval == 0) { int wake = device_can_wakeup(&hcd->self.root_hub->dev); diff --git a/include/linux/pm.h b/include/linux/pm.h index 6df2585..66be589 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -199,6 +199,12 @@ extern int device_suspend(pm_message_t s extern int dpm_runtime_suspend(struct device *, pm_message_t); extern void dpm_runtime_resume(struct device *); +extern void __suspend_report_result(const char *function, void *fn, int ret); + +#define suspend_report_result(fn, ret) \ + do { \ + __suspend_report_result(__FUNCTION__, fn, ret); \ + } while (0) #else /* !CONFIG_PM */ @@ -219,6 +225,8 @@ static inline void dpm_runtime_resume(st { } +#define suspend_report_result(fn, ret) do { } while (0) + #endif /* changes to device_may_wakeup take effect on the next pm state change. -- 1.2.6 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH 7/7] DMI: move dmi_scan.c from arch/i386 to drivers/firmware/ 2006-04-14 20:09 ` [PATCH 6/7] pm: print name of failed suspend function Greg KH @ 2006-04-14 20:09 ` Greg KH 0 siblings, 0 replies; 8+ messages in thread From: Greg KH @ 2006-04-14 20:09 UTC (permalink / raw) To: linux-kernel; +Cc: Bjorn Helgaas, Andrew Morton, Greg Kroah-Hartman dmi_scan.c is arch-independent and is used by i386, x86_64, and ia64. Currently all three arches compile it from arch/i386, which means that ia64 and x86_64 depend on things in arch/i386 that they wouldn't otherwise care about. This is simply "mv arch/i386/kernel/dmi_scan.c drivers/firmware/" (removing trailing whitespace) and the associated Makefile changes. All three architectures already set CONFIG_DMI in their top-level Kconfig files. Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com> Cc: Andi Kleen <ak@muc.de> Cc: "Luck, Tony" <tony.luck@intel.com> Cc: Andrey Panin <pazke@orbita1.ru> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> --- arch/i386/kernel/Makefile | 2 arch/i386/kernel/dmi_scan.c | 358 ------------------------------------------- arch/ia64/kernel/Makefile | 3 arch/x86_64/kernel/Makefile | 4 drivers/firmware/Makefile | 3 drivers/firmware/dmi_scan.c | 358 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 363 insertions(+), 365 deletions(-) delete mode 100644 arch/i386/kernel/dmi_scan.c create mode 100644 drivers/firmware/dmi_scan.c 4f705ae3e94ffaafe8d35f71ff4d5c499bb06814 diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile index 5b9ed21..96fb8a0 100644 --- a/arch/i386/kernel/Makefile +++ b/arch/i386/kernel/Makefile @@ -6,7 +6,7 @@ extra-y := head.o init_task.o vmlinux.ld obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o \ ptrace.o time.o ioport.o ldt.o setup.o i8259.o sys_i386.o \ - pci-dma.o i386_ksyms.o i387.o dmi_scan.o bootflag.o \ + pci-dma.o i386_ksyms.o i387.o bootflag.o \ quirks.o i8237.o topology.o alternative.o obj-y += cpu/ diff --git a/arch/i386/kernel/dmi_scan.c b/arch/i386/kernel/dmi_scan.c deleted file mode 100644 index 5efceeb..0000000 --- a/arch/i386/kernel/dmi_scan.c +++ /dev/null @@ -1,358 +0,0 @@ -#include <linux/types.h> -#include <linux/string.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/dmi.h> -#include <linux/efi.h> -#include <linux/bootmem.h> -#include <linux/slab.h> -#include <asm/dmi.h> - -static char * __init dmi_string(struct dmi_header *dm, u8 s) -{ - u8 *bp = ((u8 *) dm) + dm->length; - char *str = ""; - - if (s) { - s--; - while (s > 0 && *bp) { - bp += strlen(bp) + 1; - s--; - } - - if (*bp != 0) { - str = dmi_alloc(strlen(bp) + 1); - if (str != NULL) - strcpy(str, bp); - else - printk(KERN_ERR "dmi_string: out of memory.\n"); - } - } - - return str; -} - -/* - * We have to be cautious here. We have seen BIOSes with DMI pointers - * pointing to completely the wrong place for example - */ -static int __init dmi_table(u32 base, int len, int num, - void (*decode)(struct dmi_header *)) -{ - u8 *buf, *data; - int i = 0; - - buf = dmi_ioremap(base, len); - if (buf == NULL) - return -1; - - data = buf; - - /* - * Stop when we see all the items the table claimed to have - * OR we run off the end of the table (also happens) - */ - while ((i < num) && (data - buf + sizeof(struct dmi_header)) <= len) { - struct dmi_header *dm = (struct dmi_header *)data; - /* - * We want to know the total length (formated area and strings) - * before decoding to make sure we won't run off the table in - * dmi_decode or dmi_string - */ - data += dm->length; - while ((data - buf < len - 1) && (data[0] || data[1])) - data++; - if (data - buf < len - 1) - decode(dm); - data += 2; - i++; - } - dmi_iounmap(buf, len); - return 0; -} - -static int __init dmi_checksum(u8 *buf) -{ - u8 sum = 0; - int a; - - for (a = 0; a < 15; a++) - sum += buf[a]; - - return sum == 0; -} - -static char *dmi_ident[DMI_STRING_MAX]; -static LIST_HEAD(dmi_devices); - -/* - * Save a DMI string - */ -static void __init dmi_save_ident(struct dmi_header *dm, int slot, int string) -{ - char *p, *d = (char*) dm; - - if (dmi_ident[slot]) - return; - - p = dmi_string(dm, d[string]); - if (p == NULL) - return; - - dmi_ident[slot] = p; -} - -static void __init dmi_save_devices(struct dmi_header *dm) -{ - int i, count = (dm->length - sizeof(struct dmi_header)) / 2; - struct dmi_device *dev; - - for (i = 0; i < count; i++) { - char *d = (char *)(dm + 1) + (i * 2); - - /* Skip disabled device */ - if ((*d & 0x80) == 0) - continue; - - dev = dmi_alloc(sizeof(*dev)); - if (!dev) { - printk(KERN_ERR "dmi_save_devices: out of memory.\n"); - break; - } - - dev->type = *d++ & 0x7f; - dev->name = dmi_string(dm, *d); - dev->device_data = NULL; - - list_add(&dev->list, &dmi_devices); - } -} - -static void __init dmi_save_ipmi_device(struct dmi_header *dm) -{ - struct dmi_device *dev; - void * data; - - data = dmi_alloc(dm->length); - if (data == NULL) { - printk(KERN_ERR "dmi_save_ipmi_device: out of memory.\n"); - return; - } - - memcpy(data, dm, dm->length); - - dev = dmi_alloc(sizeof(*dev)); - if (!dev) { - printk(KERN_ERR "dmi_save_ipmi_device: out of memory.\n"); - return; - } - - dev->type = DMI_DEV_TYPE_IPMI; - dev->name = "IPMI controller"; - dev->device_data = data; - - list_add(&dev->list, &dmi_devices); -} - -/* - * Process a DMI table entry. Right now all we care about are the BIOS - * and machine entries. For 2.5 we should pull the smbus controller info - * out of here. - */ -static void __init dmi_decode(struct dmi_header *dm) -{ - switch(dm->type) { - case 0: /* BIOS Information */ - dmi_save_ident(dm, DMI_BIOS_VENDOR, 4); - dmi_save_ident(dm, DMI_BIOS_VERSION, 5); - dmi_save_ident(dm, DMI_BIOS_DATE, 8); - break; - case 1: /* System Information */ - dmi_save_ident(dm, DMI_SYS_VENDOR, 4); - dmi_save_ident(dm, DMI_PRODUCT_NAME, 5); - dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6); - dmi_save_ident(dm, DMI_PRODUCT_SERIAL, 7); - break; - case 2: /* Base Board Information */ - dmi_save_ident(dm, DMI_BOARD_VENDOR, 4); - dmi_save_ident(dm, DMI_BOARD_NAME, 5); - dmi_save_ident(dm, DMI_BOARD_VERSION, 6); - break; - case 10: /* Onboard Devices Information */ - dmi_save_devices(dm); - break; - case 38: /* IPMI Device Information */ - dmi_save_ipmi_device(dm); - } -} - -static int __init dmi_present(char __iomem *p) -{ - u8 buf[15]; - memcpy_fromio(buf, p, 15); - if ((memcmp(buf, "_DMI_", 5) == 0) && dmi_checksum(buf)) { - u16 num = (buf[13] << 8) | buf[12]; - u16 len = (buf[7] << 8) | buf[6]; - u32 base = (buf[11] << 24) | (buf[10] << 16) | - (buf[9] << 8) | buf[8]; - - /* - * DMI version 0.0 means that the real version is taken from - * the SMBIOS version, which we don't know at this point. - */ - if (buf[14] != 0) - printk(KERN_INFO "DMI %d.%d present.\n", - buf[14] >> 4, buf[14] & 0xF); - else - printk(KERN_INFO "DMI present.\n"); - if (dmi_table(base,len, num, dmi_decode) == 0) - return 0; - } - return 1; -} - -void __init dmi_scan_machine(void) -{ - char __iomem *p, *q; - int rc; - - if (efi_enabled) { - if (efi.smbios == EFI_INVALID_TABLE_ADDR) - goto out; - - /* This is called as a core_initcall() because it isn't - * needed during early boot. This also means we can - * iounmap the space when we're done with it. - */ - p = dmi_ioremap(efi.smbios, 32); - if (p == NULL) - goto out; - - rc = dmi_present(p + 0x10); /* offset of _DMI_ string */ - dmi_iounmap(p, 32); - if (!rc) - return; - } - else { - /* - * no iounmap() for that ioremap(); it would be a no-op, but - * it's so early in setup that sucker gets confused into doing - * what it shouldn't if we actually call it. - */ - p = dmi_ioremap(0xF0000, 0x10000); - if (p == NULL) - goto out; - - for (q = p; q < p + 0x10000; q += 16) { - rc = dmi_present(q); - if (!rc) - return; - } - } - out: printk(KERN_INFO "DMI not present or invalid.\n"); -} - -/** - * dmi_check_system - check system DMI data - * @list: array of dmi_system_id structures to match against - * - * Walk the blacklist table running matching functions until someone - * returns non zero or we hit the end. Callback function is called for - * each successfull match. Returns the number of matches. - */ -int dmi_check_system(struct dmi_system_id *list) -{ - int i, count = 0; - struct dmi_system_id *d = list; - - while (d->ident) { - for (i = 0; i < ARRAY_SIZE(d->matches); i++) { - int s = d->matches[i].slot; - if (s == DMI_NONE) - continue; - if (dmi_ident[s] && strstr(dmi_ident[s], d->matches[i].substr)) - continue; - /* No match */ - goto fail; - } - count++; - if (d->callback && d->callback(d)) - break; -fail: d++; - } - - return count; -} -EXPORT_SYMBOL(dmi_check_system); - -/** - * dmi_get_system_info - return DMI data value - * @field: data index (see enum dmi_filed) - * - * Returns one DMI data value, can be used to perform - * complex DMI data checks. - */ -char *dmi_get_system_info(int field) -{ - return dmi_ident[field]; -} -EXPORT_SYMBOL(dmi_get_system_info); - -/** - * dmi_find_device - find onboard device by type/name - * @type: device type or %DMI_DEV_TYPE_ANY to match all device types - * @desc: device name string or %NULL to match all - * @from: previous device found in search, or %NULL for new search. - * - * Iterates through the list of known onboard devices. If a device is - * found with a matching @vendor and @device, a pointer to its device - * structure is returned. Otherwise, %NULL is returned. - * A new search is initiated by passing %NULL to the @from argument. - * If @from is not %NULL, searches continue from next device. - */ -struct dmi_device * dmi_find_device(int type, const char *name, - struct dmi_device *from) -{ - struct list_head *d, *head = from ? &from->list : &dmi_devices; - - for(d = head->next; d != &dmi_devices; d = d->next) { - struct dmi_device *dev = list_entry(d, struct dmi_device, list); - - if (((type == DMI_DEV_TYPE_ANY) || (dev->type == type)) && - ((name == NULL) || (strcmp(dev->name, name) == 0))) - return dev; - } - - return NULL; -} -EXPORT_SYMBOL(dmi_find_device); - -/** - * dmi_get_year - Return year of a DMI date - * @field: data index (like dmi_get_system_info) - * - * Returns -1 when the field doesn't exist. 0 when it is broken. - */ -int dmi_get_year(int field) -{ - int year; - char *s = dmi_get_system_info(field); - - if (!s) - return -1; - if (*s == '\0') - return 0; - s = strrchr(s, '/'); - if (!s) - return 0; - - s += 1; - year = simple_strtoul(s, NULL, 0); - if (year && year < 100) { /* 2-digit year */ - year += 1900; - if (year < 1996) /* no dates < spec 1.0 */ - year += 100; - } - - return year; -} diff --git a/arch/ia64/kernel/Makefile b/arch/ia64/kernel/Makefile index 59e871d..09a0dbc 100644 --- a/arch/ia64/kernel/Makefile +++ b/arch/ia64/kernel/Makefile @@ -7,7 +7,7 @@ extra-y := head.o init_task.o vmlinux.ld obj-y := acpi.o entry.o efi.o efi_stub.o gate-data.o fsys.o ia64_ksyms.o irq.o irq_ia64.o \ irq_lsapic.o ivt.o machvec.o pal.o patch.o process.o perfmon.o ptrace.o sal.o \ salinfo.o semaphore.o setup.o signal.o sys_ia64.o time.o traps.o unaligned.o \ - unwind.o mca.o mca_asm.o topology.o dmi_scan.o + unwind.o mca.o mca_asm.o topology.o obj-$(CONFIG_IA64_BRL_EMU) += brl_emu.o obj-$(CONFIG_IA64_GENERIC) += acpi-ext.o @@ -30,7 +30,6 @@ obj-$(CONFIG_IA64_MCA_RECOVERY) += mca_r obj-$(CONFIG_KPROBES) += kprobes.o jprobes.o obj-$(CONFIG_IA64_UNCACHED_ALLOCATOR) += uncached.o mca_recovery-y += mca_drv.o mca_drv_asm.o -dmi_scan-y += ../../i386/kernel/dmi_scan.o # The gate DSO image is built using a special linker script. targets += gate.so gate-syms.o diff --git a/arch/x86_64/kernel/Makefile b/arch/x86_64/kernel/Makefile index a098a11..059c883 100644 --- a/arch/x86_64/kernel/Makefile +++ b/arch/x86_64/kernel/Makefile @@ -8,7 +8,7 @@ obj-y := process.o signal.o entry.o trap ptrace.o time.o ioport.o ldt.o setup.o i8259.o sys_x86_64.o \ x8664_ksyms.o i387.o syscall.o vsyscall.o \ setup64.o bootflag.o e820.o reboot.o quirks.o i8237.o \ - dmi_scan.o pci-dma.o pci-nommu.o + pci-dma.o pci-nommu.o obj-$(CONFIG_X86_MCE) += mce.o obj-$(CONFIG_X86_MCE_INTEL) += mce_intel.o @@ -49,5 +49,3 @@ intel_cacheinfo-y += ../../i386/kernel/ quirks-y += ../../i386/kernel/quirks.o i8237-y += ../../i386/kernel/i8237.o msr-$(subst m,y,$(CONFIG_X86_MSR)) += ../../i386/kernel/msr.o -dmi_scan-y += ../../i386/kernel/dmi_scan.o - diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 8542997..98e395f 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -1,7 +1,8 @@ # # Makefile for the linux kernel. # -obj-$(CONFIG_EDD) += edd.o +obj-$(CONFIG_DMI) += dmi_scan.o +obj-$(CONFIG_EDD) += edd.o obj-$(CONFIG_EFI_VARS) += efivars.o obj-$(CONFIG_EFI_PCDP) += pcdp.o obj-$(CONFIG_DELL_RBU) += dell_rbu.o diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c new file mode 100644 index 0000000..948bd7e --- /dev/null +++ b/drivers/firmware/dmi_scan.c @@ -0,0 +1,358 @@ +#include <linux/types.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/dmi.h> +#include <linux/efi.h> +#include <linux/bootmem.h> +#include <linux/slab.h> +#include <asm/dmi.h> + +static char * __init dmi_string(struct dmi_header *dm, u8 s) +{ + u8 *bp = ((u8 *) dm) + dm->length; + char *str = ""; + + if (s) { + s--; + while (s > 0 && *bp) { + bp += strlen(bp) + 1; + s--; + } + + if (*bp != 0) { + str = dmi_alloc(strlen(bp) + 1); + if (str != NULL) + strcpy(str, bp); + else + printk(KERN_ERR "dmi_string: out of memory.\n"); + } + } + + return str; +} + +/* + * We have to be cautious here. We have seen BIOSes with DMI pointers + * pointing to completely the wrong place for example + */ +static int __init dmi_table(u32 base, int len, int num, + void (*decode)(struct dmi_header *)) +{ + u8 *buf, *data; + int i = 0; + + buf = dmi_ioremap(base, len); + if (buf == NULL) + return -1; + + data = buf; + + /* + * Stop when we see all the items the table claimed to have + * OR we run off the end of the table (also happens) + */ + while ((i < num) && (data - buf + sizeof(struct dmi_header)) <= len) { + struct dmi_header *dm = (struct dmi_header *)data; + /* + * We want to know the total length (formated area and strings) + * before decoding to make sure we won't run off the table in + * dmi_decode or dmi_string + */ + data += dm->length; + while ((data - buf < len - 1) && (data[0] || data[1])) + data++; + if (data - buf < len - 1) + decode(dm); + data += 2; + i++; + } + dmi_iounmap(buf, len); + return 0; +} + +static int __init dmi_checksum(u8 *buf) +{ + u8 sum = 0; + int a; + + for (a = 0; a < 15; a++) + sum += buf[a]; + + return sum == 0; +} + +static char *dmi_ident[DMI_STRING_MAX]; +static LIST_HEAD(dmi_devices); + +/* + * Save a DMI string + */ +static void __init dmi_save_ident(struct dmi_header *dm, int slot, int string) +{ + char *p, *d = (char*) dm; + + if (dmi_ident[slot]) + return; + + p = dmi_string(dm, d[string]); + if (p == NULL) + return; + + dmi_ident[slot] = p; +} + +static void __init dmi_save_devices(struct dmi_header *dm) +{ + int i, count = (dm->length - sizeof(struct dmi_header)) / 2; + struct dmi_device *dev; + + for (i = 0; i < count; i++) { + char *d = (char *)(dm + 1) + (i * 2); + + /* Skip disabled device */ + if ((*d & 0x80) == 0) + continue; + + dev = dmi_alloc(sizeof(*dev)); + if (!dev) { + printk(KERN_ERR "dmi_save_devices: out of memory.\n"); + break; + } + + dev->type = *d++ & 0x7f; + dev->name = dmi_string(dm, *d); + dev->device_data = NULL; + + list_add(&dev->list, &dmi_devices); + } +} + +static void __init dmi_save_ipmi_device(struct dmi_header *dm) +{ + struct dmi_device *dev; + void * data; + + data = dmi_alloc(dm->length); + if (data == NULL) { + printk(KERN_ERR "dmi_save_ipmi_device: out of memory.\n"); + return; + } + + memcpy(data, dm, dm->length); + + dev = dmi_alloc(sizeof(*dev)); + if (!dev) { + printk(KERN_ERR "dmi_save_ipmi_device: out of memory.\n"); + return; + } + + dev->type = DMI_DEV_TYPE_IPMI; + dev->name = "IPMI controller"; + dev->device_data = data; + + list_add(&dev->list, &dmi_devices); +} + +/* + * Process a DMI table entry. Right now all we care about are the BIOS + * and machine entries. For 2.5 we should pull the smbus controller info + * out of here. + */ +static void __init dmi_decode(struct dmi_header *dm) +{ + switch(dm->type) { + case 0: /* BIOS Information */ + dmi_save_ident(dm, DMI_BIOS_VENDOR, 4); + dmi_save_ident(dm, DMI_BIOS_VERSION, 5); + dmi_save_ident(dm, DMI_BIOS_DATE, 8); + break; + case 1: /* System Information */ + dmi_save_ident(dm, DMI_SYS_VENDOR, 4); + dmi_save_ident(dm, DMI_PRODUCT_NAME, 5); + dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6); + dmi_save_ident(dm, DMI_PRODUCT_SERIAL, 7); + break; + case 2: /* Base Board Information */ + dmi_save_ident(dm, DMI_BOARD_VENDOR, 4); + dmi_save_ident(dm, DMI_BOARD_NAME, 5); + dmi_save_ident(dm, DMI_BOARD_VERSION, 6); + break; + case 10: /* Onboard Devices Information */ + dmi_save_devices(dm); + break; + case 38: /* IPMI Device Information */ + dmi_save_ipmi_device(dm); + } +} + +static int __init dmi_present(char __iomem *p) +{ + u8 buf[15]; + memcpy_fromio(buf, p, 15); + if ((memcmp(buf, "_DMI_", 5) == 0) && dmi_checksum(buf)) { + u16 num = (buf[13] << 8) | buf[12]; + u16 len = (buf[7] << 8) | buf[6]; + u32 base = (buf[11] << 24) | (buf[10] << 16) | + (buf[9] << 8) | buf[8]; + + /* + * DMI version 0.0 means that the real version is taken from + * the SMBIOS version, which we don't know at this point. + */ + if (buf[14] != 0) + printk(KERN_INFO "DMI %d.%d present.\n", + buf[14] >> 4, buf[14] & 0xF); + else + printk(KERN_INFO "DMI present.\n"); + if (dmi_table(base,len, num, dmi_decode) == 0) + return 0; + } + return 1; +} + +void __init dmi_scan_machine(void) +{ + char __iomem *p, *q; + int rc; + + if (efi_enabled) { + if (efi.smbios == EFI_INVALID_TABLE_ADDR) + goto out; + + /* This is called as a core_initcall() because it isn't + * needed during early boot. This also means we can + * iounmap the space when we're done with it. + */ + p = dmi_ioremap(efi.smbios, 32); + if (p == NULL) + goto out; + + rc = dmi_present(p + 0x10); /* offset of _DMI_ string */ + dmi_iounmap(p, 32); + if (!rc) + return; + } + else { + /* + * no iounmap() for that ioremap(); it would be a no-op, but + * it's so early in setup that sucker gets confused into doing + * what it shouldn't if we actually call it. + */ + p = dmi_ioremap(0xF0000, 0x10000); + if (p == NULL) + goto out; + + for (q = p; q < p + 0x10000; q += 16) { + rc = dmi_present(q); + if (!rc) + return; + } + } + out: printk(KERN_INFO "DMI not present or invalid.\n"); +} + +/** + * dmi_check_system - check system DMI data + * @list: array of dmi_system_id structures to match against + * + * Walk the blacklist table running matching functions until someone + * returns non zero or we hit the end. Callback function is called for + * each successfull match. Returns the number of matches. + */ +int dmi_check_system(struct dmi_system_id *list) +{ + int i, count = 0; + struct dmi_system_id *d = list; + + while (d->ident) { + for (i = 0; i < ARRAY_SIZE(d->matches); i++) { + int s = d->matches[i].slot; + if (s == DMI_NONE) + continue; + if (dmi_ident[s] && strstr(dmi_ident[s], d->matches[i].substr)) + continue; + /* No match */ + goto fail; + } + count++; + if (d->callback && d->callback(d)) + break; +fail: d++; + } + + return count; +} +EXPORT_SYMBOL(dmi_check_system); + +/** + * dmi_get_system_info - return DMI data value + * @field: data index (see enum dmi_filed) + * + * Returns one DMI data value, can be used to perform + * complex DMI data checks. + */ +char *dmi_get_system_info(int field) +{ + return dmi_ident[field]; +} +EXPORT_SYMBOL(dmi_get_system_info); + +/** + * dmi_find_device - find onboard device by type/name + * @type: device type or %DMI_DEV_TYPE_ANY to match all device types + * @desc: device name string or %NULL to match all + * @from: previous device found in search, or %NULL for new search. + * + * Iterates through the list of known onboard devices. If a device is + * found with a matching @vendor and @device, a pointer to its device + * structure is returned. Otherwise, %NULL is returned. + * A new search is initiated by passing %NULL to the @from argument. + * If @from is not %NULL, searches continue from next device. + */ +struct dmi_device * dmi_find_device(int type, const char *name, + struct dmi_device *from) +{ + struct list_head *d, *head = from ? &from->list : &dmi_devices; + + for(d = head->next; d != &dmi_devices; d = d->next) { + struct dmi_device *dev = list_entry(d, struct dmi_device, list); + + if (((type == DMI_DEV_TYPE_ANY) || (dev->type == type)) && + ((name == NULL) || (strcmp(dev->name, name) == 0))) + return dev; + } + + return NULL; +} +EXPORT_SYMBOL(dmi_find_device); + +/** + * dmi_get_year - Return year of a DMI date + * @field: data index (like dmi_get_system_info) + * + * Returns -1 when the field doesn't exist. 0 when it is broken. + */ +int dmi_get_year(int field) +{ + int year; + char *s = dmi_get_system_info(field); + + if (!s) + return -1; + if (*s == '\0') + return 0; + s = strrchr(s, '/'); + if (!s) + return 0; + + s += 1; + year = simple_strtoul(s, NULL, 0); + if (year && year < 100) { /* 2-digit year */ + year += 1900; + if (year < 1996) /* no dates < spec 1.0 */ + year += 100; + } + + return year; +} -- 1.2.6 ^ permalink raw reply related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2006-04-14 20:11 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2006-04-14 20:00 [GIT PATCH] Driver Core and sysfs patches for 2.6.17-rc1 Greg KH 2006-04-14 20:09 ` [PATCH 1/7] sysfs: Allow sysfs attribute files to be pollable Greg KH 2006-04-14 20:09 ` [PATCH 2/7] driver core: safely unbind drivers for devices not on a bus Greg KH 2006-04-14 20:09 ` [PATCH 3/7] BLOCK: delay all uevents until partition table is scanned Greg KH 2006-04-14 20:09 ` [PATCH 4/7] driver core: fix unnecessary NULL check in drivers/base/class.c Greg KH 2006-04-14 20:09 ` [PATCH 5/7] driver core: driver_bind attribute returns incorrect value Greg KH 2006-04-14 20:09 ` [PATCH 6/7] pm: print name of failed suspend function Greg KH 2006-04-14 20:09 ` [PATCH 7/7] DMI: move dmi_scan.c from arch/i386 to drivers/firmware/ Greg KH
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox