Commit 4efafa32 authored by Russell King's avatar Russell King

[SERIAL] Remove struct pci_board from init_fn

Traditionally, we allocated the private array of port parameters based on
the detected board->num_ports, and then called the init_fn with the board
pointer (which points into the global table.)  Some init_fn implementations
modify num_ports, which therefore affects the global table.  Unfortunately,
this means that if the init_fn increases num_ports (because we have two
almost identical cards, the first with a smaller number of ports than the
second), we will overwrite memory which hasn't been allocated to us.

This cset no longer passes the board pointer into the init_fn, but instead
allows the init_fn to return the number of ports found, or zero to use the
default value in board->num_ports, or negative error number.
parent eef76d73
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License. * the Free Software Foundation; either version 2 of the License.
* *
* $Id: 8250_pci.c,v 1.24 2002/07/29 14:39:56 rmk Exp $ * $Id: 8250_pci.c,v 1.28 2002/11/02 11:14:18 rmk Exp $
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/serialP.h> #include <linux/serialP.h>
#include <asm/bitops.h> #include <asm/bitops.h>
...@@ -55,14 +54,19 @@ struct serial_private { ...@@ -55,14 +54,19 @@ struct serial_private {
int line[0]; int line[0];
}; };
/*
* init_fn returns:
* > 0 - number of ports
* = 0 - use board->num_ports
* < 0 - error
*/
struct pci_board { struct pci_board {
int flags; int flags;
int num_ports; int num_ports;
int base_baud; int base_baud;
int uart_offset; int uart_offset;
int reg_shift; int reg_shift;
int (*init_fn)(struct pci_dev *dev, struct pci_board *board, int (*init_fn)(struct pci_dev *dev, int enable);
int enable);
int first_uart_offset; int first_uart_offset;
}; };
...@@ -201,8 +205,7 @@ get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx) ...@@ -201,8 +205,7 @@ get_pci_irq(struct pci_dev *dev, struct pci_board *board, int idx)
* seems to be mainly needed on card using the PLX which also use I/O * seems to be mainly needed on card using the PLX which also use I/O
* mapped memory. * mapped memory.
*/ */
static int __devinit static int __devinit pci_plx9050_fn(struct pci_dev *dev, int enable)
pci_plx9050_fn(struct pci_dev *dev, struct pci_board *board, int enable)
{ {
u8 *p, irq_config = 0; u8 *p, irq_config = 0;
...@@ -264,8 +267,7 @@ pci_plx9050_fn(struct pci_dev *dev, struct pci_board *board, int enable) ...@@ -264,8 +267,7 @@ pci_plx9050_fn(struct pci_dev *dev, struct pci_board *board, int enable)
#define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc) #define PCI_DEVICE_ID_SIIG_1S_10x (PCI_DEVICE_ID_SIIG_1S_10x_550 & 0xfffc)
#define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8) #define PCI_DEVICE_ID_SIIG_2S_10x (PCI_DEVICE_ID_SIIG_2S_10x_550 & 0xfff8)
static int __devinit static int __devinit pci_siig10x_fn(struct pci_dev *dev, int enable)
pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable)
{ {
u16 data, *p; u16 data, *p;
...@@ -296,8 +298,7 @@ pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable) ...@@ -296,8 +298,7 @@ pci_siig10x_fn(struct pci_dev *dev, struct pci_board *board, int enable)
#define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc) #define PCI_DEVICE_ID_SIIG_2S_20x (PCI_DEVICE_ID_SIIG_2S_20x_550 & 0xfffc)
#define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc) #define PCI_DEVICE_ID_SIIG_2S1P_20x (PCI_DEVICE_ID_SIIG_2S1P_20x_550 & 0xfffc)
static int __devinit static int __devinit pci_siig20x_fn(struct pci_dev *dev, int enable)
pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable)
{ {
u8 data; u8 data;
...@@ -318,8 +319,7 @@ pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable) ...@@ -318,8 +319,7 @@ pci_siig20x_fn(struct pci_dev *dev, struct pci_board *board, int enable)
} }
/* Added for EKF Intel i960 serial boards */ /* Added for EKF Intel i960 serial boards */
static int __devinit static int __devinit pci_inteli960ni_fn(struct pci_dev *dev, int enable)
pci_inteli960ni_fn(struct pci_dev *dev, struct pci_board *board, int enable)
{ {
unsigned long oldval; unsigned long oldval;
...@@ -378,8 +378,7 @@ static struct timedia_struct { ...@@ -378,8 +378,7 @@ static struct timedia_struct {
{ 0, 0 } { 0, 0 }
}; };
static int __devinit static int __devinit pci_timedia_fn(struct pci_dev *dev, int enable)
pci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable)
{ {
int i, j; int i, j;
unsigned short *ids; unsigned short *ids;
...@@ -389,12 +388,9 @@ pci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable) ...@@ -389,12 +388,9 @@ pci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable)
for (i = 0; timedia_data[i].num; i++) { for (i = 0; timedia_data[i].num; i++) {
ids = timedia_data[i].ids; ids = timedia_data[i].ids;
for (j = 0; ids[j]; j++) { for (j = 0; ids[j]; j++)
if (pci_get_subdevice(dev) == ids[j]) { if (pci_get_subdevice(dev) == ids[j])
board->num_ports = timedia_data[i].num; return timedia_data[i].num;
return 0;
}
}
} }
return 0; return 0;
} }
...@@ -406,9 +402,10 @@ pci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable) ...@@ -406,9 +402,10 @@ pci_timedia_fn(struct pci_dev *dev, struct pci_board *board, int enable)
* and Keystone have one Diva chip with 3 UARTs. Some later machines have * and Keystone have one Diva chip with 3 UARTs. Some later machines have
* one Diva chip, but it has been expanded to 5 UARTs. * one Diva chip, but it has been expanded to 5 UARTs.
*/ */
static int __devinit static int __devinit pci_hp_diva(struct pci_dev *dev, int enable)
pci_hp_diva(struct pci_dev *dev, struct pci_board *board, int enable)
{ {
int rc = 0;
if (!enable) if (!enable)
return 0; return 0;
...@@ -417,25 +414,24 @@ pci_hp_diva(struct pci_dev *dev, struct pci_board *board, int enable) ...@@ -417,25 +414,24 @@ pci_hp_diva(struct pci_dev *dev, struct pci_board *board, int enable)
case PCI_DEVICE_ID_HP_DIVA_HALFDOME: case PCI_DEVICE_ID_HP_DIVA_HALFDOME:
case PCI_DEVICE_ID_HP_DIVA_KEYSTONE: case PCI_DEVICE_ID_HP_DIVA_KEYSTONE:
case PCI_DEVICE_ID_HP_DIVA_EVEREST: case PCI_DEVICE_ID_HP_DIVA_EVEREST:
board->num_ports = 3; rc = 3;
break; break;
case PCI_DEVICE_ID_HP_DIVA_TOSCA2: case PCI_DEVICE_ID_HP_DIVA_TOSCA2:
board->num_ports = 2; rc = 2;
break; break;
case PCI_DEVICE_ID_HP_DIVA_MAESTRO: case PCI_DEVICE_ID_HP_DIVA_MAESTRO:
board->num_ports = 4; rc = 4;
break; break;
case PCI_DEVICE_ID_HP_DIVA_POWERBAR: case PCI_DEVICE_ID_HP_DIVA_POWERBAR:
board->num_ports = 1; rc = 1;
break; break;
} }
return 0; return rc;
} }
static int __devinit static int __devinit pci_xircom_fn(struct pci_dev *dev, int enable)
pci_xircom_fn(struct pci_dev *dev, struct pci_board *board, int enable)
{ {
__set_current_state(TASK_UNINTERRUPTIBLE); __set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(HZ/10); schedule_timeout(HZ/10);
...@@ -579,8 +575,11 @@ static struct pci_board pci_boards[] __devinitdata = { ...@@ -579,8 +575,11 @@ static struct pci_board pci_boards[] __devinitdata = {
0x400, 7, pci_plx9050_fn }, 0x400, 7, pci_plx9050_fn },
{ SPCI_FL_BASE2, 4, 921600, /* pbn_plx_romulus */ { SPCI_FL_BASE2, 4, 921600, /* pbn_plx_romulus */
0x20, 2, pci_plx9050_fn, 0x03 }, 0x20, 2, pci_plx9050_fn, 0x03 },
/* This board uses the size of PCI Base region 0 to
* signal now many ports are available */ /*
* This board uses the size of PCI Base region 0 to
* signal now many ports are available
*/
{ SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, /* pbn_oxsemi */ { SPCI_FL_BASE0 | SPCI_FL_REGION_SZ_CAP, 32, 115200 }, /* pbn_oxsemi */
{ SPCI_FL_BASE_TABLE, 1, 921600, /* pbn_timedia */ { SPCI_FL_BASE_TABLE, 1, 921600, /* pbn_timedia */
0, 0, pci_timedia_fn }, 0, 0, pci_timedia_fn },
...@@ -647,7 +646,7 @@ serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board) ...@@ -647,7 +646,7 @@ serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board)
if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) && if ((((dev->class >> 8) != PCI_CLASS_COMMUNICATION_SERIAL) &&
((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) || ((dev->class >> 8) != PCI_CLASS_COMMUNICATION_MODEM)) ||
(dev->class & 0xff) > 6) (dev->class & 0xff) > 6)
return 1; return -ENODEV;
for (i = 0; i < 6; i++) { for (i = 0; i < 6; i++) {
if (IS_PCI_REGION_IOPORT(dev, i)) { if (IS_PCI_REGION_IOPORT(dev, i)) {
...@@ -667,20 +666,31 @@ serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board) ...@@ -667,20 +666,31 @@ serial_pci_guess_board(struct pci_dev *dev, struct pci_board *board)
board->flags = first_port; board->flags = first_port;
return 0; return 0;
} }
return 1; return -ENODEV;
}
static inline int
serial_pci_matches(struct pci_board *board, int index)
{
return
board->base_baud == pci_boards[index].base_baud &&
board->num_ports == pci_boards[index].num_ports &&
board->uart_offset == pci_boards[index].uart_offset &&
board->reg_shift == pci_boards[index].reg_shift &&
board->first_uart_offset == pci_boards[index].first_uart_offset;
} }
/* /*
* return an error code to refuse. * Probe one serial board. Unfortunately, there is no rhyme nor reason
* * to the arrangement of serial ports on a PCI card.
* serial_struct is 60 bytes.
*/ */
static int __devinit pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent) static int __devinit
pci_init_one(struct pci_dev *dev, const struct pci_device_id *ent)
{ {
struct serial_private *priv; struct serial_private *priv;
struct pci_board *board, tmp; struct pci_board *board, tmp;
struct serial_struct serial_req; struct serial_struct serial_req;
int base_baud, rc, k; int base_baud, rc, nr_ports, i;
if (ent->driver_data >= ARRAY_SIZE(pci_boards)) { if (ent->driver_data >= ARRAY_SIZE(pci_boards)) {
printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n", printk(KERN_ERR "pci_init_one: invalid driver_data: %ld\n",
...@@ -694,67 +704,100 @@ static int __devinit pci_init_one(struct pci_dev *dev, const struct pci_device_i ...@@ -694,67 +704,100 @@ static int __devinit pci_init_one(struct pci_dev *dev, const struct pci_device_i
if (rc) if (rc)
return rc; return rc;
if (ent->driver_data == pbn_default && if (ent->driver_data == pbn_default) {
serial_pci_guess_board(dev, board)) { /*
pci_disable_device(dev); * Use a copy of the pci_board entry for this;
return -ENODEV; * avoid changing entries in the table.
} else if (serial_pci_guess_board(dev, &tmp) == 0) { */
printk(KERN_INFO "Redundant entry in serial pci_table. " memcpy(&tmp, board, sizeof(struct pci_board));
"Please send the output of\n" board = &tmp;
"lspci -vv, this message (%d,%d,%d,%d)\n"
"and the manufacturer and name of " /*
"serial board or modem board\n" * We matched one of our class entries. Try to
"to serial-pci-info@lists.sourceforge.net.\n", * determine the parameters of this board.
*/
rc = serial_pci_guess_board(dev, board);
if (rc)
goto disable;
} else {
/*
* We matched an explicit entry. If we are able to
* detect this boards settings with our heuristic,
* then we no longer need this entry.
*/
rc = serial_pci_guess_board(dev, &tmp);
if (rc == 0 && serial_pci_matches(board, pbn_default)) {
printk(KERN_INFO
"Redundant entry in serial pci_table. Please send the output\n"
"of lspci -vv, this message (0x%04x,0x%04x,0x%04x,0x%04x),\n"
"the manufacturer and name of serial board or modem board to\n"
"rmk@arm.linux.org.uk.\n",
dev->vendor, dev->device, dev->vendor, dev->device,
pci_get_subvendor(dev), pci_get_subdevice(dev)); pci_get_subvendor(dev), pci_get_subdevice(dev));
} }
priv = kmalloc(sizeof(struct serial_private) +
sizeof(unsigned int) * board->num_ports,
GFP_KERNEL);
if (!priv) {
pci_disable_device(dev);
return -ENOMEM;
} }
nr_ports = board->num_ports;
/* /*
* Run the initialization function, if any * Run the initialization function, if any. The initialization
* function returns:
* <0 - error
* 0 - use board->num_ports
* >0 - number of ports
*/ */
if (board->init_fn) { if (board->init_fn) {
rc = board->init_fn(dev, board, 1); rc = board->init_fn(dev, 1);
if (rc != 0) { if (rc < 0)
pci_disable_device(dev); goto disable;
kfree(priv);
return rc; if (rc)
nr_ports = rc;
} }
priv = kmalloc(sizeof(struct serial_private) +
sizeof(unsigned int) * nr_ports,
GFP_KERNEL);
if (!priv) {
rc = -ENOMEM;
goto deinit;
} }
base_baud = board->base_baud; base_baud = board->base_baud;
if (!base_baud) if (!base_baud)
base_baud = BASE_BAUD; base_baud = BASE_BAUD;
memset(&serial_req, 0, sizeof(serial_req)); memset(&serial_req, 0, sizeof(serial_req));
for (k = 0; k < board->num_ports; k++) { for (i = 0; i < nr_ports; i++) {
serial_req.irq = get_pci_irq(dev, board, k); serial_req.irq = get_pci_irq(dev, board, i);
if (get_pci_port(dev, board, &serial_req, k)) if (get_pci_port(dev, board, &serial_req, i))
break; break;
#ifdef SERIAL_DEBUG_PCI #ifdef SERIAL_DEBUG_PCI
printk("Setup PCI/PNP port: port %x, irq %d, type %d\n", printk("Setup PCI port: port %x, irq %d, type %d\n",
serial_req.port, serial_req.irq, serial_req.io_type); serial_req.port, serial_req.irq, serial_req.io_type);
#endif #endif
serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE; serial_req.flags = ASYNC_SKIP_TEST | ASYNC_AUTOPROBE;
serial_req.baud_base = base_baud; serial_req.baud_base = base_baud;
priv->line[k] = register_serial(&serial_req); priv->line[i] = register_serial(&serial_req);
if (priv->line[k] < 0) if (priv->line[i] < 0)
break; break;
} }
priv->board = board; priv->board = board;
priv->nr = k; priv->nr = i;
pci_set_drvdata(dev, priv); pci_set_drvdata(dev, priv);
return 0; return 0;
free:
kfree(priv);
deinit:
if (board->init_fn)
board->init_fn(dev, 0);
disable:
pci_disable_device(dev);
return rc;
} }
static void __devexit pci_remove_one(struct pci_dev *dev) static void __devexit pci_remove_one(struct pci_dev *dev)
...@@ -769,7 +812,7 @@ static void __devexit pci_remove_one(struct pci_dev *dev) ...@@ -769,7 +812,7 @@ static void __devexit pci_remove_one(struct pci_dev *dev)
unregister_serial(priv->line[i]); unregister_serial(priv->line[i]);
if (priv->board->init_fn) if (priv->board->init_fn)
priv->board->init_fn(dev, priv->board, 0); priv->board->init_fn(dev, 0);
pci_disable_device(dev); pci_disable_device(dev);
...@@ -1160,18 +1203,23 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = { ...@@ -1160,18 +1203,23 @@ static struct pci_device_id serial_pci_tbl[] __devinitdata = {
PCI_ANY_ID, PCI_ANY_ID, 0, 0, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_dci_pccom8 }, pbn_dci_pccom8 },
/*
* These entries match devices with class
* COMMUNICATION_SERIAL, COMMUNICATION_MODEM
* or COMMUNICATION_MULTISERIAL
*/
{ PCI_ANY_ID, PCI_ANY_ID, { PCI_ANY_ID, PCI_ANY_ID,
PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_COMMUNICATION_SERIAL << 8, PCI_CLASS_COMMUNICATION_SERIAL << 8,
0xffff00, }, 0xffff00, pbn_default },
{ PCI_ANY_ID, PCI_ANY_ID, { PCI_ANY_ID, PCI_ANY_ID,
PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_COMMUNICATION_MODEM << 8, PCI_CLASS_COMMUNICATION_MODEM << 8,
0xffff00, }, 0xffff00, pbn_default },
{ PCI_ANY_ID, PCI_ANY_ID, { PCI_ANY_ID, PCI_ANY_ID,
PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_COMMUNICATION_MULTISERIAL << 8, PCI_CLASS_COMMUNICATION_MULTISERIAL << 8,
0xffff00, }, 0xffff00, pbn_default },
{ 0, } { 0, }
}; };
......
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