linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* Re: [RFC][PATCH] Dynamic PCI Device IDs
@ 2003-05-01  0:39 Matt_Domsch
  2003-05-02 23:15 ` Greg KH
  0 siblings, 1 reply; 15+ messages in thread
From: Matt_Domsch @ 2003-05-01  0:39 UTC (permalink / raw)
  To: greg; +Cc: alan, linux-kernel, jgarzik

> First off, nice idea, but I think the implementation needs a bit of
> work.

Thanks.  I didn't expect it to be perfect first-pass.
Let me answer some questions out-of-order, maybe that will help.

> > echo 1 > probe_it
> > Why wouldn't the writing to the new_id file cause the probe to
> happen immediatly?  Why wait?  So I think we can get rid of that file.

That was my first idea, but Jeff said:
http://marc.theaimsgroup.com/?l=linux-kernel&m=104681922317051&w=2
        I think there is value in decoupling the two operations:
        
        	echo "0x0000 0x0000 0x0000 0x0000 0x0000 0x0000" >
.../3c59x/table
        	echo 1 > .../3c59x/probe_it
        
        Because you want the id table additions to be persistant in the face
of
        cardbus unplug/replug, and for the case where cardbus card may not
be
        present yet, but {will,may} be soon.
        

> >  Individual device drivers may override the behavior of the new_id
> >   file, for instance, if they need to also pass driver-specific
> >   information.  Likewise, reading the individual dynamic ID files
> >   can be overridden by the driver.
> 
> Why would a driver want to override these behaviors?

Because the one field I'm not filling in by default is the opaque
unsigned long driver_data.  Most drivers don't need it, and those that
do tend to come in two camps:  those that put a single integer there
which is an internal table lookup for equivalancy, and those that put a
pointer there to something (which definitely shouldn't be passed from
userspace).  There aren't many of the latter (which is good), but I
didn't want them to break with the introduction of this patch.  They
should be recoded to to a table lookup, but that's beyond the scope I
wanted to deal with today. :-)

That said, if drivers implement their own write routines, I wanted to
give them a way to programatically expose what data should be written,
and how.   I'll grant that the current help text isn't programatically
helpful ATM.

> Ick, don't put help files within the kernel image.  Didn't you take
> them all out for the edd patch a while ago?  :)

If we resolve the above, I'll be happy to nuke them.

> Also, do we really need to keep a list of id's visible to userspace
> (the "0" file above?  We currently don't do that for the "static ids"
> (yeah I know they are easily extracted from the module image...)

That's the only reason I know - so one can always write an app to
retreive what IDs a given module has, both static and dynamic.  I don't
think it's critical to keep.


> Is that the exists() callback?

Yes.

> Is it really needed?  Can't the pci core do this without needing to
> push that logic into the driver core?  After all, it knows if the
> pci_driver->probe() call is non-NULL, the driver core doesn't.

I'll look into this.  I did something similar in the EDD code, so I
reused the idea again.

> Also, we really need a generic way to easily create subdirectories
> within the driver model, to keep you from having to dive down into
> kobjects and the mess, like you had to do here.  Pat's said he will
> work on this next, once he emerges from his OLS paper writing hole,
> and there's even a bug assigned to him for this:
>         http://bugme.osdl.org/show_bug.cgi?id=650
> Once that is in, this patch should clean up a whole lot.  I'd
> recommend waiting for that

Yes, that's fine.  I'd love to have that ability.  I did it the way Pat
wanted it a couple months ago.
http://marc.theaimsgroup.com/?l=linux-kernel&m=104678872809999&w=2

> (or if you want to tackle it, please do) before applying something
> like your patch.

I'm srue Pat will do a fine job. :-)

Thanks for the comments.
-Matt


-- 
Matt Domsch
Sr. Software Engineer, Lead Engineer, Architect
Dell Linux Solutions www.dell.com/linux
Linux on Dell mailing lists @ http://lists.us.dell.com



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH] Dynamic PCI Device IDs
  2003-05-01  0:39 [RFC][PATCH] Dynamic PCI Device IDs Matt_Domsch
@ 2003-05-02 23:15 ` Greg KH
  2003-05-05  5:37   ` Jeff Garzik
  2003-05-05 22:51   ` Matt Domsch
  0 siblings, 2 replies; 15+ messages in thread
From: Greg KH @ 2003-05-02 23:15 UTC (permalink / raw)
  To: Matt_Domsch; +Cc: alan, linux-kernel, jgarzik

On Wed, Apr 30, 2003 at 07:39:57PM -0500, Matt_Domsch@Dell.com wrote:
> > First off, nice idea, but I think the implementation needs a bit of
> > work.
> 
> Thanks.  I didn't expect it to be perfect first-pass.
> Let me answer some questions out-of-order, maybe that will help.
> 
> > > echo 1 > probe_it
> > > Why wouldn't the writing to the new_id file cause the probe to
> > happen immediatly?  Why wait?  So I think we can get rid of that file.
> 
> That was my first idea, but Jeff said:
> http://marc.theaimsgroup.com/?l=linux-kernel&m=104681922317051&w=2
>         I think there is value in decoupling the two operations:
>         
>         	echo "0x0000 0x0000 0x0000 0x0000 0x0000 0x0000" >
> .../3c59x/table
>         	echo 1 > .../3c59x/probe_it
>         
>         Because you want the id table additions to be persistant in the face of
>         cardbus unplug/replug, and for the case where cardbus card may not be
>         present yet, but {will,may} be soon.

But by adding the device ids, they will be persistant, for that driver,
right?  Then when the device is plugged in, the core will iterate over
the static and dynamic ids, right?  If so, I don't see how a "probe_it"
file is needed.

> > >  Individual device drivers may override the behavior of the new_id
> > >   file, for instance, if they need to also pass driver-specific
> > >   information.  Likewise, reading the individual dynamic ID files
> > >   can be overridden by the driver.
> > 
> > Why would a driver want to override these behaviors?
> 
> Because the one field I'm not filling in by default is the opaque
> unsigned long driver_data.  Most drivers don't need it, and those that
> do tend to come in two camps:  those that put a single integer there
> which is an internal table lookup for equivalancy, and those that put a
> pointer there to something (which definitely shouldn't be passed from
> userspace).  There aren't many of the latter (which is good), but I
> didn't want them to break with the introduction of this patch.  They
> should be recoded to to a table lookup, but that's beyond the scope I
> wanted to deal with today. :-)
> 
> That said, if drivers implement their own write routines, I wanted to
> give them a way to programatically expose what data should be written,
> and how.   I'll grant that the current help text isn't programatically
> helpful ATM.

Ah, can't you just not worry about that driver_data field somehow?  Like
say, "Any driver that depends on it, can't use the dynamic_id"? :)

I know, wishful thinking.  But I still don't think you should be
cluttering up the driver core with that, it should be able to be
localized within the pci code somehow.

> > Ick, don't put help files within the kernel image.  Didn't you take
> > them all out for the edd patch a while ago?  :)
> 
> If we resolve the above, I'll be happy to nuke them.

Either way, we shouldn't have help files within the kernel.  I'd say
nuke them, and write up some good documentation :)

> > Also, do we really need to keep a list of id's visible to userspace
> > (the "0" file above?  We currently don't do that for the "static ids"
> > (yeah I know they are easily extracted from the module image...)
> 
> That's the only reason I know - so one can always write an app to
> retreive what IDs a given module has, both static and dynamic.  I don't
> think it's critical to keep.

I'd say drop it for now, it keeps the code simpler.  If people _really_
cry out for some way to retrieve them later, we can worry about that
then.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH] Dynamic PCI Device IDs
  2003-05-02 23:15 ` Greg KH
@ 2003-05-05  5:37   ` Jeff Garzik
  2003-05-06  0:17     ` Greg KH
  2003-05-05 22:51   ` Matt Domsch
  1 sibling, 1 reply; 15+ messages in thread
From: Jeff Garzik @ 2003-05-05  5:37 UTC (permalink / raw)
  To: Greg KH; +Cc: Matt_Domsch, alan, linux-kernel, jgarzik

Greg KH wrote:
> On Wed, Apr 30, 2003 at 07:39:57PM -0500, Matt_Domsch@Dell.com wrote:
> 
>>>First off, nice idea, but I think the implementation needs a bit of
>>>work.
>>
>>Thanks.  I didn't expect it to be perfect first-pass.
>>Let me answer some questions out-of-order, maybe that will help.
>>
>>
>>>>echo 1 > probe_it
>>>>Why wouldn't the writing to the new_id file cause the probe to
>>>
>>>happen immediatly?  Why wait?  So I think we can get rid of that file.
>>
>>That was my first idea, but Jeff said:
>>http://marc.theaimsgroup.com/?l=linux-kernel&m=104681922317051&w=2
>>        I think there is value in decoupling the two operations:
>>        
>>        	echo "0x0000 0x0000 0x0000 0x0000 0x0000 0x0000" >
>>.../3c59x/table
>>        	echo 1 > .../3c59x/probe_it
>>        
>>        Because you want the id table additions to be persistant in the face of
>>        cardbus unplug/replug, and for the case where cardbus card may not be
>>        present yet, but {will,may} be soon.
> 
> 
> But by adding the device ids, they will be persistant, for that driver,
> right?  Then when the device is plugged in, the core will iterate over
> the static and dynamic ids, right?  If so, I don't see how a "probe_it"
> file is needed.

Consider the case:
Device already exists, and is plugged in.  Like a standard PCI card.
Driver doesn't support PCI id, and the sysadmin uses /bin/echo to add one.

For unplugged case, you know you don't need to re-run the probe.

If you really don't want probe_it, I suppose you could re-run the 
driver's PCI probe for the cases where it is redundant.  However, my own 
preference is to let the sysadmin decide whether or not the driver's PCI 
probe should be re-run.

	Jeff





^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH] Dynamic PCI Device IDs
  2003-05-02 23:15 ` Greg KH
  2003-05-05  5:37   ` Jeff Garzik
@ 2003-05-05 22:51   ` Matt Domsch
  2003-05-06  0:15     ` Greg KH
  1 sibling, 1 reply; 15+ messages in thread
From: Matt Domsch @ 2003-05-05 22:51 UTC (permalink / raw)
  To: Greg KH; +Cc: alan, linux-kernel, jgarzik

> Ah, can't you just not worry about that driver_data field somehow?  

How about this?  I've added a 'uses_driver_data' bit to the struct that
holds the dynids list, and the store_new_id() function always allows
driver_data to be passed in from userspace, but unless the driver sets
'uses_driver_data' (and therefore should check that the values are
reasonable), it only ever gets passed a 0 there.

> Either way, we shouldn't have help files within the kernel.  I'd say
> nuke them, and write up some good documentation :)

Done.

> Also, do we really need to keep a list of id's visible to
> userspace?  I'd say drop it for now, it keeps the code simpler.

Done.

I've nuked the subdirectory/kobject, probe_it file, all exporting
dynids, and fixed up Documentation/pci.txt.  Certainly much simpler.

I've tested this against 2.5.69 by removing an e100 ID from the source
and adding it back in via the new_id file.

Patch against 2.5.69 below, and at:
  http://domsch.com/linux/patches/dynids/linux-2.5/linux-2.5.69-dynids-20030505.patch.bz2
  http://domsch.com/linux/patches/dynids/linux-2.5/linux-2.5.69-dynids-20030505.patch.bz2.asc

 Documentation/pci.txt      |   24 +++
 drivers/pci/pci-driver.c   |  297 +++++++++++++++++++++++++++++++++++++----
 include/linux/pci-dynids.h |   18 ++
 include/linux/pci.h        |   16 ++
 4 files changed, 326 insertions, 29 deletions

BK (with complete change history):
http://mdomsch.bkbits.net/linux-2.5-dynids

This will update the following files:

 drivers/pci/pci-sysfs-dynids.c |  251 ---------------------------
 Documentation/pci.txt          |   24 ++
 drivers/pci/Makefile           |    4 
 drivers/pci/pci-driver.c       |  381 +++++++++++++++++++++++++++++++--------
 drivers/pci/pci-sysfs-dynids.c |  251 +++++++++++++++++++++++++++
 drivers/pci/pci.h              |    2 
 include/linux/device.h         |   22 +-
 include/linux/pci-dynids.h     |   68 ++++---
 include/linux/pci.h            |   28 ++-
 9 files changed, 664 insertions(+), 367 deletions(-)

through these ChangeSets:

<Matt_Domsch@dell.com> (03/05/05 1.1066)
   pci.h whitespace cleanups

<Matt_Domsch@dell.com> (03/05/05 1.1065)
   PCI dynids - documentation fixes, id_table NULL check

<Matt_Domsch@dell.com> (03/05/05 1.1042.92.2)
   Shrink dynids feature set
   
   Per recommendation from GregKH:
   Remove directory 'dynamic_id'
   Remove exporting dynamic_id/0 files
   Remove probe_it driver attribute
   Move new_id into driver directory as a driver attribute.  Make it
   probe when new IDs are added.
   Move attribute existance test into pci-driver.c completely.
   

<Matt_Domsch@dell.com> (03/04/16 1.1042.8.1)
   Device Driver Dynamic PCI Device IDs
   
   Provides a mechanism to pass new PCI device IDs to device drivers
   at runtime, rather than relying only on a static compiled-in list
   of supported IDs.
   [...]


Feedback appreciated.

Thanks,
Matt

-- 
Matt Domsch
Sr. Software Engineer, Lead Engineer, Architect
Dell Linux Solutions www.dell.com/linux
Linux on Dell mailing lists @ http://lists.us.dell.com

diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5/Documentation/pci.txt dynids/linux-2.5-dynids/Documentation/pci.txt
--- linux-2.5/Documentation/pci.txt	Mon May  5 17:19:05 2003
+++ dynids/linux-2.5-dynids/Documentation/pci.txt	Mon May  5 17:16:31 2003
@@ -45,8 +45,6 @@
 	id_table	Pointer to table of device ID's the driver is
 			interested in.  Most drivers should export this
 			table using MODULE_DEVICE_TABLE(pci,...).
-			Set to NULL to call probe() function for every
-			PCI device known to the system.
 	probe		Pointer to a probing function which gets called (during
 			execution of pci_register_driver for already existing
 			devices or later if a new device gets inserted) for all
@@ -82,6 +80,28 @@
 	class,		Device class to match. The class_mask tells which bits
 	class_mask	of the class are honored during the comparison.
 	driver_data	Data private to the driver.
+
+Most drivers don't need to use the driver_data field.  Best practice
+for use of driver_data is to use it as an index into a static list of
+equivalant device types, not to use it as a pointer.
+
+Have a table entry {PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID}
+to have probe() called for every PCI device known to the system.
+
+New PCI IDs may be added to a device driver at runtime by writing
+to the file /sys/bus/pci/drivers/{driver}/new_id.  When added, the
+driver will probe for all devices it can support.
+
+echo "vendor device subvendor subdevice class class_mask driver_data" > \
+ /sys/bus/pci/drivers/{driver}/new_id
+where all fields are passed in as hexadecimal values (no leading 0x).
+Users need pass only as many fields as necessary; vendor, device,
+subvendor, and subdevice fields default to PCI_ANY_ID (FFFFFFFF),
+class and classmask fields default to 0, and driver_data defaults to
+0UL.  Device drivers must call
+   pci_dynids_set_use_driver_data(pci_driver *, 1)
+in order for the driver_data field to get passed to the driver.
+Otherwise, only a 0 is passed in that field.
 
 When the driver exits, it just calls pci_unregister_driver() and the PCI layer
 automatically calls the remove hook for all devices handled by the driver.
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5/drivers/pci/pci-driver.c dynids/linux-2.5-dynids/drivers/pci/pci-driver.c
--- linux-2.5/drivers/pci/pci-driver.c	Mon May  5 17:19:31 2003
+++ dynids/linux-2.5-dynids/drivers/pci/pci-driver.c	Mon May  5 17:16:50 2003
@@ -6,6 +6,8 @@
 #include <linux/pci.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/device.h>
+#include <linux/pci-dynids.h>
 #include "pci.h"
 
 /*
@@ -13,6 +15,26 @@
  */
 
 /**
+ * pci_match_one_device - Tell if a PCI device structure has a matching PCI device id structure
+ * @id: single PCI device id structure to match
+ * @dev: the PCI device structure to match against
+ * 
+ * Returns the matching pci_device_id structure or %NULL if there is no match.
+ */
+
+static inline const struct pci_device_id *
+pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
+{
+	if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&
+	    (id->device == PCI_ANY_ID || id->device == dev->device) &&
+	    (id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&
+	    (id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&
+	    !((id->class ^ dev->class) & id->class_mask))
+		return id;
+	return NULL;
+}
+
+/**
  * pci_match_device - Tell if a PCI device structure has a matching PCI device id structure
  * @ids: array of PCI device id structures to search in
  * @dev: the PCI device structure to match against
@@ -25,17 +47,90 @@
 pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev)
 {
 	while (ids->vendor || ids->subvendor || ids->class_mask) {
-		if ((ids->vendor == PCI_ANY_ID || ids->vendor == dev->vendor) &&
-		    (ids->device == PCI_ANY_ID || ids->device == dev->device) &&
-		    (ids->subvendor == PCI_ANY_ID || ids->subvendor == dev->subsystem_vendor) &&
-		    (ids->subdevice == PCI_ANY_ID || ids->subdevice == dev->subsystem_device) &&
-		    !((ids->class ^ dev->class) & ids->class_mask))
-			return ids;
+		if (pci_match_one_device(ids, dev))
+		    return ids;
 		ids++;
 	}
 	return NULL;
 }
 
+/**
+ * pci_device_probe_static()
+ * 
+ * returns 0 and sets pci_dev->driver when drv claims pci_dev, else error.
+ */
+static int
+pci_device_probe_static(struct pci_driver *drv,
+			  struct pci_dev *pci_dev)
+{		   
+	int error = -ENODEV;
+	const struct pci_device_id *id;
+
+	if (!drv->id_table)
+		return error;
+	id = pci_match_device(drv->id_table, pci_dev);
+	if (id)
+		error = drv->probe(pci_dev, id);
+	if (error >= 0) {
+		pci_dev->driver = drv;
+		return 0;
+	}
+	return error;
+}
+
+/**
+ * pci_device_probe_dynamic()
+ * 
+ * Walk the dynamic ID list looking for a match.
+ * returns 0 and sets pci_dev->driver when drv claims pci_dev, else error.
+ */
+static int
+pci_device_probe_dynamic(struct pci_driver *drv,
+			   struct pci_dev *pci_dev)
+{		   
+	int error = -ENODEV;
+	struct list_head *pos;
+	struct dynid *dynid;
+	
+	spin_lock(&drv->dynids.lock);
+	list_for_each(pos, &drv->dynids.list) {
+		dynid = list_entry(pos, struct dynid, node);
+		if (pci_match_one_device(&dynid->id, pci_dev)) {
+			spin_unlock(&drv->dynids.lock);
+			error = drv->probe(pci_dev, &dynid->id);
+			if (error >= 0) {
+				pci_dev->driver = drv;
+				return 0;
+			}
+			return error;
+		}
+	}
+	spin_unlock(&drv->dynids.lock);
+	return error;
+}
+
+/**
+ * __pci_device_probe()
+ * 
+ * returns 0  on success, else error.
+ * side-effect: pci_dev->driver is set to drv when drv claims pci_dev.
+ */
+static int
+__pci_device_probe(struct pci_driver *drv,
+		   struct pci_dev *pci_dev)
+{		   
+	int error = 0;
+
+	if (!pci_dev->driver && drv->probe) {
+		error = pci_device_probe_static(drv, pci_dev);
+		if (error >= 0)
+			return error;
+
+		error = pci_device_probe_dynamic(drv, pci_dev);
+	}
+	return error;
+}
+
 static int pci_device_probe(struct device * dev)
 {
 	int error = 0;
@@ -44,17 +139,9 @@
 
 	drv = to_pci_driver(dev->driver);
 	pci_dev = to_pci_dev(dev);
-
-	if (!pci_dev->driver && drv->probe) {
-		const struct pci_device_id *id;
-
-		id = pci_match_device(drv->id_table, pci_dev);
-		if (id)
-			error = drv->probe(pci_dev, id);
-		if (error >= 0) {
-			pci_dev->driver = drv;
-			error = 0;
-		}
+	if (get_device(dev)) {
+		error = __pci_device_probe(drv, pci_dev);
+		put_device(dev);
 	}
 	return error;
 }
@@ -101,6 +188,149 @@
 	return 0;
 }
 
+/*
+ * If __pci_device_probe() returns 0, it matched at least one previously
+ * unclaimed device.  If it returns -ENODEV, it didn't match.  Both are
+ * alright in this case, just keep searching for new devices.
+ */
+
+static int
+probe_each_pci_dev(struct pci_driver *drv)
+{
+	struct pci_dev *pci_dev=NULL;
+	int error = 0;
+	pci_for_each_dev(pci_dev) {
+		if (get_device(&pci_dev->dev)) {
+			error = __pci_device_probe(drv, pci_dev);
+			put_device(&pci_dev->dev);
+			if (error && error != -ENODEV)
+				return error;
+		}
+	}
+	return error;
+}
+
+static inline void
+dynid_init(struct dynid *dynid)
+{
+	memset(dynid, 0, sizeof(*dynid));
+	INIT_LIST_HEAD(&dynid->node);
+}
+
+/**
+ * store_new_id
+ * @ pdrv
+ * @ buf
+ * @ count
+ *
+ * Adds a new dynamic pci device ID to this driver,
+ * and causes the driver to probe for all devices again.
+ */
+static inline ssize_t
+store_new_id(struct device_driver * driver, const char * buf, size_t count)
+{
+	struct dynid *dynid;
+	struct pci_driver *pdrv = to_pci_driver(driver);
+	__u32 vendor=PCI_ANY_ID, device=PCI_ANY_ID, subvendor=PCI_ANY_ID,
+		subdevice=PCI_ANY_ID, class=0, class_mask=0;
+	unsigned long driver_data=0;
+	int fields=0, error=0;
+
+	fields = sscanf(buf, "%x %x %x %x %x %x %lux",
+			&vendor, &device, &subvendor, &subdevice,
+			&class, &class_mask, &driver_data);
+	if (fields < 0) return -EINVAL;
+
+	dynid = kmalloc(sizeof(*dynid), GFP_KERNEL);
+	if (!dynid) return -ENOMEM;
+	dynid_init(dynid);
+
+	dynid->id.vendor = vendor;
+	dynid->id.device = device;
+	dynid->id.subvendor = subvendor;
+	dynid->id.subdevice = subdevice;
+	dynid->id.class = class;
+	dynid->id.class_mask = class_mask;
+	dynid->id.driver_data = pdrv->dynids.use_driver_data ? driver_data : 0UL;
+
+	spin_lock(&pdrv->dynids.lock);
+	list_add(&pdrv->dynids.list, &dynid->node);
+	spin_unlock(&pdrv->dynids.lock);
+
+        if (get_driver(&pdrv->driver)) {
+                error = probe_each_pci_dev(pdrv);
+                put_driver(&pdrv->driver);
+        }
+        if (error < 0)
+                return error;
+        return count;
+
+
+	return count;
+}
+
+static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
+
+#define kobj_to_pci_driver(obj) container_of(obj, struct device_driver, kobj)
+#define attr_to_driver_attribute(obj) container_of(obj, struct driver_attribute, attr)
+
+static ssize_t
+pci_driver_attr_show(struct kobject * kobj, struct attribute *attr, char *buf)
+{
+	struct device_driver *driver = kobj_to_pci_driver(kobj);
+	struct driver_attribute *dattr = attr_to_driver_attribute(attr);
+	ssize_t ret = 0;
+
+	if (get_driver(driver)) {
+		if (dattr->show)
+			ret = dattr->show(driver, buf);
+		put_driver(driver);
+	}
+	return ret;
+}
+
+static ssize_t
+pci_driver_attr_store(struct kobject * kobj, struct attribute *attr,
+		      const char *buf, size_t count)
+{
+	struct device_driver *driver = kobj_to_pci_driver(kobj);
+	struct driver_attribute *dattr = attr_to_driver_attribute(attr);
+	ssize_t ret = 0;
+
+	if (get_driver(driver)) {
+		if (dattr->store)
+			ret = dattr->store(driver, buf, count);
+		put_driver(driver);
+	}
+	return ret;
+}
+
+static struct sysfs_ops pci_driver_sysfs_ops = {
+	.show = pci_driver_attr_show,
+	.store = pci_driver_attr_store,
+};
+static struct kobj_type pci_driver_kobj_type = {
+	.sysfs_ops     = &pci_driver_sysfs_ops,
+};
+
+static int
+pci_populate_driver_dir(struct pci_driver * drv)
+{
+	int error = 0;
+
+	if (drv->probe != NULL)
+		error = sysfs_create_file(&drv->driver.kobj,&driver_attr_new_id.attr);
+	return error;
+}
+
+static inline void
+pci_init_dynids(struct pci_dynids *dynids)
+{
+	memset(dynids, 0, sizeof(*dynids));
+	spin_lock_init(&dynids->lock);
+	INIT_LIST_HEAD(&dynids->list);
+}
+
 /**
  * pci_register_driver - register a new pci driver
  * @drv: the driver structure to register
@@ -122,9 +352,16 @@
 	drv->driver.resume = pci_device_resume;
 	drv->driver.suspend = pci_device_suspend;
 	drv->driver.remove = pci_device_remove;
+	drv->driver.kobj.ktype = &pci_driver_kobj_type;
+	pci_init_dynids(&drv->dynids);
 
 	/* register with core */
 	count = driver_register(&drv->driver);
+
+	if (count >= 0) {
+		pci_populate_driver_dir(drv);
+	}
+		
 	return count ? count : 1;
 }
 
