Commit f72e5c5c authored by Adam Belay's avatar Adam Belay

[PATCH] PnP Rewrite V0.9 - 2.5.43

The included patch is essentially a Linux Plug and Play Support rewrite.  It
contains many significant improvements, including the following:


1.)  A Global Plug and Play Layer
    - Now drivers do not have to worry about which plug and play
      protocol they are using.  Calls are made directly to the Linux
      Plug and Play Layer and then forwarded to the appropriate
      protocol.
    - This will make it very easy to integrate ACPI PnP support when
      it's ready


2.)  A complete Plug and Play BIOS driver
    - The Plug and Play BIOS now supports reading and writing of
      resource configurations.
    - It is now possible to enable disabled PNPBIOS devices.  Therefore
      the user can safely enable PnP OS support in their BIOS.


3.)  Driver Model Integration
    - The entire plug and play layer is integrated into the driver model
    - The user interface is housed here
    - PnP protocols are listed under the bus "pnp"


4.)  A powerful global resource configuration interface
    - The user can use this to activate PnP devices for legacy and
      user-level drivers
    - See the documentation for how to configure devices.

5.)  Automatic resource allocation for needed devices


6.)  A PnP device name database

And many more improvements.

This patch also adds me to the maintainers list, considering the current
PnP maintainer has been inactive for over 2 years now.
parent fcaf0e9c
...@@ -1750,6 +1750,11 @@ S: Sindlovy Dvory 117 ...@@ -1750,6 +1750,11 @@ S: Sindlovy Dvory 117
S: 370 01 Ceske Budejovice S: 370 01 Ceske Budejovice
S: Czech Republic S: Czech Republic
N: Adam Belay
E: ambx1@neo.rr.com
D: Linux Plug and Play Support
S: USA
N: Bas Laarhoven N: Bas Laarhoven
E: sjml@xs4all.nl E: sjml@xs4all.nl
D: Loadable modules and ftape driver D: Loadable modules and ftape driver
......
Linux Plug and Play Documentation
by Adam Belay <ambx1@neo.rr.com>
last updated: Oct. 16, 2002
---------------------------------------------------------------------------------------
Overview
--------
Plug and Play provides a means of detecting and setting resources for legacy or
otherwise unconfigurable devices. The Linux Plug and Play Layer provides these
services to compatible drivers.
The User Interface
------------------
The Linux Plug and Play user interface provides a means to activate PnP devices
for legacy and user level drivers that do not support Linux Plug and Play. The
user interface is integrated into driverfs.
In addition to the standard driverfs file the following are created in each
device's directory:
id - displays a list of support EISA IDs
possible - displays possible resource configurations
resources - displays currently allocated resources and allows resource changes
-activating a device
#echo "auto" > resources
this will invoke the automatic resource config system to activate the device
-manually activating a device
#echo "manual <depnum> <mode>" > resources
<depnum> - the configuration number
<mode> - static or dynamic
static = for next boot
dynamic = now
-disabling a device
#echo "disable" > resources
EXAMPLE:
Suppose you need to activate the floppy disk controller.
1.) change to the proper directory, in my case it is
/driver/bus/pnp/devices/00:0f
# cd /driver/bus/pnp/devices/00:0f
# cat name
PC standard floppy disk controller
2.) check if the device is already active
# cat resources
DISABLED
- Notice the string "DISABLED". THis means the device is not active.
3.) check the device's possible configurations (optional)
# cat possible
Dependent: 01 - Priority acceptable
port 0x3f0-0x3f0, align 0x7, size 0x6, 16-bit address decoding
port 0x3f7-0x3f7, align 0x0, size 0x1, 16-bit address decoding
irq 6
dma 2 8-bit compatible
Dependent: 02 - Priority acceptable
port 0x370-0x370, align 0x7, size 0x6, 16-bit address decoding
port 0x377-0x377, align 0x0, size 0x1, 16-bit address decoding
irq 6
dma 2 8-bit compatible
4.) now activate the device
# echo "auto" > resources
5.) finally check if the device is active
# cat resources
io 0x3f0-0x3f5
io 0x3f7-0x3f7
irq 6
dma 2
also there are a series of kernel parameters:
allowdma0
pnp_reserve_irq=irq1[,irq2] ....
pnp_reserve_dma=dma1[,dma2] ....
pnp_reserve_io=io1,size1[,io2,size2] ....
pnp_reserve_mem=mem1,size1[,mem2,size2] ....
The Unified Plug and Play Layer
-------------------------------
All Plug and Play drivers, protocols, and services meet at a central location
called the Plug and Play Layer. This layer is responsible for the exchange of
information between PnP drivers and PnP protocols. Thus it automatically
forwards commands to the proper protocol. This makes writting PnP drivers
significantly easier.
The following functions are available from the Plug and Play Layer:
pnp_get_protocol
- increments the number of uses by one
pnp_put_protocol
- deincrements the number of uses by one
pnp_register_protocol
- use this to register a new PnP protocol
pnp_unregister_protocol
- use this function to remove a PnP protocol from the Plug and Play Layer
pnp_register_driver
- adds a PnP driver to the Plug and Play Layer
- this includes driver model integration
pnp_unregister_driver
- removes a PnP driver from the Plug and Play Layer
Plug and Play Protocols
-----------------------
This section contains information for PnP protocol developers.
The following Protocols are currently available in the computing world:
- PNPBIOS: used for system devices such as serial and parallel ports.
- ISAPNP: provides PnP support for the ISA bus
- ACPI: among its many uses, ACPI provides information about system level
devices.
It is meant to replace the PNPBIOS. It is not currently supported by Linux
Plug and Play but it is planned to be in the near future.
Requirements for a Linux PnP protocol:
1.) the protocol must use EISA IDs
2.) the protocol must inform the PnP Layer of a devices current configuration
- the ability to set resources is optional but prefered.
The following are PnP protocol related functions:
pnp_add_device
- use this function to add a PnP device to the PnP layer
- only call this function when all wanted values are set in the pnp_dev
structure
pnp_init_device
- call this to initialize the PnP structure
pnp_remove_device
- call this to remove a device from the Plug and Play Layer.
- it will fail if the device is still in use.
- automatically will free mem used by the device and related structures
pnp_add_id
- adds a EISA ID to the list of supported IDs for the specified device
For more information consult the source of a protocol such as
/drivers/pnp/pnpbios/core.c.
Linux Plug and Play Drivers
---------------------------
This section contains information for linux PnP driver developers.
The New Way
...........
1.) first make a list of supported EISA IDS
ex:
static const struct pnp_id pnp_dev_table[] = {
/* Standard LPT Printer Port */
{.id = "PNP0400", .driver_data = 0},
/* ECP Printer Port */
{.id = "PNP0401", .driver_data = 0},
{.id = ""}
};
Please note that the character 'X' can be used as a wild card in the function
portion (last four characters).
ex:
/* Unkown PnP modems */
{ "PNPCXXX", UNKNOWN_DEV },
Supported PnP card IDs can optionally be defined.
ex:
static const struct pnp_id pnp_card_table[] = {
{ "ANYDEVS", 0 },
{ "", 0 }
};
2.) Optionally define probe and remove functions. It may make sense not to
define these functions if the driver already has a reliable method of detecting
the resources, such as the parport_pc driver.
ex:
static int
serial_pnp_probe(struct pnp_dev * dev, const struct pnp_id *card_id, const
struct pnp_id *dev_id)
{
. . .
ex:
static void serial_pnp_remove(struct pnp_dev * dev)
{
. . .
consult /drivers/serial/8250_pnp.c for more information.
3.) create a driver structure
ex:
static struct pnp_driver serial_pnp_driver = {
.name = "serial",
.card_id_table = pnp_card_table,
.id_table = pnp_dev_table,
.probe = serial_pnp_probe,
.remove = serial_pnp_remove,
};
* name and id_table can not be NULL.
4.) register the driver
ex:
static int __init serial8250_pnp_init(void)
{
return pnp_register_driver(&serial_pnp_driver);
}
The Old Way
...........
a series of compatability functions have been created to make it easy to convert
ISAPNP drivers. They should serve as a temporary solution only.
they are as follows:
struct pnp_card *pnp_find_card(unsigned short vendor,
unsigned short device,
struct pnp_card *from)
struct pnp_dev *pnp_find_dev(struct pnp_card *card,
unsigned short vendor,
unsigned short function,
struct pnp_dev *from)
...@@ -1293,11 +1293,8 @@ L: linux-net@vger.kernel.org ...@@ -1293,11 +1293,8 @@ L: linux-net@vger.kernel.org
S: Maintained S: Maintained
PNP SUPPORT PNP SUPPORT
P: Tom Lees P: Adam Belay
M: tom@lpsg.demon.co.uk M: ambx1@neo.rr.com
L: pnp-users@ferret.lmh.ox.ac.uk
L: pnp-devel@ferret.lmh.ox.ac.uk
W: http://www-jcr.lmh.ox.ac.uk/~pnp/
S: Maintained S: Maintained
PPP PROTOCOL DRIVERS AND COMPRESSORS PPP PROTOCOL DRIVERS AND COMPRESSORS
......
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/pnp.h>
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -2939,9 +2940,9 @@ static int __devinit parport_pc_pci_probe (struct pci_dev *dev, ...@@ -2939,9 +2940,9 @@ static int __devinit parport_pc_pci_probe (struct pci_dev *dev,
} }
static struct pci_driver parport_pc_pci_driver = { static struct pci_driver parport_pc_pci_driver = {
name: "parport_pc", .name = "parport_pc",
id_table: parport_pc_pci_tbl, .id_table = parport_pc_pci_tbl,
probe: parport_pc_pci_probe, .probe = parport_pc_pci_probe,
}; };
static int __init parport_pc_init_superio (int autoirq, int autodma) static int __init parport_pc_init_superio (int autoirq, int autodma)
...@@ -2968,6 +2969,25 @@ static struct pci_driver parport_pc_pci_driver; ...@@ -2968,6 +2969,25 @@ static struct pci_driver parport_pc_pci_driver;
static int __init parport_pc_init_superio(int autoirq, int autodma) {return 0;} static int __init parport_pc_init_superio(int autoirq, int autodma) {return 0;}
#endif /* CONFIG_PCI */ #endif /* CONFIG_PCI */
#ifdef CONFIG_PNP
static const struct pnp_id pnp_dev_table[] = {
/* Standard LPT Printer Port */
{.id = "PNP0400", .driver_data = 0},
/* ECP Printer Port */
{.id = "PNP0401", .driver_data = 0},
{.id = ""}
};
/* we only need the pnp layer to activate the device, at least for now */
static struct pnp_driver parport_pc_pnp_driver = {
.name = "parport_pc",
.card_id_table = NULL,
.id_table = pnp_dev_table,
};
#else
static const struct pnp_driver parport_pc_pnp_driver;
#endif
/* This is called by parport_pc_find_nonpci_ports (in asm/parport.h) */ /* This is called by parport_pc_find_nonpci_ports (in asm/parport.h) */
static int __init __attribute__((unused)) static int __init __attribute__((unused))
parport_pc_find_isa_ports (int autoirq, int autodma) parport_pc_find_isa_ports (int autoirq, int autodma)
...@@ -3021,6 +3041,8 @@ static int __init parport_pc_find_ports (int autoirq, int autodma) ...@@ -3021,6 +3041,8 @@ static int __init parport_pc_find_ports (int autoirq, int autodma)
int __init parport_pc_init (int *io, int *io_hi, int *irq, int *dma) int __init parport_pc_init (int *io, int *io_hi, int *irq, int *dma)
{ {
int count = 0, i = 0; int count = 0, i = 0;
/* try to activate any PnP parports first */
pnp_register_driver(&parport_pc_pnp_driver);
if (io && *io) { if (io && *io) {
/* Only probe the ports we were given. */ /* Only probe the ports we were given. */
...@@ -3133,5 +3155,6 @@ void cleanup_module(void) ...@@ -3133,5 +3155,6 @@ void cleanup_module(void)
p = tmp; p = tmp;
} }
pnp_unregister_driver (&parport_pc_pnp_driver);
} }
#endif #endif
...@@ -6,15 +6,22 @@ CONFIG_PNP ...@@ -6,15 +6,22 @@ CONFIG_PNP
or using a user-space utility. or using a user-space utility.
Say Y here if you would like Linux to configure your Plug and Play Say Y here if you would like Linux to configure your Plug and Play
devices. You should then also say Y to "ISA Plug and Play support", devices. You should then also say Y to all of the protocols below.
below. Alternatively, you can say N here and configure your PnP Alternatively, you can say N here and configure your PnP devices
devices using the user space utilities contained in the isapnptools using user space utilities such as the isapnptools package.
package.
This support is also available as a module ( = code which can be If unsure, say Y.
inserted in and removed from the running kernel whenever you want).
If you want to compile it as a module, say M here and read CONFIG_PNP_NAMES
<file:Documentation/modules.txt>. Select Y if you want the Plug and Play Layer to keep a database of
human readable names for your PnP devices. It will increase the size
of the kernel image by around 5 KB and use 16 KB of system memory.
If unsure, say Y.
CONFIG_PNP_DEBUG
Say Y if you want the Plug and Play Layer to print debug messages.
This is useful if you are developing a PnP driver or troubleshooting.
CONFIG_ISAPNP CONFIG_ISAPNP
Say Y here if you would like support for ISA Plug and Play devices. Say Y here if you would like support for ISA Plug and Play devices.
...@@ -32,8 +39,8 @@ CONFIG_PNPBIOS ...@@ -32,8 +39,8 @@ CONFIG_PNPBIOS
Specification Version 1.0A May 5, 1994" to autodetect built-in Specification Version 1.0A May 5, 1994" to autodetect built-in
mainboard resources (e.g. parallel port resources). mainboard resources (e.g. parallel port resources).
Other features (e.g. change resources, ESCD, event notification, Some features (e.g. event notification, docking station information,
Docking station information, ISAPNP services) are not used. ISAPNP services) are not used.
Note: ACPI is expected to supersede PNPBIOS some day, currently it Note: ACPI is expected to supersede PNPBIOS some day, currently it
co-exists nicely. co-exists nicely.
......
...@@ -4,12 +4,15 @@ ...@@ -4,12 +4,15 @@
mainmenu_option next_comment mainmenu_option next_comment
comment 'Plug and Play configuration' comment 'Plug and Play configuration'
tristate 'Plug and Play support' CONFIG_PNP dep_bool 'Plug and Play support' CONFIG_PNP
dep_tristate ' ISA Plug and Play support' CONFIG_ISAPNP $CONFIG_PNP dep_bool ' Plug and Play device name database' CONFIG_PNP_NAMES $CONFIG_PNP
dep_bool ' PnP Debug Messages' CONFIG_PNP_DEBUG $CONFIG_PNP
comment 'Protocols' $CONFIG_PNP
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
dep_bool ' PNPBIOS support (EXPERIMENTAL)' CONFIG_PNPBIOS $CONFIG_PNP dep_bool ' ISA Plug and Play support (EXPERIMENTAL)' CONFIG_ISAPNP $CONFIG_PNP
dep_bool ' Plug and Play BIOS support (EXPERIMENTAL)' CONFIG_PNPBIOS $CONFIG_PNP
fi fi
endmenu endmenu
# #
# Makefile for the kernel Plug-and-Play device drivers. # Makefile for the Linux Plug-and-Play Support.
# #
export-objs := isapnp.o pnpbios_core.o obj-y := core.o driver.o resource.o interface.o quirks.o names.o compat.o system.o
isa-pnp-proc-$(CONFIG_PROC_FS) = isapnp_proc.o obj-$(CONFIG_PNPBIOS) += pnpbios/
pnpbios-proc-$(CONFIG_PROC_FS) = pnpbios_proc.o obj-$(CONFIG_ISAPNP) += isapnp/
isa-pnp-objs := isapnp.o quirks.o $(isa-pnp-proc-y) export-objs := core.o driver.o resource.o compat.o
pnpbios-objs := pnpbios_core.o $(pnpbios-proc-y)
obj-$(CONFIG_ISAPNP) += isa-pnp.o
obj-$(CONFIG_PNPBIOS) += pnpbios.o
include $(TOPDIR)/Rules.make include $(TOPDIR)/Rules.make
extern struct bus_type pnp_bus_type;
extern spinlock_t pnp_lock;
extern void *pnp_alloc(long size);
extern int pnp_interface_attach_device(struct pnp_dev *dev);
extern void pnp_name_device(struct pnp_dev *dev);
extern void pnp_fixup_device(struct pnp_dev *dev);
extern int compare_pnp_id(struct list_head * id_list, char * id);
extern void pnp_free_ids(struct pnp_dev *dev);
extern void pnp_free_resources(struct pnp_resources *resources);
/*
* compat.c - A series of functions to make it easier to convert drivers that use
* the old isapnp APIs. If possible use the new APIs instead.
*
* Copyright 2002 Adam Belay <ambx1@neo.rr.com>
*
*/
/* TODO: see if more isapnp functions are needed here */
#include <linux/pnp.h>
#include <linux/isapnp.h>
#include <linux/string.h>
#include <linux/module.h>
#include "base.h"
static void pnp_convert_id(char *buf, unsigned short vendor, unsigned short device)
{
sprintf(buf, "%c%c%c%x%x%x%x",
'A' + ((vendor >> 2) & 0x3f) - 1,
'A' + (((vendor & 3) << 3) | ((vendor >> 13) & 7)) - 1,
'A' + ((vendor >> 8) & 0x1f) - 1,
(device >> 4) & 0x0f,
device & 0x0f,
(device >> 12) & 0x0f,
(device >> 8) & 0x0f);
return;
}
struct pnp_card *pnp_find_card(unsigned short vendor,
unsigned short device,
struct pnp_card *from)
{
char id[7];
char any[7];
struct list_head *list;
pnp_convert_id(id, vendor, device);
pnp_convert_id(any, ISAPNP_ANY_ID, ISAPNP_ANY_ID);
list = isapnp_cards.next;
if (from)
list = from->node.next;
while (list != &isapnp_cards) {
struct pnp_card *card = to_pnp_card(list);
if (compare_pnp_id(&card->ids,id) || (memcmp(id,any,7)==0))
return card;
list = list->next;
}
return NULL;
}
struct pnp_dev *pnp_find_dev(struct pnp_card *card,
unsigned short vendor,
unsigned short function,
struct pnp_dev *from)
{
char id[7];
char any[7];
pnp_convert_id(id, vendor, function);
pnp_convert_id(any, ISAPNP_ANY_ID, ISAPNP_ANY_ID);
if (card == NULL) { /* look for a logical device from all cards */
struct list_head *list;
list = pnp_global.next;
if (from)
list = from->global_list.next;
while (list != &pnp_global) {
struct pnp_dev *dev = global_to_pnp_dev(list);
if (compare_pnp_id(&dev->ids,id) || (memcmp(id,any,7)==0))
return dev;
list = list->next;
}
} else {
struct list_head *list;
list = card->devices.next;
if (from) {
list = from->card_list.next;
if (from->card != card) /* something is wrong */
return NULL;
}
while (list != &card->devices) {
struct pnp_dev *dev = card_to_pnp_dev(list);
if (compare_pnp_id(&dev->ids,id))
return dev;
list = list->next;
}
}
return NULL;
}
EXPORT_SYMBOL(pnp_find_card);
EXPORT_SYMBOL(pnp_find_dev);
/*
* core.c - contains all core device and protocol registration functions
*
* Copyright 2002 Adam Belay <ambx1@neo.rr.com>
*
*/
#include <linux/pnp.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/slab.h>
#include "base.h"
LIST_HEAD(pnp_protocols);
LIST_HEAD(pnp_global);
spinlock_t pnp_lock = SPIN_LOCK_UNLOCKED;
void *pnp_alloc(long size)
{
void *result;
result = kmalloc(size, GFP_KERNEL);
if (!result){
printk(KERN_ERR "pnp: Out of Memory\n");
return NULL;
}
memset(result, 0, size);
return result;
}
/**
* pnp_protocol_register - adds a pnp protocol to the pnp layer
* @protocol: pointer to the corresponding pnp_protocol structure
*
* Ex protocols: ISAPNP, PNPBIOS, etc
*/
int pnp_protocol_register(struct pnp_protocol *protocol)
{
int nodenum;
struct list_head * pos;
if (!protocol)
return -EINVAL;
INIT_LIST_HEAD(&protocol->devices);
nodenum = 0;
spin_lock(&pnp_lock);
/* assign the lowest unused number */
list_for_each(pos,&pnp_protocols) {
struct pnp_protocol * cur = to_pnp_protocol(pos);
if (cur->number == nodenum){
pos = &pnp_protocols;
nodenum++;
}
}
list_add_tail(&protocol->protocol_list, &pnp_protocols);
spin_unlock(&pnp_lock);
protocol->number = nodenum;
sprintf(protocol->dev.bus_id, "pnp%d", nodenum);
strncpy(protocol->dev.name,protocol->name,DEVICE_NAME_SIZE);
return device_register(&protocol->dev);
}
/**
* pnp_protocol_unregister - removes a pnp protocol from the pnp layer
* @protocol: pointer to the corresponding pnp_protocol structure
*
*/
void pnp_protocol_unregister(struct pnp_protocol *protocol)
{
spin_lock(&pnp_lock);
list_del_init(&protocol->protocol_list);
spin_unlock(&pnp_lock);
device_unregister(&protocol->dev);
return;
}
/**
* pnp_init_device - pnp protocols should call this before adding a PnP device
* @dev: pointer to dev to init
*
* for now it only inits dev->ids, more later?
*/
int pnp_init_device(struct pnp_dev *dev)
{
INIT_LIST_HEAD(&dev->ids);
return 0;
}
static void pnp_release_device(struct device *dmdev)
{
struct pnp_dev * dev = to_pnp_dev(dmdev);
if (dev->res)
pnp_free_resources(dev->res);
pnp_free_ids(dev);
kfree(dev);
return;
}
/**
* pnp_add_device - adds a pnp device to the pnp layer
* @dev: pointer to dev to add
*
* adds to driver model, name database, fixups, interface, etc.
*/
int pnp_add_device(struct pnp_dev *dev)
{
int error = 0;
if (!dev && !dev->protocol)
return -EINVAL;
if (dev->card)
sprintf(dev->dev.bus_id, "%02x:%02x.%02x", dev->protocol->number,
dev->card->number,dev->number);
else
sprintf(dev->dev.bus_id, "%02x:%02x", dev->protocol->number,
dev->number);
pnp_name_device(dev);
pnp_fixup_device(dev);
strcpy(dev->dev.name,dev->name);
dev->dev.parent = &dev->protocol->dev;
dev->dev.bus = &pnp_bus_type;
dev->dev.release = &pnp_release_device;
error = device_register(&dev->dev);
if (error == 0){
spin_lock(&pnp_lock);
list_add_tail(&dev->global_list, &pnp_global);
list_add_tail(&dev->dev_list, &dev->protocol->devices);
spin_unlock(&pnp_lock);
pnp_interface_attach_device(dev);
}
return error;
}
/**
* pnp_remove_device - removes a pnp device from the pnp layer
* @dev: pointer to dev to add
*
* this function will free all mem used by dev
*/
void pnp_remove_device(struct pnp_dev *dev)
{
if (!dev)
return;
device_unregister(&dev->dev);
spin_lock(&pnp_lock);
list_del_init(&dev->global_list);
list_del_init(&dev->dev_list);
spin_unlock(&pnp_lock);
return;
}
static int __init pnp_init(void)
{
printk(KERN_INFO "Linux Plug and Play Support v0.9 (c) Adam Belay\n");
return bus_register(&pnp_bus_type);
}
core_initcall(pnp_init);
EXPORT_SYMBOL(pnp_protocol_register);
EXPORT_SYMBOL(pnp_protocol_unregister);
EXPORT_SYMBOL(pnp_add_device);
EXPORT_SYMBOL(pnp_remove_device);
EXPORT_SYMBOL(pnp_init_device);
/*
* driver.c - device id matching, driver model, etc.
*
* Copyright 2002 Adam Belay <ambx1@neo.rr.com>
*
*/
#include <linux/config.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#ifdef CONFIG_PNP_DEBUG
#define DEBUG
#else
#undef DEBUG
#endif
#include <linux/pnp.h>
static int compare_func(const char *ida, const char *idb)
{
int i;
/* we only need to compare the last 4 chars */
for (i=3; i<7; i++)
{
if (ida[i] != 'X' &&
idb[i] != 'X' &&
toupper(ida[i]) != toupper(idb[i]))
return 0;
}
return 1;
}
int compare_pnp_id(struct list_head *id_list, const char *id)
{
struct list_head *pos;
if (!id_list || !id || (strlen(id) != 7))
return 0;
if (memcmp(id,"ANYDEVS",7)==0)
return 1;
list_for_each(pos,id_list){
struct pnp_id *pnp_id = to_pnp_id(pos);
if (memcmp(pnp_id->id,id,3)==0)
if (compare_func(pnp_id->id,id)==1)
return 1;
}
return 0;
}
static const struct pnp_id * match_card(struct pnp_driver *drv, struct pnp_card *card)
{
const struct pnp_id *drv_card_id = drv->card_id_table;
if (!drv)
return NULL;
if (!card)
return NULL;
while (*drv_card_id->id){
if (compare_pnp_id(&card->ids,drv_card_id->id))
return drv_card_id;
drv_card_id++;
}
return NULL;
}
static const struct pnp_id * match_device(struct pnp_driver *drv, struct pnp_dev *dev)
{
const struct pnp_id *drv_id = drv->id_table;
if (!drv)
return NULL;
if (!dev)
return NULL;
while (*drv_id->id){
if (compare_pnp_id(&dev->ids,drv_id->id))
return drv_id;
drv_id++;
}
return NULL;
}
static int pnp_device_probe(struct device *dev)
{
int error = 0;
struct pnp_driver *pnp_drv;
struct pnp_dev *pnp_dev;
const struct pnp_id *card_id = NULL;
const struct pnp_id *dev_id = NULL;
pnp_dev = to_pnp_dev(dev);
pnp_drv = to_pnp_driver(dev->driver);
pnp_dbg("pnp: match found with the PnP device '%s' and the driver '%s'", dev->bus_id,pnp_drv->name);
if (pnp_dev->active == 0)
if(pnp_activate_dev(pnp_dev)<0)
return 0;
if (pnp_drv->probe && pnp_dev->active) {
if (pnp_dev->card && pnp_drv->card_id_table){
card_id = match_card(pnp_drv, pnp_dev->card);
if (card_id != NULL)
dev_id = match_device(pnp_drv, pnp_dev);
if (dev_id != NULL)
error = pnp_drv->probe(pnp_dev, card_id, dev_id);
}
else{
dev_id = match_device(pnp_drv, pnp_dev);
if (dev_id != NULL)
error = pnp_drv->probe(pnp_dev, card_id, dev_id);
}
if (error >= 0){
pnp_dev->driver = pnp_drv;
error = 0;
}
}
return error;
}
static int pnp_device_remove(struct device *dev)
{
struct pnp_dev * pnp_dev = to_pnp_dev(dev);
struct pnp_driver * drv = pnp_dev->driver;
if (drv) {
if (drv->remove)
drv->remove(pnp_dev);
pnp_dev->driver = NULL;
}
pnp_disable_dev(pnp_dev);
return 0;
}
static int pnp_bus_match(struct device *dev, struct device_driver *drv)
{
struct pnp_dev * pnp_dev = to_pnp_dev(dev);
struct pnp_driver * pnp_drv = to_pnp_driver(drv);
if (pnp_dev->card && pnp_drv->card_id_table
&& match_card(pnp_drv, pnp_dev->card) == NULL)
return 0;
if (match_device(pnp_drv, pnp_dev) == NULL)
return 0;
return 1;
}
struct bus_type pnp_bus_type = {
name: "pnp",
match: pnp_bus_match,
};
int pnp_register_driver(struct pnp_driver *drv)
{
int count = 0;
pnp_dbg("the driver '%s' has been registered", drv->name);
drv->driver.name = drv->name;
drv->driver.bus = &pnp_bus_type;
drv->driver.probe = pnp_device_probe;
drv->driver.remove = pnp_device_remove;
count = driver_register(&drv->driver);
return count ? count : 1;
}
void pnp_unregister_driver(struct pnp_driver *drv)
{
pnp_dbg("the driver '%s' has been unregistered", drv->name);
remove_driver(&drv->driver);
}
/**
* pnp_add_id - adds an EISA id to the specified device
* @id: pointer to a pnp_id structure
* @dev: pointer to the desired device
*
*/
int pnp_add_id(struct pnp_id *id, struct pnp_dev *dev)
{
if (!id)
return -EINVAL;
if (!dev)
return -EINVAL;
list_add_tail(&id->id_list,&dev->ids);
return 0;
}
void pnp_free_ids(struct pnp_dev *dev)
{
struct list_head *pos;
if (!dev)
return;
list_for_each(pos,&dev->ids){
struct pnp_id *pnp_id = to_pnp_id(pos);
kfree(pnp_id);
}
return;
}
EXPORT_SYMBOL(pnp_register_driver);
EXPORT_SYMBOL(pnp_unregister_driver);
EXPORT_SYMBOL(pnp_add_id);
This diff is collapsed.
/*
* interface.c - contains everything related to the user interface
*
* Some code is based on isapnp_proc.c (c) Jaroslav Kysela <perex@suse.cz>
* Copyright 2002 Adam Belay <ambx1@neo.rr.com>
*
*/
#include <linux/pnp.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/types.h>
#include "base.h"
struct pnp_info_buffer {
char *buffer; /* pointer to begin of buffer */
char *curr; /* current position in buffer */
unsigned long size; /* current size */
unsigned long len; /* total length of buffer */
int stop; /* stop flag */
int error; /* error code */
};
typedef struct pnp_info_buffer pnp_info_buffer_t;
int pnp_printf(pnp_info_buffer_t * buffer, char *fmt,...)
{
va_list args;
int res;
char sbuffer[512];
if (buffer->stop || buffer->error)
return 0;
va_start(args, fmt);
res = vsprintf(sbuffer, fmt, args);
va_end(args);
if (buffer->size + res >= buffer->len) {
buffer->stop = 1;
return 0;
}
strcpy(buffer->curr, sbuffer);
buffer->curr += res;
buffer->size += res;
return res;
}
static void pnp_print_port(pnp_info_buffer_t *buffer, char *space, struct pnp_port *port)
{
pnp_printf(buffer, "%sport 0x%x-0x%x, align 0x%x, size 0x%x, %i-bit address decoding\n",
space, port->min, port->max, port->align ? (port->align-1) : 0, port->size,
port->flags & PNP_PORT_FLAG_16BITADDR ? 16 : 10);
}
static void pnp_print_irq(pnp_info_buffer_t *buffer, char *space, struct pnp_irq *irq)
{
int first = 1, i;
pnp_printf(buffer, "%sirq ", space);
for (i = 0; i < 16; i++)
if (irq->map & (1<<i)) {
if (!first) {
pnp_printf(buffer, ",");
} else {
first = 0;
}
if (i == 2 || i == 9)
pnp_printf(buffer, "2/9");
else
pnp_printf(buffer, "%i", i);
}
if (!irq->map)
pnp_printf(buffer, "<none>");
if (irq->flags & IORESOURCE_IRQ_HIGHEDGE)
pnp_printf(buffer, " High-Edge");
if (irq->flags & IORESOURCE_IRQ_LOWEDGE)
pnp_printf(buffer, " Low-Edge");
if (irq->flags & IORESOURCE_IRQ_HIGHLEVEL)
pnp_printf(buffer, " High-Level");
if (irq->flags & IORESOURCE_IRQ_LOWLEVEL)
pnp_printf(buffer, " Low-Level");
pnp_printf(buffer, "\n");
}
static void pnp_print_dma(pnp_info_buffer_t *buffer, char *space, struct pnp_dma *dma)
{
int first = 1, i;
char *s;
pnp_printf(buffer, "%sdma ", space);
for (i = 0; i < 8; i++)
if (dma->map & (1<<i)) {
if (!first) {
pnp_printf(buffer, ",");
} else {
first = 0;
}
pnp_printf(buffer, "%i", i);
}
if (!dma->map)
pnp_printf(buffer, "<none>");
switch (dma->flags & IORESOURCE_DMA_TYPE_MASK) {
case IORESOURCE_DMA_8BIT:
s = "8-bit";
break;
case IORESOURCE_DMA_8AND16BIT:
s = "8-bit&16-bit";
break;
default:
s = "16-bit";
}
pnp_printf(buffer, " %s", s);
if (dma->flags & IORESOURCE_DMA_MASTER)
pnp_printf(buffer, " master");
if (dma->flags & IORESOURCE_DMA_BYTE)
pnp_printf(buffer, " byte-count");
if (dma->flags & IORESOURCE_DMA_WORD)
pnp_printf(buffer, " word-count");
switch (dma->flags & IORESOURCE_DMA_SPEED_MASK) {
case IORESOURCE_DMA_TYPEA:
s = "type-A";
break;
case IORESOURCE_DMA_TYPEB:
s = "type-B";
break;
case IORESOURCE_DMA_TYPEF:
s = "type-F";
break;
default:
s = "compatible";
break;
}
pnp_printf(buffer, " %s\n", s);
}
static void pnp_print_mem(pnp_info_buffer_t *buffer, char *space, struct pnp_mem *mem)
{
char *s;
pnp_printf(buffer, "%sMemory 0x%x-0x%x, align 0x%x, size 0x%x",
space, mem->min, mem->max, mem->align, mem->size);
if (mem->flags & IORESOURCE_MEM_WRITEABLE)
pnp_printf(buffer, ", writeable");
if (mem->flags & IORESOURCE_MEM_CACHEABLE)
pnp_printf(buffer, ", cacheable");
if (mem->flags & IORESOURCE_MEM_RANGELENGTH)
pnp_printf(buffer, ", range-length");
if (mem->flags & IORESOURCE_MEM_SHADOWABLE)
pnp_printf(buffer, ", shadowable");
if (mem->flags & IORESOURCE_MEM_EXPANSIONROM)
pnp_printf(buffer, ", expansion ROM");
switch (mem->flags & IORESOURCE_MEM_TYPE_MASK) {
case IORESOURCE_MEM_8BIT:
s = "8-bit";
break;
case IORESOURCE_MEM_8AND16BIT:
s = "8-bit&16-bit";
break;
default:
s = "16-bit";
}
pnp_printf(buffer, ", %s\n", s);
}
static void pnp_print_mem32(pnp_info_buffer_t *buffer, char *space, struct pnp_mem32 *mem32)
{
int first = 1, i;
pnp_printf(buffer, "%s32-bit memory ", space);
for (i = 0; i < 17; i++) {
if (first) {
first = 0;
} else {
pnp_printf(buffer, ":");
}
pnp_printf(buffer, "%02x", mem32->data[i]);
}
}
static void pnp_print_resources(pnp_info_buffer_t *buffer, char *space, struct pnp_resources *res, int dep)
{
char *s;
struct pnp_port *port;
struct pnp_irq *irq;
struct pnp_dma *dma;
struct pnp_mem *mem;
struct pnp_mem32 *mem32;
switch (res->priority) {
case PNP_RES_PRIORITY_PREFERRED:
s = "preferred";
break;
case PNP_RES_PRIORITY_ACCEPTABLE:
s = "acceptable";
break;
case PNP_RES_PRIORITY_FUNCTIONAL:
s = "functional";
break;
default:
s = "invalid";
}
if (dep > 0)
pnp_printf(buffer, "Dependent: %02i - Priority %s\n",dep, s);
for (port = res->port; port; port = port->next)
pnp_print_port(buffer, space, port);
for (irq = res->irq; irq; irq = irq->next)
pnp_print_irq(buffer, space, irq);
for (dma = res->dma; dma; dma = dma->next)
pnp_print_dma(buffer, space, dma);
for (mem = res->mem; mem; mem = mem->next)
pnp_print_mem(buffer, space, mem);
for (mem32 = res->mem32; mem32; mem32 = mem32->next)
pnp_print_mem32(buffer, space, mem32);
}
static ssize_t pnp_show_possible_resources(struct device *dmdev, char *buf, size_t count, loff_t off)
{
struct pnp_dev *dev = to_pnp_dev(dmdev);
struct pnp_resources * res = dev->res;
int dep = 0;
pnp_info_buffer_t *buffer;
if (off)
return 0;
buffer = (pnp_info_buffer_t *) pnp_alloc(sizeof(pnp_info_buffer_t));
if (!buffer)
return -ENOMEM;
buffer->len = PAGE_SIZE;
buffer->buffer = buf;
buffer->curr = buffer->buffer;
while (res){
if (dep == 0)
pnp_print_resources(buffer, "", res, dep);
else
pnp_print_resources(buffer, " ", res, dep);
res = res->dep;
dep++;
}
return (buffer->curr - buf);
}
static DEVICE_ATTR(possible,S_IRUGO,pnp_show_possible_resources,NULL);
static ssize_t pnp_show_current_resources(struct device *dmdev, char *buf, size_t count, loff_t off)
{
struct pnp_dev *dev = to_pnp_dev(dmdev);
char *str = buf;
int i;
if (off)
return 0;
if (!dev->active){
str += sprintf(str,"DISABLED\n");
goto done;
}
for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
if (dev->resource[i].flags & IORESOURCE_IO){
str += sprintf(str,"io");
str += sprintf(str," 0x%lx-0x%lx \n",
dev->resource[i].start,
dev->resource[i].end);
}
if (dev->resource[i].flags & IORESOURCE_MEM){
str += sprintf(str,"mem");
str += sprintf(str," 0x%lx-0x%lx \n",
dev->resource[i].start,
dev->resource[i].end);
}
}
for (i = 0; i < DEVICE_COUNT_IRQ && dev->irq_resource[i].flags
& IORESOURCE_IRQ; i++) {
str += sprintf(str,"irq");
str += sprintf(str," %ld \n", dev->irq_resource[i].start);
}
for (i = 0; i < DEVICE_COUNT_DMA && dev->dma_resource[i].flags
& IORESOURCE_DMA; i++) {
str += sprintf(str,"dma");
str += sprintf(str," %ld \n", dev->dma_resource[i].start);
}
done:
return (str - buf);
}
static ssize_t
pnp_set_current_resources(struct device * dmdev, const char * buf, size_t count, loff_t off)
{
struct pnp_dev *dev = to_pnp_dev(dmdev);
char command[20];
char type[20];
int num_args;
int error = 0;
int depnum, mode = 0;
if (off)
return 0;
num_args = sscanf(buf,"%10s %i %10s",command,&depnum,type);
if (!num_args)
goto done;
if (!strnicmp(command,"disable",7)) {
error = pnp_disable_dev(dev);
goto done;
}
if (!strnicmp(command,"auto",4)) {
error = pnp_activate_dev(dev);
goto done;
}
if (!strnicmp(command,"manual",6)) {
if (num_args != 3)
goto done;
if (!strnicmp(type,"static",6))
mode = PNP_STATIC;
error = pnp_raw_set_dev(dev,depnum,mode);
goto done;
}
done:
return error < 0 ? error : count;
}
static DEVICE_ATTR(resources,S_IRUGO | S_IWUSR,
pnp_show_current_resources,pnp_set_current_resources);
static ssize_t pnp_show_current_ids(struct device *dmdev, char *buf, size_t count, loff_t off)
{
char *str = buf;
struct list_head * pos;
struct pnp_dev *dev = to_pnp_dev(dmdev);
if (off)
return 0;
list_for_each(pos,&dev->ids) {
struct pnp_id * cur = to_pnp_id(pos);
str += sprintf(str,"%s\n", cur->id);
}
return (str - buf);
}
static DEVICE_ATTR(id,S_IRUGO,pnp_show_current_ids,NULL);
int pnp_interface_attach_device(struct pnp_dev *dev)
{
device_create_file(&dev->dev,&dev_attr_possible);
device_create_file(&dev->dev,&dev_attr_resources);
device_create_file(&dev->dev,&dev_attr_id);
return 0;
}
#
# Makefile for the kernel ISAPNP driver.
#
export-objs := core.o
isapnp-proc-$(CONFIG_PROC_FS) = proc.o
obj-y := core.o $(isapnp-proc-y)
include $(TOPDIR)/Rules.make
/*
* ISA Plug & Play support
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/isapnp.h>
#include <linux/pnp.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
static struct proc_dir_entry *isapnp_proc_bus_dir = NULL;
static loff_t isapnp_proc_bus_lseek(struct file *file, loff_t off, int whence)
{
loff_t new = -1;
lock_kernel();
switch (whence) {
case 0:
new = off;
break;
case 1:
new = file->f_pos + off;
break;
case 2:
new = 256 + off;
break;
}
if (new < 0 || new > 256) {
unlock_kernel();
return -EINVAL;
}
unlock_kernel();
return (file->f_pos = new);
}
static ssize_t isapnp_proc_bus_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos)
{
struct inode *ino = file->f_dentry->d_inode;
struct proc_dir_entry *dp = PDE(ino);
struct pnp_dev *dev = dp->data;
int pos = *ppos;
int cnt, size = 256;
if (pos >= size)
return 0;
if (nbytes >= size)
nbytes = size;
if (pos + nbytes > size)
nbytes = size - pos;
cnt = nbytes;
if (!access_ok(VERIFY_WRITE, buf, cnt))
return -EINVAL;
isapnp_cfg_begin(dev->card->number, dev->number);
for ( ; pos < 256 && cnt > 0; pos++, buf++, cnt--) {
unsigned char val;
val = isapnp_read_byte(pos);
__put_user(val, buf);
}
isapnp_cfg_end();
*ppos = pos;
return nbytes;
}
static struct file_operations isapnp_proc_bus_file_operations =
{
llseek: isapnp_proc_bus_lseek,
read: isapnp_proc_bus_read,
};
static int isapnp_proc_attach_device(struct pnp_dev *dev)
{
struct pnp_card *bus = dev->card;
struct proc_dir_entry *de, *e;
char name[16];
if (!(de = bus->procdir)) {
sprintf(name, "%02x", bus->number);
de = bus->procdir = proc_mkdir(name, isapnp_proc_bus_dir);
if (!de)
return -ENOMEM;
}
sprintf(name, "%02x", dev->number);
e = dev->procent = create_proc_entry(name, S_IFREG | S_IRUGO, de);
if (!e)
return -ENOMEM;
e->proc_fops = &isapnp_proc_bus_file_operations;
e->owner = THIS_MODULE;
e->data = dev;
e->size = 256;
return 0;
}
#ifdef MODULE
static int __exit isapnp_proc_detach_device(struct pnp_dev *dev)
{
struct pnp_card *bus = dev->card;
struct proc_dir_entry *de;
char name[16];
if (!(de = bus->procdir))
return -EINVAL;
sprintf(name, "%02x", dev->number);
remove_proc_entry(name, de);
return 0;
}
static int __exit isapnp_proc_detach_bus(struct pnp_card *bus)
{
struct proc_dir_entry *de;
char name[16];
if (!(de = bus->procdir))
return -EINVAL;
sprintf(name, "%02x", bus->number);
remove_proc_entry(name, isapnp_proc_bus_dir);
return 0;
}
#endif /* MODULE */
int __init isapnp_proc_init(void)
{
struct pnp_dev *dev;
isapnp_proc_bus_dir = proc_mkdir("isapnp", proc_bus);
isapnp_for_each_dev(dev) {
isapnp_proc_attach_device(dev);
}
return 0;
}
#ifdef MODULE
int __exit isapnp_proc_done(void)
{
struct pnp_dev *dev;
struct pnp_bus *card;
isapnp_for_each_dev(dev) {
isapnp_proc_detach_device(dev);
}
isapnp_for_each_card(card) {
isapnp_proc_detach_bus(card);
}
if (isapnp_proc_bus_dir)
remove_proc_entry("isapnp", proc_bus);
return 0;
}
#endif /* MODULE */
This diff is collapsed.
/*
* names.c - a very simple name database for PnP devices
*
* Some code is based on names.c from linux pci
* Copyright 1993--1999 Drew Eckhardt, Frederic Potter,
* David Mosberger-Tang, Martin Mares
*
* Copyright 2002 Adam Belay <ambx1@neo.rr.com>
*
*/
#include <linux/string.h>
#include <linux/pnp.h>
#include "base.h"
#ifdef CONFIG_PNP_NAMES
static char *pnp_id_eisaid[] = {
#define ID(x,y) x,
#include "idlist.h"
};
static char *pnp_id_names[] = {
#define ID(x,y) y,
#include "idlist.h"
};
void
pnp_name_device(struct pnp_dev *dev)
{
int i;
char *name = dev->name;
for(i=0; i<sizeof(pnp_id_eisaid)/sizeof(pnp_id_eisaid[0]); i++){
if (compare_pnp_id(&dev->ids,pnp_id_eisaid[i])){
sprintf(name, "%s", pnp_id_names[i]);
return;
}
}
return;
}
#else
void
pnp_name_device(struct pnp_dev *dev)
{
return;
}
#endif /* CONFIG_PNP_NAMES */
#
# Makefile for the kernel PNPBIOS driver.
#
export-objs := core.o
pnpbios-proc-$(CONFIG_PROC_FS) = proc.o
obj-y := core.o $(pnpbios-proc-y)
include $(TOPDIR)/Rules.make
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -8,7 +8,7 @@ export-objs := core.o 8250.o suncore.o ...@@ -8,7 +8,7 @@ export-objs := core.o 8250.o suncore.o
serial-8250-y := serial-8250-y :=
serial-8250-$(CONFIG_PCI) += 8250_pci.o serial-8250-$(CONFIG_PCI) += 8250_pci.o
serial-8250-$(CONFIG_ISAPNP) += 8250_pnp.o serial-8250-$(CONFIG_PNP) += 8250_pnp.o
obj-$(CONFIG_SERIAL_CORE) += core.o obj-$(CONFIG_SERIAL_CORE) += core.o
obj-$(CONFIG_SERIAL_21285) += 21285.o obj-$(CONFIG_SERIAL_21285) += 21285.o
obj-$(CONFIG_SERIAL_8250) += 8250.o $(serial-8250-y) obj-$(CONFIG_SERIAL_8250) += 8250.o $(serial-8250-y)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment