Commit 3c363821 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://kernel.bkbits.net/gregkh/linux/pnp-2.5

into home.transmeta.com:/home/torvalds/v2.5/linux
parents 8e5cae4e 4513bc2f
......@@ -4,199 +4,11 @@ ISA Plug & Play support by Jaroslav Kysela <perex@suse.cz>
Interface /proc/isapnp
======================
Read commands:
--------------
The interface has been removed. See pnp.txt for more details.
No comment.
Write commands:
---------------
With the write interface you can activate or modify the configuration of
ISA Plug & Play devices. It is mainly useful for drivers which have not
been rewritten to use the ISA Plug & Play kernel support yet.
card <idx> <vendor> - select PnP device by vendor identification
csn <CSN> - select PnP device by CSN
dev <idx> <logdev> - select logical device
auto - run autoconfigure
activate - activate logical device
deactivate - deactivate logical device
port <idx> <value> - set port 0-7 to value
irq <idx> <value> - set IRQ 0-1 to value
dma <idx> <value> - set DMA 0-1 to value
memory <idx> <value> - set memory 0-3 to value
poke <reg> <value> - poke configuration byte to selected register
pokew <reg> <value> - poke configuration word to selected register
poked <reg> <value> - poke configuration dword to selected register
allow_dma0 <value> - allow dma channel 0 during auto activation: 0=off, 1=on
Explanation:
- variable <idx> begins with zero
- variable <CSN> begins with one
- <vendor> is in the standard format 'ABC1234'
- <logdev> is in the standard format 'ABC1234'
Example:
cat > /proc/isapnp <<EOF
card 0 CSC7537
dev 0 CSC0000
port 0 0x534
port 1 0x388
port 2 0x220
irq 0 5
dma 0 1
dma 1 3
poke 0x70 9
activate
logdev 0 CSC0001
port 0 0x240
activate
EOF
Information for developers
Interface /proc/bus/isapnp
==========================
Finding a device
----------------
extern struct pci_bus *isapnp_find_card(unsigned short vendor,
unsigned short device,
struct pci_bus *from);
This function finds an ISA PnP card. For the vendor argument, the
ISAPNP_VENDOR(a,b,c) macro should be used, where a,b,c are characters or
integers. For the device argument the ISAPNP_DEVICE(x) macro should be
used, where x is an integer value. Both vendor and device arguments
can be taken from contents of the /proc/isapnp file.
extern struct pci_dev *isapnp_find_dev(struct pci_bus *card,
unsigned short vendor,
unsigned short function,
struct pci_dev *from);
This function finds an ISA PnP device. If card is NULL, then the global
search mode is used (all devices are used for the searching). Otherwise
only devices which belong to the specified card are checked. For the
function number the ISAPNP_FUNCTION(x) macro can be used; it works
similarly to the ISAPNP_DEVICE(x) macro.
extern int isapnp_probe_cards(const struct isapnp_card_id *ids,
int (*probe)(struct pci_bus *card,
const struct isapnp_card_id *id));
This function is a helper for drivers which need to use more than
one device from an ISA PnP card. The probe callback is called with
appropriate arguments for each card.
Example for ids parameter initialization:
static struct isapnp_card_id card_ids[] __devinitdata = {
{
ISAPNP_CARD_ID('A','D','V', 0x550a),
devs: {
ISAPNP_DEVICE_ID('A', 'D', 'V', 0x0010),
ISAPNP_DEVICE_ID('A', 'D', 'V', 0x0011)
},
driver_data: 0x1234,
},
{
ISAPNP_CARD_END,
}
};
ISAPNP_CARD_TABLE(card_ids);
extern int isapnp_probe_devs(const struct isapnp_device_id *ids,
int (*probe)(struct pci_bus *card,
const struct isapnp_device_id *id));
This function is a helper for drivers which need to use one
device from an ISA PnP card. The probe callback is called with
appropriate arguments for each matched device.
Example for ids parameter initialization:
static struct isapnp_device_id device_ids[] __devinitdata = {
{ ISAPNP_DEVICE_SINGLE('E','S','S', 0x0968, 'E','S','S', 0x0968), },
{ ISAPNP_DEVICE_SINGLE_END, }
};
MODULE_DEVICE_TABLE(isapnp, device_ids);
ISA PnP configuration
=====================
There are two ways in which the ISA PnP interface can be used.
First way: low-level
--------------------
All ISA PNP configuration registers are accessible via the low-level
isapnp_(read|write)_(byte|word|dword) functions.
The function isapnp_cfg_begin() must be called before any lowlevel function.
The function isapnp_cfg_end() must be always called after configuration
otherwise the access to the ISA PnP configuration functions will be blocked.
Second way: auto-configuration
------------------------------
This feature gives to the driver the real power of the ISA PnP driver.
The function dev->prepare() initializes the resource members in the device
structure. This structure contains all resources set to auto configuration
values after the initialization. The device driver may modify some resources
to skip the auto configuration for a given resource.
Once the device structure contains all requested resource values, the function
dev->activate() must be called to assign free resources to resource members
with the auto configuration value.
Function dev->activate() does:
- resources with the auto configuration value are configured
- the auto configuration is created using ISA PnP resource map
- the function writes configuration to ISA PnP configuration registers
- the function returns to the caller actual used resources
When the device driver is removed, function dev->deactivate() has to be
called to free all assigned resources.
Example (game port initialization)
==================================
/*** initialization ***/
struct pci_dev *dev;
/* find the first game port, use standard PnP IDs */
dev = isapnp_find_dev(NULL,
ISAPNP_VENDOR('P','N','P'),
ISAPNP_FUNCTION(0xb02f),
NULL);
if (!dev)
return -ENODEV;
if (dev->active)
return -EBUSY;
if (dev->prepare(dev)<0)
return -EAGAIN;
if (!(dev->resource[0].flags & IORESOURCE_IO))
return -ENODEV;
if (!dev->ro) {
/* override resource */
if (user_port != USER_PORT_AUTO_VALUE)
isapnp_resource_change(&dev->resource[0], user_port, 1);
}
if (dev->activate(dev)<0) {
printk("isapnp configure failed (out of resources?)\n");
return -ENOMEM;
}
user_port = dev->resource[0].start; /* get real port */
/*** deactivation ***/
/* to deactivate use: */
if (dev)
dev->deactivate(dev);
This directory allows access to ISA PnP cards and logical devices.
The regular files contain the contents of ISA PnP registers for
a logical device.
......@@ -814,7 +814,7 @@ static void __init add_pcic(int ns, int type)
#ifdef CONFIG_ISA
#ifdef __ISAPNP__
#ifdef CONFIG_PNP
static struct isapnp_device_id id_table[] __initdata = {
{ ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('P', 'N', 'P'),
ISAPNP_FUNCTION(0x0e00), (unsigned long) "Intel 82365-Compatible" },
......@@ -826,32 +826,28 @@ static struct isapnp_device_id id_table[] __initdata = {
};
MODULE_DEVICE_TABLE(isapnp, id_table);
static struct pci_dev *i82365_pnpdev;
static struct pnp_dev *i82365_pnpdev;
#endif
static void __init isa_probe(void)
{
int i, j, sock, k, ns, id;
ioaddr_t port;
#ifdef __ISAPNP__
#ifdef CONFIG_PNP
struct isapnp_device_id *devid;
struct pci_dev *dev;
struct pnp_dev *dev;
for (devid = id_table; devid->vendor; devid++) {
if ((dev = isapnp_find_dev(NULL, devid->vendor, devid->function, NULL))) {
printk("ISAPNP ");
if ((dev = pnp_find_dev(NULL, devid->vendor, devid->function, NULL))) {
printk("PNP ");
if (dev->prepare && dev->prepare(dev) < 0) {
printk("prepare failed\n");
break;
}
if (dev->activate && dev->activate(dev) < 0) {
if (pnp_activate_dev(dev, NULL) < 0) {
printk("activate failed\n");
break;
}
if ((i365_base = pci_resource_start(dev, 0))) {
i365_base = pnp_port_start(dev, 0);
if (i365_base) {
printk("no resources ?\n");
break;
}
......@@ -1644,8 +1640,8 @@ static void __exit exit_i82365(void)
release_region(socket[i].ioaddr, 2);
}
#if defined(CONFIG_ISA) && defined(__ISAPNP__)
if (i82365_pnpdev && i82365_pnpdev->deactivate)
i82365_pnpdev->deactivate(i82365_pnpdev);
if (i82365_pnpdev)
pnp_disable_dev(i82365_pnpdev);
#endif
} /* exit_i82365 */
......
......@@ -192,6 +192,7 @@ struct pnp_dev * pnp_request_card_device(struct pnp_card *card, const char *id,
{
struct list_head *pos;
struct pnp_dev *dev;
struct pnpc_driver *cdrv;
if (!card || !id)
goto done;
if (!from) {
......@@ -212,9 +213,16 @@ struct pnp_dev * pnp_request_card_device(struct pnp_card *card, const char *id,
return NULL;
found:
if (dev->active == 0)
if(pnp_activate_dev(dev)<0)
cdrv = to_pnpc_driver(card->dev.driver);
if (dev->active == 0) {
if (!(cdrv->flags & PNPC_DRIVER_DO_NOT_ACTIVATE)) {
if(pnp_activate_dev(dev,NULL)<0)
return NULL;
}
} else {
if ((cdrv->flags & PNPC_DRIVER_DO_NOT_ACTIVATE))
pnp_disable_dev(dev);
}
spin_lock(&pnp_lock);
list_add_tail(&dev->rdev_list, &card->rdevs);
spin_unlock(&pnp_lock);
......
......@@ -115,7 +115,8 @@ int __pnp_add_device(struct pnp_dev *dev)
int error = 0;
pnp_name_device(dev);
pnp_fixup_device(dev);
strcpy(dev->dev.name,dev->name);
strncpy(dev->dev.name,dev->name,DEVICE_NAME_SIZE-1);
dev->dev.name[DEVICE_NAME_SIZE-1] = '\0';
dev->dev.bus = &pnp_bus_type;
dev->dev.release = &pnp_release_device;
error = device_register(&dev->dev);
......
......@@ -66,7 +66,7 @@ static const struct pnp_device_id * match_device(struct pnp_driver *drv, struct
static int pnp_device_probe(struct device *dev)
{
int error = 0;
int error;
struct pnp_driver *pnp_drv;
struct pnp_dev *pnp_dev;
const struct pnp_device_id *dev_id = NULL;
......@@ -75,9 +75,17 @@ static int pnp_device_probe(struct device *dev)
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 -1;
if (pnp_dev->active == 0) {
if (!(pnp_drv->flags & PNP_DRIVER_DO_NOT_ACTIVATE)) {
error = pnp_activate_dev(pnp_dev, NULL);
if (error < 0)
return error;
}
} else {
if ((pnp_drv->flags & PNP_DRIVER_DO_NOT_ACTIVATE))
pnp_disable_dev(pnp_dev);
}
error = 0;
if (pnp_drv->probe && pnp_dev->active) {
dev_id = match_device(pnp_drv, pnp_dev);
if (dev_id != NULL)
......
......@@ -5,6 +5,7 @@ ID("CSC0003", "Crystal Semiconductor CS423x sound -- MPU401")
ID("IBM3780", "IBM pointing device")
ID("IBM0071", "IBM infrared communications device")
ID("IBM3760", "IBM DSP")
ID("NSC6001", "National Semiconductor Serial Port with Fast IR")
ID("PNP0000", "AT Interrupt Controller")
ID("PNP0001", "EISA Interrupt Controller")
ID("PNP0002", "MCA Interrupt Controller")
......@@ -54,6 +55,7 @@ ID("PNP0602", "Plus Hardcard IIXL/EZ")
ID("PNP0603", "Generic IDE supporting Microsoft Device Bay Specification")
ID("PNP0700", "PC standard floppy disk controller")
ID("PNP0701", "Standard floppy controller supporting MS Device Bay Spec")
ID("PNP0802", "Microsoft Sound System or Compatible Device (obsolete)")
ID("PNP0900", "VGA Compatible")
ID("PNP0901", "Video Seven VRAM/VRAM II/1024i")
ID("PNP0902", "8514/A Compatible")
......@@ -151,7 +153,6 @@ ID("PNP0f1c", "Compaq LTE Trackball PS/2-style Mouse")
ID("PNP0f1d", "Compaq LTE Trackball Serial Mouse")
ID("PNP0f1e", "Microsoft Kids Trackball Mouse")
ID("PNP8001", "Novell/Anthem NE3200")
ID("PNP0802", "Microsoft Sound System or Compatible Device (obsolete)")
ID("PNP8004", "Compaq NE3200")
ID("PNP8006", "Intel EtherExpress/32")
ID("PNP8008", "HP EtherTwist EISA LAN Adapter/32 (HP27248A)")
......
......@@ -295,12 +295,28 @@ pnp_set_current_resources(struct device * dmdev, const char * buf, size_t count,
num_args = sscanf(buf,"%10s %i %10s",command,&depnum,type);
if (!num_args)
goto done;
if (!strnicmp(command,"lock",4)) {
if (dev->active) {
dev->lock_resources = 1;
} else {
error = -EINVAL;
}
goto done;
}
if (!strnicmp(command,"unlock",6)) {
if (dev->lock_resources) {
dev->lock_resources = 0;
} else {
error = -EINVAL;
}
goto done;
}
if (!strnicmp(command,"disable",7)) {
error = pnp_disable_dev(dev);
goto done;
}
if (!strnicmp(command,"auto",4)) {
error = pnp_activate_dev(dev);
error = pnp_activate_dev(dev,NULL);
goto done;
}
if (!strnicmp(command,"manual",6)) {
......@@ -308,7 +324,7 @@ pnp_set_current_resources(struct device * dmdev, const char * buf, size_t count,
goto done;
if (!strnicmp(type,"static",6))
mode = PNP_STATIC;
error = pnp_raw_set_dev(dev,depnum,mode);
error = pnp_raw_set_dev(dev,depnum,NULL,mode);
goto done;
}
done:
......
......@@ -2,8 +2,8 @@
# Makefile for the kernel ISAPNP driver.
#
export-objs := core.o
export-objs := core.o compat.o
isapnp-proc-$(CONFIG_PROC_FS) = proc.o
obj-y := core.o $(isapnp-proc-y)
obj-y := core.o compat.o $(isapnp-proc-y)
......@@ -34,13 +34,12 @@ struct pnp_card *pnp_find_card(unsigned short vendor,
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))
list = from ? from->global_list.next : pnp_cards.next;
while (list != &pnp_cards) {
struct pnp_card *card = global_to_pnp_card(list);
if (compare_pnp_id(card->id,id) || (memcmp(id,any,7)==0))
return card;
list = list->next;
}
......@@ -65,7 +64,7 @@ struct pnp_dev *pnp_find_dev(struct pnp_card *card,
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))
if (compare_pnp_id(dev->id,id) || (memcmp(id,any,7)==0))
return dev;
list = list->next;
}
......@@ -80,7 +79,7 @@ struct pnp_dev *pnp_find_dev(struct pnp_card *card,
}
while (list != &card->devices) {
struct pnp_dev *dev = card_to_pnp_dev(list);
if (compare_pnp_id(&dev->ids,id))
if (compare_pnp_id(dev->id,id))
return dev;
list = list->next;
}
......
......@@ -42,12 +42,8 @@
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/isapnp.h>
#include <linux/pnp.h>
#include <asm/io.h>
LIST_HEAD(isapnp_cards);
LIST_HEAD(isapnp_devices);
#if 0
#define ISAPNP_REGION_OK
#endif
......@@ -106,6 +102,8 @@ static int isapnp_detected;
/* some prototypes */
static int isapnp_config_prepare(struct pnp_dev *dev);
extern struct pnp_protocol isapnp_card_protocol;
extern struct pnp_protocol isapnp_protocol;
static inline void write_data(unsigned char x)
{
......@@ -521,7 +519,7 @@ static void __init isapnp_add_port_resource(struct pnp_dev *dev,
port->max = (tmp[4] << 8) | tmp[3];
port->align = tmp[5];
port->size = tmp[6];
port->flags = tmp[0] ? ISAPNP_PORT_FLAG_16BITADDR : 0;
port->flags = tmp[0] ? PNP_PORT_FLAG_16BITADDR : 0;
pnp_add_port_resource(dev,depnum,port);
return;
}
......@@ -543,7 +541,7 @@ static void __init isapnp_add_fixed_port_resource(struct pnp_dev *dev,
port->min = port->max = (tmp[1] << 8) | tmp[0];
port->size = tmp[2];
port->align = 0;
port->flags = ISAPNP_PORT_FLAG_FIXED;
port->flags = PNP_PORT_FLAG_FIXED;
pnp_add_port_resource(dev,depnum,port);
return;
}
......@@ -686,7 +684,7 @@ static int __init isapnp_create_device(struct pnp_card *card,
case _STAG_STARTDEP:
if (size > 1)
goto __skip;
dependent = 0x100 | ISAPNP_RES_PRIORITY_ACCEPTABLE;
dependent = 0x100 | PNP_RES_PRIORITY_ACCEPTABLE;
if (size > 0) {
isapnp_peek(tmp, size);
dependent = 0x100 | tmp[0];
......@@ -891,7 +889,7 @@ static int __init isapnp_build_device_list(void)
if (isapnp_checksum_value != 0x00)
printk(KERN_ERR "isapnp: checksum for device %i is not valid (0x%x)\n", csn, isapnp_checksum_value);
card->checksum = isapnp_checksum_value;
card->protocol = &isapnp_protocol;
card->protocol = &isapnp_card_protocol;
pnpc_add_card(card);
}
return 0;
......@@ -903,7 +901,12 @@ static int __init isapnp_build_device_list(void)
int isapnp_present(void)
{
return !list_empty(&isapnp_devices);
struct pnp_card *card;
pnp_for_each_card(card) {
if (card->protocol == &isapnp_card_protocol)
return 1;
}
return 0;
}
int isapnp_cfg_begin(int csn, int logdev)
......@@ -947,7 +950,7 @@ static int isapnp_config_prepare(struct pnp_dev *dev)
int idx;
if (dev == NULL)
return -EINVAL;
if (dev->active || dev->ro)
if (dev->active || dev->lock_resources)
return -EBUSY;
for (idx = 0; idx < DEVICE_COUNT_IRQ; idx++) {
dev->irq_resource[idx].name = NULL;
......@@ -970,24 +973,11 @@ static int isapnp_config_prepare(struct pnp_dev *dev)
return 0;
}
void isapnp_resource_change(struct resource *resource,
unsigned long start,
unsigned long size)
{
if (resource == NULL)
return;
resource->flags &= ~IORESOURCE_AUTO;
resource->start = start;
resource->end = start + size - 1;
}
/*
* Inititialization.
*/
EXPORT_SYMBOL(isapnp_cards);
EXPORT_SYMBOL(isapnp_devices);
EXPORT_SYMBOL(isapnp_present);
EXPORT_SYMBOL(isapnp_cfg_begin);
EXPORT_SYMBOL(isapnp_cfg_end);
......@@ -999,7 +989,6 @@ EXPORT_SYMBOL(isapnp_write_word);
EXPORT_SYMBOL(isapnp_write_dword);
EXPORT_SYMBOL(isapnp_wake);
EXPORT_SYMBOL(isapnp_device);
EXPORT_SYMBOL(isapnp_resource_change);
static int isapnp_get_resources(struct pnp_dev *dev)
{
......@@ -1053,8 +1042,15 @@ static int isapnp_disable_resources(struct pnp_dev *dev)
return 0;
}
struct pnp_protocol isapnp_card_protocol = {
.name = "ISA Plug and Play - card",
.get = NULL,
.set = NULL,
.disable = NULL,
};
struct pnp_protocol isapnp_protocol = {
.name = "ISA Plug and Play",
.name = "ISA Plug and Play - device",
.get = isapnp_get_resources,
.set = isapnp_set_resources,
.disable = isapnp_disable_resources,
......@@ -1064,6 +1060,7 @@ int __init isapnp_init(void)
{
int cards;
struct pnp_card *card;
struct pnp_dev *dev;
if (isapnp_disable) {
isapnp_detected = 0;
......@@ -1084,6 +1081,9 @@ int __init isapnp_init(void)
return -EBUSY;
}
if(pnp_register_protocol(&isapnp_card_protocol)<0)
return -EBUSY;
if(pnp_register_protocol(&isapnp_protocol)<0)
return -EBUSY;
......@@ -1126,13 +1126,11 @@ int __init isapnp_init(void)
protocol_for_each_card(&isapnp_protocol,card) {
cards++;
if (isapnp_verbose) {
struct list_head *devlist;
printk(KERN_INFO "isapnp: Card '%s'\n", card->name[0]?card->name:"Unknown");
if (isapnp_verbose < 2)
continue;
for (devlist = card->devices.next; devlist != &card->devices; devlist = devlist->next) {
struct pci_dev *dev = pci_dev_b(devlist);
printk(KERN_INFO "isapnp: Device '%s'\n", dev->dev.name[0]?card->name:"Unknown");
pnp_card_for_each_dev(card,dev) {
printk(KERN_INFO "isapnp: Device '%s'\n", dev->name[0]?dev->name:"Unknown");
}
}
}
......
......@@ -28,6 +28,7 @@
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
extern struct pnp_protocol isapnp_protocol;
static struct proc_dir_entry *isapnp_proc_bus_dir = NULL;
......
......@@ -1058,6 +1058,7 @@ static void inline pnpid32_to_pnpid(u32 id, char *str)
static void node_id_data_to_dev(unsigned char *p, struct pnp_bios_node *node, struct pnp_dev *dev)
{
int len;
char id[8];
struct pnp_id *dev_id;
if ((char *)p == NULL)
......@@ -1083,7 +1084,9 @@ static void node_id_data_to_dev(unsigned char *p, struct pnp_bios_node *node, st
dev_id = pnpbios_kmalloc(sizeof (struct pnp_id), GFP_KERNEL);
if (!dev_id)
return;
pnpid32_to_pnpid(p[1] | p[2] << 8 | p[3] << 16 | p[4] << 24,dev_id->id);
memset(dev_id, 0, sizeof(struct pnp_id));
pnpid32_to_pnpid(p[1] | p[2] << 8 | p[3] << 16 | p[4] << 24,id);
memcpy(&dev_id->id, id, 7);
pnp_add_id(dev_id, dev);
break;
}
......@@ -1258,7 +1261,7 @@ static int pnpbios_get_resources(struct pnp_dev *dev)
struct pnp_bios_node * node;
/* just in case */
if(dev->driver)
if(pnp_dev_has_driver(dev))
return -EBUSY;
if(!pnp_is_dynamic(dev))
return -EPERM;
......@@ -1281,7 +1284,7 @@ static int pnpbios_set_resources(struct pnp_dev *dev, struct pnp_cfg *config, ch
struct pnp_bios_node * node;
/* just in case */
if(dev->driver)
if(pnp_dev_has_driver(dev))
return -EBUSY;
if (flags == PNP_DYNAMIC && !pnp_is_dynamic(dev))
return -EPERM;
......@@ -1335,7 +1338,7 @@ static int pnpbios_disable_resources(struct pnp_dev *dev)
if (!config)
return -1;
/* just in case */
if(dev->driver)
if(pnp_dev_has_driver(dev))
return -EBUSY;
if(dev->flags & PNP_NO_DISABLE || !pnp_is_dynamic(dev))
return -EPERM;
......@@ -1396,7 +1399,7 @@ static int inline insert_device(struct pnp_dev *dev)
static void __init build_devlist(void)
{
u8 nodenum;
char id[7];
char id[8];
unsigned char *pos;
unsigned int nodes_got = 0;
unsigned int devs = 0;
......@@ -1432,14 +1435,15 @@ static void __init build_devlist(void)
break;
memset(dev,0,sizeof(struct pnp_dev));
dev_id = pnpbios_kmalloc(sizeof (struct pnp_id), GFP_KERNEL);
if (!dev_id)
if (!dev_id) {
kfree(dev);
break;
}
memset(dev_id,0,sizeof(struct pnp_id));
dev->number = thisnodenum;
memcpy(dev->name,"Unknown Device",13);
dev->name[14] = '\0';
strcpy(dev->name,"Unknown Device");
pnpid32_to_pnpid(node->eisa_id,id);
memcpy(dev_id->id,id,8);
memcpy(dev_id->id,id,7);
pnp_add_id(dev_id, dev);
pos = node_current_resource_data_to_dev(node,dev);
pos = node_possible_resource_data_to_dev(pos,node,dev);
......@@ -1448,9 +1452,10 @@ static void __init build_devlist(void)
dev->protocol = &pnpbios_protocol;
if(insert_device(dev)<0)
if(insert_device(dev)<0) {
kfree(dev_id);
kfree(dev);
else
} else
devs++;
if (nodenum <= thisnodenum) {
printk(KERN_ERR "PnPBIOS: build_devlist: Node number 0x%x is out of sequence following node 0x%x. Aborting.\n", (unsigned int)nodenum, (unsigned int)thisnodenum);
......
......@@ -588,45 +588,65 @@ static int pnp_generate_dma(struct pnp_cfg *config, int num)
return -ENOENT;
}
static int pnp_prepare_request(struct pnp_cfg *config)
int pnp_init_res_cfg(struct pnp_res_cfg *res_config)
{
struct pnp_dev *dev;
int idx;
if (!config)
return -EINVAL;
dev = &config->request;
if (dev == NULL)
if (!res_config)
return -EINVAL;
if (dev->active || dev->ro)
return -EBUSY;
for (idx = 0; idx < DEVICE_COUNT_IRQ; idx++) {
dev->irq_resource[idx].name = NULL;
dev->irq_resource[idx].start = -1;
dev->irq_resource[idx].end = -1;
dev->irq_resource[idx].flags = 0;
res_config->irq_resource[idx].start = -1;
res_config->irq_resource[idx].end = -1;
res_config->irq_resource[idx].flags = 0;
}
for (idx = 0; idx < DEVICE_COUNT_DMA; idx++) {
dev->dma_resource[idx].name = NULL;
dev->dma_resource[idx].start = -1;
dev->dma_resource[idx].end = -1;
dev->dma_resource[idx].flags = 0;
res_config->dma_resource[idx].name = NULL;
res_config->dma_resource[idx].start = -1;
res_config->dma_resource[idx].end = -1;
res_config->dma_resource[idx].flags = 0;
}
for (idx = 0; idx < DEVICE_COUNT_RESOURCE; idx++) {
dev->resource[idx].name = NULL;
dev->resource[idx].start = 0;
dev->resource[idx].end = 0;
dev->resource[idx].flags = 0;
res_config->resource[idx].name = NULL;
res_config->resource[idx].start = 0;
res_config->resource[idx].end = 0;
res_config->resource[idx].flags = 0;
}
return 0;
}
static int pnp_generate_request(struct pnp_cfg *config)
static int pnp_prepare_request(struct pnp_dev *dev, struct pnp_cfg *config, struct pnp_res_cfg *template)
{
int i;
int idx, err;
if (!config)
return -EINVAL;
if (pnp_prepare_request<0)
return -ENOENT;
if (dev->lock_resources)
return -EPERM;
if (dev->active)
return -EBUSY;
err = pnp_init_res_cfg(&config->request);
if (err < 0)
return err;
if (!template)
return 0;
for (idx = 0; idx < DEVICE_COUNT_IRQ; idx++)
if (template->irq_resource[idx].start >= 0)
config->request.irq_resource[idx] = template->irq_resource[idx];
for (idx = 0; idx < DEVICE_COUNT_DMA; idx++)
if (template->dma_resource[idx].start >= 0)
config->request.dma_resource[idx] = template->dma_resource[idx];
for (idx = 0; idx < DEVICE_COUNT_RESOURCE; idx++)
if (template->resource[idx].start > 0)
config->request.resource[idx] = template->resource[idx];
return 0;
}
static int pnp_generate_request(struct pnp_dev *dev, struct pnp_cfg *config, struct pnp_res_cfg *template)
{
int i, err;
if (!config)
return -EINVAL;
if ((err = pnp_prepare_request(dev, config, template))<0)
return err;
for (i=0; i<=7; i++)
{
if(pnp_generate_port(config,i)<0)
......@@ -745,7 +765,7 @@ static struct pnp_cfg * pnp_generate_config(struct pnp_dev *dev, int depnum)
* finds the best resource configuration and then informs the correct pnp protocol
*/
int pnp_activate_dev(struct pnp_dev *dev)
int pnp_activate_dev(struct pnp_dev *dev, struct pnp_res_cfg *template)
{
int depnum, max;
struct pnp_cfg *config;
......@@ -754,7 +774,7 @@ int pnp_activate_dev(struct pnp_dev *dev)
max = pnp_get_max_depnum(dev);
if (dev->active)
return -EBUSY;
if (dev->driver){
if (pnp_dev_has_driver(dev)){
printk(KERN_INFO "pnp: Automatic configuration failed because the PnP device '%s' is busy\n", dev->dev.bus_id);
return -EINVAL;
}
......@@ -767,7 +787,7 @@ int pnp_activate_dev(struct pnp_dev *dev)
config = pnp_generate_config(dev,depnum);
if (!config)
return -EINVAL;
if (pnp_generate_request(config)==0)
if (pnp_generate_request(dev,config,template)==0)
goto done;
kfree(config);
}
......@@ -794,10 +814,12 @@ int pnp_disable_dev(struct pnp_dev *dev)
{
if (!dev)
return -EINVAL;
if (dev->driver){
if (pnp_dev_has_driver(dev)){
printk(KERN_INFO "pnp: Disable failed becuase the PnP device '%s' is busy\n", dev->dev.bus_id);
return -EINVAL;
}
if (dev->lock_resources)
return -EPERM;
if (!dev->protocol->disable || !dev->active)
return -EINVAL;
pnp_dbg("the device '%s' has been disabled", dev->dev.bus_id);
......@@ -812,21 +834,21 @@ int pnp_disable_dev(struct pnp_dev *dev)
*
*/
int pnp_raw_set_dev(struct pnp_dev *dev, int depnum, int mode)
int pnp_raw_set_dev(struct pnp_dev *dev, int depnum, struct pnp_res_cfg *template, int mode)
{
struct pnp_cfg *config;
if (!dev)
return -EINVAL;
config = pnp_generate_config(dev,depnum);
if (dev->driver){
printk(KERN_INFO "pnp: Unable to set resources becuase the PnP device '%s' is busy\n", dev->dev.bus_id);
if (pnp_dev_has_driver(dev)){
printk(KERN_INFO "pnp: Unable to set resources because the PnP device '%s' is busy\n", dev->dev.bus_id);
return -EINVAL;
}
if (!dev->protocol->get || !dev->protocol->set)
return -EINVAL;
config = pnp_generate_config(dev,depnum);
if (!config)
return -EINVAL;
if (pnp_generate_request(config)==0)
if (pnp_generate_request(dev,config,template)==0)
goto done;
kfree(config);
printk(KERN_ERR "pnp: Manual configuration failed for device '%s' due to resource conflicts\n", dev->dev.bus_id);
......@@ -840,6 +862,23 @@ int pnp_raw_set_dev(struct pnp_dev *dev, int depnum, int mode)
return 0;
}
/**
* pnp_resource_change - change one resource
* @resource: pointer to resource to be changed
* @start: start of region
* @size: size of region
*
*/
void pnp_resource_change(struct resource *resource, unsigned long start, unsigned long size)
{
if (resource == NULL)
return;
resource->flags &= ~IORESOURCE_AUTO;
resource->start = start;
resource->end = start + size - 1;
}
EXPORT_SYMBOL(pnp_build_resource);
EXPORT_SYMBOL(pnp_find_resources);
EXPORT_SYMBOL(pnp_get_max_depnum);
......@@ -848,9 +887,11 @@ EXPORT_SYMBOL(pnp_add_dma_resource);
EXPORT_SYMBOL(pnp_add_port_resource);
EXPORT_SYMBOL(pnp_add_mem_resource);
EXPORT_SYMBOL(pnp_add_mem32_resource);
EXPORT_SYMBOL(pnp_init_res_cfg);
EXPORT_SYMBOL(pnp_activate_dev);
EXPORT_SYMBOL(pnp_disable_dev);
EXPORT_SYMBOL(pnp_raw_set_dev);
EXPORT_SYMBOL(pnp_resource_change);
/* format is: allowdma0 */
......
......@@ -360,7 +360,7 @@ static int serial_pnp_guess_board(struct pnp_dev *dev, int *flags)
struct pnp_resources *res = dev->res;
struct pnp_resources *resa;
if (!(check_name(dev->name) || check_name(dev->card->name)))
if (!(check_name(dev->name) || (dev->card && check_name(dev->card->name))))
return -ENODEV;
if (!res)
......@@ -385,8 +385,11 @@ serial_pnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id)
{
struct serial_struct serial_req;
int ret, line, flags = dev_id->driver_data;
if (flags & UNKNOWN_DEV)
if (flags & UNKNOWN_DEV) {
ret = serial_pnp_guess_board(dev, &flags);
if (ret < 0)
return ret;
}
if (flags & SPCI_FL_NO_SHIRQ)
avoid_irq_share(dev);
memset(&serial_req, 0, sizeof(serial_req));
......
......@@ -24,6 +24,7 @@
#include <linux/config.h>
#include <linux/errno.h>
#include <linux/pnp.h>
/*
* Configuration registers (TODO: change by specification)
......@@ -54,79 +55,7 @@
#ifdef __KERNEL__
#include <linux/pci.h>
#define ISAPNP_PORT_FLAG_16BITADDR (1<<0)
#define ISAPNP_PORT_FLAG_FIXED (1<<1)
struct isapnp_port {
unsigned short min; /* min base number */
unsigned short max; /* max base number */
unsigned char align; /* align boundary */
unsigned char size; /* size of range */
unsigned char flags; /* port flags */
unsigned char pad; /* pad */
struct isapnp_resources *res; /* parent */
struct isapnp_port *next; /* next port */
};
struct isapnp_irq {
unsigned short map; /* bitmaks for IRQ lines */
unsigned char flags; /* IRQ flags */
unsigned char pad; /* pad */
struct isapnp_resources *res; /* parent */
struct isapnp_irq *next; /* next IRQ */
};
struct isapnp_dma {
unsigned char map; /* bitmask for DMA channels */
unsigned char flags; /* DMA flags */
struct isapnp_resources *res; /* parent */
struct isapnp_dma *next; /* next port */
};
struct isapnp_mem {
unsigned int min; /* min base number */
unsigned int max; /* max base number */
unsigned int align; /* align boundary */
unsigned int size; /* size of range */
unsigned char flags; /* memory flags */
unsigned char pad; /* pad */
struct isapnp_resources *res; /* parent */
struct isapnp_mem *next; /* next memory resource */
};
struct isapnp_mem32 {
/* TODO */
unsigned char data[17];
struct isapnp_resources *res; /* parent */
struct isapnp_mem32 *next; /* next 32-bit memory resource */
};
struct isapnp_fixup {
unsigned short vendor; /* matching vendor */
unsigned short device; /* matching device */
void (*quirk_function)(struct pci_dev *dev); /* fixup function */
};
#define ISAPNP_RES_PRIORITY_PREFERRED 0
#define ISAPNP_RES_PRIORITY_ACCEPTABLE 1
#define ISAPNP_RES_PRIORITY_FUNCTIONAL 2
#define ISAPNP_RES_PRIORITY_INVALID 65535
struct isapnp_resources {
unsigned short priority; /* priority */
unsigned short dependent; /* dependent resources */
struct isapnp_port *port; /* first port */
struct isapnp_irq *irq; /* first IRQ */
struct isapnp_dma *dma; /* first DMA */
struct isapnp_mem *mem; /* first memory resource */
struct isapnp_mem32 *mem32; /* first 32-bit memory */
struct pci_dev *dev; /* parent */
struct isapnp_resources *alt; /* alternative resource (aka dependent resources) */
struct isapnp_resources *next; /* next resource */
};
#define DEVICE_COUNT_COMPATIBLE 4
#define ISAPNP_ANY_ID 0xffff
#define ISAPNP_CARD_DEVS 8
......@@ -162,14 +91,6 @@ struct isapnp_device_id {
unsigned long driver_data; /* data private to the driver */
};
struct isapnp_driver {
struct list_head node;
char *name;
const struct isapnp_device_id *id_table; /* NULL if wants all devices */
int (*probe) (struct pci_dev *dev, const struct isapnp_device_id *id); /* New device inserted */
void (*remove) (struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */
};
#if defined(CONFIG_ISAPNP) || (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE))
#define __ISAPNP__
......@@ -188,7 +109,6 @@ void isapnp_wake(unsigned char csn);
void isapnp_device(unsigned char device);
void isapnp_activate(unsigned char device);
void isapnp_deactivate(unsigned char device);
void isapnp_fixup_device(struct pci_dev *dev);
void *isapnp_alloc(long size);
#ifdef CONFIG_PROC_FS
......@@ -199,40 +119,8 @@ static inline isapnp_proc_init(void) { return 0; }
static inline isapnp_proc_done(void) { return 0; }
#endif
/* misc */
void isapnp_resource_change(struct resource *resource,
unsigned long start,
unsigned long size);
/* init/main.c */
int isapnp_init(void);
/* manager */
static inline struct pci_bus *isapnp_find_card(unsigned short vendor,
unsigned short device,
struct pci_bus *from) { return NULL; }
static inline struct pci_dev *isapnp_find_dev(struct pci_bus *card,
unsigned short vendor,
unsigned short function,
struct pci_dev *from) { return NULL; }
static inline int isapnp_probe_cards(const struct isapnp_card_id *ids,
int (*probe)(struct pci_bus *card,
const struct isapnp_card_id *id)) { return -ENODEV; }
static inline int isapnp_probe_devs(const struct isapnp_device_id *ids,
int (*probe)(struct pci_dev *dev,
const struct isapnp_device_id *id)) { return -ENODEV; }
static inline int isapnp_activate_dev(struct pci_dev *dev, const char *name) { return -ENODEV; }
static inline int isapnp_register_driver(struct isapnp_driver *drv) { return 0; }
static inline void isapnp_unregister_driver(struct isapnp_driver *drv) { }
extern struct list_head isapnp_cards;
extern struct list_head isapnp_devices;
extern struct pnp_protocol isapnp_protocol;
#define isapnp_for_each_card(card) \
for(card = to_pnp_card(isapnp_cards.next); card != to_pnp_card(&isapnp_cards); card = to_pnp_card(card->node.next))
#define isapnp_for_each_dev(dev) \
for(dev = protocol_to_pnp_dev(isapnp_protocol.devices.next); dev != protocol_to_pnp_dev(&isapnp_protocol.devices); dev = protocol_to_pnp_dev(dev->dev_list.next))
#else /* !CONFIG_ISAPNP */
......@@ -250,28 +138,6 @@ static inline void isapnp_wake(unsigned char csn) { ; }
static inline void isapnp_device(unsigned char device) { ; }
static inline void isapnp_activate(unsigned char device) { ; }
static inline void isapnp_deactivate(unsigned char device) { ; }
/* manager */
static inline struct pci_bus *isapnp_find_card(unsigned short vendor,
unsigned short device,
struct pci_bus *from) { return NULL; }
static inline struct pci_dev *isapnp_find_dev(struct pci_bus *card,
unsigned short vendor,
unsigned short function,
struct pci_dev *from) { return NULL; }
static inline int isapnp_probe_cards(const struct isapnp_card_id *ids,
int (*probe)(struct pci_bus *card,
const struct isapnp_card_id *id)) { return -ENODEV; }
static inline int isapnp_probe_devs(const struct isapnp_device_id *ids,
int (*probe)(struct pci_dev *dev,
const struct isapnp_device_id *id)) { return -ENODEV; }
static inline void isapnp_resource_change(struct resource *resource,
unsigned long start,
unsigned long size) { ; }
static inline int isapnp_activate_dev(struct pci_dev *dev, const char *name) { return -ENODEV; }
static inline int isapnp_register_driver(struct isapnp_driver *drv) { return 0; }
static inline void isapnp_unregister_driver(struct isapnp_driver *drv) { }
#endif /* CONFIG_ISAPNP */
......
......@@ -362,7 +362,7 @@ enum pci_mmap_state {
#define PCI_ANY_ID (~0)
/*
* The pci_dev structure is used to describe both PCI and ISAPnP devices.
* The pci_dev structure is used to describe PCI devices.
*/
struct pci_dev {
struct list_head global_list; /* node in list of all PCI devices */
......@@ -410,16 +410,9 @@ struct pci_dev {
struct resource irq_resource[DEVICE_COUNT_IRQ];
char slot_name[8]; /* slot name */
int active; /* ISAPnP: device is active */
int ro; /* ISAPnP: read only */
unsigned short regs; /* ISAPnP: supported registers */
/* These fields are used by common fixups */
unsigned short transparent:1; /* Transparent PCI bridge */
int (*prepare)(struct pci_dev *dev); /* ISAPnP hooks */
int (*activate)(struct pci_dev *dev);
int (*deactivate)(struct pci_dev *dev);
unsigned int transparent:1; /* Transparent PCI bridge */
};
#define pci_dev_g(n) list_entry(n, struct pci_dev, global_list)
......@@ -463,13 +456,7 @@ struct pci_bus {
unsigned char subordinate; /* max number of subordinate buses */
char name[48];
unsigned short vendor;
unsigned short device;
unsigned int serial; /* serial number */
unsigned char pnpver; /* Plug & Play version */
unsigned char productver; /* product version */
unsigned char checksum; /* if zero - checksum passed */
unsigned char pad1;
struct device * dev;
};
......
......@@ -26,6 +26,7 @@
struct pnp_resource;
struct pnp_protocol;
struct pnp_id;
struct pnp_cfg;
struct pnp_card {
char name[80];
......@@ -50,11 +51,15 @@ struct pnp_card {
#define global_to_pnp_card(n) list_entry(n, struct pnp_card, global_list)
#define protocol_to_pnp_card(n) list_entry(n, struct pnp_card, protocol_list)
#define to_pnp_card(n) list_entry(n, struct pnp_card, dev)
#define to_pnp_card(n) container_of(n, struct pnp_card, dev)
#define pnp_for_each_card(card) \
for(dev = global_to_pnp_card(pnp_cards.next); \
dev != global_to_pnp_card(&cards); \
dev = global_to_pnp_card(card>global_list.next))
for((card) = global_to_pnp_card(pnp_cards.next); \
(card) != global_to_pnp_card(&pnp_cards); \
(card) = global_to_pnp_card((card)->global_list.next))
#define pnp_card_for_each_dev(card,dev) \
for((dev) = card_to_pnp_dev((card)->devices.next); \
(dev) != card_to_pnp_dev(&(card)->devices); \
(dev) = card_to_pnp_dev((dev)->card_list.next))
static inline void *pnpc_get_drvdata (struct pnp_card *pcard)
{
......@@ -79,7 +84,6 @@ static inline void pnpc_set_protodata (struct pnp_card *pcard, void *data)
struct pnp_dev {
char name[80]; /* device name */
int active; /* status of the device */
int ro; /* read only */
struct list_head global_list; /* node in global list of devices */
struct list_head protocol_list; /* node in list of device's protocol */
struct list_head card_list; /* node in card's list of devices */
......@@ -93,6 +97,7 @@ struct pnp_dev {
unsigned short regs; /* ISAPnP: supported registers */
struct pnp_resources *res; /* possible resource information */
int lock_resources; /* resources are locked */
struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */
struct resource dma_resource[DEVICE_COUNT_DMA];
struct resource irq_resource[DEVICE_COUNT_IRQ];
......@@ -112,6 +117,13 @@ struct pnp_dev {
dev != global_to_pnp_dev(&pnp_global); \
dev = global_to_pnp_dev(dev->global_list.next))
static inline int pnp_dev_has_driver(struct pnp_dev *pdev)
{
if (pdev->driver || (pdev->card && pdev->card->driver))
return 1;
return 0;
}
static inline void *pnp_get_drvdata (struct pnp_dev *pdev)
{
return dev_get_drvdata(&pdev->dev);
......@@ -160,10 +172,13 @@ struct pnp_card_id {
} devs[MAX_DEVICES]; /* logical devices */
};
#define PNP_DRIVER_DO_NOT_ACTIVATE (1<<0)
struct pnp_driver {
struct list_head node;
char *name;
const struct pnp_device_id *id_table;
unsigned int flags;
int (*probe) (struct pnp_dev *dev, const struct pnp_device_id *dev_id);
void (*remove) (struct pnp_dev *dev);
struct device_driver driver;
......@@ -171,10 +186,13 @@ struct pnp_driver {
#define to_pnp_driver(drv) container_of(drv,struct pnp_driver, driver)
#define PNPC_DRIVER_DO_NOT_ACTIVATE (1<<0)
struct pnpc_driver {
struct list_head node;
char *name;
const struct pnp_card_id *id_table;
unsigned int flags;
int (*probe) (struct pnp_card *card, const struct pnp_card_id *card_id);
void (*remove) (struct pnp_card *card);
struct device_driver driver;
......@@ -279,6 +297,12 @@ struct pnp_resources {
struct pnp_resources *dep; /* dependent resources */
};
struct pnp_res_cfg {
struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */
struct resource dma_resource[DEVICE_COUNT_DMA];
struct resource irq_resource[DEVICE_COUNT_IRQ];
};
#define PNP_DYNAMIC 0 /* get or set current resource */
#define PNP_STATIC 1 /* get or set resource for next boot */
......@@ -287,7 +311,7 @@ struct pnp_cfg {
struct pnp_irq *irq[2];
struct pnp_dma *dma[2];
struct pnp_mem *mem[4];
struct pnp_dev request;
struct pnp_res_cfg request;
};
......@@ -340,9 +364,11 @@ int pnp_add_dma_resource(struct pnp_dev *dev, int depnum, struct pnp_dma *data);
int pnp_add_port_resource(struct pnp_dev *dev, int depnum, struct pnp_port *data);
int pnp_add_mem_resource(struct pnp_dev *dev, int depnum, struct pnp_mem *data);
int pnp_add_mem32_resource(struct pnp_dev *dev, int depnum, struct pnp_mem32 *data);
int pnp_activate_dev(struct pnp_dev *dev);
int pnp_init_res_cfg(struct pnp_res_cfg *template);
int pnp_activate_dev(struct pnp_dev *dev, struct pnp_res_cfg *template);
int pnp_disable_dev(struct pnp_dev *dev);
int pnp_raw_set_dev(struct pnp_dev *dev, int depnum, int mode);
int pnp_raw_set_dev(struct pnp_dev *dev, int depnum, struct pnp_res_cfg *template, int mode);
void pnp_resource_change(struct resource *resource, unsigned long start, unsigned long size);
/* driver */
int compare_pnp_id(struct pnp_id * pos, const char * id);
......@@ -366,9 +392,10 @@ static inline int pnp_add_dma_resource(struct pnp_dev *dev, int depnum, struct p
static inline int pnp_add_port_resource(struct pnp_dev *dev, int depnum, struct pnp_irq *data) { return -ENODEV; }
static inline int pnp_add_mem_resource(struct pnp_dev *dev, int depnum, struct pnp_irq *data) { return -ENODEV; }
static inline int pnp_add_mem32_resource(struct pnp_dev *dev, int depnum, struct pnp_irq *data) { return -ENODEV; }
static inline int pnp_activate_dev(struct pnp_dev *dev) { return -ENODEV; }
static inline int pnp_init_res_cfg(struct pnp_res_cfg *template) { return -ENODEV; }
static inline int pnp_activate_dev(struct pnp_dev *dev, struct pnp_res_cfg *template) { return -ENODEV; }
static inline int pnp_disable_dev(struct pnp_dev *dev) { return -ENODEV; }
static inline int pnp_raw_set_dev(struct pnp_dev *dev, int depnum, int mode) { return -ENODEV; }
static inline int pnp_raw_set_dev(struct pnp_dev *dev, int depnum, struct pnp_res_cfg *template, int mode) { return -ENODEV; }
static inline int compare_pnp_id(struct list_head * id_list, const char * id) { return -ENODEV; }
static inline int pnp_add_id(struct pnp_id *id, struct pnp_dev *dev) { return -ENODEV; }
static inline int pnp_register_driver(struct pnp_driver *drv) { return -ENODEV; }
......
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