@@ -180,22 +417,30 @@
  */
 static int pci_bus_match(struct device * dev, struct device_driver * drv) 
 {
-	struct pci_dev * pci_dev = to_pci_dev(dev);
+	const struct pci_dev * pci_dev = to_pci_dev(dev);
 	struct pci_driver * pci_drv = to_pci_driver(drv);
 	const struct pci_device_id * ids = pci_drv->id_table;
+	const struct pci_device_id *found_id;
+	struct list_head *pos;
+	struct dynid *dynid;
 
-	if (!ids) 
+	if (!ids)
 		return 0;
 
-	while (ids->vendor || ids->subvendor || ids->class_mask) {
-		if ((ids->vendor == PCI_ANY_ID || ids->vendor == pci_dev->vendor) &&
-		    (ids->device == PCI_ANY_ID || ids->device == pci_dev->device) &&
-		    (ids->subvendor == PCI_ANY_ID || ids->subvendor == pci_dev->subsystem_vendor) &&
-		    (ids->subdevice == PCI_ANY_ID || ids->subdevice == pci_dev->subsystem_device) &&
-		    !((ids->class ^ pci_dev->class) & ids->class_mask))
+	found_id = pci_match_device(ids, pci_dev);
+	if (found_id) 
+		return 1;
+
+	spin_lock(&pci_drv->dynids.lock);
+	list_for_each(pos, &pci_drv->dynids.list) {
+		dynid = list_entry(pos, struct dynid, node);
+		if (pci_match_one_device(&dynid->id, pci_dev)) {
+			spin_unlock(&pci_drv->dynids.lock);
 			return 1;
-		ids++;
+		}
 	}
+	spin_unlock(&pci_drv->dynids.lock);
+
 	return 0;
 }
 
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5/include/linux/pci-dynids.h dynids/linux-2.5-dynids/include/linux/pci-dynids.h
--- linux-2.5/include/linux/pci-dynids.h	Wed Dec 31 18:00:00 1969
+++ dynids/linux-2.5-dynids/include/linux/pci-dynids.h	Mon May  5 17:17:07 2003
@@ -0,0 +1,18 @@
+/*
+ *	PCI defines and function prototypes
+ *	Copyright 2003 Dell Computer Corporation
+ *        by Matt Domsch <Matt_Domsch@dell.com>
+ */
+
+#ifndef LINUX_PCI_DYNIDS_H
+#define LINUX_PCI_DYNIDS_H
+
+#include <linux/list.h>
+#include <linux/mod_devicetable.h>
+
+struct dynid {
+	struct list_head        node;
+	struct pci_device_id    id;
+};
+
+#endif
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5/include/linux/pci.h dynids/linux-2.5-dynids/include/linux/pci.h
--- linux-2.5/include/linux/pci.h	Mon May  5 17:19:51 2003
+++ dynids/linux-2.5-dynids/include/linux/pci.h	Mon May  5 17:26:41 2003
@@ -490,10 +490,16 @@
 	unsigned long end;
 };
 
+struct pci_dynids {
+	spinlock_t lock;            /* protects list, index */
+	struct list_head list;      /* for IDs added at runtime */
+	unsigned int use_driver_data:1; /* pci_driver->driver_data is used */
+};
+
 struct pci_driver {
 	struct list_head node;
 	char *name;
-	const struct pci_device_id *id_table;	/* NULL if wants all devices */
+	const struct pci_device_id *id_table;	/* must be non-NULL for probe to be called */
 	int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id);	/* New device inserted */
 	void (*remove) (struct pci_dev *dev);	/* Device removed (NULL if not a hot-plug capable driver) */
 	int  (*save_state) (struct pci_dev *dev, u32 state);    /* Save Device Context */
@@ -502,6 +508,7 @@
 	int  (*enable_wake) (struct pci_dev *dev, u32 state, int enable);   /* Enable wake event */
 
 	struct device_driver	driver;
+	struct pci_dynids dynids;
 };
 
 #define	to_pci_driver(drv) container_of(drv,struct pci_driver, driver)
@@ -702,6 +709,12 @@
 			 struct pci_bus_wrapped *wrapped_parent);
 extern int pci_remove_device_safe(struct pci_dev *dev);
 
+static inline void
+pci_dynids_set_use_driver_data(struct pci_driver *pdrv, int val)
+{
+	pdrv->dynids.use_driver_data = val;
+}
+
 #endif /* CONFIG_PCI */
 
 /* Include architecture-dependent settings and functions */
@@ -750,6 +763,7 @@
 static inline int scsi_to_pci_dma_dir(unsigned char scsi_dir) { return scsi_dir; }
 static inline int pci_find_capability (struct pci_dev *dev, int cap) {return 0; }
 static inline const struct pci_device_id *pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev) { return NULL; }
+static inline void pci_dynids_set_use_driver_data(struct pci_driver *pdrv, int val) { }
 
 /* Power management related routines */
 static inline int pci_save_state(struct pci_dev *dev, u32 *buffer) { return 0; }



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH] Dynamic PCI Device IDs
  2003-05-05 22:51   ` Matt Domsch
@ 2003-05-06  0:15     ` Greg KH
  0 siblings, 0 replies; 15+ messages in thread
From: Greg KH @ 2003-05-06  0:15 UTC (permalink / raw)
  To: Matt Domsch; +Cc: alan, linux-kernel, jgarzik

On Mon, May 05, 2003 at 05:51:35PM -0500, Matt Domsch wrote:
> > Ah, can't you just not worry about that driver_data field somehow?  
> 
> How about this?  I've added a 'uses_driver_data' bit to the struct that
> holds the dynids list, and the store_new_id() function always allows
> driver_data to be passed in from userspace, but unless the driver sets
> 'uses_driver_data' (and therefore should check that the values are
> reasonable), it only ever gets passed a 0 there.

I like this patch a _lot_ better, nice job.  Only one comment:

> +/**
> + * store_new_id
> + * @ pdrv
> + * @ buf
> + * @ count
> + *
> + * Adds a new dynamic pci device ID to this driver,
> + * and causes the driver to probe for all devices again.
> + */
> +static inline ssize_t
> +store_new_id(struct device_driver * driver, const char * buf, size_t count)
> +{
> +	struct dynid *dynid;
> +	struct pci_driver *pdrv = to_pci_driver(driver);
> +	__u32 vendor=PCI_ANY_ID, device=PCI_ANY_ID, subvendor=PCI_ANY_ID,
> +		subdevice=PCI_ANY_ID, class=0, class_mask=0;
> +	unsigned long driver_data=0;
> +	int fields=0, error=0;
> +
> +	fields = sscanf(buf, "%x %x %x %x %x %x %lux",
> +			&vendor, &device, &subvendor, &subdevice,
> +			&class, &class_mask, &driver_data);
> +	if (fields < 0) return -EINVAL;
> +
> +	dynid = kmalloc(sizeof(*dynid), GFP_KERNEL);
> +	if (!dynid) return -ENOMEM;
> +	dynid_init(dynid);
> +
> +	dynid->id.vendor = vendor;
> +	dynid->id.device = device;
> +	dynid->id.subvendor = subvendor;
> +	dynid->id.subdevice = subdevice;
> +	dynid->id.class = class;
> +	dynid->id.class_mask = class_mask;
> +	dynid->id.driver_data = pdrv->dynids.use_driver_data ? driver_data : 0UL;
> +
> +	spin_lock(&pdrv->dynids.lock);
> +	list_add(&pdrv->dynids.list, &dynid->node);
> +	spin_unlock(&pdrv->dynids.lock);
> +
> +        if (get_driver(&pdrv->driver)) {
> +                error = probe_each_pci_dev(pdrv);
> +                put_driver(&pdrv->driver);
> +        }
> +        if (error < 0)
> +                return error;
> +        return count;
> +
> +
> +	return count;
> +}

Oops, lost the tabs at the end of the function :)

This function will not link up a device to a driver properly within the
driver core, only with the pci code.  So if you do this, the driver core
still thinks you have a device that is unbound, right?  Also, the
symlinks don't get created from the bus to the device I think, correct?

Unfortunatly, looking at the driver core real quickly, I don't see a
simple way to kick the probe cycle off again for all pci devices, but
I'm probably just missing something somewhere...

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH] Dynamic PCI Device IDs
  2003-05-05  5:37   ` Jeff Garzik
@ 2003-05-06  0:17     ` Greg KH
  0 siblings, 0 replies; 15+ messages in thread
From: Greg KH @ 2003-05-06  0:17 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: Matt_Domsch, alan, linux-kernel, jgarzik

On Mon, May 05, 2003 at 01:37:52AM -0400, Jeff Garzik wrote:
> Greg KH wrote:
> >
> >But by adding the device ids, they will be persistant, for that driver,
> >right?  Then when the device is plugged in, the core will iterate over
> >the static and dynamic ids, right?  If so, I don't see how a "probe_it"
> >file is needed.
> 
> Consider the case:
> Device already exists, and is plugged in.  Like a standard PCI card.
> Driver doesn't support PCI id, and the sysadmin uses /bin/echo to add one.

Great, probe gets run right then, and that's what we want, right?

> For unplugged case, you know you don't need to re-run the probe.

But a probe scan across all devices doesn't really take much time,
right?  And yes this would be "redundant" but it's a whole lot tougher
to figure out that we don't need to re-run a probe, than to just always
do it :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH] Dynamic PCI Device IDs
  2003-05-13 21:28     ` Patrick Mochel
@ 2003-05-13 21:33       ` Patrick Mochel
  0 siblings, 0 replies; 15+ messages in thread
From: Patrick Mochel @ 2003-05-13 21:33 UTC (permalink / raw)
  To: Matt Domsch; +Cc: Greg KH, alan, linux-kernel, jgarzik


On Tue, 13 May 2003, Patrick Mochel wrote:

> 
> On Tue, 6 May 2003, Matt Domsch wrote:
> 
> > > You can't just call driver_attach(), as the bus semaphore needs to be
> > > locked before doing so.  In short, you almost need to duplicate
> > > bus_add_driver(), but not quite :)
> > 
> > Right, and it seems to work. I made driver_attach non-static, declared
> > it extern in pci.h, and call it in pci-driver.c while holding the bus
> > semaphore and references to the driver and the bus.  This also let me
> > delete my probe_each_pci_dev() function and let the driver core
> > handle it.
> > 
> > Pat, can you ack the changes to bus.c and device.h please?
> 
> ACK. I'll add them to my tree.

I take that back, since it's already checked into your BK tree. Go forth 
and merge. 


	-pat


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH] Dynamic PCI Device IDs
  2003-05-06 16:35   ` Matt Domsch
  2003-05-10  0:11     ` Greg KH
@ 2003-05-13 21:28     ` Patrick Mochel
  2003-05-13 21:33       ` Patrick Mochel
  1 sibling, 1 reply; 15+ messages in thread
From: Patrick Mochel @ 2003-05-13 21:28 UTC (permalink / raw)
  To: Matt Domsch; +Cc: Greg KH, alan, linux-kernel, jgarzik


On Tue, 6 May 2003, Matt Domsch wrote:

> > You can't just call driver_attach(), as the bus semaphore needs to be
> > locked before doing so.  In short, you almost need to duplicate
> > bus_add_driver(), but not quite :)
> 
> Right, and it seems to work. I made driver_attach non-static, declared
> it extern in pci.h, and call it in pci-driver.c while holding the bus
> semaphore and references to the driver and the bus.  This also let me
> delete my probe_each_pci_dev() function and let the driver core
> handle it.
> 
> Pat, can you ack the changes to bus.c and device.h please?

ACK. I'll add them to my tree.

Thanks,


	-pat


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH] Dynamic PCI Device IDs
  2003-05-06 16:35   ` Matt Domsch
@ 2003-05-10  0:11     ` Greg KH
  2003-05-13 21:28     ` Patrick Mochel
  1 sibling, 0 replies; 15+ messages in thread
From: Greg KH @ 2003-05-10  0:11 UTC (permalink / raw)
  To: Matt Domsch; +Cc: mochel, alan, linux-kernel, jgarzik

On Tue, May 06, 2003 at 11:35:17AM -0500, Matt Domsch wrote:
> > You can't just call driver_attach(), as the bus semaphore needs to be
> > locked before doing so.  In short, you almost need to duplicate
> > bus_add_driver(), but not quite :)
> 
> Right, and it seems to work. I made driver_attach non-static, declared
> it extern in pci.h, and call it in pci-driver.c while holding the bus
> semaphore and references to the driver and the bus.  This also let me
> delete my probe_each_pci_dev() function and let the driver core
> handle it.

Nice, this looks much better.  I don't have a problem with this patch
anymore.  I'll wait for Pat to ack the driver core changes to see if he
agrees with them before sending this on.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH] Dynamic PCI Device IDs
  2003-05-06  3:56 ` Greg KH
@ 2003-05-06 16:35   ` Matt Domsch
  2003-05-10  0:11     ` Greg KH
  2003-05-13 21:28     ` Patrick Mochel
  0 siblings, 2 replies; 15+ messages in thread
From: Matt Domsch @ 2003-05-06 16:35 UTC (permalink / raw)
  To: Greg KH, mochel; +Cc: alan, linux-kernel, jgarzik

> You can't just call driver_attach(), as the bus semaphore needs to be
> locked before doing so.  In short, you almost need to duplicate
> bus_add_driver(), but not quite :)

Right, and it seems to work. I made driver_attach non-static, declared
it extern in pci.h, and call it in pci-driver.c while holding the bus
semaphore and references to the driver and the bus.  This also let me
delete my probe_each_pci_dev() function and let the driver core
handle it.

Pat, can you ack the changes to bus.c and device.h please?

Now when an ID is added and a new device is found, the symlink for the
device shows up under sys/bus/pci/drivers/{driver}/.

Before:

/sys/bus/pci/drivers/e100
`-- new_id

After:

/sys/bus/pci/drivers/e100
|-- 00:04.0 -> ../../../../devices/pci0/00:04.0
`-- new_id


Patch below, and at:
http://domsch.com/linux/patches/dynids/linux-2.5/linux-2.5.69-dynids-20030506.patch.bz2
http://domsch.com/linux/patches/dynids/linux-2.5/linux-2.5.69-dynids-20030506.patch.bz2.asc

 Documentation/pci.txt      |   24 +++
 drivers/base/bus.c         |    2
 drivers/pci/pci-driver.c   |  281 ++++++++++++++++++++++++++++++++++++++-----
 include/linux/device.h     |    1
 include/linux/pci-dynids.h |   18 ++
 include/linux/pci.h        |   16 ++
 6 files changed, 312 insertions(+), 30 deletions(-)

BK:
http://mdomsch.bkbits.net/linux-2.5-dynids

Thanks,
Matt

-- 
Matt Domsch
Sr. Software Engineer
Dell Linux Solutions www.dell.com/linux
Linux on Dell mailing lists @ http://lists.us.dell.com

diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5/Documentation/pci.txt dynids/linux-2.5-dynids/Documentation/pci.txt
--- linux-2.5/Documentation/pci.txt	Mon May  5 17:19:05 2003
+++ dynids/linux-2.5-dynids/Documentation/pci.txt	Tue May  6 11:16:08 2003
@@ -45,8 +45,6 @@
 	id_table	Pointer to table of device ID's the driver is
 			interested in.  Most drivers should export this
 			table using MODULE_DEVICE_TABLE(pci,...).
-			Set to NULL to call probe() function for every
-			PCI device known to the system.
 	probe		Pointer to a probing function which gets called (during
 			execution of pci_register_driver for already existing
 			devices or later if a new device gets inserted) for all
@@ -82,6 +80,28 @@
 	class,		Device class to match. The class_mask tells which bits
 	class_mask	of the class are honored during the comparison.
 	driver_data	Data private to the driver.
+
+Most drivers don't need to use the driver_data field.  Best practice
+for use of driver_data is to use it as an index into a static list of
+equivalant device types, not to use it as a pointer.
+
+Have a table entry {PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID}
+to have probe() called for every PCI device known to the system.
+
+New PCI IDs may be added to a device driver at runtime by writing
+to the file /sys/bus/pci/drivers/{driver}/new_id.  When added, the
+driver will probe for all devices it can support.
+
+echo "vendor device subvendor subdevice class class_mask driver_data" > \
+ /sys/bus/pci/drivers/{driver}/new_id
+where all fields are passed in as hexadecimal values (no leading 0x).
+Users need pass only as many fields as necessary; vendor, device,
+subvendor, and subdevice fields default to PCI_ANY_ID (FFFFFFFF),
+class and classmask fields default to 0, and driver_data defaults to
+0UL.  Device drivers must call
+   pci_dynids_set_use_driver_data(pci_driver *, 1)
+in order for the driver_data field to get passed to the driver.
+Otherwise, only a 0 is passed in that field.
 
 When the driver exits, it just calls pci_unregister_driver() and the PCI layer
 automatically calls the remove hook for all devices handled by the driver.
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5/drivers/base/bus.c dynids/linux-2.5-dynids/drivers/base/bus.c
--- linux-2.5/drivers/base/bus.c	Mon May  5 17:19:17 2003
+++ dynids/linux-2.5-dynids/drivers/base/bus.c	Tue May  6 11:16:18 2003
@@ -316,7 +316,7 @@
  *	Note that we ignore the error from bus_match(), since it's perfectly
  *	valid for a driver not to bind to any devices.
  */
-static void driver_attach(struct device_driver * drv)
+void driver_attach(struct device_driver * drv)
 {
 	struct bus_type * bus = drv->bus;
 	struct list_head * entry;
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5/drivers/pci/pci-driver.c dynids/linux-2.5-dynids/drivers/pci/pci-driver.c
--- linux-2.5/drivers/pci/pci-driver.c	Mon May  5 17:19:31 2003
+++ dynids/linux-2.5-dynids/drivers/pci/pci-driver.c	Tue May  6 11:16:29 2003
@@ -6,6 +6,8 @@
 #include <linux/pci.h>
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/device.h>
+#include <linux/pci-dynids.h>
 #include "pci.h"
 
 /*
@@ -13,7 +15,29 @@
  */
 
 /**
- * pci_match_device - Tell if a PCI device structure has a matching PCI device id structure
+ * pci_match_one_device - Tell if a PCI device structure has a matching
+ *                        PCI device id structure
+ * @id: single PCI device id structure to match
+ * @dev: the PCI device structure to match against
+ * 
+ * Returns the matching pci_device_id structure or %NULL if there is no match.
+ */
+
+static inline const struct pci_device_id *
+pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
+{
+	if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&
+	    (id->device == PCI_ANY_ID || id->device == dev->device) &&
+	    (id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&
+	    (id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&
+	    !((id->class ^ dev->class) & id->class_mask))
+		return id;
+	return NULL;
+}
+
+/**
+ * pci_match_device - Tell if a PCI device structure has a matching
+ *                    PCI device id structure
  * @ids: array of PCI device id structures to search in
  * @dev: the PCI device structure to match against
  * 
@@ -25,17 +49,87 @@
 pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev)
 {
 	while (ids->vendor || ids->subvendor || ids->class_mask) {
-		if ((ids->vendor == PCI_ANY_ID || ids->vendor == dev->vendor) &&
-		    (ids->device == PCI_ANY_ID || ids->device == dev->device) &&
-		    (ids->subvendor == PCI_ANY_ID || ids->subvendor == dev->subsystem_vendor) &&
-		    (ids->subdevice == PCI_ANY_ID || ids->subdevice == dev->subsystem_device) &&
-		    !((ids->class ^ dev->class) & ids->class_mask))
+		if (pci_match_one_device(ids, dev))
 			return ids;
 		ids++;
 	}
 	return NULL;
 }
 
+/**
+ * pci_device_probe_static()
+ * 
+ * returns 0 and sets pci_dev->driver when drv claims pci_dev, else error.
+ */
+static int
+pci_device_probe_static(struct pci_driver *drv, struct pci_dev *pci_dev)
+{		   
+	int error = -ENODEV;
+	const struct pci_device_id *id;
+
+	if (!drv->id_table)
+		return error;
+	id = pci_match_device(drv->id_table, pci_dev);
+	if (id)
+		error = drv->probe(pci_dev, id);
+	if (error >= 0) {
+		pci_dev->driver = drv;
+		return 0;
+	}
+	return error;
+}
+
+/**
+ * pci_device_probe_dynamic()
+ * 
+ * Walk the dynamic ID list looking for a match.
+ * returns 0 and sets pci_dev->driver when drv claims pci_dev, else error.
+ */
+static int
+pci_device_probe_dynamic(struct pci_driver *drv, struct pci_dev *pci_dev)
+{		   
+	int error = -ENODEV;
+	struct list_head *pos;
+	struct dynid *dynid;
+
+	spin_lock(&drv->dynids.lock);
+	list_for_each(pos, &drv->dynids.list) {
+		dynid = list_entry(pos, struct dynid, node);
+		if (pci_match_one_device(&dynid->id, pci_dev)) {
+			spin_unlock(&drv->dynids.lock);
+			error = drv->probe(pci_dev, &dynid->id);
+			if (error >= 0) {
+				pci_dev->driver = drv;
+				return 0;
+			}
+			return error;
+		}
+	}
+	spin_unlock(&drv->dynids.lock);
+	return error;
+}
+
+/**
+ * __pci_device_probe()
+ * 
+ * returns 0  on success, else error.
+ * side-effect: pci_dev->driver is set to drv when drv claims pci_dev.
+ */
+static int
+__pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev)
+{		   
+	int error = 0;
+
+	if (!pci_dev->driver && drv->probe) {
+		error = pci_device_probe_static(drv, pci_dev);
+		if (error >= 0)
+			return error;
+
+		error = pci_device_probe_dynamic(drv, pci_dev);
+	}
+	return error;
+}
+
 static int pci_device_probe(struct device * dev)
 {
 	int error = 0;
@@ -44,17 +138,9 @@
 
 	drv = to_pci_driver(dev->driver);
 	pci_dev = to_pci_dev(dev);
-
-	if (!pci_dev->driver && drv->probe) {
-		const struct pci_device_id *id;
-
-		id = pci_match_device(drv->id_table, pci_dev);
-		if (id)
-			error = drv->probe(pci_dev, id);
-		if (error >= 0) {
-			pci_dev->driver = drv;
-			error = 0;
-		}
+	if (get_device(dev)) {
+		error = __pci_device_probe(drv, pci_dev);
+		put_device(dev);
 	}
 	return error;
 }
@@ -101,6 +187,134 @@
 	return 0;
 }
 
+static inline void
+dynid_init(struct dynid *dynid)
+{
+	memset(dynid, 0, sizeof(*dynid));
+	INIT_LIST_HEAD(&dynid->node);
+}
+
+/**
+ * store_new_id
+ * @ pdrv
+ * @ buf
+ * @ count
+ *
+ * Adds a new dynamic pci device ID to this driver,
+ * and causes the driver to probe for all devices again.
+ */
+static inline ssize_t
+store_new_id(struct device_driver * driver, const char * buf, size_t count)
+{
+	struct dynid *dynid;
+	struct bus_type * bus;
+	struct pci_driver *pdrv = to_pci_driver(driver);
+	__u32 vendor=PCI_ANY_ID, device=PCI_ANY_ID, subvendor=PCI_ANY_ID,
+		subdevice=PCI_ANY_ID, class=0, class_mask=0;
+	unsigned long driver_data=0;
+	int fields=0;
+
+	fields = sscanf(buf, "%x %x %x %x %x %x %lux",
+			&vendor, &device, &subvendor, &subdevice,
+			&class, &class_mask, &driver_data);
+	if (fields < 0)
+		return -EINVAL;
+
+	dynid = kmalloc(sizeof(*dynid), GFP_KERNEL);
+	if (!dynid)
+		return -ENOMEM;
+	dynid_init(dynid);
+
+	dynid->id.vendor = vendor;
+	dynid->id.device = device;
+	dynid->id.subvendor = subvendor;
+	dynid->id.subdevice = subdevice;
+	dynid->id.class = class;
+	dynid->id.class_mask = class_mask;
+	dynid->id.driver_data = pdrv->dynids.use_driver_data ?
+		driver_data : 0UL;
+
+	spin_lock(&pdrv->dynids.lock);
+	list_add(&pdrv->dynids.list, &dynid->node);
+	spin_unlock(&pdrv->dynids.lock);
+
+	bus = get_bus(pdrv->driver.bus);
+	if (bus) {
+		if (get_driver(&pdrv->driver)) {
+			down_write(&bus->subsys.rwsem);
+			driver_attach(&pdrv->driver);
+			up_write(&bus->subsys.rwsem);
+			put_driver(&pdrv->driver);
+		}
+		put_bus(bus);
+	}
+	
+	return count;
+}
+
+static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
+
+#define kobj_to_pci_driver(obj) container_of(obj, struct device_driver, kobj)
+#define attr_to_driver_attribute(obj) container_of(obj, struct driver_attribute, attr)
+
+static ssize_t
+pci_driver_attr_show(struct kobject * kobj, struct attribute *attr, char *buf)
+{
+	struct device_driver *driver = kobj_to_pci_driver(kobj);
+	struct driver_attribute *dattr = attr_to_driver_attribute(attr);
+	ssize_t ret = 0;
+
+	if (get_driver(driver)) {
+		if (dattr->show)
+			ret = dattr->show(driver, buf);
+		put_driver(driver);
+	}
+	return ret;
+}
+
+static ssize_t
+pci_driver_attr_store(struct kobject * kobj, struct attribute *attr,
+		      const char *buf, size_t count)
+{
+	struct device_driver *driver = kobj_to_pci_driver(kobj);
+	struct driver_attribute *dattr = attr_to_driver_attribute(attr);
+	ssize_t ret = 0;
+
+	if (get_driver(driver)) {
+		if (dattr->store)
+			ret = dattr->store(driver, buf, count);
+		put_driver(driver);
+	}
+	return ret;
+}
+
+static struct sysfs_ops pci_driver_sysfs_ops = {
+	.show = pci_driver_attr_show,
+	.store = pci_driver_attr_store,
+};
+static struct kobj_type pci_driver_kobj_type = {
+	.sysfs_ops = &pci_driver_sysfs_ops,
+};
+
+static int
+pci_populate_driver_dir(struct pci_driver * drv)
+{
+	int error = 0;
+
+	if (drv->probe != NULL)
+		error = sysfs_create_file(&drv->driver.kobj,
+					  &driver_attr_new_id.attr);
+	return error;
+}
+
+static inline void
+pci_init_dynids(struct pci_dynids *dynids)
+{
+	memset(dynids, 0, sizeof(*dynids));
+	spin_lock_init(&dynids->lock);
+	INIT_LIST_HEAD(&dynids->list);
+}
+
 /**
  * pci_register_driver - register a new pci driver
  * @drv: the driver structure to register
@@ -122,9 +336,16 @@
 	drv->driver.resume = pci_device_resume;
 	drv->driver.suspend = pci_device_suspend;
 	drv->driver.remove = pci_device_remove;
+	drv->driver.kobj.ktype = &pci_driver_kobj_type;
+	pci_init_dynids(&drv->dynids);
 
 	/* register with core */
 	count = driver_register(&drv->driver);
+
+	if (count >= 0) {
+		pci_populate_driver_dir(drv);
+	}
+
 	return count ? count : 1;
 }
 
@@ -180,22 +401,30 @@
  */
 static int pci_bus_match(struct device * dev, struct device_driver * drv) 
 {
-	struct pci_dev * pci_dev = to_pci_dev(dev);
+	const struct pci_dev * pci_dev = to_pci_dev(dev);
 	struct pci_driver * pci_drv = to_pci_driver(drv);
 	const struct pci_device_id * ids = pci_drv->id_table;
+	const struct pci_device_id *found_id;
+	struct list_head *pos;
+	struct dynid *dynid;
 
-	if (!ids) 
+	if (!ids)
 		return 0;
 
-	while (ids->vendor || ids->subvendor || ids->class_mask) {
-		if ((ids->vendor == PCI_ANY_ID || ids->vendor == pci_dev->vendor) &&
-		    (ids->device == PCI_ANY_ID || ids->device == pci_dev->device) &&
-		    (ids->subvendor == PCI_ANY_ID || ids->subvendor == pci_dev->subsystem_vendor) &&
-		    (ids->subdevice == PCI_ANY_ID || ids->subdevice == pci_dev->subsystem_device) &&
-		    !((ids->class ^ pci_dev->class) & ids->class_mask))
+	found_id = pci_match_device(ids, pci_dev);
+	if (found_id)
+		return 1;
+
+	spin_lock(&pci_drv->dynids.lock);
+	list_for_each(pos, &pci_drv->dynids.list) {
+		dynid = list_entry(pos, struct dynid, node);
+		if (pci_match_one_device(&dynid->id, pci_dev)) {
+			spin_unlock(&pci_drv->dynids.lock);
 			return 1;
-		ids++;
+		}
 	}
+	spin_unlock(&pci_drv->dynids.lock);
+
 	return 0;
 }
 
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5/include/linux/device.h dynids/linux-2.5-dynids/include/linux/device.h
--- linux-2.5/include/linux/device.h	Mon May  5 17:19:50 2003
+++ dynids/linux-2.5-dynids/include/linux/device.h	Tue May  6 11:16:45 2003
@@ -318,6 +318,7 @@
  */
 extern void device_bind_driver(struct device * dev);
 extern void device_release_driver(struct device * dev);
+extern void driver_attach(struct device_driver * drv);
 
 
 /* driverfs interface for exporting device attributes */
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5/include/linux/pci-dynids.h dynids/linux-2.5-dynids/include/linux/pci-dynids.h
--- linux-2.5/include/linux/pci-dynids.h	Wed Dec 31 18:00:00 1969
+++ dynids/linux-2.5-dynids/include/linux/pci-dynids.h	Tue May  6 11:16:45 2003
@@ -0,0 +1,18 @@
+/*
+ *	PCI defines and function prototypes
+ *	Copyright 2003 Dell Computer Corporation
+ *        by Matt Domsch <Matt_Domsch@dell.com>
+ */
+
+#ifndef LINUX_PCI_DYNIDS_H
+#define LINUX_PCI_DYNIDS_H
+
+#include <linux/list.h>
+#include <linux/mod_devicetable.h>
+
+struct dynid {
+	struct list_head        node;
+	struct pci_device_id    id;
+};
+
+#endif
diff -Nur --exclude=RCS --exclude=CVS --exclude=SCCS --exclude=BitKeeper --exclude=ChangeSet linux-2.5/include/linux/pci.h dynids/linux-2.5-dynids/include/linux/pci.h
--- linux-2.5/include/linux/pci.h	Mon May  5 17:19:51 2003
+++ dynids/linux-2.5-dynids/include/linux/pci.h	Tue May  6 11:16:45 2003
@@ -490,10 +490,16 @@
 	unsigned long end;
 };
 
+struct pci_dynids {
+	spinlock_t lock;            /* protects list, index */
+	struct list_head list;      /* for IDs added at runtime */
+	unsigned int use_driver_data:1; /* pci_driver->driver_data is used */
+};
+
 struct pci_driver {
 	struct list_head node;
 	char *name;
-	const struct pci_device_id *id_table;	/* NULL if wants all devices */
+	const struct pci_device_id *id_table;	/* must be non-NULL for probe to be called */
 	int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id);	/* New device inserted */
 	void (*remove) (struct pci_dev *dev);	/* Device removed (NULL if not a hot-plug capable driver) */
 	int  (*save_state) (struct pci_dev *dev, u32 state);    /* Save Device Context */
@@ -502,6 +508,7 @@
 	int  (*enable_wake) (struct pci_dev *dev, u32 state, int enable);   /* Enable wake event */
 
 	struct device_driver	driver;
+	struct pci_dynids dynids;
 };
 
 #define	to_pci_driver(drv) container_of(drv,struct pci_driver, driver)
@@ -702,6 +709,12 @@
 			 struct pci_bus_wrapped *wrapped_parent);
 extern int pci_remove_device_safe(struct pci_dev *dev);
 
+static inline void
+pci_dynids_set_use_driver_data(struct pci_driver *pdrv, int val)
+{
+	pdrv->dynids.use_driver_data = val;
+}
+
 #endif /* CONFIG_PCI */
 
 /* Include architecture-dependent settings and functions */
@@ -750,6 +763,7 @@
 static inline int scsi_to_pci_dma_dir(unsigned char scsi_dir) { return scsi_dir; }
 static inline int pci_find_capability (struct pci_dev *dev, int cap) {return 0; }
 static inline const struct pci_device_id *pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev) { return NULL; }
+static inline void pci_dynids_set_use_driver_data(struct pci_driver *pdrv, int val) { }
 
 /* Power management related routines */
 static inline int pci_save_state(struct pci_dev *dev, u32 *buffer) { return 0; }


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH] Dynamic PCI Device IDs
  2003-05-06  2:04 Matt_Domsch
@ 2003-05-06  3:56 ` Greg KH
  2003-05-06 16:35   ` Matt Domsch
  0 siblings, 1 reply; 15+ messages in thread
From: Greg KH @ 2003-05-06  3:56 UTC (permalink / raw)
  To: Matt_Domsch; +Cc: mochel, alan, linux-kernel, jgarzik

On Mon, May 05, 2003 at 09:04:35PM -0500, Matt_Domsch@Dell.com wrote:
> > Unfortunatly, looking at the driver core real quickly, I don't see a
> > simple way to kick the probe cycle off again for all pci devices, but
> > I'm probably just missing something somewhere...
> 
> I think drivers/base/bus.c: driver_attach() is what we want, which will
> walk the list of the bus's devices and run bus_match() which is
> pci_bus_match() which will scan for us.  Just need to un-static
> driver_attach() I expect.  Pat, does this sound right?

You can't just call driver_attach(), as the bus semaphore needs to be
locked before doing so.  In short, you almost need to duplicate
bus_add_driver(), but not quite :)

Good luck,

greg k-h

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH] Dynamic PCI Device IDs
@ 2003-05-06  2:04 Matt_Domsch
  2003-05-06  3:56 ` Greg KH
  0 siblings, 1 reply; 15+ messages in thread
From: Matt_Domsch @ 2003-05-06  2:04 UTC (permalink / raw)
  To: greg, mochel; +Cc: alan, linux-kernel, jgarzik

> I like this patch a _lot_ better, nice job.

Thanks.

>   Only one comment:
> > +        if (error < 0)
> > +                return error;
> > +        return count;
> > +
> > +
> > +     return count;
> > +}
> 
> Oops, lost the tabs at the end of the function :)

Duh.  Good eyes.  Fixed.

> This function will not link up a device to a driver properly within
> the driver core, only with the pci code.  So if you do this, the
> driver core still thinks you have a device that is unbound, right? 
> Also, the symlinks don't get created from the bus to the device I
> think, correct?

I think you're right.

> Unfortunatly, looking at the driver core real quickly, I don't see a
> simple way to kick the probe cycle off again for all pci devices, but
> I'm probably just missing something somewhere...

I think drivers/base/bus.c: driver_attach() is what we want, which will
walk the list of the bus's devices and run bus_match() which is
pci_bus_match() which will scan for us.  Just need to un-static
driver_attach() I expect.  Pat, does this sound right?

If that works, probe_each_pci_dev() can go away.  I'll play with it some
more.

Thanks,
Matt
-- 
Matt Domsch
Sr. Software Engineer, Lead Engineer, Architect
Dell Linux Solutions www.dell.com/linux
Linux on Dell mailing lists @ http://lists.us.dell.com



^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH] Dynamic PCI Device IDs
  2003-04-30 21:45 Matt Domsch
  2003-04-30 21:53 ` Jeff Garzik
@ 2003-04-30 22:24 ` Greg KH
  1 sibling, 0 replies; 15+ messages in thread
From: Greg KH @ 2003-04-30 22:24 UTC (permalink / raw)
  To: Matt Domsch; +Cc: alan, linux-kernel, jgarzik

First off, nice idea, but I think the implementation needs a bit of
work.

On Wed, Apr 30, 2003 at 04:45:14PM -0500, Matt Domsch wrote:
>   
>   One may read the new_id file, which by default returns:
>   $ cat new_id
>   echo vendor device subvendor subdevice class classmask
>   where each field is a 32-bit value in ABCD (hex) format (no leading 0x).
>   Pass only as many fields as you need to override the defaults below.
>   Default vendor, device, subvendor, and subdevice fields
>   are set to FFFFFFFF (PCI_ANY_ID).
>   Default class and classmask fields are set to 0.

Ick, don't put help files within the kernel image.  Didn't you take them
all out for the edd patch a while ago?  :)

Just make it a write only file.

>   One can then cause the driver to probe for devices again.
>   echo 1 > probe_it

Why wouldn't the writing to the new_id file cause the probe to happen
immediatly?  Why wait?  So I think we can get rid of that file.

Also, do we really need to keep a list of id's visible to userspace (the
"0" file above?  We currently don't do that for the "static ids" (yeah I
know they are easily extracted from the module image...)

>   Individual device drivers may override the behavior of the new_id
>   file, for instance, if they need to also pass driver-specific
>   information.  Likewise, reading the individual dynamic ID files
>   can be overridden by the driver.

Why would a driver want to override these behaviors?

>   This also adds an existance test field to struct driver_attribute,
>   necessary because we only want the probe_it file to appear iff
>   struct pci_driver->probe is non-NULL.

Is that the exists() callback?  Is it really needed?  Can't the pci core
do this without needing to push that logic into the driver core?  After
all, it knows if the pci_driver->probe() call is non-NULL, the driver
core doesn't.

Also, we really need a generic way to easily create subdirectories
within the driver model, to keep you from having to dive down into
kobjects and the mess, like you had to do here.  Pat's said he will work
on this next, once he emerges from his OLS paper writing hole, and
there's even a bug assigned to him for this:
	http://bugme.osdl.org/show_bug.cgi?id=650
Once that is in, this patch should clean up a whole lot.  I'd recommend
waiting for that (or if you want to tackle it, please do) before
applying something like your patch.

Sound ok?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [RFC][PATCH] Dynamic PCI Device IDs
  2003-04-30 21:45 Matt Domsch
@ 2003-04-30 21:53 ` Jeff Garzik
  2003-04-30 22:24 ` Greg KH
  1 sibling, 0 replies; 15+ messages in thread
From: Jeff Garzik @ 2003-04-30 21:53 UTC (permalink / raw)
  To: Matt Domsch; +Cc: greg, alan, linux-kernel

Well, gee.  It's so pretty it's hard to say no.  :)

I've wanted this capability, dynamically adding PCI ids to drivers, for
a while.  In addition to this feature, it also makes the existing code
a bit better.  And it certainly seems sysfs-friendly, though getting
an expert in that area to look over the sysfs parts would be nice, too.

	Jeff




^ permalink raw reply	[flat|nested] 15+ messages in thread

* [RFC][PATCH] Dynamic PCI Device IDs
@ 2003-04-30 21:45 Matt Domsch
  2003-04-30 21:53 ` Jeff Garzik
  2003-04-30 22:24 ` Greg KH
  0 siblings, 2 replies; 15+ messages in thread
From: Matt Domsch @ 2003-04-30 21:45 UTC (permalink / raw)
  To: greg, alan; +Cc: linux-kernel, jgarzik

Greg and Alan, for your consideration.

Patch below and at: 
http://domsch.com/linux/patches/dynids/linux-2.5/linux-2.5.68-dynids-20030416.patch.bz2

BK:  http://mdomsch.bkbits.net/linux-2.5-dynids

Feedback welcome.

Thanks,
Matt

-- 
Matt Domsch
Sr. Software Engineer
Dell Linux Solutions www.dell.com/linux
Linux on Dell mailing lists @ http://lists.us.dell.com

You can import this changeset into BK by piping this whole message to
'| bk receive [path to repository]' or apply the patch as usual.

===================================================================


ChangeSet@1.1067, 2003-04-16 16:03:20-05:00, Matt_Domsch@dell.com
  Device Driver Dynamic PCI Device IDs
  
  Provides a mechanism to pass new PCI device IDs to device drivers
  at runtime, rather than relying only on a static compiled-in list
  of supported IDs.
  
  For each driver which has a pci_driver->probe routine, two things
  are added: a probe_it file, and a dynamic_id directory.  In the
  dynamic_id directory is a new file, new_id.
  
  /sys/bus/pci/drivers/e100
  |-- dynamic_id
  |   `-- new_id
  `-- probe_it
  
  One may read the new_id file, which by default returns:
  $ cat new_id
  echo vendor device subvendor subdevice class classmask
  where each field is a 32-bit value in ABCD (hex) format (no leading 0x).
  Pass only as many fields as you need to override the defaults below.
  Default vendor, device, subvendor, and subdevice fields
  are set to FFFFFFFF (PCI_ANY_ID).
  Default class and classmask fields are set to 0.
  
  One may write new PCI device IDs into the new_id file:
  echo "8086 1229" > new_id
  
  which will cause a new device ID (sysfs name 0) to be added to the driver.
  
  /sys/bus/pci/drivers/e100
  |-- dynamic_id
  |   |-- 0
  |   `-- new_id
  `-- probe_it
  
  $ cat 0
  00008086 00001229 ffffffff ffffffff 00000000 00000000
  
  One can then cause the driver to probe for devices again.
  echo 1 > probe_it
  
  Individual device drivers may override the behavior of the new_id
  file, for instance, if they need to also pass driver-specific
  information.  Likewise, reading the individual dynamic ID files
  can be overridden by the driver.
  
  This also adds an existance test field to struct driver_attribute,
  necessary because we only want the probe_it file to appear iff
  struct pci_driver->probe is non-NULL.
  
  The device probing routines in pci-driver.c are enhanced to scan
  first the static list of IDs, then the dynamic list (if any).
  


 drivers/pci/Makefile           |    2 
 drivers/pci/pci-driver.c       |  267 +++++++++++++++++++++++++++++++++++++----
 drivers/pci/pci-sysfs-dynids.c |  251 ++++++++++++++++++++++++++++++++++++++
 drivers/pci/pci.h              |    1 
 include/linux/device.h         |   11 +
 include/linux/pci-dynids.h     |   40 ++++++
 include/linux/pci.h            |    2 
 7 files changed, 547 insertions(+), 27 deletions(-)


diff -Nru a/drivers/pci/Makefile b/drivers/pci/Makefile
--- a/drivers/pci/Makefile	Wed Apr 16 16:07:05 2003
+++ b/drivers/pci/Makefile	Wed Apr 16 16:07:05 2003
@@ -4,7 +4,7 @@
 
 obj-y		+= access.o bus.o probe.o pci.o pool.o quirks.o \
 			names.o pci-driver.o search.o hotplug.o \
-			pci-sysfs.o
+			pci-sysfs.o pci-sysfs-dynids.o
 obj-$(CONFIG_PM)  += power.o
 obj-$(CONFIG_PROC_FS) += proc.o
 
diff -Nru a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
--- a/drivers/pci/pci-driver.c	Wed Apr 16 16:07:05 2003
+++ b/drivers/pci/pci-driver.c	Wed Apr 16 16:07:05 2003
@@ -13,6 +13,26 @@
  */
 
 /**
+ * pci_match_one_device - Tell if a PCI device structure has a matching PCI device id structure
+ * @id: single PCI device id structure to match
+ * @dev: the PCI device structure to match against
+ * 
+ * Returns the matching pci_device_id structure or %NULL if there is no match.
+ */
+
+static inline const struct pci_device_id *
+pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
+{
+	if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&
+	    (id->device == PCI_ANY_ID || id->device == dev->device) &&
+	    (id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&
+	    (id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&
+	    !((id->class ^ dev->class) & id->class_mask))
+		return id;
+	return NULL;
+}
+
+/**
  * pci_match_device - Tell if a PCI device structure has a matching PCI device id structure
  * @ids: array of PCI device id structures to search in
  * @dev: the PCI device structure to match against
@@ -25,17 +45,88 @@
 pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev)
 {
 	while (ids->vendor || ids->subvendor || ids->class_mask) {
-		if ((ids->vendor == PCI_ANY_ID || ids->vendor == dev->vendor) &&
-		    (ids->device == PCI_ANY_ID || ids->device == dev->device) &&
-		    (ids->subvendor == PCI_ANY_ID || ids->subvendor == dev->subsystem_vendor) &&
-		    (ids->subdevice == PCI_ANY_ID || ids->subdevice == dev->subsystem_device) &&
-		    !((ids->class ^ dev->class) & ids->class_mask))
-			return ids;
+		if (pci_match_one_device(ids, dev))
+		    return ids;
 		ids++;
 	}
 	return NULL;
 }
 
+/**
+ * pci_device_probe_static()
+ * 
+ * returns 0 and sets pci_dev->driver when drv claims pci_dev, else error.
+ */
+static int
+pci_device_probe_static(struct pci_driver *drv,
+			  struct pci_dev *pci_dev)
+{		   
+	int error = -ENODEV;
+	const struct pci_device_id *id;
+	
+	id = pci_match_device(drv->id_table, pci_dev);
+	if (id)
+		error = drv->probe(pci_dev, id);
+	if (error >= 0) {
+		pci_dev->driver = drv;
+		return 0;
+	}
+	return error;
+}
+
+/**
+ * pci_device_probe_dynamic()
+ * 
+ * Walk the dynamic ID list looking for a match.
+ * returns 0 and sets pci_dev->driver when drv claims pci_dev, else error.
+ */
+static int
+pci_device_probe_dynamic(struct pci_driver *drv,
+			   struct pci_dev *pci_dev)
+{		   
+	int error = -ENODEV;
+	struct list_head *pos;
+	struct dynid_attribute *dattr;
+	
+	spin_lock(&drv->dynids.lock);
+	list_for_each(pos, &drv->dynids.list) {
+		dattr = list_entry(pos, struct dynid_attribute, node);
+		if (pci_match_one_device(dattr->id, pci_dev)) {
+			spin_unlock(&drv->dynids.lock);
+			error = drv->probe(pci_dev, dattr->id);
+			if (error >= 0) {
+				pci_dev->driver = drv;
+				return 0;
+			}
+			return error;
+		}
+	}
+	spin_unlock(&drv->dynids.lock);
+	return error;
+}
+
+/**
+ * __pci_device_probe()
+ * 
+ * returns 0  on success, else error.
+ * side-effect: pci_dev->driver is set to drv when drv claims pci_dev.
+ */
+static int
+__pci_device_probe(struct pci_driver *drv,
+		   struct pci_dev *pci_dev)
+{		   
+	int error = 0;
+
+	if (!pci_dev->driver && drv->probe) {
+		error = pci_device_probe_static(drv, pci_dev);
+		if (error >= 0)
+			return error;
+
+		error = pci_device_probe_dynamic(drv, pci_dev);
+	}
+	return error;
+}
+
 static int pci_device_probe(struct device * dev)
 {
 	int error = 0;
@@ -44,17 +135,9 @@
 
 	drv = to_pci_driver(dev->driver);
 	pci_dev = to_pci_dev(dev);
-
-	if (!pci_dev->driver && drv->probe) {
-		const struct pci_device_id *id;
-
-		id = pci_match_device(drv->id_table, pci_dev);
-		if (id)
-			error = drv->probe(pci_dev, id);
-		if (error >= 0) {
-			pci_dev->driver = drv;
-			error = 0;
-		}
+	if (get_device(dev)) {
+		error = __pci_device_probe(drv, pci_dev);
+		put_device(dev);
 	}
 	return error;
 }
@@ -101,6 +184,122 @@
 	return 0;
 }
 
+/*
+ * Attribute to force driver probe for devices
+ * If __pci_device_probe() returns 0, it matched at least one previously
+ * unclaimed device.  If it returns -ENODEV, it didn't match.  Both are
+ * alright in this case, just keep searching for new devices.
+ */
+
+static int
+probe_each_pci_dev(struct pci_driver *drv)
+{
+	struct pci_dev *pci_dev=NULL;
+	int error = 0;
+	pci_for_each_dev(pci_dev) {
+		if (get_device(&pci_dev->dev)) {
+			error = __pci_device_probe(drv, pci_dev);
+			put_device(&pci_dev->dev);
+			if (error && error != -ENODEV)
+				return error;
+		}
+	}
+	return error;
+}
+
+static ssize_t
+store_probe_it(struct device_driver *driver, const char *buf, size_t count)
+{
+	int error = 0;
+	struct pci_driver *drv = to_pci_driver(driver);
+	int writeone = 0;
+	if (!((sscanf(buf, "%d", &writeone) == 1) && writeone == 1))
+		return -EINVAL;
+	
+	if (get_driver(driver)) {
+		error = probe_each_pci_dev(drv);
+		put_driver(driver);
+	}
+	if (error < 0)
+		return error;
+	return count;
+}
+
+static int
+probe_it_exists(struct device_driver *driver)
+{
+	struct pci_driver *pdrv = to_pci_driver(driver);
+	return pdrv->probe != NULL;
+}
+
+static DRIVER_ATTR_EXISTS(probe_it, S_IWUSR, NULL, store_probe_it, probe_it_exists);
+
+#define kobj_to_pci_driver(obj) container_of(obj, struct device_driver, kobj)
+#define attr_to_driver_attribute(obj) container_of(obj, struct driver_attribute, attr)
+
+static ssize_t
+pci_driver_attr_show(struct kobject * kobj, struct attribute *attr, char *buf)
+{
+	struct device_driver *driver = kobj_to_pci_driver(kobj);
+	struct driver_attribute *dattr = attr_to_driver_attribute(attr);
+	ssize_t ret = 0;
+
+	if (get_driver(driver)) {
+		if (dattr->show)
+			ret = dattr->show(driver, buf);
+		put_driver(driver);
+	}
+	return ret;
+}
+
+static ssize_t
+pci_driver_attr_store(struct kobject * kobj, struct attribute *attr,
+		      const char *buf, size_t count)
+{
+	struct device_driver *driver = kobj_to_pci_driver(kobj);
+	struct driver_attribute *dattr = attr_to_driver_attribute(attr);
+	ssize_t ret = 0;
+
+	if (get_driver(driver)) {
+		if (dattr->store)
+			ret = dattr->store(driver, buf, count);
+		put_driver(driver);
+	}
+	return ret;
+}
+
+static struct sysfs_ops pci_driver_sysfs_ops = {
+	.show = pci_driver_attr_show,
+	.store = pci_driver_attr_store,
+};
+static struct kobj_type pci_driver_kobj_type = {
+	.sysfs_ops     = &pci_driver_sysfs_ops,
+};
+static struct driver_attribute * driver_attrs[] = {
+	&driver_attr_probe_it,
+	NULL,
+};
+
+static void pci_populate_driver_dir(struct pci_driver * drv)
+{
+	struct driver_attribute * attr;
+	int error = 0, i;
+
+	for (i = 0; (attr = driver_attrs[i]) && !error; i++) {
+		if (!attr->exists || 
+		    (attr->exists && attr->exists(&drv->driver)))
+			error = sysfs_create_file(&drv->driver.kobj,&attr->attr);
+	}
+}
+
+static inline void
+pci_init_dynids(struct pci_dynamic_id_kobj *dynids)
+{
+	memset(dynids, 0, sizeof(*dynids));
+	spin_lock_init(&dynids->lock);
+	INIT_LIST_HEAD(&dynids->list);
+}
+
 /**
  * pci_register_driver - register a new pci driver
  * @drv: the driver structure to register
@@ -122,9 +321,17 @@
 	drv->driver.resume = pci_device_resume;
 	drv->driver.suspend = pci_device_suspend;
 	drv->driver.remove = pci_device_remove;
+	drv->driver.kobj.ktype = &pci_driver_kobj_type;
+	pci_init_dynids(&drv->dynids);
 
 	/* register with core */
 	count = driver_register(&drv->driver);
+
+	if (count >= 0) {
+		pci_populate_driver_dir(drv);
+		pci_register_dynids(drv);
+	}
+		
 	return count ? count : 1;
 }
 
@@ -180,22 +387,30 @@
  */
 static int pci_bus_match(struct device * dev, struct device_driver * drv) 
 {
-	struct pci_dev * pci_dev = to_pci_dev(dev);
+	const struct pci_dev * pci_dev = to_pci_dev(dev);
 	struct pci_driver * pci_drv = to_pci_driver(drv);
 	const struct pci_device_id * ids = pci_drv->id_table;
+	const struct pci_device_id *found_id;
+	struct list_head *pos;
+	struct dynid_attribute *dattr;
 
-	if (!ids) 
+	if (!ids)
 		return 0;
 
-	while (ids->vendor || ids->subvendor || ids->class_mask) {
-		if ((ids->vendor == PCI_ANY_ID || ids->vendor == pci_dev->vendor) &&
-		    (ids->device == PCI_ANY_ID || ids->device == pci_dev->device) &&
-		    (ids->subvendor == PCI_ANY_ID || ids->subvendor == pci_dev->subsystem_vendor) &&
-		    (ids->subdevice == PCI_ANY_ID || ids->subdevice == pci_dev->subsystem_device) &&
-		    !((ids->class ^ pci_dev->class) & ids->class_mask))
+	found_id = pci_match_device(ids, pci_dev);
+	if (found_id) 
+		return 1;
+
+	spin_lock(&pci_drv->dynids.lock);
+	list_for_each(pos, &pci_drv->dynids.list) {
+		dattr = list_entry(pos, struct dynid_attribute, node);
+		if (pci_match_one_device(dattr->id, pci_dev)) {
+			spin_unlock(&pci_drv->dynids.lock);
 			return 1;
-		ids++;
+		}
 	}
+	spin_unlock(&pci_drv->dynids.lock);
+
 	return 0;
 }
 
diff -Nru a/drivers/pci/pci-sysfs-dynids.c b/drivers/pci/pci-sysfs-dynids.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/pci/pci-sysfs-dynids.c	Wed Apr 16 16:07:05 2003
@@ -0,0 +1,251 @@
+/*
+ * linux/drivers/pci/pci-sysfs-dynids.c
+ *  Copyright (C) 2003 Dell Computer Corporation
+ *  by Matt Domsch <Matt_Domsch@dell.com>
+ *
+ * sysfs interface for exporting dynamic device IDs
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/stat.h>
+#include <linux/sysfs.h>
+#include <linux/pci-dynids.h>
+#include "pci.h"
+
+/**
+ *	dynid_create_file - create sysfs file for a dynamic ID
+ *	@pdrv:	pci_driver
+ *	@dattr:	the attribute to create
+ */
+static int dynid_create_file(struct pci_driver * pdrv, struct dynid_attribute * dattr)
+{
+	int error;
+
+	if (get_driver(&pdrv->driver)) {
+		error = sysfs_create_file(&pdrv->dynids.kobj,&dattr->attr);
+		put_driver(&pdrv->driver);
+	} else
+		error = -EINVAL;
+	return error;
+}
+
+/**
+ *	dynid_remove_file - remove sysfs file for a dynamic ID
+ *	@drv:	driver.
+ *	@id:	the id to to remove
+ */
+static void dynid_remove_file(struct pci_driver * pdrv, struct dynid_attribute * dattr)
+{
+	if (get_driver(&pdrv->driver)) {
+		sysfs_remove_file(&pdrv->dynids.kobj,&dattr->attr);
+		put_driver(&pdrv->driver);
+	}
+}
+
+#define kobj_to_dynids(obj) container_of(obj,struct pci_dynamic_id_kobj,kobj)
+#define dynids_to_pci_driver(obj) container_of(obj,struct pci_driver,dynids)
+#define attr_to_dattr(_attr) container_of(_attr, struct dynid_attribute, attr)
+
+static inline ssize_t
+default_show_id(const struct pci_device_id * id, char * buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%08x %08x %08x %08x %08x %08x\n",
+			id->vendor,
+			id->device,
+			id->subvendor,
+			id->subdevice,
+			id->class,
+			id->class_mask);
+}
+
+static ssize_t
+dynid_show_id(struct pci_driver * pdrv, struct dynid_attribute *dattr, char *buf)
+{
+	return pdrv->dynids.show_id ?
+		pdrv->dynids.show_id(dattr->id, dattr, buf) :
+		default_show_id(dattr->id, buf);
+}	
+
+static ssize_t
+default_show_new_id(char * buf)
+{
+	char *p = buf;	
+	p += sprintf(p,
+		     "echo vendor device subvendor subdevice class classmask\n");
+	p += sprintf(p,
+		     "where each field is a 32-bit value in ABCD (hex) format (no leading 0x).\n");
+	p += sprintf(p,
+		     "Pass only as many fields as you need to override the defaults below.\n");
+	p += sprintf(p,
+		     "Default vendor, device, subvendor, and subdevice fields\n");
+	p += sprintf(p, "are set to FFFFFFFF (PCI_ANY_ID).\n");
+	p += sprintf(p,
+		     "Default class and classmask fields are set to 0.\n");
+	return p - buf;
+}
+
+static inline void
+__dattr_init(struct dynid_attribute *dattr)
+{
+	memset(dattr, 0, sizeof(*dattr));
+	INIT_LIST_HEAD(&dattr->node);
+	dattr->attr.mode = S_IRUGO;
+	dattr->attr.name = dattr->name;
+}
+
+static inline ssize_t
+default_store_new_id(struct pci_driver * pdrv, const char * buf, size_t count)
+{
+	struct dynid_attribute *dattr;
+	struct pci_device_id *id;
+	__u32 vendor=PCI_ANY_ID, device=PCI_ANY_ID, subvendor=PCI_ANY_ID,
+		subdevice=PCI_ANY_ID, class=0, class_mask=0;
+	int fields=0, error=0;
+
+	fields = sscanf(buf, "%x %x %x %x %x %x",
+			&vendor, &device, &subvendor, &subdevice,
+			&class, &class_mask);
+	if (fields < 0) return -EINVAL;
+
+	dattr = kmalloc(sizeof(*dattr), GFP_KERNEL);
+	if (!dattr) return -ENOMEM;
+	__dattr_init(dattr);
+	
+	id = kmalloc(sizeof(*id), GFP_KERNEL);
+	if (!id) {
+		kfree(dattr);
+		return -ENOMEM;
+	}
+	dattr->id = id;
+	dattr->show = dynid_show_id;
+
+	id->vendor = vendor;
+	id->device = device;
+	id->subvendor = subvendor;
+	id->subdevice = subdevice;
+	id->class = class;
+	id->class_mask = class_mask;
+
+	spin_lock(&pdrv->dynids.lock);
+	snprintf(dattr->name, KOBJ_NAME_LEN, "%d", pdrv->dynids.nextname);
+	pdrv->dynids.nextname++;
+	spin_unlock(&pdrv->dynids.lock);
+	
+	error = dynid_create_file(pdrv,dattr);
+	if (error) {
+		kfree(id);
+		kfree(dattr);
+		return error;
+	}
+
+	spin_lock(&pdrv->dynids.lock);
+	list_add(&pdrv->dynids.list, &dattr->node);
+	spin_unlock(&pdrv->dynids.lock);
+	return count;
+}
+
+static ssize_t
+dynid_show_new_id(struct pci_driver * pdrv, struct dynid_attribute *unused, char * buf)
+{
+	return pdrv->dynids.show_new_id ?
+		pdrv->dynids.show_new_id(pdrv, buf) :
+		default_show_new_id(buf);
+}
+
+static ssize_t
+dynid_store_new_id(struct pci_driver * pdrv, struct dynid_attribute *unused, const char * buf, size_t count)
+{
+	return pdrv->dynids.store_new_id ?
+		pdrv->dynids.store_new_id(pdrv, buf, count) :
+		default_store_new_id(pdrv, buf, count);
+}
+
+#define DYNID_ATTR(_name,_mode,_show,_store) \
+struct dynid_attribute dynid_attr_##_name = { 		\
+	.attr = {.name = __stringify(_name), .mode = _mode },	\
+        .id   = NULL,                                   \
+	.show	= _show,				\
+	.store	= _store,				\
+}
+
+static DYNID_ATTR(new_id,S_IRUSR|S_IWUSR,dynid_show_new_id,dynid_store_new_id);
+
+static struct attribute * dynids_def_attrs[] = {
+	&dynid_attr_new_id.attr,
+	NULL,
+};
+
+static ssize_t
+dynid_show(struct kobject * kobj, struct attribute *attr, char *buf)
+{
+	struct pci_dynamic_id_kobj *dynid_kobj = kobj_to_dynids(kobj);
+	struct pci_driver *pdrv = dynids_to_pci_driver(dynid_kobj);
+	struct dynid_attribute *dattr = attr_to_dattr(attr);
+
+	if (dattr->show)
+		return dattr->show(pdrv, dattr, buf);
+	return -ENOSYS;
+}
+
+static ssize_t
+dynid_store(struct kobject * kobj, struct attribute *attr, const char *buf, size_t count)
+{
+	struct pci_dynamic_id_kobj *dynid_kobj = kobj_to_dynids(kobj);
+	struct pci_driver *pdrv = dynids_to_pci_driver(dynid_kobj);
+	struct dynid_attribute *dattr = attr_to_dattr(attr);
+	
+	if (dattr->store)
+		return dattr->store(pdrv, dattr, buf, count);
+	return -ENOSYS;
+}
+
+static void
+dynids_release(struct kobject *kobj)
+{
+	struct pci_dynamic_id_kobj *dynids = kobj_to_dynids(kobj);
+	struct pci_driver *pdrv = dynids_to_pci_driver(dynids);
+	struct list_head *pos, *n;
+	struct dynid_attribute *dattr;
+
+	spin_lock(&dynids->lock);
+	list_for_each_safe(pos, n, &dynids->list) {
+		dattr = list_entry(pos, struct dynid_attribute, node);
+		dynid_remove_file(pdrv, dattr);
+		list_del(&dattr->node);
+		if (dattr->id)
+			kfree(dattr->id);
+		kfree(dattr);
+	}
+	spin_unlock(&dynids->lock);
+}
+
+static struct sysfs_ops dynids_attr_ops = {
+	.show = dynid_show,
+	.store = dynid_store,
+};
+static struct kobj_type dynids_kobj_type = {
+	.release = dynids_release,
+	.sysfs_ops = &dynids_attr_ops,
+	.default_attrs = dynids_def_attrs,
+};
+
+/**
+ * pci_register_dynids - initialize and register driver dynamic_ids kobject
+ * @driver - the device_driver structure
+ * @dynids - the dynamic ids structure
+ */
+int
+pci_register_dynids(struct pci_driver *drv)
+{
+	struct device_driver *driver = &drv->driver;
+	struct pci_dynamic_id_kobj *dynids = &drv->dynids;
+	if (drv->probe) {
+		dynids->kobj.parent = &driver->kobj;
+		dynids->kobj.ktype = &dynids_kobj_type;
+		snprintf(dynids->kobj.name, KOBJ_NAME_LEN, "dynamic_id");
+		return kobject_register(&dynids->kobj);
+	}
+	return -ENODEV;
+}
+
diff -Nru a/drivers/pci/pci.h b/drivers/pci/pci.h
--- a/drivers/pci/pci.h	Wed Apr 16 16:07:05 2003
+++ b/drivers/pci/pci.h	Wed Apr 16 16:07:05 2003
@@ -3,3 +3,4 @@
 extern int pci_hotplug (struct device *dev, char **envp, int num_envp,
 			 char *buffer, int buffer_size);
 extern void pci_create_sysfs_dev_files(struct pci_dev *pdev);
+extern int pci_register_dynids(struct pci_driver *drv);
diff -Nru a/include/linux/device.h b/include/linux/device.h
--- a/include/linux/device.h	Wed Apr 16 16:07:05 2003
+++ b/include/linux/device.h	Wed Apr 16 16:07:05 2003
@@ -144,6 +144,7 @@
 	struct attribute	attr;
 	ssize_t (*show)(struct device_driver *, char * buf);
 	ssize_t (*store)(struct device_driver *, const char * buf, size_t count);
+	int (*exists)(struct device_driver *);
 };
 
 #define DRIVER_ATTR(_name,_mode,_show,_store)	\
@@ -151,7 +152,17 @@
 	.attr = {.name = __stringify(_name), .mode = _mode },	\
 	.show	= _show,				\
 	.store	= _store,				\
+	.exists	= NULL,				        \
 };
+
+#define DRIVER_ATTR_EXISTS(_name,_mode,_show,_store,_exists)	\
+struct driver_attribute driver_attr_##_name = { 		\
+	.attr = {.name = __stringify(_name), .mode = _mode },	\
+	.show	= _show,				\
+	.store	= _store,				\
+	.exists	= _exists,				\
+};
+
 
 extern int driver_create_file(struct device_driver *, struct driver_attribute *);
 extern void driver_remove_file(struct device_driver *, struct driver_attribute *);
diff -Nru a/include/linux/pci-dynids.h b/include/linux/pci-dynids.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/linux/pci-dynids.h	Wed Apr 16 16:07:05 2003
@@ -0,0 +1,40 @@
+/*
+ *	PCI defines and function prototypes
+ *	Copyright 2003 Dell Computer Corporation
+ *        by Matt Domsch <Matt_Domsch@dell.com>
+ */
+
+#ifndef LINUX_PCI_DYNIDS_H
+#define LINUX_PCI_DYNIDS_H
+
+#include <linux/mod_devicetable.h>
+#include <linux/types.h>
+#include <linux/config.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+
+struct pci_driver;
+struct pci_device_id;
+
+struct dynid_attribute {
+	struct attribute	attr;
+	struct list_head        node;
+	struct pci_device_id   *id;
+	ssize_t (*show)(struct pci_driver * pdrv, struct dynid_attribute *dattr, char * buf);
+	ssize_t (*store)(struct pci_driver * pdrv, struct dynid_attribute *dattr, const char * buf, size_t count);
+	char name[KOBJ_NAME_LEN];
+};
+
+struct pci_dynamic_id_kobj {
+	ssize_t (*show_new_id)(struct pci_driver * pdrv, char * buf);
+	ssize_t (*store_new_id)(struct pci_driver * pdrv, const char * buf, size_t count);
+	ssize_t (*show_id)(struct pci_device_id * id, struct dynid_attribute *dattr, char * buf);
+
+	spinlock_t lock;            /* protects list, index */
+	struct list_head list;      /* for IDs added at runtime */
+	struct kobject kobj;        /* for displaying the list in sysfs */
+	unsigned int nextname;     /* name of next dynamic ID twhen created */
+};
+
+#endif
diff -Nru a/include/linux/pci.h b/include/linux/pci.h
--- a/include/linux/pci.h	Wed Apr 16 16:07:05 2003
+++ b/include/linux/pci.h	Wed Apr 16 16:07:05 2003
@@ -343,6 +343,7 @@
 #include <linux/list.h>
 #include <linux/errno.h>
 #include <linux/device.h>
+#include <linux/pci-dynids.h>
 
 /* File state for mmap()s on /proc/bus/pci/X/Y */
 enum pci_mmap_state {
@@ -502,6 +503,7 @@
 	int  (*enable_wake) (struct pci_dev *dev, u32 state, int enable);   /* Enable wake event */
 
 	struct device_driver	driver;
+	struct pci_dynamic_id_kobj  dynids;
 };
 
 #define	to_pci_driver(drv) container_of(drv,struct pci_driver, driver)

===================================================================


This BitKeeper patch contains the following changesets:
1.1067
## Wrapped with gzip_uu ##


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
18:?;';3[MO&_R1:UPC-8    
 


^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2003-05-13 21:21 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2003-05-01  0:39 [RFC][PATCH] Dynamic PCI Device IDs Matt_Domsch
2003-05-02 23:15 ` Greg KH
2003-05-05  5:37   ` Jeff Garzik
2003-05-06  0:17     ` Greg KH
2003-05-05 22:51   ` Matt Domsch
2003-05-06  0:15     ` Greg KH
  -- strict thread matches above, loose matches on Subject: below --
2003-05-06  2:04 Matt_Domsch
2003-05-06  3:56 ` Greg KH
2003-05-06 16:35   ` Matt Domsch
2003-05-10  0:11     ` Greg KH
2003-05-13 21:28     ` Patrick Mochel
2003-05-13 21:33       ` Patrick Mochel
2003-04-30 21:45 Matt Domsch
2003-04-30 21:53 ` Jeff Garzik
2003-04-30 22:24 ` Greg KH

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).